move some common shit to common

This commit is contained in:
Michael Zhang 2021-08-09 17:06:49 -05:00
parent 7945051eac
commit 68f63b5a36
Signed by: michael
GPG key ID: BDA47A31A3C8EE6B
22 changed files with 84 additions and 50 deletions

11
Cargo.lock generated
View file

@ -671,6 +671,7 @@ dependencies = [
"futures", "futures",
"log", "log",
"nom", "nom",
"panorama-proto-common",
"stderrlog", "stderrlog",
"tokio", "tokio",
"tokio-rustls", "tokio-rustls",
@ -678,6 +679,16 @@ dependencies = [
"webpki-roots", "webpki-roots",
] ]
[[package]]
name = "panorama-proto-common"
version = "0.0.1"
dependencies = [
"anyhow",
"bytes",
"format-bytes",
"nom",
]
[[package]] [[package]]
name = "panorama-smtp" name = "panorama-smtp"
version = "0.0.1" version = "0.0.1"

View file

@ -3,6 +3,7 @@ members = [
"daemon", "daemon",
"imap", "imap",
"smtp", "smtp",
"proto-common",
] ]
[profile.release] [profile.release]

View file

@ -36,3 +36,4 @@ tokio = { version = "1.9.0", features = ["full"] }
tokio-rustls = { version = "0.22.0", features = ["dangerous_configuration"] } tokio-rustls = { version = "0.22.0", features = ["dangerous_configuration"] }
tokio-util = { version = "0.6.7", features = ["codec"] } tokio-util = { version = "0.6.7", features = ["codec"] }
webpki-roots = "0.21.1" webpki-roots = "0.21.1"
panorama-proto-common = { path = "../proto-common" }

View file

@ -1,12 +1,10 @@
use tokio::io::{AsyncRead, AsyncWrite}; use tokio::io::{AsyncRead, AsyncWrite};
use anyhow::Result; use anyhow::Result;
use panorama_proto_common::Bytes;
use crate::client::inner::Inner; use crate::client::inner::Inner;
use crate::proto::{ use crate::proto::command::{Command, CommandLogin};
bytes::Bytes,
command::{Command, CommandLogin},
};
pub trait Client: AsyncRead + AsyncWrite + Unpin + Sync + Send + 'static {} pub trait Client: AsyncRead + AsyncWrite + Unpin + Sync + Send + 'static {}
impl<C> Client for C where C: Send + Sync + Unpin + AsyncWrite + AsyncRead + 'static {} impl<C> Client for C where C: Send + Sync + Unpin + AsyncWrite + AsyncRead + 'static {}

View file

@ -6,11 +6,11 @@ use futures::{
future::{self, FutureExt}, future::{self, FutureExt},
stream::{Stream, StreamExt}, stream::{Stream, StreamExt},
}; };
use panorama_proto_common::Bytes;
use tokio::net::TcpStream; use tokio::net::TcpStream;
use tokio_rustls::client::TlsStream; use tokio_rustls::client::TlsStream;
use crate::proto::{ use crate::proto::{
bytes::Bytes,
command::{ command::{
Command, CommandFetch, CommandList, CommandSearch, CommandSelect, FetchItems, Command, CommandFetch, CommandList, CommandSearch, CommandSelect, FetchItems,
SearchCriteria, SearchCriteria,

View file

@ -2,10 +2,10 @@ use std::io::{self};
use bytes::{BufMut, BytesMut}; use bytes::{BufMut, BytesMut};
use nom::Needed; use nom::Needed;
use panorama_proto_common::Bytes;
use tokio_util::codec::{Decoder, Encoder}; use tokio_util::codec::{Decoder, Encoder};
use crate::proto::{ use crate::proto::{
bytes::Bytes,
command::Command, command::Command,
convert_error::convert_error, convert_error::convert_error,
response::{Response, Tag}, response::{Response, Tag},

View file

@ -5,6 +5,7 @@ use futures::{
future::{self, FutureExt, TryFutureExt}, future::{self, FutureExt, TryFutureExt},
stream::StreamExt, stream::StreamExt,
}; };
use panorama_proto_common::Bytes;
use tokio::{ use tokio::{
io::{split, AsyncRead, AsyncWrite, AsyncWriteExt, BufWriter, ReadHalf, WriteHalf}, io::{split, AsyncRead, AsyncWrite, AsyncWriteExt, BufWriter, ReadHalf, WriteHalf},
sync::{mpsc, oneshot}, sync::{mpsc, oneshot},
@ -14,7 +15,6 @@ use tokio_rustls::client::TlsStream;
use tokio_util::codec::FramedRead; use tokio_util::codec::FramedRead;
use crate::proto::{ use crate::proto::{
bytes::Bytes,
command::Command, command::Command,
response::{Condition, Response, Status, Tag}, response::{Condition, Response, Status, Tag},
rfc3501::capability as parse_capability, rfc3501::capability as parse_capability,

View file

@ -10,8 +10,8 @@ extern crate format_bytes;
extern crate futures; extern crate futures;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
// #[macro_use] #[macro_use]
// extern crate bitflags; extern crate panorama_proto_common;
pub mod client; pub mod client;
// pub mod events; // pub mod events;

View file

@ -1,8 +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 crate::proto::{bytes::Bytes, formatter::quote_string as q}; use super::rfc3501::is_quoted_specials as isq;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum Command { pub enum Command {
@ -59,15 +60,25 @@ impl DisplayBytes for Command {
// command-nonauth // command-nonauth
Command::Login(login) => { 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::Starttls => write_bytes!(w, b"STARTTLS"),
// command-auth // command-auth
Command::List(list) => { 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")] #[cfg(feature = "rfc2177")]
Command::Idle => write_bytes!(w, b"IDLE"), Command::Idle => write_bytes!(w, b"IDLE"),

View file

@ -1,25 +1,9 @@
macro_rules! rule { macro_rules! rule {
($vis:vis $name:ident : $ret:ty => $expr:expr) => { ($vis:vis $name:ident : $ret:ty => $expr:expr) => {
$vis fn $name ( $vis fn $name (
i: crate::proto::bytes::Bytes i: panorama_proto_common::Bytes
) -> crate::proto::parsers::VResult<crate::proto::bytes::Bytes, $ret> { ) -> panorama_proto_common::VResult<panorama_proto_common::Bytes, $ret> {
$expr(i) $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) }
// };
// }

View file

@ -5,11 +5,7 @@
// utils // utils
#[macro_use] #[macro_use]
mod macros; mod macros;
pub mod bytes;
#[macro_use]
pub mod parsers;
pub mod convert_error; pub mod convert_error;
pub mod formatter;
// data types // data types
pub mod command; pub mod command;

View file

@ -1,8 +1,7 @@
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use chrono::{DateTime, FixedOffset}; use chrono::{DateTime, FixedOffset};
use panorama_proto_common::Bytes;
use super::bytes::Bytes;
pub type Atom = Bytes; pub type Atom = Bytes;

View file

@ -8,8 +8,7 @@ use nom::{
multi::many0, multi::many0,
sequence::{pair, preceded}, sequence::{pair, preceded},
}; };
use panorama_proto_common::{byte, satisfy, skip};
use super::parsers::{byte, satisfy, skip};
rule!(pub ALPHA : u8 => satisfy(|c| (c >= b'a' && c <= b'z') || (c >= b'A' && c <= b'Z'))); rule!(pub ALPHA : u8 => satisfy(|c| (c >= b'a' && c <= b'z') || (c >= b'A' && c <= b'Z')));

View file

@ -9,9 +9,10 @@ use nom::{
multi::{many0, many1}, multi::{many0, many1},
sequence::{delimited, pair, preceded, separated_pair, terminated, tuple}, 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::{ use super::response::{
Address, Atom, Capability, Condition, Envelope, Flag, Mailbox, MailboxData, MailboxList, Address, Atom, Capability, Condition, Envelope, Flag, Mailbox, MailboxData, MailboxList,
MailboxListFlag, MessageAttribute, Response, ResponseCode, ResponseText, Status, Tag, MailboxListFlag, MessageAttribute, Response, ResponseCode, ResponseText, Status, Tag,

View file

@ -1,4 +1,5 @@
use super::bytes::Bytes; use panorama_proto_common::Bytes;
use super::rfc3501::*; use super::rfc3501::*;
#[test] #[test]

15
proto-common/Cargo.toml Normal file
View file

@ -0,0 +1,15 @@
[package]
name = "panorama-proto-common"
description = "Common code between protocol implementations"
version = "0.0.1"
edition = "2018"
authors = ["Michael Zhang <mail@mzhang.io>"]
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"

View file

@ -1,12 +1,10 @@
use super::rfc3501::is_quoted_specials; pub fn quote_string(input: impl AsRef<[u8]>, should_escape: impl Fn(u8) -> bool) -> Vec<u8> {
pub fn quote_string(input: impl AsRef<[u8]>) -> Vec<u8> {
let input = input.as_ref(); let input = input.as_ref();
let mut ret = Vec::with_capacity(input.len() + 2); let mut ret = Vec::with_capacity(input.len() + 2);
ret.push(b'\x22'); ret.push(b'\x22');
for c in input { for c in input {
if is_quoted_specials(*c) { if should_escape(*c) {
ret.push(b'\\'); ret.push(b'\\');
} }
ret.push(*c); ret.push(*c);

10
proto-common/src/lib.rs Normal file
View file

@ -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};

View file

@ -5,8 +5,8 @@ use nom::{
}; };
use super::bytes::{ShitCompare, ShitNeededForParsing}; use super::bytes::{ShitCompare, ShitNeededForParsing};
use super::rfc2234::is_digit;
/// A specific form of `nom::IResult` that uses `nom::error::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
@ -16,6 +16,7 @@ pub type VResult<I, O> = IResult<I, O, VerboseError<I>>;
/// vec. /// vec.
/// ///
/// If `d` is not passed then it defaults to `SP`. /// If `d` is not passed then it defaults to `SP`.
#[macro_export]
macro_rules! sep_list { macro_rules! sep_list {
($t:expr) => { ($t:expr) => {
map( map(
@ -55,27 +56,31 @@ macro_rules! sep_list {
}; };
} }
#[macro_export]
macro_rules! opt_nil { macro_rules! opt_nil {
($t:expr) => { ($t:expr) => {
alt((map($t, Some), map(crate::proto::rfc3501::nil, |_| None))) alt((map($t, Some), map(crate::proto::rfc3501::nil, |_| None)))
}; };
} }
#[macro_export]
macro_rules! paren { macro_rules! paren {
($t:expr) => { ($t:expr) => {
delimited(byte(b'('), $t, byte(b')')) 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<u32> { pub fn parse_u32(s: impl AsRef<[u8]>) -> Result<u32> {
let mut total = 0u32; let mut total = 0u32;
let s = s.as_ref(); let s = s.as_ref();
for digit in s.iter() { for digit in s.iter() {
let digit = *digit;
total *= 10; total *= 10;
if !is_digit(*digit) { if !(digit >= b'0' && digit <= b'9') {
bail!("invalid digit {}", digit) bail!("invalid digit {}", digit)
} }
total += (*digit - b'\x30') as u32; total += (digit - b'\x30') as u32;
} }
Ok(total) Ok(total)
} }

View file

@ -0,0 +1,4 @@
//! SMTP Specification
//! ---
//!
//! Grammar from <https://datatracker.ietf.org/doc/html/rfc5321>