diff --git a/imap/src/proto/response.rs b/imap/src/proto/response.rs index d3f6e91..8d4fb62 100644 --- a/imap/src/proto/response.rs +++ b/imap/src/proto/response.rs @@ -23,11 +23,12 @@ pub enum Status { } pub enum ResponseCode<'a> { + Alert, Capabilities(Vec>), } pub enum Capability<'a> { Imap4rev1, - Auth(Cow<'a, str>), - Atom(Cow<'a, str>), + Auth(Cow<'a, [u8]>), + Atom(Cow<'a, [u8]>), } diff --git a/imap/src/proto/rfc2234.rs b/imap/src/proto/rfc2234.rs index ce30f56..529be11 100644 --- a/imap/src/proto/rfc2234.rs +++ b/imap/src/proto/rfc2234.rs @@ -11,7 +11,8 @@ rule!(pub BIT : u8 => satisfy(|c| c == b'0' || c == b'1')); pub fn is_char(c: u8) -> bool { c != b'\0' } rule!(pub CHAR : u8 => satisfy(is_char)); -rule!(pub CR : u8 => byte(b'\x0d')); +pub fn is_cr(c: u8) -> bool { c == b'\x0d' } +rule!(pub CR : u8 => satisfy(is_cr)); rule!(pub CRLF : (u8, u8) => pair(CR, LF)); @@ -27,7 +28,8 @@ rule!(pub HEXDIG : u8 => alt((DIGIT, satisfy(|c| c >= b'A' && c <= b'F')))); rule!(pub HTAB : u8 => byte(b'\x09')); -rule!(pub LF : u8 => byte(b'\x0a')); +pub fn is_lf(c: u8) -> bool { c == b'\x0a' } +rule!(pub LF : u8 => satisfy(is_lf)); rule!(pub LWSP : () => skip(many0(alt((skip(WSP), skip(pair(CRLF, WSP))))))); diff --git a/imap/src/proto/rfc3501.rs b/imap/src/proto/rfc3501.rs index 5c7767c..842071b 100644 --- a/imap/src/proto/rfc3501.rs +++ b/imap/src/proto/rfc3501.rs @@ -1,17 +1,20 @@ //! Grammar from https://datatracker.ietf.org/doc/html/rfc3501#section-9 +use std::borrow::Cow; + use nom::{ branch::alt, - bytes::streaming::{tag_no_case, take}, + bytes::streaming::{tag_no_case, take_while1, take}, character::streaming::char, combinator::{map, map_res}, multi::{many0, many1}, - sequence::{delimited, terminated}, + sequence::{delimited, pair, preceded, separated_pair, terminated}, IResult, }; use super::parsers::satisfy; -use super::rfc2234::{is_char, is_ctl, is_dquote, is_sp, CRLF, DIGIT, DQUOTE}; +use super::response::{Capability, ResponseCode}; +use super::rfc2234::{is_char, is_cr, is_ctl, is_dquote, is_lf, is_sp, CRLF, DIGIT, DQUOTE, SP}; rule!(pub astring : Vec => alt((many1(ASTRING_CHAR), string))); @@ -37,6 +40,21 @@ pub fn is_atom_specials(c: u8) -> bool { } rule!(pub atom_specials : u8 => satisfy(is_atom_specials)); +rule!(pub auth_type : Vec => atom); + +rule!(pub capability : Capability => alt(( + map(preceded(tag_no_case("AUTH="), auth_type), |s| Capability::Auth(Cow::from(s))), + map(atom, |s| Capability::Atom(Cow::from(s))), +))); + +rule!(pub capability_data : Vec => preceded(tag_no_case("CAPABILITY"), { + map(separated_pair( + many0(preceded(SP, capability)), + pair(SP, tag_no_case("IMAP4rev1")), + many0(preceded(SP, capability)) + ), |(mut a, b)| { a.extend(b); a }) +})); + pub fn is_list_wildcards(c: u8) -> bool { c == b'%' || c == b'*' } rule!(pub list_wildcards : u8 => satisfy(is_list_wildcards)); @@ -77,6 +95,14 @@ rule!(pub quoted_specials : u8 => satisfy(is_quoted_specials)); pub fn is_resp_specials(c: u8) -> bool { c == b']' } rule!(pub resp_specials : u8 => satisfy(is_resp_specials)); +rule!(pub resp_text_code : ResponseCode => alt(( + map(tag_no_case("ALERT"), |_| ResponseCode::Alert), + map(capability_data, ResponseCode::Capabilities), +))); + rule!(pub string : Vec => alt((quoted, literal))); -pub fn TEXT_CHAR(i: &[u8]) -> IResult<&[u8], u8> { todo!() } +rule!(pub text : &[u8] => take_while1(is_text_char)); + +pub fn is_text_char(c: u8) -> bool { is_char(c) && !is_cr(c) && !is_lf(c) } +rule!(pub TEXT_CHAR : u8 => satisfy(is_text_char));