io_smtp/rfc4954/
auth_data.rs1use alloc::{
4 boxed::Box,
5 string::{String, ToString},
6 vec::Vec,
7};
8
9use base64::{Engine, engine::general_purpose::STANDARD as base64};
10use secrecy::{ExposeSecret, SecretBox};
11use thiserror::Error;
12
13#[derive(Debug, Error)]
15pub enum SmtpAuthDataError {
16 #[error("Parse SMTP auth data error: incomplete input")]
18 Incomplete,
19 #[error("Parse SMTP auth data error: {0}")]
21 Base64(String),
22}
23
24#[derive(Debug)]
29pub enum SmtpAuthData {
30 Continue(SecretBox<[u8]>),
32 Cancel,
37}
38
39impl SmtpAuthData {
40 pub fn r#continue(data: impl Into<Box<[u8]>>) -> Self {
42 Self::Continue(SecretBox::new(data.into()))
43 }
44
45 pub fn is_complete(buf: &[u8]) -> bool {
47 buf.ends_with(b"\r\n")
48 }
49
50 pub fn parse(input: &[u8]) -> Result<SmtpAuthData, SmtpAuthDataError> {
52 if !input.ends_with(b"\r\n") {
53 return Err(SmtpAuthDataError::Incomplete);
54 }
55
56 let line = &input[..input.len() - 2];
57 if line == b"*" {
58 return Ok(SmtpAuthData::Cancel);
59 }
60
61 let decoded = base64
62 .decode(line)
63 .map_err(|e| SmtpAuthDataError::Base64(e.to_string()))?;
64
65 Ok(SmtpAuthData::r#continue(decoded.into_boxed_slice()))
66 }
67}
68
69impl From<SmtpAuthData> for Vec<u8> {
70 fn from(data: SmtpAuthData) -> Vec<u8> {
71 let mut buf = Vec::new();
72
73 match data {
74 SmtpAuthData::Continue(secret) => {
75 buf.extend_from_slice(base64.encode(secret.expose_secret()).as_bytes());
76 }
77 SmtpAuthData::Cancel => buf.push(b'*'),
78 }
79
80 buf.extend_from_slice(b"\r\n");
81 buf
82 }
83}