Finish refactor and add a number of doc comments
This commit is contained in:
parent
02de655640
commit
4a40603efc
15 changed files with 193 additions and 155 deletions
|
@ -1,16 +1,7 @@
|
|||
use digest::Digest;
|
||||
use rustls::Certificate;
|
||||
use sha2::Sha256;
|
||||
use std::fmt::{self, Write};
|
||||
use x509_parser::prelude::*;
|
||||
|
||||
pub trait Fingerprint {
|
||||
type Error;
|
||||
|
||||
fn fingerprint(&self) -> Result<(String, String), Self::Error>;
|
||||
}
|
||||
use {std::fmt, x509_parser::prelude::X509Error};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
/// Errors which can occur when fingerprinting a certificate
|
||||
pub enum Error {
|
||||
Fmt,
|
||||
X509(X509Error),
|
||||
|
@ -45,21 +36,3 @@ impl From<x509_parser::nom::Err<x509_parser::error::X509Error>> for Error {
|
|||
Self::X509(value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Fingerprint for Certificate {
|
||||
type Error = Error;
|
||||
|
||||
fn fingerprint(&self) -> Result<(String, String), Self::Error> {
|
||||
let (_, pk) = X509Certificate::from_der(self.as_ref())?;
|
||||
let subject = pk.subject().to_string();
|
||||
let key = pk.public_key().subject_public_key.as_ref();
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(key);
|
||||
let res = hasher.finalize();
|
||||
let mut s = String::with_capacity(res.len());
|
||||
for c in res {
|
||||
write!(s, "{c:02x}")?;
|
||||
}
|
||||
Ok((subject[3..].to_string(), s))
|
||||
}
|
||||
}
|
33
src/fingerprint/mod.rs
Normal file
33
src/fingerprint/mod.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use digest::Digest;
|
||||
use rustls::Certificate;
|
||||
use sha2::Sha256;
|
||||
use std::fmt::Write;
|
||||
use x509_parser::prelude::*;
|
||||
|
||||
mod error;
|
||||
pub use error::Error;
|
||||
|
||||
/// Creates an sha256 fingerprint for a certificate
|
||||
pub trait Fingerprint {
|
||||
type Error;
|
||||
|
||||
fn fingerprint(&self) -> Result<(String, String), Self::Error>;
|
||||
}
|
||||
|
||||
impl Fingerprint for Certificate {
|
||||
type Error = Error;
|
||||
|
||||
fn fingerprint(&self) -> Result<(String, String), Self::Error> {
|
||||
let (_, pk) = X509Certificate::from_der(self.as_ref())?;
|
||||
let subject = pk.subject().to_string();
|
||||
let key = pk.public_key().subject_public_key.as_ref();
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(key);
|
||||
let res = hasher.finalize();
|
||||
let mut s = String::with_capacity(res.len());
|
||||
for c in res {
|
||||
write!(s, "{c:02x}")?;
|
||||
}
|
||||
Ok((subject[3..].to_string(), s))
|
||||
}
|
||||
}
|
19
src/host/error.rs
Normal file
19
src/host/error.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
/// Errors which can occur when parsing a host from a string
|
||||
pub enum Error {
|
||||
MissingSeparator,
|
||||
EmptyDomain,
|
||||
EmptyTld,
|
||||
EmptySubdomain,
|
||||
IllegalWhitespace,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
|
@ -1,6 +1,9 @@
|
|||
use std::{fmt, str::FromStr};
|
||||
mod error;
|
||||
pub use error::Error;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
/// Represents the fully qualified domain name for this host
|
||||
pub struct Host {
|
||||
pub subdomain: Option<String>,
|
||||
pub domain: String,
|
||||
|
@ -17,40 +20,23 @@ impl fmt::Display for Host {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ParseHostError {
|
||||
MissingSeparator,
|
||||
EmptyDomain,
|
||||
EmptyTld,
|
||||
EmptySubdomain,
|
||||
IllegalWhitespace,
|
||||
}
|
||||
|
||||
impl fmt::Display for ParseHostError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParseHostError {}
|
||||
|
||||
impl FromStr for Host {
|
||||
type Err = ParseHostError;
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s.contains(char::is_whitespace) {
|
||||
return Err(ParseHostError::IllegalWhitespace);
|
||||
return Err(Error::IllegalWhitespace);
|
||||
}
|
||||
if let Some((domain, tld)) = s.rsplit_once('.') {
|
||||
if domain.is_empty() {
|
||||
Err(ParseHostError::EmptyDomain)
|
||||
Err(Error::EmptyDomain)
|
||||
} else if tld.is_empty() {
|
||||
Err(ParseHostError::EmptyTld)
|
||||
Err(Error::EmptyTld)
|
||||
} else if let Some((subdomain, domain)) = domain.rsplit_once('.') {
|
||||
if subdomain.is_empty() {
|
||||
Err(ParseHostError::EmptySubdomain)
|
||||
Err(Error::EmptySubdomain)
|
||||
} else if domain.is_empty() {
|
||||
Err(ParseHostError::EmptyDomain)
|
||||
Err(Error::EmptyDomain)
|
||||
} else {
|
||||
Ok(Host {
|
||||
subdomain: Some(subdomain.to_string()),
|
||||
|
@ -66,7 +52,7 @@ impl FromStr for Host {
|
|||
})
|
||||
}
|
||||
} else {
|
||||
Err(ParseHostError::MissingSeparator)
|
||||
Err(Error::MissingSeparator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,37 +65,31 @@ mod tests {
|
|||
fn parse_missing_separator() {
|
||||
assert_eq!(
|
||||
"exampledotcom".parse::<Host>(),
|
||||
Err(ParseHostError::MissingSeparator)
|
||||
Err(Error::MissingSeparator)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_empty_tld() {
|
||||
assert_eq!("example.".parse::<Host>(), Err(ParseHostError::EmptyTld));
|
||||
assert_eq!("example.".parse::<Host>(), Err(Error::EmptyTld));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_empty_domain() {
|
||||
assert_eq!(".com".parse::<Host>(), Err(ParseHostError::EmptyDomain));
|
||||
assert_eq!(
|
||||
"example..com".parse::<Host>(),
|
||||
Err(ParseHostError::EmptyDomain)
|
||||
);
|
||||
assert_eq!(".com".parse::<Host>(), Err(Error::EmptyDomain));
|
||||
assert_eq!("example..com".parse::<Host>(), Err(Error::EmptyDomain));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_empty_subdomain() {
|
||||
assert_eq!(
|
||||
".example.com".parse::<Host>(),
|
||||
Err(ParseHostError::EmptySubdomain)
|
||||
);
|
||||
assert_eq!(".example.com".parse::<Host>(), Err(Error::EmptySubdomain));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_illegal_whitespace() {
|
||||
assert_eq!(
|
||||
"exam\tple.com".parse::<Host>(),
|
||||
Err(ParseHostError::IllegalWhitespace)
|
||||
Err(Error::IllegalWhitespace)
|
||||
);
|
||||
}
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
pub use super::{
|
||||
fingerprint::{Fingerprint, Error as FingerprintError},
|
||||
host::{Host, ParseHostError},
|
||||
fingerprint::{Error as FingerprintError, Fingerprint},
|
||||
host::{Error as ParseHostError, Host},
|
||||
receiver,
|
||||
response::{Response, Error as ParseResponseError},
|
||||
request::{Request, ParseRequestError},
|
||||
request::{Error as ParseRequestError, Request},
|
||||
response::{Error as ParseResponseError, Response},
|
||||
sender::{CertificateStore, Error as SenderError, Sender, Verifier},
|
||||
status::*,
|
||||
status::{Error as ParseStatusError, Status, Redirect, TemporaryFailure, PermanentFailure, AuthenticationFailure},
|
||||
};
|
||||
|
|
36
src/request/error.rs
Normal file
36
src/request/error.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use {crate::prelude::ParseHostError, std::fmt};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
/// Errors which can occur when parsing a request
|
||||
pub enum Error {
|
||||
MissingSeparator,
|
||||
EmptyUser,
|
||||
EmptyHost,
|
||||
EmptyMessage,
|
||||
Malformed,
|
||||
ParseHostError(ParseHostError),
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::ParseHostError(e) => write!(f, "{e}"),
|
||||
_ => write!(f, "{self:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Self::ParseHostError(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseHostError> for Error {
|
||||
fn from(value: ParseHostError) -> Self {
|
||||
Self::ParseHostError(value)
|
||||
}
|
||||
}
|
|
@ -1,11 +1,18 @@
|
|||
use crate::host::{Host, ParseHostError};
|
||||
use crate::prelude::Host;
|
||||
use std::{fmt, str::FromStr};
|
||||
|
||||
mod error;
|
||||
pub use error::Error;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
/// The full request as sent by the `Sender` and received by the `Receiver`
|
||||
pub struct Request {
|
||||
user: String,
|
||||
host: Host,
|
||||
message: String,
|
||||
/// The username of the sender
|
||||
pub user: String,
|
||||
/// The fully qualified domain name of the sending server
|
||||
pub host: Host,
|
||||
/// The message body
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for Request {
|
||||
|
@ -18,60 +25,26 @@ impl fmt::Display for Request {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ParseRequestError {
|
||||
MissingSeparator,
|
||||
EmptyUser,
|
||||
EmptyHost,
|
||||
EmptyMessage,
|
||||
Malformed,
|
||||
ParseHostError(ParseHostError),
|
||||
}
|
||||
|
||||
impl fmt::Display for ParseRequestError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::ParseHostError(e) => write!(f, "{e}"),
|
||||
_ => write!(f, "{self:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParseRequestError {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Self::ParseHostError(e) => Some(e),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseHostError> for ParseRequestError {
|
||||
fn from(value: ParseHostError) -> Self {
|
||||
Self::ParseHostError(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Request {
|
||||
type Err = ParseRequestError;
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if let Some((user, message)) = s.split_once(' ') {
|
||||
let Some(message) = message.strip_suffix("\r\n").map(ToString::to_string) else {
|
||||
return Err(ParseRequestError::Malformed);
|
||||
return Err(Error::Malformed);
|
||||
};
|
||||
if message.is_empty() {
|
||||
return Err(ParseRequestError::EmptyMessage);
|
||||
return Err(Error::EmptyMessage);
|
||||
}
|
||||
if let Some((user, host)) = user.rsplit_once('@') {
|
||||
if host.is_empty() {
|
||||
return Err(ParseRequestError::EmptyHost);
|
||||
return Err(Error::EmptyHost);
|
||||
} else if user == "misfin://" {
|
||||
return Err(ParseRequestError::EmptyUser);
|
||||
return Err(Error::EmptyUser);
|
||||
}
|
||||
let host = host.parse()?;
|
||||
let Some(user) = user.strip_prefix("misfin://").map(ToString::to_string) else {
|
||||
return Err(ParseRequestError::Malformed);
|
||||
return Err(Error::Malformed);
|
||||
};
|
||||
Ok(Request {
|
||||
user,
|
||||
|
@ -79,10 +52,10 @@ impl FromStr for Request {
|
|||
message,
|
||||
})
|
||||
} else {
|
||||
Err(ParseRequestError::MissingSeparator)
|
||||
Err(Error::MissingSeparator)
|
||||
}
|
||||
} else {
|
||||
Err(ParseRequestError::MissingSeparator)
|
||||
Err(Error::MissingSeparator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -90,6 +63,7 @@ impl FromStr for Request {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::prelude::ParseHostError;
|
||||
|
||||
const REQ_STR: &'static str = "misfin://john@misfin.example.com Anyone seen Jane?\r\n";
|
||||
|
||||
|
@ -118,33 +92,33 @@ mod tests {
|
|||
#[test]
|
||||
fn parse_missing_sep() {
|
||||
let req = "misfin://john@example.comHelloWorld!\r\n".parse::<Request>();
|
||||
assert_eq!(req, Err(ParseRequestError::MissingSeparator));
|
||||
assert_eq!(req, Err(Error::MissingSeparator));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_malformed() {
|
||||
let req = "misfin://john@example.com Hello World!\n".parse::<Request>();
|
||||
assert_eq!(req, Err(ParseRequestError::Malformed));
|
||||
assert_eq!(req, Err(Error::Malformed));
|
||||
let req = "mail://john@example.com Hello World!\r\n".parse::<Request>();
|
||||
assert_eq!(req, Err(ParseRequestError::Malformed));
|
||||
assert_eq!(req, Err(Error::Malformed));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_empty_user() {
|
||||
let req = "misfin://@example.com Hello World!\r\n".parse::<Request>();
|
||||
assert_eq!(req, Err(ParseRequestError::EmptyUser));
|
||||
assert_eq!(req, Err(Error::EmptyUser));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_empty_host() {
|
||||
let req = "misfin://john@ Hello World!\r\n".parse::<Request>();
|
||||
assert_eq!(req, Err(ParseRequestError::EmptyHost));
|
||||
assert_eq!(req, Err(Error::EmptyHost));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_empty_msg() {
|
||||
let req = "misfin://john@example.com \r\n".parse::<Request>();
|
||||
assert_eq!(req, Err(ParseRequestError::EmptyMessage));
|
||||
assert_eq!(req, Err(Error::EmptyMessage));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -152,9 +126,7 @@ mod tests {
|
|||
let req = "misfin://john@example\tfairy.com Hello World!\r\n".parse::<Request>();
|
||||
assert_eq!(
|
||||
req,
|
||||
Err(ParseRequestError::ParseHostError(
|
||||
ParseHostError::IllegalWhitespace
|
||||
))
|
||||
Err(Error::ParseHostError(ParseHostError::IllegalWhitespace))
|
||||
);
|
||||
}
|
||||
}
|
|
@ -4,10 +4,17 @@ use {
|
|||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
/// Errors which can occur when parsing the response sent by the receving server
|
||||
/// back to the sender
|
||||
pub enum Error {
|
||||
/// The message was too long. The Misfin spec allows a maximum length of
|
||||
/// 2048 bytes for the message body.
|
||||
TooLong,
|
||||
/// An error occurred parsing the status code as a number.
|
||||
ParseInt(ParseIntError),
|
||||
/// The server sent an invalid or unrecognized status code
|
||||
StatusError,
|
||||
/// The response was malformed
|
||||
Malformed,
|
||||
}
|
||||
|
||||
|
@ -42,4 +49,3 @@ impl From<ParseStatusError> for Error {
|
|||
Self::StatusError
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use std::{fmt, str::FromStr};
|
||||
use crate::prelude::Status;
|
||||
use std::{fmt, str::FromStr};
|
||||
|
||||
mod error;
|
||||
pub use error::Error;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
/// Sent from the receiving server back to the sending server
|
||||
pub struct Response {
|
||||
pub status: Status,
|
||||
pub meta: String,
|
|
@ -4,6 +4,7 @@ use {
|
|||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Errors which might occur when sending a message
|
||||
pub enum Error {
|
||||
TlsError(rustls::Error),
|
||||
RequestError(ParseRequestError),
|
||||
|
@ -56,4 +57,3 @@ impl From<io::Error> for Error {
|
|||
Self::IoError(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,22 +1,26 @@
|
|||
use crate::{
|
||||
request::Request,
|
||||
response::Response,
|
||||
pub use self::{
|
||||
error::Error,
|
||||
verifier::{CertificateStore, Verifier},
|
||||
};
|
||||
use crate::{request::Request, response::Response};
|
||||
use std::io::{Read, Write};
|
||||
pub use self::{error::Error, verifier::{CertificateStore, Verifier}};
|
||||
|
||||
mod error;
|
||||
mod verifier;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Sends a piece of mail from the sending server to the receiving server
|
||||
pub struct Sender<S, C, T>
|
||||
where
|
||||
S: CertificateStore,
|
||||
C: Sized,
|
||||
T: Read + Write + Sized,
|
||||
{
|
||||
/// The full message text to be sent
|
||||
pub request: Request,
|
||||
/// Verifies the receiving server's certificate
|
||||
pub verifier: Verifier<S>,
|
||||
/// The TLS stream used for the connection
|
||||
pub stream: rustls::StreamOwned<C, T>,
|
||||
}
|
||||
|
|
@ -5,14 +5,18 @@ use rustls::{
|
|||
};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// An item which stores known certificates
|
||||
pub trait CertificateStore: Send + Sync {
|
||||
fn get(&self, host: &str) -> Option<String>;
|
||||
fn insert(&mut self, host: &str, fingerprint: &str);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// A verifier is used to verify certificates sent by the receiving server
|
||||
/// during the tls handshake.
|
||||
pub struct Verifier<S: CertificateStore> {
|
||||
store: Arc<Mutex<S>>,
|
||||
/// An item which serves as storage for certificates
|
||||
pub store: Arc<Mutex<S>>,
|
||||
}
|
||||
|
||||
impl<S: CertificateStore> ServerCertVerifier for Verifier<S> {
|
||||
|
|
12
src/status/error.rs
Normal file
12
src/status/error.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Error;
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
|
@ -1,18 +1,10 @@
|
|||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct ParseStatusError;
|
||||
|
||||
impl fmt::Display for ParseStatusError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{self:?}")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParseStatusError {}
|
||||
mod error;
|
||||
pub use error::Error;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[repr(u8)]
|
||||
/// Status codes sent back to the sender representing how a receiving server has
|
||||
/// processed a message.
|
||||
pub enum Status {
|
||||
/// These codes are reserved, and must not be sent by a Misfin server.
|
||||
Input = 10,
|
||||
|
@ -46,7 +38,7 @@ impl From<Status> for u8 {
|
|||
}
|
||||
|
||||
impl TryFrom<u8> for Status {
|
||||
type Error = ParseStatusError;
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value / 10 {
|
||||
|
@ -56,12 +48,13 @@ impl TryFrom<u8> for Status {
|
|||
4 => Ok(Self::TemporaryFailure((value % 40).try_into()?)),
|
||||
5 => Ok(Self::PermanentFailure((value % 50).try_into()?)),
|
||||
6 => Ok(Self::AuthenticationFailure((value % 60).try_into()?)),
|
||||
_ => Err(ParseStatusError),
|
||||
_ => Err(Error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
/// Status codes representing that a redirect is required
|
||||
pub enum Redirect {
|
||||
/// The mailbox has moved to a different address, and this message
|
||||
/// should be resent to that address.
|
||||
|
@ -73,19 +66,21 @@ pub enum Redirect {
|
|||
}
|
||||
|
||||
impl TryFrom<u8> for Redirect {
|
||||
type Error = ParseStatusError;
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(Self::Temporary),
|
||||
1 => Ok(Self::Permanent),
|
||||
n if n < 10 => Ok(Self::Other),
|
||||
_ => Err(ParseStatusError),
|
||||
_ => Err(Error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
/// Status codes representing that a temporary failure has occurred. The sending server should
|
||||
/// retry sending the message.
|
||||
pub enum TemporaryFailure {
|
||||
/// The mailserver experienced a transient issue, and the message
|
||||
/// should be resent.
|
||||
|
@ -105,7 +100,7 @@ pub enum TemporaryFailure {
|
|||
}
|
||||
|
||||
impl TryFrom<u8> for TemporaryFailure {
|
||||
type Error = ParseStatusError;
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
|
@ -116,12 +111,14 @@ impl TryFrom<u8> for TemporaryFailure {
|
|||
4 => Ok(Self::RateLimit),
|
||||
5 => Ok(Self::MailboxFull),
|
||||
n if n < 10 => Ok(Self::Other),
|
||||
_ => Err(ParseStatusError),
|
||||
_ => Err(Error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
/// Status codes representing that a permanent failure has occurred and the sending server should
|
||||
/// not resend the message.
|
||||
pub enum PermanentFailure {
|
||||
PermanentError = 0,
|
||||
MailboxNonexistent = 1,
|
||||
|
@ -132,7 +129,7 @@ pub enum PermanentFailure {
|
|||
}
|
||||
|
||||
impl TryFrom<u8> for PermanentFailure {
|
||||
type Error = ParseStatusError;
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
|
@ -142,12 +139,13 @@ impl TryFrom<u8> for PermanentFailure {
|
|||
3 => Ok(Self::DomainNotServiced),
|
||||
9 => Ok(Self::BadRequest),
|
||||
n if n < 10 => Ok(Self::Other),
|
||||
_ => Err(ParseStatusError),
|
||||
_ => Err(Error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
/// Status codes representing an authentication failure
|
||||
pub enum AuthenticationFailure {
|
||||
CertificateRequired = 0,
|
||||
UnauthorizedSender = 1,
|
||||
|
@ -158,7 +156,7 @@ pub enum AuthenticationFailure {
|
|||
}
|
||||
|
||||
impl TryFrom<u8> for AuthenticationFailure {
|
||||
type Error = ParseStatusError;
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
|
@ -168,7 +166,7 @@ impl TryFrom<u8> for AuthenticationFailure {
|
|||
3 => Ok(Self::IdentityMismatch),
|
||||
4 => Ok(Self::ProofRequired),
|
||||
n if n < 10 => Ok(Self::Other),
|
||||
_ => Err(ParseStatusError),
|
||||
_ => Err(Error),
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue