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.

760 lines
32KB

  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. use super::{
  22. BackendFolder, BackendOp, Folder, FolderHash, MailBackend, RefreshEvent, RefreshEventConsumer,
  23. RefreshEventKind::*,
  24. };
  25. use super::{MaildirFolder, MaildirOp};
  26. use crate::async_workers::*;
  27. use crate::conf::AccountSettings;
  28. use crate::email::{Envelope, EnvelopeHash};
  29. use crate::error::{MeliError, Result};
  30. extern crate notify;
  31. use self::notify::{watcher, DebouncedEvent, RecursiveMode, Watcher};
  32. use std::time::Duration;
  33. use std::sync::mpsc::channel;
  34. //use std::sync::mpsc::sync_channel;
  35. //use std::sync::mpsc::SyncSender;
  36. //use std::time::Duration;
  37. use fnv::{FnvHashMap, FnvHasher};
  38. use std::collections::hash_map::DefaultHasher;
  39. use std::ffi::OsStr;
  40. use std::fs;
  41. use std::hash::{Hash, Hasher};
  42. use std::io::{self, Read, Write};
  43. use std::ops::{Deref, DerefMut};
  44. use std::path::{Component, Path, PathBuf};
  45. use std::result;
  46. use std::sync::{Arc, Mutex};
  47. use std::thread;
  48. #[derive(Debug, Default)]
  49. pub struct MaildirPath {
  50. pub(super) buf: PathBuf,
  51. pub(super) modified: Option<PathBuf>,
  52. }
  53. impl Deref for MaildirPath {
  54. type Target = PathBuf;
  55. fn deref(&self) -> &PathBuf {
  56. &self.buf
  57. }
  58. }
  59. impl DerefMut for MaildirPath {
  60. fn deref_mut(&mut self) -> &mut PathBuf {
  61. &mut self.buf
  62. }
  63. }
  64. impl From<PathBuf> for MaildirPath {
  65. fn from(val: PathBuf) -> MaildirPath {
  66. MaildirPath {
  67. buf: val,
  68. modified: None,
  69. }
  70. }
  71. }
  72. #[derive(Debug, Default)]
  73. pub struct HashIndex {
  74. index: FnvHashMap<EnvelopeHash, MaildirPath>,
  75. hash: FolderHash,
  76. }
  77. impl Deref for HashIndex {
  78. type Target = FnvHashMap<EnvelopeHash, MaildirPath>;
  79. fn deref(&self) -> &FnvHashMap<EnvelopeHash, MaildirPath> {
  80. &self.index
  81. }
  82. }
  83. impl DerefMut for HashIndex {
  84. fn deref_mut(&mut self) -> &mut FnvHashMap<EnvelopeHash, MaildirPath> {
  85. &mut self.index
  86. }
  87. }
  88. pub type HashIndexes = Arc<Mutex<FnvHashMap<FolderHash, HashIndex>>>;
  89. /// Maildir backend https://cr.yp.to/proto/maildir.html
  90. #[derive(Debug)]
  91. pub struct MaildirType {
  92. name: String,
  93. folders: FnvHashMap<FolderHash, MaildirFolder>,
  94. //folder_index: FnvHashMap<FolderHash, usize>,
  95. hash_indexes: HashIndexes,
  96. path: PathBuf,
  97. }
  98. macro_rules! path_is_new {
  99. ($path:expr) => {
  100. if $path.is_dir() {
  101. false
  102. } else {
  103. let mut iter = $path.components().rev();
  104. iter.next();
  105. iter.next() == Some(Component::Normal(OsStr::new("new")))
  106. }
  107. };
  108. }
  109. #[macro_export]
  110. macro_rules! get_path_hash {
  111. ($path:expr) => {{
  112. let mut path = $path.clone();
  113. if path.is_dir() {
  114. if path.ends_with("cur") | path.ends_with("new") {
  115. path.pop();
  116. }
  117. } else {
  118. path.pop();
  119. path.pop();
  120. };
  121. let mut hasher = DefaultHasher::new();
  122. path.hash(&mut hasher);
  123. hasher.finish()
  124. }};
  125. }
  126. pub(super) fn get_file_hash(file: &Path) -> EnvelopeHash {
  127. /*
  128. let mut buf = Vec::with_capacity(2048);
  129. let mut f = fs::File::open(&file).unwrap_or_else(|_| panic!("Can't open {}", file.display()));
  130. f.read_to_end(&mut buf)
  131. .unwrap_or_else(|_| panic!("Can't read {}", file.display()));
  132. let mut hasher = FnvHasher::default();
  133. hasher.write(&buf);
  134. hasher.finish()
  135. */
  136. let mut hasher = FnvHasher::default();
  137. file.hash(&mut hasher);
  138. hasher.finish()
  139. }
  140. fn move_to_cur(p: PathBuf) -> PathBuf {
  141. let mut new = p.clone();
  142. let file_name = p.to_string_lossy();
  143. let slash_pos = file_name.bytes().rposition(|c| c == b'/').unwrap() + 1;
  144. new.pop();
  145. new.pop();
  146. new.push("cur");
  147. new.push(&file_name[slash_pos..]);
  148. if !file_name.ends_with(":2,") {
  149. new.set_extension(":2,");
  150. }
  151. debug!("moved to cur: {}", new.display());
  152. fs::rename(&p, &new).unwrap();
  153. new
  154. }
  155. impl MailBackend for MaildirType {
  156. fn folders(&self) -> FnvHashMap<FolderHash, Folder> {
  157. self.folders
  158. .iter()
  159. .map(|(h, f)| (*h, f.clone() as Folder))
  160. .collect()
  161. }
  162. fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
  163. self.multicore(4, folder)
  164. }
  165. fn watch(&self, sender: RefreshEventConsumer) -> Result<()> {
  166. let (tx, rx) = channel();
  167. let mut watcher = watcher(tx, Duration::from_secs(2)).unwrap();
  168. let root_path = self.path.to_path_buf();
  169. watcher.watch(&root_path, RecursiveMode::Recursive).unwrap();
  170. let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
  171. debug!("watching {:?}", root_path);
  172. let hash_indexes = self.hash_indexes.clone();
  173. thread::Builder::new()
  174. .name("folder watch".to_string())
  175. .spawn(move || {
  176. // Move `watcher` in the closure's scope so that it doesn't get dropped.
  177. let _watcher = watcher;
  178. loop {
  179. match rx.recv() {
  180. /*
  181. * Event types:
  182. *
  183. * pub enum RefreshEventKind {
  184. * Update(EnvelopeHash, Envelope), // Old hash, new envelope
  185. * Create(Envelope),
  186. * Remove(EnvelopeHash),
  187. * Rescan,
  188. * }
  189. */
  190. Ok(event) => match event {
  191. /* Create */
  192. DebouncedEvent::Create(mut pathbuf) => {
  193. debug!("DebouncedEvent::Create(path = {:?}", pathbuf);
  194. if path_is_new!(pathbuf) {
  195. debug!("path_is_new");
  196. /* This creates a Rename event that we will receive later */
  197. pathbuf = move_to_cur(pathbuf);
  198. }
  199. let folder_hash = get_path_hash!(pathbuf);
  200. let file_name = pathbuf
  201. .as_path()
  202. .strip_prefix(&root_path)
  203. .unwrap()
  204. .to_path_buf();
  205. if let Some(env) = add_path_to_index(
  206. &hash_indexes,
  207. folder_hash,
  208. pathbuf.as_path(),
  209. &cache_dir,
  210. file_name,
  211. ) {
  212. debug!(
  213. "Create event {} {} {}",
  214. env.hash(),
  215. env.subject(),
  216. pathbuf.display()
  217. );
  218. sender.send(RefreshEvent {
  219. hash: folder_hash,
  220. kind: Create(Box::new(env)),
  221. });
  222. }
  223. }
  224. /* Update */
  225. DebouncedEvent::NoticeWrite(pathbuf)
  226. | DebouncedEvent::Write(pathbuf) => {
  227. debug!("DebouncedEvent::Write(path = {:?}", &pathbuf);
  228. let folder_hash = get_path_hash!(pathbuf);
  229. let mut hash_indexes_lock = hash_indexes.lock().unwrap();
  230. let index_lock =
  231. &mut hash_indexes_lock.entry(folder_hash).or_default();
  232. let file_name = pathbuf
  233. .as_path()
  234. .strip_prefix(&root_path)
  235. .unwrap()
  236. .to_path_buf();
  237. /* Linear search in hash_index to find old hash */
  238. let old_hash: EnvelopeHash = {
  239. if let Some((k, v)) =
  240. index_lock.iter_mut().find(|(_, v)| *v.buf == pathbuf)
  241. {
  242. //TODO FIXME This doesn't make sense?
  243. *v = pathbuf.clone().into();
  244. *k
  245. } else {
  246. /* Did we just miss a Create event? In any case, create
  247. * envelope. */
  248. if let Some(env) = add_path_to_index(
  249. &hash_indexes,
  250. folder_hash,
  251. pathbuf.as_path(),
  252. &cache_dir,
  253. file_name,
  254. ) {
  255. sender.send(RefreshEvent {
  256. hash: folder_hash,
  257. kind: Create(Box::new(env)),
  258. });
  259. }
  260. return;
  261. }
  262. };
  263. let new_hash: EnvelopeHash = get_file_hash(pathbuf.as_path());
  264. if index_lock.get_mut(&new_hash).is_none() {
  265. debug!("write notice");
  266. let op = Box::new(MaildirOp::new(
  267. new_hash,
  268. hash_indexes.clone(),
  269. folder_hash,
  270. ));
  271. if let Some(env) = Envelope::from_token(op, new_hash) {
  272. debug!("{}\t{:?}", new_hash, &pathbuf);
  273. debug!(
  274. "hash {}, path: {:?} couldn't be parsed",
  275. new_hash, &pathbuf
  276. );
  277. index_lock.insert(new_hash, pathbuf.into());
  278. /* Send Write notice */
  279. sender.send(RefreshEvent {
  280. hash: folder_hash,
  281. kind: Update(old_hash, Box::new(env)),
  282. });
  283. }
  284. }
  285. }
  286. /* Remove */
  287. DebouncedEvent::NoticeRemove(pathbuf)
  288. | DebouncedEvent::Remove(pathbuf) => {
  289. debug!("DebouncedEvent::Remove(path = {:?}", pathbuf);
  290. let folder_hash = get_path_hash!(pathbuf);
  291. let mut hash_indexes_lock = hash_indexes.lock().unwrap();
  292. let index_lock = hash_indexes_lock.entry(folder_hash).or_default();
  293. let hash: EnvelopeHash = if let Some((k, _)) =
  294. index_lock.iter().find(|(_, v)| *v.buf == pathbuf)
  295. {
  296. *k
  297. } else {
  298. debug!("removed but not contained in index");
  299. continue;
  300. };
  301. if let Some(path) = &index_lock[&hash].modified {
  302. debug!(
  303. "envelope {} has modified path set {}",
  304. hash,
  305. path.display()
  306. );
  307. continue;
  308. }
  309. index_lock.remove(&hash);
  310. sender.send(RefreshEvent {
  311. hash: folder_hash,
  312. kind: Remove(hash),
  313. });
  314. }
  315. /* Envelope hasn't changed */
  316. DebouncedEvent::Rename(src, dest) => {
  317. debug!(
  318. "DebouncedEvent::Rename(src = {:?}, dest = {:?})",
  319. src, dest
  320. );
  321. let folder_hash = get_path_hash!(src);
  322. let old_hash: EnvelopeHash = get_file_hash(src.as_path());
  323. let new_hash: EnvelopeHash = get_file_hash(dest.as_path());
  324. let mut hash_indexes_lock = hash_indexes.lock().unwrap();
  325. let index_lock = hash_indexes_lock.entry(folder_hash).or_default();
  326. if index_lock.contains_key(&old_hash) {
  327. debug!("contains_old_key");
  328. sender.send(RefreshEvent {
  329. hash: get_path_hash!(dest),
  330. kind: Rename(old_hash, new_hash),
  331. });
  332. index_lock.remove(&old_hash);
  333. index_lock.insert(new_hash, dest.into());
  334. continue;
  335. } else if !index_lock.contains_key(&new_hash) {
  336. debug!("not contains_new_key");
  337. let file_name = dest
  338. .as_path()
  339. .strip_prefix(&root_path)
  340. .unwrap()
  341. .to_path_buf();
  342. debug!("filename = {:?}", file_name);
  343. if let Some(env) = add_path_to_index(
  344. &hash_indexes,
  345. folder_hash,
  346. dest.as_path(),
  347. &cache_dir,
  348. file_name,
  349. ) {
  350. debug!(
  351. "Create event {} {} {}",
  352. env.hash(),
  353. env.subject(),
  354. dest.display()
  355. );
  356. sender.send(RefreshEvent {
  357. hash: folder_hash,
  358. kind: Create(Box::new(env)),
  359. });
  360. continue;
  361. } else {
  362. debug!("not valid email");
  363. }
  364. } else {
  365. sender.send(RefreshEvent {
  366. hash: get_path_hash!(dest),
  367. kind: Rename(old_hash, new_hash),
  368. });
  369. debug!("contains_new_key");
  370. }
  371. /* Maybe a re-read should be triggered here just to be safe.
  372. sender.send(RefreshEvent {
  373. hash: get_path_hash!(dest),
  374. kind: Rescan,
  375. });
  376. */
  377. }
  378. /* Trigger rescan of folder */
  379. DebouncedEvent::Rescan => {
  380. /* Actually should rescan all folders */
  381. unreachable!("Unimplemented: rescan of all folders in MaildirType")
  382. }
  383. _ => {}
  384. },
  385. Err(e) => debug!("watch error: {:?}", e),
  386. }
  387. }
  388. })?;
  389. Ok(())
  390. }
  391. fn operation(&self, hash: EnvelopeHash, folder_hash: FolderHash) -> Box<BackendOp> {
  392. Box::new(MaildirOp::new(hash, self.hash_indexes.clone(), folder_hash))
  393. }
  394. fn save(&self, bytes: &[u8], folder: &str) -> Result<()> {
  395. for f in self.folders.values() {
  396. if f.name == folder {
  397. let mut path = f.path.clone();
  398. path.push("cur");
  399. {
  400. let mut rand_buf = [0u8; 16];
  401. let mut f = fs::File::open("/dev/urandom")
  402. .expect("Could not open /dev/urandom for reading");
  403. f.read_exact(&mut rand_buf)
  404. .expect("Could not read from /dev/urandom/");
  405. let mut hostn_buf = String::with_capacity(256);
  406. let mut f = fs::File::open("/etc/hostname")
  407. .expect("Could not open /etc/hostname for reading");
  408. f.read_to_string(&mut hostn_buf)
  409. .expect("Could not read from /etc/hostname");
  410. let timestamp = std::time::SystemTime::now()
  411. .duration_since(std::time::SystemTime::UNIX_EPOCH)
  412. .unwrap()
  413. .as_millis();
  414. path.push(&format!(
  415. "{}.{:x}_{}.{}:2,",
  416. timestamp,
  417. u128::from_be_bytes(rand_buf),
  418. std::process::id(),
  419. hostn_buf.trim()
  420. ));
  421. }
  422. debug!("saving at {}", path.display());
  423. let file = fs::File::create(path).unwrap();
  424. let mut writer = io::BufWriter::new(file);
  425. writer.write_all(bytes).unwrap();
  426. return Ok(());
  427. }
  428. }
  429. Err(MeliError::new(format!(
  430. "'{}' is not a valid folder.",
  431. folder
  432. )))
  433. }
  434. }
  435. impl MaildirType {
  436. pub fn new(f: &AccountSettings) -> Self {
  437. let mut folders: FnvHashMap<FolderHash, MaildirFolder> = Default::default();
  438. fn recurse_folders<P: AsRef<Path>>(
  439. folders: &mut FnvHashMap<FolderHash, MaildirFolder>,
  440. p: P,
  441. ) -> Vec<FolderHash> {
  442. let mut children = Vec::new();
  443. for mut f in fs::read_dir(p).unwrap() {
  444. 'entries: for f in f.iter_mut() {
  445. {
  446. let path = f.path();
  447. if path.ends_with("cur") || path.ends_with("new") || path.ends_with("tmp") {
  448. continue 'entries;
  449. }
  450. if path.is_dir() {
  451. let path_children = std::dbg!(recurse_folders(folders, &path));
  452. if let Ok(f) = MaildirFolder::new(
  453. path.to_str().unwrap().to_string(),
  454. path.file_name().unwrap().to_str().unwrap().to_string(),
  455. None,
  456. path_children,
  457. ) {
  458. f.children
  459. .iter()
  460. .map(|c| folders.get_mut(c).map(|f| f.parent = Some(f.hash)))
  461. .count();
  462. children.push(f.hash);
  463. folders.insert(f.hash, f);
  464. }
  465. }
  466. }
  467. }
  468. }
  469. children
  470. };
  471. let path = PathBuf::from(f.root_folder());
  472. if path.is_dir() {
  473. if let Ok(f) = MaildirFolder::new(
  474. path.to_str().unwrap().to_string(),
  475. path.file_name().unwrap().to_str().unwrap().to_string(),
  476. None,
  477. Vec::with_capacity(0),
  478. ) {
  479. let l: MaildirFolder = f;
  480. folders.insert(l.hash, l);
  481. }
  482. }
  483. if folders.is_empty() {
  484. let children = recurse_folders(&mut folders, &path);
  485. children
  486. .iter()
  487. .map(|c| folders.get_mut(c).map(|f| f.parent = None))
  488. .count();
  489. } else {
  490. let root_hash = *folders.keys().nth(0).unwrap();
  491. let children = recurse_folders(&mut folders, &path);
  492. children
  493. .iter()
  494. .map(|c| folders.get_mut(c).map(|f| f.parent = Some(root_hash)))
  495. .count();
  496. folders.get_mut(&root_hash).map(|f| f.children = children);
  497. }
  498. let hash_indexes = Arc::new(Mutex::new(FnvHashMap::with_capacity_and_hasher(
  499. folders.len(),
  500. Default::default(),
  501. )));
  502. {
  503. let mut hash_indexes = hash_indexes.lock().unwrap();
  504. for &fh in folders.keys() {
  505. hash_indexes.insert(
  506. fh,
  507. HashIndex {
  508. index: FnvHashMap::with_capacity_and_hasher(0, Default::default()),
  509. hash: fh,
  510. },
  511. );
  512. }
  513. }
  514. MaildirType {
  515. name: f.name().to_string(),
  516. folders,
  517. hash_indexes,
  518. path: PathBuf::from(f.root_folder()),
  519. }
  520. }
  521. fn owned_folder_idx(&self, folder: &Folder) -> FolderHash {
  522. *self
  523. .folders
  524. .iter()
  525. .find(|(_, f)| f.hash() == folder.hash())
  526. .unwrap()
  527. .0
  528. }
  529. pub fn multicore(&mut self, cores: usize, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
  530. let mut w = AsyncBuilder::new();
  531. let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
  532. let handle = {
  533. let tx = w.tx();
  534. // TODO: Avoid clone
  535. let folder: &MaildirFolder = &self.folders[&self.owned_folder_idx(folder)];
  536. let folder_hash = folder.hash();
  537. let tx_final = w.tx();
  538. let path: PathBuf = folder.path().into();
  539. let name = format!("parsing {:?}", folder.name());
  540. let root_path = self.path.to_path_buf();
  541. let map = self.hash_indexes.clone();
  542. let closure = move || {
  543. let name = name.clone();
  544. let root_path = root_path.clone();
  545. let map = map.clone();
  546. let tx = tx.clone();
  547. let cache_dir = cache_dir.clone();
  548. let path = path.clone();
  549. let thunk = move || {
  550. let mut path = path.clone();
  551. let cache_dir = cache_dir.clone();
  552. path.push("new");
  553. for d in path.read_dir()? {
  554. if let Ok(p) = d {
  555. move_to_cur(p.path());
  556. }
  557. }
  558. path.pop();
  559. path.push("cur");
  560. let iter = path.read_dir()?;
  561. let count = path.read_dir()?.count();
  562. let mut files: Vec<PathBuf> = Vec::with_capacity(count);
  563. let mut ret = Vec::with_capacity(count);
  564. for e in iter {
  565. let e = e.and_then(|x| {
  566. let path = x.path();
  567. Ok(path)
  568. })?;
  569. files.push(e);
  570. }
  571. let mut threads = Vec::with_capacity(cores);
  572. if !files.is_empty() {
  573. crossbeam::scope(|scope| {
  574. let cache_dir = cache_dir.clone();
  575. let chunk_size = if count / cores > 0 {
  576. count / cores
  577. } else {
  578. count
  579. };
  580. for chunk in files.chunks(chunk_size) {
  581. let cache_dir = cache_dir.clone();
  582. let tx = tx.clone();
  583. let map = map.clone();
  584. let root_path = root_path.clone();
  585. let s = scope.builder().name(name.clone()).spawn(move || {
  586. let len = chunk.len();
  587. let size = if len <= 100 { 100 } else { (len / 100) * 100 };
  588. let mut local_r: Vec<Envelope> =
  589. Vec::with_capacity(chunk.len());
  590. for c in chunk.chunks(size) {
  591. //thread::yield_now();
  592. let map = map.clone();
  593. let len = c.len();
  594. for file in c {
  595. /* Check if we have a cache file with this email's
  596. * filename */
  597. let file_name = PathBuf::from(file)
  598. .strip_prefix(&root_path)
  599. .unwrap()
  600. .to_path_buf();
  601. if let Some(cached) =
  602. cache_dir.find_cache_file(&file_name)
  603. {
  604. /* Cached struct exists, try to load it */
  605. let reader = io::BufReader::new(
  606. fs::File::open(&cached).unwrap(),
  607. );
  608. let result: result::Result<Envelope, _> =
  609. bincode::deserialize_from(reader);
  610. if let Ok(env) = result {
  611. let mut map = map.lock().unwrap();
  612. let map = map.entry(folder_hash).or_default();
  613. let hash = env.hash();
  614. map.insert(hash, file.clone().into());
  615. local_r.push(env);
  616. continue;
  617. }
  618. };
  619. let hash = get_file_hash(file);
  620. {
  621. let mut map = map.lock().unwrap();
  622. let map = map.entry(folder_hash).or_default();
  623. (*map).insert(hash, PathBuf::from(file).into());
  624. }
  625. let op = Box::new(MaildirOp::new(
  626. hash,
  627. map.clone(),
  628. folder_hash,
  629. ));
  630. if let Some(e) = Envelope::from_token(op, hash) {
  631. if let Ok(cached) =
  632. cache_dir.place_cache_file(file_name)
  633. {
  634. /* place result in cache directory */
  635. let f = match fs::File::create(cached) {
  636. Ok(f) => f,
  637. Err(e) => {
  638. panic!("{}", e);
  639. }
  640. };
  641. let writer = io::BufWriter::new(f);
  642. bincode::serialize_into(writer, &e).unwrap();
  643. }
  644. local_r.push(e);
  645. } else {
  646. debug!(
  647. "DEBUG: hash {}, path: {} couldn't be parsed",
  648. hash,
  649. file.as_path().display()
  650. );
  651. continue;
  652. }
  653. }
  654. tx.send(AsyncStatus::ProgressReport(len));
  655. }
  656. local_r
  657. });
  658. threads.push(s.unwrap());
  659. }
  660. });
  661. }
  662. for t in threads {
  663. let mut result = t.join();
  664. ret.append(&mut result);
  665. }
  666. Ok(ret)
  667. };
  668. let result = thunk();
  669. tx_final.send(AsyncStatus::Payload(result));
  670. };
  671. Box::new(closure)
  672. };
  673. w.build(handle)
  674. }
  675. }
  676. fn add_path_to_index(
  677. hash_index: &HashIndexes,
  678. folder_hash: FolderHash,
  679. path: &Path,
  680. cache_dir: &xdg::BaseDirectories,
  681. file_name: PathBuf,
  682. ) -> Option<Envelope> {
  683. let env: Envelope;
  684. debug!("add_path_to_index path {:?} filename{:?}", path, file_name);
  685. let hash = get_file_hash(path);
  686. {
  687. let mut map = hash_index.lock().unwrap();
  688. let map = map.entry(folder_hash).or_default();;
  689. map.insert(hash, path.to_path_buf().into());
  690. debug!(
  691. "inserted {} in {} map, len={}",
  692. hash,
  693. folder_hash,
  694. map.len()
  695. );
  696. }
  697. let op = Box::new(MaildirOp::new(hash, hash_index.clone(), folder_hash));
  698. if let Some(e) = Envelope::from_token(op, hash) {
  699. debug!("add_path_to_index gen {}\t{}", hash, file_name.display());
  700. if let Ok(cached) = cache_dir.place_cache_file(file_name) {
  701. debug!("putting in cache");
  702. /* place result in cache directory */
  703. let f = match fs::File::create(cached) {
  704. Ok(f) => f,
  705. Err(e) => {
  706. panic!("{}", e);
  707. }
  708. };
  709. let writer = io::BufWriter::new(f);
  710. bincode::serialize_into(writer, &e).unwrap();
  711. }
  712. env = e;
  713. } else {
  714. debug!(
  715. "DEBUG: hash {}, path: {} couldn't be parsed in `add_path_to_index`",
  716. hash,
  717. path.display()
  718. );
  719. return None;
  720. }
  721. Some(env)
  722. }