From 60e0bb3bbb6ad2195b07ed4512d1e733a2f9392a Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Mon, 9 Aug 2021 17:56:17 -0500 Subject: [PATCH] wow testing --- Justfile | 3 ++ imap/src/client/configurable_cert_verifier.rs | 2 +- imap/src/proto/command.rs | 15 ++++---- imap/src/proto/response.rs | 4 +- imap/src/proto/rfc3501.rs | 7 ++++ imap/src/proto/test_rfc3501.rs | 18 ++++++++- proto-common/src/bytes.rs | 19 +++++++--- proto-common/src/formatter.rs | 37 +++++++++++++------ proto-common/src/lib.rs | 2 +- proto-common/src/parsers.rs | 20 +++++----- smtp/src/client/mod.rs | 1 + smtp/src/lib.rs | 1 + 12 files changed, 90 insertions(+), 39 deletions(-) create mode 100644 smtp/src/client/mod.rs diff --git a/Justfile b/Justfile index 5de3e0b..0a1164c 100644 --- a/Justfile +++ b/Justfile @@ -1,2 +1,5 @@ fmt: cargo +nightly fmt --all + +doc: + cargo doc --workspace --no-deps diff --git a/imap/src/client/configurable_cert_verifier.rs b/imap/src/client/configurable_cert_verifier.rs index 4e90170..ae2b59a 100644 --- a/imap/src/client/configurable_cert_verifier.rs +++ b/imap/src/client/configurable_cert_verifier.rs @@ -1,7 +1,7 @@ //! Configurable cert verifier for rustls, can disable hostname verification, //! etc. //! -//! Based closely on https://github.com/rustls/rustls/blob/v/0.19.0/rustls/src/verify.rs#L253 +//! Based closely on use tokio_rustls::{ rustls::{ diff --git a/imap/src/proto/command.rs b/imap/src/proto/command.rs index b941830..713a51f 100644 --- a/imap/src/proto/command.rs +++ b/imap/src/proto/command.rs @@ -1,9 +1,9 @@ use std::io::{self, Write}; use format_bytes::DisplayBytes; -use panorama_proto_common::{quote_string as q, Bytes}; +use panorama_proto_common::{quote_string, Bytes}; -use super::rfc3501::is_quoted_specials as isq; +use super::rfc3501::is_quoted_specials; #[derive(Clone, Debug)] pub enum Command { @@ -52,6 +52,7 @@ pub enum Command { impl DisplayBytes for Command { fn display_bytes(&self, w: &mut dyn Write) -> io::Result<()> { + let quote = quote_string(b'\x22', b'\\', is_quoted_specials); match self { // command-any Command::Capability => write_bytes!(w, b"CAPABILITY"), @@ -63,8 +64,8 @@ impl DisplayBytes for Command { write_bytes!( w, b"LOGIN {} {}", - q(&login.userid, isq), - q(&login.password, isq) + quote(&login.userid), + quote(&login.password) ) } Command::Starttls => write_bytes!(w, b"STARTTLS"), @@ -74,11 +75,11 @@ impl DisplayBytes for Command { write_bytes!( w, b"LIST {} {}", - q(&list.reference, isq), - q(&list.mailbox, isq) + quote(&list.reference), + quote(&list.mailbox) ) } - Command::Select(select) => write_bytes!(w, b"SELECT {}", q(&select.mailbox, isq)), + Command::Select(select) => write_bytes!(w, b"SELECT {}", quote(&select.mailbox)), #[cfg(feature = "rfc2177")] Command::Idle => write_bytes!(w, b"IDLE"), diff --git a/imap/src/proto/response.rs b/imap/src/proto/response.rs index 13b27f9..202ac8f 100644 --- a/imap/src/proto/response.rs +++ b/imap/src/proto/response.rs @@ -159,11 +159,11 @@ pub struct MailboxList { pub mailbox: Mailbox, } -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq)] pub enum MailboxListFlag { NoInferiors, NoSelect, Marked, Unmarked, Extension(Atom), -} +} \ No newline at end of file diff --git a/imap/src/proto/rfc3501.rs b/imap/src/proto/rfc3501.rs index a7eb22b..ad1e7b7 100644 --- a/imap/src/proto/rfc3501.rs +++ b/imap/src/proto/rfc3501.rs @@ -19,6 +19,13 @@ use super::response::{ }; use super::rfc2234::{is_char, is_cr, is_ctl, is_digit, is_dquote, is_lf, is_sp, CRLF, DQUOTE, SP}; +#[macro_export] +macro_rules! opt_nil { + ($t:expr) => { + alt((map($t, Some), map(crate::proto::rfc3501::nil, |_| None))) + }; +} + rule!(pub address : Address => map(paren!(tuple(( terminated(addr_name, SP), terminated(addr_adl, SP), diff --git a/imap/src/proto/test_rfc3501.rs b/imap/src/proto/test_rfc3501.rs index 318a407..2b96388 100644 --- a/imap/src/proto/test_rfc3501.rs +++ b/imap/src/proto/test_rfc3501.rs @@ -1,5 +1,6 @@ use panorama_proto_common::Bytes; +use super::response::*; use super::rfc3501::*; #[test] @@ -15,7 +16,20 @@ fn test_literal() { #[test] fn test_list() { - let _ = response(Bytes::from( - b"* LIST (\\HasChildren \\UnMarked \\Trash) \".\" Trash\r\n", + assert!(matches!( + response(Bytes::from( + b"* LIST (\\HasChildren \\UnMarked \\Trash) \".\" Trash\r\n", + )) + .unwrap() + .1, + Response::MailboxData(MailboxData::List(MailboxList { + flags, + delimiter: Some(b'.'), + mailbox: Mailbox::Name(mailbox), + }) ) if flags.len() == 3 && + flags.contains(&MailboxListFlag::Extension(Atom::from(b"HasChildren"))) && + flags.contains(&MailboxListFlag::Extension(Atom::from(b"UnMarked"))) && + flags.contains(&MailboxListFlag::Extension(Atom::from(b"Trash"))) && + &*mailbox == &b"Trash"[..] )); } diff --git a/proto-common/src/bytes.rs b/proto-common/src/bytes.rs index 8641627..a20ea66 100644 --- a/proto-common/src/bytes.rs +++ b/proto-common/src/bytes.rs @@ -7,12 +7,25 @@ use nom::{ CompareResult, Err, IResult, InputLength, Needed, }; -/// Glue code between nom and Bytes so they work together +/// Glue code between nom and Bytes so they work together. #[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct Bytes(bytes::Bytes); impl Bytes { + /// Length of the internal `Bytes`. + /// + /// # Example + /// + /// ``` + /// # use panorama_proto_common::Bytes; + /// // the &, [..] is needed since &[u8; N] doesn't coerce automatically to &[u8] + /// let b = Bytes::from(&b"hello"[..]); + /// assert_eq!(b.len(), 5); + /// ``` pub fn len(&self) -> usize { self.0.len() } + + /// Consumes the wrapper, returning the original `Bytes`. + pub fn inner(self) -> bytes::Bytes { self.0 } } impl DisplayBytes for Bytes { @@ -224,10 +237,6 @@ array_impls! { 50 51 52 53 54 55 56 57 58 59 } -impl Bytes { - pub fn inner(self) -> bytes::Bytes { self.0 } -} - impl Deref for Bytes { type Target = [u8]; fn deref(&self) -> &Self::Target { &*self.0 } diff --git a/proto-common/src/formatter.rs b/proto-common/src/formatter.rs index 51f302c..0631d76 100644 --- a/proto-common/src/formatter.rs +++ b/proto-common/src/formatter.rs @@ -1,15 +1,30 @@ -pub fn quote_string(input: impl AsRef<[u8]>, should_escape: impl Fn(u8) -> bool) -> Vec { - let input = input.as_ref(); - let mut ret = Vec::with_capacity(input.len() + 2); +/// Adds quotes around a byte-string according to a quote function. +/// +/// # Example +/// +/// ``` +/// # use panorama_proto_common::quote_string; +/// let quote = quote_string(b'\x22', b'\\', |c| c == b'\x22' || c == b'\x27'); +/// assert_eq!(quote(b"hello \"' world"), b"\"hello \\\"\\' world\""); +/// ``` +pub fn quote_string(quote: u8, escape: u8, should_escape: F) -> impl Fn(B) -> Vec +where + B: AsRef<[u8]>, + F: Fn(u8) -> bool, +{ + move |input: B| { + let input = input.as_ref(); + let mut ret = Vec::with_capacity(input.len() + 2); - ret.push(b'\x22'); - for c in input { - if should_escape(*c) { - ret.push(b'\\'); + ret.push(quote); + for c in input { + if should_escape(*c) { + ret.push(escape); + } + ret.push(*c); } - ret.push(*c); - } - ret.push(b'\x22'); + ret.push(quote); - ret + ret + } } diff --git a/proto-common/src/lib.rs b/proto-common/src/lib.rs index cb9d160..060e752 100644 --- a/proto-common/src/lib.rs +++ b/proto-common/src/lib.rs @@ -5,6 +5,6 @@ mod bytes; mod formatter; mod parsers; -pub use crate::bytes::Bytes; +pub use crate::bytes::{Bytes, ShitCompare, ShitNeededForParsing}; pub use crate::formatter::quote_string; pub use crate::parsers::{byte, never, parse_u32, satisfy, skip, tagi, take, take_while1, VResult}; diff --git a/proto-common/src/parsers.rs b/proto-common/src/parsers.rs index a7c3bdc..6bc2c7a 100644 --- a/proto-common/src/parsers.rs +++ b/proto-common/src/parsers.rs @@ -6,7 +6,7 @@ use nom::{ use super::bytes::{ShitCompare, ShitNeededForParsing}; -/// A specific form of `nom::IResult` that uses `nom::error::VerboseError`. +/// A specific form of `IResult` that uses `VerboseError`. pub type VResult = IResult>; /// `sep_list!(t, d)` represents `t *(d t)` and automatically collapses it into @@ -20,7 +20,10 @@ pub type VResult = IResult>; macro_rules! sep_list { ($t:expr) => { map( - pair($t, many0(preceded(crate::proto::rfc2234::SP, $t))), + pair( + $t, + many0(preceded(panorama_proto_common::byte(b'\x20'), $t)), + ), |(hd, mut tl)| { tl.insert(0, hd); tl @@ -35,7 +38,10 @@ macro_rules! sep_list { }; (? $t:expr) => { map( - opt(pair($t, many0(preceded(crate::proto::rfc2234::SP, $t)))), + opt(pair( + $t, + many0(preceded(panorama_proto_common::byte(b'\x20'), $t)), + )), |opt| { opt.map(|(hd, mut tl)| { tl.insert(0, hd); @@ -56,13 +62,7 @@ macro_rules! sep_list { }; } -#[macro_export] -macro_rules! opt_nil { - ($t:expr) => { - alt((map($t, Some), map(crate::proto::rfc3501::nil, |_| None))) - }; -} - +/// Helper macro for wrapping a parser in parentheses. #[macro_export] macro_rules! paren { ($t:expr) => { diff --git a/smtp/src/client/mod.rs b/smtp/src/client/mod.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/smtp/src/client/mod.rs @@ -0,0 +1 @@ + diff --git a/smtp/src/lib.rs b/smtp/src/lib.rs index febacec..ead41dc 100644 --- a/smtp/src/lib.rs +++ b/smtp/src/lib.rs @@ -1 +1,2 @@ +mod client; pub mod proto;