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.

566 lines
20KB

  1. /*
  2. * meli - accounts 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. /*!
  22. * Account management from user configuration.
  23. */
  24. use super::AccountConf;
  25. use super::ToggleFlag;
  26. use fnv::FnvHashMap;
  27. use melib::async_workers::{Async, AsyncBuilder, AsyncStatus};
  28. use melib::backends::{
  29. BackendOp, Backends, Folder, FolderHash, MailBackend, NotifyFn, RefreshEvent,
  30. RefreshEventConsumer, RefreshEventKind,
  31. };
  32. use melib::error::Result;
  33. use melib::mailbox::*;
  34. use melib::thread::ThreadHash;
  35. use melib::AddressBook;
  36. use melib::StackVec;
  37. use crate::types::UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate, Notification};
  38. use std::collections::VecDeque;
  39. use std::fs;
  40. use std::io;
  41. use std::mem;
  42. use std::ops::{Index, IndexMut};
  43. use std::result;
  44. use std::sync::Arc;
  45. pub type Worker = Option<Async<(Result<FnvHashMap<EnvelopeHash, Envelope>>, Result<Mailbox>)>>;
  46. macro_rules! mailbox {
  47. ($idx:expr, $folders:expr) => {
  48. $folders
  49. .get_mut(&$idx)
  50. .unwrap()
  51. .as_mut()
  52. .unwrap()
  53. .as_mut()
  54. .unwrap()
  55. };
  56. }
  57. #[derive(Debug)]
  58. pub struct Account {
  59. name: String,
  60. pub(crate) folders: FnvHashMap<FolderHash, Option<Result<Mailbox>>>,
  61. pub(crate) folders_order: Vec<FolderHash>,
  62. folder_names: FnvHashMap<FolderHash, String>,
  63. tree: Vec<FolderNode>,
  64. sent_folder: Option<FolderHash>,
  65. pub(crate) collection: Collection,
  66. pub(crate) address_book: AddressBook,
  67. pub(crate) workers: FnvHashMap<FolderHash, Worker>,
  68. pub(crate) settings: AccountConf,
  69. pub(crate) runtime_settings: AccountConf,
  70. pub(crate) backend: Box<dyn MailBackend>,
  71. event_queue: VecDeque<(FolderHash, RefreshEvent)>,
  72. notify_fn: Arc<NotifyFn>,
  73. }
  74. impl Drop for Account {
  75. fn drop(&mut self) {
  76. //TODO: Avoid panics
  77. let data_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
  78. if let Ok(data) = data_dir.place_data_file("addressbook") {
  79. /* place result in cache directory */
  80. let f = match fs::File::create(data) {
  81. Ok(f) => f,
  82. Err(e) => {
  83. panic!("{}", e);
  84. }
  85. };
  86. let writer = io::BufWriter::new(f);
  87. serde_json::to_writer(writer, &self.address_book).unwrap();
  88. };
  89. if let Ok(data) = data_dir.place_data_file("mailbox") {
  90. /* place result in cache directory */
  91. let f = match fs::File::create(data) {
  92. Ok(f) => f,
  93. Err(e) => {
  94. panic!("{}", e);
  95. }
  96. };
  97. let writer = io::BufWriter::new(f);
  98. bincode::serialize_into(writer, &self.folders).unwrap();
  99. };
  100. }
  101. }
  102. pub struct MailboxIterator<'a> {
  103. folders_order: &'a [FolderHash],
  104. folders: &'a FnvHashMap<FolderHash, Option<Result<Mailbox>>>,
  105. pos: usize,
  106. }
  107. impl<'a> Iterator for MailboxIterator<'a> {
  108. type Item = Option<&'a Mailbox>;
  109. fn next(&mut self) -> Option<Option<&'a Mailbox>> {
  110. if self.pos == self.folders.len() {
  111. return None;
  112. }
  113. let fh = &self.folders_order[self.pos];
  114. if self.pos == self.folders.len() {
  115. return None;
  116. }
  117. self.pos += 1;
  118. if self.folders[&fh].is_none() {
  119. return Some(None);
  120. }
  121. if let Some(Err(_)) = self.folders[&fh] {
  122. return Some(None);
  123. }
  124. return Some(Some(self.folders[&fh].as_ref().unwrap().as_ref().unwrap()));
  125. }
  126. }
  127. #[derive(Serialize, Debug, Default)]
  128. struct FolderNode {
  129. hash: FolderHash,
  130. kids: Vec<FolderNode>,
  131. }
  132. impl Account {
  133. pub fn new(
  134. name: String,
  135. mut settings: AccountConf,
  136. map: &Backends,
  137. notify_fn: NotifyFn,
  138. ) -> Self {
  139. let mut backend = map.get(settings.account().format())(settings.account());
  140. let mut ref_folders: FnvHashMap<FolderHash, Folder> = backend.folders();
  141. let mut folders: FnvHashMap<FolderHash, Option<Result<Mailbox>>> =
  142. FnvHashMap::with_capacity_and_hasher(ref_folders.len(), Default::default());
  143. let mut folders_order: Vec<FolderHash> = Vec::with_capacity(ref_folders.len());
  144. let mut workers: FnvHashMap<FolderHash, Worker> = FnvHashMap::default();
  145. let notify_fn = Arc::new(notify_fn);
  146. let mut folder_names = FnvHashMap::default();
  147. let mut sent_folder = None;
  148. for f in ref_folders.values_mut() {
  149. let entry = settings
  150. .folder_confs
  151. .entry(f.name().to_string())
  152. .or_default();
  153. if f.name().eq_ignore_ascii_case("sent") {
  154. sent_folder = Some(f.hash());
  155. }
  156. if (f.name().eq_ignore_ascii_case("junk")
  157. || f.name().eq_ignore_ascii_case("spam")
  158. || f.name().eq_ignore_ascii_case("sent")
  159. || f.name().eq_ignore_ascii_case("trash"))
  160. && entry.ignore.is_unset()
  161. {
  162. entry.ignore = ToggleFlag::InternalVal(true);
  163. }
  164. folder_names.insert(f.hash(), f.name().to_string());
  165. }
  166. let mut stack: StackVec<FolderHash> = StackVec::new();
  167. let mut tree: Vec<FolderNode> = Vec::new();
  168. for (h, f) in ref_folders.iter() {
  169. if f.parent().is_none() {
  170. fn rec(h: FolderHash, ref_folders: &FnvHashMap<FolderHash, Folder>) -> FolderNode {
  171. let mut node = FolderNode {
  172. hash: h,
  173. kids: Vec::new(),
  174. };
  175. for &c in ref_folders[&h].children() {
  176. node.kids.push(rec(c, ref_folders));
  177. }
  178. node
  179. };
  180. tree.push(rec(*h, &ref_folders));
  181. for &c in f.children() {
  182. stack.push(c);
  183. }
  184. while let Some(next) = stack.pop() {
  185. for c in ref_folders[&next].children() {
  186. stack.push(*c);
  187. }
  188. }
  189. }
  190. folders.insert(*h, None);
  191. workers.insert(
  192. *h,
  193. Account::new_worker(f.clone(), &mut backend, notify_fn.clone()),
  194. );
  195. }
  196. tree.sort_unstable_by_key(|f| ref_folders[&f.hash].name());
  197. let mut stack: StackVec<Option<&FolderNode>> = StackVec::new();
  198. for n in tree.iter_mut() {
  199. folders_order.push(n.hash);
  200. n.kids.sort_unstable_by_key(|f| ref_folders[&f.hash].name());
  201. stack.extend(n.kids.iter().rev().map(Some));
  202. while let Some(Some(next)) = stack.pop() {
  203. folders_order.push(next.hash);
  204. stack.extend(next.kids.iter().rev().map(Some));
  205. }
  206. }
  207. let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap();
  208. let address_book = if let Ok(data) = data_dir.place_data_file("addressbook") {
  209. if data.exists() {
  210. let reader = io::BufReader::new(fs::File::open(data).unwrap());
  211. let result: result::Result<AddressBook, _> = serde_json::from_reader(reader);
  212. if let Ok(data_t) = result {
  213. data_t
  214. } else {
  215. AddressBook::new(name.clone())
  216. }
  217. } else {
  218. AddressBook::new(name.clone())
  219. }
  220. } else {
  221. AddressBook::new(name.clone())
  222. };
  223. Account {
  224. name,
  225. folders,
  226. folders_order,
  227. folder_names,
  228. tree,
  229. address_book,
  230. sent_folder,
  231. collection: Collection::new(Default::default()),
  232. workers,
  233. settings: settings.clone(),
  234. runtime_settings: settings,
  235. backend,
  236. notify_fn,
  237. event_queue: VecDeque::with_capacity(8),
  238. }
  239. }
  240. fn new_worker(
  241. folder: Folder,
  242. backend: &mut Box<MailBackend>,
  243. notify_fn: Arc<NotifyFn>,
  244. ) -> Worker {
  245. let mailbox_handle = backend.get(&folder);
  246. let mut builder = AsyncBuilder::new();
  247. let tx = builder.tx();
  248. Some(builder.build(Box::new(move || {
  249. let mut handle = mailbox_handle.clone();
  250. let folder = folder.clone();
  251. let work = handle.work().unwrap();
  252. work.compute();
  253. handle.join();
  254. let envelopes: Result<FnvHashMap<EnvelopeHash, Envelope>> = handle.extract().map(|v| {
  255. v.into_iter()
  256. .map(|e| (e.hash(), e))
  257. .collect::<FnvHashMap<EnvelopeHash, Envelope>>()
  258. });
  259. let hash = folder.hash();
  260. let m = Mailbox::new(folder, envelopes.as_ref().map_err(Clone::clone));
  261. tx.send(AsyncStatus::Payload((envelopes, m)));
  262. notify_fn.notify(hash);
  263. })))
  264. }
  265. pub fn reload(&mut self, event: RefreshEvent, folder_hash: FolderHash) -> Option<UIEvent> {
  266. if self.folders[&folder_hash].is_none()
  267. || self.folders[&folder_hash].as_ref().unwrap().is_err()
  268. {
  269. self.event_queue.push_back((folder_hash, event));
  270. return None;
  271. }
  272. let kind = event.kind();
  273. {
  274. //let mailbox: &mut Mailbox = self.folders[idx].as_mut().unwrap().as_mut().unwrap();
  275. match kind {
  276. RefreshEventKind::Update(old_hash, envelope) => {
  277. mailbox!(&folder_hash, self.folders).rename(old_hash, envelope.hash());
  278. self.collection.update(old_hash, *envelope, folder_hash);
  279. return Some(EnvelopeUpdate(old_hash));
  280. }
  281. RefreshEventKind::Rename(old_hash, new_hash) => {
  282. debug!("rename {} to {}", old_hash, new_hash);
  283. let mailbox = mailbox!(&folder_hash, self.folders);
  284. mailbox.rename(old_hash, new_hash);
  285. self.collection.rename(old_hash, new_hash, folder_hash);
  286. return Some(EnvelopeRename(old_hash, new_hash));
  287. }
  288. RefreshEventKind::Create(envelope) => {
  289. let env_hash = envelope.hash();
  290. let mailbox = mailbox!(&folder_hash, self.folders);
  291. mailbox.insert(env_hash);
  292. self.collection.insert(*envelope, folder_hash);
  293. if self
  294. .sent_folder
  295. .as_ref()
  296. .map(|h| *h == folder_hash)
  297. .unwrap_or(false)
  298. {
  299. self.collection.insert_reply(env_hash);
  300. }
  301. let ref_folders: FnvHashMap<FolderHash, Folder> = self.backend.folders();
  302. let folder_conf = &self.settings.folder_confs[&self.folder_names[&folder_hash]];
  303. if folder_conf.ignore.is_true() {
  304. return None;
  305. }
  306. let (_, thread_node) = self.mail_and_thread(env_hash, folder_hash);
  307. if thread_node.snoozed() {
  308. return None;
  309. }
  310. let env = self.get_env(&env_hash);
  311. return Some(Notification(
  312. Some("new mail".into()),
  313. format!(
  314. "{} {:.15}:\n\nFrom: {:.15}\nSubject: {:.15}",
  315. self.name,
  316. ref_folders[&folder_hash].name(),
  317. env.subject(),
  318. env.field_from_to_string(),
  319. ),
  320. ));
  321. }
  322. RefreshEventKind::Remove(envelope_hash) => {
  323. mailbox!(&folder_hash, self.folders).remove(envelope_hash);
  324. self.collection.remove(envelope_hash, folder_hash);
  325. return Some(EnvelopeRemove(envelope_hash));
  326. }
  327. RefreshEventKind::Rescan => {
  328. let ref_folders: FnvHashMap<FolderHash, Folder> = self.backend.folders();
  329. let handle = Account::new_worker(
  330. ref_folders[&folder_hash].clone(),
  331. &mut self.backend,
  332. self.notify_fn.clone(),
  333. );
  334. self.workers.insert(folder_hash, handle);
  335. }
  336. }
  337. }
  338. None
  339. }
  340. pub fn watch(&self, r: RefreshEventConsumer) {
  341. self.backend.watch(r).unwrap();
  342. }
  343. /* This doesn't represent the number of correctly parsed mailboxes though */
  344. pub fn len(&self) -> usize {
  345. self.folders.len()
  346. }
  347. pub fn is_empty(&self) -> bool {
  348. self.folders.is_empty()
  349. }
  350. pub fn list_folders(&self) -> Vec<Folder> {
  351. let mut folders = self.backend.folders();
  352. if let Some(folder_confs) = self.settings.conf().folders() {
  353. //debug!("folder renames: {:?}", folder_renames);
  354. for f in folders.values_mut() {
  355. if let Some(r) = folder_confs.get(f.name()) {
  356. if let Some(rename) = r.rename() {
  357. f.change_name(rename);
  358. }
  359. }
  360. }
  361. }
  362. /*
  363. if let Some(pos) = folders
  364. .iter()
  365. .position(|f| f.name().eq_ignore_ascii_case("INBOX"))
  366. {
  367. folders.swap(pos, 0);
  368. }
  369. */
  370. let order: FnvHashMap<FolderHash, usize> = self
  371. .folders_order
  372. .iter()
  373. .enumerate()
  374. .map(|(i, &fh)| (fh, i))
  375. .collect();
  376. let mut folders: Vec<Folder> = folders.drain().map(|(_, f)| f).collect();
  377. folders.sort_unstable_by(|a, b| order[&a.hash()].partial_cmp(&order[&b.hash()]).unwrap());
  378. folders
  379. }
  380. pub fn folders_order(&self) -> &Vec<FolderHash> {
  381. &self.folders_order
  382. }
  383. pub fn name(&self) -> &str {
  384. &self.name
  385. }
  386. pub fn workers(&mut self) -> &mut FnvHashMap<FolderHash, Worker> {
  387. &mut self.workers
  388. }
  389. fn load_mailbox(
  390. &mut self,
  391. folder_hash: FolderHash,
  392. mailbox: (Result<FnvHashMap<EnvelopeHash, Envelope>>, Result<Mailbox>),
  393. ) {
  394. let (envs, mut mailbox) = mailbox;
  395. if envs.is_err() {
  396. self.folders.insert(folder_hash, None);
  397. return;
  398. }
  399. let envs = envs.unwrap();
  400. self.collection
  401. .merge(envs, folder_hash, &mut mailbox, self.sent_folder);
  402. self.folders.insert(folder_hash, Some(mailbox));
  403. }
  404. pub fn status(&mut self, folder_hash: FolderHash) -> result::Result<(), usize> {
  405. match self.workers.get_mut(&folder_hash).unwrap() {
  406. None => {
  407. return Ok(());
  408. }
  409. Some(ref mut w) if self.folders[&folder_hash].is_none() => match w.poll() {
  410. Ok(AsyncStatus::NoUpdate) => {
  411. return Err(0);
  412. }
  413. Ok(AsyncStatus::Finished) => {}
  414. Ok(AsyncStatus::ProgressReport(n)) => {
  415. return Err(n);
  416. }
  417. _ => {
  418. return Err(0);
  419. }
  420. },
  421. Some(_) => return Ok(()),
  422. };
  423. let m = mem::replace(self.workers.get_mut(&folder_hash).unwrap(), None)
  424. .unwrap()
  425. .extract();
  426. self.workers.insert(folder_hash, None);
  427. self.load_mailbox(folder_hash, m);
  428. Ok(())
  429. }
  430. pub fn save_draft(&self, draft: Draft) -> Result<()> {
  431. let finalize = draft.finalise()?;
  432. self.backend
  433. .save(&finalize.as_bytes(), &self.settings.conf.draft_folder)
  434. }
  435. pub fn save(&self, bytes: &[u8], folder: &str) -> Result<()> {
  436. self.backend.save(bytes, folder)
  437. }
  438. pub fn iter_mailboxes(&self) -> MailboxIterator {
  439. MailboxIterator {
  440. folders_order: &self.folders_order,
  441. folders: &self.folders,
  442. pos: 0,
  443. }
  444. }
  445. pub fn get_env(&self, h: &EnvelopeHash) -> &Envelope {
  446. &self.collection[h]
  447. }
  448. pub fn get_env_mut(&mut self, h: &EnvelopeHash) -> &mut Envelope {
  449. self.collection.entry(*h).or_default()
  450. }
  451. pub fn contains_key(&self, h: EnvelopeHash) -> bool {
  452. self.collection.contains_key(&h)
  453. }
  454. pub fn operation(&self, h: EnvelopeHash) -> Box<BackendOp> {
  455. for mailbox in self.folders.values() {
  456. if let Some(Ok(m)) = mailbox {
  457. if m.envelopes.contains(&h) {
  458. return self.backend.operation(h, m.folder.hash());
  459. }
  460. }
  461. }
  462. debug!("didn't find {}", h);
  463. std::dbg!(&self.folders);
  464. std::dbg!(&self.collection.envelopes);
  465. unreachable!()
  466. }
  467. pub fn thread_to_mail_mut(&mut self, h: ThreadHash, f: FolderHash) -> &mut Envelope {
  468. self.collection
  469. .envelopes
  470. .entry(self.collection.threads[&f].thread_to_mail(h))
  471. .or_default()
  472. }
  473. pub fn thread_to_mail(&self, h: ThreadHash, f: FolderHash) -> &Envelope {
  474. &self.collection.envelopes[&self.collection.threads[&f].thread_to_mail(h)]
  475. }
  476. pub fn threaded_mail(&self, h: ThreadHash, f: FolderHash) -> EnvelopeHash {
  477. self.collection.threads[&f].thread_to_mail(h)
  478. }
  479. pub fn mail_and_thread(
  480. &mut self,
  481. i: EnvelopeHash,
  482. f: FolderHash,
  483. ) -> (&mut Envelope, &ThreadNode) {
  484. let thread;
  485. {
  486. let x = &mut self.collection.envelopes.entry(i).or_default();
  487. thread = &self.collection.threads[&f][&x.thread()];
  488. }
  489. (self.collection.envelopes.entry(i).or_default(), thread)
  490. }
  491. pub fn thread(&self, h: ThreadHash, f: FolderHash) -> &ThreadNode {
  492. &self.collection.threads[&f].thread_nodes()[&h]
  493. }
  494. }
  495. impl Index<FolderHash> for Account {
  496. type Output = Result<Mailbox>;
  497. fn index(&self, index: FolderHash) -> &Result<Mailbox> {
  498. &self.folders[&index]
  499. .as_ref()
  500. .expect("BUG: Requested mailbox that is not yet available.")
  501. }
  502. }
  503. /// Will panic if mailbox hasn't loaded, ask `status()` first.
  504. impl IndexMut<FolderHash> for Account {
  505. fn index_mut(&mut self, index: FolderHash) -> &mut Result<Mailbox> {
  506. self.folders
  507. .get_mut(&index)
  508. .unwrap()
  509. .as_mut()
  510. .expect("BUG: Requested mailbox that is not yet available.")
  511. }
  512. }
  513. impl Index<usize> for Account {
  514. type Output = Result<Mailbox>;
  515. fn index(&self, index: usize) -> &Result<Mailbox> {
  516. &self.folders[&self.folders_order[index]]
  517. .as_ref()
  518. .expect("BUG: Requested mailbox that is not yet available.")
  519. }
  520. }
  521. /// Will panic if mailbox hasn't loaded, ask `status()` first.
  522. impl IndexMut<usize> for Account {
  523. fn index_mut(&mut self, index: usize) -> &mut Result<Mailbox> {
  524. self.folders
  525. .get_mut(&self.folders_order[index])
  526. .unwrap()
  527. .as_mut()
  528. .expect("BUG: Requested mailbox that is not yet available.")
  529. }
  530. }