🐝 I really like where this mua is(was?) headed, but it seems as though there has not been much activity recently.
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.

881 lines
33 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 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. use crate::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
  35. use crate::backends::BackendOp;
  36. use crate::backends::MailboxHash;
  37. use crate::backends::RefreshEvent;
  38. use crate::backends::RefreshEventKind::{self, *};
  39. use crate::backends::{BackendMailbox, MailBackend, Mailbox, RefreshEventConsumer};
  40. use crate::conf::AccountSettings;
  41. use crate::email::*;
  42. use crate::error::{MeliError, Result};
  43. use fnv::{FnvHashMap, FnvHashSet};
  44. use std::collections::{hash_map::DefaultHasher, BTreeMap};
  45. use std::hash::Hasher;
  46. use std::str::FromStr;
  47. use std::sync::{Arc, Mutex, RwLock};
  48. use std::time::Instant;
  49. pub type UID = usize;
  50. pub static SUPPORTED_CAPABILITIES: &'static [&'static str] =
  51. &["IDLE", "LOGIN", "LOGINDISABLED", "ENABLE", "IMAP4REV1"];
  52. #[derive(Debug, Default)]
  53. pub struct EnvelopeCache {
  54. bytes: Option<String>,
  55. headers: Option<String>,
  56. body: Option<String>,
  57. flags: Option<Flag>,
  58. }
  59. #[derive(Debug, Clone)]
  60. pub struct ImapServerConf {
  61. pub server_hostname: String,
  62. pub server_username: String,
  63. pub server_password: String,
  64. pub server_port: u16,
  65. pub use_starttls: bool,
  66. pub danger_accept_invalid_certs: bool,
  67. }
  68. struct IsSubscribedFn(Box<dyn Fn(&str) -> bool + Send + Sync>);
  69. impl std::fmt::Debug for IsSubscribedFn {
  70. fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  71. write!(f, "IsSubscribedFn Box")
  72. }
  73. }
  74. impl std::ops::Deref for IsSubscribedFn {
  75. type Target = Box<dyn Fn(&str) -> bool + Send + Sync>;
  76. fn deref(&self) -> &Box<dyn Fn(&str) -> bool + Send + Sync> {
  77. &self.0
  78. }
  79. }
  80. type Capabilities = FnvHashSet<Vec<u8>>;
  81. macro_rules! get_conf_val {
  82. ($s:ident[$var:literal]) => {
  83. $s.extra.get($var).ok_or_else(|| {
  84. MeliError::new(format!(
  85. "Configuration error ({}): IMAP connection requires the field `{}` set",
  86. $s.name.as_str(),
  87. $var
  88. ))
  89. })
  90. };
  91. ($s:ident[$var:literal], $default:expr) => {
  92. $s.extra
  93. .get($var)
  94. .map(|v| {
  95. <_>::from_str(v).map_err(|e| {
  96. MeliError::new(format!(
  97. "Configuration error ({}): Invalid value for field `{}`: {}\n{}",
  98. $s.name.as_str(),
  99. $var,
  100. v,
  101. e
  102. ))
  103. })
  104. })
  105. .unwrap_or_else(|| Ok($default))
  106. };
  107. }
  108. #[derive(Debug)]
  109. pub struct UIDStore {
  110. uidvalidity: Arc<Mutex<FnvHashMap<MailboxHash, UID>>>,
  111. hash_index: Arc<Mutex<FnvHashMap<EnvelopeHash, (UID, MailboxHash)>>>,
  112. uid_index: Arc<Mutex<FnvHashMap<UID, EnvelopeHash>>>,
  113. byte_cache: Arc<Mutex<FnvHashMap<UID, EnvelopeCache>>>,
  114. }
  115. #[derive(Debug)]
  116. pub struct ImapType {
  117. account_name: String,
  118. online: Arc<Mutex<(Instant, Result<()>)>>,
  119. is_subscribed: Arc<IsSubscribedFn>,
  120. connection: Arc<Mutex<ImapConnection>>,
  121. server_conf: ImapServerConf,
  122. uid_store: Arc<UIDStore>,
  123. can_create_flags: Arc<Mutex<bool>>,
  124. tag_index: Arc<RwLock<BTreeMap<u64, String>>>,
  125. mailboxes: Arc<RwLock<FnvHashMap<MailboxHash, ImapMailbox>>>,
  126. }
  127. impl MailBackend for ImapType {
  128. fn is_online(&self) -> Result<()> {
  129. self.online.lock().unwrap().1.clone()
  130. }
  131. fn connect(&mut self) {
  132. if self.is_online().is_err() {
  133. if Instant::now().duration_since(self.online.lock().unwrap().0)
  134. >= std::time::Duration::new(2, 0)
  135. {
  136. let _ = self.connection.lock().unwrap().connect();
  137. }
  138. }
  139. }
  140. fn get(&mut self, mailbox: &Mailbox) -> Async<Result<Vec<Envelope>>> {
  141. let mut w = AsyncBuilder::new();
  142. let handle = {
  143. let tx = w.tx();
  144. let uid_store = self.uid_store.clone();
  145. let tag_index = self.tag_index.clone();
  146. let can_create_flags = self.can_create_flags.clone();
  147. let mailbox_hash = mailbox.hash();
  148. let (permissions, mailbox_path, mailbox_exists, no_select, unseen) = {
  149. let f = &self.mailboxes.read().unwrap()[&mailbox_hash];
  150. (
  151. f.permissions.clone(),
  152. f.imap_path().to_string(),
  153. f.exists.clone(),
  154. f.no_select,
  155. f.unseen.clone(),
  156. )
  157. };
  158. let connection = self.connection.clone();
  159. let closure = move |_work_context| {
  160. if no_select {
  161. tx.send(AsyncStatus::Payload(Ok(Vec::new()))).unwrap();
  162. tx.send(AsyncStatus::Finished).unwrap();
  163. return;
  164. }
  165. let _tx = tx.clone();
  166. if let Err(err) = (move || {
  167. let tx = _tx;
  168. let mut response = String::with_capacity(8 * 1024);
  169. let mut conn = connection.lock()?;
  170. debug!("locked for get {}", mailbox_path);
  171. /* first SELECT the mailbox to get READ/WRITE permissions (because EXAMINE only
  172. * returns READ-ONLY for both cases) */
  173. conn.send_command(format!("SELECT \"{}\"", mailbox_path).as_bytes())?;
  174. conn.read_response(&mut response)?;
  175. let examine_response = protocol_parser::select_response(&response)?;
  176. *can_create_flags.lock().unwrap() = examine_response.can_create_flags;
  177. debug!(
  178. "mailbox: {} examine_response: {:?}",
  179. mailbox_path, examine_response
  180. );
  181. let mut exists: usize = examine_response.uidnext - 1;
  182. {
  183. let mut uidvalidities = uid_store.uidvalidity.lock().unwrap();
  184. let v = uidvalidities
  185. .entry(mailbox_hash)
  186. .or_insert(examine_response.uidvalidity);
  187. *v = examine_response.uidvalidity;
  188. let mut permissions = permissions.lock().unwrap();
  189. permissions.create_messages = !examine_response.read_only;
  190. permissions.remove_messages = !examine_response.read_only;
  191. permissions.set_flags = !examine_response.read_only;
  192. permissions.rename_messages = !examine_response.read_only;
  193. permissions.delete_messages = !examine_response.read_only;
  194. permissions.delete_messages = !examine_response.read_only;
  195. let mut mailbox_exists = mailbox_exists.lock().unwrap();
  196. *mailbox_exists = exists;
  197. }
  198. /* reselecting the same mailbox with EXAMINE prevents expunging it */
  199. conn.send_command(format!("EXAMINE \"{}\"", mailbox_path).as_bytes())?;
  200. conn.read_response(&mut response)?;
  201. let mut tag_lck = tag_index.write().unwrap();
  202. let mut our_unseen = 0;
  203. while exists > 1 {
  204. let mut envelopes = vec![];
  205. conn.send_command(
  206. format!(
  207. "UID FETCH {}:{} (UID FLAGS ENVELOPE BODYSTRUCTURE)",
  208. std::cmp::max(exists.saturating_sub(500), 1),
  209. exists
  210. )
  211. .as_bytes(),
  212. )?;
  213. conn.read_response(&mut response)?;
  214. debug!(
  215. "fetch response is {} bytes and {} lines",
  216. response.len(),
  217. response.lines().collect::<Vec<&str>>().len()
  218. );
  219. let (_, v, _) = protocol_parser::uid_fetch_responses(&response)?;
  220. debug!("responses len is {}", v.len());
  221. for UidFetchResponse {
  222. uid,
  223. flags,
  224. envelope,
  225. ..
  226. } in v
  227. {
  228. let mut env = envelope.unwrap();
  229. let mut h = DefaultHasher::new();
  230. h.write_usize(uid);
  231. h.write(mailbox_path.as_bytes());
  232. env.set_hash(h.finish());
  233. if let Some((flags, keywords)) = flags {
  234. if !flags.contains(Flag::SEEN) {
  235. our_unseen += 1;
  236. }
  237. env.set_flags(flags);
  238. for f in keywords {
  239. let hash = tag_hash!(f);
  240. if !tag_lck.contains_key(&hash) {
  241. tag_lck.insert(hash, f);
  242. }
  243. env.labels_mut().push(hash);
  244. }
  245. }
  246. uid_store
  247. .hash_index
  248. .lock()
  249. .unwrap()
  250. .insert(env.hash(), (uid, mailbox_hash));
  251. uid_store.uid_index.lock().unwrap().insert(uid, env.hash());
  252. envelopes.push(env);
  253. }
  254. exists = std::cmp::max(exists.saturating_sub(500), 1);
  255. debug!("sending payload");
  256. *unseen.lock().unwrap() = our_unseen;
  257. tx.send(AsyncStatus::Payload(Ok(envelopes))).unwrap();
  258. }
  259. drop(conn);
  260. Ok(())
  261. })() {
  262. tx.send(AsyncStatus::Payload(Err(err))).unwrap();
  263. }
  264. tx.send(AsyncStatus::Finished).unwrap();
  265. };
  266. Box::new(closure)
  267. };
  268. w.build(handle)
  269. }
  270. fn refresh(
  271. &mut self,
  272. mailbox_hash: MailboxHash,
  273. sender: RefreshEventConsumer,
  274. ) -> Result<Async<()>> {
  275. self.connection.lock().unwrap().connect()?;
  276. let inbox = self
  277. .mailboxes
  278. .read()
  279. .unwrap()
  280. .get(&mailbox_hash)
  281. .map(std::clone::Clone::clone)
  282. .unwrap();
  283. let tag_index = self.tag_index.clone();
  284. let main_conn = self.connection.clone();
  285. let uid_store = self.uid_store.clone();
  286. let account_name = self.account_name.clone();
  287. let w = AsyncBuilder::new();
  288. let closure = move |work_context: WorkContext| {
  289. let thread = std::thread::current();
  290. let mut conn = main_conn.lock().unwrap();
  291. work_context
  292. .set_name
  293. .send((
  294. thread.id(),
  295. format!("refreshing {} imap connection", account_name.as_str(),),
  296. ))
  297. .unwrap();
  298. work_context
  299. .set_status
  300. .send((thread.id(), "refresh".to_string()))
  301. .unwrap();
  302. watch::examine_updates(
  303. &inbox,
  304. &sender,
  305. &mut conn,
  306. &uid_store,
  307. &work_context,
  308. &tag_index,
  309. )
  310. .ok()
  311. .take();
  312. };
  313. Ok(w.build(Box::new(closure)))
  314. }
  315. fn watch(
  316. &self,
  317. sender: RefreshEventConsumer,
  318. work_context: WorkContext,
  319. ) -> Result<std::thread::ThreadId> {
  320. let mailboxes = self.mailboxes.clone();
  321. let tag_index = self.tag_index.clone();
  322. let conn = ImapConnection::new_connection(&self.server_conf, self.online.clone());
  323. let main_conn = self.connection.clone();
  324. let is_online = self.online.clone();
  325. let uid_store = self.uid_store.clone();
  326. let handle = std::thread::Builder::new()
  327. .name(format!("{} imap connection", self.account_name.as_str(),))
  328. .spawn(move || {
  329. let thread = std::thread::current();
  330. work_context
  331. .set_status
  332. .send((thread.id(), "watching".to_string()))
  333. .unwrap();
  334. let has_idle: bool = main_conn
  335. .lock()
  336. .unwrap()
  337. .capabilities
  338. .iter()
  339. .any(|cap| cap.eq_ignore_ascii_case(b"IDLE"));
  340. let kit = ImapWatchKit {
  341. conn,
  342. is_online,
  343. main_conn,
  344. uid_store,
  345. mailboxes,
  346. sender,
  347. work_context,
  348. tag_index,
  349. };
  350. if has_idle {
  351. idle(kit).ok().take();
  352. } else {
  353. poll_with_examine(kit).ok().take();
  354. }
  355. })?;
  356. Ok(handle.thread().id())
  357. }
  358. fn mailboxes(&self) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
  359. {
  360. let mailboxes = self.mailboxes.read().unwrap();
  361. if !mailboxes.is_empty() {
  362. return Ok(mailboxes
  363. .iter()
  364. .map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Mailbox))
  365. .collect());
  366. }
  367. }
  368. let mut mailboxes = self.mailboxes.write()?;
  369. *mailboxes = ImapType::imap_mailboxes(&self.connection)?;
  370. mailboxes.retain(|_, f| (self.is_subscribed)(f.path()));
  371. let keys = mailboxes
  372. .keys()
  373. .cloned()
  374. .collect::<FnvHashSet<MailboxHash>>();
  375. let mut uid_lock = self.uid_store.uidvalidity.lock().unwrap();
  376. for f in mailboxes.values_mut() {
  377. uid_lock.entry(f.hash()).or_default();
  378. f.children.retain(|c| keys.contains(c));
  379. }
  380. drop(uid_lock);
  381. Ok(mailboxes
  382. .iter()
  383. .filter(|(_, f)| f.is_subscribed)
  384. .map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Mailbox))
  385. .collect())
  386. }
  387. fn operation(&self, hash: EnvelopeHash) -> Box<dyn BackendOp> {
  388. let (uid, mailbox_hash) = self.uid_store.hash_index.lock().unwrap()[&hash];
  389. Box::new(ImapOp::new(
  390. uid,
  391. self.mailboxes.read().unwrap()[&mailbox_hash]
  392. .imap_path()
  393. .to_string(),
  394. self.connection.clone(),
  395. self.uid_store.clone(),
  396. self.tag_index.clone(),
  397. ))
  398. }
  399. fn save(&self, bytes: &[u8], mailbox: &str, flags: Option<Flag>) -> Result<()> {
  400. let path = {
  401. let mailboxes = self.mailboxes.read().unwrap();
  402. let f_result = mailboxes
  403. .values()
  404. .find(|v| v.path == mailbox || v.name == mailbox);
  405. if f_result
  406. .map(|f| !f.permissions.lock().unwrap().create_messages)
  407. .unwrap_or(false)
  408. {
  409. return Err(MeliError::new(format!(
  410. "You are not allowed to create messages in mailbox {}",
  411. mailbox
  412. )));
  413. }
  414. f_result
  415. .map(|v| v.imap_path().to_string())
  416. .ok_or(MeliError::new(format!(
  417. "Mailbox with name {} not found.",
  418. mailbox
  419. )))?
  420. };
  421. let mut response = String::with_capacity(8 * 1024);
  422. let mut conn = self.connection.lock().unwrap();
  423. let flags = flags.unwrap_or(Flag::empty());
  424. conn.send_command(
  425. format!(
  426. "APPEND \"{}\" ({}) {{{}}}",
  427. &path,
  428. flags_to_imap_list!(flags),
  429. bytes.len()
  430. )
  431. .as_bytes(),
  432. )?;
  433. // wait for "+ Ready for literal data" reply
  434. conn.wait_for_continuation_request()?;
  435. conn.send_literal(bytes)?;
  436. conn.read_response(&mut response)?;
  437. Ok(())
  438. }
  439. fn as_any(&self) -> &dyn::std::any::Any {
  440. self
  441. }
  442. fn as_any_mut(&mut self) -> &mut dyn::std::any::Any {
  443. self
  444. }
  445. fn tags(&self) -> Option<Arc<RwLock<BTreeMap<u64, String>>>> {
  446. if *self.can_create_flags.lock().unwrap() {
  447. Some(self.tag_index.clone())
  448. } else {
  449. None
  450. }
  451. }
  452. fn create_mailbox(
  453. &mut self,
  454. mut path: String,
  455. ) -> Result<(MailboxHash, FnvHashMap<MailboxHash, Mailbox>)> {
  456. /* Must transform path to something the IMAP server will accept
  457. *
  458. * Each root mailbox has a hierarchy delimeter reported by the LIST entry. All paths
  459. * must use this delimeter to indicate children of this mailbox.
  460. *
  461. * A new root mailbox should have the default delimeter, which can be found out by issuing
  462. * an empty LIST command as described in RFC3501:
  463. * C: A101 LIST "" ""
  464. * S: * LIST (\Noselect) "/" ""
  465. *
  466. * The default delimiter for us is '/' just like UNIX paths. I apologise if this
  467. * decision is unpleasant for you.
  468. */
  469. let mut mailboxes = self.mailboxes.write().unwrap();
  470. for root_mailbox in mailboxes.values().filter(|f| f.parent.is_none()) {
  471. if path.starts_with(&root_mailbox.name) {
  472. debug!("path starts with {:?}", &root_mailbox);
  473. path = path.replace(
  474. '/',
  475. (root_mailbox.separator as char).encode_utf8(&mut [0; 4]),
  476. );
  477. break;
  478. }
  479. }
  480. if mailboxes.values().any(|f| f.path == path) {
  481. return Err(MeliError::new(format!(
  482. "Mailbox named `{}` in account `{}` already exists.",
  483. path, self.account_name,
  484. )));
  485. }
  486. let mut response = String::with_capacity(8 * 1024);
  487. {
  488. let mut conn_lck = self.connection.lock()?;
  489. conn_lck.send_command(format!("CREATE \"{}\"", path,).as_bytes())?;
  490. conn_lck.read_response(&mut response)?;
  491. conn_lck.send_command(format!("SUBSCRIBE \"{}\"", path,).as_bytes())?;
  492. conn_lck.read_response(&mut response)?;
  493. }
  494. let ret: Result<()> = ImapResponse::from(&response).into();
  495. ret?;
  496. let new_hash = get_path_hash!(path.as_str());
  497. mailboxes.clear();
  498. drop(mailboxes);
  499. Ok((new_hash, self.mailboxes().map_err(|err| MeliError::new(format!("Mailbox create was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err)))?))
  500. }
  501. fn delete_mailbox(
  502. &mut self,
  503. mailbox_hash: MailboxHash,
  504. ) -> Result<FnvHashMap<MailboxHash, Mailbox>> {
  505. let mut mailboxes = self.mailboxes.write().unwrap();
  506. let permissions = mailboxes[&mailbox_hash].permissions();
  507. if !permissions.delete_mailbox {
  508. return Err(MeliError::new(format!("You do not have permission to delete `{}`. Set permissions for this mailbox are {}", mailboxes[&mailbox_hash].name(), permissions)));
  509. }
  510. let mut response = String::with_capacity(8 * 1024);
  511. {
  512. let mut conn_lck = self.connection.lock()?;
  513. if !mailboxes[&mailbox_hash].no_select {
  514. /* make sure mailbox is not selected before it gets deleted, otherwise
  515. * connection gets dropped by server */
  516. if conn_lck
  517. .capabilities
  518. .iter()
  519. .any(|cap| cap.eq_ignore_ascii_case(b"UNSELECT"))
  520. {
  521. conn_lck.send_command(
  522. format!("UNSELECT \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
  523. )?;
  524. conn_lck.read_response(&mut response)?;
  525. } else {
  526. conn_lck.send_command(
  527. format!("SELECT \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
  528. )?;
  529. conn_lck.read_response(&mut response)?;
  530. conn_lck.send_command(
  531. format!("EXAMINE \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
  532. )?;
  533. conn_lck.read_response(&mut response)?;
  534. }
  535. }
  536. if mailboxes[&mailbox_hash].is_subscribed() {
  537. conn_lck.send_command(
  538. format!("UNSUBSCRIBE \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
  539. )?;
  540. conn_lck.read_response(&mut response)?;
  541. }
  542. conn_lck.send_command(
  543. debug!(format!(
  544. "DELETE \"{}\"",
  545. mailboxes[&mailbox_hash].imap_path()
  546. ))
  547. .as_bytes(),
  548. )?;
  549. conn_lck.read_response(&mut response)?;
  550. }
  551. let ret: Result<()> = ImapResponse::from(&response).into();
  552. ret?;
  553. mailboxes.clear();
  554. drop(mailboxes);
  555. self.mailboxes().map_err(|err| format!("Mailbox delete was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err).into())
  556. }
  557. fn set_mailbox_subscription(&mut self, mailbox_hash: MailboxHash, new_val: bool) -> Result<()> {
  558. let mut mailboxes = self.mailboxes.write().unwrap();
  559. if mailboxes[&mailbox_hash].is_subscribed() == new_val {
  560. return Ok(());
  561. }
  562. let mut response = String::with_capacity(8 * 1024);
  563. {
  564. let mut conn_lck = self.connection.lock()?;
  565. if new_val {
  566. conn_lck.send_command(
  567. format!("SUBSCRIBE \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
  568. )?;
  569. } else {
  570. conn_lck.send_command(
  571. format!("UNSUBSCRIBE \"{}\"", mailboxes[&mailbox_hash].imap_path()).as_bytes(),
  572. )?;
  573. }
  574. conn_lck.read_response(&mut response)?;
  575. }
  576. let ret: Result<()> = ImapResponse::from(&response).into();
  577. if ret.is_ok() {
  578. mailboxes.entry(mailbox_hash).and_modify(|entry| {
  579. let _ = entry.set_is_subscribed(new_val);
  580. });
  581. }
  582. ret
  583. }
  584. fn rename_mailbox(
  585. &mut self,
  586. mailbox_hash: MailboxHash,
  587. mut new_path: String,
  588. ) -> Result<Mailbox> {
  589. let mut mailboxes = self.mailboxes.write().unwrap();
  590. let permissions = mailboxes[&mailbox_hash].permissions();
  591. if !permissions.delete_mailbox {
  592. 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)));
  593. }
  594. let mut response = String::with_capacity(8 * 1024);
  595. if mailboxes[&mailbox_hash].separator != b'/' {
  596. new_path = new_path.replace(
  597. '/',
  598. (mailboxes[&mailbox_hash].separator as char).encode_utf8(&mut [0; 4]),
  599. );
  600. }
  601. {
  602. let mut conn_lck = self.connection.lock()?;
  603. conn_lck.send_command(
  604. debug!(format!(
  605. "RENAME \"{}\" \"{}\"",
  606. mailboxes[&mailbox_hash].imap_path(),
  607. new_path
  608. ))
  609. .as_bytes(),
  610. )?;
  611. conn_lck.read_response(&mut response)?;
  612. }
  613. let new_hash = get_path_hash!(new_path.as_str());
  614. let ret: Result<()> = ImapResponse::from(&response).into();
  615. ret?;
  616. mailboxes.clear();
  617. drop(mailboxes);
  618. self.mailboxes().map_err(|err| format!("Mailbox rename was succesful (returned `{}`) but listing mailboxes afterwards returned `{}`", response, err))?;
  619. Ok(BackendMailbox::clone(
  620. &self.mailboxes.read().unwrap()[&new_hash],
  621. ))
  622. }
  623. fn set_mailbox_permissions(
  624. &mut self,
  625. mailbox_hash: MailboxHash,
  626. _val: crate::backends::MailboxPermissions,
  627. ) -> Result<()> {
  628. let mailboxes = self.mailboxes.write().unwrap();
  629. let permissions = mailboxes[&mailbox_hash].permissions();
  630. if !permissions.change_permissions {
  631. 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)));
  632. }
  633. Err(MeliError::new("Unimplemented."))
  634. }
  635. }
  636. impl ImapType {
  637. pub fn new(
  638. s: &AccountSettings,
  639. is_subscribed: Box<dyn Fn(&str) -> bool + Send + Sync>,
  640. ) -> Result<Box<dyn MailBackend>> {
  641. let server_hostname = get_conf_val!(s["server_hostname"])?;
  642. let server_username = get_conf_val!(s["server_username"])?;
  643. let server_password = get_conf_val!(s["server_password"])?;
  644. let server_port = get_conf_val!(s["server_port"], 143)?;
  645. let use_starttls = get_conf_val!(s["use_starttls"], !(server_port == 993))?;
  646. let danger_accept_invalid_certs: bool =
  647. get_conf_val!(s["danger_accept_invalid_certs"], false)?;
  648. let server_conf = ImapServerConf {
  649. server_hostname: server_hostname.to_string(),
  650. server_username: server_username.to_string(),
  651. server_password: server_password.to_string(),
  652. server_port,
  653. use_starttls,
  654. danger_accept_invalid_certs,
  655. };
  656. let online = Arc::new(Mutex::new((
  657. Instant::now(),
  658. Err(MeliError::new("Account is uninitialised.")),
  659. )));
  660. let connection = ImapConnection::new_connection(&server_conf, online.clone());
  661. Ok(Box::new(ImapType {
  662. account_name: s.name().to_string(),
  663. online,
  664. server_conf,
  665. is_subscribed: Arc::new(IsSubscribedFn(is_subscribed)),
  666. can_create_flags: Arc::new(Mutex::new(false)),
  667. tag_index: Arc::new(RwLock::new(Default::default())),
  668. mailboxes: Arc::new(RwLock::new(Default::default())),
  669. connection: Arc::new(Mutex::new(connection)),
  670. uid_store: Arc::new(UIDStore {
  671. uidvalidity: Default::default(),
  672. hash_index: Default::default(),
  673. uid_index: Default::default(),
  674. byte_cache: Default::default(),
  675. }),
  676. }))
  677. }
  678. pub fn shell(&mut self) {
  679. let mut conn = ImapConnection::new_connection(&self.server_conf, self.online.clone());
  680. conn.connect().unwrap();
  681. let mut res = String::with_capacity(8 * 1024);
  682. conn.send_command(b"NOOP").unwrap();
  683. conn.read_response(&mut res).unwrap();
  684. let mut input = String::new();
  685. loop {
  686. use std::io;
  687. input.clear();
  688. match io::stdin().read_line(&mut input) {
  689. Ok(_) => {
  690. if input.trim().eq_ignore_ascii_case("logout") {
  691. break;
  692. }
  693. conn.send_command(input.as_bytes()).unwrap();
  694. conn.read_lines(&mut res, String::new()).unwrap();
  695. if input.trim() == "IDLE" {
  696. let mut iter = ImapBlockingConnection::from(conn);
  697. while let Some(line) = iter.next() {
  698. debug!("out: {}", unsafe { std::str::from_utf8_unchecked(&line) });
  699. }
  700. conn = iter.into_conn();
  701. }
  702. debug!("out: {}", &res);
  703. }
  704. Err(error) => debug!("error: {}", error),
  705. }
  706. }
  707. }
  708. pub fn imap_mailboxes(
  709. connection: &Arc<Mutex<ImapConnection>>,
  710. ) -> Result<FnvHashMap<MailboxHash, ImapMailbox>> {
  711. let mut mailboxes: FnvHashMap<MailboxHash, ImapMailbox> = Default::default();
  712. let mut res = String::with_capacity(8 * 1024);
  713. let mut conn = connection.lock().unwrap();
  714. conn.send_command(b"LIST \"\" \"*\"")?;
  715. conn.read_response(&mut res)?;
  716. debug!("out: {}", &res);
  717. let mut lines = res.lines();
  718. /* Remove "M__ OK .." line */
  719. lines.next_back();
  720. for l in lines.map(|l| l.trim()) {
  721. if let Ok(mut mailbox) =
  722. protocol_parser::list_mailbox_result(l.as_bytes()).to_full_result()
  723. {
  724. if let Some(parent) = mailbox.parent {
  725. if mailboxes.contains_key(&parent) {
  726. mailboxes
  727. .entry(parent)
  728. .and_modify(|e| e.children.push(mailbox.hash));
  729. } else {
  730. /* Insert dummy parent entry, populating only the children field. Later
  731. * when we encounter the parent entry we will swap its children with
  732. * dummy's */
  733. mailboxes.insert(
  734. parent,
  735. ImapMailbox {
  736. children: vec![mailbox.hash],
  737. ..ImapMailbox::default()
  738. },
  739. );
  740. }
  741. }
  742. if mailboxes.contains_key(&mailbox.hash) {
  743. let entry = mailboxes.entry(mailbox.hash).or_default();
  744. std::mem::swap(&mut entry.children, &mut mailbox.children);
  745. *entry = mailbox;
  746. } else {
  747. mailboxes.insert(mailbox.hash, mailbox);
  748. }
  749. } else {
  750. debug!("parse error for {:?}", l);
  751. }
  752. }
  753. conn.send_command(b"LSUB \"\" \"*\"")?;
  754. conn.read_response(&mut res)?;
  755. debug!("out: {}", &res);
  756. let mut lines = res.lines();
  757. /* Remove "M__ OK .." line */
  758. lines.next_back();
  759. for l in lines.map(|l| l.trim()) {
  760. if let Ok(subscription) =
  761. protocol_parser::list_mailbox_result(l.as_bytes()).to_full_result()
  762. {
  763. if let Some(f) = mailboxes.get_mut(&subscription.hash()) {
  764. if subscription.no_select {
  765. continue;
  766. }
  767. f.is_subscribed = true;
  768. }
  769. } else {
  770. debug!("parse error for {:?}", l);
  771. }
  772. }
  773. Ok(debug!(mailboxes))
  774. }
  775. pub fn capabilities(&self) -> Vec<String> {
  776. self.connection
  777. .lock()
  778. .unwrap()
  779. .capabilities
  780. .iter()
  781. .map(|c| String::from_utf8_lossy(c).into())
  782. .collect::<Vec<String>>()
  783. }
  784. pub fn search(
  785. &self,
  786. query: String,
  787. mailbox_hash: MailboxHash,
  788. ) -> Result<SmallVec<[EnvelopeHash; 512]>> {
  789. let mailboxes_lck = self.mailboxes.read()?;
  790. let mut response = String::with_capacity(8 * 1024);
  791. let mut conn = self.connection.lock()?;
  792. conn.send_command(
  793. format!("EXAMINE \"{}\"", mailboxes_lck[&mailbox_hash].imap_path()).as_bytes(),
  794. )?;
  795. conn.read_response(&mut response)?;
  796. conn.send_command(format!("UID SEARCH CHARSET UTF-8 {}", query).as_bytes())?;
  797. conn.read_response(&mut response)?;
  798. debug!(&response);
  799. let mut lines = response.lines();
  800. for l in lines.by_ref() {
  801. if l.starts_with("* SEARCH") {
  802. use std::iter::FromIterator;
  803. let uid_index = self.uid_store.uid_index.lock()?;
  804. return Ok(SmallVec::from_iter(
  805. l["* SEARCH".len()..]
  806. .trim()
  807. .split_whitespace()
  808. .map(usize::from_str)
  809. .filter_map(std::result::Result::ok)
  810. .filter_map(|uid| uid_index.get(&uid))
  811. .map(|env_hash_ref| *env_hash_ref),
  812. ));
  813. }
  814. }
  815. Err(MeliError::new(response))
  816. }
  817. pub fn validate_config(s: &AccountSettings) -> Result<()> {
  818. get_conf_val!(s["server_hostname"])?;
  819. get_conf_val!(s["server_username"])?;
  820. get_conf_val!(s["server_password"])?;
  821. get_conf_val!(s["server_port"], 143)?;
  822. get_conf_val!(s["use_starttls"], false)?;
  823. get_conf_val!(s["danger_accept_invalid_certs"], false)?;
  824. Ok(())
  825. }
  826. }