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.

1607 lines
60 KiB

2 years ago
2 years ago
  1. /*
  2. * meli - imap module.
  3. *
  4. * Copyright 2019 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. use crate::get_path_hash;
  22. use smallvec::SmallVec;
  23. #[macro_use]
  24. mod protocol_parser;
  25. pub use protocol_parser::{UntaggedResponse::*, *};
  26. mod mailbox;
  27. pub use mailbox::*;
  28. mod operations;
  29. pub use operations::*;
  30. mod connection;
  31. pub use connection::*;
  32. mod watch;
  33. pub use watch::*;
  34. mod cache;
  35. pub mod managesieve;
  36. mod untagged;
  37. use crate::async_workers::{Async, WorkContext};
  38. use crate::backends::{
  39. RefreshEventKind::{self, *},
  40. *,
  41. };
  42. use crate::conf::AccountSettings;
  43. use crate::email::*;
  44. use crate::error::{MeliError, Result, ResultIntoMeliError};
  45. use futures::lock::Mutex as FutureMutex;
  46. use futures::stream::Stream;
  47. use std::collections::{hash_map::DefaultHasher, BTreeMap};
  48. use std::collections::{BTreeSet, HashMap, HashSet};
  49. use std::future::Future;
  50. use std::hash::Hasher;
  51. use std::pin::Pin;
  52. use std::str::FromStr;
  53. use std::sync::{Arc, Mutex, RwLock};
  54. use std::time::Instant;
  55. pub type UID = usize;
  56. pub static SUPPORTED_CAPABILITIES: &[&str] = &[
  57. "IDLE",
  58. "LOGIN",
  59. "LOGINDISABLED",
  60. "LIST-STATUS",
  61. #[cfg(feature = "deflate_compression")]
  62. "COMPRESS=DEFLATE",
  63. "ENABLE",
  64. "IMAP4REV1",
  65. "SPECIAL-USE",
  66. "UNSELECT",
  67. "LITERAL+",
  68. ];
  69. #[derive(Debug, Default)]
  70. pub struct EnvelopeCache {
  71. bytes: Option<String>,
  72. headers: Option<String>,
  73. body: Option<String>,
  74. flags: Option<Flag>,
  75. }
  76. #[derive(Debug, Clone)]
  77. pub struct ImapServerConf {
  78. pub server_hostname: String,
  79. pub server_username: String,
  80. pub server_password: String,
  81. pub server_port: u16,
  82. pub use_starttls: bool,
  83. pub use_tls: bool,
  84. pub danger_accept_invalid_certs: bool,
  85. pub protocol: ImapProtocol,
  86. }
  87. struct IsSubscribedFn(Box<dyn Fn(&str) -> bool + Send + Sync>);
  88. impl std::fmt::Debug for IsSubscribedFn {
  89. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  90. write!(f, "IsSubscribedFn Box")
  91. }
  92. }
  93. impl std::ops::Deref for IsSubscribedFn {
  94. type Target = Box<dyn Fn(&str) -> bool + Send + Sync>;
  95. fn deref(&self) -> &Box<dyn Fn(&str) -> bool + Send + Sync> {
  96. &self.0
  97. }
  98. }
  99. type Capabilities = HashSet<Vec<u8>>;
  100. #[macro_export]
  101. macro_rules! get_conf_val {
  102. ($s:ident[$var:literal]) => {
  103. $s.extra.get($var).ok_or_else(|| {
  104. MeliError::new(format!(
  105. "Configuration error ({}): IMAP connection requires the field `{}` set",
  106. $s.name.as_str(),
  107. $var
  108. ))
  109. })
  110. };
  111. ($s:ident[$var:literal], $default:expr) => {
  112. $s.extra
  113. .get($var)
  114. .map(|v| {
  115. <_>::from_str(v).map_err(|e| {
  116. MeliError::new(format!(
  117. "Configuration error ({}): Invalid value for field `{}`: {}\n{}",
  118. $s.name.as_str(),
  119. $var,
  120. v,
  121. e
  122. ))
  123. })
  124. })
  125. .unwrap_or_else(|| Ok($default))
  126. };
  127. }
  128. #[derive(Debug)]
  129. pub struct UIDStore {
  130. account_hash: AccountHash,
  131. cache_headers: bool,
  132. account_name: Arc<String>,
  133. capabilities: Arc<Mutex<Capabilities>>,
  134. uidvalidity: Arc<Mutex<HashMap<MailboxHash, UID>>>,
  135. hash_index: Arc<Mutex<HashMap<EnvelopeHash, (UID, MailboxHash)>>>,
  136. uid_index: Arc<Mutex<HashMap<(MailboxHash, UID), EnvelopeHash>>>,
  137. msn_index: Arc<Mutex<HashMap<MailboxHash, Vec<UID>>>>,
  138. byte_cache: Arc<Mutex<HashMap<UID, EnvelopeCache>>>,
  139. tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
  140. mailboxes: Arc<FutureMutex<HashMap<MailboxHash, ImapMailbox>>>,
  141. is_online: Arc<Mutex<(Instant, Result<()>)>>,
  142. refresh_events: Arc<Mutex<Vec<RefreshEvent>>>,
  143. sender: Arc<RwLock<Option<RefreshEventConsumer>>>,
  144. }
  145. impl Default for UIDStore {
  146. fn default() -> Self {
  147. UIDStore {
  148. account_hash: 0,
  149. cache_headers: false,
  150. account_name: Arc::new(String::new()),
  151. capabilities: Default::default(),
  152. uidvalidity: Default::default(),
  153. hash_index: Default::default(),
  154. uid_index: Default::default(),
  155. msn_index: Default::default(),
  156. byte_cache: Default::default(),
  157. mailboxes: Arc::new(FutureMutex::new(Default::default())),
  158. tag_index: Arc::new(RwLock::new(Default::default())),
  159. is_online: Arc::new(Mutex::new((
  160. Instant::now(),
  161. Err(MeliError::new("Account is uninitialised.")),
  162. ))),
  163. refresh_events: Default::default(),
  164. sender: Arc::new(RwLock::new(None)),
  165. }
  166. }
  167. }
  168. #[derive(Debug)]
  169. pub struct ImapType {
  170. is_subscribed: Arc<IsSubscribedFn>,
  171. connection: Arc<FutureMutex<ImapConnection>>,
  172. server_conf: ImapServerConf,
  173. uid_store: Arc<UIDStore>,
  174. can_create_flags: Arc<Mutex<bool>>,
  175. }
  176. impl MailBackend for ImapType {
  177. fn capabilities(&self) -> MailBackendCapabilities {
  178. const CAPABILITIES: MailBackendCapabilities = MailBackendCapabilities {
  179. is_async: true,
  180. is_remote: true,
  181. supports_search: true,
  182. supports_tags: true,
  183. };
  184. CAPABILITIES
  185. }
  186. fn fetch_async(
  187. &mut self,
  188. mailbox_hash: MailboxHash,
  189. ) -> Result<Pin<Box<dyn Stream<Item = Result<Vec<Envelope>>> + Send + 'static>>> {
  190. let uid_store = self.uid_store.clone();
  191. let can_create_flags = self.can_create_flags.clone();
  192. let connection = self.connection.clone();
  193. let mut max_uid: Option<usize> = None;
  194. let mut valid_hash_set: HashSet<EnvelopeHash> = HashSet::default();
  195. let mut our_unseen: BTreeSet<EnvelopeHash> = Default::default();
  196. Ok(Box::pin(async_stream::try_stream! {
  197. {
  198. let f = &uid_store.mailboxes.lock().await[&mailbox_hash];
  199. f.exists.lock().unwrap().clear();
  200. f.unseen.lock().unwrap().clear();
  201. };
  202. let (cached_hash_set, cached_payload) = fetch_cached_envs(mailbox_hash, &mut our_unseen, &uid_store)?;
  203. yield cached_payload;
  204. loop {
  205. let res = fetch_hlpr(&connection, mailbox_hash, &cached_hash_set, &can_create_flags, &mut our_unseen, &mut valid_hash_set, &uid_store, &mut max_uid).await?;
  206. yield res;
  207. if max_uid == Some(1) || max_uid == Some(0) {
  208. return;
  209. }
  210. }
  211. }))
  212. }
  213. fn refresh_async(
  214. &mut self,
  215. mailbox_hash: MailboxHash,
  216. sender: RefreshEventConsumer,
  217. ) -> ResultFuture<()> {
  218. let main_conn = self.connection.clone();
  219. *self.uid_store.sender.write().unwrap() = Some(sender);
  220. let uid_store = self.uid_store.clone();
  221. Ok(Box::pin(async move {
  222. let inbox = uid_store
  223. .mailboxes
  224. .lock()
  225. .await
  226. .get(&mailbox_hash)
  227. .map(std::clone::Clone::clone)
  228. .unwrap();
  229. let mut conn = main_conn.lock().await;
  230. watch::examine_updates(inbox, &mut conn, &uid_store).await?;
  231. Ok(())
  232. }))
  233. }
  234. fn mailboxes_async(&self) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
  235. let uid_store = self.uid_store.clone();
  236. let connection = self.connection.clone();
  237. Ok(Box::pin(async move {
  238. {
  239. let mailboxes = uid_store.mailboxes.lock().await;
  240. if !mailboxes.is_empty() {
  241. return Ok(mailboxes
  242. .iter()
  243. .map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Mailbox))
  244. .collect());
  245. }
  246. }
  247. let new_mailboxes = ImapType::imap_mailboxes(&connection).await?;
  248. let mut mailboxes = uid_store.mailboxes.lock().await;
  249. *mailboxes = new_mailboxes;
  250. /*
  251. let mut invalid_configs = vec![];
  252. for m in mailboxes.values() {
  253. if m.is_subscribed() != (self.is_subscribed)(m.path()) {
  254. invalid_configs.push((m.path(), m.is_subscribed()));
  255. }
  256. }
  257. if !invalid_configs.is_empty() {
  258. let mut err_string = format!("{}: ", self.account_name);
  259. for (m, server_value) in invalid_configs.iter() {
  260. err_string.extend(format!(
  261. "Mailbox `{}` is {}subscribed on server but {}subscribed in your configuration. These settings have to match.\n",
  262. if *server_value { "" } else { "not " },
  263. if *server_value { "not " } else { "" },
  264. m
  265. ).chars());
  266. }
  267. return Err(MeliError::new(err_string));
  268. }
  269. mailboxes.retain(|_, f| (self.is_subscribed)(f.path()));
  270. */
  271. let keys = mailboxes.keys().cloned().collect::<HashSet<MailboxHash>>();
  272. let mut uid_lock = uid_store.uidvalidity.lock().unwrap();
  273. for f in mailboxes.values_mut() {
  274. uid_lock.entry(f.hash()).or_default();
  275. f.children.retain(|c| keys.contains(c));
  276. }
  277. drop(uid_lock);
  278. Ok(mailboxes
  279. .iter()
  280. .filter(|(_, f)| f.is_subscribed)
  281. .map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Mailbox))
  282. .collect())
  283. }))
  284. }
  285. fn is_online_async(&self) -> ResultFuture<()> {
  286. let connection = self.connection.clone();
  287. Ok(Box::pin(async move {
  288. let mut conn = connection.lock().await;
  289. conn.connect().await?;
  290. Ok(())
  291. }))
  292. }
  293. fn fetch(&mut self, _mailbox_hash: MailboxHash) -> Result<Async<Result<Vec<Envelope>>>> {
  294. Err(MeliError::new("Unimplemented."))
  295. }
  296. fn refresh(
  297. &mut self,
  298. _mailbox_hash: MailboxHash,
  299. _sender: RefreshEventConsumer,
  300. ) -> Result<Async<()>> {
  301. Err(MeliError::new("Unimplemented."))
  302. }
  303. fn watch(
  304. &self,
  305. _sender: RefreshEventConsumer,
  306. _work_context: WorkContext,
  307. ) -> Result<std::thread::ThreadId> {
  308. Err(MeliError::new("Unimplemented."))
  309. }
  310. fn watch_async(&self, sender: RefreshEventConsumer) -> ResultFuture<()> {
  311. debug!("watch_async called");
  312. let conn = ImapConnection::new_connection(&self.server_conf, self.uid_store.clone());
  313. let main_conn = self.connection.clone();
  314. *self.uid_store.sender.write().unwrap() = Some(sender);
  315. let uid_store = self.uid_store.clone();
  316. let has_idle: bool = match self.server_conf.protocol {
  317. ImapProtocol::IMAP {
  318. extension_use: ImapExtensionUse { idle, .. },
  319. } => {
  320. idle && uid_store
  321. .capabilities
  322. .lock()
  323. .unwrap()
  324. .iter()
  325. .any(|cap| cap.eq_ignore_ascii_case(b"IDLE"))
  326. }
  327. _ => false,
  328. };
  329. Ok(Box::pin(async move {
  330. debug!(has_idle);
  331. let kit = ImapWatchKit {
  332. conn,
  333. main_conn,
  334. uid_store,
  335. };
  336. if has_idle {
  337. idle(kit).await?;
  338. } else {
  339. poll_with_examine(kit).await?;
  340. }
  341. debug!("watch_async future returning");
  342. Ok(())
  343. }))
  344. }
  345. fn mailboxes(&self) -> Result<HashMap<MailboxHash, Mailbox>> {
  346. Err(MeliError::new("Unimplemented."))
  347. }
  348. fn operation(&self, hash: EnvelopeHash) -> Result<Box<dyn BackendOp>> {
  349. let (uid, mailbox_hash) = if let Some(v) =
  350. self.uid_store.hash_index.lock().unwrap().get(&hash)
  351. {
  352. *v
  353. } else {
  354. return Err(MeliError::new(
  355. "Message not found in local cache, it might have been deleted before you requested it."
  356. ));
  357. };
  358. Ok(Box::new(ImapOp::new(
  359. uid,
  360. mailbox_hash,
  361. self.connection.clone(),
  362. self.uid_store.clone(),
  363. )))
  364. }
  365. fn save(
  366. &self,
  367. bytes: Vec<u8>,
  368. mailbox_hash: MailboxHash,
  369. flags: Option<Flag>,
  370. ) -> ResultFuture<()> {
  371. let uid_store = self.uid_store.clone();
  372. let connection = self.connection.clone();
  373. Ok(Box::pin(async move {
  374. let path = {
  375. let mailboxes = uid_store.mailboxes.lock().await;
  376. let mailbox = mailboxes.get(&mailbox_hash).ok_or_else(|| {
  377. MeliError::new(format!("Mailbox with hash {} not found.", mailbox_hash))
  378. })?;
  379. if !mailbox.permissions.lock().unwrap().create_messages {
  380. return Err(MeliError::new(format!(
  381. "You are not allowed to create messages in mailbox {}",
  382. mailbox.path()
  383. )));
  384. }
  385. mailbox.imap_path().to_string()
  386. };
  387. let mut response = String::with_capacity(8 * 1024);
  388. let mut conn = connection.lock().await;
  389. let flags = flags.unwrap_or_else(Flag::empty);
  390. let has_literal_plus: bool = uid_store
  391. .capabilities
  392. .lock()
  393. .unwrap()
  394. .iter()
  395. .any(|cap| cap.eq_ignore_ascii_case(b"LITERAL+"));
  396. if has_literal_plus {
  397. conn.send_command(
  398. format!(
  399. "APPEND \"{}\" ({}) {{{}+}}",
  400. &path,
  401. flags_to_imap_list!(flags),
  402. bytes.len()
  403. )
  404. .as_bytes(),
  405. )
  406. .await?;
  407. } else {
  408. conn.send_command(
  409. format!(
  410. "APPEND \"{}\" ({}) {{{}}}",
  411. &path,
  412. flags_to_imap_list!(flags),
  413. bytes.len()
  414. )
  415. .as_bytes(),
  416. )
  417. .await?;
  418. // wait for "+ Ready for literal data" reply
  419. conn.wait_for_continuation_request().await?;
  420. }
  421. conn.send_literal(&bytes).await?;
  422. conn.read_response(&mut response, RequiredResponses::empty())
  423. .await?;
  424. Ok(())
  425. }))
  426. }
  427. fn copy_messages(
  428. &mut self,
  429. env_hashes: EnvelopeHashBatch,
  430. source_mailbox_hash: MailboxHash,
  431. destination_mailbox_hash: MailboxHash,
  432. move_: bool,
  433. destination_flags: Option<Flag>,
  434. ) -> ResultFuture<()> {
  435. let uid_store = self.uid_store.clone();
  436. let connection = self.connection.clone();
  437. Ok(Box::pin(async move {
  438. let dest_path = {
  439. let mailboxes = uid_store.mailboxes.lock().await;
  440. let mailbox = mailboxes
  441. .get(&source_mailbox_hash)
  442. .ok_or_else(|| MeliError::new("Source mailbox not found"))?;
  443. if move_ && !mailbox.permissions.lock().unwrap().delete_messages {
  444. return Err(MeliError::new(format!(
  445. "You are not allowed to delete messages from mailbox {}",
  446. mailbox.path()
  447. )));
  448. }
  449. let mailbox = mailboxes
  450. .get(&destination_mailbox_hash)
  451. .ok_or_else(|| MeliError::new("Destination mailbox not found"))?;
  452. if !mailbox.permissions.lock().unwrap().create_messages {
  453. return Err(MeliError::new(format!(
  454. "You are not allowed to delete messages from mailbox {}",
  455. mailbox.path()
  456. )));
  457. }
  458. mailbox.imap_path().to_string()
  459. };
  460. let mut response = String::with_capacity(8 * 1024);
  461. let mut conn = connection.lock().await;
  462. conn.select_mailbox(source_mailbox_hash, &mut response, false)
  463. .await?;
  464. let command = {
  465. let hash_index_lck = uid_store.hash_index.lock().unwrap();
  466. let mut cmd = format!("UID COPY {}", hash_index_lck[&env_hashes.first].0);
  467. for env_hash in &env_hashes.rest {
  468. cmd = format!("{},{}", cmd, hash_index_lck[env_hash].0);
  469. }
  470. format!("{} \"{}\"", cmd, dest_path)
  471. };
  472. conn.send_command(command.as_bytes()).await?;
  473. conn.read_response(&mut response, RequiredResponses::empty())
  474. .await?;
  475. if let Some(_flags) = destination_flags {
  476. //FIXME
  477. }
  478. if move_ {
  479. let command = {
  480. let hash_index_lck = uid_store.hash_index.lock().unwrap();
  481. let mut cmd = format!("UID STORE {}", hash_index_lck[&env_hashes.first].0);
  482. for env_hash in env_hashes.rest {
  483. cmd = format!("{},{}", cmd, hash_index_lck[&env_hash].0);
  484. }
  485. format!("{} +FLAGS (\\Deleted)", cmd)
  486. };
  487. conn.send_command(command.as_bytes()).await?;
  488. conn.read_response(&mut response, RequiredResponses::empty())
  489. .await?;
  490. }
  491. Ok(())
  492. }))
  493. }
  494. fn set_flags(
  495. &mut self,
  496. env_hashes: EnvelopeHashBatch,
  497. mailbox_hash: MailboxHash,
  498. flags: SmallVec<[(std::result::Result<Flag, String>, bool); 8]>,
  499. ) -> ResultFuture<()> {
  500. let connection = self.connection.clone();
  501. let uid_store = self.uid_store.clone();
  502. Ok(Box::pin(async move {
  503. let mut response = String::with_capacity(8 * 1024);
  504. let mut conn = connection.lock().await;
  505. conn.select_mailbox(mailbox_hash, &mut response, false)
  506. .await?;
  507. if flags.iter().any(|(_, b)| *b) {
  508. /* Set flags/tags to true */
  509. let command = {
  510. let hash_index_lck = uid_store.hash_index.lock().unwrap();
  511. let mut cmd = format!("UID STORE {}", hash_index_lck[&env_hashes.first].0);
  512. for env_hash in &env_hashes.rest {
  513. cmd = format!("{},{}", cmd, hash_index_lck[env_hash].0);
  514. }
  515. cmd = format!("{} +FLAGS (", cmd);
  516. for (f, v) in flags.iter() {
  517. if !*v {
  518. continue;
  519. }
  520. match f {
  521. Ok(flag) if *flag == Flag::REPLIED => {
  522. cmd.push_str("\\Answered ");
  523. }
  524. Ok(flag) if *flag == Flag::FLAGGED => {
  525. cmd.push_str("\\Flagged ");
  526. }
  527. Ok(flag) if *flag == Flag::TRASHED => {
  528. cmd.push_str("\\Deleted ");
  529. }
  530. Ok(flag) if *flag == Flag::SEEN => {
  531. cmd.push_str("\\Seen ");
  532. }
  533. Ok(flag) if *flag == Flag::DRAFT => {
  534. cmd.push_str("\\Draft ");
  535. }
  536. Ok(_) => {
  537. crate::log(
  538. format!(
  539. "Application error: more than one flag bit set in set_flags: {:?}", flags
  540. ),
  541. crate::ERROR,
  542. );
  543. return Err(MeliError::new(format!(
  544. "Application error: more than one flag bit set in set_flags: {:?}", flags
  545. )));
  546. }
  547. Err(tag) => {
  548. cmd.push_str(tag);
  549. cmd.push(' ');
  550. }
  551. }
  552. }
  553. // pop last space
  554. cmd.pop();
  555. cmd.push(')');
  556. cmd
  557. };
  558. conn.send_command(command.as_bytes()).await?;
  559. conn.read_response(&mut response, RequiredResponses::empty())
  560. .await?;
  561. }
  562. if flags.iter().any(|(_, b)| !*b) {
  563. /* Set flags/tags to false */
  564. let command = {
  565. let hash_index_lck = uid_store.hash_index.lock().unwrap();
  566. let mut cmd = format!("UID STORE {}", hash_index_lck[&env_hashes.first].0);
  567. for env_hash in &env_hashes.rest {
  568. cmd = format!("{},{}", cmd, hash_index_lck[env_hash].0);
  569. }
  570. cmd = format!("{} -FLAGS (", cmd);
  571. for (f, v) in flags.iter() {
  572. if *v {
  573. continue;
  574. }
  575. match f {
  576. Ok(flag) if *flag == Flag::REPLIED => {
  577. cmd.push_str("\\Answered ");
  578. }
  579. Ok(flag) if *flag == Flag::FLAGGED => {
  580. cmd.push_str("\\Flagged ");
  581. }
  582. Ok(flag) if *flag == Flag::TRASHED => {
  583. cmd.push_str("\\Deleted ");
  584. }
  585. Ok(flag) if *flag == Flag::SEEN => {
  586. cmd.push_str("\\Seen ");
  587. }
  588. Ok(flag) if *flag == Flag::DRAFT => {
  589. cmd.push_str("\\Draft ");
  590. }
  591. Ok(_) => {
  592. crate::log(
  593. format!(
  594. "Application error: more than one flag bit set in set_flags: {:?}", flags
  595. ),
  596. crate::ERROR,
  597. );
  598. return Err(MeliError::new(format!(
  599. "Application error: more than one flag bit set in set_flags: {:?}", flags
  600. )));
  601. }
  602. Err(tag) => {
  603. cmd.push_str(tag);
  604. cmd.push(' ');
  605. }
  606. }
  607. }
  608. // pop last space
  609. cmd.pop();
  610. cmd.push(')');
  611. cmd
  612. };
  613. conn.send_command(command.as_bytes()).await?;
  614. conn.read_response(&mut response, RequiredResponses::empty())
  615. .await?;
  616. }
  617. Ok(())
  618. }))
  619. }
  620. fn as_any(&self) -> &dyn ::std::any::Any {
  621. self
  622. }
  623. fn as_any_mut(&mut self) -> &mut dyn ::std::any::Any {
  624. self
  625. }
  626. fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
  627. if *self.can_create_flags.lock().unwrap() {
  628. Some(self.uid_store.tag_index.clone())
  629. } else {
  630. None
  631. }
  632. }
  633. fn create_mailbox(
  634. &mut self,
  635. mut path: String,
  636. ) -> ResultFuture<(MailboxHash, HashMap<MailboxHash, Mailbox>)> {
  637. let uid_store = self.uid_store.clone();
  638. let connection = self.connection.clone();
  639. let new_mailbox_fut = self.mailboxes_async();
  640. Ok(Box::pin(async move {
  641. /* Must transform path to something the IMAP server will accept
  642. *
  643. * Each root mailbox has a hierarchy delimeter reported by the LIST entry. All paths
  644. * must use this delimeter to indicate children of this mailbox.
  645. *
  646. * A new root mailbox should have the default delimeter, which can be found out by issuing
  647. * an empty LIST command as described in RFC3501:
  648. * C: A101 LIST "" ""
  649. * S: * LIST (\Noselect) "/" ""
  650. *
  651. * The default delimiter for us is '/' just like UNIX paths. I apologise if this
  652. * decision is unpleasant for you.
  653. */
  654. {
  655. let mailboxes = uid_store.mailboxes.lock().await;
  656. if mailboxes.values().any(|f| f.path == path) {
  657. return Err(MeliError::new(format!(
  658. "Mailbox named `{}` already exists.",
  659. path,
  660. )));
  661. }
  662. for root_mailbox in mailboxes.values().filter(|f| f.parent.is_none()) {
  663. if path.starts_with(&root_mailbox.name) {
  664. debug!("path starts with {:?}", &root_mailbox);
  665. path = path.replace(
  666. '/',
  667. (root_mailbox.separator as char).encode_utf8(&mut [0; 4]),
  668. );
  669. break;
  670. }
  671. }
  672. /* FIXME Do not try to CREATE a sub-mailbox in a mailbox that has the \Noinferiors
  673. * flag set. */
  674. }
  675. let mut response = String::with_capacity(8 * 1024);
  676. {
  677. let mut conn_lck = connection.lock().await;
  678. conn_lck
  679. .send_command(format!("CREATE \"{}\"", path,).as_bytes())
  680. .await?;
  681. conn_lck
  682. .read_response(&mut response, RequiredResponses::empty())
  683. .await?;
  684. conn_lck
  685. .send_command(format!("SUBSCRIBE \"{}\"", path,).as_bytes())
  686. .await?;
  687. conn_lck
  688. .read_response(&mut response, RequiredResponses::empty())
  689. .await?;
  690. }
  691. let ret: Result<()> = ImapResponse::from(&response).into();
  692. ret?;
  693. let new_hash = get_path_hash!(path.as_str());
  694. uid_store.mailboxes.lock().await.clear();
  695. Ok((new_hash, new_mailbox_fut?.await.map_err(|err| MeliError::new(format!("Mailbox create was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err)))?))
  696. }))
  697. }
  698. fn delete_mailbox(
  699. &mut self,
  700. mailbox_hash: MailboxHash,
  701. ) -> ResultFuture<HashMap<MailboxHash, Mailbox>> {
  702. let uid_store = self.uid_store.clone();
  703. let connection = self.connection.clone();
  704. let new_mailbox_fut = self.mailboxes_async();
  705. Ok(Box::pin(async move {
  706. let imap_path: String;
  707. let no_select: bool;
  708. let is_subscribed: bool;
  709. {
  710. let mailboxes = uid_store.mailboxes.lock().await;
  711. no_select = mailboxes[&mailbox_hash].no_select;
  712. is_subscribed = mailboxes[&mailbox_hash].is_subscribed();
  713. imap_path = mailboxes[&mailbox_hash].imap_path().to_string();
  714. let permissions = mailboxes[&mailbox_hash].permissions();
  715. if !permissions.delete_mailbox {
  716. return Err(MeliError::new(format!("You do not have permission to delete `{}`. Set permissions for this mailbox are {}", mailboxes[&mailbox_hash].name(), permissions)));
  717. }
  718. }
  719. let mut response = String::with_capacity(8 * 1024);
  720. {
  721. let mut conn_lck = connection.lock().await;
  722. let current_mailbox = conn_lck.stream.as_ref()?.current_mailbox;
  723. if !no_select
  724. && (current_mailbox == MailboxSelection::Examine(mailbox_hash)
  725. || current_mailbox == MailboxSelection::Select(mailbox_hash))
  726. {
  727. /* make sure mailbox is not selected before it gets deleted, otherwise
  728. * connection gets dropped by server */
  729. conn_lck.unselect().await?;
  730. }
  731. if is_subscribed {
  732. conn_lck
  733. .send_command(format!("UNSUBSCRIBE \"{}\"", &imap_path).as_bytes())
  734. .await?;
  735. conn_lck
  736. .read_response(&mut response, RequiredResponses::empty())
  737. .await?;
  738. }
  739. conn_lck
  740. .send_command(debug!(format!("DELETE \"{}\"", &imap_path,)).as_bytes())
  741. .await?;
  742. conn_lck
  743. .read_response(&mut response, RequiredResponses::empty())
  744. .await?;
  745. }
  746. let ret: Result<()> = ImapResponse::from(&response).into();
  747. ret?;
  748. uid_store.mailboxes.lock().await.clear();
  749. new_mailbox_fut?.await.map_err(|err| format!("Mailbox delete was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err).into())
  750. }))
  751. }
  752. fn set_mailbox_subscription(
  753. &mut self,
  754. mailbox_hash: MailboxHash,
  755. new_val: bool,
  756. ) -> ResultFuture<()> {
  757. let uid_store = self.uid_store.clone();
  758. let connection = self.connection.clone();
  759. Ok(Box::pin(async move {
  760. let command: String;
  761. {
  762. let mailboxes = uid_store.mailboxes.lock().await;
  763. if mailboxes[&mailbox_hash].is_subscribed() == new_val {
  764. return Ok(());
  765. }
  766. command = format!("SUBSCRIBE \"{}\"", mailboxes[&mailbox_hash].imap_path());
  767. }
  768. let mut response = String::with_capacity(8 * 1024);
  769. {
  770. let mut conn_lck = connection.lock().await;
  771. if new_val {
  772. conn_lck.send_command(command.as_bytes()).await?;
  773. } else {
  774. conn_lck
  775. .send_command(format!("UN{}", command).as_bytes())
  776. .await?;
  777. }
  778. conn_lck
  779. .read_response(&mut response, RequiredResponses::empty())
  780. .await?;
  781. }
  782. let ret: Result<()> = ImapResponse::from(&response).into();
  783. if ret.is_ok() {
  784. uid_store
  785. .mailboxes
  786. .lock()
  787. .await
  788. .entry(mailbox_hash)
  789. .and_modify(|entry| {
  790. let _ = entry.set_is_subscribed(new_val);
  791. });
  792. }
  793. ret
  794. }))
  795. }
  796. fn rename_mailbox(
  797. &mut self,
  798. mailbox_hash: MailboxHash,
  799. mut new_path: String,
  800. ) -> ResultFuture<Mailbox> {
  801. let uid_store = self.uid_store.clone();
  802. let connection = self.connection.clone();
  803. let new_mailbox_fut = self.mailboxes_async();
  804. Ok(Box::pin(async move {
  805. let command: String;
  806. let mut response = String::with_capacity(8 * 1024);
  807. {
  808. let mailboxes = uid_store.mailboxes.lock().await;
  809. let permissions = mailboxes[&mailbox_hash].permissions();
  810. if !permissions.delete_mailbox {
  811. return Err(MeliError::new(format!("You do not have permission to rename mailbox `{}` (rename is equivalent to delete + create). Set permissions for this mailbox are {}", mailboxes[&mailbox_hash].name(), permissions)));
  812. }
  813. if mailboxes[&mailbox_hash].separator != b'/' {
  814. new_path = new_path.replace(
  815. '/',
  816. (mailboxes[&mailbox_hash].separator as char).encode_utf8(&mut [0; 4]),
  817. );
  818. }
  819. command = format!(
  820. "RENAME \"{}\" \"{}\"",
  821. mailboxes[&mailbox_hash].imap_path(),
  822. new_path
  823. );
  824. }
  825. {
  826. let mut conn_lck = connection.lock().await;
  827. conn_lck.send_command(debug!(command).as_bytes()).await?;
  828. conn_lck
  829. .read_response(&mut response, RequiredResponses::empty())
  830. .await?;
  831. }
  832. let new_hash = get_path_hash!(new_path.as_str());
  833. let ret: Result<()> = ImapResponse::from(&response).into();
  834. ret?;
  835. uid_store.mailboxes.lock().await.clear();
  836. new_mailbox_fut?.await.map_err(|err| format!("Mailbox rename was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err))?;
  837. Ok(BackendMailbox::clone(
  838. &uid_store.mailboxes.lock().await[&new_hash],
  839. ))
  840. }))
  841. }
  842. fn set_mailbox_permissions(
  843. &mut self,
  844. mailbox_hash: MailboxHash,
  845. _val: crate::backends::MailboxPermissions,
  846. ) -> ResultFuture<()> {
  847. let uid_store = self.uid_store.clone();
  848. //let connection = self.connection.clone();
  849. Ok(Box::pin(async move {
  850. let mailboxes = uid_store.mailboxes.lock().await;
  851. let permissions = mailboxes[&mailbox_hash].permissions();
  852. if !permissions.change_permissions {
  853. return Err(MeliError::new(format!("You do not have permission to change permissions for mailbox `{}`. Set permissions for this mailbox are {}", mailboxes[&mailbox_hash].name(), permissions)));
  854. }
  855. Err(MeliError::new("Unimplemented."))
  856. }))
  857. }
  858. fn search(
  859. &self,
  860. query: crate::search::Query,
  861. mailbox_hash: Option<MailboxHash>,
  862. ) -> ResultFuture<SmallVec<[EnvelopeHash; 512]>> {
  863. if mailbox_hash.is_none() {
  864. return Err(MeliError::new(
  865. "Cannot search without specifying mailbox on IMAP",
  866. ));
  867. }
  868. let mailbox_hash = mailbox_hash.unwrap();
  869. fn rec(q: &crate::search::Query, s: &mut String) {
  870. use crate::search::{escape_double_quote, Query::*};
  871. match q {
  872. Subject(t) => {
  873. s.push_str(" SUBJECT \"");
  874. s.extend(escape_double_quote(t).chars());
  875. s.push_str("\"");
  876. }
  877. From(t) => {
  878. s.push_str(" FROM \"");
  879. s.extend(escape_double_quote(t).chars());
  880. s.push_str("\"");
  881. }
  882. To(t) => {
  883. s.push_str(" TO \"");
  884. s.extend(escape_double_quote(t).chars());
  885. s.push_str("\"");
  886. }
  887. Cc(t) => {
  888. s.push_str(" CC \"");
  889. s.extend(escape_double_quote(t).chars());
  890. s.push_str("\"");
  891. }
  892. Bcc(t) => {
  893. s.push_str(" BCC \"");
  894. s.extend(escape_double_quote(t).chars());
  895. s.push_str("\"");
  896. }
  897. AllText(t) => {
  898. s.push_str(" TEXT \"");
  899. s.extend(escape_double_quote(t).chars());
  900. s.push_str("\"");
  901. }
  902. Flags(v) => {
  903. for f in v {
  904. match f.as_str() {
  905. "draft" => {
  906. s.push_str(" DRAFT ");
  907. }
  908. "deleted" => {
  909. s.push_str(" DELETED ");
  910. }
  911. "flagged" => {
  912. s.push_str(" FLAGGED ");
  913. }
  914. "recent" => {
  915. s.push_str(" RECENT ");
  916. }
  917. "seen" | "read" => {
  918. s.push_str(" SEEN ");
  919. }
  920. "unseen" | "unread" => {
  921. s.push_str(" UNSEEN ");
  922. }
  923. "answered" => {
  924. s.push_str(" ANSWERED ");
  925. }
  926. "unanswered" => {
  927. s.push_str(" UNANSWERED ");
  928. }
  929. keyword => {
  930. s.push_str(" KEYWORD ");
  931. s.push_str(keyword);
  932. s.push_str(" ");
  933. }
  934. }
  935. }
  936. }
  937. And(q1, q2) => {
  938. rec(q1, s);
  939. s.push_str(" ");
  940. rec(q2, s);
  941. }
  942. Or(q1, q2) => {
  943. s.push_str(" OR ");
  944. rec(q1, s);
  945. s.push_str(" ");
  946. rec(q2, s);
  947. }
  948. Not(q) => {
  949. s.push_str(" NOT ");
  950. rec(q, s);
  951. }
  952. _ => {}
  953. }
  954. }
  955. let mut query_str = String::new();
  956. rec(&query, &mut query_str);
  957. let connection = self.connection.clone();
  958. let uid_store = self.uid_store.clone();
  959. Ok(Box::pin(async move {
  960. let mut response = String::with_capacity(8 * 1024);
  961. let mut conn = connection.lock().await;
  962. conn.examine_mailbox(mailbox_hash, &mut response, false)
  963. .await?;
  964. conn.send_command(format!("UID SEARCH CHARSET UTF-8 {}", query_str).as_bytes())
  965. .await?;
  966. conn.read_response(&mut response, RequiredResponses::SEARCH)
  967. .await?;
  968. debug!(&response);
  969. let mut lines = response.lines();
  970. for l in lines.by_ref() {
  971. if l.starts_with("* SEARCH") {
  972. use std::iter::FromIterator;
  973. let uid_index = uid_store.uid_index.lock()?;
  974. return Ok(SmallVec::from_iter(
  975. l["* SEARCH".len()..]
  976. .trim()
  977. .split_whitespace()
  978. .map(usize::from_str)
  979. .filter_map(std::result::Result::ok)
  980. .filter_map(|uid| uid_index.get(&(mailbox_hash, uid)))
  981. .copied(),
  982. ));
  983. }
  984. }
  985. Err(MeliError::new(response))
  986. }))
  987. }
  988. }
  989. impl ImapType {
  990. pub fn new(
  991. s: &AccountSettings,
  992. is_subscribed: Box<dyn Fn(&str) -> bool + Send + Sync>,
  993. ) -> Result<Box<dyn MailBackend>> {
  994. let server_hostname = get_conf_val!(s["server_hostname"])?;
  995. let server_username = get_conf_val!(s["server_username"])?;
  996. let server_password = if !s.extra.contains_key("server_password_command") {
  997. get_conf_val!(s["server_password"])?.to_string()
  998. } else {
  999. let invocation = get_conf_val!(s["server_password_command"])?;
  1000. let output = std::process::Command::new("sh")
  1001. .args(&["-c", invocation])
  1002. .stdin(std::process::Stdio::piped())
  1003. .stdout(std::process::Stdio::piped())
  1004. .stderr(std::process::Stdio::piped())
  1005. .output()?;
  1006. if !output.status.success() {
  1007. return Err(MeliError::new(format!(
  1008. "({}) server_password_command `{}` returned {}: {}",
  1009. s.name,
  1010. get_conf_val!(s["server_password_command"])?,
  1011. output.status,
  1012. String::from_utf8_lossy(&output.stderr)
  1013. )));
  1014. }
  1015. std::str::from_utf8(&output.stdout)?.trim_end().to_string()
  1016. };
  1017. let server_port = get_conf_val!(s["server_port"], 143)?;
  1018. let use_tls = get_conf_val!(s["use_tls"], true)?;
  1019. let use_starttls = use_tls && get_conf_val!(s["use_starttls"], !(server_port == 993))?;
  1020. let danger_accept_invalid_certs: bool =
  1021. get_conf_val!(s["danger_accept_invalid_certs"], false)?;
  1022. let server_conf = ImapServerConf {
  1023. server_hostname: server_hostname.to_string(),
  1024. server_username: server_username.to_string(),
  1025. server_password,
  1026. server_port,
  1027. use_tls,
  1028. use_starttls,
  1029. danger_accept_invalid_certs,
  1030. protocol: ImapProtocol::IMAP {
  1031. extension_use: ImapExtensionUse {
  1032. idle: get_conf_val!(s["use_idle"], true)?,
  1033. #[cfg(feature = "deflate_compression")]
  1034. deflate: get_conf_val!(s["use_deflate"], true)?,
  1035. },
  1036. },
  1037. };
  1038. let account_hash = {
  1039. let mut hasher = DefaultHasher::new();
  1040. hasher.write(s.name.as_bytes());
  1041. hasher.finish()
  1042. };
  1043. let account_name = Arc::new(s.name().to_string());
  1044. let uid_store: Arc<UIDStore> = Arc::new(UIDStore {
  1045. account_hash,
  1046. cache_headers: get_conf_val!(s["X_header_caching"], false)?,
  1047. account_name,
  1048. ..UIDStore::default()
  1049. });
  1050. let connection = ImapConnection::new_connection(&server_conf, uid_store.clone());
  1051. Ok(Box::new(ImapType {
  1052. server_conf,
  1053. is_subscribed: Arc::new(IsSubscribedFn(is_subscribed)),
  1054. can_create_flags: Arc::new(Mutex::new(false)),
  1055. connection: Arc::new(FutureMutex::new(connection)),
  1056. uid_store,
  1057. }))
  1058. }
  1059. pub fn shell(&mut self) {
  1060. unimplemented!()
  1061. /*
  1062. let mut conn = ImapConnection::new_connection(&self.server_conf, self.uid_store.clone());
  1063. conn.connect().unwrap();
  1064. let mut res = String::with_capacity(8 * 1024);
  1065. conn.send_command(b"NOOP").unwrap();
  1066. conn.read_response(&mut res, RequiredResponses::empty())
  1067. .unwrap();
  1068. let mut input = String::new();
  1069. loop {
  1070. use std::io;
  1071. input.clear();
  1072. match io::stdin().read_line(&mut input) {
  1073. Ok(_) => {
  1074. if input.trim().eq_ignore_ascii_case("logout") {
  1075. break;
  1076. }
  1077. conn.send_command(input.as_bytes()).unwrap();
  1078. conn.read_lines(&mut res, String::new()).unwrap();
  1079. if input.trim() == "IDLE" {
  1080. let mut iter = ImapBlockingConnection::from(conn);
  1081. while let Some(line) = iter.next() {
  1082. debug!("out: {}", unsafe { std::str::from_utf8_unchecked(&line) });
  1083. }
  1084. conn = iter.into_conn();
  1085. }
  1086. debug!("out: {}", &res);
  1087. }
  1088. Err(error) => debug!("error: {}", error),
  1089. }
  1090. }
  1091. */
  1092. }
  1093. pub async fn imap_mailboxes(
  1094. connection: &Arc<FutureMutex<ImapConnection>>,
  1095. ) -> Result<HashMap<MailboxHash, ImapMailbox>> {
  1096. let mut mailboxes: HashMap<MailboxHash, ImapMailbox> = Default::default();
  1097. let mut res = String::with_capacity(8 * 1024);
  1098. let mut conn = connection.lock().await;
  1099. let has_list_status: bool = conn
  1100. .uid_store
  1101. .capabilities
  1102. .lock()
  1103. .unwrap()
  1104. .iter()
  1105. .any(|cap| cap.eq_ignore_ascii_case(b"LIST-STATUS"));
  1106. if has_list_status {
  1107. conn.send_command(b"LIST \"\" \"*\" RETURN (STATUS (MESSAGES UNSEEN))")
  1108. .await?;
  1109. conn.read_response(
  1110. &mut res,
  1111. RequiredResponses::LIST_REQUIRED | RequiredResponses::STATUS,
  1112. )
  1113. .await?;
  1114. } else {
  1115. conn.send_command(b"LIST \"\" \"*\"").await?;
  1116. conn.read_response(&mut res, RequiredResponses::LIST_REQUIRED)
  1117. .await?;
  1118. }
  1119. debug!("out: {}", &res);
  1120. let mut lines = res.split_rn();
  1121. /* Remove "M__ OK .." line */
  1122. lines.next_back();
  1123. for l in lines {
  1124. if let Ok(mut mailbox) =
  1125. protocol_parser::list_mailbox_result(l.as_bytes()).map(|(_, v)| v)
  1126. {
  1127. if let Some(parent) = mailbox.parent {
  1128. if mailboxes.contains_key(&parent) {
  1129. mailboxes
  1130. .entry(parent)
  1131. .and_modify(|e| e.children.push(mailbox.hash));
  1132. } else {
  1133. /* Insert dummy parent entry, populating only the children field. Later
  1134. * when we encounter the parent entry we will swap its children with
  1135. * dummy's */
  1136. mailboxes.insert(
  1137. parent,
  1138. ImapMailbox {
  1139. children: vec![mailbox.hash],
  1140. ..ImapMailbox::default()
  1141. },
  1142. );
  1143. }
  1144. }
  1145. if mailboxes.contains_key(&mailbox.hash) {
  1146. let entry = mailboxes.entry(mailbox.hash).or_default();
  1147. std::mem::swap(&mut entry.children, &mut mailbox.children);
  1148. *entry = mailbox;
  1149. } else {
  1150. mailboxes.insert(mailbox.hash, mailbox);
  1151. }
  1152. } else if let Ok(status) =
  1153. protocol_parser::status_response(l.as_bytes()).map(|(_, v)| v)
  1154. {
  1155. if let Some(mailbox_hash) = status.mailbox {
  1156. if mailboxes.contains_key(&mailbox_hash) {
  1157. let entry = mailboxes.entry(mailbox_hash).or_default();
  1158. if let Some(total) = status.messages {
  1159. entry.exists.lock().unwrap().set_not_yet_seen(total);
  1160. }
  1161. if let Some(total) = status.unseen {
  1162. entry.unseen.lock().unwrap().set_not_yet_seen(total);
  1163. }
  1164. }
  1165. }
  1166. } else {
  1167. debug!("parse error for {:?}", l);
  1168. }
  1169. }
  1170. mailboxes.retain(|_, v| v.hash != 0);
  1171. conn.send_command(b"LSUB \"\" \"*\"").await?;
  1172. conn.read_response(&mut res, RequiredResponses::LSUB_REQUIRED)
  1173. .await?;
  1174. let mut lines = res.split_rn();
  1175. debug!("out: {}", &res);
  1176. /* Remove "M__ OK .." line */
  1177. lines.next_back();
  1178. for l in lines {
  1179. if let Ok(subscription) =
  1180. protocol_parser::list_mailbox_result(l.as_bytes()).map(|(_, v)| v)
  1181. {
  1182. if let Some(f) = mailboxes.get_mut(&subscription.hash()) {
  1183. if subscription.no_select {
  1184. continue;
  1185. }
  1186. f.is_subscribed = true;
  1187. }
  1188. } else {
  1189. debug!("parse error for {:?}", l);
  1190. }
  1191. }
  1192. Ok(debug!(mailboxes))
  1193. }
  1194. pub fn validate_config(s: &AccountSettings) -> Result<()> {
  1195. get_conf_val!(s["server_hostname"])?;
  1196. get_conf_val!(s["server_username"])?;
  1197. if !s.extra.contains_key("server_password_command") {
  1198. get_conf_val!(s["server_password"])?;
  1199. } else if s.extra.contains_key("server_password") {
  1200. return Err(MeliError::new(format!(
  1201. "Configuration error ({}): both server_password and server_password_command are set, cannot choose",
  1202. s.name.as_str(),
  1203. )));
  1204. }
  1205. let server_port = get_conf_val!(s["server_port"], 143)?;
  1206. let use_tls = get_conf_val!(s["use_tls"], true)?;
  1207. let use_starttls = get_conf_val!(s["use_starttls"], !(server_port == 993))?;
  1208. if !use_tls && use_starttls {
  1209. return Err(MeliError::new(format!(
  1210. "Configuration error ({}): incompatible use_tls and use_starttls values: use_tls = false, use_starttls = true",
  1211. s.name.as_str(),
  1212. )));
  1213. }
  1214. get_conf_val!(s["danger_accept_invalid_certs"], false)?;
  1215. get_conf_val!(s["X_header_caching"], false)?;
  1216. get_conf_val!(s["use_idle"], true)?;
  1217. #[cfg(feature = "deflate_compression")]
  1218. get_conf_val!(s["use_deflate"], true)?;
  1219. #[cfg(not(feature = "deflate_compression"))]
  1220. if s.extra.contains_key("use_deflate") {
  1221. return Err(MeliError::new(format!(
  1222. "Configuration error ({}): setting `use_deflate` is set but this version of meli isn't compiled with DEFLATE support.",
  1223. s.name.as_str(),
  1224. )));
  1225. }
  1226. Ok(())
  1227. }
  1228. pub fn capabilities(&self) -> Vec<String> {
  1229. self.uid_store
  1230. .capabilities
  1231. .lock()
  1232. .unwrap()
  1233. .iter()
  1234. .map(|c| String::from_utf8_lossy(c).into())
  1235. .collect::<Vec<String>>()
  1236. }
  1237. }
  1238. fn fetch_cached_envs(
  1239. mailbox_hash: MailboxHash,
  1240. our_unseen: &mut BTreeSet<EnvelopeHash>,
  1241. uid_store: &UIDStore,
  1242. ) -> Result<(HashSet<EnvelopeHash>, Vec<Envelope>)> {
  1243. if !uid_store.cache_headers {
  1244. return Ok((HashSet::default(), vec![]));
  1245. }
  1246. let uidvalidities = uid_store.uidvalidity.lock().unwrap();
  1247. let v = if let Some(v) = uidvalidities.get(&mailbox_hash) {
  1248. v
  1249. } else {
  1250. return Ok((HashSet::default(), vec![]));
  1251. };
  1252. let cached_envs: (cache::MaxUID, Vec<(UID, Envelope)>);
  1253. cache::save_envelopes(uid_store.account_hash, mailbox_hash, *v, &[])
  1254. .chain_err_summary(|| "Could not save envelopes in cache in get()")?;
  1255. cached_envs = cache::fetch_envelopes(uid_store.account_hash, mailbox_hash, *v)
  1256. .chain_err_summary(|| "Could not get envelopes in cache in get()")?;
  1257. let (_max_uid, envelopes) = debug!(cached_envs);
  1258. let ret = envelopes.iter().map(|(_, env)| env.hash()).collect();
  1259. let payload = if !envelopes.is_empty() {
  1260. let mut payload = vec![];
  1261. for (uid, env) in envelopes {
  1262. if !env.is_seen() {
  1263. our_unseen.insert(env.hash());
  1264. }
  1265. uid_store
  1266. .hash_index
  1267. .lock()
  1268. .unwrap()
  1269. .insert(env.hash(), (uid, mailbox_hash));
  1270. uid_store
  1271. .uid_index
  1272. .lock()
  1273. .unwrap()
  1274. .insert((mailbox_hash, uid), env.hash());
  1275. payload.push(env);
  1276. }
  1277. debug!("sending cached payload for {}", mailbox_hash);
  1278. payload
  1279. } else {
  1280. vec![]
  1281. };
  1282. Ok((ret, payload))
  1283. }
  1284. async fn fetch_hlpr(
  1285. connection: &Arc<FutureMutex<ImapConnection>>,
  1286. mailbox_hash: MailboxHash,
  1287. cached_hash_set: &HashSet<EnvelopeHash>,
  1288. can_create_flags: &Arc<Mutex<bool>>,
  1289. our_unseen: &mut BTreeSet<EnvelopeHash>,
  1290. valid_hash_set: &mut HashSet<EnvelopeHash>,
  1291. uid_store: &UIDStore,
  1292. max_uid: &mut Option<usize>,
  1293. ) -> Result<Vec<Envelope>> {
  1294. let (permissions, mailbox_path, mailbox_exists, no_select, unseen) = {
  1295. let f = &uid_store.mailboxes.lock().await[&mailbox_hash];
  1296. (
  1297. f.permissions.clone(),
  1298. f.imap_path().to_string(),
  1299. f.exists.clone(),
  1300. f.no_select,
  1301. f.unseen.clone(),
  1302. )
  1303. };
  1304. if no_select {
  1305. *max_uid = Some(0);
  1306. return Ok(Vec::new());
  1307. }
  1308. let mut conn = connection.lock().await;
  1309. debug!("locked for fetch {}", mailbox_path);
  1310. let mut response = String::with_capacity(8 * 1024);
  1311. let max_uid_left = if let Some(max_uid) = max_uid {
  1312. *max_uid
  1313. } else {
  1314. conn.create_uid_msn_cache(mailbox_hash, 1).await?;
  1315. /* first SELECT the mailbox to get READ/WRITE permissions (because EXAMINE only
  1316. * returns READ-ONLY for both cases) */
  1317. conn.select_mailbox(mailbox_hash, &mut response, true)
  1318. .await
  1319. .chain_err_summary(|| format!("Could not select mailbox {}", mailbox_path))?;
  1320. let mut examine_response =
  1321. protocol_parser::select_response(&response).chain_err_summary(|| {
  1322. format!(
  1323. "Could not parse select response for mailbox {}",
  1324. mailbox_path
  1325. )
  1326. })?;
  1327. *can_create_flags.lock().unwrap() = examine_response.can_create_flags;
  1328. debug!(
  1329. "mailbox: {} examine_response: {:?}",
  1330. mailbox_path, examine_response
  1331. );
  1332. {
  1333. let mut uidvalidities = uid_store.uidvalidity.lock().unwrap();
  1334. let v = uidvalidities
  1335. .entry(mailbox_hash)
  1336. .or_insert(examine_response.uidvalidity);
  1337. if uid_store.cache_headers {
  1338. let _ = cache::save_envelopes(
  1339. uid_store.account_hash,
  1340. mailbox_hash,
  1341. examine_response.uidvalidity,
  1342. &[],
  1343. );
  1344. }
  1345. *v = examine_response.uidvalidity;
  1346. let mut permissions = permissions.lock().unwrap();
  1347. permissions.create_messages = !examine_response.read_only;
  1348. permissions.remove_messages = !examine_response.read_only;
  1349. permissions.set_flags = !examine_response.read_only;
  1350. permissions.rename_messages = !examine_response.read_only;
  1351. permissions.delete_messages = !examine_response.read_only;
  1352. mailbox_exists
  1353. .lock()
  1354. .unwrap()
  1355. .set_not_yet_seen(examine_response.exists);
  1356. }
  1357. if examine_response.exists == 0 {
  1358. if uid_store.cache_headers {
  1359. for &env_hash in cached_hash_set {
  1360. conn.add_refresh_event(RefreshEvent {
  1361. account_hash: uid_store.account_hash,
  1362. mailbox_hash,
  1363. kind: RefreshEventKind::Remove(env_hash),
  1364. });
  1365. }
  1366. let _ = cache::save_envelopes(
  1367. uid_store.account_hash,
  1368. mailbox_hash,
  1369. examine_response.uidvalidity,
  1370. &[],
  1371. );
  1372. }
  1373. *max_uid = Some(0);
  1374. return Ok(Vec::new());
  1375. }
  1376. /* reselecting the same mailbox with EXAMINE prevents expunging it */
  1377. conn.examine_mailbox(mailbox_hash, &mut response, true)
  1378. .await?;
  1379. if examine_response.uidnext == 0 {
  1380. /* UIDNEXT shouldn't be 0, since exists != 0 at this point */
  1381. conn.send_command(format!("STATUS \"{}\" (UIDNEXT)", mailbox_path).as_bytes())
  1382. .await?;
  1383. conn.read_response(&mut response, RequiredResponses::STATUS)
  1384. .await?;
  1385. let (_, status) = protocol_parser::status_response(response.as_bytes())?;
  1386. if let Some(uidnext) = status.uidnext {
  1387. if uidnext == 0 {
  1388. return Err(MeliError::new(
  1389. "IMAP server error: zero UIDNEXt with nonzero exists.",
  1390. ));
  1391. }
  1392. examine_response.uidnext = uidnext;
  1393. } else {
  1394. return Err(MeliError::new("IMAP server did not reply with UIDNEXT"));
  1395. }
  1396. }
  1397. *max_uid = Some(examine_response.uidnext - 1);
  1398. examine_response.uidnext - 1
  1399. };
  1400. let chunk_size = 600;
  1401. let mut payload = vec![];
  1402. conn.examine_mailbox(mailbox_hash, &mut response, false)
  1403. .await?;
  1404. if max_uid_left > 0 {
  1405. let mut envelopes = vec![];
  1406. debug!("{} max_uid_left= {}", mailbox_hash, max_uid_left);
  1407. if max_uid_left == 1 {
  1408. debug!("UID FETCH 1 (UID FLAGS ENVELOPE BODYSTRUCTURE)");
  1409. conn.send_command(b"UID FETCH 1 (UID FLAGS ENVELOPE BODYSTRUCTURE)")
  1410. .await?;
  1411. } else {
  1412. conn.send_command(
  1413. debug!(format!(
  1414. "UID FETCH {}:{} (UID FLAGS ENVELOPE BODYSTRUCTURE)",
  1415. std::cmp::max(std::cmp::max(max_uid_left.saturating_sub(chunk_size), 1), 1),
  1416. max_uid_left
  1417. ))
  1418. .as_bytes(),
  1419. )
  1420. .await?
  1421. };
  1422. conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED)
  1423. .await
  1424. .chain_err_summary(|| {
  1425. format!(
  1426. "Could not parse fetch response for mailbox {}",
  1427. mailbox_path
  1428. )
  1429. })?;
  1430. debug!(
  1431. "fetch response is {} bytes and {} lines",
  1432. response.len(),
  1433. response.lines().count()
  1434. );
  1435. let (_, v, _) = protocol_parser::uid_fetch_responses(&response)?;
  1436. debug!("responses len is {}", v.len());
  1437. for UidFetchResponse {
  1438. uid,
  1439. message_sequence_number,
  1440. flags,
  1441. envelope,
  1442. ..
  1443. } in v
  1444. {
  1445. let mut env = envelope.unwrap();
  1446. let mut h = DefaultHasher::new();
  1447. h.write_usize(uid);
  1448. h.write(mailbox_path.as_bytes());
  1449. env.set_hash(h.finish());
  1450. /*
  1451. debug!(
  1452. "env hash {} {} UID = {} MSN = {}",
  1453. env.hash(),
  1454. env.subject(),
  1455. uid,
  1456. message_sequence_number
  1457. );
  1458. */
  1459. valid_hash_set.insert(env.hash());
  1460. let mut tag_lck = uid_store.tag_index.write().unwrap();
  1461. if let Some((flags, keywords)) = flags {
  1462. if !flags.intersects(Flag::SEEN) {
  1463. our_unseen.insert(env.hash());
  1464. }
  1465. env.set_flags(flags);
  1466. for f in keywords {
  1467. let hash = tag_hash!(f);
  1468. if !tag_lck.contains_key(&hash) {
  1469. tag_lck.insert(hash, f);
  1470. }
  1471. env.labels_mut().push(hash);
  1472. }
  1473. }
  1474. uid_store
  1475. .msn_index
  1476. .lock()
  1477. .unwrap()
  1478. .entry(mailbox_hash)
  1479. .or_default()
  1480. .insert(message_sequence_number - 1, uid);
  1481. uid_store
  1482. .hash_index
  1483. .lock()
  1484. .unwrap()
  1485. .insert(env.hash(), (uid, mailbox_hash));
  1486. uid_store
  1487. .uid_index
  1488. .lock()
  1489. .unwrap()
  1490. .insert((mailbox_hash, uid), env.hash());
  1491. envelopes.push((uid, env));
  1492. }
  1493. debug!("sending payload for {}", mailbox_hash);
  1494. if uid_store.cache_headers {
  1495. //FIXME
  1496. cache::save_envelopes(
  1497. uid_store.account_hash,
  1498. mailbox_hash,
  1499. uid_store.uidvalidity.lock().unwrap()[&mailbox_hash],
  1500. &envelopes
  1501. .iter()
  1502. .map(|(uid, env)| (*uid, env))
  1503. .collect::<SmallVec<[(UID, &Envelope); 1024]>>(),
  1504. )
  1505. .chain_err_summary(|| {
  1506. format!(
  1507. "Could not save envelopes in cache for mailbox {}",
  1508. mailbox_path
  1509. )
  1510. })?;
  1511. }
  1512. for &env_hash in cached_hash_set.difference(&valid_hash_set) {
  1513. conn.add_refresh_event(RefreshEvent {
  1514. account_hash: uid_store.account_hash,
  1515. mailbox_hash,
  1516. kind: RefreshEventKind::Remove(env_hash),
  1517. });
  1518. }
  1519. unseen
  1520. .lock()
  1521. .unwrap()
  1522. .insert_set(our_unseen.iter().cloned().collect());
  1523. mailbox_exists
  1524. .lock()
  1525. .unwrap()
  1526. .insert_existing_set(envelopes.iter().map(|(_, env)| env.hash()).collect::<_>());
  1527. drop(conn);
  1528. payload.extend(envelopes.into_iter().map(|(_, env)| env));
  1529. }
  1530. *max_uid = if max_uid_left <= 1 {
  1531. Some(0)
  1532. } else {
  1533. Some(std::cmp::max(
  1534. std::cmp::max(max_uid_left.saturating_sub(chunk_size), 1),
  1535. 1,
  1536. ))
  1537. };
  1538. Ok(payload)
  1539. }