Get rid of unnecessary dependency
This commit is contained in:
parent
db7fd2300f
commit
e7cf12b4a0
|
@ -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 }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)?;
|
||||||
|
|
|
@ -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"),
|
||||||
)),
|
)),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
|
@ -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)))),
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
|
|
@ -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)?;
|
||||||
|
|
|
@ -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)?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue