diff --git a/CHANGELOG.md b/CHANGELOG.md index 5da49eafe..4dfd2cbb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Changes - switch from `async-std` to `tokio` as the async runtime #3449 - upgrade to `pgp@0.8.0` #3467 +- add IMAP ID extension support #3468 ### Fixes - mailing list: remove square-brackets only for first name #3452 diff --git a/Cargo.lock b/Cargo.lock index be902d192..6a91b6942 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,8 +115,7 @@ dependencies = [ [[package]] name = "async-imap" version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31dd46675b8c5a2ecadd4ef690c84966eacf21b1e2327373a0d167494d1a1d28" +source = "git+https://github.com/async-email/async-imap?branch=master#8755b666fcd8991ed4d09864b67aa88a1eb6934f" dependencies = [ "async-channel", "async-native-tls", diff --git a/Cargo.toml b/Cargo.toml index 710c8e7ef..1b1c1a47b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,7 @@ deltachat_derive = { path = "./deltachat_derive" } ansi_term = { version = "0.12.1", optional = true } anyhow = "1" -async-imap = { version = "0.6", default-features = false, features = ["runtime-tokio"] } +async-imap = { git = "https://github.com/async-email/async-imap", branch = "master", default-features = false, features = ["runtime-tokio"] } async-native-tls = { version = "0.4", default-features = false, features = ["runtime-tokio"] } async-smtp = { version = "0.5", default-features = false, features = ["smtp-transport", "socks5", "runtime-tokio"] } trust-dns-resolver = "0.21" diff --git a/src/configure.rs b/src/configure.rs index a44028521..620932c06 100644 --- a/src/configure.rs +++ b/src/configure.rs @@ -416,6 +416,9 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> { let create_mvbox = ctx.should_watch_mvbox().await?; + // Send client ID as soon as possible before doing anything else. + imap.determine_capabilities(ctx).await?; + imap.configure_folders(ctx, create_mvbox).await?; imap.select_with_uidvalidity(ctx, "INBOX") diff --git a/src/context.rs b/src/context.rs index 279633af2..cd727a36b 100644 --- a/src/context.rs +++ b/src/context.rs @@ -61,6 +61,11 @@ pub struct InnerContext { /// Set to `None` if quota was never tried to load. pub(crate) quota: RwLock>, + /// Server ID response if ID capability is supported + /// and the server returned non-NIL on the inbox connection. + /// + pub(crate) server_id: RwLock>>, + pub(crate) last_full_folder_scan: Mutex>, /// ID for this `Context` in the current process. @@ -190,6 +195,7 @@ impl Context { scheduler: RwLock::new(None), ratelimit: RwLock::new(Ratelimit::new(Duration::new(60, 0), 3.0)), // Allow to send 3 messages immediately, no more than once every 20 seconds. quota: RwLock::new(None), + server_id: RwLock::new(None), creation_time: std::time::SystemTime::now(), last_full_folder_scan: Mutex::new(None), last_error: std::sync::RwLock::new("".to_string()), @@ -429,6 +435,11 @@ impl Context { res.insert("socks5_enabled", socks5_enabled.to_string()); res.insert("entered_account_settings", l.to_string()); res.insert("used_account_settings", l2.to_string()); + + let server_id = self.server_id.read().await; + res.insert("imap_server_id", format!("{:?}", server_id)); + drop(server_id); + res.insert("secondary_addrs", secondary_addrs); res.insert( "fetch_existing_msgs", diff --git a/src/imap.rs b/src/imap.rs index 6dc003091..75a9fc9b5 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -451,7 +451,9 @@ impl Imap { } /// Determine server capabilities if not done yet. - async fn determine_capabilities(&mut self) -> Result<()> { + /// + /// If server supports ID capability, send our client ID. + pub(crate) async fn determine_capabilities(&mut self, context: &Context) -> Result<()> { if self.capabilities_determined { return Ok(()); } @@ -463,6 +465,12 @@ impl Imap { .capabilities() .await .context("CAPABILITY command error")?; + if caps.has_str("ID") { + let server_id = session.id([("name", Some("Delta Chat"))]).await?; + info!(context, "Server ID: {:?}", server_id); + let mut lock = context.server_id.write().await; + *lock = server_id; + } self.config.can_idle = caps.has_str("IDLE"); self.config.can_move = caps.has_str("MOVE"); self.config.can_check_quota = caps.has_str("QUOTA"); @@ -481,8 +489,8 @@ impl Imap { return Err(err); } + self.determine_capabilities(context).await?; self.ensure_configured_folders(context, true).await?; - self.determine_capabilities().await?; Ok(()) } diff --git a/standards.md b/standards.md index 8836f29e1..87f1f8d20 100644 --- a/standards.md +++ b/standards.md @@ -13,6 +13,7 @@ Identify server folders | IMAP LIST Extension ([RFC 6154](https://tools Push | IMAP IDLE ([RFC 2177](https://tools.ietf.org/html/rfc2177)) Quota | IMAP QUOTA extension ([RFC 2087](https://tools.ietf.org/html/rfc2087)) Seen status synchronization | IMAP CONDSTORE extension ([RFC 7162](https://tools.ietf.org/html/rfc7162)) +Client/server identification | IMAP ID extension ([RFC 2971](https://datatracker.ietf.org/doc/html/rfc2971)) Authorization | OAuth2 ([RFC 6749](https://tools.ietf.org/html/rfc6749)) End-to-end encryption | [Autocrypt Level 1](https://autocrypt.org/level1.html), OpenPGP ([RFC 4880](https://tools.ietf.org/html/rfc4880)), Security Multiparts for MIME ([RFC 1847](https://tools.ietf.org/html/rfc1847)) and [“Mixed Up” Encryption repairing](https://tools.ietf.org/id/draft-dkg-openpgp-pgpmime-message-mangling-00.html) Header encryption | [Protected Headers for Cryptographic E-mail](https://datatracker.ietf.org/doc/draft-autocrypt-lamps-protected-headers/)