diff --git a/Cargo.lock b/Cargo.lock index 86a3774a0..a0dc28f8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -85,6 +85,7 @@ dependencies = [ [[package]] name = "async-imap" version = "0.2.0" +source = "git+https://github.com/async-email/async-imap?branch=feat/send#be7b9cace12d0f323ef1e6e0a9fc698f4e26b64d" dependencies = [ "async-native-tls 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "async-std 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -618,7 +619,7 @@ dependencies = [ name = "deltachat" version = "1.27.0" dependencies = [ - "async-imap 0.2.0", + "async-imap 0.2.0 (git+https://github.com/async-email/async-imap?branch=feat/send)", "async-native-tls 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "async-smtp 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "async-std 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3143,6 +3144,7 @@ dependencies = [ "checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" "checksum ascii_utils 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "71938f30533e4d95a6d17aa530939da3842c2ab6f4f84b9dae68447e4129f74a" "checksum async-attributes 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "efd3d156917d94862e779f356c5acae312b08fd3121e792c857d7928c8088423" +"checksum async-imap 0.2.0 (git+https://github.com/async-email/async-imap?branch=feat/send)" = "" "checksum async-native-tls 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9e9e7a929bd34c68a82d58a4de7f86fffdaf97fb2af850162a7bb19dd7269b33" "checksum async-smtp 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b3652e5c6072c0694a2bcdb7e8409980d2676bd4f024adf4aab10c68fd2b48f5" "checksum async-std 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "538ecb01eb64eecd772087e5b6f7540cbc917f047727339a472dafed2185b267" diff --git a/Cargo.toml b/Cargo.toml index 142f3ecd0..2393bb68d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" license = "MPL-2.0" [profile.release] -lto = true +# lto = true [dependencies] deltachat_derive = { path = "./deltachat_derive" } @@ -23,7 +23,7 @@ num-traits = "0.2.6" async-smtp = "0.2" email = { git = "https://github.com/deltachat/rust-email", branch = "master" } lettre_email = { git = "https://github.com/deltachat/lettre", branch = "master" } -async-imap = { path = "../../async-imap"} # "0.2" +async-imap = { git = "https://github.com/async-email/async-imap", branch = "feat/send" } async-native-tls = "0.3.1" async-std = { version = "1.4", features = ["unstable"] } base64 = "0.11" @@ -90,4 +90,3 @@ required-features = ["rustyline"] default = ["nightly"] vendored = ["async-native-tls/vendored", "reqwest/native-tls-vendored", "async-smtp/native-tls-vendored"] nightly = ["pgp/nightly"] - diff --git a/src/imap/client.rs b/src/imap/client.rs index b2674afa4..923bc65ec 100644 --- a/src/imap/client.rs +++ b/src/imap/client.rs @@ -1,20 +1,80 @@ +use std::ops::{Deref, DerefMut}; + use async_imap::{ error::{Error as ImapError, Result as ImapResult}, Client as ImapClient, }; -use async_native_tls::TlsStream; use async_std::net::{self, TcpStream}; use super::session::Session; use crate::login_param::{dc_build_tls, CertificateChecks}; +use super::session::SessionStream; + #[derive(Debug)] -pub(crate) enum Client { - Secure(ImapClient>), - Insecure(ImapClient), +pub(crate) struct Client { + is_secure: bool, + inner: ImapClient>, +} + +impl Deref for Client { + type Target = ImapClient>; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for Client { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } } impl Client { + pub async fn login, P: AsRef>( + self, + username: U, + password: P, + ) -> std::result::Result { + let Client { inner, is_secure } = self; + let session = inner + .login(username, password) + .await + .map_err(|(err, client)| { + ( + err, + Client { + is_secure, + inner: client, + }, + ) + })?; + Ok(Session { inner: session }) + } + + pub async fn authenticate>( + self, + auth_type: S, + authenticator: &A, + ) -> std::result::Result { + let Client { inner, is_secure } = self; + let session = + inner + .authenticate(auth_type, authenticator) + .await + .map_err(|(err, client)| { + ( + err, + Client { + is_secure, + inner: client, + }, + ) + })?; + Ok(Session { inner: session }) + } + pub async fn connect_secure>( addr: A, domain: S, @@ -22,7 +82,8 @@ impl Client { ) -> ImapResult { let stream = TcpStream::connect(addr).await?; let tls = dc_build_tls(certificate_checks); - let tls_stream = tls.connect(domain.as_ref(), stream).await?; + let tls_stream: Box = + Box::new(tls.connect(domain.as_ref(), stream).await?); let mut client = ImapClient::new(tls_stream); if std::env::var(crate::DCC_IMAP_DEBUG).is_ok() { client.debug = true; @@ -33,11 +94,14 @@ impl Client { .await .ok_or_else(|| ImapError::Bad("failed to read greeting".to_string()))?; - Ok(Client::Secure(client)) + Ok(Client { + is_secure: true, + inner: client, + }) } pub async fn connect_insecure(addr: A) -> ImapResult { - let stream = TcpStream::connect(addr).await?; + let stream: Box = Box::new(TcpStream::connect(addr).await?); let mut client = ImapClient::new(stream); if std::env::var(crate::DCC_IMAP_DEBUG).is_ok() { @@ -48,57 +112,29 @@ impl Client { .await .ok_or_else(|| ImapError::Bad("failed to read greeting".to_string()))?; - Ok(Client::Insecure(client)) + Ok(Client { + is_secure: false, + inner: client, + }) } pub async fn secure>( self, - domain: S, - certificate_checks: CertificateChecks, + _domain: S, + _certificate_checks: CertificateChecks, ) -> ImapResult { - match self { - Client::Insecure(client) => { - let tls = dc_build_tls(certificate_checks); - let client_sec = client.secure(domain, tls).await?; + if self.is_secure { + Ok(self) + } else { + unimplemented!() + // let Client { inner, .. } = self; + // let tls = dc_build_tls(certificate_checks); + // let client_sec = inner.secure(domain, tls).await?; - Ok(Client::Secure(client_sec)) - } - // Nothing to do - Client::Secure(_) => Ok(self), - } - } - - pub async fn authenticate>( - self, - auth_type: S, - authenticator: &A, - ) -> Result { - match self { - Client::Secure(i) => match i.authenticate(auth_type, authenticator).await { - Ok(session) => Ok(Session::Secure(session)), - Err((err, c)) => Err((err, Client::Secure(c))), - }, - Client::Insecure(i) => match i.authenticate(auth_type, authenticator).await { - Ok(session) => Ok(Session::Insecure(session)), - Err((err, c)) => Err((err, Client::Insecure(c))), - }, - } - } - - pub async fn login, P: AsRef>( - self, - username: U, - password: P, - ) -> Result { - match self { - Client::Secure(i) => match i.login(username, password).await { - Ok(session) => Ok(Session::Secure(session)), - Err((err, c)) => Err((err, Client::Secure(c))), - }, - Client::Insecure(i) => match i.login(username, password).await { - Ok(session) => Ok(Session::Insecure(session)), - Err((err, c)) => Err((err, Client::Insecure(c))), - }, + // Ok(Client { + // is_secure: true, + // inner: client_sec, + // }) } } } diff --git a/src/imap/idle.rs b/src/imap/idle.rs index 21a0b46c5..6982c122c 100644 --- a/src/imap/idle.rs +++ b/src/imap/idle.rs @@ -1,8 +1,6 @@ use super::Imap; -use async_imap::extensions::idle::{Handle as ImapIdleHandle, IdleResponse}; -use async_native_tls::TlsStream; -use async_std::net::TcpStream; +use async_imap::extensions::idle::IdleResponse; use async_std::prelude::*; use std::time::{Duration, SystemTime}; @@ -37,27 +35,6 @@ impl From for Error { } } -#[derive(Debug)] -pub(crate) enum IdleHandle { - Secure(ImapIdleHandle>), - Insecure(ImapIdleHandle), -} - -impl Session { - pub fn idle(self) -> IdleHandle { - match self { - Session::Secure(i) => { - let h = i.idle(); - IdleHandle::Secure(h) - } - Session::Insecure(i) => { - let h = i.idle(); - IdleHandle::Insecure(h) - } - } - } -} - impl Imap { pub fn can_idle(&self) -> bool { self.config.can_idle @@ -80,138 +57,69 @@ impl Imap { let timeout = Duration::from_secs(23 * 60); if let Some(session) = session { - match session.idle() { - // BEWARE: If you change the Secure branch you - // typically also need to change the Insecure branch. - IdleHandle::Secure(mut handle) => { - if let Err(err) = handle.init().await { - return Err(Error::IdleProtocolFailed(err)); + let mut handle = session.idle(); + if let Err(err) = handle.init().await { + return Err(Error::IdleProtocolFailed(err)); + } + + let (idle_wait, interrupt) = handle.wait_with_timeout(timeout); + + if self.skip_next_idle_wait { + // interrupt_idle has happened before we + // provided self.interrupt + self.skip_next_idle_wait = false; + drop(idle_wait); + drop(interrupt); + + info!(context, "Idle wait was skipped"); + } else { + info!(context, "Idle entering wait-on-remote state"); + let fut = idle_wait.race( + self.idle_interrupt + .recv() + .map(|_| Ok(IdleResponse::ManualInterrupt)), + ); + + match fut.await { + Ok(IdleResponse::NewData(_)) => { + info!(context, "Idle has NewData"); } - - let (idle_wait, interrupt) = handle.wait_with_timeout(timeout); - - if self.skip_next_idle_wait { - // interrupt_idle has happened before we - // provided self.interrupt - self.skip_next_idle_wait = false; - drop(idle_wait); - drop(interrupt); - - info!(context, "Idle wait was skipped"); - } else { - info!(context, "Idle entering wait-on-remote state"); - let fut = idle_wait.race( - self.idle_interrupt - .recv() - .map(|_| Ok(IdleResponse::ManualInterrupt)), - ); - - match fut.await { - Ok(IdleResponse::NewData(_)) => { - info!(context, "Idle has NewData"); - } - // TODO: idle_wait does not distinguish manual interrupts - // from Timeouts if we would know it's a Timeout we could bail - // directly and reconnect . - Ok(IdleResponse::Timeout) => { - info!(context, "Idle-wait timeout or interruption"); - } - Ok(IdleResponse::ManualInterrupt) => { - info!(context, "Idle wait was interrupted"); - } - Err(err) => { - warn!(context, "Idle wait errored: {:?}", err); - } - } + // TODO: idle_wait does not distinguish manual interrupts + // from Timeouts if we would know it's a Timeout we could bail + // directly and reconnect . + Ok(IdleResponse::Timeout) => { + info!(context, "Idle-wait timeout or interruption"); } - - // if we can't properly terminate the idle - // protocol let's break the connection. - let res = handle - .done() - .timeout(Duration::from_secs(15)) - .await - .map_err(|err| { - self.trigger_reconnect(); - Error::IdleTimeout(err) - })?; - - match res { - Ok(session) => { - self.session = Some(Session::Secure(session)); - } - Err(err) => { - // if we cannot terminate IDLE it probably - // means that we waited long (with idle_wait) - // but the network went away/changed - self.trigger_reconnect(); - return Err(Error::IdleProtocolFailed(err)); - } + Ok(IdleResponse::ManualInterrupt) => { + info!(context, "Idle wait was interrupted"); + } + Err(err) => { + warn!(context, "Idle wait errored: {:?}", err); } } - IdleHandle::Insecure(mut handle) => { - if let Err(err) = handle.init().await { - return Err(Error::IdleProtocolFailed(err)); - } + } - let (idle_wait, interrupt) = handle.wait_with_timeout(timeout); + // if we can't properly terminate the idle + // protocol let's break the connection. + let res = handle + .done() + .timeout(Duration::from_secs(15)) + .await + .map_err(|err| { + self.trigger_reconnect(); + Error::IdleTimeout(err) + })?; - if self.skip_next_idle_wait { - // interrupt_idle has happened before we - // provided self.interrupt - self.skip_next_idle_wait = false; - drop(idle_wait); - drop(interrupt); - info!(context, "Idle wait was skipped"); - } else { - info!(context, "Idle entering wait-on-remote state"); - let fut = idle_wait.race( - self.idle_interrupt - .recv() - .map(|_| Ok(IdleResponse::ManualInterrupt)), - ); - - match fut.await { - Ok(IdleResponse::NewData(_)) => { - info!(context, "Idle has NewData"); - } - // TODO: idle_wait does not distinguish manual interrupts - // from Timeouts if we would know it's a Timeout we could bail - // directly and reconnect . - Ok(IdleResponse::Timeout) => { - info!(context, "Idle-wait timeout or interruption"); - } - Ok(IdleResponse::ManualInterrupt) => { - info!(context, "Idle wait was interrupted"); - } - Err(err) => { - warn!(context, "Idle wait errored: {:?}", err); - } - } - } - // if we can't properly terminate the idle - // protocol let's break the connection. - let res = handle - .done() - .timeout(Duration::from_secs(15)) - .await - .map_err(|err| { - self.trigger_reconnect(); - Error::IdleTimeout(err) - })?; - - match res { - Ok(session) => { - self.session = Some(Session::Insecure(session)); - } - Err(err) => { - // if we cannot terminate IDLE it probably - // means that we waited long (with idle_wait) - // but the network went away/changed - self.trigger_reconnect(); - return Err(Error::IdleProtocolFailed(err)); - } - } + match res { + Ok(session) => { + self.session = Some(Session { inner: session }); + } + Err(err) => { + // if we cannot terminate IDLE it probably + // means that we waited long (with idle_wait) + // but the network went away/changed + self.trigger_reconnect(); + return Err(Error::IdleProtocolFailed(err)); } } } diff --git a/src/imap/session.rs b/src/imap/session.rs index 9cac58515..8b8a680a3 100644 --- a/src/imap/session.rs +++ b/src/imap/session.rs @@ -1,155 +1,39 @@ -use async_imap::{ - error::Result as ImapResult, - types::{Capabilities, Fetch, Mailbox, Name}, - Session as ImapSession, -}; +use std::ops::{Deref, DerefMut}; + +use async_imap::Session as ImapSession; use async_native_tls::TlsStream; use async_std::net::TcpStream; -use async_std::prelude::*; #[derive(Debug)] -pub(crate) enum Session { - Secure(ImapSession>), - Insecure(ImapSession), +pub(crate) struct Session { + pub(super) inner: ImapSession>, +} + +pub(crate) trait SessionStream: + async_std::io::Read + async_std::io::Write + Unpin + Send + Sync + std::fmt::Debug +{ +} + +impl SessionStream for TlsStream {} +impl SessionStream for TcpStream {} + +impl Deref for Session { + type Target = ImapSession>; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for Session { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } } impl Session { - pub async fn capabilities(&mut self) -> ImapResult { - let res = match self { - Session::Secure(i) => i.capabilities().await?, - Session::Insecure(i) => i.capabilities().await?, - }; - - Ok(res) - } - - pub async fn list( - &mut self, - reference_name: Option<&str>, - mailbox_pattern: Option<&str>, - ) -> ImapResult> + '_ + Send + Unpin> { - match self { - Session::Secure(i) => i.list(reference_name, mailbox_pattern).await, - Session::Insecure(_i) => { - unimplemented!() - // i.list(reference_name, mailbox_pattern).await - } - } - } - - pub async fn create>(&mut self, mailbox_name: S) -> ImapResult<()> { - match self { - Session::Secure(i) => i.create(mailbox_name).await?, - Session::Insecure(i) => i.create(mailbox_name).await?, - } - Ok(()) - } - - pub async fn subscribe>(&mut self, mailbox: S) -> ImapResult<()> { - match self { - Session::Secure(i) => i.subscribe(mailbox).await?, - Session::Insecure(i) => i.subscribe(mailbox).await?, - } - Ok(()) - } - - pub async fn close(&mut self) -> ImapResult<()> { - match self { - Session::Secure(i) => i.close().await?, - Session::Insecure(i) => i.close().await?, - } - Ok(()) - } - - pub async fn select>(&mut self, mailbox_name: S) -> ImapResult { - let mbox = match self { - Session::Secure(i) => i.select(mailbox_name).await?, - Session::Insecure(i) => i.select(mailbox_name).await?, - }; - - Ok(mbox) - } - - pub async fn fetch<'a, S1, S2>( - &'a mut self, - sequence_set: S1, - query: S2, - ) -> ImapResult> + 'a + Send + Unpin> - where - S1: 'a + AsRef, - S2: 'a + AsRef, - { - let res = match self { - Session::Secure(i) => i.fetch(sequence_set, query).await?, - Session::Insecure(_i) => { - unimplemented!() - // i.fetch(sequence_set, query).await? - } - }; - Ok(res) - } - - pub async fn uid_fetch<'a, S1, S2>( - &'a mut self, - uid_set: S1, - query: S2, - ) -> ImapResult> + 'a + Send + Unpin> - where - S1: 'a + AsRef, - S2: 'a + AsRef, - { - let res = match self { - Session::Secure(i) => i.uid_fetch(uid_set, query).await?, - Session::Insecure(_i) => { - unimplemented!() - // i.uid_fetch(uid_set, query).await? - } - }; - - Ok(res) - } - - pub async fn uid_store<'a, S1, S2>( - &'a mut self, - uid_set: S1, - query: S2, - ) -> ImapResult> + 'a + Send + Unpin> - where - S1: 'a + AsRef, - S2: 'a + AsRef, - { - let res = match self { - Session::Secure(i) => i.uid_store(uid_set, query).await?, - Session::Insecure(_i) => { - unimplemented!() - // i.uid_store(uid_set, query).await? - } - }; - Ok(res) - } - - pub async fn uid_mv, S2: AsRef>( - &mut self, - uid_set: S1, - mailbox_name: S2, - ) -> ImapResult<()> { - match self { - Session::Secure(i) => i.uid_mv(uid_set, mailbox_name).await?, - Session::Insecure(i) => i.uid_mv(uid_set, mailbox_name).await?, - } - Ok(()) - } - - pub async fn uid_copy, S2: AsRef>( - &mut self, - uid_set: S1, - mailbox_name: S2, - ) -> ImapResult<()> { - match self { - Session::Secure(i) => i.uid_copy(uid_set, mailbox_name).await?, - Session::Insecure(i) => i.uid_copy(uid_set, mailbox_name).await?, - } - - Ok(()) + pub fn idle(self) -> async_imap::extensions::idle::Handle> { + let Session { inner } = self; + inner.idle() } }