mod error; pub use error::Error; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[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, /// Status codes beginning with 2 are SUCCESS status codes, which mean /// that the client's message has been delivered. Success = 20, /// The mailbox has moved to a different address, and this message should /// be resent to that address. Redirect(Redirect) = 30, /// Status codes beginning with 5 are PERMANENT FAILURE status codes, which /// mean the request did not succeed, and should not be retried. TemporaryFailure(TemporaryFailure) = 40, /// Something is wrong with the mailserver, and you should not try to /// resend your message. PermanentFailure(PermanentFailure) = 50, /// Status codes beginning with 6 are AUTHENTICATION FAILURE status codes, /// which mean that there was an issue with the client's certificate. AuthenticationFailure(AuthenticationFailure) = 60, } impl From for u8 { fn from(value: Status) -> Self { match value { Status::Input => 10, Status::Success => 20, Status::Redirect(n) => 30 + n as u8, Status::TemporaryFailure(n) => 40 + n as u8, Status::PermanentFailure(n) => 50 + n as u8, Status::AuthenticationFailure(n) => 60 + n as u8, } } } impl TryFrom for Status { type Error = Error; fn try_from(value: u8) -> Result { match value / 10 { 1 => Ok(Self::Input), 2 => Ok(Self::Success), 3 => Ok(Self::Redirect((value % 30).try_into()?)), 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(Error), } } } #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] /// 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. Temporary = 0, /// The mailbox has moved to a different address, and all future /// messages should be sent to that address. Permanent = 1, Other = 2, } impl TryFrom for Redirect { type Error = Error; fn try_from(value: u8) -> Result { match value { 0 => Ok(Self::Temporary), 1 => Ok(Self::Permanent), n if n < 10 => Ok(Self::Other), _ => Err(Error), } } } #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] /// 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. TemporaryError = 0, /// The mailserver can't accept mail right now. ServerUnavailable = 1, /// A mailserver script ran for your message, but experienced an error. CgiError = 2, /// There was a problem accepting mail for that domain, but it might /// resolve itself. ProxyError = 3, /// You are being rate limited - wait before trying to send more mail. RateLimit = 4, /// The mailbox isn't accepting mail right now, but it might in the future. MailboxFull = 5, Other = 6, } impl TryFrom for TemporaryFailure { type Error = Error; fn try_from(value: u8) -> Result { match value { 0 => Ok(Self::TemporaryError), 1 => Ok(Self::ServerUnavailable), 2 => Ok(Self::CgiError), 3 => Ok(Self::ProxyError), 4 => Ok(Self::RateLimit), 5 => Ok(Self::MailboxFull), n if n < 10 => Ok(Self::Other), _ => Err(Error), } } } #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] /// Status codes representing that a permanent failure has occurred and the sending /// server should not resend the message. pub enum PermanentFailure { /// Something is wrong with the mailserver, and you should not try to resend /// your message. PermanentError = 0, /// The mailbox you are trying to send to doesn't exist, and the mailserver /// won't accept your message. MailboxNonexistent = 1, /// The mailbox you are trying to send to existed once, but doesn't anymore. MailboxGone = 2, /// This mailserver doesn't serve mail for the hostname you provided. DomainNotServiced = 3, /// Your request is malformed, and won't be accepted by the mailserver. BadRequest = 9, Other = 4, } impl TryFrom for PermanentFailure { type Error = Error; fn try_from(value: u8) -> Result { match value { 0 => Ok(Self::PermanentError), 1 => Ok(Self::MailboxNonexistent), 2 => Ok(Self::MailboxGone), 3 => Ok(Self::DomainNotServiced), 9 => Ok(Self::BadRequest), n if n < 10 => Ok(Self::Other), _ => Err(Error), } } } #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] /// Status codes representing an authentication failure pub enum AuthenticationFailure { /// This mailserver doesn't accept anonymous mail, and you need to repeat your /// request with a certificate. CertificateRequired = 0, /// Your certificate was validated, but you are not allowed to send mail to /// that mailbox. UnauthorizedSender = 1, /// Your certificate might be legitimate, but it has a problem - it is expired, /// or it doesn't point to a valid Misfin identity, etc. CertificateInvalid = 2, /// Your certificate matches an identity that the mailserver recognizes, but /// the fingerprint has changed, so it is rejecting your message. IdentityMismatch = 3, /// The mailserver needs you to complete a task to confirm that you are a /// legitimate sender. (This is reserved for a Hashcash style anti-spam measure). ProofRequired = 4, Other = 5, } impl TryFrom for AuthenticationFailure { type Error = Error; fn try_from(value: u8) -> Result { match value { 0 => Ok(Self::CertificateRequired), 1 => Ok(Self::UnauthorizedSender), 2 => Ok(Self::CertificateInvalid), 3 => Ok(Self::IdentityMismatch), 4 => Ok(Self::ProofRequired), n if n < 10 => Ok(Self::Other), _ => Err(Error), } } } #[cfg(test)] mod tests { use super::*; #[test] fn to_number_success() { let num: u8 = Status::Success.into(); assert_eq!(num, 20); } #[test] fn to_number_redirect() { assert_eq!(u8::from(Status::Redirect(Redirect::Temporary)), 30); assert_eq!(u8::from(Status::Redirect(Redirect::Permanent)), 31); assert_eq!(u8::from(Status::Redirect(Redirect::Other)), 32); } #[test] fn to_number_temporary() { assert_eq!( u8::from(Status::TemporaryFailure(TemporaryFailure::TemporaryError)), 40 ); assert_eq!( u8::from(Status::TemporaryFailure( TemporaryFailure::ServerUnavailable )), 41 ); assert_eq!( u8::from(Status::TemporaryFailure(TemporaryFailure::CgiError)), 42 ); assert_eq!( u8::from(Status::TemporaryFailure(TemporaryFailure::ProxyError)), 43 ); assert_eq!( u8::from(Status::TemporaryFailure(TemporaryFailure::RateLimit)), 44 ); assert_eq!( u8::from(Status::TemporaryFailure(TemporaryFailure::MailboxFull)), 45 ); assert_eq!( u8::from(Status::TemporaryFailure(TemporaryFailure::Other)), 46 ); } #[test] fn to_number_permanent() { assert_eq!( u8::from(Status::PermanentFailure(PermanentFailure::PermanentError)), 50 ); assert_eq!( u8::from(Status::PermanentFailure( PermanentFailure::MailboxNonexistent )), 51 ); assert_eq!( u8::from(Status::PermanentFailure(PermanentFailure::MailboxGone)), 52 ); assert_eq!( u8::from(Status::PermanentFailure( PermanentFailure::DomainNotServiced )), 53 ); assert_eq!( u8::from(Status::PermanentFailure(PermanentFailure::BadRequest)), 59 ); assert_eq!( u8::from(Status::PermanentFailure(PermanentFailure::Other)), 54 ); } #[test] fn to_number_auth() { assert_eq!( u8::from(Status::AuthenticationFailure( AuthenticationFailure::CertificateRequired )), 60 ); assert_eq!( u8::from(Status::AuthenticationFailure( AuthenticationFailure::UnauthorizedSender )), 61 ); assert_eq!( u8::from(Status::AuthenticationFailure( AuthenticationFailure::CertificateInvalid )), 62 ); assert_eq!( u8::from(Status::AuthenticationFailure( AuthenticationFailure::IdentityMismatch )), 63 ); assert_eq!( u8::from(Status::AuthenticationFailure( AuthenticationFailure::ProofRequired )), 64 ); assert_eq!( u8::from(Status::AuthenticationFailure(AuthenticationFailure::Other)), 65 ); } #[test] fn parse_status_success() { let status = Status::try_from(21).unwrap(); assert_eq!(status, Status::Success); } #[test] fn parse_redirect_err() { assert!(Redirect::try_from(42).is_err()); } #[test] fn parse_status_redirect() { assert_eq!( Status::try_from(30).unwrap(), Status::Redirect(Redirect::Temporary) ); assert_eq!( Status::try_from(31).unwrap(), Status::Redirect(Redirect::Permanent) ); assert_eq!( Status::try_from(32).unwrap(), Status::Redirect(Redirect::Other) ); } #[test] fn parse_temporary_err() { assert!(TemporaryFailure::try_from(42).is_err()); } #[test] fn parse_status_temporary() { assert_eq!( Status::try_from(40).unwrap(), Status::TemporaryFailure(TemporaryFailure::TemporaryError) ); assert_eq!( Status::try_from(41).unwrap(), Status::TemporaryFailure(TemporaryFailure::ServerUnavailable) ); assert_eq!( Status::try_from(42).unwrap(), Status::TemporaryFailure(TemporaryFailure::CgiError) ); assert_eq!( Status::try_from(43).unwrap(), Status::TemporaryFailure(TemporaryFailure::ProxyError) ); assert_eq!( Status::try_from(44).unwrap(), Status::TemporaryFailure(TemporaryFailure::RateLimit) ); assert_eq!( Status::try_from(45).unwrap(), Status::TemporaryFailure(TemporaryFailure::MailboxFull) ); assert_eq!( Status::try_from(46).unwrap(), Status::TemporaryFailure(TemporaryFailure::Other) ); } #[test] fn parse_permanent_err() { assert!(PermanentFailure::try_from(42).is_err()); } #[test] fn parse_status_permanent() { assert_eq!( Status::try_from(50).unwrap(), Status::PermanentFailure(PermanentFailure::PermanentError) ); assert_eq!( Status::try_from(51).unwrap(), Status::PermanentFailure(PermanentFailure::MailboxNonexistent) ); assert_eq!( Status::try_from(52).unwrap(), Status::PermanentFailure(PermanentFailure::MailboxGone) ); assert_eq!( Status::try_from(53).unwrap(), Status::PermanentFailure(PermanentFailure::DomainNotServiced) ); assert_eq!( Status::try_from(59).unwrap(), Status::PermanentFailure(PermanentFailure::BadRequest) ); assert_eq!( Status::try_from(57).unwrap(), Status::PermanentFailure(PermanentFailure::Other) ); } #[test] fn parse_auth_err() { assert!(AuthenticationFailure::try_from(42).is_err()); } #[test] fn parse_status_auth() { assert_eq!( Status::try_from(60).unwrap(), Status::AuthenticationFailure(AuthenticationFailure::CertificateRequired) ); assert_eq!( Status::try_from(61).unwrap(), Status::AuthenticationFailure(AuthenticationFailure::UnauthorizedSender) ); assert_eq!( Status::try_from(62).unwrap(), Status::AuthenticationFailure(AuthenticationFailure::CertificateInvalid) ); assert_eq!( Status::try_from(63).unwrap(), Status::AuthenticationFailure(AuthenticationFailure::IdentityMismatch) ); assert_eq!( Status::try_from(64).unwrap(), Status::AuthenticationFailure(AuthenticationFailure::ProofRequired) ); assert_eq!( Status::try_from(65).unwrap(), Status::AuthenticationFailure(AuthenticationFailure::Other) ); } }