melib/smtp: implement gmail XOAUTH2 authentication method
parent
4914f29e20
commit
453bb0b2b2
|
@ -1132,7 +1132,7 @@ subsection
|
|||
.El
|
||||
.Ss SmtpAuth
|
||||
.Bl -tag -width 36n
|
||||
.It Ic type Ar "none" | "auto"
|
||||
.It Ic type Ar "none" | "auto" | "xoauth2"
|
||||
.El
|
||||
.Pp
|
||||
For type "auto":
|
||||
|
@ -1146,6 +1146,17 @@ require authentication in every case
|
|||
.Pq Em true
|
||||
.El
|
||||
.sp
|
||||
For type "xoauth2":
|
||||
.Bl -tag -width 36n
|
||||
.It Ic token_command Ar String
|
||||
Command to evaluate that returns an XOAUTH2 token.
|
||||
.It Ic require_auth Ar bool
|
||||
.Pq Em optional
|
||||
require authentication in every case
|
||||
.\" default value
|
||||
.Pq Em true
|
||||
.El
|
||||
.sp
|
||||
Examples:
|
||||
.Bd -literal
|
||||
auth = { type = "auto", username = "user", password = { type = "raw", value = "hunter2" } }
|
||||
|
@ -1156,6 +1167,13 @@ auth = { type = "auto", username = "user", password = "hunter2" }
|
|||
.Bd -literal
|
||||
auth = { type = "none" }
|
||||
.Ed
|
||||
.sp
|
||||
For Gmail (see
|
||||
.Sx Gmail OAUTH2
|
||||
for details on the authentication token command):
|
||||
.Bd -literal
|
||||
auth = { type = "xoauth2", token_command = "TOKEN=$(python3 oauth2.py --user=xxx@gmail.com --quiet --client_id=1038[...].apps.googleusercontent.com --client_secret=[..] --refresh_token=[..] && python3 oauth2.py --user=xxx@gmail.com --generate_oauth2_string --quiet --access_token=$TOKEN" }
|
||||
.Ed
|
||||
.Ss SmtpPassword
|
||||
.Bl -tag -width 36n
|
||||
.It Ic type Ar "raw" | "command_evaluation"
|
||||
|
|
|
@ -140,6 +140,12 @@ pub enum SmtpAuth {
|
|||
#[serde(skip_serializing, skip_deserializing, default)]
|
||||
auth_type: SmtpAuthType,
|
||||
},
|
||||
#[serde(alias = "xoauth2")]
|
||||
XOAuth2 {
|
||||
token_command: String,
|
||||
#[serde(default = "true_val")]
|
||||
require_auth: bool,
|
||||
},
|
||||
// md5, sasl, etc
|
||||
}
|
||||
|
||||
|
@ -162,7 +168,7 @@ impl SmtpAuth {
|
|||
use SmtpAuth::*;
|
||||
match self {
|
||||
None => false,
|
||||
Auto { require_auth, .. } => *require_auth,
|
||||
Auto { require_auth, .. } | XOAuth2 { require_auth, .. } => *require_auth,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -505,6 +511,39 @@ impl SmtpConnection {
|
|||
.chain_err_kind(crate::error::ErrorKind::Authentication)?;
|
||||
ret.send_command(&[b"EHLO meli.delivery"]).await?;
|
||||
}
|
||||
SmtpAuth::XOAuth2 { token_command, .. } => {
|
||||
let password_token = {
|
||||
let _token_command = token_command.clone();
|
||||
let mut output = unblock(move || {
|
||||
Command::new("sh")
|
||||
.args(&["-c", &_token_command])
|
||||
.stdin(std::process::Stdio::piped())
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.output()
|
||||
})
|
||||
.await?;
|
||||
if !output.status.success() {
|
||||
return Err(MeliError::new(format!(
|
||||
"SMTP XOAUTH2 token evaluation command `{}` returned {}: {}",
|
||||
&token_command,
|
||||
output.status,
|
||||
String::from_utf8_lossy(&output.stderr)
|
||||
)));
|
||||
}
|
||||
if output.stdout.ends_with(b"\n") {
|
||||
output.stdout.pop();
|
||||
}
|
||||
output.stdout
|
||||
};
|
||||
// https://developers.google.com/gmail/imap/xoauth2-protocol#smtp_protocol_exchange
|
||||
ret.send_command(&[b"AUTH XOAUTH2 ", &password_token])
|
||||
.await?;
|
||||
ret.read_lines(&mut res, Some((ReplyCode::_235, &[])))
|
||||
.await
|
||||
.chain_err_kind(crate::error::ErrorKind::Authentication)?;
|
||||
ret.send_command(&[b"EHLO meli.delivery"]).await?;
|
||||
}
|
||||
}
|
||||
{
|
||||
let extensions_reply = ret
|
||||
|
@ -966,6 +1005,9 @@ async fn read_lines<'r>(
|
|||
}
|
||||
}
|
||||
}
|
||||
if ret.len() < 3 {
|
||||
return Err(MeliError::new(format!("Invalid SMTP reply: {}", ret)));
|
||||
}
|
||||
let code = ReplyCode::try_from(&ret[..3])?;
|
||||
let reply = Reply::new(ret, code);
|
||||
//debug!(&reply);
|
||||
|
|
Loading…
Reference in New Issue