diff --git a/src/dc_imap.rs b/src/dc_imap.rs index a6b6eda87..ca614b53d 100644 --- a/src/dc_imap.rs +++ b/src/dc_imap.rs @@ -7,10 +7,12 @@ use crate::constants::*; use crate::dc_context::dc_context_t; use crate::dc_log::*; use crate::dc_loginparam::*; +use crate::dc_oauth2::dc_get_oauth2_access_token; use crate::dc_sqlite3::*; use crate::types::*; pub const DC_IMAP_SEEN: usize = 0x0001; +pub const DC_REGENERATE: usize = 0x01; pub const DC_SUCCESS: usize = 3; pub const DC_ALREADY_DONE: usize = 2; @@ -34,6 +36,23 @@ pub struct Imap { session: Arc, Option)>>, } +struct OAuth2 { + user: String, + access_token: String, +} + +impl imap::Authenticator for OAuth2 { + type Response = String; + + #[allow(unused_variables)] + fn process(&self, data: &[u8]) -> Self::Response { + format!( + "user={}\x01auth=Bearer {}\x01\x01", + self.user, self.access_token + ) + } +} + #[derive(Debug)] pub enum FolderMeaning { Unknown, @@ -121,6 +140,23 @@ impl Client { } } + pub fn authenticate>( + self, + auth_type: S, + authenticator: &A, + ) -> Result<(Session, net::TcpStream), (imap::error::Error, Client)> { + match self { + Client::Secure(i, stream) => match i.authenticate(auth_type, authenticator) { + Ok(session) => Ok((Session::Secure(session), stream)), + Err((err, c)) => Err((err, Client::Secure(c, stream))), + }, + Client::Insecure(i, stream) => match i.authenticate(auth_type, authenticator) { + Ok(session) => Ok((Session::Insecure(session), stream)), + Err((err, c)) => Err((err, Client::Insecure(c, stream))), + }, + } + } + pub fn login, P: AsRef>( self, username: U, @@ -365,53 +401,27 @@ impl Imap { Client::connect_secure((imap_server, imap_port), imap_server) }; - match connection_res { + let login_res = match connection_res { Ok(client) => { - // TODO: handle oauth2 - match client.login(imap_user, imap_pw) { - Ok((mut session, stream)) => { - // TODO: error handling - let caps = session.capabilities().unwrap(); - let can_idle = caps.has("IDLE"); - let has_xlist = caps.has("XLIST"); + if (server_flags & DC_LP_AUTH_OAUTH2) != 0 { + let access_token = unsafe { + CStr::from_ptr(dc_get_oauth2_access_token( + context, + lp.addr, + lp.mail_pw, + DC_REGENERATE as libc::c_int, + )) + .to_str() + .unwrap() + }; - let caps_list = caps.iter().fold(String::new(), |mut s, c| { - s += " "; - s += c; - s - }); - let caps_list_c = std::ffi::CString::new(caps_list).unwrap(); - - info!(context, 0, "IMAP-capabilities:%s", caps_list_c.as_ptr()); - - let mut config = self.config.write().unwrap(); - config.can_idle = can_idle; - config.has_xlist = has_xlist; - config.addr = Some(addr.into()); - config.imap_server = Some(imap_server.into()); - config.imap_port = Some(imap_port.into()); - config.imap_user = Some(imap_user.into()); - config.imap_pw = Some(imap_pw.into()); - config.server_flags = Some(server_flags); - - *self.session.lock().unwrap() = (Some(session), Some(stream)); - - 1 - } - Err((err, _)) => { - eprintln!("failed to login: {:?}", err); - - unsafe { - dc_log_event_seq( - context, - Event::ERROR_NETWORK, - &mut 0 as *mut i32, - b"Cannot login\x00" as *const u8 as *const libc::c_char, - ) - }; - - 0 - } + let auth = OAuth2 { + user: imap_user.into(), + access_token: access_token.into(), + }; + client.authenticate("XOAUTH2", &auth) + } else { + client.login(imap_user, imap_pw) } } Err(err) => { @@ -428,6 +438,52 @@ impl Imap { ) }; + return 0; + } + }; + + match login_res { + Ok((mut session, stream)) => { + // TODO: error handling + let caps = session.capabilities().unwrap(); + let can_idle = caps.has("IDLE"); + let has_xlist = caps.has("XLIST"); + + let caps_list = caps.iter().fold(String::new(), |mut s, c| { + s += " "; + s += c; + s + }); + let caps_list_c = std::ffi::CString::new(caps_list).unwrap(); + + info!(context, 0, "IMAP-capabilities:%s", caps_list_c.as_ptr()); + + let mut config = self.config.write().unwrap(); + config.can_idle = can_idle; + config.has_xlist = has_xlist; + config.addr = Some(addr.into()); + config.imap_server = Some(imap_server.into()); + config.imap_port = Some(imap_port.into()); + config.imap_user = Some(imap_user.into()); + config.imap_pw = Some(imap_pw.into()); + config.server_flags = Some(server_flags); + + *self.session.lock().unwrap() = (Some(session), Some(stream)); + + 1 + } + Err((err, _)) => { + eprintln!("failed to login: {:?}", err); + + unsafe { + dc_log_event_seq( + context, + Event::ERROR_NETWORK, + &mut 0 as *mut i32, + b"Cannot login\x00" as *const u8 as *const libc::c_char, + ) + }; + 0 } }