2019-06-28 19:34:40 +03:00
/*
* meli - imap module .
*
* Copyright 2017 - 2019 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/>.
* /
2020-05-30 14:40:47 +03:00
use super ::protocol_parser ::{ ImapLineSplit , ImapResponse , RequiredResponses } ;
use crate ::backends ::MailboxHash ;
2020-08-16 19:57:28 +03:00
use crate ::connections ::{ lookup_ipv4 , timeout , Connection } ;
2019-06-28 19:34:40 +03:00
use crate ::email ::parser ::BytesExt ;
use crate ::error ::* ;
2019-09-14 15:59:53 +03:00
extern crate native_tls ;
2020-07-06 11:12:38 +03:00
use futures ::io ::{ AsyncReadExt , AsyncWriteExt } ;
2019-09-14 15:59:53 +03:00
use native_tls ::TlsConnector ;
2020-07-06 11:12:38 +03:00
pub use smol ::Async as AsyncWrapper ;
2020-05-10 21:14:49 +03:00
use std ::collections ::HashSet ;
2020-08-24 11:46:07 +03:00
use std ::convert ::TryFrom ;
2020-07-06 11:12:38 +03:00
use std ::future ::Future ;
2019-09-14 15:59:53 +03:00
use std ::iter ::FromIterator ;
2020-07-06 11:12:38 +03:00
use std ::pin ::Pin ;
2020-05-30 14:40:47 +03:00
use std ::sync ::Arc ;
2020-08-16 19:57:28 +03:00
use std ::time ::{ Duration , Instant } ;
2019-09-14 15:59:53 +03:00
use super ::protocol_parser ;
2020-05-30 14:40:47 +03:00
use super ::{ Capabilities , ImapServerConf , UIDStore } ;
2019-06-28 19:34:40 +03:00
2020-07-27 14:59:49 +03:00
#[ derive(Debug, Clone, Copy) ]
2020-02-28 15:47:07 +02:00
pub enum ImapProtocol {
2020-07-27 14:59:49 +03:00
IMAP { extension_use : ImapExtensionUse } ,
2020-02-28 15:47:07 +02:00
ManageSieve ,
}
2020-07-27 14:59:49 +03:00
#[ derive(Debug, Clone, Copy) ]
pub struct ImapExtensionUse {
pub idle : bool ,
2020-07-28 16:16:08 +03:00
#[ cfg(feature = " deflate_compression " ) ]
pub deflate : bool ,
2020-07-27 14:59:49 +03:00
}
impl Default for ImapExtensionUse {
fn default ( ) -> Self {
Self {
idle : true ,
2020-07-28 16:16:08 +03:00
#[ cfg(feature = " deflate_compression " ) ]
2020-08-01 12:36:47 +03:00
deflate : true ,
2020-07-27 14:59:49 +03:00
}
}
}
2019-09-20 09:07:55 +03:00
#[ derive(Debug) ]
pub struct ImapStream {
2020-07-27 12:31:50 +03:00
pub cmd_id : usize ,
pub stream : AsyncWrapper < Connection > ,
pub protocol : ImapProtocol ,
pub current_mailbox : MailboxSelection ,
2019-09-20 09:07:55 +03:00
}
2020-07-06 11:32:03 +03:00
#[ derive(Debug, Copy, Clone, Eq, PartialEq) ]
pub enum MailboxSelection {
None ,
Select ( MailboxHash ) ,
Examine ( MailboxHash ) ,
}
impl MailboxSelection {
pub fn take ( & mut self ) -> Self {
std ::mem ::replace ( self , MailboxSelection ::None )
}
}
2020-07-06 11:12:38 +03:00
2020-07-13 18:53:34 +03:00
async fn try_await ( cl : impl Future < Output = Result < ( ) > > + Send ) -> Result < ( ) > {
cl . await
}
2019-06-28 19:34:40 +03:00
#[ derive(Debug) ]
pub struct ImapConnection {
2019-09-20 09:07:55 +03:00
pub stream : Result < ImapStream > ,
2019-09-21 21:02:35 +03:00
pub server_conf : ImapServerConf ,
2020-05-30 14:40:47 +03:00
pub uid_store : Arc < UIDStore > ,
2019-06-28 19:34:40 +03:00
}
2019-09-20 09:07:55 +03:00
impl ImapStream {
2020-07-06 11:12:38 +03:00
pub async fn new_connection (
server_conf : & ImapServerConf ,
) -> Result < ( Capabilities , ImapStream ) > {
2019-09-14 15:59:53 +03:00
use std ::net ::TcpStream ;
2019-09-21 21:02:35 +03:00
let path = & server_conf . server_hostname ;
2019-09-14 15:59:53 +03:00
2020-02-28 15:47:07 +02:00
let cmd_id = 1 ;
2020-07-06 11:12:38 +03:00
let stream = if server_conf . use_tls {
let mut connector = TlsConnector ::builder ( ) ;
if server_conf . danger_accept_invalid_certs {
connector . danger_accept_invalid_certs ( true ) ;
2020-02-28 15:47:07 +02:00
}
2020-07-13 18:53:34 +03:00
let connector = connector
. build ( )
. chain_err_kind ( crate ::error ::ErrorKind ::Network ) ? ;
2019-11-10 13:30:33 +02:00
2020-07-06 11:12:38 +03:00
let addr = if let Ok ( a ) = lookup_ipv4 ( path , server_conf . server_port ) {
a
} else {
return Err ( MeliError ::new ( format! (
" Could not lookup address {} " ,
& path
) ) ) ;
} ;
2020-07-13 18:53:34 +03:00
let mut socket = AsyncWrapper ::new ( Connection ::Tcp (
2020-08-16 19:57:28 +03:00
TcpStream ::connect_timeout ( & addr , Duration ::new ( 4 , 0 ) )
2020-07-13 18:53:34 +03:00
. chain_err_kind ( crate ::error ::ErrorKind ::Network ) ? ,
) )
. chain_err_kind ( crate ::error ::ErrorKind ::Network ) ? ;
2020-07-06 11:12:38 +03:00
if server_conf . use_starttls {
2020-07-28 16:16:08 +03:00
let mut buf = vec! [ 0 ; Connection ::IO_BUF_SIZE ] ;
2020-02-28 15:47:07 +02:00
match server_conf . protocol {
2020-07-27 14:59:49 +03:00
ImapProtocol ::IMAP { .. } = > socket
2020-07-13 18:53:34 +03:00
. write_all ( format! ( " M {} STARTTLS \r \n " , cmd_id ) . as_bytes ( ) )
. await
. chain_err_kind ( crate ::error ::ErrorKind ::Network ) ? ,
2020-02-28 15:47:07 +02:00
ImapProtocol ::ManageSieve = > {
2020-07-13 18:53:34 +03:00
socket
. read ( & mut buf )
. await
. chain_err_kind ( crate ::error ::ErrorKind ::Network ) ? ;
socket
. write_all ( b " STARTTLS \r \n " )
. await
. chain_err_kind ( crate ::error ::ErrorKind ::Network ) ? ;
2020-07-06 11:12:38 +03:00
}
}
2020-08-01 23:53:45 +03:00
socket
. flush ( )
. await
. chain_err_kind ( crate ::error ::ErrorKind ::Network ) ? ;
2020-07-06 11:12:38 +03:00
let mut response = String ::with_capacity ( 1024 ) ;
let mut broken = false ;
2020-08-16 19:57:28 +03:00
let now = Instant ::now ( ) ;
2020-07-06 11:12:38 +03:00
while now . elapsed ( ) . as_secs ( ) < 3 {
2020-07-13 18:53:34 +03:00
let len = socket
. read ( & mut buf )
. await
. chain_err_kind ( crate ::error ::ErrorKind ::Network ) ? ;
2020-07-06 11:12:38 +03:00
response . push_str ( unsafe { std ::str ::from_utf8_unchecked ( & buf [ 0 .. len ] ) } ) ;
match server_conf . protocol {
2020-07-27 14:59:49 +03:00
ImapProtocol ::IMAP { .. } = > {
2020-07-06 11:12:38 +03:00
if response . starts_with ( " * OK " ) & & response . find ( " \r \n " ) . is_some ( ) {
if let Some ( pos ) = response . as_bytes ( ) . find ( b " \r \n " ) {
response . drain ( 0 .. pos + 2 ) ;
}
}
}
ImapProtocol ::ManageSieve = > {
if response . starts_with ( " OK " ) & & response . find ( " \r \n " ) . is_some ( ) {
response . clear ( ) ;
broken = true ;
break ;
}
2020-02-28 15:47:07 +02:00
}
2019-09-14 15:59:53 +03:00
}
2020-07-06 11:12:38 +03:00
if response . starts_with ( " M1 OK " ) {
broken = true ;
break ;
}
2019-09-14 15:59:53 +03:00
}
2020-07-06 11:12:38 +03:00
if ! broken {
return Err ( MeliError ::new ( format! (
" Could not initiate TLS negotiation to {}. " ,
path
) ) ) ;
2019-09-14 15:59:53 +03:00
}
}
2020-07-06 11:12:38 +03:00
{
// FIXME: This is blocking
2020-07-13 18:53:34 +03:00
let socket = socket
. into_inner ( )
. chain_err_kind ( crate ::error ::ErrorKind ::Network ) ? ;
2020-07-06 11:12:38 +03:00
let mut conn_result = connector . connect ( path , socket ) ;
if let Err ( native_tls ::HandshakeError ::WouldBlock ( midhandshake_stream ) ) =
conn_result
{
let mut midhandshake_stream = Some ( midhandshake_stream ) ;
loop {
match midhandshake_stream . take ( ) . unwrap ( ) . handshake ( ) {
Ok ( r ) = > {
conn_result = Ok ( r ) ;
break ;
}
Err ( native_tls ::HandshakeError ::WouldBlock ( stream ) ) = > {
midhandshake_stream = Some ( stream ) ;
}
p = > {
2020-07-13 18:53:34 +03:00
p . chain_err_kind ( crate ::error ::ErrorKind ::Network ) ? ;
2020-07-06 11:12:38 +03:00
}
2019-09-14 15:59:53 +03:00
}
}
}
2020-07-13 18:53:34 +03:00
AsyncWrapper ::new ( Connection ::Tls (
conn_result . chain_err_kind ( crate ::error ::ErrorKind ::Network ) ? ,
) )
. chain_err_kind ( crate ::error ::ErrorKind ::Network ) ?
2019-09-14 15:59:53 +03:00
}
2020-07-06 11:12:38 +03:00
} else {
let addr = if let Ok ( a ) = lookup_ipv4 ( path , server_conf . server_port ) {
a
} else {
return Err ( MeliError ::new ( format! (
" Could not lookup address {} " ,
& path
) ) ) ;
} ;
2020-07-13 18:53:34 +03:00
AsyncWrapper ::new ( Connection ::Tcp (
2020-08-16 19:57:28 +03:00
TcpStream ::connect_timeout ( & addr , Duration ::new ( 4 , 0 ) )
2020-07-13 18:53:34 +03:00
. chain_err_kind ( crate ::error ::ErrorKind ::Network ) ? ,
) )
. chain_err_kind ( crate ::error ::ErrorKind ::Network ) ?
2019-09-14 15:59:53 +03:00
} ;
2019-12-11 00:01:22 +02:00
let mut res = String ::with_capacity ( 8 * 1024 ) ;
2020-02-28 15:47:07 +02:00
let mut ret = ImapStream {
cmd_id ,
stream ,
protocol : server_conf . protocol ,
2020-07-27 12:31:50 +03:00
current_mailbox : MailboxSelection ::None ,
2020-02-28 15:47:07 +02:00
} ;
if let ImapProtocol ::ManageSieve = server_conf . protocol {
use data_encoding ::BASE64 ;
2020-07-06 11:12:38 +03:00
ret . read_response ( & mut res ) . await ? ;
2020-02-28 15:47:07 +02:00
ret . send_command (
format! (
" AUTHENTICATE \" PLAIN \" \" {} \" " ,
BASE64 . encode (
format! (
" \0 {} \0 {} " ,
& server_conf . server_username , & server_conf . server_password
)
. as_bytes ( )
)
)
. as_bytes ( ) ,
2020-07-06 11:12:38 +03:00
)
. await ? ;
ret . read_response ( & mut res ) . await ? ;
2020-02-28 15:47:07 +02:00
return Ok ( ( Default ::default ( ) , ret ) ) ;
}
2020-07-06 11:12:38 +03:00
ret . send_command ( b " CAPABILITY " ) . await ? ;
ret . read_response ( & mut res ) . await ? ;
2019-12-11 00:01:22 +02:00
let capabilities : std ::result ::Result < Vec < & [ u8 ] > , _ > = res
. split_rn ( )
. find ( | l | l . starts_with ( " * CAPABILITY " ) )
. ok_or_else ( | | MeliError ::new ( " " ) )
. and_then ( | res | {
protocol_parser ::capabilities ( res . as_bytes ( ) )
. map_err ( | _ | MeliError ::new ( " " ) )
2020-06-06 19:38:20 +03:00
. map ( | ( _ , v ) | v )
2019-12-11 00:01:22 +02:00
} ) ;
if capabilities . is_err ( ) {
return Err ( MeliError ::new ( format! (
" Could not connect to {}: expected CAPABILITY response but got:{} " ,
& server_conf . server_hostname , res
) ) ) ;
}
let capabilities = capabilities . unwrap ( ) ;
if ! capabilities
. iter ( )
. any ( | cap | cap . eq_ignore_ascii_case ( b " IMAP4rev1 " ) )
{
return Err ( MeliError ::new ( format! (
" Could not connect to {}: server is not IMAP4rev1 compliant " ,
& server_conf . server_hostname
) ) ) ;
} else if capabilities
. iter ( )
. any ( | cap | cap . eq_ignore_ascii_case ( b " LOGINDISABLED " ) )
{
return Err ( MeliError ::new ( format! (
" Could not connect to {}: server does not accept logins [LOGINDISABLED] " ,
& server_conf . server_hostname
2020-07-13 18:53:34 +03:00
) )
. set_err_kind ( crate ::error ::ErrorKind ::Authentication ) ) ;
2019-12-11 00:01:22 +02:00
}
let mut capabilities = None ;
2019-09-14 15:59:53 +03:00
ret . send_command (
2019-09-21 21:02:35 +03:00
format! (
" LOGIN \" {} \" \" {} \" " ,
& server_conf . server_username , & server_conf . server_password
)
. as_bytes ( ) ,
2020-07-06 11:12:38 +03:00
)
. await ? ;
2019-11-23 17:50:22 +02:00
let tag_start = format! ( " M {} " , ( ret . cmd_id - 1 ) ) ;
2019-12-11 00:01:22 +02:00
2019-11-23 17:50:22 +02:00
loop {
2020-07-06 11:12:38 +03:00
ret . read_lines ( & mut res , & String ::new ( ) , false ) . await ? ;
2019-12-11 00:01:22 +02:00
let mut should_break = false ;
for l in res . split_rn ( ) {
if l . starts_with ( " * CAPABILITY " ) {
capabilities = protocol_parser ::capabilities ( l . as_bytes ( ) )
2020-06-06 19:38:20 +03:00
. map ( | ( _ , capabilities ) | {
2020-05-10 21:14:49 +03:00
HashSet ::from_iter ( capabilities . into_iter ( ) . map ( | s : & [ u8 ] | s . to_vec ( ) ) )
2019-12-11 00:01:22 +02:00
} )
. ok ( ) ;
2019-11-23 17:50:22 +02:00
}
2019-12-11 00:01:22 +02:00
if l . starts_with ( tag_start . as_str ( ) ) {
if ! l [ tag_start . len ( ) .. ] . trim ( ) . starts_with ( " OK " ) {
return Err ( MeliError ::new ( format! (
" Could not connect. Server replied with '{}' " ,
l [ tag_start . len ( ) .. ] . trim ( )
2020-07-13 18:53:34 +03:00
) )
. set_err_kind ( crate ::error ::ErrorKind ::Authentication ) ) ;
2019-12-11 00:01:22 +02:00
}
should_break = true ;
2019-11-23 17:50:22 +02:00
}
2019-12-11 00:01:22 +02:00
}
if should_break {
2019-11-23 17:50:22 +02:00
break ;
}
}
2020-07-06 11:12:38 +03:00
if capabilities . is_none ( ) {
2019-11-23 17:50:22 +02:00
/* sending CAPABILITY after LOGIN automatically is an RFC recommendation, so check
* for lazy servers * /
drop ( capabilities ) ;
2020-07-06 11:12:38 +03:00
ret . send_command ( b " CAPABILITY " ) . await ? ;
ret . read_response ( & mut res ) . await . unwrap ( ) ;
2020-06-06 19:38:20 +03:00
let capabilities = protocol_parser ::capabilities ( res . as_bytes ( ) ) ? . 1 ;
2020-05-10 21:14:49 +03:00
let capabilities = HashSet ::from_iter ( capabilities . into_iter ( ) . map ( | s | s . to_vec ( ) ) ) ;
2019-11-23 17:50:22 +02:00
Ok ( ( capabilities , ret ) )
2020-07-06 11:12:38 +03:00
} else {
let capabilities = capabilities . unwrap ( ) ;
Ok ( ( capabilities , ret ) )
2019-11-23 17:50:22 +02:00
}
2019-09-14 15:59:53 +03:00
}
2020-01-15 12:31:49 +02:00
2020-07-06 11:12:38 +03:00
pub async fn read_response ( & mut self , ret : & mut String ) -> Result < ( ) > {
2020-02-28 15:47:07 +02:00
let id = match self . protocol {
2020-07-27 14:59:49 +03:00
ImapProtocol ::IMAP { .. } = > format! ( " M {} " , self . cmd_id - 1 ) ,
2020-02-28 15:47:07 +02:00
ImapProtocol ::ManageSieve = > String ::new ( ) ,
} ;
2020-07-06 11:12:38 +03:00
self . read_lines ( ret , & id , true ) . await ? ;
Ok ( ( ) )
2020-01-15 12:31:49 +02:00
}
2020-07-06 11:12:38 +03:00
pub async fn read_lines (
2020-01-15 12:31:49 +02:00
& mut self ,
ret : & mut String ,
termination_string : & str ,
keep_termination_string : bool ,
) -> Result < ( ) > {
2020-07-28 16:16:08 +03:00
let mut buf : Vec < u8 > = vec! [ 0 ; Connection ::IO_BUF_SIZE ] ;
2020-01-15 12:31:49 +02:00
ret . clear ( ) ;
let mut last_line_idx : usize = 0 ;
loop {
2020-08-16 19:57:28 +03:00
match timeout ( Duration ::from_secs ( 16 ) , self . stream . read ( & mut buf ) ) . await ? {
2020-01-15 12:31:49 +02:00
Ok ( 0 ) = > break ,
Ok ( b ) = > {
ret . push_str ( unsafe { std ::str ::from_utf8_unchecked ( & buf [ 0 .. b ] ) } ) ;
if let Some ( mut pos ) = ret [ last_line_idx .. ] . rfind ( " \r \n " ) {
if ret [ last_line_idx .. ] . starts_with ( " * BYE " ) {
return Err ( MeliError ::new ( " Disconnected " ) ) ;
}
if let Some ( prev_line ) =
ret [ last_line_idx .. pos + last_line_idx ] . rfind ( " \r \n " )
{
2020-07-06 11:12:38 +03:00
last_line_idx + = prev_line + " \r \n " . len ( ) ;
pos - = prev_line + " \r \n " . len ( ) ;
2020-01-15 12:31:49 +02:00
}
2020-07-06 11:12:38 +03:00
if Some ( pos + " \r \n " . len ( ) ) = = ret . get ( last_line_idx .. ) . map ( | r | r . len ( ) ) {
2020-01-15 12:31:49 +02:00
if ! termination_string . is_empty ( )
& & ret [ last_line_idx .. ] . starts_with ( termination_string )
{
debug! ( & ret [ last_line_idx .. ] ) ;
if ! keep_termination_string {
ret . replace_range ( last_line_idx .. , " " ) ;
}
break ;
} else if termination_string . is_empty ( ) {
break ;
}
}
2020-07-06 11:12:38 +03:00
last_line_idx + = pos + " \r \n " . len ( ) ;
2020-01-15 12:31:49 +02:00
}
}
Err ( e ) = > {
2020-07-13 18:53:34 +03:00
return Err ( MeliError ::from ( e ) . set_err_kind ( crate ::error ::ErrorKind ::Network ) ) ;
2020-01-15 12:31:49 +02:00
}
}
}
2020-07-06 11:12:38 +03:00
//debug!("returning IMAP response:\n{:?}", &ret);
2020-01-15 12:31:49 +02:00
Ok ( ( ) )
}
2020-07-06 11:12:38 +03:00
pub async fn wait_for_continuation_request ( & mut self ) -> Result < ( ) > {
2020-01-15 12:31:49 +02:00
let term = " + " . to_string ( ) ;
let mut ret = String ::new ( ) ;
2020-07-06 11:12:38 +03:00
self . read_lines ( & mut ret , & term , false ) . await ? ;
Ok ( ( ) )
2020-01-15 12:31:49 +02:00
}
2020-07-06 11:12:38 +03:00
pub async fn send_command ( & mut self , command : & [ u8 ] ) -> Result < ( ) > {
2020-08-16 19:57:28 +03:00
if let Err ( err ) = timeout (
Duration ::from_secs ( 16 ) ,
try_await ( async move {
let command = command . trim ( ) ;
match self . protocol {
ImapProtocol ::IMAP { .. } = > {
self . stream . write_all ( b " M " ) . await ? ;
self . stream
. write_all ( self . cmd_id . to_string ( ) . as_bytes ( ) )
. await ? ;
self . stream . write_all ( b " " ) . await ? ;
self . cmd_id + = 1 ;
}
ImapProtocol ::ManageSieve = > { }
2020-07-13 18:53:34 +03:00
}
2020-02-28 15:47:07 +02:00
2020-08-16 19:57:28 +03:00
self . stream . write_all ( command ) . await ? ;
self . stream . write_all ( b " \r \n " ) . await ? ;
self . stream . flush ( ) . await ? ;
match self . protocol {
ImapProtocol ::IMAP { .. } = > {
debug! ( " sent: M{} {} " , self . cmd_id - 1 , unsafe {
std ::str ::from_utf8_unchecked ( command )
} ) ;
}
ImapProtocol ::ManageSieve = > { }
2020-07-13 18:53:34 +03:00
}
2020-08-16 19:57:28 +03:00
Ok ( ( ) )
} ) ,
)
2020-07-13 18:53:34 +03:00
. await
{
Err ( err . set_err_kind ( crate ::error ::ErrorKind ::Network ) )
} else {
Ok ( ( ) )
2020-02-28 15:47:07 +02:00
}
2020-01-15 12:31:49 +02:00
}
2020-07-06 11:12:38 +03:00
pub async fn send_literal ( & mut self , data : & [ u8 ] ) -> Result < ( ) > {
2020-07-13 18:53:34 +03:00
if let Err ( err ) = try_await ( async move {
self . stream . write_all ( data ) . await ? ;
self . stream . write_all ( b " \r \n " ) . await ? ;
2020-08-01 23:53:45 +03:00
self . stream . flush ( ) . await ? ;
2020-07-13 18:53:34 +03:00
Ok ( ( ) )
} )
. await
{
Err ( err . set_err_kind ( crate ::error ::ErrorKind ::Network ) )
} else {
Ok ( ( ) )
}
2020-01-15 12:31:49 +02:00
}
2020-07-06 11:12:38 +03:00
pub async fn send_raw ( & mut self , raw : & [ u8 ] ) -> Result < ( ) > {
2020-07-13 18:53:34 +03:00
if let Err ( err ) = try_await ( async move {
self . stream . write_all ( raw ) . await ? ;
self . stream . write_all ( b " \r \n " ) . await ? ;
2020-08-01 23:53:45 +03:00
self . stream . flush ( ) . await ? ;
2020-07-13 18:53:34 +03:00
Ok ( ( ) )
} )
. await
{
Err ( err . set_err_kind ( crate ::error ::ErrorKind ::Network ) )
} else {
Ok ( ( ) )
}
2020-01-15 12:31:49 +02:00
}
2019-06-28 19:34:40 +03:00
}
2019-09-20 09:07:55 +03:00
impl ImapConnection {
2019-12-14 18:46:12 +02:00
pub fn new_connection (
server_conf : & ImapServerConf ,
2020-05-30 14:40:47 +03:00
uid_store : Arc < UIDStore > ,
2019-12-14 18:46:12 +02:00
) -> ImapConnection {
2019-09-20 09:07:55 +03:00
ImapConnection {
stream : Err ( MeliError ::new ( " Offline " . to_string ( ) ) ) ,
2019-09-21 21:02:35 +03:00
server_conf : server_conf . clone ( ) ,
2020-05-30 14:40:47 +03:00
uid_store ,
2019-09-20 09:07:55 +03:00
}
}
2020-07-28 16:16:08 +03:00
pub fn connect < ' a > ( & ' a mut self ) -> Pin < Box < dyn Future < Output = Result < ( ) > > + Send + ' a > > {
Box ::pin ( async move {
if let ( instant , ref mut status @ Ok ( ( ) ) ) = * self . uid_store . is_online . lock ( ) . unwrap ( ) {
2020-08-16 19:57:28 +03:00
if Instant ::now ( ) . duration_since ( instant ) > = Duration ::new ( 60 * 30 , 0 ) {
2020-07-28 16:16:08 +03:00
* status = Err ( MeliError ::new ( " Connection timed out " ) ) ;
self . stream = Err ( MeliError ::new ( " Connection timed out " ) ) ;
}
2020-02-22 11:22:11 +02:00
}
2020-07-28 16:16:08 +03:00
if self . stream . is_ok ( ) {
self . uid_store . is_online . lock ( ) . unwrap ( ) . 0 = Instant ::now ( ) ;
return Ok ( ( ) ) ;
}
let new_stream = debug! ( ImapStream ::new_connection ( & self . server_conf ) . await ) ;
if let Err ( err ) = new_stream . as_ref ( ) {
* self . uid_store . is_online . lock ( ) . unwrap ( ) = ( Instant ::now ( ) , Err ( err . clone ( ) ) ) ;
} else {
* self . uid_store . is_online . lock ( ) . unwrap ( ) = ( Instant ::now ( ) , Ok ( ( ) ) ) ;
}
let ( capabilities , stream ) = new_stream ? ;
self . stream = Ok ( stream ) ;
match self . stream . as_ref ( ) ? . protocol {
ImapProtocol ::IMAP {
extension_use :
ImapExtensionUse {
#[ cfg(feature = " deflate_compression " ) ]
deflate ,
idle : _idle ,
} ,
} = >
{
#[ cfg(feature = " deflate_compression " ) ]
if capabilities . contains ( & b " COMPRESS=DEFLATE " [ .. ] ) & & deflate {
let mut ret = String ::new ( ) ;
self . send_command ( b " COMPRESS DEFLATE " ) . await ? ;
self . read_response ( & mut ret , RequiredResponses ::empty ( ) )
. await ? ;
2020-08-24 11:46:07 +03:00
match ImapResponse ::try_from ( ret . as_str ( ) ) ? {
2020-07-28 16:16:08 +03:00
ImapResponse ::No ( code )
| ImapResponse ::Bad ( code )
| ImapResponse ::Preauth ( code )
| ImapResponse ::Bye ( code ) = > {
crate ::log ( format! ( " Could not use COMPRESS=DEFLATE in account ` {} `: server replied with ` {} ` " , self . uid_store . account_name , code ) , crate ::LoggingLevel ::WARN ) ;
}
ImapResponse ::Ok ( _ ) = > {
let ImapStream {
cmd_id ,
stream ,
protocol ,
current_mailbox ,
} = std ::mem ::replace ( & mut self . stream , Err ( MeliError ::new ( " " ) ) ) ? ;
let stream = stream . into_inner ( ) ? ;
self . stream = Ok ( ImapStream {
cmd_id ,
stream : AsyncWrapper ::new ( stream . deflate ( ) ) ? ,
protocol ,
current_mailbox ,
} ) ;
}
}
}
}
ImapProtocol ::ManageSieve = > { }
}
* self . uid_store . capabilities . lock ( ) . unwrap ( ) = capabilities ;
Ok ( ( ) )
} )
2020-02-22 11:22:11 +02:00
}
2020-07-06 11:12:38 +03:00
pub fn read_response < ' a > (
& ' a mut self ,
ret : & ' a mut String ,
2020-05-30 14:40:47 +03:00
required_responses : RequiredResponses ,
2020-07-06 11:12:38 +03:00
) -> Pin < Box < dyn Future < Output = Result < ( ) > > + Send + ' a > > {
Box ::pin ( async move {
let mut response = String ::new ( ) ;
ret . clear ( ) ;
self . stream . as_mut ( ) ? . read_response ( & mut response ) . await ? ;
2020-02-28 15:47:07 +02:00
2020-07-06 11:12:38 +03:00
match self . server_conf . protocol {
2020-07-27 14:59:49 +03:00
ImapProtocol ::IMAP { .. } = > {
2020-08-24 11:46:07 +03:00
let r : ImapResponse = ImapResponse ::try_from ( response . as_str ( ) ) ? ;
2020-07-06 11:12:38 +03:00
match r {
ImapResponse ::Bye ( ref response_code ) = > {
self . stream = Err ( MeliError ::new ( format! (
" Offline: received BYE: {:?} " ,
response_code
) ) ) ;
ret . push_str ( & response ) ;
2020-08-24 11:46:07 +03:00
return r . into ( ) ;
2020-07-06 11:12:38 +03:00
}
ImapResponse ::No ( ref response_code ) = > {
2020-07-24 22:05:01 +03:00
//FIXME return error
2020-07-06 11:12:38 +03:00
debug! ( " Received NO response: {:?} {:?} " , response_code , response ) ;
ret . push_str ( & response ) ;
2020-08-24 11:46:07 +03:00
return r . into ( ) ;
2020-07-06 11:12:38 +03:00
}
ImapResponse ::Bad ( ref response_code ) = > {
2020-07-24 22:05:01 +03:00
//FIXME return error
2020-07-06 11:12:38 +03:00
debug! ( " Received BAD response: {:?} {:?} " , response_code , response ) ;
ret . push_str ( & response ) ;
2020-08-24 11:46:07 +03:00
return r . into ( ) ;
2020-07-06 11:12:38 +03:00
}
2020-08-24 11:46:07 +03:00
_ = > { }
}
/* debug!(
" check every line for required_responses: {:#?} " ,
& required_responses
) ; * /
for l in response . split_rn ( ) {
/* debug!("check line: {}", &l); */
if required_responses . check ( l ) | | ! self . process_untagged ( l ) . await ? {
ret . push_str ( l ) ;
2020-05-30 14:40:47 +03:00
}
}
2020-08-24 11:46:07 +03:00
Ok ( ( ) )
2020-07-06 11:12:38 +03:00
}
ImapProtocol ::ManageSieve = > {
ret . push_str ( & response ) ;
Ok ( ( ) )
2020-02-28 15:47:07 +02:00
}
2020-05-30 14:40:47 +03:00
}
2020-07-06 11:12:38 +03:00
} )
2019-09-20 09:07:55 +03:00
}
2020-07-06 11:12:38 +03:00
pub async fn read_lines ( & mut self , ret : & mut String , termination_string : String ) -> Result < ( ) > {
self . stream
. as_mut ( ) ?
. read_lines ( ret , & termination_string , false )
. await ? ;
Ok ( ( ) )
2019-09-20 09:07:55 +03:00
}
2020-07-06 11:12:38 +03:00
pub async fn wait_for_continuation_request ( & mut self ) -> Result < ( ) > {
self . stream
. as_mut ( ) ?
. wait_for_continuation_request ( )
. await ? ;
Ok ( ( ) )
2019-09-20 09:07:55 +03:00
}
2020-07-06 11:12:38 +03:00
pub async fn send_command ( & mut self , command : & [ u8 ] ) -> Result < ( ) > {
2020-07-13 18:53:34 +03:00
if let Err ( err ) =
try_await ( async { self . stream . as_mut ( ) ? . send_command ( command ) . await } ) . await
{
2020-07-29 14:33:09 +03:00
self . stream = Err ( err . clone ( ) ) ;
2020-07-13 18:53:34 +03:00
if err . kind . is_network ( ) {
self . connect ( ) . await ? ;
}
Err ( err )
} else {
Ok ( ( ) )
}
2019-09-20 09:07:55 +03:00
}
2020-07-06 11:12:38 +03:00
pub async fn send_literal ( & mut self , data : & [ u8 ] ) -> Result < ( ) > {
2020-07-13 18:53:34 +03:00
if let Err ( err ) = try_await ( async { self . stream . as_mut ( ) ? . send_literal ( data ) . await } ) . await
{
2020-07-29 14:33:09 +03:00
self . stream = Err ( err . clone ( ) ) ;
2020-07-13 18:53:34 +03:00
if err . kind . is_network ( ) {
self . connect ( ) . await ? ;
}
Err ( err )
} else {
Ok ( ( ) )
}
2020-01-15 12:31:49 +02:00
}
2020-07-06 11:12:38 +03:00
pub async fn send_raw ( & mut self , raw : & [ u8 ] ) -> Result < ( ) > {
2020-07-13 18:53:34 +03:00
if let Err ( err ) = try_await ( async { self . stream . as_mut ( ) ? . send_raw ( raw ) . await } ) . await {
2020-07-29 14:33:09 +03:00
self . stream = Err ( err . clone ( ) ) ;
2020-07-13 18:53:34 +03:00
if err . kind . is_network ( ) {
self . connect ( ) . await ? ;
}
Err ( err )
} else {
Ok ( ( ) )
}
2019-09-20 09:07:55 +03:00
}
2020-05-30 14:40:47 +03:00
2020-07-06 11:12:38 +03:00
pub async fn select_mailbox (
2020-07-06 11:32:03 +03:00
& mut self ,
mailbox_hash : MailboxHash ,
ret : & mut String ,
force : bool ,
) -> Result < ( ) > {
2020-07-27 12:31:50 +03:00
if ! force & & self . stream . as_ref ( ) ? . current_mailbox = = MailboxSelection ::Select ( mailbox_hash )
{
2020-07-06 11:32:03 +03:00
return Ok ( ( ) ) ;
}
2020-05-30 14:40:47 +03:00
self . send_command (
format! (
" SELECT \" {} \" " ,
2020-07-06 11:12:38 +03:00
self . uid_store . mailboxes . lock ( ) . await [ & mailbox_hash ] . imap_path ( )
2020-05-30 14:40:47 +03:00
)
. as_bytes ( ) ,
2020-07-06 11:12:38 +03:00
)
. await ? ;
self . read_response ( ret , RequiredResponses ::SELECT_REQUIRED )
. await ? ;
2020-05-30 14:40:47 +03:00
debug! ( " select response {} " , ret ) ;
2020-07-27 12:31:50 +03:00
self . stream . as_mut ( ) ? . current_mailbox = MailboxSelection ::Select ( mailbox_hash ) ;
2020-05-30 14:40:47 +03:00
Ok ( ( ) )
}
2020-07-06 11:12:38 +03:00
pub async fn examine_mailbox (
2020-07-06 11:32:03 +03:00
& mut self ,
mailbox_hash : MailboxHash ,
ret : & mut String ,
force : bool ,
) -> Result < ( ) > {
2020-07-27 12:31:50 +03:00
if ! force
& & self . stream . as_ref ( ) ? . current_mailbox = = MailboxSelection ::Examine ( mailbox_hash )
{
2020-07-06 11:32:03 +03:00
return Ok ( ( ) ) ;
}
2020-05-30 14:40:47 +03:00
self . send_command (
format! (
" EXAMINE \" {} \" " ,
2020-07-06 11:12:38 +03:00
self . uid_store . mailboxes . lock ( ) . await [ & mailbox_hash ] . imap_path ( )
2020-05-30 14:40:47 +03:00
)
. as_bytes ( ) ,
2020-07-06 11:12:38 +03:00
)
. await ? ;
self . read_response ( ret , RequiredResponses ::EXAMINE_REQUIRED )
. await ? ;
2020-05-30 14:40:47 +03:00
debug! ( " examine response {} " , ret ) ;
2020-07-27 12:31:50 +03:00
self . stream . as_mut ( ) ? . current_mailbox = MailboxSelection ::Examine ( mailbox_hash ) ;
2020-05-30 14:40:47 +03:00
Ok ( ( ) )
}
2020-07-06 11:12:38 +03:00
pub async fn unselect ( & mut self ) -> Result < ( ) > {
2020-07-27 12:31:50 +03:00
match self . stream . as_mut ( ) ? . current_mailbox . take ( ) {
2020-07-06 11:12:38 +03:00
MailboxSelection ::Examine ( mailbox_hash ) |
MailboxSelection ::Select ( mailbox_hash ) = > {
let mut response = String ::with_capacity ( 8 * 1024 ) ;
if self
. uid_store
. capabilities
. lock ( )
. unwrap ( )
. iter ( )
. any ( | cap | cap . eq_ignore_ascii_case ( b " UNSELECT " ) )
{
self . send_command ( b " UNSELECT " ) . await ? ;
self . read_response ( & mut response , RequiredResponses ::empty ( ) )
. await ? ;
} else {
/* `RFC3691 - UNSELECT Command` states: "[..] IMAP4 provides this
* functionality ( via a SELECT command with a nonexistent mailbox name or
* reselecting the same mailbox with EXAMINE command ) [ .. ]
* /
self . select_mailbox ( mailbox_hash , & mut response , true ) . await ? ;
self . examine_mailbox ( mailbox_hash , & mut response , true ) . await ? ;
}
} ,
2020-07-06 11:32:03 +03:00
MailboxSelection ::None = > { } ,
2020-05-30 14:40:47 +03:00
}
Ok ( ( ) )
}
pub fn add_refresh_event ( & mut self , ev : crate ::backends ::RefreshEvent ) {
2020-08-20 01:55:24 +03:00
( self . uid_store . event_consumer ) (
self . uid_store . account_hash ,
crate ::backends ::BackendEvent ::Refresh ( ev ) ,
) ;
2020-05-30 14:40:47 +03:00
}
2020-06-23 17:25:42 +03:00
2020-07-06 11:12:38 +03:00
pub async fn create_uid_msn_cache (
& mut self ,
mailbox_hash : MailboxHash ,
low : usize ,
) -> Result < ( ) > {
2020-06-23 17:25:42 +03:00
debug_assert! ( low > 0 ) ;
let mut response = String ::new ( ) ;
2020-07-06 11:12:38 +03:00
self . examine_mailbox ( mailbox_hash , & mut response , false )
. await ? ;
self . send_command ( format! ( " UID SEARCH {} :* " , low ) . as_bytes ( ) )
. await ? ;
self . read_response ( & mut response , RequiredResponses ::SEARCH )
. await ? ;
2020-06-23 17:25:42 +03:00
debug! ( " uid search response {:?} " , & response ) ;
let mut msn_index_lck = self . uid_store . msn_index . lock ( ) . unwrap ( ) ;
let msn_index = msn_index_lck . entry ( mailbox_hash ) . or_default ( ) ;
let _ = msn_index . drain ( low - 1 .. ) ;
msn_index . extend (
debug! ( protocol_parser ::search_results ( response . as_bytes ( ) ) ) ?
. 1
. into_iter ( ) ,
) ;
Ok ( ( ) )
}
2019-09-20 09:07:55 +03:00
}
2019-06-28 19:34:40 +03:00
pub struct ImapBlockingConnection {
2020-07-28 16:16:08 +03:00
buf : Vec < u8 > ,
2019-06-28 19:34:40 +03:00
result : Vec < u8 > ,
prev_res_length : usize ,
pub conn : ImapConnection ,
2019-11-23 17:50:22 +02:00
err : Option < String > ,
2019-06-28 19:34:40 +03:00
}
impl From < ImapConnection > for ImapBlockingConnection {
2020-07-06 11:12:38 +03:00
fn from ( conn : ImapConnection ) -> Self {
2019-06-28 19:34:40 +03:00
ImapBlockingConnection {
2020-07-28 16:16:08 +03:00
buf : vec ! [ 0 ; Connection ::IO_BUF_SIZE ] ,
2019-06-28 19:34:40 +03:00
conn ,
prev_res_length : 0 ,
result : Vec ::with_capacity ( 8 * 1024 ) ,
2019-11-23 17:50:22 +02:00
err : None ,
2019-06-28 19:34:40 +03:00
}
}
}
impl ImapBlockingConnection {
pub fn into_conn ( self ) -> ImapConnection {
self . conn
}
2019-11-23 17:50:22 +02:00
pub fn err ( & self ) -> Option < & str > {
self . err . as_ref ( ) . map ( String ::as_str )
}
2019-06-28 19:34:40 +03:00
2020-07-06 11:12:38 +03:00
pub fn as_stream < ' a > ( & ' a mut self ) -> impl Future < Output = Option < Vec < u8 > > > + ' a {
2019-06-28 19:34:40 +03:00
self . result . drain ( 0 .. self . prev_res_length ) ;
self . prev_res_length = 0 ;
2020-07-06 11:12:38 +03:00
let mut break_flag = false ;
2020-06-11 11:46:15 +03:00
let mut prev_failure = None ;
2020-07-06 11:12:38 +03:00
async move {
if self . conn . stream . is_err ( ) {
debug! ( & self . conn . stream ) ;
2019-11-10 13:30:33 +02:00
return None ;
2019-09-20 09:07:55 +03:00
}
2020-07-06 11:12:38 +03:00
loop {
if let Some ( y ) = read ( self , & mut break_flag , & mut prev_failure ) . await {
return Some ( y ) ;
2019-06-28 19:34:40 +03:00
}
2020-07-06 11:12:38 +03:00
if break_flag {
return None ;
2020-02-28 09:11:41 +02:00
}
2020-07-06 11:12:38 +03:00
}
}
}
}
async fn read (
conn : & mut ImapBlockingConnection ,
break_flag : & mut bool ,
2020-08-16 19:57:28 +03:00
prev_failure : & mut Option < Instant > ,
2020-07-06 11:12:38 +03:00
) -> Option < Vec < u8 > > {
let ImapBlockingConnection {
ref mut prev_res_length ,
ref mut result ,
ref mut conn ,
ref mut buf ,
ref mut err ,
} = conn ;
match conn . stream . as_mut ( ) . unwrap ( ) . stream . read ( buf ) . await {
Ok ( 0 ) = > {
* break_flag = true ;
}
Ok ( b ) = > {
result . extend_from_slice ( & buf [ 0 .. b ] ) ;
debug! ( unsafe { std ::str ::from_utf8_unchecked ( result ) } ) ;
if let Some ( pos ) = result . find ( b " \r \n " ) {
* prev_res_length = pos + b " \r \n " . len ( ) ;
return Some ( result [ 0 .. * prev_res_length ] . to_vec ( ) ) ;
}
* prev_failure = None ;
}
Err ( e ) = > {
debug! ( & conn . stream ) ;
debug! ( & e ) ;
* err = Some ( e . to_string ( ) ) ;
* break_flag = true ;
2020-07-29 14:33:09 +03:00
* prev_failure = Some ( Instant ::now ( ) ) ;
2020-07-06 11:12:38 +03:00
}
2019-06-28 19:34:40 +03:00
}
2020-07-06 11:12:38 +03:00
None
2019-06-28 19:34:40 +03:00
}