summaryrefslogtreecommitdiff
path: root/melib/src
diff options
context:
space:
mode:
Diffstat (limited to 'melib/src')
-rw-r--r--melib/src/backends/imap.rs26
-rw-r--r--melib/src/backends/imap/connection.rs44
2 files changed, 61 insertions, 9 deletions
diff --git a/melib/src/backends/imap.rs b/melib/src/backends/imap.rs
index a060ca97..e3186a65 100644
--- a/melib/src/backends/imap.rs
+++ b/melib/src/backends/imap.rs
@@ -64,6 +64,7 @@ pub type UIDVALIDITY = UID;
pub type MessageSequenceNumber = ImapNum;
pub static SUPPORTED_CAPABILITIES: &[&str] = &[
+ "AUTH=OAUTH2",
#[cfg(feature = "deflate_compression")]
"COMPRESS=DEFLATE",
"CONDSTORE",
@@ -232,6 +233,7 @@ impl MailBackend for ImapType {
#[cfg(feature = "deflate_compression")]
deflate,
condstore,
+ oauth2,
},
} = self.server_conf.protocol
{
@@ -273,6 +275,15 @@ impl MailBackend for ImapType {
};
}
}
+ "AUTH=OAUTH2" => {
+ if oauth2 {
+ *status = MailBackendExtensionStatus::Enabled { comment: None };
+ } else {
+ *status = MailBackendExtensionStatus::Supported {
+ comment: Some("Disabled by user configuration"),
+ };
+ }
+ }
_ => {
if SUPPORTED_CAPABILITIES
.iter()
@@ -1218,7 +1229,14 @@ impl ImapType {
) -> Result<Box<dyn MailBackend>> {
let server_hostname = get_conf_val!(s["server_hostname"])?;
let server_username = get_conf_val!(s["server_username"])?;
+ let use_oauth2: bool = get_conf_val!(s["use_oauth2"], false)?;
let server_password = if !s.extra.contains_key("server_password_command") {
+ if use_oauth2 {
+ return Err(MeliError::new(format!(
+ "({}) `use_oauth2` use requires `server_password_command` set with a command that returns an OAUTH2 token. Consult documentation for guidance.",
+ s.name,
+ )));
+ }
get_conf_val!(s["server_password"])?.to_string()
} else {
let invocation = get_conf_val!(s["server_password_command"])?;
@@ -1275,6 +1293,7 @@ impl ImapType {
condstore: get_conf_val!(s["use_condstore"], true)?,
#[cfg(feature = "deflate_compression")]
deflate: get_conf_val!(s["use_deflate"], true)?,
+ oauth2: use_oauth2,
},
},
timeout,
@@ -1463,7 +1482,14 @@ impl ImapType {
pub fn validate_config(s: &AccountSettings) -> Result<()> {
get_conf_val!(s["server_hostname"])?;
get_conf_val!(s["server_username"])?;
+ let use_oauth2: bool = get_conf_val!(s["use_oauth2"], false)?;
if !s.extra.contains_key("server_password_command") {
+ if use_oauth2 {
+ return Err(MeliError::new(format!(
+ "({}) `use_oauth2` use requires `server_password_command` set with a command that returns an OAUTH2 token. Consult documentation for guidance.",
+ s.name,
+ )));
+ }
get_conf_val!(s["server_password"])?;
} else if s.extra.contains_key("server_password") {
return Err(MeliError::new(format!(
diff --git a/melib/src/backends/imap/connection.rs b/melib/src/backends/imap/connection.rs
index 69aa622a..1d2353c1 100644
--- a/melib/src/backends/imap/connection.rs
+++ b/melib/src/backends/imap/connection.rs
@@ -63,6 +63,7 @@ pub struct ImapExtensionUse {
pub idle: bool,
#[cfg(feature = "deflate_compression")]
pub deflate: bool,
+ pub oauth2: bool,
}
impl Default for ImapExtensionUse {
@@ -72,6 +73,7 @@ impl Default for ImapExtensionUse {
idle: true,
#[cfg(feature = "deflate_compression")]
deflate: true,
+ oauth2: false,
}
}
}
@@ -351,16 +353,39 @@ impl ImapStream {
.set_err_kind(crate::error::ErrorKind::Authentication));
}
- let mut capabilities = None;
- ret.send_command(
- format!(
- "LOGIN \"{}\" \"{}\"",
- &server_conf.server_username, &server_conf.server_password
- )
- .as_bytes(),
- )
- .await?;
+ match server_conf.protocol {
+ ImapProtocol::IMAP {
+ extension_use: ImapExtensionUse { oauth2, .. },
+ } if oauth2 => {
+ if !capabilities
+ .iter()
+ .any(|cap| cap.eq_ignore_ascii_case(b"AUTH=XOAUTH2"))
+ {
+ return Err(MeliError::new(format!(
+ "Could not connect to {}: OAUTH2 is enabled but server did not return AUTH=XOAUTH2 capability. Returned capabilities were: {}",
+ &server_conf.server_hostname,
+ capabilities.iter().map(|capability|
+ String::from_utf8_lossy(capability).to_string()).collect::<Vec<String>>().join(" ")
+ )));
+ }
+ ret.send_command(
+ format!("AUTHENTICATE XOAUTH2 {}", &server_conf.server_password).as_bytes(),
+ )
+ .await?;
+ }
+ _ => {
+ ret.send_command(
+ format!(
+ "LOGIN \"{}\" \"{}\"",
+ &server_conf.server_username, &server_conf.server_password
+ )
+ .as_bytes(),
+ )
+ .await?;
+ }
+ }
let tag_start = format!("M{} ", (ret.cmd_id - 1));
+ let mut capabilities = None;
loop {
ret.read_lines(&mut res, &[], false).await?;
@@ -604,6 +629,7 @@ impl ImapConnection {
#[cfg(feature = "deflate_compression")]
deflate,
idle: _idle,
+ oauth2: _,
},
} => {
if capabilities.contains(&b"CONDSTORE"[..]) && condstore {