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.

238 lines
8.5 KiB

  1. /*
  2. * meli - imap module.
  3. *
  4. * Copyright 2017 - 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 super::*;
  22. use crate::backends::BackendOp;
  23. use crate::email::*;
  24. use crate::error::{MeliError, Result};
  25. use std::cell::Cell;
  26. use std::sync::{Arc, Mutex};
  27. /// `BackendOp` implementor for Imap
  28. #[derive(Debug, Clone)]
  29. pub struct ImapOp {
  30. uid: usize,
  31. bytes: Option<String>,
  32. headers: Option<String>,
  33. body: Option<String>,
  34. mailbox_path: String,
  35. mailbox_hash: MailboxHash,
  36. flags: Cell<Option<Flag>>,
  37. connection: Arc<Mutex<ImapConnection>>,
  38. uid_store: Arc<UIDStore>,
  39. }
  40. impl ImapOp {
  41. pub fn new(
  42. uid: usize,
  43. mailbox_path: String,
  44. mailbox_hash: MailboxHash,
  45. connection: Arc<Mutex<ImapConnection>>,
  46. uid_store: Arc<UIDStore>,
  47. ) -> Self {
  48. ImapOp {
  49. uid,
  50. connection,
  51. bytes: None,
  52. headers: None,
  53. body: None,
  54. mailbox_path,
  55. mailbox_hash,
  56. flags: Cell::new(None),
  57. uid_store,
  58. }
  59. }
  60. }
  61. impl BackendOp for ImapOp {
  62. fn description(&self) -> String {
  63. format!("Message in mailbox: {}", &self.mailbox_path)
  64. }
  65. fn as_bytes(&mut self) -> Result<&[u8]> {
  66. if self.bytes.is_none() {
  67. let mut bytes_cache = self.uid_store.byte_cache.lock()?;
  68. let cache = bytes_cache.entry(self.uid).or_default();
  69. if cache.bytes.is_some() {
  70. self.bytes = cache.bytes.clone();
  71. } else {
  72. drop(cache);
  73. drop(bytes_cache);
  74. let mut response = String::with_capacity(8 * 1024);
  75. {
  76. let mut conn =
  77. try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?;
  78. conn.examine_mailbox(self.mailbox_hash, &mut response)?;
  79. conn.send_command(format!("UID FETCH {} (FLAGS RFC822)", self.uid).as_bytes())?;
  80. conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED)?;
  81. }
  82. debug!(
  83. "fetch response is {} bytes and {} lines",
  84. response.len(),
  85. response.lines().collect::<Vec<&str>>().len()
  86. );
  87. let UidFetchResponse {
  88. uid, flags, body, ..
  89. } = protocol_parser::uid_fetch_response(&response)?.1;
  90. assert_eq!(uid, self.uid);
  91. assert!(body.is_some());
  92. let mut bytes_cache = self.uid_store.byte_cache.lock()?;
  93. let cache = bytes_cache.entry(self.uid).or_default();
  94. if let Some((flags, _)) = flags {
  95. self.flags.set(Some(flags));
  96. cache.flags = Some(flags);
  97. }
  98. cache.bytes =
  99. Some(unsafe { std::str::from_utf8_unchecked(body.unwrap()).to_string() });
  100. self.bytes = cache.bytes.clone();
  101. }
  102. }
  103. Ok(self.bytes.as_ref().unwrap().as_bytes())
  104. }
  105. fn fetch_flags(&self) -> Flag {
  106. macro_rules! or_return_default {
  107. ($expr:expr) => {
  108. match $expr {
  109. Ok(ok) => ok,
  110. Err(_) => return Default::default(),
  111. }
  112. };
  113. }
  114. if self.flags.get().is_some() {
  115. return self.flags.get().unwrap();
  116. }
  117. let mut bytes_cache = or_return_default!(self.uid_store.byte_cache.lock());
  118. let cache = bytes_cache.entry(self.uid).or_default();
  119. if cache.flags.is_some() {
  120. self.flags.set(cache.flags);
  121. } else {
  122. let mut response = String::with_capacity(8 * 1024);
  123. let mut conn = or_return_default!(try_lock(
  124. &self.connection,
  125. Some(std::time::Duration::new(2, 0))
  126. ));
  127. or_return_default!(conn.examine_mailbox(self.mailbox_hash, &mut response));
  128. or_return_default!(
  129. conn.send_command(format!("UID FETCH {} FLAGS", self.uid).as_bytes())
  130. );
  131. or_return_default!(conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED));
  132. debug!(
  133. "fetch response is {} bytes and {} lines",
  134. response.len(),
  135. response.lines().collect::<Vec<&str>>().len()
  136. );
  137. match protocol_parser::uid_fetch_flags_response(response.as_bytes())
  138. .to_full_result()
  139. .map_err(MeliError::from)
  140. {
  141. Ok(v) => {
  142. if v.len() != 1 {
  143. debug!("responses len is {}", v.len());
  144. /* TODO: Trigger cache invalidation here. */
  145. panic!(format!("message with UID {} was not found", self.uid));
  146. }
  147. let (uid, (flags, _)) = v[0];
  148. assert_eq!(uid, self.uid);
  149. cache.flags = Some(flags);
  150. self.flags.set(Some(flags));
  151. }
  152. Err(e) => or_return_default!(Err(e)),
  153. }
  154. }
  155. self.flags.get().unwrap()
  156. }
  157. fn set_flag(&mut self, _envelope: &mut Envelope, f: Flag, value: bool) -> Result<()> {
  158. let mut flags = self.fetch_flags();
  159. flags.set(f, value);
  160. let mut response = String::with_capacity(8 * 1024);
  161. let mut conn = try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?;
  162. conn.select_mailbox(self.mailbox_hash, &mut response)?;
  163. debug!(&response);
  164. conn.send_command(
  165. format!(
  166. "UID STORE {} FLAGS.SILENT ({})",
  167. self.uid,
  168. flags_to_imap_list!(flags)
  169. )
  170. .as_bytes(),
  171. )?;
  172. conn.read_response(&mut response, RequiredResponses::STORE_REQUIRED)?;
  173. debug!(&response);
  174. match protocol_parser::uid_fetch_flags_response(response.as_bytes())
  175. .to_full_result()
  176. .map_err(MeliError::from)
  177. {
  178. Ok(v) => {
  179. if v.len() == 1 {
  180. debug!("responses len is {}", v.len());
  181. let (uid, (flags, _)) = v[0];
  182. assert_eq!(uid, self.uid);
  183. self.flags.set(Some(flags));
  184. }
  185. }
  186. Err(e) => Err(e)?,
  187. }
  188. let mut bytes_cache = self.uid_store.byte_cache.lock()?;
  189. let cache = bytes_cache.entry(self.uid).or_default();
  190. cache.flags = Some(flags);
  191. Ok(())
  192. }
  193. fn set_tag(&mut self, envelope: &mut Envelope, tag: String, value: bool) -> Result<()> {
  194. let mut response = String::with_capacity(8 * 1024);
  195. let mut conn = try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?;
  196. conn.select_mailbox(self.mailbox_hash, &mut response)?;
  197. conn.send_command(
  198. format!(
  199. "UID STORE {} {}FLAGS.SILENT ({})",
  200. self.uid,
  201. if value { "+" } else { "-" },
  202. &tag
  203. )
  204. .as_bytes(),
  205. )?;
  206. conn.read_response(&mut response, RequiredResponses::STORE_REQUIRED)?;
  207. protocol_parser::uid_fetch_flags_response(response.as_bytes())
  208. .to_full_result()
  209. .map_err(MeliError::from)?;
  210. let hash = tag_hash!(tag);
  211. if value {
  212. self.uid_store.tag_index.write().unwrap().insert(hash, tag);
  213. } else {
  214. self.uid_store.tag_index.write().unwrap().remove(&hash);
  215. }
  216. if !envelope.labels().iter().any(|&h_| h_ == hash) {
  217. if value {
  218. envelope.labels_mut().push(hash);
  219. }
  220. }
  221. if !value {
  222. if let Some(pos) = envelope.labels().iter().position(|&h_| h_ == hash) {
  223. envelope.labels_mut().remove(pos);
  224. }
  225. }
  226. Ok(())
  227. }
  228. }