Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ ratelimit = { path = "./deltachat-ratelimit" }
anyhow = { workspace = true }
async-broadcast = "0.7.2"
async-channel = { workspace = true }
async-imap = { version = "0.11.1", default-features = false, features = ["runtime-tokio", "compress"] }
async-imap = { version = "0.11.3", default-features = false, features = ["runtime-tokio", "compress"] }
async-native-tls = { version = "0.6", default-features = false, features = ["runtime-tokio"] }
async-smtp = { version = "0.10.2", default-features = false, features = ["runtime-tokio"] }
async_zip = { version = "0.0.18", default-features = false, features = ["deflate", "tokio-fs"] }
Expand Down
23 changes: 16 additions & 7 deletions src/imap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
36 changes: 31 additions & 5 deletions src/imap/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,24 @@ fn alpn(port: u16) -> &'static str {
pub(crate) async fn determine_capabilities(
session: &mut ImapSession<Box<dyn SessionStream>>,
) -> Result<Capabilities> {
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 <https://github.com/chatmail/core/issues/3458>,
/// <https://github.com/OfflineIMAP/offlineimap3/issues/71>
/// and <https://github.com/pimalaya/himalaya/issues/652>
/// for details.
pub(crate) async fn identify_server(
session: &mut ImapSession<Box<dyn SessionStream>>,
caps: async_imap::types::Capabilities,
) -> Result<Capabilities> {
let server_id = if caps.has_str("ID") {
session.id([("name", Some("Delta Chat"))]).await?
} else {
Expand All @@ -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<ImapSession<Box<dyn SessionStream>>> {
) -> Result<(ImapSession<Box<dyn SessionStream>>, Option<Capabilities>)> {
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(
Expand Down