Get rid of unnecessary dependency

This commit is contained in:
Dirkjan Ochtman 2022-11-21 14:46:53 -08:00
parent db7fd2300f
commit e7cf12b4a0
7 changed files with 110 additions and 97 deletions

View File

@ -9,5 +9,4 @@ edition = "2021"
[dependencies] [dependencies]
nom = "7" nom = "7"
abnf-core = "0.5"
serde = { version = "1.0", features = ["derive"], optional = true } serde = { version = "1.0", features = ["derive"], optional = true }

View File

@ -1,10 +1,9 @@
//! 4.1.3. Address Literals (RFC 5321) //! 4.1.3. Address Literals (RFC 5321)
use abnf_core::streaming::is_DIGIT;
use nom::{ use nom::{
branch::alt, branch::alt,
bytes::streaming::{tag, tag_no_case, take_while1, take_while_m_n}, bytes::streaming::{tag, tag_no_case, take_while1, take_while_m_n},
character::is_hex_digit, character::{is_digit, is_hex_digit},
combinator::{map_res, opt, recognize}, combinator::{map_res, opt, recognize},
multi::{count, many_m_n}, multi::{count, many_m_n},
sequence::{delimited, tuple}, sequence::{delimited, tuple},
@ -47,7 +46,7 @@ pub fn IPv4_address_literal(input: &[u8]) -> IResult<&[u8], &[u8]> {
/// ///
/// Snum = 1*3DIGIT /// Snum = 1*3DIGIT
pub fn Snum(input: &[u8]) -> IResult<&[u8], &[u8]> { pub fn Snum(input: &[u8]) -> IResult<&[u8], &[u8]> {
take_while_m_n(1, 3, is_DIGIT)(input) take_while_m_n(1, 3, is_digit)(input)
} }
/// IPv6-address-literal = "IPv6:" IPv6-addr /// IPv6-address-literal = "IPv6:" IPv6-addr

View File

@ -1,7 +1,7 @@
use abnf_core::streaming::{is_ALPHA, is_DIGIT, CRLF, SP};
use nom::{ use nom::{
branch::alt, branch::alt,
bytes::streaming::{tag, tag_no_case, take_while, take_while1, take_while_m_n}, bytes::streaming::{tag, tag_no_case, take_while, take_while1, take_while_m_n},
character::{is_alphabetic, is_digit},
combinator::{map, map_res, opt, recognize, value}, combinator::{map, map_res, opt, recognize, value},
multi::separated_list1, multi::separated_list1,
sequence::{delimited, preceded, tuple}, sequence::{delimited, preceded, tuple},
@ -22,18 +22,18 @@ pub fn command(input: &[u8]) -> IResult<&[u8], Command> {
))(input) ))(input)
} }
/// helo = "HELO" SP Domain CRLF /// helo = "HELO" tag(" ") Domain CRLF
pub fn helo(input: &[u8]) -> IResult<&[u8], Command> { pub fn helo(input: &[u8]) -> IResult<&[u8], Command> {
let mut parser = tuple(( let mut parser = tuple((
tag_no_case(b"HELO"), tag_no_case(b"HELO"),
SP, tag(" "),
alt(( alt((
map(Domain, |domain| DomainOrAddress::Domain(domain.into())), map(Domain, |domain| DomainOrAddress::Domain(domain.into())),
map(address_literal, |address| { map(address_literal, |address| {
DomainOrAddress::Address(address.into()) DomainOrAddress::Address(address.into())
}), }),
)), )),
CRLF, tag("\r\n"),
)); ));
let (remaining, (_, _, domain_or_address, _)) = parser(input)?; let (remaining, (_, _, domain_or_address, _)) = parser(input)?;
@ -41,18 +41,18 @@ pub fn helo(input: &[u8]) -> IResult<&[u8], Command> {
Ok((remaining, Command::Helo { domain_or_address })) Ok((remaining, Command::Helo { domain_or_address }))
} }
/// ehlo = "EHLO" SP ( Domain / address-literal ) CRLF /// ehlo = "EHLO" tag(" ") ( Domain / address-literal ) CRLF
pub fn ehlo(input: &[u8]) -> IResult<&[u8], Command> { pub fn ehlo(input: &[u8]) -> IResult<&[u8], Command> {
let mut parser = tuple(( let mut parser = tuple((
tag_no_case(b"EHLO"), tag_no_case(b"EHLO"),
SP, tag(" "),
alt(( alt((
map(Domain, |domain| DomainOrAddress::Domain(domain.into())), map(Domain, |domain| DomainOrAddress::Domain(domain.into())),
map(address_literal, |address| { map(address_literal, |address| {
DomainOrAddress::Address(address.into()) DomainOrAddress::Address(address.into())
}), }),
)), )),
CRLF, tag("\r\n"),
)); ));
let (remaining, (_, _, domain_or_address, _)) = parser(input)?; let (remaining, (_, _, domain_or_address, _)) = parser(input)?;
@ -60,14 +60,14 @@ pub fn ehlo(input: &[u8]) -> IResult<&[u8], Command> {
Ok((remaining, Command::Ehlo { domain_or_address })) Ok((remaining, Command::Ehlo { domain_or_address }))
} }
/// mail = "MAIL FROM:" Reverse-path [SP Mail-parameters] CRLF /// mail = "MAIL FROM:" Reverse-path [tag(" ") Mail-parameters] CRLF
pub fn mail(input: &[u8]) -> IResult<&[u8], Command> { pub fn mail(input: &[u8]) -> IResult<&[u8], Command> {
let mut parser = tuple(( let mut parser = tuple((
tag_no_case(b"MAIL FROM:"), tag_no_case(b"MAIL FROM:"),
opt(SP), // Out-of-Spec, but Outlook does it ... opt(tag(" ")), // Out-of-tag(" ")ec, but Outlook does it ...
Reverse_path, Reverse_path,
opt(preceded(SP, Mail_parameters)), opt(preceded(tag(" "), Mail_parameters)),
CRLF, tag("\r\n"),
)); ));
let (remaining, (_, _, data, maybe_params, _)) = parser(input)?; let (remaining, (_, _, data, maybe_params, _)) = parser(input)?;
@ -81,9 +81,9 @@ pub fn mail(input: &[u8]) -> IResult<&[u8], Command> {
)) ))
} }
/// Mail-parameters = esmtp-param *(SP esmtp-param) /// Mail-parameters = esmtp-param *(tag(" ") esmtp-param)
pub fn Mail_parameters(input: &[u8]) -> IResult<&[u8], Vec<Parameter>> { pub fn Mail_parameters(input: &[u8]) -> IResult<&[u8], Vec<Parameter>> {
separated_list1(SP, esmtp_param)(input) separated_list1(tag(" "), esmtp_param)(input)
} }
/// esmtp-param = esmtp-keyword ["=" esmtp-value] /// esmtp-param = esmtp-keyword ["=" esmtp-value]
@ -105,8 +105,8 @@ pub fn esmtp_param(input: &[u8]) -> IResult<&[u8], Parameter> {
/// esmtp-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-") /// esmtp-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-")
pub fn esmtp_keyword(input: &[u8]) -> IResult<&[u8], &str> { pub fn esmtp_keyword(input: &[u8]) -> IResult<&[u8], &str> {
let parser = tuple(( let parser = tuple((
take_while_m_n(1, 1, |byte| is_ALPHA(byte) || is_DIGIT(byte)), take_while_m_n(1, 1, |byte| is_alphabetic(byte) || is_digit(byte)),
take_while(|byte| is_ALPHA(byte) || is_DIGIT(byte) || byte == b'-'), take_while(|byte| is_alphabetic(byte) || is_digit(byte) || byte == b'-'),
)); ));
let (remaining, parsed) = map_res(recognize(parser), std::str::from_utf8)(input)?; let (remaining, parsed) = map_res(recognize(parser), std::str::from_utf8)(input)?;
@ -114,7 +114,7 @@ pub fn esmtp_keyword(input: &[u8]) -> IResult<&[u8], &str> {
Ok((remaining, parsed)) Ok((remaining, parsed))
} }
/// Any CHAR excluding "=", SP, and control characters. /// Any CHAR excluding "=", tag(" "), and control characters.
/// If this string is an email address, i.e., a Mailbox, /// If this string is an email address, i.e., a Mailbox,
/// then the "xtext" syntax [32] SHOULD be used. /// then the "xtext" syntax [32] SHOULD be used.
/// ///
@ -127,7 +127,7 @@ pub fn esmtp_value(input: &[u8]) -> IResult<&[u8], &str> {
map_res(take_while1(is_value_character), std::str::from_utf8)(input) map_res(take_while1(is_value_character), std::str::from_utf8)(input)
} }
/// rcpt = "RCPT TO:" ( "<Postmaster@" Domain ">" / "<Postmaster>" / Forward-path ) [SP Rcpt-parameters] CRLF /// rcpt = "RCPT TO:" ( "<Postmaster@" Domain ">" / "<Postmaster>" / Forward-path ) [tag(" ") Rcpt-parameters] CRLF
/// ///
/// Note that, in a departure from the usual rules for /// Note that, in a departure from the usual rules for
/// local-parts, the "Postmaster" string shown above is /// local-parts, the "Postmaster" string shown above is
@ -135,7 +135,7 @@ pub fn esmtp_value(input: &[u8]) -> IResult<&[u8], &str> {
pub fn rcpt(input: &[u8]) -> IResult<&[u8], Command> { pub fn rcpt(input: &[u8]) -> IResult<&[u8], Command> {
let mut parser = tuple(( let mut parser = tuple((
tag_no_case(b"RCPT TO:"), tag_no_case(b"RCPT TO:"),
opt(SP), // Out-of-Spec, but Outlook does it ... opt(tag(" ")), // Out-of-tag(" ")ec, but Outlook does it ...
alt(( alt((
map_res( map_res(
recognize(tuple((tag_no_case("<Postmaster@"), Domain, tag(">")))), recognize(tuple((tag_no_case("<Postmaster@"), Domain, tag(">")))),
@ -144,8 +144,8 @@ pub fn rcpt(input: &[u8]) -> IResult<&[u8], Command> {
map_res(tag_no_case("<Postmaster>"), std::str::from_utf8), map_res(tag_no_case("<Postmaster>"), std::str::from_utf8),
Forward_path, Forward_path,
)), )),
opt(preceded(SP, Rcpt_parameters)), opt(preceded(tag(" "), Rcpt_parameters)),
CRLF, tag("\r\n"),
)); ));
let (remaining, (_, _, data, maybe_params, _)) = parser(input)?; let (remaining, (_, _, data, maybe_params, _)) = parser(input)?;
@ -159,24 +159,24 @@ pub fn rcpt(input: &[u8]) -> IResult<&[u8], Command> {
)) ))
} }
/// Rcpt-parameters = esmtp-param *(SP esmtp-param) /// Rcpt-parameters = esmtp-param *(tag(" ") esmtp-param)
pub fn Rcpt_parameters(input: &[u8]) -> IResult<&[u8], Vec<Parameter>> { pub fn Rcpt_parameters(input: &[u8]) -> IResult<&[u8], Vec<Parameter>> {
separated_list1(SP, esmtp_param)(input) separated_list1(tag(" "), esmtp_param)(input)
} }
/// data = "DATA" CRLF /// data = "DATA" CRLF
pub fn data(input: &[u8]) -> IResult<&[u8], Command> { pub fn data(input: &[u8]) -> IResult<&[u8], Command> {
value(Command::Data, tuple((tag_no_case(b"DATA"), CRLF)))(input) value(Command::Data, tuple((tag_no_case(b"DATA"), tag("\r\n"))))(input)
} }
/// rset = "RSET" CRLF /// rset = "RSET" CRLF
pub fn rset(input: &[u8]) -> IResult<&[u8], Command> { pub fn rset(input: &[u8]) -> IResult<&[u8], Command> {
value(Command::Rset, tuple((tag_no_case(b"RSET"), CRLF)))(input) value(Command::Rset, tuple((tag_no_case(b"RSET"), tag("\r\n"))))(input)
} }
/// vrfy = "VRFY" SP String CRLF /// vrfy = "VRFY" tag(" ") String CRLF
pub fn vrfy(input: &[u8]) -> IResult<&[u8], Command> { pub fn vrfy(input: &[u8]) -> IResult<&[u8], Command> {
let mut parser = tuple((tag_no_case(b"VRFY"), SP, String, CRLF)); let mut parser = tuple((tag_no_case(b"VRFY"), tag(" "), String, tag("\r\n")));
let (remaining, (_, _, data, _)) = parser(input)?; let (remaining, (_, _, data, _)) = parser(input)?;
@ -188,18 +188,22 @@ pub fn vrfy(input: &[u8]) -> IResult<&[u8], Command> {
)) ))
} }
/// expn = "EXPN" SP String CRLF /// expn = "EXPN" tag(" ") String CRLF
pub fn expn(input: &[u8]) -> IResult<&[u8], Command> { pub fn expn(input: &[u8]) -> IResult<&[u8], Command> {
let mut parser = tuple((tag_no_case(b"EXPN"), SP, String, CRLF)); let mut parser = tuple((tag_no_case(b"EXPN"), tag(" "), String, tag("\r\n")));
let (remaining, (_, _, data, _)) = parser(input)?; let (remaining, (_, _, data, _)) = parser(input)?;
Ok((remaining, Command::Expn { mailing_list: data })) Ok((remaining, Command::Expn { mailing_list: data }))
} }
/// help = "HELP" [ SP String ] CRLF /// help = "HELP" [ tag(" ") String ] CRLF
pub fn help(input: &[u8]) -> IResult<&[u8], Command> { pub fn help(input: &[u8]) -> IResult<&[u8], Command> {
let mut parser = tuple((tag_no_case(b"HELP"), opt(preceded(SP, String)), CRLF)); let mut parser = tuple((
tag_no_case(b"HELP"),
opt(preceded(tag(" "), String)),
tag("\r\n"),
));
let (remaining, (_, maybe_data, _)) = parser(input)?; let (remaining, (_, maybe_data, _)) = parser(input)?;
@ -211,9 +215,13 @@ pub fn help(input: &[u8]) -> IResult<&[u8], Command> {
)) ))
} }
/// noop = "NOOP" [ SP String ] CRLF /// noop = "NOOP" [ tag(" ") String ] CRLF
pub fn noop(input: &[u8]) -> IResult<&[u8], Command> { pub fn noop(input: &[u8]) -> IResult<&[u8], Command> {
let mut parser = tuple((tag_no_case(b"NOOP"), opt(preceded(SP, String)), CRLF)); let mut parser = tuple((
tag_no_case(b"NOOP"),
opt(preceded(tag(" "), String)),
tag("\r\n"),
));
let (remaining, (_, maybe_data, _)) = parser(input)?; let (remaining, (_, maybe_data, _)) = parser(input)?;
@ -227,11 +235,14 @@ pub fn noop(input: &[u8]) -> IResult<&[u8], Command> {
/// quit = "QUIT" CRLF /// quit = "QUIT" CRLF
pub fn quit(input: &[u8]) -> IResult<&[u8], Command> { pub fn quit(input: &[u8]) -> IResult<&[u8], Command> {
value(Command::Quit, tuple((tag_no_case(b"QUIT"), CRLF)))(input) value(Command::Quit, tuple((tag_no_case(b"QUIT"), tag("\r\n"))))(input)
} }
pub fn starttls(input: &[u8]) -> IResult<&[u8], Command> { pub fn starttls(input: &[u8]) -> IResult<&[u8], Command> {
value(Command::StartTLS, tuple((tag_no_case(b"STARTTLS"), CRLF)))(input) value(
Command::StartTLS,
tuple((tag_no_case(b"STARTTLS"), tag("\r\n"))),
)(input)
} }
/// https://interoperability.blob.core.windows.net/files/MS-XLOGIN/[MS-XLOGIN].pdf /// https://interoperability.blob.core.windows.net/files/MS-XLOGIN/[MS-XLOGIN].pdf
@ -239,19 +250,19 @@ pub fn starttls(input: &[u8]) -> IResult<&[u8], Command> {
/// username = 1*CHAR ; Base64-encoded username /// username = 1*CHAR ; Base64-encoded username
/// password = 1*CHAR ; Base64-encoded password /// password = 1*CHAR ; Base64-encoded password
/// ///
/// auth_login_command = "AUTH LOGIN" [SP username] CRLF /// auth_login_command = "AUTH LOGIN" [tag(" ") username] CRLF
/// ///
/// auth_login_username_challenge = "334 VXNlcm5hbWU6" CRLF /// auth_login_username_challenge = "334 VXNlcm5hbWU6" CRLF
/// auth_login_username_response = username CRLF /// auth_login_username_retag(" ")onse = username CRLF
/// auth_login_password_challenge = "334 UGFzc3dvcmQ6" CRLF /// auth_login_password_challenge = "334 UGFzc3dvcmQ6" CRLF
/// auth_login_password_response = password CRLF /// auth_login_password_retag(" ")onse = password CRLF
pub fn auth_login(input: &[u8]) -> IResult<&[u8], Command> { pub fn auth_login(input: &[u8]) -> IResult<&[u8], Command> {
let mut parser = tuple(( let mut parser = tuple((
tag_no_case(b"AUTH"), tag_no_case(b"AUTH"),
SP, tag(" "),
tag_no_case("LOGIN"), tag_no_case("LOGIN"),
opt(preceded(SP, base64)), opt(preceded(tag(" "), base64)),
CRLF, tag("\r\n"),
)); ));
let (remaining, (_, _, _, maybe_username_b64, _)) = parser(input)?; let (remaining, (_, _, _, maybe_username_b64, _)) = parser(input)?;
@ -265,10 +276,10 @@ pub fn auth_login(input: &[u8]) -> IResult<&[u8], Command> {
pub fn auth_plain(input: &[u8]) -> IResult<&[u8], Command> { pub fn auth_plain(input: &[u8]) -> IResult<&[u8], Command> {
let mut parser = tuple(( let mut parser = tuple((
tag_no_case(b"AUTH"), tag_no_case(b"AUTH"),
SP, tag(" "),
tag_no_case("PLAIN"), tag_no_case("PLAIN"),
opt(preceded(SP, base64)), opt(preceded(tag(" "), base64)),
CRLF, tag("\r\n"),
)); ));
let (remaining, (_, _, _, maybe_credentials_b64, _)) = parser(input)?; let (remaining, (_, _, _, maybe_credentials_b64, _)) = parser(input)?;

View File

@ -4,7 +4,6 @@
/// 3.2.1. Quoted characters /// 3.2.1. Quoted characters
pub mod quoted_characters { pub mod quoted_characters {
use abnf_core::streaming::{is_VCHAR, WSP};
use nom::{ use nom::{
branch::alt, branch::alt,
bytes::streaming::{tag, take_while_m_n}, bytes::streaming::{tag, take_while_m_n},
@ -20,7 +19,10 @@ pub mod quoted_characters {
let parser = alt(( let parser = alt((
recognize(tuple(( recognize(tuple((
tag(b"\\"), tag(b"\\"),
alt((take_while_m_n(1, 1, is_VCHAR), WSP)), alt((
take_while_m_n(1, 1, |byte| matches!(byte, 0x21..=0x7E)),
alt((tag(" "), tag("\t"))),
)),
))), ))),
obs_qp, obs_qp,
)); ));
@ -58,9 +60,9 @@ pub mod folding_ws_and_comment {
/// 3.2.3. Atom /// 3.2.3. Atom
pub mod atom { pub mod atom {
use abnf_core::streaming::{is_ALPHA, is_DIGIT};
use nom::{ use nom::{
bytes::streaming::{tag, take_while1}, bytes::streaming::{tag, take_while1},
character::{is_alphabetic, is_digit},
combinator::{opt, recognize}, combinator::{opt, recognize},
multi::many0, multi::many0,
sequence::tuple, sequence::tuple,
@ -86,7 +88,7 @@ pub mod atom {
pub fn is_atext(byte: u8) -> bool { pub fn is_atext(byte: u8) -> bool {
let allowed = b"!#$%&'*+-/=?^_`{|}~"; let allowed = b"!#$%&'*+-/=?^_`{|}~";
is_ALPHA(byte) || is_DIGIT(byte) || allowed.contains(&byte) is_alphabetic(byte) || is_digit(byte) || allowed.contains(&byte)
} }
/// atom = [CFWS] 1*atext [CFWS] /// atom = [CFWS] 1*atext [CFWS]
@ -133,10 +135,9 @@ pub mod atom {
/// 3.2.4. Quoted Strings /// 3.2.4. Quoted Strings
pub mod quoted_strings { pub mod quoted_strings {
use abnf_core::streaming::DQUOTE;
use nom::{ use nom::{
branch::alt, branch::alt,
bytes::streaming::take_while_m_n, bytes::streaming::{tag, take_while_m_n},
combinator::{opt, recognize}, combinator::{opt, recognize},
multi::many0, multi::many0,
sequence::tuple, sequence::tuple,
@ -173,10 +174,10 @@ pub mod quoted_strings {
pub fn quoted_string(input: &[u8]) -> IResult<&[u8], &[u8]> { pub fn quoted_string(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple(( let parser = tuple((
opt(CFWS), opt(CFWS),
DQUOTE, tag("\""),
many0(tuple((opt(FWS), qcontent))), many0(tuple((opt(FWS), qcontent))),
opt(FWS), opt(FWS),
DQUOTE, tag("\""),
opt(CFWS), opt(CFWS),
)); ));
@ -206,10 +207,10 @@ pub mod miscellaneous {
/// 3.3. Date and Time Specification /// 3.3. Date and Time Specification
pub mod datetime { pub mod datetime {
use abnf_core::streaming::is_DIGIT;
use nom::{ use nom::{
branch::alt, branch::alt,
bytes::streaming::{tag, tag_no_case, take_while_m_n}, bytes::streaming::{tag, tag_no_case, take_while_m_n},
character::is_digit,
combinator::{opt, recognize}, combinator::{opt, recognize},
sequence::tuple, sequence::tuple,
IResult, IResult,
@ -263,7 +264,7 @@ pub mod datetime {
// day = ([FWS] 1*2DIGIT FWS) / obs-day // day = ([FWS] 1*2DIGIT FWS) / obs-day
pub fn day(input: &[u8]) -> IResult<&[u8], &[u8]> { pub fn day(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((opt(FWS), take_while_m_n(1, 2, is_DIGIT), FWS)); let parser = tuple((opt(FWS), take_while_m_n(1, 2, is_digit), FWS));
let (remaining, parsed) = recognize(parser)(input)?; let (remaining, parsed) = recognize(parser)(input)?;
@ -294,7 +295,7 @@ pub mod datetime {
// year = (FWS 4*DIGIT FWS) / obs-year // year = (FWS 4*DIGIT FWS) / obs-year
pub fn year(input: &[u8]) -> IResult<&[u8], &[u8]> { pub fn year(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((FWS, take_while_m_n(4, 8, is_DIGIT), FWS)); // FIXME: 4*?! let parser = tuple((FWS, take_while_m_n(4, 8, is_digit), FWS)); // FIXME: 4*?!
let (remaining, parsed) = recognize(parser)(input)?; let (remaining, parsed) = recognize(parser)(input)?;
@ -323,7 +324,7 @@ pub mod datetime {
pub fn hour(input: &[u8]) -> IResult<&[u8], &[u8]> { pub fn hour(input: &[u8]) -> IResult<&[u8], &[u8]> {
// FIXME: obs- forms must not be used in SMTP. Never? // FIXME: obs- forms must not be used in SMTP. Never?
let parser = take_while_m_n(2, 2, is_DIGIT); let parser = take_while_m_n(2, 2, is_digit);
let (remaining, parsed) = recognize(parser)(input)?; let (remaining, parsed) = recognize(parser)(input)?;
@ -334,7 +335,7 @@ pub mod datetime {
pub fn minute(input: &[u8]) -> IResult<&[u8], &[u8]> { pub fn minute(input: &[u8]) -> IResult<&[u8], &[u8]> {
// FIXME: obs- forms must not be used in SMTP. Never? // FIXME: obs- forms must not be used in SMTP. Never?
let parser = take_while_m_n(2, 2, is_DIGIT); let parser = take_while_m_n(2, 2, is_digit);
let (remaining, parsed) = recognize(parser)(input)?; let (remaining, parsed) = recognize(parser)(input)?;
@ -345,7 +346,7 @@ pub mod datetime {
pub fn second(input: &[u8]) -> IResult<&[u8], &[u8]> { pub fn second(input: &[u8]) -> IResult<&[u8], &[u8]> {
// FIXME: obs- forms must not be used in SMTP. Never? // FIXME: obs- forms must not be used in SMTP. Never?
let parser = take_while_m_n(2, 2, is_DIGIT); let parser = take_while_m_n(2, 2, is_digit);
let (remaining, parsed) = recognize(parser)(input)?; let (remaining, parsed) = recognize(parser)(input)?;
@ -359,7 +360,7 @@ pub mod datetime {
let parser = tuple(( let parser = tuple((
FWS, FWS,
alt((tag(b"+"), tag(b"-"))), alt((tag(b"+"), tag(b"-"))),
take_while_m_n(4, 4, is_DIGIT), take_while_m_n(4, 4, is_digit),
)); ));
let (remaining, parsed) = recognize(parser)(input)?; let (remaining, parsed) = recognize(parser)(input)?;
@ -520,7 +521,6 @@ pub mod identification {
/// 4.1. Miscellaneous Obsolete Tokens /// 4.1. Miscellaneous Obsolete Tokens
pub mod obsolete { pub mod obsolete {
use abnf_core::streaming::{CR, LF};
use nom::{ use nom::{
branch::alt, branch::alt,
bytes::streaming::{tag, take_while_m_n}, bytes::streaming::{tag, take_while_m_n},
@ -557,8 +557,8 @@ pub mod obsolete {
alt(( alt((
take_while_m_n(1, 1, |x| x == 0x00), take_while_m_n(1, 1, |x| x == 0x00),
take_while_m_n(1, 1, is_obs_NO_WS_CTL), take_while_m_n(1, 1, is_obs_NO_WS_CTL),
LF, tag("\n"),
CR, tag("\r"),
)), )),
)); ));

View File

@ -2,11 +2,11 @@
use std::{borrow::Cow, str::from_utf8}; use std::{borrow::Cow, str::from_utf8};
use abnf_core::streaming::{is_ALPHA, is_DIGIT, DQUOTE};
use nom::{ use nom::{
branch::alt, branch::alt,
bytes::streaming::{tag, take_while, take_while1, take_while_m_n}, bytes::streaming::{tag, take_while, take_while1, take_while_m_n},
character::streaming::digit1, character::streaming::digit1,
character::{is_alphabetic, is_digit},
combinator::{map, map_res, opt, recognize}, combinator::{map, map_res, opt, recognize},
multi::{many0, separated_list1}, multi::{many0, separated_list1},
sequence::{delimited, tuple}, sequence::{delimited, tuple},
@ -37,7 +37,7 @@ pub fn base64(input: &[u8]) -> IResult<&[u8], &str> {
} }
fn is_base64_char(i: u8) -> bool { fn is_base64_char(i: u8) -> bool {
is_ALPHA(i) || is_DIGIT(i) || i == b'+' || i == b'/' is_alphabetic(i) || is_digit(i) || i == b'+' || i == b'/'
} }
pub fn number(input: &[u8]) -> IResult<&[u8], u32> { pub fn number(input: &[u8]) -> IResult<&[u8], u32> {
@ -63,9 +63,9 @@ pub fn Atom(input: &[u8]) -> IResult<&[u8], &str> {
pub fn Quoted_string(input: &[u8]) -> IResult<&[u8], Cow<'_, str>> { pub fn Quoted_string(input: &[u8]) -> IResult<&[u8], Cow<'_, str>> {
map( map(
delimited( delimited(
DQUOTE, tag("\""),
map_res(recognize(many0(QcontentSMTP)), std::str::from_utf8), map_res(recognize(many0(QcontentSMTP)), std::str::from_utf8),
DQUOTE, tag("\""),
), ),
unescape_quoted, unescape_quoted,
)(input) )(input)
@ -132,14 +132,14 @@ pub fn sub_domain(input: &[u8]) -> IResult<&[u8], &[u8]> {
/// Let-dig = ALPHA / DIGIT /// Let-dig = ALPHA / DIGIT
pub fn is_Let_dig(byte: u8) -> bool { pub fn is_Let_dig(byte: u8) -> bool {
is_ALPHA(byte) || is_DIGIT(byte) is_alphabetic(byte) || is_digit(byte)
} }
/// Ldh-str = *( ALPHA / DIGIT / "-" ) Let-dig /// Ldh-str = *( ALPHA / DIGIT / "-" ) Let-dig
pub fn Ldh_str(input: &[u8]) -> IResult<&[u8], &[u8]> { pub fn Ldh_str(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = many0(alt(( let parser = many0(alt((
take_while_m_n(1, 1, is_ALPHA), take_while_m_n(1, 1, is_alphabetic),
take_while_m_n(1, 1, is_DIGIT), take_while_m_n(1, 1, is_digit),
recognize(tuple((tag(b"-"), take_while_m_n(1, 1, is_Let_dig)))), recognize(tuple((tag(b"-"), take_while_m_n(1, 1, is_Let_dig)))),
))); )));

View File

@ -1,9 +1,9 @@
use std::str::FromStr; use std::str::FromStr;
use abnf_core::streaming::{is_ALPHA, is_DIGIT, CRLF, SP};
use nom::{ use nom::{
branch::alt, branch::alt,
bytes::streaming::{tag, tag_no_case, take_while, take_while1, take_while_m_n}, bytes::streaming::{tag, tag_no_case, take_while, take_while1, take_while_m_n},
character::{is_alphabetic, is_digit},
combinator::{map, map_res, opt, recognize, value}, combinator::{map, map_res, opt, recognize, value},
multi::{many0, separated_list0}, multi::{many0, separated_list0},
sequence::{delimited, preceded, tuple}, sequence::{delimited, preceded, tuple},
@ -25,8 +25,8 @@ pub fn Greeting(input: &[u8]) -> IResult<&[u8], Response> {
tuple(( tuple((
tag(b"220 "), tag(b"220 "),
alt((Domain, address_literal)), alt((Domain, address_literal)),
opt(preceded(SP, textstring)), opt(preceded(tag(" "), textstring)),
CRLF, tag("\r\n"),
)), )),
|(_, domain, maybe_text, _)| Response::Greeting { |(_, domain, maybe_text, _)| Response::Greeting {
domain: domain.to_owned(), domain: domain.to_owned(),
@ -39,12 +39,12 @@ pub fn Greeting(input: &[u8]) -> IResult<&[u8], Response> {
tuple(( tuple((
tag(b"220-"), tag(b"220-"),
alt((Domain, address_literal)), alt((Domain, address_literal)),
opt(preceded(SP, textstring)), opt(preceded(tag(" "), textstring)),
CRLF, tag("\r\n"),
many0(delimited(tag(b"220-"), opt(textstring), CRLF)), many0(delimited(tag(b"220-"), opt(textstring), tag("\r\n"))),
tag(b"220"), tag(b"220"),
opt(preceded(SP, textstring)), opt(preceded(tag(" "), textstring)),
CRLF, tag("\r\n"),
)), )),
|(_, domain, maybe_text, _, more_text, _, moar_text, _)| Response::Greeting { |(_, domain, maybe_text, _, more_text, _, moar_text, _)| Response::Greeting {
domain: domain.to_owned(), domain: domain.to_owned(),
@ -76,7 +76,7 @@ pub fn Greeting(input: &[u8]) -> IResult<&[u8], Response> {
Ok((remaining, parsed)) Ok((remaining, parsed))
} }
/// HT, SP, Printable US-ASCII /// HT, tag(" "), Printable US-ASCII
/// ///
/// textstring = 1*(%d09 / %d32-126) /// textstring = 1*(%d09 / %d32-126)
pub fn textstring(input: &[u8]) -> IResult<&[u8], TextString<'_>> { pub fn textstring(input: &[u8]) -> IResult<&[u8], TextString<'_>> {
@ -97,10 +97,10 @@ pub(crate) fn is_text_string_byte(byte: u8) -> bool {
pub fn Reply_lines(input: &[u8]) -> IResult<&[u8], Response> { pub fn Reply_lines(input: &[u8]) -> IResult<&[u8], Response> {
let mut parser = map( let mut parser = map(
tuple(( tuple((
many0(tuple((Reply_code, tag(b"-"), opt(textstring), CRLF))), many0(tuple((Reply_code, tag(b"-"), opt(textstring), tag("\r\n")))),
Reply_code, Reply_code,
opt(tuple((SP, textstring))), opt(tuple((tag(" "), textstring))),
CRLF, tag("\r\n"),
)), )),
|(intermediate, code, text, _)| { |(intermediate, code, text, _)| {
let mut lines = let mut lines =
@ -151,7 +151,12 @@ pub fn Reply_code(input: &[u8]) -> IResult<&[u8], ReplyCode> {
pub fn ehlo_ok_rsp(input: &[u8]) -> IResult<&[u8], Response> { pub fn ehlo_ok_rsp(input: &[u8]) -> IResult<&[u8], Response> {
let mut parser = alt(( let mut parser = alt((
map( map(
tuple((tag(b"250 "), Domain, opt(preceded(SP, ehlo_greet)), CRLF)), tuple((
tag(b"250 "),
Domain,
opt(preceded(tag(" "), ehlo_greet)),
tag("\r\n"),
)),
|(_, domain, maybe_ehlo, _)| Response::Ehlo { |(_, domain, maybe_ehlo, _)| Response::Ehlo {
domain: domain.to_owned(), domain: domain.to_owned(),
greet: maybe_ehlo.map(|ehlo| ehlo.to_owned()), greet: maybe_ehlo.map(|ehlo| ehlo.to_owned()),
@ -162,12 +167,12 @@ pub fn ehlo_ok_rsp(input: &[u8]) -> IResult<&[u8], Response> {
tuple(( tuple((
tag(b"250-"), tag(b"250-"),
Domain, Domain,
opt(preceded(SP, ehlo_greet)), opt(preceded(tag(" "), ehlo_greet)),
CRLF, tag("\r\n"),
many0(delimited(tag(b"250-"), ehlo_line, CRLF)), many0(delimited(tag(b"250-"), ehlo_line, tag("\r\n"))),
tag(b"250 "), tag(b"250 "),
ehlo_line, ehlo_line,
CRLF, tag("\r\n"),
)), )),
|(_, domain, maybe_ehlo, _, mut lines, _, line, _)| Response::Ehlo { |(_, domain, maybe_ehlo, _, mut lines, _, line, _)| Response::Ehlo {
domain: domain.to_owned(), domain: domain.to_owned(),
@ -203,14 +208,14 @@ pub fn ehlo_line(input: &[u8]) -> IResult<&[u8], Capability> {
let auth = tuple(( let auth = tuple((
tag_no_case("AUTH"), tag_no_case("AUTH"),
alt((tag_no_case(" "), tag_no_case("="))), alt((tag_no_case(" "), tag_no_case("="))),
separated_list0(SP, auth_mechanism), separated_list0(tag(" "), auth_mechanism),
)); ));
let other = tuple(( let other = tuple((
map_res(ehlo_keyword, std::str::from_utf8), map_res(ehlo_keyword, std::str::from_utf8),
opt(preceded( opt(preceded(
alt((SP, tag("="))), // TODO: For Outlook? alt((tag(" "), tag("="))), // TODO: For Outlook?
separated_list0(SP, ehlo_param), separated_list0(tag(" "), ehlo_param),
)), )),
)); ));
@ -257,8 +262,8 @@ pub fn ehlo_line(input: &[u8]) -> IResult<&[u8], Capability> {
/// ehlo-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-") /// ehlo-keyword = (ALPHA / DIGIT) *(ALPHA / DIGIT / "-")
pub fn ehlo_keyword(input: &[u8]) -> IResult<&[u8], &[u8]> { pub fn ehlo_keyword(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple(( let parser = tuple((
take_while_m_n(1, 1, |byte| is_ALPHA(byte) || is_DIGIT(byte)), take_while_m_n(1, 1, |byte| is_alphabetic(byte) || is_digit(byte)),
take_while(|byte| is_ALPHA(byte) || is_DIGIT(byte) || byte == b'-'), take_while(|byte| is_alphabetic(byte) || is_digit(byte) || byte == b'-'),
)); ));
let (remaining, parsed) = recognize(parser)(input)?; let (remaining, parsed) = recognize(parser)(input)?;

View File

@ -1,5 +1,4 @@
/// 4.4. Trace Information (RFC 5321) /// 4.4. Trace Information (RFC 5321)
use abnf_core::streaming::CRLF;
use nom::{ use nom::{
branch::alt, branch::alt,
bytes::streaming::{tag, tag_no_case}, bytes::streaming::{tag, tag_no_case},
@ -22,7 +21,7 @@ use crate::parse::{
/// Return-path-line = "Return-Path:" FWS Reverse-path <CRLF> /// Return-path-line = "Return-Path:" FWS Reverse-path <CRLF>
pub fn Return_path_line(input: &[u8]) -> IResult<&[u8], &[u8]> { pub fn Return_path_line(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((tag_no_case(b"Return-Path:"), FWS, Reverse_path, CRLF)); let parser = tuple((tag_no_case(b"Return-Path:"), FWS, Reverse_path, tag("\r\n")));
let (remaining, parsed) = recognize(parser)(input)?; let (remaining, parsed) = recognize(parser)(input)?;
@ -31,7 +30,7 @@ pub fn Return_path_line(input: &[u8]) -> IResult<&[u8], &[u8]> {
/// Time-stamp-line = "Received:" FWS Stamp <CRLF> /// Time-stamp-line = "Received:" FWS Stamp <CRLF>
pub fn Time_stamp_line(input: &[u8]) -> IResult<&[u8], &[u8]> { pub fn Time_stamp_line(input: &[u8]) -> IResult<&[u8], &[u8]> {
let parser = tuple((tag_no_case(b"Received:"), FWS, Stamp, CRLF)); let parser = tuple((tag_no_case(b"Received:"), FWS, Stamp, tag("\r\n")));
let (remaining, parsed) = recognize(parser)(input)?; let (remaining, parsed) = recognize(parser)(input)?;