diff options
Diffstat (limited to 'melib/src')
-rw-r--r-- | melib/src/backends/imap.rs | 26 | ||||
-rw-r--r-- | melib/src/backends/imap/connection.rs | 44 |
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 { |