2017-09-07 23:00:08 +03:00
/*
* meli - lib . rs
*
* 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/>.
* /
2019-12-01 17:11:13 +02:00
//! A crate that performs mail client operations such as
2020-09-09 14:24:30 +03:00
//! - Hold an [`Envelope`](./email/struct.Envelope.html) with methods convenient for mail client use. (see module [`email`](./email/index.html))
//! - Abstract through mail storages through the [`MailBackend`](./backends/trait.MailBackend.html) trait, and handle read/writes/updates through it. (see module [`backends`](./backends/index.html))
//! - Decode attachments (see module [`email::attachments`](./email/attachments/index.html))
//! - Create new mail (see [`email::Draft`](./email/compose/struct.Draft.html))
//! - Send mail with an SMTP client (see module [`smtp`](./smtp/index.html))
//! - Manage an `addressbook` i.e. have contacts (see module [`addressbook`](./addressbook/index.html))
//! - Build thread structures out of a list of mail via their `In-Reply-To` and `References` header values (see module [`thread`](./thread/index.html))
2019-12-01 17:11:13 +02:00
//!
//! Other exports are
2020-09-09 14:24:30 +03:00
//! - Basic mail account configuration to use with [`backends`](./backends/index.html) (see module [`conf`](./conf/index.html))
//! - Parser combinators (see module [`parsec`](./parsec/index.html))
2019-12-01 17:11:13 +02:00
//! - A `ShellExpandTrait` to expand paths like a shell.
2020-09-09 14:24:30 +03:00
//! - A `debug` macro that works like `std::dbg` but for multiple threads. (see [`debug` macro](./macro.debug.html))
2019-05-01 19:20:33 +03:00
#[ macro_use ]
pub mod dbg {
2020-05-16 12:46:01 +03:00
#[ macro_export ]
macro_rules ! log_tag {
( ) = > {
eprint! (
" [{}][{:?}] {}:{}_{}: " ,
crate ::datetime ::timestamp_to_string ( crate ::datetime ::now ( ) , Some ( " %Y-%m-%d %T " ) ) ,
std ::thread ::current ( )
. name ( )
. map ( std ::string ::ToString ::to_string )
. unwrap_or_else ( | | format! ( " {:?} " , std ::thread ::current ( ) . id ( ) ) ) ,
file! ( ) ,
line! ( ) ,
column! ( )
) ;
} ;
}
2019-05-13 22:05:00 +03:00
#[ allow(clippy::redundant_closure) ]
2019-05-01 19:20:33 +03:00
#[ macro_export ]
macro_rules ! debug {
2019-05-07 01:54:20 +03:00
( $val :literal ) = > {
2019-05-12 17:37:07 +03:00
{
2019-09-21 21:25:21 +03:00
if cfg! ( feature = " debug-tracing " ) {
2020-05-16 12:46:01 +03:00
log_tag! ( ) ;
2019-05-07 01:54:20 +03:00
eprintln! ( $val ) ;
}
2019-05-12 17:37:07 +03:00
$val
}
2019-05-07 01:54:20 +03:00
} ;
2019-05-01 19:20:33 +03:00
( $val :expr ) = > {
2019-09-21 21:25:21 +03:00
if cfg! ( feature = " debug-tracing " ) {
2019-07-15 00:04:20 +03:00
let stringify = stringify! ( $val ) ;
2019-07-09 13:05:11 +03:00
// Use of `match` here is intentional because it affects the lifetimes
// of temporaries - https://stackoverflow.com/a/48732525/1063961
match $val {
tmp = > {
2020-05-16 12:46:01 +03:00
log_tag! ( ) ;
2019-07-15 00:04:20 +03:00
eprintln! ( " {} = {:?} " , stringify , tmp ) ;
2019-07-09 13:05:11 +03:00
tmp
}
}
} else {
$val
2019-05-12 17:37:07 +03:00
}
2019-05-01 19:20:33 +03:00
} ;
( $fmt :literal , $( $arg :tt ) * ) = > {
2019-09-21 21:25:21 +03:00
if cfg! ( feature = " debug-tracing " ) {
2020-05-16 12:46:01 +03:00
log_tag! ( ) ;
2019-05-01 19:20:33 +03:00
eprintln! ( $fmt , $( $arg ) * ) ;
}
} ;
}
}
2019-09-21 21:23:06 +03:00
#[ cfg(feature = " unicode_algorithms " ) ]
2020-02-04 17:26:25 +02:00
pub mod text_processing ;
2019-07-22 15:14:39 +03:00
2020-01-06 16:10:36 +02:00
pub mod datetime ;
pub use datetime ::UnixTimestamp ;
2019-09-15 20:39:16 +03:00
#[ macro_use ]
mod logging ;
pub use self ::logging ::LoggingLevel ::* ;
pub use self ::logging ::* ;
2019-03-14 12:19:25 +02:00
pub mod addressbook ;
2020-09-09 14:24:30 +03:00
pub use addressbook ::* ;
2019-05-26 15:54:45 +03:00
pub mod backends ;
2020-09-09 14:24:30 +03:00
pub use backends ::* ;
2019-05-26 15:54:45 +03:00
mod collection ;
2020-09-09 14:24:30 +03:00
pub use collection ::* ;
2017-09-07 23:00:08 +03:00
pub mod conf ;
2020-09-09 14:24:30 +03:00
pub use conf ::* ;
2019-05-26 15:54:45 +03:00
pub mod email ;
2020-09-09 14:24:30 +03:00
pub use email ::* ;
2017-09-07 23:00:08 +03:00
pub mod error ;
2020-09-09 14:24:30 +03:00
pub use crate ::error ::* ;
2019-05-26 15:54:45 +03:00
pub mod thread ;
2020-09-09 14:24:30 +03:00
pub use thread ::* ;
2020-06-27 21:40:46 +03:00
pub mod connections ;
2019-11-07 09:46:09 +02:00
pub mod parsec ;
2020-04-04 19:49:54 +03:00
pub mod search ;
2017-09-07 23:00:08 +03:00
2020-07-13 19:00:13 +03:00
#[ cfg(feature = " smtp " ) ]
pub mod smtp ;
2020-05-30 15:35:51 +03:00
#[ cfg(feature = " sqlite3 " ) ]
pub mod sqlite3 ;
2017-09-07 23:00:08 +03:00
#[ macro_use ]
extern crate serde_derive ;
/* parser */
2018-07-24 20:20:32 +03:00
extern crate data_encoding ;
2018-07-27 21:37:56 +03:00
extern crate encoding ;
2020-09-09 14:24:30 +03:00
pub extern crate nom ;
2017-09-16 19:15:51 +03:00
#[ macro_use ]
extern crate bitflags ;
2020-09-09 14:24:30 +03:00
pub extern crate futures ;
2020-08-18 12:20:23 +03:00
pub extern crate indexmap ;
2020-09-09 14:24:30 +03:00
pub extern crate smallvec ;
pub extern crate smol ;
pub extern crate uuid ;
2020-09-16 19:57:06 +03:00
pub extern crate xdg_utils ;
2019-09-26 18:27:13 +03:00
pub use shellexpand ::ShellExpandTrait ;
pub mod shellexpand {
2020-01-21 02:42:18 +02:00
use smallvec ::SmallVec ;
2020-05-30 14:09:54 +03:00
use std ::ffi ::OsStr ;
use std ::os ::unix ::ffi ::OsStrExt ;
use std ::os ::unix ::io ::AsRawFd ;
2020-01-21 02:42:18 +02:00
use std ::path ::{ Path , PathBuf } ;
2019-09-26 18:27:13 +03:00
pub trait ShellExpandTrait {
fn expand ( & self ) -> PathBuf ;
2020-01-21 02:42:18 +02:00
fn complete ( & self , force : bool ) -> SmallVec < [ String ; 128 ] > ;
2019-09-26 18:27:13 +03:00
}
impl ShellExpandTrait for Path {
fn expand ( & self ) -> PathBuf {
let mut ret = PathBuf ::new ( ) ;
for c in self . components ( ) {
let c_to_str = c . as_os_str ( ) . to_str ( ) ;
match c_to_str {
Some ( " ~ " ) = > {
2020-08-25 16:39:12 +03:00
if let Ok ( home_dir ) = std ::env ::var ( " HOME " ) {
2019-09-26 18:27:13 +03:00
ret . push ( home_dir )
} else {
return PathBuf ::new ( ) ;
}
}
2020-08-25 16:39:12 +03:00
Some ( var ) if var . starts_with ( '$' ) = > {
2019-09-26 18:27:13 +03:00
let env_name = var . split_at ( 1 ) . 1 ;
if env_name . chars ( ) . all ( char ::is_uppercase ) {
2020-08-25 16:39:12 +03:00
ret . push ( std ::env ::var ( env_name ) . unwrap_or_default ( ) ) ;
2019-09-26 18:27:13 +03:00
} else {
ret . push ( c ) ;
}
}
Some ( _ ) = > {
ret . push ( c ) ;
}
None = > {
/* path is invalid */
return PathBuf ::new ( ) ;
}
}
}
ret
}
2020-01-21 02:42:18 +02:00
2020-05-30 14:09:54 +03:00
#[ cfg(target_os = " linux " ) ]
2020-01-21 02:42:18 +02:00
fn complete ( & self , force : bool ) -> SmallVec < [ String ; 128 ] > {
use libc ::dirent64 ;
use nix ::fcntl ::OFlag ;
const BUF_SIZE : ::libc ::size_t = 8 < < 10 ;
let ( prefix , _match ) = if self . as_os_str ( ) . as_bytes ( ) . ends_with ( b " /. " ) {
( self . components ( ) . as_path ( ) , OsStr ::from_bytes ( b " . " ) )
} else {
if self . exists ( ) & & ( ! force | | self . as_os_str ( ) . as_bytes ( ) . ends_with ( b " / " ) ) {
// println!("{} {:?}", self.display(), self.components().last());
return SmallVec ::new ( ) ;
} else {
let last_component = self
. components ( )
. last ( )
. map ( | c | c . as_os_str ( ) )
. unwrap_or ( OsStr ::from_bytes ( b " " ) ) ;
let prefix = if let Some ( p ) = self . parent ( ) {
p
} else {
return SmallVec ::new ( ) ;
} ;
( prefix , last_component )
}
} ;
let dir = match ::nix ::dir ::Dir ::openat (
::libc ::AT_FDCWD ,
prefix ,
OFlag ::O_DIRECTORY | OFlag ::O_NOATIME | OFlag ::O_RDONLY | OFlag ::O_CLOEXEC ,
::nix ::sys ::stat ::Mode ::S_IRUSR | ::nix ::sys ::stat ::Mode ::S_IXUSR ,
)
. or_else ( | _ | {
::nix ::dir ::Dir ::openat (
::libc ::AT_FDCWD ,
prefix ,
OFlag ::O_DIRECTORY | OFlag ::O_RDONLY | OFlag ::O_CLOEXEC ,
::nix ::sys ::stat ::Mode ::S_IRUSR | ::nix ::sys ::stat ::Mode ::S_IXUSR ,
)
} ) {
Ok ( dir ) = > dir ,
Err ( err ) = > {
debug! ( prefix ) ;
debug! ( err ) ;
return SmallVec ::new ( ) ;
}
} ;
let mut buf : Vec < u8 > = Vec ::with_capacity ( BUF_SIZE ) ;
let mut entries = SmallVec ::new ( ) ;
loop {
let n : i64 = unsafe {
::libc ::syscall (
::libc ::SYS_getdents64 ,
dir . as_raw_fd ( ) ,
buf . as_ptr ( ) ,
BUF_SIZE - 256 ,
)
} ;
if n < 0 {
return SmallVec ::new ( ) ;
} else if n = = 0 {
break ;
}
let n = n as usize ;
unsafe {
buf . set_len ( n ) ;
}
let mut pos = 0 ;
while pos < n {
let dir = unsafe { std ::mem ::transmute ::< & [ u8 ] , & [ dirent64 ] > ( & buf [ pos .. ] ) } ;
let entry = unsafe { std ::ffi ::CStr ::from_ptr ( dir [ 0 ] . d_name . as_ptr ( ) ) } ;
if entry . to_bytes ( ) ! = b " . " & & entry . to_bytes ( ) ! = b " .. " {
if entry . to_bytes ( ) . starts_with ( _match . as_bytes ( ) ) {
if dir [ 0 ] . d_type = = ::libc ::DT_DIR & & ! entry . to_bytes ( ) . ends_with ( b " / " )
{
let mut s = unsafe {
String ::from_utf8_unchecked (
entry . to_bytes ( ) [ _match . as_bytes ( ) . len ( ) .. ] . to_vec ( ) ,
)
} ;
s . push ( '/' ) ;
entries . push ( s ) ;
} else {
entries . push ( unsafe {
String ::from_utf8_unchecked (
entry . to_bytes ( ) [ _match . as_bytes ( ) . len ( ) .. ] . to_vec ( ) ,
)
} ) ;
}
}
}
pos + = dir [ 0 ] . d_reclen as usize ;
}
// https://github.com/romkatv/gitstatus/blob/caf44f7aaf33d0f46e6749e50595323c277e0908/src/dir.cc
// "It's tempting to bail here if n + sizeof(linux_dirent64) + 512 <= n. After all, there
// was enough space for another entry but SYS_getdents64 didn't write it, so this must be
// the end of the directory listing, right? Unfortunately, no. SYS_getdents64 is finicky.
// It sometimes writes a partial list of entries even if the full list would fit."
}
2020-07-05 15:28:55 +03:00
entries
2020-01-21 02:42:18 +02:00
}
2020-05-30 14:09:54 +03:00
#[ cfg(not(target_os = " linux " )) ]
fn complete ( & self , force : bool ) -> SmallVec < [ String ; 128 ] > {
let mut entries = SmallVec ::new ( ) ;
let ( prefix , _match ) = {
if self . exists ( ) & & ( ! force | | self . as_os_str ( ) . as_bytes ( ) . ends_with ( b " / " ) ) {
// println!("{} {:?}", self.display(), self.components().last());
return entries ;
} else {
let last_component = self
. components ( )
. last ( )
. map ( | c | c . as_os_str ( ) )
. unwrap_or ( OsStr ::from_bytes ( b " " ) ) ;
let prefix = if let Some ( p ) = self . parent ( ) {
p
} else {
return entries ;
} ;
( prefix , last_component )
}
} ;
if force & & self . is_dir ( ) & & ! self . as_os_str ( ) . as_bytes ( ) . ends_with ( b " / " ) {
entries . push ( " / " . to_string ( ) ) ;
}
if let Ok ( iter ) = std ::fs ::read_dir ( & prefix ) {
for entry in iter {
if let Ok ( entry ) = entry {
if entry . path ( ) . as_os_str ( ) . as_bytes ( ) ! = b " . "
& & entry . path ( ) . as_os_str ( ) . as_bytes ( ) ! = b " .. "
& & entry
. path ( )
. as_os_str ( )
. as_bytes ( )
. starts_with ( _match . as_bytes ( ) )
{
if entry . path ( ) . is_dir ( )
& & ! entry . path ( ) . as_os_str ( ) . as_bytes ( ) . ends_with ( b " / " )
{
let mut s = unsafe {
String ::from_utf8_unchecked (
entry . path ( ) . as_os_str ( ) . as_bytes ( )
[ _match . as_bytes ( ) . len ( ) .. ]
. to_vec ( ) ,
)
} ;
s . push ( '/' ) ;
entries . push ( s ) ;
} else {
entries . push ( unsafe {
String ::from_utf8_unchecked (
entry . path ( ) . as_os_str ( ) . as_bytes ( )
[ _match . as_bytes ( ) . len ( ) .. ]
. to_vec ( ) ,
)
} ) ;
}
}
}
}
}
entries
}
2020-01-21 02:42:18 +02:00
}
#[ test ]
fn test_shellexpandtrait ( ) {
assert! ( Path ::new ( " ~ " ) . expand ( ) . complete ( false ) . is_empty ( ) ) ;
assert! ( ! Path ::new ( " ~ " ) . expand ( ) . complete ( true ) . is_empty ( ) ) ;
2019-09-26 18:27:13 +03:00
}
}