diff --git a/src/imap.rs b/src/imap.rs index 29acf6e8c..fd455e193 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -387,19 +387,28 @@ impl Imap { user: imap_user.into(), access_token: token, }; - client.authenticate("XOAUTH2", auth).await + client + .authenticate("XOAUTH2", auth) + .await + .map(|session| (session, None)) } else { info!(context, "Logging into IMAP server with LOGIN."); client.login(imap_user, imap_pw).await }; match login_res { - Ok(mut session) => { - let capabilities = match determine_capabilities(&mut session).await { - Ok(capabilities) => capabilities, - Err(err) => { - warn!(context, "Failed to determine capabilities: {err:#}."); - continue 'candidate; + Ok((mut session, login_capabilities_opt)) => { + let capabilities = if let Some(login_capabilities) = login_capabilities_opt { + login_capabilities + } else { + // OK response did not contain the CAPABILITY response code. + // Request capabilities explicitly. + match determine_capabilities(&mut session).await { + Ok(capabilities) => capabilities, + Err(err) => { + warn!(context, "Failed to determine capabilities: {err:#}."); + continue 'candidate; + } } }; let resync_request_sender = self.resync_request_sender.clone(); diff --git a/src/imap/client.rs b/src/imap/client.rs index fff839843..cb758f46d 100644 --- a/src/imap/client.rs +++ b/src/imap/client.rs @@ -53,10 +53,24 @@ fn alpn(port: u16) -> &'static str { pub(crate) async fn determine_capabilities( session: &mut ImapSession>, ) -> Result { - let caps = session + let imap_capabilities = session .capabilities() .await .context("CAPABILITY command error")?; + identify_server(session, imap_capabilities).await +} + +/// Identifies the server by sending ID command if it is supported. +/// +/// Some IMAP servers require sending this command, +/// see , +/// +/// and +/// for details. +pub(crate) async fn identify_server( + session: &mut ImapSession>, + caps: async_imap::types::Capabilities, +) -> Result { let server_id = if caps.has_str("ID") { session.id([("name", Some("Delta Chat"))]).await? } else { @@ -82,18 +96,30 @@ impl Client { } } + /// Logs in with the LOGIN command. + /// + /// If the server supports [ID extension], sends ID command + /// and records the server response in the [`Capabilities`] structure. + /// + /// [ID extension]: https://datatracker.ietf.org/doc/rfc2971/ pub(crate) async fn login( self, username: &str, password: &str, - ) -> Result>> { + ) -> Result<(ImapSession>, Option)> { let Client { inner, .. } = self; - let session = inner - .login(username, password) + let (mut session, login_capabilities_opt) = inner + .login_with_capabilities(username, password) .await .map_err(|(err, _client)| err)?; - Ok(session) + + let capabilities = if let Some(login_capabilities) = login_capabilities_opt { + Some(identify_server(&mut session, login_capabilities).await?) + } else { + None + }; + Ok((session, capabilities)) } pub(crate) async fn authenticate(