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/>.
* /
2018-08-07 15:01:15 +03:00
/*!
* https ://wiki2.dovecot.org/MailboxFormat/mbox
* /
2019-09-11 17:57:55 +03:00
use crate ::async_workers ::{ Async , AsyncBuilder , AsyncStatus , WorkContext } ;
2020-06-28 19:16:13 +03:00
use crate ::backends ::* ;
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 ;
2019-07-11 11:44:27 +03:00
use libc ;
use memmap ::{ Mmap , Protection } ;
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 ;
2019-07-11 11:44:27 +03:00
use std ::io ::BufReader ;
use std ::io ::Read ;
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
2020-01-30 04:18:45 +02:00
type Offset = usize ;
type Length = usize ;
2019-07-11 11:44:27 +03:00
const F_OFD_SETLKW : libc ::c_int = 38 ;
// Open file description locking
// # man fcntl
fn get_rw_lock_blocking ( f : & File ) {
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." */
} ;
let ptr : * mut libc ::flock = & mut flock ;
let ret_val = unsafe { libc ::fcntl ( fd , F_OFD_SETLKW , ptr as * mut libc ::c_void ) } ;
debug! ( & ret_val ) ;
assert! ( - 1 ! = ret_val ) ;
}
#[ 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 ,
slice : Option < Mmap > ,
}
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 ( ) ,
slice : None ,
offset ,
length ,
}
2018-07-13 18:38:57 +03:00
}
}
impl BackendOp for MboxOp {
fn description ( & self ) -> String {
2019-07-11 11:44:27 +03:00
String ::new ( )
2018-07-13 18:38:57 +03:00
}
2019-07-11 11:44:27 +03:00
2018-07-13 18:38:57 +03:00
fn as_bytes ( & mut self ) -> Result < & [ u8 ] > {
2019-07-11 11:44:27 +03:00
if self . slice . is_none ( ) {
self . slice = Some ( Mmap ::open_path ( & self . path , Protection ::Read ) ? ) ;
}
/* Unwrap is safe since we use ? above. */
Ok ( unsafe {
& self . slice . as_ref ( ) . unwrap ( ) . as_slice ( ) [ self . offset .. self . offset + self . length ]
} )
2018-07-13 18:38:57 +03:00
}
2019-07-11 11:44:27 +03:00
2020-06-28 16:53:52 +03:00
fn fetch_flags ( & self ) -> Result < Flag > {
2019-07-11 11:44:27 +03:00
let mut flags = Flag ::empty ( ) ;
2020-06-28 16:53:52 +03:00
let file = std ::fs ::OpenOptions ::new ( )
2019-07-11 11:44:27 +03:00
. read ( true )
. write ( true )
2020-06-28 16:53:52 +03:00
. open ( & self . path ) ? ;
2019-07-11 11:44:27 +03:00
get_rw_lock_blocking ( & file ) ;
let mut buf_reader = BufReader ::new ( file ) ;
let mut contents = Vec ::new ( ) ;
2020-06-28 16:53:52 +03:00
buf_reader . read_to_end ( & mut contents ) ? ;
let ( _ , headers ) = parser ::headers ::headers_raw ( contents . as_slice ( ) ) ? ;
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-28 16:53:52 +03:00
Ok ( flags )
2018-07-13 18:38:57 +03:00
}
2019-07-11 11:44:27 +03:00
2020-06-28 19:16:13 +03:00
fn set_flag (
& mut self ,
_flag : Flag ,
_value : bool ,
) -> Result < Pin < Box < dyn Future < Output = Result < ( ) > > + Send > > > {
Err ( MeliError ::new ( " Unimplemented. " ) )
2018-08-06 11:05:09 +03:00
}
2019-12-08 10:57:36 +02:00
2020-06-28 19:16:13 +03:00
fn set_tag (
& mut self ,
_tag : String ,
_value : bool ,
) -> Result < Pin < Box < dyn Future < Output = Result < ( ) > > + Send > > > {
2019-12-08 10:57:36 +02:00
Err ( MeliError ::new ( " mbox doesn't support tags. " ) )
}
2018-07-13 18:38:57 +03:00
}
2020-06-20 11:52:05 +03:00
#[ derive(Debug, Clone, Copy) ]
pub enum MboxReader {
MboxO ,
MboxRd ,
MboxCl ,
MboxCl2 ,
}
impl Default for MboxReader {
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
} } ;
}
impl MboxReader {
fn parse < ' i > ( & self , input : & ' i [ u8 ] ) -> IResult < & ' i [ u8 ] , Envelope > {
let orig_input = input ;
let mut input = input ;
match self {
Self ::MboxO = > {
let next_offset : Option < ( usize , usize ) > = find_From__line! ( input )
. and_then ( | end | input . find ( b " \n " ) . and_then ( | start | Some ( ( start + 1 , end ) ) ) ) ;
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 " ) {
if env . other_headers ( ) [ " Status " ] . contains ( " F " ) {
flags . set ( Flag ::FLAGGED , true ) ;
}
if env . other_headers ( ) [ " Status " ] . contains ( " A " ) {
flags . set ( Flag ::REPLIED , true ) ;
}
if env . other_headers ( ) [ " Status " ] . contains ( " R " ) {
flags . set ( Flag ::SEEN , true ) ;
}
if env . other_headers ( ) [ " Status " ] . contains ( " D " ) {
flags . set ( Flag ::TRASHED , true ) ;
}
}
if env . other_headers ( ) . contains_key ( " X-Status " ) {
if env . other_headers ( ) [ " X-Status " ] . contains ( " F " ) {
flags . set ( Flag ::FLAGGED , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " A " ) {
flags . set ( Flag ::REPLIED , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " R " ) {
flags . set ( Flag ::SEEN , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " D " ) {
flags . set ( Flag ::TRASHED , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " T " ) {
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 " ) {
if env . other_headers ( ) [ " Status " ] . contains ( " F " ) {
flags . set ( Flag ::FLAGGED , true ) ;
}
if env . other_headers ( ) [ " Status " ] . contains ( " A " ) {
flags . set ( Flag ::REPLIED , true ) ;
}
if env . other_headers ( ) [ " Status " ] . contains ( " R " ) {
flags . set ( Flag ::SEEN , true ) ;
}
if env . other_headers ( ) [ " Status " ] . contains ( " D " ) {
flags . set ( Flag ::TRASHED , true ) ;
}
}
if env . other_headers ( ) . contains_key ( " X-Status " ) {
if env . other_headers ( ) [ " X-Status " ] . contains ( " F " ) {
flags . set ( Flag ::FLAGGED , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " A " ) {
flags . set ( Flag ::REPLIED , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " R " ) {
flags . set ( Flag ::SEEN , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " D " ) {
flags . set ( Flag ::TRASHED , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " T " ) {
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 )
. and_then ( | end | input . find ( b " \n " ) . and_then ( | start | Some ( ( start + 1 , end ) ) ) ) ;
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 " ) {
if env . other_headers ( ) [ " Status " ] . contains ( " F " ) {
flags . set ( Flag ::FLAGGED , true ) ;
}
if env . other_headers ( ) [ " Status " ] . contains ( " A " ) {
flags . set ( Flag ::REPLIED , true ) ;
}
if env . other_headers ( ) [ " Status " ] . contains ( " R " ) {
flags . set ( Flag ::SEEN , true ) ;
}
if env . other_headers ( ) [ " Status " ] . contains ( " D " ) {
flags . set ( Flag ::TRASHED , true ) ;
}
}
if env . other_headers ( ) . contains_key ( " X-Status " ) {
if env . other_headers ( ) [ " X-Status " ] . contains ( " F " ) {
flags . set ( Flag ::FLAGGED , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " A " ) {
flags . set ( Flag ::REPLIED , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " R " ) {
flags . set ( Flag ::SEEN , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " D " ) {
flags . set ( Flag ::TRASHED , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " T " ) {
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 " ) {
if env . other_headers ( ) [ " Status " ] . contains ( " F " ) {
flags . set ( Flag ::FLAGGED , true ) ;
}
if env . other_headers ( ) [ " Status " ] . contains ( " A " ) {
flags . set ( Flag ::REPLIED , true ) ;
}
if env . other_headers ( ) [ " Status " ] . contains ( " R " ) {
flags . set ( Flag ::SEEN , true ) ;
}
if env . other_headers ( ) [ " Status " ] . contains ( " D " ) {
flags . set ( Flag ::TRASHED , true ) ;
}
}
if env . other_headers ( ) . contains_key ( " X-Status " ) {
if env . other_headers ( ) [ " X-Status " ] . contains ( " F " ) {
flags . set ( Flag ::FLAGGED , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " A " ) {
flags . set ( Flag ::REPLIED , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " R " ) {
flags . set ( Flag ::SEEN , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " D " ) {
flags . set ( Flag ::TRASHED , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " T " ) {
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 " ) {
if env . other_headers ( ) [ " Status " ] . contains ( " F " ) {
flags . set ( Flag ::FLAGGED , true ) ;
}
if env . other_headers ( ) [ " Status " ] . contains ( " A " ) {
flags . set ( Flag ::REPLIED , true ) ;
}
if env . other_headers ( ) [ " Status " ] . contains ( " R " ) {
flags . set ( Flag ::SEEN , true ) ;
}
if env . other_headers ( ) [ " Status " ] . contains ( " D " ) {
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 " ) {
if env . other_headers ( ) [ " X-Status " ] . contains ( " F " ) {
flags . set ( Flag ::FLAGGED , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " A " ) {
flags . set ( Flag ::REPLIED , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " R " ) {
flags . set ( Flag ::SEEN , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " D " ) {
flags . set ( Flag ::TRASHED , true ) ;
}
if env . other_headers ( ) [ " X-Status " ] . contains ( " T " ) {
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-06-20 11:52:05 +03:00
Err ( _err ) = > {
return 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 ,
reader : Option < MboxReader > ,
) -> 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 ) ;
let reader = reader . unwrap_or ( MboxReader ::MboxCl2 ) ;
while ! input [ offset + file_offset .. ] . is_empty ( ) {
let ( next_input , env ) = match reader . parse ( & input [ offset + file_offset .. ] ) {
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 {
Err ( e ) ? ;
}
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-06-06 19:38:20 +03:00
return Ok ( ( & [ ] , envelopes ) ) ;
2019-07-11 11:44:27 +03:00
}
2018-07-27 21:37:56 +03:00
/// Mbox backend
2019-07-11 11:44:27 +03:00
#[ derive(Debug, Default) ]
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-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 > > > ,
2020-06-20 11:52:05 +03:00
prefer_mbox_type : Option < MboxReader > ,
2019-07-11 11:44:27 +03:00
}
2018-07-13 18:38:57 +03:00
impl MailBackend for MboxType {
2019-12-14 18:46:12 +02:00
fn is_online ( & self ) -> Result < ( ) > {
Ok ( ( ) )
2019-10-24 20:30:17 +03:00
}
2020-06-20 11:52:05 +03:00
2020-02-26 10:54:10 +02:00
fn get ( & mut self , mailbox : & Mailbox ) -> Async < Result < Vec < Envelope > > > {
2019-07-11 11:44:27 +03:00
let mut w = AsyncBuilder ::new ( ) ;
let handle = {
let tx = w . tx ( ) ;
2020-02-26 10:54:10 +02:00
let mailbox_hash = mailbox . hash ( ) ;
2020-06-20 14:49:02 +03:00
let mailbox_index = self . mailbox_index . clone ( ) ;
2020-02-26 10:54:10 +02:00
let mailboxes = self . mailboxes . clone ( ) ;
2020-06-20 14:49:02 +03:00
let mailbox_path = mailboxes . lock ( ) . unwrap ( ) [ & mailbox_hash ] . fs_path . clone ( ) ;
2020-06-20 11:52:05 +03:00
let prefer_mbox_type = self . prefer_mbox_type . clone ( ) ;
2019-09-11 17:57:55 +03:00
let closure = move | _work_context | {
2019-07-11 11:44:27 +03:00
let tx = tx . clone ( ) ;
let file = match std ::fs ::OpenOptions ::new ( )
. read ( true )
. write ( true )
2020-02-26 10:54:10 +02:00
. open ( & mailbox_path )
2019-07-11 11:44:27 +03:00
{
Ok ( f ) = > f ,
Err ( e ) = > {
2019-09-09 12:53:39 +03:00
tx . send ( AsyncStatus ::Payload ( Err ( MeliError ::from ( e ) ) ) )
. unwrap ( ) ;
2019-07-11 11:44:27 +03:00
return ;
}
} ;
get_rw_lock_blocking ( & file ) ;
let mut buf_reader = BufReader ::new ( file ) ;
let mut contents = Vec ::new ( ) ;
if let Err ( e ) = buf_reader . read_to_end ( & mut contents ) {
2019-09-09 12:53:39 +03:00
tx . send ( AsyncStatus ::Payload ( Err ( MeliError ::from ( e ) ) ) )
. unwrap ( ) ;
2019-07-11 11:44:27 +03:00
return ;
} ;
2020-06-20 14:49:02 +03:00
let mailboxes_lck = mailboxes . lock ( ) . unwrap ( ) ;
let mut mailbox_index_lck = mailbox_index . lock ( ) . unwrap ( ) ;
let index = mailboxes_lck [ & mailbox_hash ] . index . clone ( ) ;
drop ( mailboxes_lck ) ;
2020-06-20 11:52:05 +03:00
let payload = mbox_parse ( index , contents . as_slice ( ) , 0 , prefer_mbox_type )
2020-06-06 19:38:20 +03:00
. map_err ( | e | MeliError ::from ( e ) )
2020-06-20 14:49:02 +03:00
. map ( | ( _ , v ) | {
for v in v . iter ( ) {
mailbox_index_lck . insert ( v . hash ( ) , mailbox_hash ) ;
}
v
} ) ;
2019-07-11 11:44:27 +03:00
{
2020-02-26 10:54:10 +02:00
let mut mailbox_lock = mailboxes . lock ( ) . unwrap ( ) ;
mailbox_lock
. entry ( mailbox_hash )
2019-07-11 11:44:27 +03:00
. and_modify ( | f | f . content = contents ) ;
}
2019-09-09 12:53:39 +03:00
tx . send ( AsyncStatus ::Payload ( payload ) ) . unwrap ( ) ;
2019-12-03 13:26:24 +02:00
tx . send ( AsyncStatus ::Finished ) . unwrap ( ) ;
2019-07-11 11:44:27 +03:00
} ;
Box ::new ( closure )
} ;
w . build ( handle )
}
2019-09-11 17:57:55 +03:00
fn watch (
& self ,
sender : RefreshEventConsumer ,
work_context : WorkContext ,
) -> Result < std ::thread ::ThreadId > {
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-06-20 11:52:05 +03:00
let prefer_mbox_type = self . prefer_mbox_type . clone ( ) ;
2019-09-11 17:57:55 +03:00
let handle = std ::thread ::Builder ::new ( )
2020-05-10 22:05:04 +03:00
. name ( format! ( " watching {} " , self . account_name , ) )
2019-07-11 11:44:27 +03:00
. spawn ( move | | {
// Move `watcher` in the closure's scope so that it doesn't get dropped.
let _watcher = watcher ;
2019-09-11 17:57:55 +03:00
let _work_context = work_context ;
2020-02-26 10:54:10 +02:00
let mailboxes = mailboxes ;
2019-07-11 11:44:27 +03:00
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 ) = > {
2020-02-26 10:54:10 +02:00
let mailbox_hash = get_path_hash! ( & pathbuf ) ;
2019-07-11 11:44:27 +03:00
let file = match std ::fs ::OpenOptions ::new ( )
. read ( true )
. write ( true )
. open ( & pathbuf )
{
Ok ( f ) = > f ,
Err ( _ ) = > {
continue ;
}
} ;
get_rw_lock_blocking ( & file ) ;
2020-02-26 10:54:10 +02:00
let mut mailbox_lock = mailboxes . lock ( ) . unwrap ( ) ;
2019-07-11 11:44:27 +03:00
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
2020-02-26 10:54:10 +02:00
. starts_with ( mailbox_lock [ & mailbox_hash ] . content . as_slice ( ) )
2019-07-11 11:44:27 +03:00
{
2020-06-06 19:38:20 +03:00
if let Ok ( ( _ , envelopes ) ) = mbox_parse (
2020-06-20 14:49:02 +03:00
mailbox_lock [ & mailbox_hash ] . index . clone ( ) ,
2020-02-26 10:54:10 +02:00
& contents [ mailbox_lock [ & mailbox_hash ] . content . len ( ) .. ] ,
mailbox_lock [ & mailbox_hash ] . content . len ( ) ,
2020-06-20 11:52:05 +03:00
prefer_mbox_type ,
2020-06-06 19:38:20 +03:00
) {
2019-07-11 11:44:27 +03:00
for env in envelopes {
sender . send ( RefreshEvent {
2020-05-10 22:05:04 +03:00
account_hash ,
mailbox_hash ,
2019-07-11 11:44:27 +03:00
kind : RefreshEventKind ::Create ( Box ::new ( env ) ) ,
} ) ;
}
}
} else {
sender . send ( RefreshEvent {
2020-05-10 22:05:04 +03:00
account_hash ,
mailbox_hash ,
2019-07-11 11:44:27 +03:00
kind : RefreshEventKind ::Rescan ,
} ) ;
}
2020-02-26 10:54:10 +02:00
mailbox_lock
. entry ( mailbox_hash )
2019-07-11 11:44:27 +03:00
. and_modify ( | f | f . content = contents ) ;
}
/* Remove */
DebouncedEvent ::NoticeRemove ( pathbuf )
| DebouncedEvent ::Remove ( pathbuf ) = > {
2020-02-26 10:54:10 +02:00
if mailboxes
2019-11-27 14:23:35 +02:00
. lock ( )
. unwrap ( )
. values ( )
2020-06-20 14:49:02 +03:00
. any ( | f | & f . fs_path = = & pathbuf )
2019-11-27 14:23:35 +02:00
{
2020-02-26 10:54:10 +02:00
let mailbox_hash = get_path_hash! ( & pathbuf ) ;
2019-11-27 14:23:35 +02:00
sender . send ( RefreshEvent {
2020-05-10 22:05:04 +03:00
account_hash ,
mailbox_hash ,
2019-11-27 14:23:35 +02:00
kind : RefreshEventKind ::Failure ( MeliError ::new ( format! (
2020-02-26 10:54:10 +02:00
" mbox mailbox {} was removed. " ,
2019-11-27 14:23:35 +02:00
pathbuf . display ( )
) ) ) ,
} ) ;
return ;
}
}
DebouncedEvent ::Rename ( src , dest ) = > {
2020-06-20 14:49:02 +03:00
if mailboxes
. lock ( )
. unwrap ( )
. values ( )
. any ( | f | & f . fs_path = = & src )
{
2020-02-26 10:54:10 +02:00
let mailbox_hash = get_path_hash! ( & src ) ;
2019-11-27 14:23:35 +02:00
sender . send ( RefreshEvent {
2020-05-10 22:05:04 +03:00
account_hash ,
mailbox_hash ,
2019-11-27 14:23:35 +02:00
kind : RefreshEventKind ::Failure ( MeliError ::new ( format! (
2020-02-26 10:54:10 +02:00
" mbox mailbox {} was renamed to {}. " ,
2019-11-27 14:23:35 +02:00
src . display ( ) ,
dest . display ( )
) ) ) ,
} ) ;
return ;
}
2019-07-11 11:44:27 +03:00
}
2020-02-26 10:54:10 +02:00
/* Trigger rescan of mailboxes */
2019-07-11 11:44:27 +03:00
DebouncedEvent ::Rescan = > {
2020-05-10 22:05:04 +03:00
for & mailbox_hash in mailboxes . lock ( ) . unwrap ( ) . keys ( ) {
2019-11-27 14:23:35 +02:00
sender . send ( RefreshEvent {
2020-05-10 22:05:04 +03:00
account_hash ,
mailbox_hash ,
2019-11-27 14:23:35 +02:00
kind : RefreshEventKind ::Rescan ,
} ) ;
}
return ;
2019-07-11 11:44:27 +03:00
}
_ = > { }
} ,
Err ( e ) = > debug! ( " watch error: {:?} " , e ) ,
}
}
} ) ? ;
2019-09-11 17:57:55 +03:00
Ok ( handle . thread ( ) . id ( ) )
2018-07-13 18:38:57 +03:00
}
2020-05-10 21:14:49 +03:00
fn mailboxes ( & self ) -> Result < HashMap < MailboxHash , Mailbox > > {
2019-11-23 17:47:24 +02:00
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 ) )
2019-11-23 17:47:24 +02:00
. collect ( ) )
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-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-06-28 16:53:52 +03:00
fn as_any ( & self ) -> & dyn ::std ::any ::Any {
2019-11-06 14:53:12 +02:00
self
}
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 > ,
) -> 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 ( ) ,
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 ,
" mboxo " = > Some ( MboxReader ::MboxO ) ,
" mboxrd " = > Some ( MboxReader ::MboxRd ) ,
" mboxcl " = > Some ( MboxReader ::MboxCl ) ,
" mboxcl2 " = > Some ( MboxReader ::MboxCl2 ) ,
_ = > {
return Err ( MeliError ::new ( format! (
" {} invalid `prefer_mbox_type` value: `{}` " ,
s . name ( ) ,
prefer_mbox_type ,
) ) )
}
} ,
2019-07-11 11:44:27 +03:00
.. Default ::default ( )
} ;
let name : String = ret
. path
. file_name ( )
. map ( | f | f . to_string_lossy ( ) . into ( ) )
. unwrap_or ( String ::new ( ) ) ;
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
}