new_tokio_smtp/command/auth/
plain.rs1use std::error::Error as ErrorTrait;
2use std::fmt::{self, Display};
3use std::sync::Arc;
4
5use base64::encode;
6
7use crate::{error::MissingCapabilities, Cmd, EhloData, ExecFuture, Io};
8
9use super::validate_auth_capability;
10
11#[derive(Debug, Clone)]
13pub struct Plain {
14 authorization_identity: String,
15 authentication_identity: String,
16 password: String,
17}
18
19impl Plain {
20 pub fn from_username<I1, I2>(user: I1, password: I2) -> Result<Self, NullCodePointError>
22 where
23 I1: Into<String> + AsRef<str>,
24 I2: Into<String> + AsRef<str>,
25 {
26 validate_no_null_cps(&user)?;
27 validate_no_null_cps(&password)?;
28
29 let user = user.into();
30 Ok(Plain {
31 authentication_identity: user.clone(),
32 authorization_identity: user,
33 password: password.into(),
34 })
35 }
36
37 pub fn new<I1, I2, I3>(
42 authorization_identity: I1,
43 authentication_identity: I2,
44 password: I3,
45 ) -> Result<Self, NullCodePointError>
46 where
47 I1: Into<String> + AsRef<str>,
48 I2: Into<String> + AsRef<str>,
49 I3: Into<String> + AsRef<str>,
50 {
51 validate_no_null_cps(&authorization_identity)?;
52 validate_no_null_cps(&authentication_identity)?;
53 validate_no_null_cps(&password)?;
54
55 Ok(Plain {
56 authentication_identity: authentication_identity.into(),
57 authorization_identity: authorization_identity.into(),
58 password: password.into(),
59 })
60 }
61
62 pub fn authorization_identity(&self) -> &str {
64 &self.authorization_identity
65 }
66
67 pub fn authentication_identity(&self) -> &str {
69 &self.authentication_identity
70 }
71
72 fn exec_ref(&self, io: Io) -> ExecFuture {
75 let auth_str = encode(&format!(
76 "{}\0{}\0{}",
77 &self.authorization_identity, &self.authentication_identity, &self.password
78 ));
79
80 io.exec_simple_cmd(&["AUTH PLAIN ", auth_str.as_str()])
81 }
82}
83
84impl Cmd for Plain {
85 fn check_cmd_availability(&self, caps: Option<&EhloData>) -> Result<(), MissingCapabilities> {
86 validate_auth_capability(caps, "PLAIN")
87 }
88
89 fn exec(self, con: Io) -> ExecFuture {
90 self.exec_ref(con)
91 }
92}
93
94impl Cmd for Arc<Plain> {
95 fn check_cmd_availability(&self, caps: Option<&EhloData>) -> Result<(), MissingCapabilities> {
96 let me: &Plain = &*self;
97 me.check_cmd_availability(caps)
98 }
99
100 fn exec(self, con: Io) -> ExecFuture {
101 self.exec_ref(con)
102 }
103}
104
105fn validate_no_null_cps<R>(inp: R) -> Result<(), NullCodePointError>
106where
107 R: AsRef<str>,
108{
109 for bch in inp.as_ref().bytes() {
110 if bch == b'\0' {
111 return Err(NullCodePointError);
112 }
113 }
114 Ok(())
115}
116
117#[derive(Copy, Clone, Debug)]
119pub struct NullCodePointError;
120
121impl Display for NullCodePointError {
122 fn fmt(&self, fter: &mut fmt::Formatter) -> fmt::Result {
123 write!(fter, "input (username/password) contained null byte")
124 }
125}
126
127impl ErrorTrait for NullCodePointError {}