From 68f63b5a3617d1b559224192ddfcb7911fa02412 Mon Sep 17 00:00:00 2001 From: Michael Zhang Date: Mon, 9 Aug 2021 17:06:49 -0500 Subject: [PATCH] move some common shit to common --- Cargo.lock | 11 ++++++++++ Cargo.toml | 1 + imap/Cargo.toml | 1 + imap/src/client/auth.rs | 6 ++---- imap/src/client/client.rs | 2 +- imap/src/client/codec.rs | 2 +- imap/src/client/inner.rs | 2 +- imap/src/lib.rs | 4 ++-- imap/src/proto/command.rs | 19 ++++++++++++++---- imap/src/proto/macros.rs | 20 ++----------------- imap/src/proto/mod.rs | 4 ---- imap/src/proto/response.rs | 3 +-- imap/src/proto/rfc2234.rs | 3 +-- imap/src/proto/rfc3501.rs | 5 +++-- imap/src/proto/test_rfc3501.rs | 3 ++- proto-common/Cargo.toml | 15 ++++++++++++++ {imap/src/proto => proto-common/src}/bytes.rs | 0 .../proto => proto-common/src}/formatter.rs | 6 ++---- proto-common/src/lib.rs | 10 ++++++++++ .../src/proto => proto-common/src}/parsers.rs | 11 +++++++--- smtp/src/lib.rs | 2 +- smtp/src/proto/rfc5321.rs | 4 ++++ 22 files changed, 84 insertions(+), 50 deletions(-) create mode 100644 proto-common/Cargo.toml rename {imap/src/proto => proto-common/src}/bytes.rs (100%) rename {imap/src/proto => proto-common/src}/formatter.rs (63%) create mode 100644 proto-common/src/lib.rs rename {imap/src/proto => proto-common/src}/parsers.rs (93%) create mode 100644 smtp/src/proto/rfc5321.rs diff --git a/Cargo.lock b/Cargo.lock index 8cf74c2..12d51f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -671,6 +671,7 @@ dependencies = [ "futures", "log", "nom", + "panorama-proto-common", "stderrlog", "tokio", "tokio-rustls", @@ -678,6 +679,16 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "panorama-proto-common" +version = "0.0.1" +dependencies = [ + "anyhow", + "bytes", + "format-bytes", + "nom", +] + [[package]] name = "panorama-smtp" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index e07f8bd..0ac3dd0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "daemon", "imap", "smtp", + "proto-common", ] [profile.release] diff --git a/imap/Cargo.toml b/imap/Cargo.toml index 5429097..d28f258 100644 --- a/imap/Cargo.toml +++ b/imap/Cargo.toml @@ -36,3 +36,4 @@ tokio = { version = "1.9.0", features = ["full"] } tokio-rustls = { version = "0.22.0", features = ["dangerous_configuration"] } tokio-util = { version = "0.6.7", features = ["codec"] } webpki-roots = "0.21.1" +panorama-proto-common = { path = "../proto-common" } \ No newline at end of file diff --git a/imap/src/client/auth.rs b/imap/src/client/auth.rs index 9022475..f820ce3 100644 --- a/imap/src/client/auth.rs +++ b/imap/src/client/auth.rs @@ -1,12 +1,10 @@ use tokio::io::{AsyncRead, AsyncWrite}; use anyhow::Result; +use panorama_proto_common::Bytes; use crate::client::inner::Inner; -use crate::proto::{ - bytes::Bytes, - command::{Command, CommandLogin}, -}; +use crate::proto::command::{Command, CommandLogin}; pub trait Client: AsyncRead + AsyncWrite + Unpin + Sync + Send + 'static {} impl Client for C where C: Send + Sync + Unpin + AsyncWrite + AsyncRead + 'static {} diff --git a/imap/src/client/client.rs b/imap/src/client/client.rs index 133db0c..b1d2d58 100644 --- a/imap/src/client/client.rs +++ b/imap/src/client/client.rs @@ -6,11 +6,11 @@ use futures::{ future::{self, FutureExt}, stream::{Stream, StreamExt}, }; +use panorama_proto_common::Bytes; use tokio::net::TcpStream; use tokio_rustls::client::TlsStream; use crate::proto::{ - bytes::Bytes, command::{ Command, CommandFetch, CommandList, CommandSearch, CommandSelect, FetchItems, SearchCriteria, diff --git a/imap/src/client/codec.rs b/imap/src/client/codec.rs index 5134d83..ff5a877 100644 --- a/imap/src/client/codec.rs +++ b/imap/src/client/codec.rs @@ -2,10 +2,10 @@ use std::io::{self}; use bytes::{BufMut, BytesMut}; use nom::Needed; +use panorama_proto_common::Bytes; use tokio_util::codec::{Decoder, Encoder}; use crate::proto::{ - bytes::Bytes, command::Command, convert_error::convert_error, response::{Response, Tag}, diff --git a/imap/src/client/inner.rs b/imap/src/client/inner.rs index 656349f..56fbffa 100644 --- a/imap/src/client/inner.rs +++ b/imap/src/client/inner.rs @@ -5,6 +5,7 @@ use futures::{ future::{self, FutureExt, TryFutureExt}, stream::StreamExt, }; +use panorama_proto_common::Bytes; use tokio::{ io::{split, AsyncRead, AsyncWrite, AsyncWriteExt, BufWriter, ReadHalf, WriteHalf}, sync::{mpsc, oneshot}, @@ -14,7 +15,6 @@ use tokio_rustls::client::TlsStream; use tokio_util::codec::FramedRead; use crate::proto::{ - bytes::Bytes, command::Command, response::{Condition, Response, Status, Tag}, rfc3501::capability as parse_capability, diff --git a/imap/src/lib.rs b/imap/src/lib.rs index d3c11f8..cd9b6a6 100644 --- a/imap/src/lib.rs +++ b/imap/src/lib.rs @@ -10,8 +10,8 @@ extern crate format_bytes; extern crate futures; #[macro_use] extern crate log; -// #[macro_use] -// extern crate bitflags; +#[macro_use] +extern crate panorama_proto_common; pub mod client; // pub mod events; diff --git a/imap/src/proto/command.rs b/imap/src/proto/command.rs index a54380e..b941830 100644 --- a/imap/src/proto/command.rs +++ b/imap/src/proto/command.rs @@ -1,8 +1,9 @@ use std::io::{self, Write}; use format_bytes::DisplayBytes; +use panorama_proto_common::{quote_string as q, Bytes}; -use crate::proto::{bytes::Bytes, formatter::quote_string as q}; +use super::rfc3501::is_quoted_specials as isq; #[derive(Clone, Debug)] pub enum Command { @@ -59,15 +60,25 @@ impl DisplayBytes for Command { // command-nonauth Command::Login(login) => { - write_bytes!(w, b"LOGIN {} {}", q(&login.userid), q(&login.password)) + write_bytes!( + w, + b"LOGIN {} {}", + q(&login.userid, isq), + q(&login.password, isq) + ) } Command::Starttls => write_bytes!(w, b"STARTTLS"), // command-auth Command::List(list) => { - write_bytes!(w, b"LIST {} {}", q(&list.reference), q(&list.mailbox)) + write_bytes!( + w, + b"LIST {} {}", + q(&list.reference, isq), + q(&list.mailbox, isq) + ) } - Command::Select(select) => write_bytes!(w, b"SELECT {}", q(&select.mailbox)), + Command::Select(select) => write_bytes!(w, b"SELECT {}", q(&select.mailbox, isq)), #[cfg(feature = "rfc2177")] Command::Idle => write_bytes!(w, b"IDLE"), diff --git a/imap/src/proto/macros.rs b/imap/src/proto/macros.rs index 0101d44..de289b5 100644 --- a/imap/src/proto/macros.rs +++ b/imap/src/proto/macros.rs @@ -1,25 +1,9 @@ macro_rules! rule { ($vis:vis $name:ident : $ret:ty => $expr:expr) => { $vis fn $name ( - i: crate::proto::bytes::Bytes - ) -> crate::proto::parsers::VResult { + i: panorama_proto_common::Bytes + ) -> panorama_proto_common::VResult { $expr(i) } }; } - -// macro_rules! pred { -// ($($expr:tt)*) => { |c: u8| _pred!(expr { $($expr)* })(c) }; -// } - -// macro_rules! _pred { -// (expr {}) => {}; -// (expr { $name:ident }) => { |b| $name(b) }; -// (expr { ! $($expr:tt)* }) => { |b| !_pred!(expr { $($expr)* })(b) }; -// (expr { ($($L:tt)*) && ($($R:tt)*) }) => { -// |b| { _pred!(expr { $($L)* })(b) && _pred!(expr { $($R)* })(b) } -// }; -// (expr { ($($L:tt)*) || ($($R:tt)*) }) => { -// |b| { _pred!(expr { $($L)* })(b) || _pred!(expr { $($R)* })(b) } -// }; -// } diff --git a/imap/src/proto/mod.rs b/imap/src/proto/mod.rs index 710ab52..490ba2e 100644 --- a/imap/src/proto/mod.rs +++ b/imap/src/proto/mod.rs @@ -5,11 +5,7 @@ // utils #[macro_use] mod macros; -pub mod bytes; -#[macro_use] -pub mod parsers; pub mod convert_error; -pub mod formatter; // data types pub mod command; diff --git a/imap/src/proto/response.rs b/imap/src/proto/response.rs index 8329b39..13b27f9 100644 --- a/imap/src/proto/response.rs +++ b/imap/src/proto/response.rs @@ -1,8 +1,7 @@ use std::ops::RangeInclusive; use chrono::{DateTime, FixedOffset}; - -use super::bytes::Bytes; +use panorama_proto_common::Bytes; pub type Atom = Bytes; diff --git a/imap/src/proto/rfc2234.rs b/imap/src/proto/rfc2234.rs index 689e7f5..38cc070 100644 --- a/imap/src/proto/rfc2234.rs +++ b/imap/src/proto/rfc2234.rs @@ -8,8 +8,7 @@ use nom::{ multi::many0, sequence::{pair, preceded}, }; - -use super::parsers::{byte, satisfy, skip}; +use panorama_proto_common::{byte, satisfy, skip}; rule!(pub ALPHA : u8 => satisfy(|c| (c >= b'a' && c <= b'z') || (c >= b'A' && c <= b'Z'))); diff --git a/imap/src/proto/rfc3501.rs b/imap/src/proto/rfc3501.rs index 0a39403..a7eb22b 100644 --- a/imap/src/proto/rfc3501.rs +++ b/imap/src/proto/rfc3501.rs @@ -9,9 +9,10 @@ use nom::{ multi::{many0, many1}, sequence::{delimited, pair, preceded, separated_pair, terminated, tuple}, }; +use panorama_proto_common::{ + byte, never, parse_u32, satisfy, tagi, take, take_while1, Bytes, VResult, +}; -use super::bytes::Bytes; -use super::parsers::{byte, never, parse_u32, satisfy, tagi, take, take_while1, VResult}; use super::response::{ Address, Atom, Capability, Condition, Envelope, Flag, Mailbox, MailboxData, MailboxList, MailboxListFlag, MessageAttribute, Response, ResponseCode, ResponseText, Status, Tag, diff --git a/imap/src/proto/test_rfc3501.rs b/imap/src/proto/test_rfc3501.rs index 5dd26bd..318a407 100644 --- a/imap/src/proto/test_rfc3501.rs +++ b/imap/src/proto/test_rfc3501.rs @@ -1,4 +1,5 @@ -use super::bytes::Bytes; +use panorama_proto_common::Bytes; + use super::rfc3501::*; #[test] diff --git a/proto-common/Cargo.toml b/proto-common/Cargo.toml new file mode 100644 index 0000000..9742cc0 --- /dev/null +++ b/proto-common/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "panorama-proto-common" +description = "Common code between protocol implementations" +version = "0.0.1" +edition = "2018" +authors = ["Michael Zhang "] +license = "GPL-3.0-or-later" +repository = "https://git.mzhang.io/michael/panorama" +workspace = ".." + +[dependencies] +anyhow = "1.0.42" +bytes = "1.0.1" +format-bytes = "0.2.2" +nom = "6.2.1" diff --git a/imap/src/proto/bytes.rs b/proto-common/src/bytes.rs similarity index 100% rename from imap/src/proto/bytes.rs rename to proto-common/src/bytes.rs diff --git a/imap/src/proto/formatter.rs b/proto-common/src/formatter.rs similarity index 63% rename from imap/src/proto/formatter.rs rename to proto-common/src/formatter.rs index 407824b..51f302c 100644 --- a/imap/src/proto/formatter.rs +++ b/proto-common/src/formatter.rs @@ -1,12 +1,10 @@ -use super::rfc3501::is_quoted_specials; - -pub fn quote_string(input: impl AsRef<[u8]>) -> Vec { +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); ret.push(b'\x22'); for c in input { - if is_quoted_specials(*c) { + if should_escape(*c) { ret.push(b'\\'); } ret.push(*c); diff --git a/proto-common/src/lib.rs b/proto-common/src/lib.rs new file mode 100644 index 0000000..cb9d160 --- /dev/null +++ b/proto-common/src/lib.rs @@ -0,0 +1,10 @@ +#[macro_use] +extern crate anyhow; + +mod bytes; +mod formatter; +mod parsers; + +pub use crate::bytes::Bytes; +pub use crate::formatter::quote_string; +pub use crate::parsers::{byte, never, parse_u32, satisfy, skip, tagi, take, take_while1, VResult}; diff --git a/imap/src/proto/parsers.rs b/proto-common/src/parsers.rs similarity index 93% rename from imap/src/proto/parsers.rs rename to proto-common/src/parsers.rs index 5456b8f..a7c3bdc 100644 --- a/imap/src/proto/parsers.rs +++ b/proto-common/src/parsers.rs @@ -5,8 +5,8 @@ use nom::{ }; use super::bytes::{ShitCompare, ShitNeededForParsing}; -use super::rfc2234::is_digit; +/// A specific form of `nom::IResult` that uses `nom::error::VerboseError`. pub type VResult = IResult>; /// `sep_list!(t, d)` represents `t *(d t)` and automatically collapses it into @@ -16,6 +16,7 @@ pub type VResult = IResult>; /// vec. /// /// If `d` is not passed then it defaults to `SP`. +#[macro_export] macro_rules! sep_list { ($t:expr) => { map( @@ -55,27 +56,31 @@ macro_rules! sep_list { }; } +#[macro_export] macro_rules! opt_nil { ($t:expr) => { alt((map($t, Some), map(crate::proto::rfc3501::nil, |_| None))) }; } +#[macro_export] macro_rules! paren { ($t:expr) => { delimited(byte(b'('), $t, byte(b')')) }; } +/// Parse from a [u8] into a u32 without first decoding it to UTF-8. pub fn parse_u32(s: impl AsRef<[u8]>) -> Result { let mut total = 0u32; let s = s.as_ref(); for digit in s.iter() { + let digit = *digit; total *= 10; - if !is_digit(*digit) { + if !(digit >= b'0' && digit <= b'9') { bail!("invalid digit {}", digit) } - total += (*digit - b'\x30') as u32; + total += (digit - b'\x30') as u32; } Ok(total) } diff --git a/smtp/src/lib.rs b/smtp/src/lib.rs index 3e1772e..febacec 100644 --- a/smtp/src/lib.rs +++ b/smtp/src/lib.rs @@ -1 +1 @@ -pub mod proto; \ No newline at end of file +pub mod proto; diff --git a/smtp/src/proto/rfc5321.rs b/smtp/src/proto/rfc5321.rs new file mode 100644 index 0000000..77d3700 --- /dev/null +++ b/smtp/src/proto/rfc5321.rs @@ -0,0 +1,4 @@ +//! SMTP Specification +//! --- +//! +//! Grammar from \ No newline at end of file