diff --git a/src/lib.rs b/src/lib.rs index 51c2329..a55eb58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -217,7 +217,7 @@ pub struct EhloResponse { #[derive(Debug, Clone, PartialEq, Eq)] pub struct Response { - pub code: [u8; 3], + pub code: u16, pub esc: [u8; 3], pub message: T, } @@ -251,15 +251,14 @@ pub enum Error { SyntaxError { syntax: &'static str }, InvalidParameter { param: &'static str }, UnsupportedParameter { param: String }, - LineTooLong, ResponseTooLong, - InvalidResponse { code: [u8; 3] }, + InvalidResponse { code: u16 }, } pub(crate) const LF: u8 = b'\n'; pub(crate) const SP: u8 = b' '; -pub(crate) trait IntoString: Sized { +pub trait IntoString: Sized { fn into_string(self) -> String; } diff --git a/src/request/mod.rs b/src/request/mod.rs index aaedca1..e45293e 100644 --- a/src/request/mod.rs +++ b/src/request/mod.rs @@ -32,7 +32,7 @@ pub(crate) const ETRN: u64 = (b'E' as u64) | (b'T' as u64) << 8 | (b'R' as u64) << 16 | (b'N' as u64) << 24; pub(crate) const ATRN: u64 = (b'A' as u64) | (b'T' as u64) << 8 | (b'R' as u64) << 16 | (b'N' as u64) << 24; -pub(crate) const AUTH: u64 = +pub const AUTH: u64 = (b'A' as u64) | (b'U' as u64) << 8 | (b'T' as u64) << 16 | (b'H' as u64) << 24; pub(crate) const BURL: u64 = (b'B' as u64) | (b'U' as u64) << 8 | (b'R' as u64) << 16 | (b'L' as u64) << 24; diff --git a/src/request/parser.rs b/src/request/parser.rs index cda44b5..5824ba3 100644 --- a/src/request/parser.rs +++ b/src/request/parser.rs @@ -289,9 +289,9 @@ impl Request { } } -pub(crate) struct Rfc5321Parser<'x, 'y> { +pub struct Rfc5321Parser<'x, 'y> { bytes: &'x mut Iter<'y, u8>, - pub(crate) stop_char: u8, + pub stop_char: u8, pub bytes_left: usize, } @@ -1046,7 +1046,7 @@ impl<'x, 'y> Rfc5321Parser<'x, 'y> { Ok(params) } - pub(crate) fn mechanism(&mut self) -> Result, Error> { + pub fn mechanism(&mut self) -> Result, Error> { let mut trailing_chars = [0u8; 8]; let mut pos = 0; let mechanism = self.hashed_value_long()?; diff --git a/src/request/receiver.rs b/src/request/receiver.rs index a742911..1722f0d 100644 --- a/src/request/receiver.rs +++ b/src/request/receiver.rs @@ -28,6 +28,7 @@ pub struct DummyDataReceiver { prev_last_ch: u8, } +#[derive(Default)] pub struct DummyLineReceiver {} impl RequestReceiver { @@ -43,7 +44,7 @@ impl RequestReceiver { if bytes_left < MAX_LINE_LENGTH { self.buf = buf[buf.len() - bytes_left..].to_vec(); } else { - return Err(Error::LineTooLong); + return Err(Error::ResponseTooLong); } } } @@ -58,7 +59,7 @@ impl RequestReceiver { return result; } else if self.buf.len() == MAX_LINE_LENGTH { self.buf.clear(); - return Err(Error::LineTooLong); + return Err(Error::ResponseTooLong); } } } diff --git a/src/response/generate.rs b/src/response/generate.rs index e19bb5e..48de3de 100644 --- a/src/response/generate.rs +++ b/src/response/generate.rs @@ -25,7 +25,7 @@ impl EhloResponse { let mut capabilities = self.capabilities; while capabilities != 0 { - let capability = 63 - capabilities.leading_zeros(); + let capability = 31 - capabilities.leading_zeros(); capabilities ^= 1 << capability; writer.write_all(b"250")?; @@ -109,14 +109,8 @@ impl Response { pub fn write(&self, mut writer: impl Write) -> io::Result<()> { write!( writer, - "{}{}{} {}.{}.{} {}\r\n", - self.code[0], - self.code[1], - self.code[2], - self.esc[0], - self.esc[1], - self.esc[2], - self.message + "{} {}.{}.{} {}\r\n", + self.code, self.esc[0], self.esc[1], self.esc[2], self.message ) } } @@ -176,17 +170,17 @@ impl BitToString for u64 { } impl Response { - pub fn new(c0: u8, c1: u8, c2: u8, e0: u8, e1: u8, e2: u8, message: T) -> Self { + pub fn new(code: u16, e0: u8, e1: u8, e2: u8, message: T) -> Self { Self { - code: [c0, c1, c2], + code, esc: [e0, e1, e2], message, } } /// Returns the reply's numeric status. - pub fn code(&self) -> &[u8] { - &self.code + pub fn code(&self) -> u16 { + self.code } /// Returns the message included in the reply. @@ -196,18 +190,18 @@ impl Response { /// Returns the status severity (first digit of the status code). pub fn severity(&self) -> Severity { - match self.code[0] { - 2 => Severity::PositiveCompletion, - 3 => Severity::PositiveIntermediate, - 4 => Severity::TransientNegativeCompletion, - 5 => Severity::PermanentNegativeCompletion, + match self.code { + 200..=299 => Severity::PositiveCompletion, + 300..=399 => Severity::PositiveIntermediate, + 400..=499 => Severity::TransientNegativeCompletion, + 500..=599 => Severity::PermanentNegativeCompletion, _ => Severity::Invalid, } } /// Returns the status category (second digit of the status code). pub fn category(&self) -> Category { - match self.code[1] { + match (self.code / 10) % 10 { 0 => Category::Syntax, 1 => Category::Information, 2 => Category::Connections, @@ -219,8 +213,8 @@ impl Response { } /// Returns the status details (third digit of the status code). - pub fn details(&self) -> u8 { - self.code[2] + pub fn details(&self) -> u16 { + self.code % 10 } /// Returns `true` if the reply is a positive completion. diff --git a/src/response/mod.rs b/src/response/mod.rs index 156e402..4fdb351 100644 --- a/src/response/mod.rs +++ b/src/response/mod.rs @@ -221,14 +221,8 @@ impl Display for Response { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, - "Code: {}{}{}, Enhanced code: {}.{}.{}, Message: {}", - self.code[0], - self.code[1], - self.code[2], - self.esc[0], - self.esc[1], - self.esc[2], - self.message, + "Code: {}, Enhanced code: {}.{}.{}, Message: {}", + self.code, self.esc[0], self.esc[1], self.esc[2], self.message, ) } } diff --git a/src/response/parser.rs b/src/response/parser.rs index ea0069c..d04a4b6 100644 --- a/src/response/parser.rs +++ b/src/response/parser.rs @@ -9,15 +9,17 @@ pub const MAX_REPONSE_LENGTH: usize = 4096; #[derive(Default)] pub struct ResponseReceiver { buf: Vec, - code: [u8; 6], + code: u16, + esc: [u8; 3], is_last: bool, pos: usize, } impl ResponseReceiver { - pub fn from_code(code: [u8; 3]) -> Self { + pub fn from_code(code: u16) -> Self { Self { - code: [code[0], code[1], code[2], 0, 0, 0], + code, + esc: [0, 0, 0], pos: 3, is_last: false, buf: Vec::new(), @@ -30,7 +32,10 @@ impl ResponseReceiver { 0..=2 => { if ch.is_ascii_digit() { if self.buf.is_empty() { - self.code[self.pos] = ch - b'0'; + self.code = self + .code + .saturating_mul(10) + .saturating_add((ch - b'0') as u16); } self.pos += 1; } else { @@ -62,11 +67,11 @@ impl ResponseReceiver { 4 | 5 | 6 => match ch { b'0'..=b'9' => { if self.buf.is_empty() { - let code = &mut self.code[self.pos - 1]; + let code = &mut self.esc[self.pos - 4]; *code = code.saturating_mul(10).saturating_add(ch - b'0'); } } - b'.' if self.pos < 6 && self.code[self.pos - 1] > 0 => { + b'.' if self.pos < 6 && self.esc[self.pos - 4] > 0 => { self.pos += 1; } _ => { @@ -91,8 +96,8 @@ impl ResponseReceiver { if ch == b'\n' { if self.is_last { return Ok(Response { - code: [self.code[0], self.code[1], self.code[2]], - esc: [self.code[3], self.code[4], self.code[5]], + code: self.code, + esc: self.esc, message: std::mem::take(&mut self.buf).into_string(), }); } else { @@ -107,7 +112,8 @@ impl ResponseReceiver { pub fn reset(&mut self) { self.is_last = false; - self.code.fill(0); + self.code = 0; + self.esc.fill(0); self.pos = 0; } } @@ -116,15 +122,15 @@ impl EhloResponse { pub fn parse(bytes: &mut Iter<'_, u8>) -> Result { let mut parser = Rfc5321Parser::new(bytes); let mut response = EhloResponse::default(); - let mut code = [0u8; 3]; let mut eol = false; let mut is_first_line = true; while !eol { - for code in code.iter_mut() { + let mut code: u16 = 0; + for _ in 0..3 { match parser.read_char()? { ch @ b'0'..=b'9' => { - *code = ch - b'0'; + code = code.saturating_mul(10).saturating_add((ch - b'0') as u16); } _ => { return Err(Error::SyntaxError { @@ -134,7 +140,7 @@ impl EhloResponse { } } - if code[0] != 2 || code[1] != 5 || code[2] != 0 { + if code != 250 { return Err(Error::InvalidResponse { code }); } @@ -143,7 +149,7 @@ impl EhloResponse { eol = true; } b'-' => (), - b'\n' if code[0] < 6 => { + b'\n' if code < 600 => { break; } _ => { @@ -416,7 +422,7 @@ mod tests { ), ( concat!("523-Massive\n", "523-Error\n", "523 Message\n"), - Err(Error::UnknownCommand), + Err(Error::InvalidResponse { code: 523 }), ), ] { let (response, parsed_response): (&str, Result, Error>) = item; @@ -446,7 +452,7 @@ mod tests { ( "250 2.1.1 Originator ok\n", Response { - code: [2, 5, 0], + code: 250, esc: [2, 1, 1], message: "Originator ok".to_string(), }, @@ -458,7 +464,7 @@ mod tests { "551 5.7.1 Select another host to act as your forwarder\n" ), Response { - code: [5, 5, 1], + code: 551, esc: [5, 7, 1], message: concat!( "Forwarding to remote hosts disabled\n", @@ -474,7 +480,7 @@ mod tests { "550 user has moved with no forwarding address\n" ), Response { - code: [5, 5, 0], + code: 550, esc: [0, 0, 0], message: "mailbox unavailable\nuser has moved with no forwarding address" .to_string(), @@ -487,7 +493,7 @@ mod tests { "550 user has moved with no forwarding address\n" ), Response { - code: [5, 5, 0], + code: 550, esc: [0, 0, 0], message: "mailbox unavailable\nuser has moved with no forwarding address" .to_string(), @@ -508,7 +514,7 @@ mod tests { "432 6.8.9 World!\n" ), Response { - code: [4, 3, 2], + code: 432, esc: [6, 8, 9], message: "\nHello\n\n,\n\n\n\n\n\nWorld!".to_string(), }, @@ -517,7 +523,7 @@ mod tests { ( concat!("250-Missing space\n", "250\n", "250 Ignore this"), Response { - code: [2, 5, 0], + code: 250, esc: [0, 0, 0], message: "Missing space\n".to_string(), },