Skip to content

Commit 1071b1c

Browse files
committed
feat: IMAP COMPRESS support
1 parent 8acf391 commit 1071b1c

File tree

6 files changed

+58
-16
lines changed

6 files changed

+58
-16
lines changed

Cargo.lock

Lines changed: 16 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ ratelimit = { path = "./deltachat-ratelimit" }
4141
anyhow = { workspace = true }
4242
async-broadcast = "0.7.1"
4343
async-channel = { workspace = true }
44-
async-imap = { version = "0.10.1", default-features = false, features = ["runtime-tokio"] }
44+
async-imap = { git = "https://github.com/async-email/async-imap.git", default-features = false, features = ["runtime-tokio", "compress"], branch = "link2xt/imap-compress" }
4545
async-native-tls = { version = "0.5", default-features = false, features = ["runtime-tokio"] }
4646
async-smtp = { version = "0.9", default-features = false, features = ["runtime-tokio"] }
4747
async_zip = { version = "0.0.12", default-features = false, features = ["deflate", "fs"] }

src/imap.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use crate::login_param::{
3939
use crate::message::{self, Message, MessageState, MessengerMessage, MsgId, Viewtype};
4040
use crate::mimeparser;
4141
use crate::net::proxy::ProxyConfig;
42+
use crate::net::session::SessionStream;
4243
use crate::oauth2::get_oauth2_access_token;
4344
use crate::receive_imf::{
4445
from_field_to_contact_id, get_prefetch_parent_message, receive_imf_inner, ReceivedMsg,
@@ -55,7 +56,7 @@ pub mod scan_folders;
5556
pub mod select_folder;
5657
pub(crate) mod session;
5758

58-
use client::Client;
59+
use client::{determine_capabilities, Client};
5960
use mailparse::SingleInfo;
6061
use session::Session;
6162

@@ -376,7 +377,23 @@ impl Imap {
376377
};
377378

378379
match login_res {
379-
Ok(session) => {
380+
Ok(mut session) => {
381+
let capabilities = determine_capabilities(&mut session).await?;
382+
383+
let session = if capabilities.can_compress {
384+
info!(context, "Enabling IMAP compression.");
385+
let compressed_session = session
386+
.compress(|s| {
387+
let session_stream: Box<dyn SessionStream> = Box::new(s);
388+
session_stream
389+
})
390+
.await
391+
.context("Failed to enable IMAP compression")?;
392+
Session::new(compressed_session, capabilities)
393+
} else {
394+
Session::new(session, capabilities)
395+
};
396+
380397
// Store server ID in the context to display in account info.
381398
let mut lock = context.server_id.write().await;
382399
lock.clone_from(&session.capabilities.server_id);

src/imap/capabilities.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ pub(crate) struct Capabilities {
2525
/// <https://tools.ietf.org/html/rfc5464>
2626
pub can_metadata: bool,
2727

28+
/// True if the server has COMPRESS=DEFLATE capability as defined in
29+
/// <https://tools.ietf.org/html/rfc4978>
30+
pub can_compress: bool,
31+
2832
/// True if the server supports XDELTAPUSH capability.
2933
/// This capability means setting /private/devicetoken IMAP METADATA
3034
/// on the INBOX results in new mail notifications

src/imap/client.rs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ use async_imap::Session as ImapSession;
77
use tokio::io::BufWriter;
88

99
use super::capabilities::Capabilities;
10-
use super::session::Session;
1110
use crate::context::Context;
1211
use crate::login_param::{ConnectionCandidate, ConnectionSecurity};
1312
use crate::net::dns::{lookup_host_with_cache, update_connect_timestamp};
@@ -51,7 +50,7 @@ fn alpn(port: u16) -> &'static [&'static str] {
5150
/// Determine server capabilities.
5251
///
5352
/// If server supports ID capability, send our client ID.
54-
async fn determine_capabilities(
53+
pub(crate) async fn determine_capabilities(
5554
session: &mut ImapSession<Box<dyn SessionStream>>,
5655
) -> Result<Capabilities> {
5756
let caps = session
@@ -69,6 +68,7 @@ async fn determine_capabilities(
6968
can_check_quota: caps.has_str("QUOTA"),
7069
can_condstore: caps.has_str("CONDSTORE"),
7170
can_metadata: caps.has_str("METADATA"),
71+
can_compress: caps.has_str("COMPRESS=DEFLATE"),
7272
can_push: caps.has_str("XDELTAPUSH"),
7373
is_chatmail: caps.has_str("XCHATMAIL"),
7474
server_id,
@@ -83,28 +83,31 @@ impl Client {
8383
}
8484
}
8585

86-
pub(crate) async fn login(self, username: &str, password: &str) -> Result<Session> {
86+
pub(crate) async fn login(
87+
self,
88+
username: &str,
89+
password: &str,
90+
) -> Result<ImapSession<Box<dyn SessionStream>>> {
8791
let Client { inner, .. } = self;
88-
let mut session = inner
92+
93+
let session = inner
8994
.login(username, password)
9095
.await
9196
.map_err(|(err, _client)| err)?;
92-
let capabilities = determine_capabilities(&mut session).await?;
93-
Ok(Session::new(session, capabilities))
97+
Ok(session)
9498
}
9599

96100
pub(crate) async fn authenticate(
97101
self,
98102
auth_type: &str,
99103
authenticator: impl async_imap::Authenticator,
100-
) -> Result<Session> {
104+
) -> Result<ImapSession<Box<dyn SessionStream>>> {
101105
let Client { inner, .. } = self;
102-
let mut session = inner
106+
let session = inner
103107
.authenticate(auth_type, authenticator)
104108
.await
105109
.map_err(|(err, _client)| err)?;
106-
let capabilities = determine_capabilities(&mut session).await?;
107-
Ok(Session::new(session, capabilities))
110+
Ok(session)
108111
}
109112

110113
async fn connection_attempt(

src/net/session.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ impl<T: SessionStream> SessionStream for shadowsocks::ProxyClientStream<T> {
5353
self.get_mut().set_read_timeout(timeout)
5454
}
5555
}
56+
impl<T: SessionStream> SessionStream for async_imap::DeflateStream<T> {
57+
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
58+
self.with_lock(|mut stream| stream.set_read_timeout(timeout))
59+
}
60+
}
5661

5762
/// Session stream with a read buffer.
5863
pub(crate) trait SessionBufStream: SessionStream + AsyncBufRead {}

0 commit comments

Comments
 (0)