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.

256 lines
7.1KB

  1. /*
  2. * meli - mailbox 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. #[macro_use]
  22. mod backend;
  23. pub use self::backend::*;
  24. use crate::backends::*;
  25. use crate::email::parser;
  26. use crate::email::{Envelope, Flag};
  27. use crate::error::{MeliError, Result};
  28. use memmap::{Mmap, Protection};
  29. use std::collections::hash_map::DefaultHasher;
  30. use std::fs;
  31. use std::hash::{Hash, Hasher};
  32. use std::path::{Path, PathBuf};
  33. /// `BackendOp` implementor for Maildir
  34. #[derive(Debug)]
  35. pub struct MaildirOp {
  36. hash_index: HashIndexes,
  37. folder_hash: FolderHash,
  38. hash: EnvelopeHash,
  39. slice: Option<Mmap>,
  40. }
  41. impl Clone for MaildirOp {
  42. fn clone(&self) -> Self {
  43. MaildirOp {
  44. hash_index: self.hash_index.clone(),
  45. folder_hash: self.folder_hash,
  46. hash: self.hash,
  47. slice: None,
  48. }
  49. }
  50. }
  51. impl MaildirOp {
  52. pub fn new(hash: EnvelopeHash, hash_index: HashIndexes, folder_hash: FolderHash) -> Self {
  53. MaildirOp {
  54. hash_index,
  55. folder_hash,
  56. hash,
  57. slice: None,
  58. }
  59. }
  60. fn path(&self) -> PathBuf {
  61. let map = self.hash_index.lock().unwrap();
  62. let map = &map[&self.folder_hash];
  63. debug!("looking for {} in {} map", self.hash, self.folder_hash);
  64. if !map.contains_key(&self.hash) {
  65. debug!("doesn't contain it though len = {}\n{:#?}", map.len(), map);
  66. for e in map.iter() {
  67. debug!("{:#?}", e);
  68. }
  69. }
  70. if let Some(path) = &map[&self.hash].modified {
  71. path.clone()
  72. } else {
  73. map.get(&self.hash).unwrap().buf.to_path_buf()
  74. }
  75. }
  76. }
  77. impl<'a> BackendOp for MaildirOp {
  78. fn description(&self) -> String {
  79. format!("Path of file: {}", self.path().display())
  80. }
  81. fn as_bytes(&mut self) -> Result<&[u8]> {
  82. if self.slice.is_none() {
  83. self.slice = Some(Mmap::open_path(self.path(), Protection::Read)?);
  84. }
  85. /* Unwrap is safe since we use ? above. */
  86. Ok(unsafe { self.slice.as_ref().unwrap().as_slice() })
  87. }
  88. fn fetch_headers(&mut self) -> Result<&[u8]> {
  89. let raw = self.as_bytes()?;
  90. let result = parser::headers_raw(raw).to_full_result()?;
  91. Ok(result)
  92. }
  93. fn fetch_body(&mut self) -> Result<&[u8]> {
  94. let raw = self.as_bytes()?;
  95. let result = parser::headers_raw(raw).to_full_result()?;
  96. Ok(result)
  97. }
  98. fn fetch_flags(&self) -> Flag {
  99. let mut flag = Flag::default();
  100. let path = self.path();
  101. let path = path.to_str().unwrap(); // Assume UTF-8 validity
  102. if !path.contains(":2,") {
  103. return flag;
  104. }
  105. for f in path.chars().rev() {
  106. match f {
  107. ',' => break,
  108. 'D' => flag |= Flag::DRAFT,
  109. 'F' => flag |= Flag::FLAGGED,
  110. 'P' => flag |= Flag::PASSED,
  111. 'R' => flag |= Flag::REPLIED,
  112. 'S' => flag |= Flag::SEEN,
  113. 'T' => flag |= Flag::TRASHED,
  114. _ => {
  115. debug!("DEBUG: in fetch_flags, path is {}", path);
  116. }
  117. }
  118. }
  119. flag
  120. }
  121. fn set_flag(&mut self, envelope: &mut Envelope, f: Flag) -> Result<()> {
  122. let path = self.path();
  123. let path = path.to_str().unwrap(); // Assume UTF-8 validity
  124. let idx: usize = path
  125. .rfind(":2,")
  126. .ok_or_else(|| MeliError::new(format!("Invalid email filename: {:?}", self)))?
  127. + 3;
  128. let mut new_name: String = path[..idx].to_string();
  129. let mut flags = self.fetch_flags();
  130. if !(flags & f).is_empty() {
  131. return Ok(());
  132. }
  133. flags.toggle(f);
  134. if !(flags & Flag::DRAFT).is_empty() {
  135. new_name.push('D');
  136. }
  137. if !(flags & Flag::FLAGGED).is_empty() {
  138. new_name.push('F');
  139. }
  140. if !(flags & Flag::PASSED).is_empty() {
  141. new_name.push('P');
  142. }
  143. if !(flags & Flag::REPLIED).is_empty() {
  144. new_name.push('R');
  145. }
  146. if !(flags & Flag::SEEN).is_empty() {
  147. new_name.push('S');
  148. }
  149. if !(flags & Flag::TRASHED).is_empty() {
  150. new_name.push('T');
  151. }
  152. debug!("renaming {:?} to {:?}", path, new_name);
  153. fs::rename(&path, &new_name)?;
  154. debug!("success in rename");
  155. let old_hash = envelope.hash();
  156. let new_name: PathBuf = new_name.into();
  157. let hash_index = self.hash_index.clone();
  158. let mut map = hash_index.lock().unwrap();
  159. let map = map.entry(self.folder_hash).or_default();
  160. map.entry(old_hash).or_default().modified = Some(new_name.clone());
  161. Ok(())
  162. }
  163. }
  164. #[derive(Debug, Default)]
  165. pub struct MaildirFolder {
  166. hash: FolderHash,
  167. name: String,
  168. path: PathBuf,
  169. parent: Option<FolderHash>,
  170. children: Vec<FolderHash>,
  171. }
  172. impl MaildirFolder {
  173. pub fn new(
  174. path: String,
  175. file_name: String,
  176. parent: Option<FolderHash>,
  177. children: Vec<FolderHash>,
  178. ) -> Result<Self> {
  179. let pathbuf = PathBuf::from(path);
  180. let mut h = DefaultHasher::new();
  181. pathbuf.hash(&mut h);
  182. let ret = MaildirFolder {
  183. hash: h.finish(),
  184. name: file_name,
  185. path: pathbuf,
  186. parent,
  187. children,
  188. };
  189. ret.is_valid()?;
  190. Ok(ret)
  191. }
  192. pub fn path(&self) -> &Path {
  193. self.path.as_path()
  194. }
  195. fn is_valid(&self) -> Result<()> {
  196. let path = self.path();
  197. let mut p = PathBuf::from(path);
  198. for d in &["cur", "new", "tmp"] {
  199. p.push(d);
  200. if !p.is_dir() {
  201. return Err(MeliError::new(format!(
  202. "{} is not a valid maildir folder",
  203. path.display()
  204. )));
  205. }
  206. p.pop();
  207. }
  208. Ok(())
  209. }
  210. }
  211. impl BackendFolder for MaildirFolder {
  212. fn hash(&self) -> FolderHash {
  213. self.hash
  214. }
  215. fn name(&self) -> &str {
  216. &self.name
  217. }
  218. fn change_name(&mut self, s: &str) {
  219. self.name = s.to_string();
  220. }
  221. fn children(&self) -> &Vec<FolderHash> {
  222. &self.children
  223. }
  224. fn clone(&self) -> Folder {
  225. Box::new(MaildirFolder {
  226. hash: self.hash,
  227. name: self.name.clone(),
  228. path: self.path.clone(),
  229. children: self.children.clone(),
  230. parent: self.parent,
  231. })
  232. }
  233. fn parent(&self) -> Option<FolderHash> {
  234. self.parent
  235. }
  236. }