mirror of
https://github.com/chatmail/core.git
synced 2026-05-02 04:46:29 +03:00
feat: IMAP COMPRESS support
This commit is contained in:
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -273,24 +273,26 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-compression"
|
name = "async-compression"
|
||||||
version = "0.4.14"
|
version = "0.4.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "998282f8f49ccd6116b0ed8a4de0fbd3151697920e7c7533416d6e25e76434a7"
|
checksum = "e26a9844c659a2a293d239c7910b752f8487fe122c6c8bd1659bf85a6507c302"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"flate2",
|
"flate2",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"memchr",
|
"memchr",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-imap"
|
name = "async-imap"
|
||||||
version = "0.10.1"
|
version = "0.10.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2162818f7b394e342a6591864bfc960824ed13e3f6609fee0d19e770ebcd99e1"
|
checksum = "5488cd022c3c7bc41a9b34a540d9ac0d9c5cd42fdb106a67616521b7592d5b4e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-channel 2.3.1",
|
"async-channel 2.3.1",
|
||||||
|
"async-compression",
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
"bytes",
|
"bytes",
|
||||||
"chrono",
|
"chrono",
|
||||||
@@ -299,6 +301,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"nom",
|
"nom",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"pin-project",
|
||||||
"pin-utils",
|
"pin-utils",
|
||||||
"self_cell",
|
"self_cell",
|
||||||
"stop-token",
|
"stop-token",
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ ratelimit = { path = "./deltachat-ratelimit" }
|
|||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
async-broadcast = "0.7.1"
|
async-broadcast = "0.7.1"
|
||||||
async-channel = { workspace = true }
|
async-channel = { workspace = true }
|
||||||
async-imap = { version = "0.10.1", default-features = false, features = ["runtime-tokio"] }
|
async-imap = { version = "0.10.2", default-features = false, features = ["runtime-tokio", "compress"] }
|
||||||
async-native-tls = { version = "0.5", default-features = false, features = ["runtime-tokio"] }
|
async-native-tls = { version = "0.5", default-features = false, features = ["runtime-tokio"] }
|
||||||
async-smtp = { version = "0.9", default-features = false, features = ["runtime-tokio"] }
|
async-smtp = { version = "0.9", default-features = false, features = ["runtime-tokio"] }
|
||||||
async_zip = { version = "0.0.17", default-features = false, features = ["deflate", "tokio-fs"] }
|
async_zip = { version = "0.0.17", default-features = false, features = ["deflate", "tokio-fs"] }
|
||||||
|
|||||||
21
src/imap.rs
21
src/imap.rs
@@ -39,6 +39,7 @@ use crate::login_param::{
|
|||||||
use crate::message::{self, Message, MessageState, MessengerMessage, MsgId, Viewtype};
|
use crate::message::{self, Message, MessageState, MessengerMessage, MsgId, Viewtype};
|
||||||
use crate::mimeparser;
|
use crate::mimeparser;
|
||||||
use crate::net::proxy::ProxyConfig;
|
use crate::net::proxy::ProxyConfig;
|
||||||
|
use crate::net::session::SessionStream;
|
||||||
use crate::oauth2::get_oauth2_access_token;
|
use crate::oauth2::get_oauth2_access_token;
|
||||||
use crate::receive_imf::{
|
use crate::receive_imf::{
|
||||||
from_field_to_contact_id, get_prefetch_parent_message, receive_imf_inner, ReceivedMsg,
|
from_field_to_contact_id, get_prefetch_parent_message, receive_imf_inner, ReceivedMsg,
|
||||||
@@ -55,7 +56,7 @@ pub mod scan_folders;
|
|||||||
pub mod select_folder;
|
pub mod select_folder;
|
||||||
pub(crate) mod session;
|
pub(crate) mod session;
|
||||||
|
|
||||||
use client::Client;
|
use client::{determine_capabilities, Client};
|
||||||
use mailparse::SingleInfo;
|
use mailparse::SingleInfo;
|
||||||
use session::Session;
|
use session::Session;
|
||||||
|
|
||||||
@@ -376,7 +377,23 @@ impl Imap {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match login_res {
|
match login_res {
|
||||||
Ok(session) => {
|
Ok(mut session) => {
|
||||||
|
let capabilities = determine_capabilities(&mut session).await?;
|
||||||
|
|
||||||
|
let session = if capabilities.can_compress {
|
||||||
|
info!(context, "Enabling IMAP compression.");
|
||||||
|
let compressed_session = session
|
||||||
|
.compress(|s| {
|
||||||
|
let session_stream: Box<dyn SessionStream> = Box::new(s);
|
||||||
|
session_stream
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.context("Failed to enable IMAP compression")?;
|
||||||
|
Session::new(compressed_session, capabilities)
|
||||||
|
} else {
|
||||||
|
Session::new(session, capabilities)
|
||||||
|
};
|
||||||
|
|
||||||
// Store server ID in the context to display in account info.
|
// Store server ID in the context to display in account info.
|
||||||
let mut lock = context.server_id.write().await;
|
let mut lock = context.server_id.write().await;
|
||||||
lock.clone_from(&session.capabilities.server_id);
|
lock.clone_from(&session.capabilities.server_id);
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ pub(crate) struct Capabilities {
|
|||||||
/// <https://tools.ietf.org/html/rfc5464>
|
/// <https://tools.ietf.org/html/rfc5464>
|
||||||
pub can_metadata: bool,
|
pub can_metadata: bool,
|
||||||
|
|
||||||
|
/// True if the server has COMPRESS=DEFLATE capability as defined in
|
||||||
|
/// <https://tools.ietf.org/html/rfc4978>
|
||||||
|
pub can_compress: bool,
|
||||||
|
|
||||||
/// True if the server supports XDELTAPUSH capability.
|
/// True if the server supports XDELTAPUSH capability.
|
||||||
/// This capability means setting /private/devicetoken IMAP METADATA
|
/// This capability means setting /private/devicetoken IMAP METADATA
|
||||||
/// on the INBOX results in new mail notifications
|
/// on the INBOX results in new mail notifications
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ use async_imap::Session as ImapSession;
|
|||||||
use tokio::io::BufWriter;
|
use tokio::io::BufWriter;
|
||||||
|
|
||||||
use super::capabilities::Capabilities;
|
use super::capabilities::Capabilities;
|
||||||
use super::session::Session;
|
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::login_param::{ConnectionCandidate, ConnectionSecurity};
|
use crate::login_param::{ConnectionCandidate, ConnectionSecurity};
|
||||||
use crate::net::dns::{lookup_host_with_cache, update_connect_timestamp};
|
use crate::net::dns::{lookup_host_with_cache, update_connect_timestamp};
|
||||||
@@ -51,7 +50,7 @@ fn alpn(port: u16) -> &'static [&'static str] {
|
|||||||
/// Determine server capabilities.
|
/// Determine server capabilities.
|
||||||
///
|
///
|
||||||
/// If server supports ID capability, send our client ID.
|
/// If server supports ID capability, send our client ID.
|
||||||
async fn determine_capabilities(
|
pub(crate) async fn determine_capabilities(
|
||||||
session: &mut ImapSession<Box<dyn SessionStream>>,
|
session: &mut ImapSession<Box<dyn SessionStream>>,
|
||||||
) -> Result<Capabilities> {
|
) -> Result<Capabilities> {
|
||||||
let caps = session
|
let caps = session
|
||||||
@@ -69,6 +68,7 @@ async fn determine_capabilities(
|
|||||||
can_check_quota: caps.has_str("QUOTA"),
|
can_check_quota: caps.has_str("QUOTA"),
|
||||||
can_condstore: caps.has_str("CONDSTORE"),
|
can_condstore: caps.has_str("CONDSTORE"),
|
||||||
can_metadata: caps.has_str("METADATA"),
|
can_metadata: caps.has_str("METADATA"),
|
||||||
|
can_compress: caps.has_str("COMPRESS=DEFLATE"),
|
||||||
can_push: caps.has_str("XDELTAPUSH"),
|
can_push: caps.has_str("XDELTAPUSH"),
|
||||||
is_chatmail: caps.has_str("XCHATMAIL"),
|
is_chatmail: caps.has_str("XCHATMAIL"),
|
||||||
server_id,
|
server_id,
|
||||||
@@ -83,28 +83,31 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn login(self, username: &str, password: &str) -> Result<Session> {
|
pub(crate) async fn login(
|
||||||
|
self,
|
||||||
|
username: &str,
|
||||||
|
password: &str,
|
||||||
|
) -> Result<ImapSession<Box<dyn SessionStream>>> {
|
||||||
let Client { inner, .. } = self;
|
let Client { inner, .. } = self;
|
||||||
let mut session = inner
|
|
||||||
|
let session = inner
|
||||||
.login(username, password)
|
.login(username, password)
|
||||||
.await
|
.await
|
||||||
.map_err(|(err, _client)| err)?;
|
.map_err(|(err, _client)| err)?;
|
||||||
let capabilities = determine_capabilities(&mut session).await?;
|
Ok(session)
|
||||||
Ok(Session::new(session, capabilities))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn authenticate(
|
pub(crate) async fn authenticate(
|
||||||
self,
|
self,
|
||||||
auth_type: &str,
|
auth_type: &str,
|
||||||
authenticator: impl async_imap::Authenticator,
|
authenticator: impl async_imap::Authenticator,
|
||||||
) -> Result<Session> {
|
) -> Result<ImapSession<Box<dyn SessionStream>>> {
|
||||||
let Client { inner, .. } = self;
|
let Client { inner, .. } = self;
|
||||||
let mut session = inner
|
let session = inner
|
||||||
.authenticate(auth_type, authenticator)
|
.authenticate(auth_type, authenticator)
|
||||||
.await
|
.await
|
||||||
.map_err(|(err, _client)| err)?;
|
.map_err(|(err, _client)| err)?;
|
||||||
let capabilities = determine_capabilities(&mut session).await?;
|
Ok(session)
|
||||||
Ok(Session::new(session, capabilities))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn connection_attempt(
|
async fn connection_attempt(
|
||||||
|
|||||||
@@ -53,6 +53,11 @@ impl<T: SessionStream> SessionStream for shadowsocks::ProxyClientStream<T> {
|
|||||||
self.get_mut().set_read_timeout(timeout)
|
self.get_mut().set_read_timeout(timeout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl<T: SessionStream> SessionStream for async_imap::DeflateStream<T> {
|
||||||
|
fn set_read_timeout(&mut self, timeout: Option<Duration>) {
|
||||||
|
self.get_mut().set_read_timeout(timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Session stream with a read buffer.
|
/// Session stream with a read buffer.
|
||||||
pub(crate) trait SessionBufStream: SessionStream + AsyncBufRead {}
|
pub(crate) trait SessionBufStream: SessionStream + AsyncBufRead {}
|
||||||
|
|||||||
Reference in New Issue
Block a user