move some common shit to common
This commit is contained in:
parent
7945051eac
commit
68f63b5a36
22 changed files with 84 additions and 50 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -3,6 +3,7 @@ members = [
|
|||
"daemon",
|
||||
"imap",
|
||||
"smtp",
|
||||
"proto-common",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
|
|
@ -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" }
|
|
@ -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<C> Client for C where C: Send + Sync + Unpin + AsyncWrite + AsyncRead + 'static {}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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<crate::proto::bytes::Bytes, $ret> {
|
||||
i: panorama_proto_common::Bytes
|
||||
) -> panorama_proto_common::VResult<panorama_proto_common::Bytes, $ret> {
|
||||
$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) }
|
||||
// };
|
||||
// }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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')));
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use super::bytes::Bytes;
|
||||
use panorama_proto_common::Bytes;
|
||||
|
||||
use super::rfc3501::*;
|
||||
|
||||
#[test]
|
||||
|
|
15
proto-common/Cargo.toml
Normal file
15
proto-common/Cargo.toml
Normal 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"
|
|
@ -1,12 +1,10 @@
|
|||
use super::rfc3501::is_quoted_specials;
|
||||
|
||||
pub fn quote_string(input: impl AsRef<[u8]>) -> Vec<u8> {
|
||||
pub fn quote_string(input: impl AsRef<[u8]>, should_escape: impl Fn(u8) -> bool) -> Vec<u8> {
|
||||
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);
|
10
proto-common/src/lib.rs
Normal file
10
proto-common/src/lib.rs
Normal 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};
|
|
@ -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<I, O> = IResult<I, O, VerboseError<I>>;
|
||||
|
||||
/// `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.
|
||||
///
|
||||
/// 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<u32> {
|
||||
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)
|
||||
}
|
4
smtp/src/proto/rfc5321.rs
Normal file
4
smtp/src/proto/rfc5321.rs
Normal file
|
@ -0,0 +1,4 @@
|
|||
//! SMTP Specification
|
||||
//! ---
|
||||
//!
|
||||
//! Grammar from <https://datatracker.ietf.org/doc/html/rfc5321>
|
Loading…
Reference in a new issue