2018-07-13 18:38:57 +03:00
/*
* meli - mailbox module .
*
* Copyright 2017 Manos Pitsidianakis
*
* This file is part of meli .
*
* meli is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* meli is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with meli . If not , see < http ://www.gnu.org/licenses/>.
* /
2021-01-10 12:34:44 +02:00
//! # Mbox formats
//!
//! ## Resources
//!
//! - [0] <https://web.archive.org/web/20160812091518/https://jdebp.eu./FGA/mail-mbox-formats.html>
//! - [1] <https://wiki2.dovecot.org/MailboxFormat/mbox>
//! - [2] <https://manpages.debian.org/buster/mutt/mbox.5.en.html>
//!
//! ## `mbox` format
//! `mbox` describes a family of incompatible legacy formats.
//!
//! "All of the 'mbox' formats store all of the messages in the mailbox in a single file. Delivery appends new messages to the end of the file." [0]
//!
//! "Each message is preceded by a From_ line and followed by a blank line. A From_ line is a line that begins with the five characters 'F', 'r', 'o', 'm', and ' '." [0]
//!
//! ## `From ` / postmark line
//!
//! "An mbox is a text file containing an arbitrary number of e-mail messages. Each message
//! consists of a postmark, followed by an e-mail message formatted according to RFC822, RFC2822.
//! The file format is line-oriented. Lines are separated by line feed characters (ASCII 10).
//!
//! "A postmark line consists of the four characters 'From', followed by a space character,
//! followed by the message's envelope sender address, followed by whitespace, and followed by a
//! time stamp. This line is often called From_ line.
//!
//! "The sender address is expected to be addr-spec as defined in RFC2822 3.4.1. The date is expected
//! to be date-time as output by asctime(3). For compatibility reasons with legacy software,
//! two-digit years greater than or equal to 70 should be interpreted as the years 1970+, while
//! two-digit years less than 70 should be interpreted as the years 2000-2069. Software reading
//! files in this format should also be prepared to accept non-numeric timezone information such as
//! 'CET DST' for Central European Time, daylight saving time.
//!
//! "Example:
//!
//!```text
//!From example@example.com Fri Jun 23 02:56:55 2000
//!```
//!
//! "In order to avoid misinterpretation of lines in message bodies which begin with the four
//! characters 'From', followed by a space character, the mail delivery agent must quote
//! any occurrence of 'From ' at the start of a body line." [2]
//!
//! ## Metadata
//!
//! `melib` recognizes the CClient (a [Pine client API](https://web.archive.org/web/20050203003235/http://www.washington.edu/imap/)) convention for metadata in `mbox` format:
//!
//! - `Status`: R (Seen) and O (non-Recent) flags
//! - `X-Status`: A (Answered), F (Flagged), T (Draft) and D (Deleted) flags
//! - `X-Keywords`: Message’ s keywords
//!
//! ## Parsing an mbox file
//!
//! ```
//! # use melib::{Result, Envelope, EnvelopeHash, mbox::*};
//! # use std::collections::HashMap;
//! # use std::sync::{Arc, Mutex};
//! let file_contents = vec![]; // Replace with actual mbox file contents
//! let index: Arc<Mutex<HashMap<EnvelopeHash, (Offset, Length)>>> = Arc::new(Mutex::new(HashMap::default()));
//! let mut message_iter = MessageIterator {
//! index: index.clone(),
//! input: &file_contents.as_slice(),
//! offset: 0,
//! file_offset: 0,
//! format: Some(MboxFormat::MboxCl2),
//! };
//! let envelopes: Result<Vec<Envelope>> = message_iter.collect();
//! ```
//!
//! ## Writing / Appending an mbox file
//!
//! ```no_run
//! # use melib::mbox::*;
//! # use std::io::Write;
//! let mbox_1: &[u8] = br#"From: <a@b.c>\n\nHello World"#;
//! let mbox_2: &[u8] = br#"From: <d@e.f>\n\nHello World #2"#;
//! let mut file = std::io::BufWriter::new(std::fs::File::create(&"out.mbox")?);
//! let format = MboxFormat::MboxCl2;
//! format.append(
//! &mut file,
//! mbox_1,
//! None, // Envelope From
//! Some(melib::datetime::now()), // Delivered date
//! true,
//! false,
//! )?;
//! format.append(
//! &mut file,
//! mbox_2,
//! None,
//! Some(melib::datetime::now()),
//! false,
//! false,
//! )?;
//! file.flush()?;
//! # Ok::<(), melib::MeliError>(())
//! ```
2018-08-07 15:01:15 +03:00
2020-06-28 19:16:13 +03:00
use crate ::backends ::* ;
2020-08-10 14:24:21 +03:00
use crate ::collection ::Collection ;
2019-07-11 11:44:27 +03:00
use crate ::conf ::AccountSettings ;
use crate ::email ::parser ::BytesExt ;
use crate ::email ::* ;
use crate ::error ::{ MeliError , Result } ;
2020-02-06 01:49:18 +02:00
use crate ::get_path_hash ;
2019-09-26 18:27:13 +03:00
use crate ::shellexpand ::ShellExpandTrait ;
2020-06-20 11:52:05 +03:00
use nom ::bytes ::complete ::tag ;
use nom ::character ::complete ::digit1 ;
use nom ::combinator ::map_res ;
2020-06-06 19:38:20 +03:00
use nom ::{ self , error ::ErrorKind , IResult } ;
2020-06-20 11:52:05 +03:00
2019-07-11 11:44:27 +03:00
extern crate notify ;
use self ::notify ::{ watcher , DebouncedEvent , RecursiveMode , Watcher } ;
2020-05-10 22:05:04 +03:00
use std ::collections ::hash_map ::{ DefaultHasher , HashMap } ;
2019-07-11 11:44:27 +03:00
use std ::fs ::File ;
2020-05-10 22:05:04 +03:00
use std ::hash ::Hasher ;
2020-10-20 22:36:57 +03:00
use std ::io ::{ BufReader , Read } ;
2019-07-11 11:44:27 +03:00
use std ::os ::unix ::io ::AsRawFd ;
use std ::path ::{ Path , PathBuf } ;
2020-06-20 11:52:05 +03:00
use std ::str ::FromStr ;
2019-07-11 11:44:27 +03:00
use std ::sync ::mpsc ::channel ;
2019-12-17 14:12:41 +02:00
use std ::sync ::{ Arc , Mutex , RwLock } ;
2019-07-11 11:44:27 +03:00
2021-01-09 16:37:29 +02:00
pub mod write ;
2021-01-10 12:34:44 +02:00
pub type Offset = usize ;
pub type Length = usize ;
2020-01-30 04:18:45 +02:00
2020-12-29 21:11:52 +02:00
#[ cfg(target_os = " linux " ) ]
2019-07-11 11:44:27 +03:00
const F_OFD_SETLKW : libc ::c_int = 38 ;
// Open file description locking
// # man fcntl
2020-12-29 21:11:52 +02:00
fn get_rw_lock_blocking ( f : & File , path : & Path ) -> Result < ( ) > {
2019-07-11 11:44:27 +03:00
let fd : libc ::c_int = f . as_raw_fd ( ) ;
let mut flock : libc ::flock = libc ::flock {
l_type : libc ::F_WRLCK as libc ::c_short ,
l_whence : libc ::SEEK_SET as libc ::c_short ,
l_start : 0 ,
l_len : 0 , /* "Specifying 0 for l_len has the special meaning: lock all bytes starting at the location
specified by l_whence and l_start through to the end of file , no matter how large the file grows . " */
l_pid : 0 , /* "By contrast with traditional record locks, the l_pid field of that structure must be set to zero when using the commands described below." */
2020-12-29 21:12:38 +02:00
#[ cfg(target_os = " freebsd " ) ]
l_sysid : 0 ,
2019-07-11 11:44:27 +03:00
} ;
let ptr : * mut libc ::flock = & mut flock ;
2020-12-29 21:11:52 +02:00
#[ cfg(not(target_os = " linux " )) ]
let ret_val = unsafe { libc ::fcntl ( fd , libc ::F_SETLKW , ptr as * mut libc ::c_void ) } ;
#[ cfg(target_os = " linux " ) ]
2019-07-11 11:44:27 +03:00
let ret_val = unsafe { libc ::fcntl ( fd , F_OFD_SETLKW , ptr as * mut libc ::c_void ) } ;
2020-12-29 21:11:52 +02:00
if ret_val = = - 1 {
let err = nix ::errno ::Errno ::from_i32 ( nix ::errno ::errno ( ) ) ;
return Err ( MeliError ::new ( format! (
" Could not lock {}: fcntl() returned {} " ,
path . display ( ) ,
err . desc ( )
) ) ) ;
}
Ok ( ( ) )
2019-07-11 11:44:27 +03:00
}
#[ derive(Debug) ]
2020-02-26 10:54:10 +02:00
struct MboxMailbox {
hash : MailboxHash ,
2019-07-11 11:44:27 +03:00
name : String ,
path : PathBuf ,
2020-06-20 14:49:02 +03:00
fs_path : PathBuf ,
2019-07-11 11:44:27 +03:00
content : Vec < u8 > ,
2020-02-26 10:54:10 +02:00
children : Vec < MailboxHash > ,
parent : Option < MailboxHash > ,
2019-12-17 14:12:41 +02:00
usage : Arc < RwLock < SpecialUsageMailbox > > ,
is_subscribed : bool ,
2020-02-26 10:54:10 +02:00
permissions : MailboxPermissions ,
2019-12-17 14:12:41 +02:00
pub total : Arc < Mutex < usize > > ,
pub unseen : Arc < Mutex < usize > > ,
2020-06-20 14:49:02 +03:00
index : Arc < Mutex < HashMap < EnvelopeHash , ( Offset , Length ) > > > ,
2019-07-11 11:44:27 +03:00
}
2020-02-26 10:54:10 +02:00
impl BackendMailbox for MboxMailbox {
fn hash ( & self ) -> MailboxHash {
2019-07-11 11:44:27 +03:00
self . hash
}
fn name ( & self ) -> & str {
self . name . as_str ( )
}
fn path ( & self ) -> & str {
/* We know it's valid UTF-8 because we supplied it */
self . path . to_str ( ) . unwrap ( )
}
fn change_name ( & mut self , s : & str ) {
self . name = s . to_string ( ) ;
}
2020-02-26 10:54:10 +02:00
fn clone ( & self ) -> Mailbox {
Box ::new ( MboxMailbox {
2019-07-11 11:44:27 +03:00
hash : self . hash ,
name : self . name . clone ( ) ,
path : self . path . clone ( ) ,
2020-06-20 14:49:02 +03:00
fs_path : self . fs_path . clone ( ) ,
2019-07-11 11:44:27 +03:00
content : self . content . clone ( ) ,
children : self . children . clone ( ) ,
2019-12-17 14:12:41 +02:00
usage : self . usage . clone ( ) ,
is_subscribed : self . is_subscribed ,
2019-07-11 11:44:27 +03:00
parent : self . parent ,
2019-11-11 00:47:23 +02:00
permissions : self . permissions ,
2019-12-17 14:12:41 +02:00
unseen : self . unseen . clone ( ) ,
total : self . total . clone ( ) ,
2020-06-20 14:49:02 +03:00
index : self . index . clone ( ) ,
2019-07-11 11:44:27 +03:00
} )
}
2020-02-26 10:54:10 +02:00
fn children ( & self ) -> & [ MailboxHash ] {
2019-07-11 11:44:27 +03:00
& self . children
}
2020-02-26 10:54:10 +02:00
fn parent ( & self ) -> Option < MailboxHash > {
2019-07-11 11:44:27 +03:00
self . parent
}
2019-11-11 00:47:23 +02:00
2019-12-11 00:15:36 +02:00
fn special_usage ( & self ) -> SpecialUsageMailbox {
2019-12-17 14:12:41 +02:00
* self . usage . read ( ) . unwrap ( )
2019-12-11 00:15:36 +02:00
}
2020-02-26 10:54:10 +02:00
fn permissions ( & self ) -> MailboxPermissions {
2019-11-11 00:47:23 +02:00
self . permissions
}
2019-12-17 14:12:41 +02:00
fn is_subscribed ( & self ) -> bool {
self . is_subscribed
}
fn set_is_subscribed ( & mut self , new_val : bool ) -> Result < ( ) > {
self . is_subscribed = new_val ;
Ok ( ( ) )
}
fn set_special_usage ( & mut self , new_val : SpecialUsageMailbox ) -> Result < ( ) > {
* self . usage . write ( ) ? = new_val ;
Ok ( ( ) )
}
fn count ( & self ) -> Result < ( usize , usize ) > {
Ok ( ( * self . unseen . lock ( ) ? , * self . total . lock ( ) ? ) )
}
2019-07-11 11:44:27 +03:00
}
2018-07-13 18:38:57 +03:00
/// `BackendOp` implementor for Mbox
2019-07-11 11:44:27 +03:00
#[ derive(Debug, Default) ]
pub struct MboxOp {
hash : EnvelopeHash ,
path : PathBuf ,
offset : Offset ,
length : Length ,
2020-10-20 22:36:57 +03:00
slice : std ::cell ::RefCell < Option < Vec < u8 > > > ,
2019-07-11 11:44:27 +03:00
}
2018-07-13 18:38:57 +03:00
impl MboxOp {
2019-07-11 11:44:27 +03:00
pub fn new ( hash : EnvelopeHash , path : & Path , offset : Offset , length : Length ) -> Self {
MboxOp {
hash ,
path : path . to_path_buf ( ) ,
2020-10-20 22:36:57 +03:00
slice : std ::cell ::RefCell ::new ( None ) ,
2019-07-11 11:44:27 +03:00
offset ,
length ,
}
2018-07-13 18:38:57 +03:00
}
}
impl BackendOp for MboxOp {
2020-07-04 17:38:57 +03:00
fn as_bytes ( & mut self ) -> ResultFuture < Vec < u8 > > {
2020-10-20 22:36:57 +03:00
if self . slice . get_mut ( ) . is_none ( ) {
let file = std ::fs ::OpenOptions ::new ( )
. read ( true )
2020-12-29 21:11:52 +02:00
. write ( true )
2020-10-20 22:36:57 +03:00
. open ( & self . path ) ? ;
2020-12-29 21:11:52 +02:00
get_rw_lock_blocking ( & file , & self . path ) ? ;
2020-10-20 22:36:57 +03:00
let mut buf_reader = BufReader ::new ( file ) ;
let mut contents = Vec ::new ( ) ;
buf_reader . read_to_end ( & mut contents ) ? ;
* self . slice . get_mut ( ) = Some ( contents ) ;
2019-07-11 11:44:27 +03:00
}
2020-10-20 22:36:57 +03:00
let ret = Ok ( self . slice . get_mut ( ) . as_ref ( ) . unwrap ( ) . as_slice ( )
[ self . offset .. self . offset + self . length ]
. to_vec ( ) ) ;
2020-07-04 17:38:57 +03:00
Ok ( Box ::pin ( async move { ret } ) )
2018-07-13 18:38:57 +03:00
}
2019-07-11 11:44:27 +03:00
2020-06-30 19:36:02 +03:00
fn fetch_flags ( & self ) -> ResultFuture < Flag > {
2019-07-11 11:44:27 +03:00
let mut flags = Flag ::empty ( ) ;
2020-10-20 22:36:57 +03:00
if self . slice . borrow ( ) . is_none ( ) {
let file = std ::fs ::OpenOptions ::new ( )
. read ( true )
2020-12-29 21:11:52 +02:00
. write ( true )
2020-10-20 22:36:57 +03:00
. open ( & self . path ) ? ;
2020-12-29 21:11:52 +02:00
get_rw_lock_blocking ( & file , & self . path ) ? ;
2020-10-20 22:36:57 +03:00
let mut buf_reader = BufReader ::new ( file ) ;
let mut contents = Vec ::new ( ) ;
buf_reader . read_to_end ( & mut contents ) ? ;
* self . slice . borrow_mut ( ) = Some ( contents ) ;
}
let slice_ref = self . slice . borrow ( ) ;
let ( _ , headers ) = parser ::headers ::headers_raw ( slice_ref . as_ref ( ) . unwrap ( ) . as_slice ( ) ) ? ;
2020-06-28 16:53:52 +03:00
if let Some ( start ) = headers . find ( b " Status: " ) {
if let Some ( end ) = headers [ start .. ] . find ( b " \n " ) {
let start = start + b " Status: " . len ( ) ;
let status = headers [ start .. start + end ] . trim ( ) ;
if status . contains ( & b 'F' ) {
flags . set ( Flag ::FLAGGED , true ) ;
}
if status . contains ( & b 'A' ) {
flags . set ( Flag ::REPLIED , true ) ;
}
if status . contains ( & b 'R' ) {
flags . set ( Flag ::SEEN , true ) ;
}
if status . contains ( & b 'D' ) {
flags . set ( Flag ::TRASHED , true ) ;
}
if status . contains ( & b 'T' ) {
flags . set ( Flag ::DRAFT , true ) ;
2019-07-11 11:44:27 +03:00
}
}
2020-06-28 16:53:52 +03:00
}
if let Some ( start ) = headers . find ( b " X-Status: " ) {
let start = start + b " X-Status: " . len ( ) ;
if let Some ( end ) = headers [ start .. ] . find ( b " \n " ) {
let status = headers [ start .. start + end ] . trim ( ) ;
if status . contains ( & b 'F' ) {
flags . set ( Flag ::FLAGGED , true ) ;
}
if status . contains ( & b 'A' ) {
flags . set ( Flag ::REPLIED , true ) ;
}
if status . contains ( & b 'R' ) {
flags . set ( Flag ::SEEN , true ) ;
}
if status . contains ( & b 'D' ) {
flags . set ( Flag ::TRASHED , true ) ;
}
if status . contains ( & b 'T' ) {
flags . set ( Flag ::DRAFT , true ) ;
2019-07-11 11:44:27 +03:00
}
}
}
2020-06-30 19:36:02 +03:00
Ok ( Box ::pin ( async move { Ok ( flags ) } ) )
2018-07-13 18:38:57 +03:00
}
}
2021-01-10 12:34:44 +02:00
/// Choose between "mboxo", "mboxrd", "mboxcl", "mboxcl2". For new mailboxes, prefer "mboxcl2"
/// which does not alter the mail body.
2020-06-20 11:52:05 +03:00
#[ derive(Debug, Clone, Copy) ]
2021-01-08 23:45:58 +02:00
pub enum MboxFormat {
2020-06-20 11:52:05 +03:00
MboxO ,
MboxRd ,
MboxCl ,
MboxCl2 ,
}
2021-01-08 23:45:58 +02:00
impl Default for MboxFormat {
2020-06-20 11:52:05 +03:00
fn default ( ) -> Self {
Self ::MboxCl2
2019-07-11 11:44:27 +03:00
}
2020-06-20 11:52:05 +03:00
}
macro_rules ! find_From__line {
( $input :expr ) = > { {
//debug!("find_From__line invocation");
let input = $input ;
let mut ptr = 0 ;
let mut found = None ;
while ptr < input . len ( ) {
// Find next From_ candidate line.
const TAG : & 'static [ u8 ] = b " \n \n From " ;
if let Some ( end ) = input [ ptr .. ] . find ( TAG ) {
// This candidate is a valid From_ if it ends in a new line and the next line is
// a header.
if let Some ( line_end ) = input [ ptr + end + TAG . len ( ) .. ] . find ( b " \n " ) {
if crate ::email ::parser ::headers ::header (
& input [ ptr + end + TAG . len ( ) + line_end + 1 .. ] ,
)
. is_ok ( )
{
found = Some ( ptr + end ) ;
break ;
} else {
/* Ignore invalid From_ line. */
ptr + = end + TAG . len ( ) + line_end ;
2019-07-11 11:44:27 +03:00
}
2020-06-20 11:52:05 +03:00
} else {
/* Ignore invalid From_ line. */
ptr + = end + TAG . len ( ) ;
}
} else {
found = Some ( input . len ( ) ) ;
break ;
}
}
found
} } ;
}
2021-01-08 23:45:58 +02:00
impl MboxFormat {
2021-01-09 16:37:29 +02:00
pub fn parse < ' i > ( & self , input : & ' i [ u8 ] ) -> IResult < & ' i [ u8 ] , Envelope > {
2020-06-20 11:52:05 +03:00
let orig_input = input ;
let mut input = input ;
match self {
Self ::MboxO = > {
let next_offset : Option < ( usize , usize ) > = find_From__line! ( input )
2020-08-25 16:39:12 +03:00
. and_then ( | end | input . find ( b " \n " ) . map ( | start | ( start + 1 , end ) ) ) ;
2020-06-20 11:52:05 +03:00
if let Some ( ( start , len ) ) = next_offset {
match Envelope ::from_bytes ( & input [ start .. len ] , None ) {
Ok ( mut env ) = > {
let mut flags = Flag ::empty ( ) ;
if env . other_headers ( ) . contains_key ( " Status " ) {
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'F' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::FLAGGED , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'A' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::REPLIED , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'R' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::SEEN , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'D' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::TRASHED , true ) ;
}
}
if env . other_headers ( ) . contains_key ( " X-Status " ) {
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'F' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::FLAGGED , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'A' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::REPLIED , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'R' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::SEEN , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'D' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::TRASHED , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'T' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::DRAFT , true ) ;
}
}
env . set_flags ( flags ) ;
if len = = input . len ( ) {
Ok ( ( & [ ] , env ) )
} else {
input = & input [ len + 2 .. ] ;
Ok ( ( input , env ) )
}
2019-07-11 11:44:27 +03:00
}
2020-06-20 11:52:05 +03:00
Err ( err ) = > {
debug! ( " Could not parse mail {:?} " , err ) ;
Err ( nom ::Err ::Error ( ( input , ErrorKind ::Tag ) ) )
2019-07-11 11:44:27 +03:00
}
2020-06-20 11:52:05 +03:00
}
} else {
let start : Offset = input . find ( b " \n " ) . map ( | v | v + 1 ) . unwrap_or ( 0 ) ;
match Envelope ::from_bytes ( & input [ start .. ] , None ) {
Ok ( mut env ) = > {
let mut flags = Flag ::empty ( ) ;
if env . other_headers ( ) . contains_key ( " Status " ) {
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'F' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::FLAGGED , true ) ;
}
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'A' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::REPLIED , true ) ;
}
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'R' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::SEEN , true ) ;
}
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'D' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::TRASHED , true ) ;
}
}
if env . other_headers ( ) . contains_key ( " X-Status " ) {
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'F' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::FLAGGED , true ) ;
}
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'A' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::REPLIED , true ) ;
}
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'R' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::SEEN , true ) ;
}
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'D' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::TRASHED , true ) ;
}
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'T' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::DRAFT , true ) ;
}
}
env . set_flags ( flags ) ;
Ok ( ( & [ ] , env ) )
2019-07-11 11:44:27 +03:00
}
2020-06-20 11:52:05 +03:00
Err ( err ) = > {
debug! ( " Could not parse mail at {:?} " , err ) ;
Err ( nom ::Err ::Error ( ( input , ErrorKind ::Tag ) ) )
2019-07-11 11:44:27 +03:00
}
}
}
}
2020-06-20 11:52:05 +03:00
Self ::MboxRd = > {
let next_offset : Option < ( usize , usize ) > = find_From__line! ( input )
2020-08-25 16:39:12 +03:00
. and_then ( | end | input . find ( b " \n " ) . map ( | start | ( start + 1 , end ) ) ) ;
2020-06-20 11:52:05 +03:00
if let Some ( ( start , len ) ) = next_offset {
match Envelope ::from_bytes ( & input [ start .. len ] , None ) {
Ok ( mut env ) = > {
let mut flags = Flag ::empty ( ) ;
if env . other_headers ( ) . contains_key ( " Status " ) {
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'F' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::FLAGGED , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'A' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::REPLIED , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'R' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::SEEN , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'D' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::TRASHED , true ) ;
}
}
if env . other_headers ( ) . contains_key ( " X-Status " ) {
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'F' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::FLAGGED , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'A' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::REPLIED , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'R' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::SEEN , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'D' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::TRASHED , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'T' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::DRAFT , true ) ;
}
}
env . set_flags ( flags ) ;
if len = = input . len ( ) {
Ok ( ( & [ ] , env ) )
} else {
input = & input [ len + 2 .. ] ;
Ok ( ( input , env ) )
}
2019-07-11 11:44:27 +03:00
}
2020-06-20 11:52:05 +03:00
Err ( err ) = > {
debug! ( " Could not parse mail {:?} " , err ) ;
Err ( nom ::Err ::Error ( ( input , ErrorKind ::Tag ) ) )
2019-07-11 11:44:27 +03:00
}
}
2020-06-20 11:52:05 +03:00
} else {
let start : Offset = input . find ( b " \n " ) . map ( | v | v + 1 ) . unwrap_or ( 0 ) ;
match Envelope ::from_bytes ( & input [ start .. ] , None ) {
Ok ( mut env ) = > {
let mut flags = Flag ::empty ( ) ;
if env . other_headers ( ) . contains_key ( " Status " ) {
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'F' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::FLAGGED , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'A' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::REPLIED , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'R' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::SEEN , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'D' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::TRASHED , true ) ;
}
}
if env . other_headers ( ) . contains_key ( " X-Status " ) {
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'F' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::FLAGGED , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'A' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::REPLIED , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'R' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::SEEN , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'D' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::TRASHED , true ) ;
}
2020-07-05 15:28:55 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'T' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::DRAFT , true ) ;
}
}
env . set_flags ( flags ) ;
Ok ( ( & [ ] , env ) )
2019-07-11 11:44:27 +03:00
}
2020-06-20 11:52:05 +03:00
Err ( err ) = > {
debug! ( " Could not parse mail {:?} " , err ) ;
Err ( nom ::Err ::Error ( ( input , ErrorKind ::Tag ) ) )
2019-07-11 11:44:27 +03:00
}
2020-06-20 11:52:05 +03:00
}
}
}
Self ::MboxCl | Self ::MboxCl2 = > {
let start : Offset = input . find ( b " \n " ) . map ( | v | v + 1 ) . unwrap_or ( 0 ) ;
input = & input [ start .. ] ;
let headers_end : usize = input . find ( b " \n \n " ) . unwrap_or ( input . len ( ) ) ;
let content_length = if let Some ( v ) = input [ .. headers_end ] . find ( b " Content-Length: " )
{
v
} else {
// Is not MboxCl{,2}
return Self ::MboxRd . parse ( orig_input ) ;
} ;
let ( _input , _ ) = if let Ok ( s ) = tag ::< _ , & [ u8 ] , ( & [ u8 ] , nom ::error ::ErrorKind ) > (
" Content-Length: " ,
) ( & input [ content_length .. ] )
{
s
} else {
return Self ::MboxRd . parse ( orig_input ) ;
} ;
let ( _input , bytes ) = if let Ok ( s ) =
map_res ::< & [ u8 ] , _ , _ , ( & [ u8 ] , nom ::error ::ErrorKind ) , _ , _ , _ > (
digit1 ,
| s : & [ u8 ] | String ::from_utf8_lossy ( s ) . parse ::< usize > ( ) ,
) ( _input . ltrim ( ) )
{
s
} else {
return Self ::MboxRd . parse ( orig_input ) ;
} ;
match Envelope ::from_bytes ( & input [ .. headers_end + bytes ] , None ) {
Ok ( mut env ) = > {
let mut flags = Flag ::empty ( ) ;
if env . other_headers ( ) . contains_key ( " Status " ) {
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'F' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::FLAGGED , true ) ;
}
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'A' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::REPLIED , true ) ;
}
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'R' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::SEEN , true ) ;
}
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " Status " ] . contains ( 'D' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::TRASHED , true ) ;
}
2019-07-11 11:44:27 +03:00
}
2020-06-20 11:52:05 +03:00
if env . other_headers ( ) . contains_key ( " X-Status " ) {
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'F' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::FLAGGED , true ) ;
}
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'A' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::REPLIED , true ) ;
}
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'R' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::SEEN , true ) ;
}
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'D' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::TRASHED , true ) ;
}
2020-08-25 16:39:12 +03:00
if env . other_headers ( ) [ " X-Status " ] . contains ( 'T' ) {
2020-06-20 11:52:05 +03:00
flags . set ( Flag ::DRAFT , true ) ;
}
2019-07-11 11:44:27 +03:00
}
2020-06-20 11:52:05 +03:00
env . set_flags ( flags ) ;
if headers_end + 2 + bytes > = input . len ( ) {
Ok ( ( & [ ] , env ) )
} else {
input = & input [ headers_end + 3 + bytes .. ] ;
Ok ( ( input , env ) )
2019-07-11 11:44:27 +03:00
}
}
2020-07-05 15:28:55 +03:00
Err ( _err ) = > Self ::MboxRd . parse ( orig_input ) ,
2019-07-11 11:44:27 +03:00
}
}
}
}
2020-06-20 11:52:05 +03:00
}
pub fn mbox_parse (
index : Arc < Mutex < HashMap < EnvelopeHash , ( Offset , Length ) > > > ,
input : & [ u8 ] ,
file_offset : usize ,
2021-01-08 23:45:58 +02:00
format : Option < MboxFormat > ,
2020-06-20 11:52:05 +03:00
) -> IResult < & [ u8 ] , Vec < Envelope > > {
if input . is_empty ( ) {
return Err ( nom ::Err ::Error ( ( input , ErrorKind ::Tag ) ) ) ;
}
let mut offset = 0 ;
let mut index = index . lock ( ) . unwrap ( ) ;
let mut envelopes = Vec ::with_capacity ( 32 ) ;
2021-01-08 23:45:58 +02:00
let format = format . unwrap_or ( MboxFormat ::MboxCl2 ) ;
2020-06-20 11:52:05 +03:00
while ! input [ offset + file_offset .. ] . is_empty ( ) {
2021-01-08 23:45:58 +02:00
let ( next_input , env ) = match format . parse ( & input [ offset + file_offset .. ] ) {
2020-06-20 11:52:05 +03:00
Ok ( v ) = > v ,
Err ( e ) = > {
// Try to recover from this error by finding a new candidate From_ line
if let Some ( next_offset ) = find_From__line! ( & input [ offset + file_offset .. ] ) {
offset + = next_offset ;
if offset ! = input . len ( ) {
// If we are not at EOF, we will be at this point
// "\n\nFrom ..."
// ↑
// So, skip those two newlines.
offset + = 2 ;
}
} else {
2020-07-05 15:28:55 +03:00
return Err ( e ) ;
2020-06-20 11:52:05 +03:00
}
continue ;
}
} ;
let start : Offset = input [ offset + file_offset .. ]
. find ( b " \n " )
. map ( | v | v + 1 )
. unwrap_or ( 0 ) ;
let len = input . len ( ) - next_input . len ( ) - offset - file_offset - start ;
index . insert ( env . hash ( ) , ( offset + file_offset + start , len ) ) ;
offset + = len + start ;
envelopes . push ( env ) ;
}
2020-07-05 15:28:55 +03:00
Ok ( ( & [ ] , envelopes ) )
2019-07-11 11:44:27 +03:00
}
2021-01-09 16:37:29 +02:00
pub struct MessageIterator < ' a > {
pub index : Arc < Mutex < HashMap < EnvelopeHash , ( Offset , Length ) > > > ,
pub input : & ' a [ u8 ] ,
pub file_offset : usize ,
pub offset : usize ,
pub format : Option < MboxFormat > ,
2020-07-16 17:59:27 +03:00
}
impl < ' a > Iterator for MessageIterator < ' a > {
type Item = Result < Envelope > ;
fn next ( & mut self ) -> Option < Self ::Item > {
if self . input . is_empty ( ) {
return None ;
}
let mut index = self . index . lock ( ) . unwrap ( ) ;
2021-01-08 23:45:58 +02:00
let format = self . format . unwrap_or ( MboxFormat ::MboxCl2 ) ;
2020-07-16 17:59:27 +03:00
while ! self . input [ self . offset + self . file_offset .. ] . is_empty ( ) {
let ( next_input , env ) =
2021-01-08 23:45:58 +02:00
match format . parse ( & self . input [ self . offset + self . file_offset .. ] ) {
2020-07-16 17:59:27 +03:00
Ok ( v ) = > v ,
Err ( e ) = > {
// Try to recover from this error by finding a new candidate From_ line
if let Some ( next_offset ) =
find_From__line! ( & self . input [ self . offset + self . file_offset .. ] )
{
self . offset + = next_offset ;
if self . offset ! = self . input . len ( ) {
// If we are not at EOF, we will be at this point
// "\n\nFrom ..."
// ↑
// So, skip those two newlines.
self . offset + = 2 ;
}
} else {
self . input = b " " ;
return Some ( Err ( e . into ( ) ) ) ;
}
continue ;
}
} ;
let start : Offset = self . input [ self . offset + self . file_offset .. ]
. find ( b " \n " )
. map ( | v | v + 1 )
. unwrap_or ( 0 ) ;
let len = self . input . len ( ) - next_input . len ( ) - self . offset - self . file_offset - start ;
index . insert ( env . hash ( ) , ( self . offset + self . file_offset + start , len ) ) ;
self . offset + = len + start ;
return Some ( Ok ( env ) ) ;
}
None
}
}
2018-07-27 21:37:56 +03:00
/// Mbox backend
2020-08-20 01:55:24 +03:00
#[ derive(Debug) ]
2019-07-11 11:44:27 +03:00
pub struct MboxType {
2020-05-10 22:05:04 +03:00
account_name : String ,
2019-07-11 11:44:27 +03:00
path : PathBuf ,
2020-08-10 14:24:21 +03:00
collection : Collection ,
2020-06-20 14:49:02 +03:00
mailbox_index : Arc < Mutex < HashMap < EnvelopeHash , MailboxHash > > > ,
2020-05-10 21:14:49 +03:00
mailboxes : Arc < Mutex < HashMap < MailboxHash , MboxMailbox > > > ,
2021-01-08 23:45:58 +02:00
prefer_mbox_type : Option < MboxFormat > ,
2020-08-20 01:55:24 +03:00
event_consumer : BackendEventConsumer ,
2019-07-11 11:44:27 +03:00
}
2018-07-13 18:38:57 +03:00
impl MailBackend for MboxType {
2020-07-25 17:53:04 +03:00
fn capabilities ( & self ) -> MailBackendCapabilities {
const CAPABILITIES : MailBackendCapabilities = MailBackendCapabilities {
is_async : false ,
is_remote : false ,
supports_search : false ,
2020-08-01 12:36:47 +03:00
extensions : None ,
2020-07-25 17:53:04 +03:00
supports_tags : false ,
2020-08-09 14:56:34 +03:00
supports_submission : false ,
2020-07-25 17:53:04 +03:00
} ;
CAPABILITIES
2020-07-17 00:01:35 +03:00
}
2020-08-20 17:37:19 +03:00
fn is_online ( & self ) -> ResultFuture < ( ) > {
Ok ( Box ::pin ( async { Ok ( ( ) ) } ) )
2019-10-24 20:30:17 +03:00
}
2020-06-20 11:52:05 +03:00
2020-08-20 17:37:19 +03:00
fn fetch (
& mut self ,
mailbox_hash : MailboxHash ,
) -> Result < Pin < Box < dyn Stream < Item = Result < Vec < Envelope > > > + Send + 'static > > > {
struct FetchState {
mailbox_hash : MailboxHash ,
mailbox_index : Arc < Mutex < HashMap < EnvelopeHash , MailboxHash > > > ,
mailboxes : Arc < Mutex < HashMap < MailboxHash , MboxMailbox > > > ,
2021-01-08 23:45:58 +02:00
prefer_mbox_type : Option < MboxFormat > ,
2020-08-20 17:37:19 +03:00
offset : usize ,
file_offset : usize ,
contents : Vec < u8 > ,
}
impl FetchState {
async fn fetch ( & mut self ) -> Result < Option < Vec < Envelope > > > {
let mailboxes_lck = self . mailboxes . lock ( ) . unwrap ( ) ;
let index = mailboxes_lck [ & self . mailbox_hash ] . index . clone ( ) ;
2020-06-20 14:49:02 +03:00
drop ( mailboxes_lck ) ;
2020-07-16 17:59:27 +03:00
let mut message_iter = MessageIterator {
index ,
2020-08-20 17:37:19 +03:00
input : & self . contents . as_slice ( ) ,
offset : self . offset ,
file_offset : self . file_offset ,
2021-01-08 23:45:58 +02:00
format : self . prefer_mbox_type ,
2020-07-16 17:59:27 +03:00
} ;
2020-08-20 17:37:19 +03:00
let mut payload = vec! [ ] ;
let mut done = false ;
' iter_for_loop : for _i in 0 .. 150 {
match message_iter . next ( ) {
Some ( Ok ( env ) ) = > {
payload . push ( env ) ;
}
Some ( Err ( _err ) ) = > {
debug! ( & _err ) ;
}
None = > {
done = true ;
break 'iter_for_loop ;
2020-06-20 14:49:02 +03:00
}
2020-07-16 17:59:27 +03:00
}
2020-08-20 17:37:19 +03:00
}
self . offset = message_iter . offset ;
self . file_offset = message_iter . file_offset ;
{
let mut mailbox_index_lck = self . mailbox_index . lock ( ) . unwrap ( ) ;
2020-07-16 17:59:27 +03:00
for env in & payload {
2020-08-20 17:37:19 +03:00
mailbox_index_lck . insert ( env . hash ( ) , self . mailbox_hash ) ;
2020-07-16 17:59:27 +03:00
}
}
2020-08-20 17:37:19 +03:00
if done {
if payload . is_empty ( ) {
2020-08-25 16:39:12 +03:00
Ok ( None )
2020-08-20 17:37:19 +03:00
} else {
let mut mailbox_lock = self . mailboxes . lock ( ) . unwrap ( ) ;
let contents = std ::mem ::replace ( & mut self . contents , vec! [ ] ) ;
mailbox_lock
. entry ( self . mailbox_hash )
. and_modify ( | f | f . content = contents ) ;
Ok ( Some ( payload ) )
}
} else {
Ok ( Some ( payload ) )
2019-07-11 11:44:27 +03:00
}
2020-08-20 17:37:19 +03:00
}
}
let mailboxes = self . mailboxes . clone ( ) ;
let mailbox_path = mailboxes . lock ( ) . unwrap ( ) [ & mailbox_hash ] . fs_path . clone ( ) ;
let file = std ::fs ::OpenOptions ::new ( )
. read ( true )
2020-12-29 21:11:52 +02:00
. write ( true )
2020-08-20 17:37:19 +03:00
. open ( & mailbox_path ) ? ;
2020-12-29 21:11:52 +02:00
get_rw_lock_blocking ( & file , & mailbox_path ) ? ;
2020-08-20 17:37:19 +03:00
let mut buf_reader = BufReader ::new ( file ) ;
let mut contents = Vec ::new ( ) ;
buf_reader . read_to_end ( & mut contents ) ? ;
let mut state = FetchState {
mailbox_hash ,
mailboxes ,
mailbox_index : self . mailbox_index . clone ( ) ,
prefer_mbox_type : self . prefer_mbox_type ,
contents ,
offset : 0 ,
file_offset : 0 ,
2019-07-11 11:44:27 +03:00
} ;
2020-08-20 17:37:19 +03:00
Ok ( Box ::pin ( async_stream ::try_stream! {
loop {
if let Some ( res ) = state . fetch ( ) . await . map_err ( | err | {
debug! ( " fetch err {:?} " , & err ) ;
err } ) ? {
yield res ;
} else {
return ;
}
}
} ) )
2019-07-11 11:44:27 +03:00
}
2020-08-20 21:25:12 +03:00
fn refresh ( & mut self , _mailbox_hash : MailboxHash ) -> ResultFuture < ( ) > {
Err ( MeliError ::new ( " Unimplemented. " ) )
}
2020-08-20 17:37:19 +03:00
fn watch ( & self ) -> ResultFuture < ( ) > {
2020-08-20 01:55:24 +03:00
let sender = self . event_consumer . clone ( ) ;
2019-07-11 11:44:27 +03:00
let ( tx , rx ) = channel ( ) ;
2019-11-27 14:22:53 +02:00
let mut watcher = watcher ( tx , std ::time ::Duration ::from_secs ( 10 ) )
. map_err ( | e | e . to_string ( ) )
. map_err ( MeliError ::new ) ? ;
2020-02-26 10:54:10 +02:00
for f in self . mailboxes . lock ( ) . unwrap ( ) . values ( ) {
2019-11-27 14:22:53 +02:00
watcher
2020-06-20 14:49:02 +03:00
. watch ( & f . fs_path , RecursiveMode ::Recursive )
2019-11-27 14:22:53 +02:00
. map_err ( | e | e . to_string ( ) )
. map_err ( MeliError ::new ) ? ;
2020-06-20 14:49:02 +03:00
debug! ( " watching {:?} " , f . fs_path . as_path ( ) ) ;
2019-07-11 11:44:27 +03:00
}
2020-05-10 22:05:04 +03:00
let account_hash = {
let mut hasher = DefaultHasher ::new ( ) ;
hasher . write ( self . account_name . as_bytes ( ) ) ;
hasher . finish ( )
} ;
2020-02-26 10:54:10 +02:00
let mailboxes = self . mailboxes . clone ( ) ;
2020-07-14 23:17:41 +03:00
let mailbox_index = self . mailbox_index . clone ( ) ;
2020-07-05 15:28:55 +03:00
let prefer_mbox_type = self . prefer_mbox_type ;
2020-08-20 17:37:19 +03:00
Ok ( Box ::pin ( async move {
loop {
match rx . recv ( ) {
/*
* Event types :
*
* pub enum RefreshEventKind {
* Update ( EnvelopeHash , Envelope ) , // Old hash, new envelope
* Create ( Envelope ) ,
* Remove ( EnvelopeHash ) ,
* Rescan ,
* }
* /
Ok ( event ) = > match event {
/* Update */
DebouncedEvent ::NoticeWrite ( pathbuf ) | DebouncedEvent ::Write ( pathbuf ) = > {
let mailbox_hash = get_path_hash! ( & pathbuf ) ;
let file = match std ::fs ::OpenOptions ::new ( )
. read ( true )
2020-12-29 21:11:52 +02:00
. write ( true )
2020-08-20 17:37:19 +03:00
. open ( & pathbuf )
{
Ok ( f ) = > f ,
Err ( _ ) = > {
2019-07-11 11:44:27 +03:00
continue ;
2020-08-20 17:37:19 +03:00
}
} ;
2020-12-29 21:11:52 +02:00
get_rw_lock_blocking ( & file , & pathbuf ) ? ;
2020-08-20 17:37:19 +03:00
let mut mailbox_lock = mailboxes . lock ( ) . unwrap ( ) ;
let mut buf_reader = BufReader ::new ( file ) ;
let mut contents = Vec ::new ( ) ;
if let Err ( e ) = buf_reader . read_to_end ( & mut contents ) {
debug! ( e ) ;
continue ;
} ;
if contents . starts_with ( mailbox_lock [ & mailbox_hash ] . content . as_slice ( ) )
{
if let Ok ( ( _ , envelopes ) ) = mbox_parse (
mailbox_lock [ & mailbox_hash ] . index . clone ( ) ,
& contents ,
mailbox_lock [ & mailbox_hash ] . content . len ( ) ,
prefer_mbox_type ,
) {
let mut mailbox_index_lck = mailbox_index . lock ( ) . unwrap ( ) ;
for env in envelopes {
mailbox_index_lck . insert ( env . hash ( ) , mailbox_hash ) ;
( sender ) (
account_hash ,
BackendEvent ::Refresh ( RefreshEvent {
2020-05-10 22:05:04 +03:00
account_hash ,
2020-08-20 17:37:19 +03:00
mailbox_hash ,
kind : RefreshEventKind ::Create ( Box ::new ( env ) ) ,
} ) ,
) ;
2019-07-11 11:44:27 +03:00
}
2020-08-20 17:37:19 +03:00
}
} else {
( sender ) (
account_hash ,
BackendEvent ::Refresh ( RefreshEvent {
2020-05-10 22:05:04 +03:00
account_hash ,
2020-08-20 17:37:19 +03:00
mailbox_hash ,
kind : RefreshEventKind ::Rescan ,
} ) ,
) ;
2019-07-11 11:44:27 +03:00
}
2020-08-20 17:37:19 +03:00
mailbox_lock
. entry ( mailbox_hash )
. and_modify ( | f | f . content = contents ) ;
}
/* Remove */
DebouncedEvent ::NoticeRemove ( pathbuf ) | DebouncedEvent ::Remove ( pathbuf ) = > {
if mailboxes
. lock ( )
. unwrap ( )
. values ( )
. any ( | f | f . fs_path = = pathbuf )
{
let mailbox_hash = get_path_hash! ( & pathbuf ) ;
( sender ) (
account_hash ,
BackendEvent ::Refresh ( RefreshEvent {
2020-05-10 22:05:04 +03:00
account_hash ,
2020-08-20 17:37:19 +03:00
mailbox_hash ,
kind : RefreshEventKind ::Failure ( MeliError ::new ( format! (
" mbox mailbox {} was removed. " ,
pathbuf . display ( )
) ) ) ,
} ) ,
) ;
return Ok ( ( ) ) ;
2019-11-27 14:23:35 +02:00
}
2020-08-20 17:37:19 +03:00
}
DebouncedEvent ::Rename ( src , dest ) = > {
2020-08-25 16:39:12 +03:00
if mailboxes . lock ( ) . unwrap ( ) . values ( ) . any ( | f | f . fs_path = = src ) {
2020-08-20 17:37:19 +03:00
let mailbox_hash = get_path_hash! ( & src ) ;
( sender ) (
account_hash ,
BackendEvent ::Refresh ( RefreshEvent {
2020-05-10 22:05:04 +03:00
account_hash ,
2020-08-20 17:37:19 +03:00
mailbox_hash ,
kind : RefreshEventKind ::Failure ( MeliError ::new ( format! (
" mbox mailbox {} was renamed to {}. " ,
src . display ( ) ,
dest . display ( )
) ) ) ,
} ) ,
) ;
return Ok ( ( ) ) ;
2019-07-11 11:44:27 +03:00
}
2020-08-20 17:37:19 +03:00
}
/* Trigger rescan of mailboxes */
DebouncedEvent ::Rescan = > {
for & mailbox_hash in mailboxes . lock ( ) . unwrap ( ) . keys ( ) {
( sender ) (
account_hash ,
BackendEvent ::Refresh ( RefreshEvent {
2020-05-10 22:05:04 +03:00
account_hash ,
2020-08-20 17:37:19 +03:00
mailbox_hash ,
kind : RefreshEventKind ::Rescan ,
} ) ,
) ;
2019-07-11 11:44:27 +03:00
}
2020-08-20 17:37:19 +03:00
return Ok ( ( ) ) ;
}
_ = > { }
} ,
Err ( e ) = > debug! ( " watch error: {:?} " , e ) ,
2019-07-11 11:44:27 +03:00
}
2020-08-20 17:37:19 +03:00
}
} ) )
2018-07-13 18:38:57 +03:00
}
2020-08-20 17:37:19 +03:00
fn mailboxes ( & self ) -> ResultFuture < HashMap < MailboxHash , Mailbox > > {
let ret = Ok ( self
2020-02-26 10:54:10 +02:00
. mailboxes
2019-07-11 11:44:27 +03:00
. lock ( )
. unwrap ( )
. iter ( )
2020-02-26 10:54:10 +02:00
. map ( | ( h , f ) | ( * h , f . clone ( ) as Mailbox ) )
2020-08-20 17:37:19 +03:00
. collect ( ) ) ;
Ok ( Box ::pin ( async { ret } ) )
2019-07-11 11:44:27 +03:00
}
2020-06-23 17:21:50 +03:00
fn operation ( & self , env_hash : EnvelopeHash ) -> Result < Box < dyn BackendOp > > {
2020-06-20 14:49:02 +03:00
let mailbox_hash = self . mailbox_index . lock ( ) . unwrap ( ) [ & env_hash ] ;
let mailboxes_lck = self . mailboxes . lock ( ) . unwrap ( ) ;
2019-07-11 11:44:27 +03:00
let ( offset , length ) = {
2020-06-20 14:49:02 +03:00
let index = mailboxes_lck [ & mailbox_hash ] . index . lock ( ) . unwrap ( ) ;
index [ & env_hash ]
2019-07-11 11:44:27 +03:00
} ;
2020-06-20 14:49:02 +03:00
let mailbox_path = mailboxes_lck [ & mailbox_hash ] . fs_path . clone ( ) ;
2020-06-23 17:21:50 +03:00
Ok ( Box ::new ( MboxOp ::new (
2020-06-20 14:49:02 +03:00
env_hash ,
mailbox_path . as_path ( ) ,
offset ,
length ,
2020-06-23 17:21:50 +03:00
) ) )
2019-07-11 11:44:27 +03:00
}
2020-09-11 16:58:56 +03:00
fn copy_messages (
& mut self ,
_env_hashes : EnvelopeHashBatch ,
_source_mailbox_hash : MailboxHash ,
_destination_mailbox_hash : MailboxHash ,
_move_ : bool ,
) -> ResultFuture < ( ) > {
Err ( MeliError ::new ( " Unimplemented. " ) )
}
fn set_flags (
& mut self ,
_env_hashes : EnvelopeHashBatch ,
_mailbox_hash : MailboxHash ,
_flags : SmallVec < [ ( std ::result ::Result < Flag , String > , bool ) ; 8 ] > ,
) -> ResultFuture < ( ) > {
Err ( MeliError ::new ( " Unimplemented. " ) )
}
2020-10-13 13:57:04 +03:00
fn delete_messages (
& mut self ,
_env_hashes : EnvelopeHashBatch ,
_mailbox_hash : MailboxHash ,
) -> ResultFuture < ( ) > {
Err ( MeliError ::new ( " Unimplemented. " ) )
}
2020-06-30 11:40:26 +03:00
fn save (
& self ,
_bytes : Vec < u8 > ,
_mailbox_hash : MailboxHash ,
_flags : Option < Flag > ,
) -> ResultFuture < ( ) > {
2020-01-30 04:22:25 +02:00
Err ( MeliError ::new ( " Unimplemented. " ) )
2018-07-13 18:38:57 +03:00
}
2019-11-06 14:53:12 +02:00
2020-08-20 21:25:12 +03:00
fn as_any ( & self ) -> & dyn Any {
self
}
fn as_any_mut ( & mut self ) -> & mut dyn Any {
2019-11-06 14:53:12 +02:00
self
}
2020-08-10 14:24:21 +03:00
fn collection ( & self ) -> Collection {
self . collection . clone ( )
}
2018-07-13 18:38:57 +03:00
}
2020-06-20 11:52:05 +03:00
macro_rules ! get_conf_val {
( $s :ident [ $var :literal ] ) = > {
$s . extra . get ( $var ) . ok_or_else ( | | {
MeliError ::new ( format! (
" Configuration error ({}): mbox backend requires the field `{}` set " ,
$s . name . as_str ( ) ,
$var
) )
} )
} ;
( $s :ident [ $var :literal ] , $default :expr ) = > {
$s . extra
. get ( $var )
. map ( | v | {
< _ > ::from_str ( v ) . map_err ( | e | {
MeliError ::new ( format! (
" Configuration error ({}): Invalid value for field `{}`: {} \n {} " ,
$s . name . as_str ( ) ,
$var ,
v ,
e
) )
} )
} )
. unwrap_or_else ( | | Ok ( $default ) )
} ;
}
2018-07-13 18:38:57 +03:00
impl MboxType {
2019-11-16 00:33:22 +02:00
pub fn new (
s : & AccountSettings ,
_is_subscribed : Box < dyn Fn ( & str ) -> bool > ,
2020-08-20 01:55:24 +03:00
event_consumer : BackendEventConsumer ,
2019-11-16 00:33:22 +02:00
) -> Result < Box < dyn MailBackend > > {
2020-02-26 10:54:10 +02:00
let path = Path ::new ( s . root_mailbox . as_str ( ) ) . expand ( ) ;
2019-07-11 11:44:27 +03:00
if ! path . exists ( ) {
2019-11-16 00:33:22 +02:00
return Err ( MeliError ::new ( format! (
2020-02-26 10:54:10 +02:00
" \" root_mailbox \" {} for account {} is not a valid path. " ,
s . root_mailbox . as_str ( ) ,
2019-07-11 11:44:27 +03:00
s . name ( )
2019-11-16 00:33:22 +02:00
) ) ) ;
2019-07-11 11:44:27 +03:00
}
2020-06-20 11:52:05 +03:00
let prefer_mbox_type : String = get_conf_val! ( s [ " prefer_mbox_type " ] , " auto " . to_string ( ) ) ? ;
2019-07-11 11:44:27 +03:00
let ret = MboxType {
2020-05-10 22:05:04 +03:00
account_name : s . name ( ) . to_string ( ) ,
2020-08-20 01:55:24 +03:00
event_consumer ,
2019-09-26 18:27:13 +03:00
path ,
2020-06-20 11:52:05 +03:00
prefer_mbox_type : match prefer_mbox_type . as_str ( ) {
" auto " = > None ,
2021-01-08 23:45:58 +02:00
" mboxo " = > Some ( MboxFormat ::MboxO ) ,
" mboxrd " = > Some ( MboxFormat ::MboxRd ) ,
" mboxcl " = > Some ( MboxFormat ::MboxCl ) ,
" mboxcl2 " = > Some ( MboxFormat ::MboxCl2 ) ,
2020-06-20 11:52:05 +03:00
_ = > {
return Err ( MeliError ::new ( format! (
" {} invalid `prefer_mbox_type` value: `{}` " ,
s . name ( ) ,
prefer_mbox_type ,
) ) )
}
} ,
2020-08-10 14:24:21 +03:00
collection : Collection ::default ( ) ,
2020-08-20 01:55:24 +03:00
mailbox_index : Default ::default ( ) ,
mailboxes : Default ::default ( ) ,
2019-07-11 11:44:27 +03:00
} ;
let name : String = ret
. path
. file_name ( )
. map ( | f | f . to_string_lossy ( ) . into ( ) )
2020-07-05 15:28:55 +03:00
. unwrap_or_default ( ) ;
2019-09-26 18:27:13 +03:00
let hash = get_path_hash! ( & ret . path ) ;
2019-11-11 00:47:23 +02:00
let read_only = if let Ok ( metadata ) = std ::fs ::metadata ( & ret . path ) {
metadata . permissions ( ) . readonly ( )
} else {
true
} ;
2020-02-26 10:54:10 +02:00
ret . mailboxes . lock ( ) . unwrap ( ) . insert (
2019-07-11 11:44:27 +03:00
hash ,
2020-02-26 10:54:10 +02:00
MboxMailbox {
2019-07-11 11:44:27 +03:00
hash ,
2020-06-20 14:49:02 +03:00
path : name . clone ( ) . into ( ) ,
fs_path : ret . path . clone ( ) ,
2019-07-11 11:44:27 +03:00
name ,
content : Vec ::new ( ) ,
children : Vec ::new ( ) ,
parent : None ,
2019-12-17 14:12:41 +02:00
usage : Arc ::new ( RwLock ::new ( SpecialUsageMailbox ::Normal ) ) ,
is_subscribed : true ,
2020-02-26 10:54:10 +02:00
permissions : MailboxPermissions {
2019-11-11 00:47:23 +02:00
create_messages : ! read_only ,
remove_messages : ! read_only ,
set_flags : ! read_only ,
create_child : ! read_only ,
rename_messages : ! read_only ,
delete_messages : ! read_only ,
delete_mailbox : ! read_only ,
change_permissions : false ,
} ,
2019-12-17 14:12:41 +02:00
unseen : Arc ::new ( Mutex ::new ( 0 ) ) ,
total : Arc ::new ( Mutex ::new ( 0 ) ) ,
2020-06-20 14:49:02 +03:00
index : Default ::default ( ) ,
2019-07-11 11:44:27 +03:00
} ,
) ;
/* Look for other mailboxes */
2020-06-20 14:49:02 +03:00
for ( k , f ) in s . mailboxes . iter ( ) {
if let Some ( path_str ) = f . extra . get ( " path " ) {
let hash = get_path_hash! ( path_str ) ;
let pathbuf : PathBuf = path_str . into ( ) ;
if ! pathbuf . exists ( ) | | pathbuf . is_dir ( ) {
return Err ( MeliError ::new ( format! (
" mbox mailbox configuration entry \" {} \" path value {} is not a file. " ,
k , path_str
) ) ) ;
2019-07-11 11:44:27 +03:00
}
2020-06-20 14:49:02 +03:00
let read_only = if let Ok ( metadata ) = std ::fs ::metadata ( & pathbuf ) {
metadata . permissions ( ) . readonly ( )
} else {
true
} ;
ret . mailboxes . lock ( ) . unwrap ( ) . insert (
hash ,
MboxMailbox {
2019-07-11 11:44:27 +03:00
hash ,
2020-06-20 14:49:02 +03:00
name : k . to_string ( ) ,
fs_path : pathbuf ,
path : k . into ( ) ,
content : Vec ::new ( ) ,
children : Vec ::new ( ) ,
parent : None ,
usage : Arc ::new ( RwLock ::new ( f . usage . unwrap_or_default ( ) ) ) ,
is_subscribed : f . subscribe . is_true ( ) ,
permissions : MailboxPermissions {
create_messages : ! read_only ,
remove_messages : ! read_only ,
set_flags : ! read_only ,
create_child : ! read_only ,
rename_messages : ! read_only ,
delete_messages : ! read_only ,
delete_mailbox : ! read_only ,
change_permissions : false ,
2019-07-11 11:44:27 +03:00
} ,
2020-06-20 14:49:02 +03:00
unseen : Arc ::new ( Mutex ::new ( 0 ) ) ,
total : Arc ::new ( Mutex ::new ( 0 ) ) ,
index : Default ::default ( ) ,
} ,
) ;
} else {
return Err ( MeliError ::new ( format! (
" mbox mailbox configuration entry \" {} \" should have a \" path \" value set pointing to an mbox file. " ,
k
) ) ) ;
2019-07-11 11:44:27 +03:00
}
}
2019-11-16 00:33:22 +02:00
Ok ( Box ::new ( ret ) )
2018-07-13 18:38:57 +03:00
}
2019-11-27 14:22:53 +02:00
pub fn validate_config ( s : & AccountSettings ) -> Result < ( ) > {
2020-02-26 10:54:10 +02:00
let path = Path ::new ( s . root_mailbox . as_str ( ) ) . expand ( ) ;
2019-11-27 14:22:53 +02:00
if ! path . exists ( ) {
return Err ( MeliError ::new ( format! (
2020-02-26 10:54:10 +02:00
" \" root_mailbox \" {} for account {} is not a valid path. " ,
s . root_mailbox . as_str ( ) ,
2019-11-27 14:22:53 +02:00
s . name ( )
) ) ) ;
}
2020-06-20 11:52:05 +03:00
let prefer_mbox_type : Result < String > =
get_conf_val! ( s [ " prefer_mbox_type " ] , " auto " . to_string ( ) ) ;
prefer_mbox_type ? ;
2019-11-27 14:22:53 +02:00
Ok ( ( ) )
}
2018-07-13 18:38:57 +03:00
}