wow testing
This commit is contained in:
parent
0405b478eb
commit
60e0bb3bbb
12 changed files with 90 additions and 39 deletions
3
Justfile
3
Justfile
|
@ -1,2 +1,5 @@
|
||||||
fmt:
|
fmt:
|
||||||
cargo +nightly fmt --all
|
cargo +nightly fmt --all
|
||||||
|
|
||||||
|
doc:
|
||||||
|
cargo doc --workspace --no-deps
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Configurable cert verifier for rustls, can disable hostname verification,
|
//! Configurable cert verifier for rustls, can disable hostname verification,
|
||||||
//! etc.
|
//! etc.
|
||||||
//!
|
//!
|
||||||
//! Based closely on https://github.com/rustls/rustls/blob/v/0.19.0/rustls/src/verify.rs#L253
|
//! Based closely on <https://github.com/rustls/rustls/blob/v/0.19.0/rustls/src/verify.rs#L253>
|
||||||
|
|
||||||
use tokio_rustls::{
|
use tokio_rustls::{
|
||||||
rustls::{
|
rustls::{
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::io::{self, Write};
|
use std::io::{self, Write};
|
||||||
|
|
||||||
use format_bytes::DisplayBytes;
|
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)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
|
@ -52,6 +52,7 @@ pub enum Command {
|
||||||
|
|
||||||
impl DisplayBytes for Command {
|
impl DisplayBytes for Command {
|
||||||
fn display_bytes(&self, w: &mut dyn Write) -> io::Result<()> {
|
fn display_bytes(&self, w: &mut dyn Write) -> io::Result<()> {
|
||||||
|
let quote = quote_string(b'\x22', b'\\', is_quoted_specials);
|
||||||
match self {
|
match self {
|
||||||
// command-any
|
// command-any
|
||||||
Command::Capability => write_bytes!(w, b"CAPABILITY"),
|
Command::Capability => write_bytes!(w, b"CAPABILITY"),
|
||||||
|
@ -63,8 +64,8 @@ impl DisplayBytes for Command {
|
||||||
write_bytes!(
|
write_bytes!(
|
||||||
w,
|
w,
|
||||||
b"LOGIN {} {}",
|
b"LOGIN {} {}",
|
||||||
q(&login.userid, isq),
|
quote(&login.userid),
|
||||||
q(&login.password, isq)
|
quote(&login.password)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Command::Starttls => write_bytes!(w, b"STARTTLS"),
|
Command::Starttls => write_bytes!(w, b"STARTTLS"),
|
||||||
|
@ -74,11 +75,11 @@ impl DisplayBytes for Command {
|
||||||
write_bytes!(
|
write_bytes!(
|
||||||
w,
|
w,
|
||||||
b"LIST {} {}",
|
b"LIST {} {}",
|
||||||
q(&list.reference, isq),
|
quote(&list.reference),
|
||||||
q(&list.mailbox, isq)
|
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")]
|
#[cfg(feature = "rfc2177")]
|
||||||
Command::Idle => write_bytes!(w, b"IDLE"),
|
Command::Idle => write_bytes!(w, b"IDLE"),
|
||||||
|
|
|
@ -159,7 +159,7 @@ pub struct MailboxList {
|
||||||
pub mailbox: Mailbox,
|
pub mailbox: Mailbox,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum MailboxListFlag {
|
pub enum MailboxListFlag {
|
||||||
NoInferiors,
|
NoInferiors,
|
||||||
NoSelect,
|
NoSelect,
|
||||||
|
|
|
@ -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};
|
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((
|
rule!(pub address : Address => map(paren!(tuple((
|
||||||
terminated(addr_name, SP),
|
terminated(addr_name, SP),
|
||||||
terminated(addr_adl, SP),
|
terminated(addr_adl, SP),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use panorama_proto_common::Bytes;
|
use panorama_proto_common::Bytes;
|
||||||
|
|
||||||
|
use super::response::*;
|
||||||
use super::rfc3501::*;
|
use super::rfc3501::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -15,7 +16,20 @@ fn test_literal() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_list() {
|
fn test_list() {
|
||||||
let _ = response(Bytes::from(
|
assert!(matches!(
|
||||||
b"* LIST (\\HasChildren \\UnMarked \\Trash) \".\" Trash\r\n",
|
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"[..]
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,25 @@ use nom::{
|
||||||
CompareResult, Err, IResult, InputLength, Needed,
|
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)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||||
pub struct Bytes(bytes::Bytes);
|
pub struct Bytes(bytes::Bytes);
|
||||||
|
|
||||||
impl 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() }
|
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 {
|
impl DisplayBytes for Bytes {
|
||||||
|
@ -224,10 +237,6 @@ array_impls! {
|
||||||
50 51 52 53 54 55 56 57 58 59
|
50 51 52 53 54 55 56 57 58 59
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bytes {
|
|
||||||
pub fn inner(self) -> bytes::Bytes { self.0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Bytes {
|
impl Deref for Bytes {
|
||||||
type Target = [u8];
|
type Target = [u8];
|
||||||
fn deref(&self) -> &Self::Target { &*self.0 }
|
fn deref(&self) -> &Self::Target { &*self.0 }
|
||||||
|
|
|
@ -1,15 +1,30 @@
|
||||||
pub fn quote_string(input: impl AsRef<[u8]>, should_escape: impl Fn(u8) -> bool) -> Vec<u8> {
|
/// Adds quotes around a byte-string according to a quote function.
|
||||||
let input = input.as_ref();
|
///
|
||||||
let mut ret = Vec::with_capacity(input.len() + 2);
|
/// # 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<B, F>(quote: u8, escape: u8, should_escape: F) -> impl Fn(B) -> Vec<u8>
|
||||||
|
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');
|
ret.push(quote);
|
||||||
for c in input {
|
for c in input {
|
||||||
if should_escape(*c) {
|
if should_escape(*c) {
|
||||||
ret.push(b'\\');
|
ret.push(escape);
|
||||||
|
}
|
||||||
|
ret.push(*c);
|
||||||
}
|
}
|
||||||
ret.push(*c);
|
ret.push(quote);
|
||||||
}
|
|
||||||
ret.push(b'\x22');
|
|
||||||
|
|
||||||
ret
|
ret
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,6 @@ mod bytes;
|
||||||
mod formatter;
|
mod formatter;
|
||||||
mod parsers;
|
mod parsers;
|
||||||
|
|
||||||
pub use crate::bytes::Bytes;
|
pub use crate::bytes::{Bytes, ShitCompare, ShitNeededForParsing};
|
||||||
pub use crate::formatter::quote_string;
|
pub use crate::formatter::quote_string;
|
||||||
pub use crate::parsers::{byte, never, parse_u32, satisfy, skip, tagi, take, take_while1, VResult};
|
pub use crate::parsers::{byte, never, parse_u32, satisfy, skip, tagi, take, take_while1, VResult};
|
||||||
|
|
|
@ -6,7 +6,7 @@ use nom::{
|
||||||
|
|
||||||
use super::bytes::{ShitCompare, ShitNeededForParsing};
|
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<I, O> = IResult<I, O, VerboseError<I>>;
|
pub type VResult<I, O> = IResult<I, O, VerboseError<I>>;
|
||||||
|
|
||||||
/// `sep_list!(t, d)` represents `t *(d t)` and automatically collapses it into
|
/// `sep_list!(t, d)` represents `t *(d t)` and automatically collapses it into
|
||||||
|
@ -20,7 +20,10 @@ pub type VResult<I, O> = IResult<I, O, VerboseError<I>>;
|
||||||
macro_rules! sep_list {
|
macro_rules! sep_list {
|
||||||
($t:expr) => {
|
($t:expr) => {
|
||||||
map(
|
map(
|
||||||
pair($t, many0(preceded(crate::proto::rfc2234::SP, $t))),
|
pair(
|
||||||
|
$t,
|
||||||
|
many0(preceded(panorama_proto_common::byte(b'\x20'), $t)),
|
||||||
|
),
|
||||||
|(hd, mut tl)| {
|
|(hd, mut tl)| {
|
||||||
tl.insert(0, hd);
|
tl.insert(0, hd);
|
||||||
tl
|
tl
|
||||||
|
@ -35,7 +38,10 @@ macro_rules! sep_list {
|
||||||
};
|
};
|
||||||
(? $t:expr) => {
|
(? $t:expr) => {
|
||||||
map(
|
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| {
|
||||||
opt.map(|(hd, mut tl)| {
|
opt.map(|(hd, mut tl)| {
|
||||||
tl.insert(0, hd);
|
tl.insert(0, hd);
|
||||||
|
@ -56,13 +62,7 @@ macro_rules! sep_list {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
/// Helper macro for wrapping a parser in parentheses.
|
||||||
macro_rules! opt_nil {
|
|
||||||
($t:expr) => {
|
|
||||||
alt((map($t, Some), map(crate::proto::rfc3501::nil, |_| None)))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! paren {
|
macro_rules! paren {
|
||||||
($t:expr) => {
|
($t:expr) => {
|
||||||
|
|
1
smtp/src/client/mod.rs
Normal file
1
smtp/src/client/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
mod client;
|
||||||
pub mod proto;
|
pub mod proto;
|
||||||
|
|
Loading…
Reference in a new issue