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.

945 lines
33KB

  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, FileFolderConf};
  25. use fnv::FnvHashMap;
  26. use melib::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
  27. use melib::backends::{
  28. BackendOp, Backends, Folder, FolderHash, FolderOperation, MailBackend, NotifyFn, ReadOnlyOp,
  29. RefreshEvent, RefreshEventConsumer, RefreshEventKind, SpecialUseMailbox,
  30. };
  31. use melib::error::{MeliError, Result};
  32. use melib::mailbox::*;
  33. use melib::thread::{SortField, SortOrder, ThreadHash, ThreadNode, Threads};
  34. use melib::AddressBook;
  35. use melib::StackVec;
  36. use crate::types::UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate, Notification};
  37. use crate::{workers::WorkController, StatusEvent, ThreadEvent};
  38. use crossbeam::Sender;
  39. use std::collections::VecDeque;
  40. use std::fs;
  41. use std::io;
  42. use std::ops::{Index, IndexMut};
  43. use std::result;
  44. use std::sync::{Arc, RwLock};
  45. pub type Worker = Option<Async<Result<Vec<Envelope>>>>;
  46. macro_rules! mailbox {
  47. ($idx:expr, $folders:expr) => {
  48. $folders.get_mut(&$idx).unwrap().unwrap_mut()
  49. };
  50. }
  51. #[derive(Serialize, Debug)]
  52. pub enum MailboxEntry {
  53. Available(Mailbox),
  54. Failed(MeliError),
  55. /// first argument is done work, and second is total work
  56. Parsing(Mailbox, usize, usize),
  57. }
  58. impl Default for MailboxEntry {
  59. fn default() -> Self {
  60. MailboxEntry::Parsing(Mailbox::default(), 0, 0)
  61. }
  62. }
  63. impl std::fmt::Display for MailboxEntry {
  64. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  65. write!(
  66. f,
  67. "{}",
  68. match self {
  69. MailboxEntry::Available(ref m) => m.name().to_string(),
  70. MailboxEntry::Failed(ref e) => e.to_string(),
  71. MailboxEntry::Parsing(_, done, total) => {
  72. format!("Parsing messages. [{}/{}]", done, total)
  73. }
  74. }
  75. )
  76. }
  77. }
  78. impl MailboxEntry {
  79. pub fn is_available(&self) -> bool {
  80. if let MailboxEntry::Available(_) = self {
  81. true
  82. } else {
  83. false
  84. }
  85. }
  86. pub fn is_parsing(&self) -> bool {
  87. if let MailboxEntry::Parsing(_, _, _) = self {
  88. true
  89. } else {
  90. false
  91. }
  92. }
  93. pub fn as_result(&self) -> Result<&Mailbox> {
  94. match self {
  95. MailboxEntry::Available(ref m) => Ok(m),
  96. MailboxEntry::Parsing(ref m, _, _) => Ok(m),
  97. MailboxEntry::Failed(ref e) => Err(MeliError::new(format!(
  98. "Mailbox is not available: {}",
  99. e.to_string()
  100. ))),
  101. }
  102. }
  103. pub fn as_mut_result(&mut self) -> Result<&mut Mailbox> {
  104. match self {
  105. MailboxEntry::Available(ref mut m) => Ok(m),
  106. MailboxEntry::Parsing(ref mut m, _, _) => Ok(m),
  107. MailboxEntry::Failed(ref e) => Err(MeliError::new(format!(
  108. "Mailbox is not available: {}",
  109. e.to_string()
  110. ))),
  111. }
  112. }
  113. pub fn unwrap_mut(&mut self) -> &mut Mailbox {
  114. match self {
  115. MailboxEntry::Available(ref mut m) => m,
  116. MailboxEntry::Parsing(ref mut m, _, _) => m,
  117. e => panic!(format!("mailbox is not available! {:#}", e)),
  118. }
  119. }
  120. pub fn unwrap(&self) -> &Mailbox {
  121. match self {
  122. MailboxEntry::Available(ref m) => m,
  123. MailboxEntry::Parsing(ref m, _, _) => m,
  124. e => panic!(format!("mailbox is not available! {:#}", e)),
  125. }
  126. }
  127. }
  128. #[derive(Debug)]
  129. pub struct Account {
  130. pub index: usize,
  131. name: String,
  132. pub is_online: bool,
  133. pub(crate) folders: FnvHashMap<FolderHash, MailboxEntry>,
  134. pub(crate) folder_confs: FnvHashMap<FolderHash, FileFolderConf>,
  135. pub(crate) folders_order: Vec<FolderHash>,
  136. pub(crate) folder_names: FnvHashMap<FolderHash, String>,
  137. tree: Vec<FolderNode>,
  138. sent_folder: Option<FolderHash>,
  139. pub(crate) collection: Collection,
  140. pub(crate) address_book: AddressBook,
  141. pub(crate) workers: FnvHashMap<FolderHash, Worker>,
  142. pub(crate) work_context: WorkContext,
  143. pub(crate) settings: AccountConf,
  144. pub(crate) runtime_settings: AccountConf,
  145. pub(crate) backend: Arc<RwLock<Box<dyn MailBackend>>>,
  146. event_queue: VecDeque<(FolderHash, RefreshEvent)>,
  147. notify_fn: Arc<NotifyFn>,
  148. }
  149. impl Drop for Account {
  150. fn drop(&mut self) {
  151. //TODO: Avoid panics
  152. let data_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
  153. if let Ok(data) = data_dir.place_data_file("addressbook") {
  154. /* place result in cache directory */
  155. let f = match fs::File::create(data) {
  156. Ok(f) => f,
  157. Err(e) => {
  158. panic!("{}", e);
  159. }
  160. };
  161. let writer = io::BufWriter::new(f);
  162. serde_json::to_writer(writer, &self.address_book).unwrap();
  163. };
  164. if let Ok(data) = data_dir.place_data_file("mailbox") {
  165. /* place result in cache directory */
  166. let f = match fs::File::create(data) {
  167. Ok(f) => f,
  168. Err(e) => {
  169. panic!("{}", e);
  170. }
  171. };
  172. let writer = io::BufWriter::new(f);
  173. bincode::serialize_into(writer, &self.folders).unwrap();
  174. };
  175. }
  176. }
  177. pub struct MailboxIterator<'a> {
  178. folders_order: &'a [FolderHash],
  179. folders: &'a FnvHashMap<FolderHash, MailboxEntry>,
  180. pos: usize,
  181. }
  182. impl<'a> Iterator for MailboxIterator<'a> {
  183. type Item = &'a MailboxEntry;
  184. fn next(&mut self) -> Option<&'a MailboxEntry> {
  185. if self.pos == self.folders.len() {
  186. return None;
  187. }
  188. let fh = &self.folders_order[self.pos];
  189. self.pos += 1;
  190. Some(&self.folders[&fh])
  191. }
  192. }
  193. #[derive(Serialize, Debug, Default)]
  194. struct FolderNode {
  195. hash: FolderHash,
  196. kids: Vec<FolderNode>,
  197. }
  198. impl Account {
  199. pub fn new(
  200. index: usize,
  201. name: String,
  202. mut settings: AccountConf,
  203. map: &Backends,
  204. work_context: WorkContext,
  205. notify_fn: NotifyFn,
  206. ) -> Self {
  207. let s = settings.clone();
  208. let backend = map.get(settings.account().format())(
  209. settings.account(),
  210. Box::new(move |path: &str| {
  211. s.folder_confs.contains_key(path)
  212. && s.folder_confs[path].folder_conf().subscribe.is_true()
  213. }),
  214. );
  215. let notify_fn = Arc::new(notify_fn);
  216. let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap();
  217. let address_book = if let Ok(data) = data_dir.place_data_file("addressbook") {
  218. if data.exists() {
  219. let reader = io::BufReader::new(fs::File::open(data).unwrap());
  220. let result: result::Result<AddressBook, _> = serde_json::from_reader(reader);
  221. if let Ok(data_t) = result {
  222. data_t
  223. } else {
  224. AddressBook::new(name.clone())
  225. }
  226. } else {
  227. AddressBook::new(name.clone())
  228. }
  229. } else {
  230. AddressBook::new(name.clone())
  231. };
  232. if settings.account().format() == "imap" {
  233. settings.conf.cache_type = crate::conf::CacheType::None;
  234. }
  235. Account {
  236. index,
  237. name,
  238. is_online: false,
  239. folders: Default::default(),
  240. folder_confs: Default::default(),
  241. folders_order: Default::default(),
  242. folder_names: Default::default(),
  243. tree: Default::default(),
  244. address_book,
  245. sent_folder: Default::default(),
  246. collection: Default::default(),
  247. workers: Default::default(),
  248. work_context,
  249. runtime_settings: settings.clone(),
  250. settings,
  251. backend: Arc::new(RwLock::new(backend)),
  252. notify_fn,
  253. event_queue: VecDeque::with_capacity(8),
  254. }
  255. }
  256. fn init(&mut self) {
  257. let mut ref_folders: FnvHashMap<FolderHash, Folder> =
  258. self.backend.read().unwrap().folders();
  259. let mut folders: FnvHashMap<FolderHash, MailboxEntry> =
  260. FnvHashMap::with_capacity_and_hasher(ref_folders.len(), Default::default());
  261. let mut folders_order: Vec<FolderHash> = Vec::with_capacity(ref_folders.len());
  262. let mut workers: FnvHashMap<FolderHash, Worker> = FnvHashMap::default();
  263. let mut folder_names = FnvHashMap::default();
  264. let mut folder_confs = FnvHashMap::default();
  265. let mut sent_folder = None;
  266. for f in ref_folders.values_mut() {
  267. if !self.settings.folder_confs.contains_key(f.path())
  268. || self.settings.folder_confs[f.path()]
  269. .folder_conf()
  270. .subscribe
  271. .is_false()
  272. {
  273. /* Skip unsubscribed folder */
  274. continue;
  275. }
  276. match self.settings.folder_confs[f.path()].folder_conf().usage {
  277. Some(SpecialUseMailbox::Sent) => {
  278. sent_folder = Some(f.hash());
  279. }
  280. _ => {}
  281. }
  282. folder_confs.insert(f.hash(), self.settings.folder_confs[f.path()].clone());
  283. folder_names.insert(f.hash(), f.path().to_string());
  284. }
  285. let mut stack: StackVec<FolderHash> = StackVec::new();
  286. let mut tree: Vec<FolderNode> = Vec::new();
  287. let mut collection: Collection = Collection::new(Default::default());
  288. for (h, f) in ref_folders.iter() {
  289. if !self.settings.folder_confs.contains_key(f.path())
  290. || self.settings.folder_confs[f.path()]
  291. .folder_conf()
  292. .subscribe
  293. .is_false()
  294. {
  295. /* Skip unsubscribed folder */
  296. continue;
  297. }
  298. if f.parent().is_none() {
  299. fn rec(h: FolderHash, ref_folders: &FnvHashMap<FolderHash, Folder>) -> FolderNode {
  300. let mut node = FolderNode {
  301. hash: h,
  302. kids: Vec::new(),
  303. };
  304. for &c in ref_folders[&h].children() {
  305. node.kids.push(rec(c, ref_folders));
  306. }
  307. node
  308. };
  309. tree.push(rec(*h, &ref_folders));
  310. for &c in f.children() {
  311. stack.push(c);
  312. }
  313. while let Some(next) = stack.pop() {
  314. for c in ref_folders[&next].children() {
  315. stack.push(*c);
  316. }
  317. }
  318. }
  319. folders.insert(
  320. *h,
  321. MailboxEntry::Parsing(Mailbox::new(f.clone(), &FnvHashMap::default()), 0, 0),
  322. );
  323. workers.insert(
  324. *h,
  325. Account::new_worker(
  326. &self.settings,
  327. f.clone(),
  328. &mut self.backend,
  329. &self.work_context,
  330. self.notify_fn.clone(),
  331. ),
  332. );
  333. collection.threads.insert(*h, Threads::default());
  334. }
  335. tree.sort_unstable_by_key(|f| ref_folders[&f.hash].path());
  336. let mut stack: StackVec<Option<&FolderNode>> = StackVec::new();
  337. for n in tree.iter_mut() {
  338. folders_order.push(n.hash);
  339. n.kids.sort_unstable_by_key(|f| ref_folders[&f.hash].path());
  340. stack.extend(n.kids.iter().rev().map(Some));
  341. while let Some(Some(next)) = stack.pop() {
  342. folders_order.push(next.hash);
  343. stack.extend(next.kids.iter().rev().map(Some));
  344. }
  345. }
  346. self.folders = folders;
  347. self.folder_confs = folder_confs;
  348. self.folders_order = folders_order;
  349. self.folder_names = folder_names;
  350. self.tree = tree;
  351. self.sent_folder = sent_folder;
  352. self.collection = collection;
  353. self.workers = workers;
  354. }
  355. fn new_worker(
  356. settings: &AccountConf,
  357. folder: Folder,
  358. backend: &Arc<RwLock<Box<dyn MailBackend>>>,
  359. work_context: &WorkContext,
  360. notify_fn: Arc<NotifyFn>,
  361. ) -> Worker {
  362. let mailbox_handle = backend.write().unwrap().get(&folder);
  363. let mut builder = AsyncBuilder::new();
  364. let our_tx = builder.tx();
  365. let folder_hash = folder.hash();
  366. let priority = match settings.folder_confs[folder.path()].folder_conf().usage {
  367. Some(SpecialUseMailbox::Inbox) => 0,
  368. Some(SpecialUseMailbox::Sent) => 1,
  369. Some(SpecialUseMailbox::Drafts) | Some(SpecialUseMailbox::Trash) => 2,
  370. Some(_) | None => {
  371. 3 * folder
  372. .path()
  373. .split(if folder.path().contains('/') {
  374. '/'
  375. } else {
  376. '.'
  377. })
  378. .count() as u64
  379. }
  380. };
  381. /* This polling closure needs to be 'static', that is to spawn its own thread instead of
  382. * being assigned to a worker thread. Otherwise the polling closures could fill up the
  383. * workers causing no actual parsing to be done. If we could yield from within the worker
  384. * threads' closures this could be avoided, but it requires green threads.
  385. */
  386. builder.set_priority(priority).set_is_static(true);
  387. let mut w = builder.build(Box::new(move |work_context| {
  388. let name = format!("Parsing {}", folder.path());
  389. let mut mailbox_handle = mailbox_handle.clone();
  390. let work = mailbox_handle.work().unwrap();
  391. work_context.new_work.send(work).unwrap();
  392. let thread_id = std::thread::current().id();
  393. work_context.set_name.send((thread_id, name)).unwrap();
  394. work_context
  395. .set_status
  396. .send((thread_id, "Waiting for subworkers..".to_string()))
  397. .unwrap();
  398. loop {
  399. match debug!(mailbox_handle.poll_block()) {
  400. Ok(s @ AsyncStatus::Payload(_)) => {
  401. our_tx.send(s).unwrap();
  402. debug!("notifying for {}", folder_hash);
  403. notify_fn.notify(folder_hash);
  404. }
  405. Ok(s @ AsyncStatus::Finished) => {
  406. our_tx.send(s).unwrap();
  407. notify_fn.notify(folder_hash);
  408. debug!("exiting");
  409. work_context.finished.send(thread_id).unwrap();
  410. return;
  411. }
  412. Ok(s) => {
  413. our_tx.send(s).unwrap();
  414. }
  415. Err(_) => {
  416. debug!("poll error");
  417. return;
  418. }
  419. }
  420. }
  421. }));
  422. if let Some(w) = w.work() {
  423. work_context.new_work.send(w).unwrap();
  424. }
  425. Some(w)
  426. }
  427. pub fn reload(
  428. &mut self,
  429. event: RefreshEvent,
  430. folder_hash: FolderHash,
  431. context: (
  432. &mut WorkController,
  433. &Sender<ThreadEvent>,
  434. &mut VecDeque<UIEvent>,
  435. ),
  436. ) -> Option<UIEvent> {
  437. if !self.folders[&folder_hash].is_available() {
  438. self.event_queue.push_back((folder_hash, event));
  439. return None;
  440. }
  441. let kind = event.kind();
  442. {
  443. //let mailbox: &mut Mailbox = self.folders[idx].as_mut().unwrap().as_mut().unwrap();
  444. match kind {
  445. RefreshEventKind::Update(old_hash, envelope) => {
  446. mailbox!(&folder_hash, self.folders).rename(old_hash, envelope.hash());
  447. #[cfg(feature = "sqlite3")]
  448. {
  449. if let Err(err) = crate::sqlite3::remove(old_hash).and_then(|_| {
  450. crate::sqlite3::insert(&envelope, &self.backend, &self.name)
  451. }) {
  452. melib::log(
  453. format!(
  454. "Failed to update envelope {} in cache: {}",
  455. envelope.message_id_display(),
  456. err.to_string()
  457. ),
  458. melib::ERROR,
  459. );
  460. }
  461. }
  462. self.collection.update(old_hash, *envelope, folder_hash);
  463. return Some(EnvelopeUpdate(old_hash));
  464. }
  465. RefreshEventKind::Rename(old_hash, new_hash) => {
  466. debug!("rename {} to {}", old_hash, new_hash);
  467. mailbox!(&folder_hash, self.folders).rename(old_hash, new_hash);
  468. self.collection.rename(old_hash, new_hash, folder_hash);
  469. #[cfg(feature = "sqlite3")]
  470. {
  471. let envelopes = self.collection.envelopes.read();
  472. let envelopes = envelopes.unwrap();
  473. if let Err(err) = crate::sqlite3::remove(old_hash).and_then(|_| {
  474. crate::sqlite3::insert(&envelopes[&new_hash], &self.backend, &self.name)
  475. }) {
  476. melib::log(
  477. format!(
  478. "Failed to update envelope {} in cache: {}",
  479. &envelopes[&new_hash].message_id_display(),
  480. err.to_string()
  481. ),
  482. melib::ERROR,
  483. );
  484. }
  485. }
  486. return Some(EnvelopeRename(old_hash, new_hash));
  487. }
  488. RefreshEventKind::Create(envelope) => {
  489. let env_hash = envelope.hash();
  490. if self.collection.contains_key(&env_hash)
  491. && mailbox!(&folder_hash, self.folders)
  492. .envelopes
  493. .contains(&env_hash)
  494. {
  495. return None;
  496. }
  497. mailbox!(&folder_hash, self.folders).insert(env_hash);
  498. let (is_seen, is_draft) =
  499. { (envelope.is_seen(), envelope.flags().contains(Flag::DRAFT)) };
  500. let (subject, from) = {
  501. (
  502. envelope.subject().into_owned(),
  503. envelope.field_from_to_string(),
  504. )
  505. };
  506. #[cfg(feature = "sqlite3")]
  507. {
  508. if let Err(err) =
  509. crate::sqlite3::insert(&envelope, &self.backend, &self.name)
  510. {
  511. melib::log(
  512. format!(
  513. "Failed to insert envelope {} in cache: {}",
  514. envelope.message_id_display(),
  515. err.to_string()
  516. ),
  517. melib::ERROR,
  518. );
  519. }
  520. }
  521. self.collection.insert(*envelope, folder_hash);
  522. if self
  523. .sent_folder
  524. .as_ref()
  525. .map(|h| *h == folder_hash)
  526. .unwrap_or(false)
  527. {
  528. self.collection.insert_reply(env_hash);
  529. }
  530. let ref_folders: FnvHashMap<FolderHash, Folder> =
  531. self.backend.read().unwrap().folders();
  532. let folder_conf = &self.settings.folder_confs[&self.folder_names[&folder_hash]];
  533. if folder_conf.folder_conf().ignore.is_true() {
  534. return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
  535. }
  536. let thread_node = {
  537. let thread_hash = &mut self.collection.get_env(env_hash).thread();
  538. &self.collection.threads[&folder_hash][&thread_hash]
  539. };
  540. if thread_node.snoozed() {
  541. return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
  542. }
  543. if is_seen || is_draft {
  544. return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
  545. }
  546. return Some(Notification(
  547. Some(format!("new e-mail from: {}", from)),
  548. format!(
  549. "{}\n{} {}",
  550. subject,
  551. self.name,
  552. ref_folders[&folder_hash].name(),
  553. ),
  554. Some(crate::types::NotificationType::NewMail),
  555. ));
  556. }
  557. RefreshEventKind::Remove(envelope_hash) => {
  558. mailbox!(&folder_hash, self.folders).remove(envelope_hash);
  559. #[cfg(feature = "sqlite3")]
  560. {
  561. let envelopes = self.collection.envelopes.read();
  562. let envelopes = envelopes.unwrap();
  563. if let Err(err) = crate::sqlite3::remove(envelope_hash) {
  564. melib::log(
  565. format!(
  566. "Failed to remove envelope {} [{}] in cache: {}",
  567. &envelopes[&envelope_hash].message_id_display(),
  568. envelope_hash,
  569. err.to_string()
  570. ),
  571. melib::ERROR,
  572. );
  573. }
  574. }
  575. self.collection.remove(envelope_hash, folder_hash);
  576. return Some(EnvelopeRemove(envelope_hash));
  577. }
  578. RefreshEventKind::Rescan => {
  579. let ref_folders: FnvHashMap<FolderHash, Folder> =
  580. self.backend.read().unwrap().folders();
  581. let handle = Account::new_worker(
  582. &self.settings,
  583. ref_folders[&folder_hash].clone(),
  584. &mut self.backend,
  585. &self.work_context,
  586. self.notify_fn.clone(),
  587. );
  588. self.workers.insert(folder_hash, handle);
  589. }
  590. RefreshEventKind::Failure(e) => {
  591. debug!("RefreshEvent Failure: {}", e.to_string());
  592. self.watch(context);
  593. }
  594. }
  595. }
  596. None
  597. }
  598. pub fn watch(
  599. &self,
  600. context: (
  601. &mut WorkController,
  602. &Sender<ThreadEvent>,
  603. &mut VecDeque<UIEvent>,
  604. ),
  605. ) {
  606. let (work_controller, sender, replies) = context;
  607. let sender = sender.clone();
  608. let r = RefreshEventConsumer::new(Box::new(move |r| {
  609. sender.send(ThreadEvent::from(r)).unwrap();
  610. }));
  611. match self
  612. .backend
  613. .read()
  614. .unwrap()
  615. .watch(r, work_controller.get_context())
  616. {
  617. Ok(id) => {
  618. work_controller
  619. .static_threads
  620. .lock()
  621. .unwrap()
  622. .insert(id, format!("watching {}", self.name()).into());
  623. }
  624. Err(e) => {
  625. replies.push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage(
  626. e.to_string(),
  627. )));
  628. }
  629. }
  630. }
  631. pub fn len(&self) -> usize {
  632. self.folders.len()
  633. }
  634. pub fn is_empty(&self) -> bool {
  635. self.folders.is_empty()
  636. }
  637. pub fn list_folders(&self) -> Vec<Folder> {
  638. let mut folders = self.backend.read().unwrap().folders();
  639. let folder_confs = self.settings.conf().folders();
  640. //debug!("folder renames: {:?}", folder_renames);
  641. for f in folders.values_mut() {
  642. if let Some(r) = folder_confs.get(f.path()) {
  643. if let Some(rename) = r.folder_conf().rename() {
  644. f.change_name(rename);
  645. }
  646. }
  647. }
  648. /*
  649. if let Some(pos) = folders
  650. .iter()
  651. .position(|f| f.name().eq_ignore_ascii_case("INBOX"))
  652. {
  653. folders.swap(pos, 0);
  654. }
  655. */
  656. let order: FnvHashMap<FolderHash, usize> = self
  657. .folders_order
  658. .iter()
  659. .enumerate()
  660. .map(|(i, &fh)| (fh, i))
  661. .collect();
  662. let mut folders: Vec<Folder> = folders
  663. .drain()
  664. .map(|(_, f)| f)
  665. .filter(|f| self.folders.contains_key(&f.hash()))
  666. .collect();
  667. folders.sort_unstable_by(|a, b| order[&a.hash()].partial_cmp(&order[&b.hash()]).unwrap());
  668. folders
  669. }
  670. pub fn folders_order(&self) -> &Vec<FolderHash> {
  671. &self.folders_order
  672. }
  673. pub fn name(&self) -> &str {
  674. &self.name
  675. }
  676. pub fn workers(&mut self) -> &mut FnvHashMap<FolderHash, Worker> {
  677. &mut self.workers
  678. }
  679. fn load_mailbox(&mut self, folder_hash: FolderHash, payload: Result<Vec<Envelope>>) {
  680. if payload.is_err() {
  681. self.folders
  682. .insert(folder_hash, MailboxEntry::Failed(payload.unwrap_err()));
  683. return;
  684. }
  685. let envelopes = payload
  686. .unwrap()
  687. .into_iter()
  688. .map(|e| (e.hash(), e))
  689. .collect::<FnvHashMap<EnvelopeHash, Envelope>>();
  690. match self.folders.entry(folder_hash).or_default() {
  691. MailboxEntry::Failed(_) => {}
  692. MailboxEntry::Parsing(ref mut m, _, _) | MailboxEntry::Available(ref mut m) => {
  693. m.merge(&envelopes);
  694. if let Some(updated_folders) =
  695. self.collection
  696. .merge(envelopes, folder_hash, self.sent_folder)
  697. {
  698. for f in updated_folders {
  699. self.notify_fn.notify(f);
  700. }
  701. }
  702. }
  703. }
  704. self.notify_fn.notify(folder_hash);
  705. }
  706. pub fn status(&mut self, folder_hash: FolderHash) -> result::Result<(), usize> {
  707. match self.workers.get_mut(&folder_hash).unwrap() {
  708. None => {
  709. return Ok(());
  710. }
  711. Some(ref mut w) => match w.poll() {
  712. Ok(AsyncStatus::NoUpdate) => {
  713. //return Err(0);
  714. }
  715. Ok(AsyncStatus::Payload(envs)) => {
  716. debug!("got payload in status for {}", folder_hash);
  717. self.load_mailbox(folder_hash, envs);
  718. }
  719. Ok(AsyncStatus::Finished) => {
  720. debug!("got finished in status for {}", folder_hash);
  721. self.folders.entry(folder_hash).and_modify(|f| {
  722. let m = if let MailboxEntry::Parsing(m, _, _) = f {
  723. std::mem::replace(m, Mailbox::default())
  724. } else {
  725. return;
  726. };
  727. *f = MailboxEntry::Available(m);
  728. });
  729. self.workers.insert(folder_hash, None);
  730. }
  731. Ok(AsyncStatus::ProgressReport(n)) => {
  732. self.folders.entry(folder_hash).and_modify(|f| {
  733. if let MailboxEntry::Parsing(_, ref mut d, _) = f {
  734. *d += n;
  735. }
  736. });
  737. //return Err(n);
  738. }
  739. _ => {
  740. //return Err(0);
  741. }
  742. },
  743. };
  744. if self.folders[&folder_hash].is_available()
  745. || (self.folders[&folder_hash].is_parsing()
  746. && self.collection.threads.contains_key(&folder_hash))
  747. {
  748. Ok(())
  749. } else {
  750. Err(0)
  751. }
  752. }
  753. pub fn save(&self, bytes: &[u8], folder: &str, flags: Option<Flag>) -> Result<()> {
  754. if self.settings.account.read_only() {
  755. return Err(MeliError::new(format!(
  756. "Account {} is read-only.",
  757. self.name.as_str()
  758. )));
  759. }
  760. self.backend.write().unwrap().save(bytes, folder, flags)
  761. }
  762. pub fn iter_mailboxes(&self) -> MailboxIterator {
  763. MailboxIterator {
  764. folders_order: &self.folders_order,
  765. folders: &self.folders,
  766. pos: 0,
  767. }
  768. }
  769. pub fn contains_key(&self, h: EnvelopeHash) -> bool {
  770. self.collection.contains_key(&h)
  771. }
  772. pub fn operation(&self, h: EnvelopeHash) -> Box<dyn BackendOp> {
  773. let operation = self.backend.read().unwrap().operation(h);
  774. if self.settings.account.read_only() {
  775. ReadOnlyOp::new(operation)
  776. } else {
  777. operation
  778. }
  779. }
  780. pub fn thread(&self, h: ThreadHash, f: FolderHash) -> &ThreadNode {
  781. &self.collection.threads[&f].thread_nodes()[&h]
  782. }
  783. pub fn folder_operation(&mut self, path: &str, op: FolderOperation) -> Result<()> {
  784. self.backend.write().unwrap().folder_operation(path, op)
  785. }
  786. pub fn folder_confs(&self, folder_hash: FolderHash) -> &FileFolderConf {
  787. &self.folder_confs[&folder_hash]
  788. }
  789. pub fn sent_folder(&self) -> &str {
  790. let sent_folder = self
  791. .settings
  792. .folder_confs
  793. .iter()
  794. .find(|(_, f)| f.folder_conf().usage == Some(SpecialUseMailbox::Sent));
  795. if let Some(sent_folder) = sent_folder.as_ref() {
  796. sent_folder.0
  797. } else {
  798. ""
  799. }
  800. }
  801. pub fn special_use_folder(&self, special_use: SpecialUseMailbox) -> Option<&str> {
  802. let ret = self
  803. .settings
  804. .folder_confs
  805. .iter()
  806. .find(|(_, f)| f.folder_conf().usage == Some(special_use));
  807. ret.as_ref().map(|r| r.0.as_str())
  808. }
  809. /* Call only in Context::is_online, since only Context can launch the watcher threads if an
  810. * account goes from offline to online. */
  811. pub fn is_online(&mut self) -> bool {
  812. let ret = self.backend.read().unwrap().is_online();
  813. if ret != self.is_online && ret {
  814. self.init();
  815. }
  816. self.is_online = ret;
  817. ret
  818. }
  819. pub fn search(
  820. &self,
  821. search_term: &str,
  822. sort: (SortField, SortOrder),
  823. folder_hash: FolderHash,
  824. ) -> Result<StackVec<EnvelopeHash>> {
  825. if self.settings.account().format() == "imap" {
  826. return crate::cache::imap_search(search_term, sort, folder_hash, &self.backend);
  827. }
  828. #[cfg(feature = "sqlite3")]
  829. {
  830. crate::sqlite3::search(search_term, sort)
  831. }
  832. #[cfg(not(feature = "sqlite3"))]
  833. {
  834. let mut ret = StackVec::new();
  835. let envelopes = self.collection.envelopes.clone().read();
  836. let envelopes = envelopes.unwrap();
  837. for env_hash in self.folders[folder_hash].as_result()?.envelopes {
  838. let envelope = &envelopes[&env_hash];
  839. if envelope.subject().contains(&search_term) {
  840. ret.push(env_hash);
  841. continue;
  842. }
  843. if envelope.field_from_to_string().contains(&search_term) {
  844. ret.push(env_hash);
  845. continue;
  846. }
  847. let op = self.operation(env_hash);
  848. let body = envelope.body(op)?;
  849. let decoded = decode_rec(&body, None);
  850. let body_text = String::from_utf8_lossy(&decoded);
  851. if body_text.contains(&search_term) {
  852. ret.push(env_hash);
  853. }
  854. }
  855. ret
  856. }
  857. }
  858. }
  859. impl Index<FolderHash> for Account {
  860. type Output = MailboxEntry;
  861. fn index(&self, index: FolderHash) -> &MailboxEntry {
  862. &self.folders[&index]
  863. }
  864. }
  865. impl IndexMut<FolderHash> for Account {
  866. fn index_mut(&mut self, index: FolderHash) -> &mut MailboxEntry {
  867. self.folders.get_mut(&index).unwrap()
  868. }
  869. }
  870. impl Index<usize> for Account {
  871. type Output = MailboxEntry;
  872. fn index(&self, index: usize) -> &MailboxEntry {
  873. &self.folders[&self.folders_order[index]]
  874. }
  875. }
  876. /// Will panic if mailbox hasn't loaded, ask `status()` first.
  877. impl IndexMut<usize> for Account {
  878. fn index_mut(&mut self, index: usize) -> &mut MailboxEntry {
  879. self.folders.get_mut(&self.folders_order[index]).unwrap()
  880. }
  881. }