add rfc3501 rules that relate to response parsing
This commit is contained in:
parent
e0ca51ef79
commit
875041edfd
2 changed files with 116 additions and 4 deletions
|
@ -14,10 +14,10 @@ pub fn parse_capability(s: &str) -> Result<Capability, Error<Rule>> {
|
||||||
let mut inner = pair.into_inner();
|
let mut inner = pair.into_inner();
|
||||||
let pair = inner.next().unwrap();
|
let pair = inner.next().unwrap();
|
||||||
match pair.as_rule() {
|
match pair.as_rule() {
|
||||||
Rule::auth_type => Capability::Auth(pair.as_str().to_owned()),
|
Rule::auth_type => Capability::Auth(pair.as_str().to_uppercase().to_owned()),
|
||||||
Rule::atom => match pair.as_str() {
|
Rule::atom => match pair.as_str() {
|
||||||
"IMAP4rev1" => Capability::Imap4rev1,
|
"IMAP4rev1" => Capability::Imap4rev1,
|
||||||
s => Capability::Atom(s.to_owned()),
|
s => Capability::Atom(s.to_uppercase().to_owned()),
|
||||||
},
|
},
|
||||||
_ => unreachable!("{:?}", pair),
|
_ => unreachable!("{:?}", pair),
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,10 @@ pub fn parse_capability(s: &str) -> Result<Capability, Error<Rule>> {
|
||||||
Ok(cap)
|
Ok(cap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_response(s: &str) -> Result<Response, Error<Rule>> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
@ -39,6 +43,7 @@ mod tests {
|
||||||
assert_eq!(parse_capability("IMAP4rev1"), Ok(Capability::Imap4rev1));
|
assert_eq!(parse_capability("IMAP4rev1"), Ok(Capability::Imap4rev1));
|
||||||
assert_eq!(parse_capability("LOGINDISABLED"), Ok(Capability::Atom("LOGINDISABLED".to_owned())));
|
assert_eq!(parse_capability("LOGINDISABLED"), Ok(Capability::Atom("LOGINDISABLED".to_owned())));
|
||||||
assert_eq!(parse_capability("AUTH=PLAIN"), Ok(Capability::Auth("PLAIN".to_owned())));
|
assert_eq!(parse_capability("AUTH=PLAIN"), Ok(Capability::Auth("PLAIN".to_owned())));
|
||||||
|
assert_eq!(parse_capability("auth=plain"), Ok(Capability::Auth("PLAIN".to_owned())));
|
||||||
|
|
||||||
assert!(parse_capability("(OSU)").is_err());
|
assert!(parse_capability("(OSU)").is_err());
|
||||||
assert!(parse_capability("\x01HELLO").is_err());
|
assert!(parse_capability("\x01HELLO").is_err());
|
||||||
|
|
|
@ -1,17 +1,124 @@
|
||||||
// formal syntax from https://tools.ietf.org/html/rfc3501#section-9
|
// formal syntax from https://tools.ietf.org/html/rfc3501#section-9
|
||||||
|
addr_adl = { nstring }
|
||||||
|
addr_host = { nstring }
|
||||||
|
addr_mailbox = { nstring }
|
||||||
|
addr_name = { nstring }
|
||||||
|
address = { "(" ~ addr_name ~ sp ~ addr_adl ~ sp ~ addr_mailbox ~ sp ~ addr_host ~ ")" }
|
||||||
|
astring = @{ astring_char{1,} | string }
|
||||||
|
astring_char = @{ atom_char | resp_specials }
|
||||||
atom = @{ atom_char{1,} }
|
atom = @{ atom_char{1,} }
|
||||||
atom_char = @{ !atom_specials ~ char }
|
atom_char = @{ !atom_specials ~ char }
|
||||||
atom_specials = @{ "(" | ")" | "{" | sp | ctl | list_wildcards | quoted_specials | resp_specials }
|
atom_specials = @{ "(" | ")" | "{" | sp | ctl | list_wildcards | quoted_specials | resp_specials }
|
||||||
auth_type = { atom }
|
auth_type = { atom }
|
||||||
capability = ${ "AUTH=" ~ auth_type | atom }
|
base64 = @{ (base64_char{4})* ~ base64_terminal }
|
||||||
|
base64_char = @{ alpha | digit | "+" | "/" }
|
||||||
|
base64_terminal = @{ (base64_char{2} ~ "==") | (base64_char{3} ~ "=") }
|
||||||
|
body = { "(" ~ (body_type_1part | body_type_mpart) ~ ")" }
|
||||||
|
body_ext_1part = { body_fld_md5 ~ (sp ~ body_fld_dsp ~ (sp ~ body_fld_lang ~ (sp ~ body_fld_loc ~ (sp ~ body_extension)*)?)?)? }
|
||||||
|
body_ext_mpart = { body_fld_param ~ (sp ~ body_fld_dsp ~ (sp ~ body_fld_lang ~ (sp ~ body_fld_loc ~ (sp ~ body_extension)*)?)?)? }
|
||||||
|
body_extension = { nstring | number | "(" ~ body_extension ~ (sp ~ body_extension)* ~ ")" }
|
||||||
|
body_fields = { body_fld_param ~ sp ~ body_fld_id ~ sp ~ body_fld_desc ~ sp ~ body_fld_enc ~ sp ~ body_fld_octets }
|
||||||
|
body_fld_desc = { nstring }
|
||||||
|
body_fld_dsp = { "(" ~ string ~ sp ~ body_fld_param ~ ")" | nil }
|
||||||
|
body_fld_enc = { (dquote ~ (^"7BIT" | ^"8BIT" | ^"BINARY" | ^"BASE64" | ^"QUOTED-PRINTABLE") ~ dquote) | string }
|
||||||
|
body_fld_id = { nstring }
|
||||||
|
body_fld_lang = { nstring | "(" ~ string ~ (sp ~ string)* ~ ")" }
|
||||||
|
body_fld_lines = { number }
|
||||||
|
body_fld_loc = { nstring }
|
||||||
|
body_fld_md5 = { nstring }
|
||||||
|
body_fld_octets = { number }
|
||||||
|
body_fld_param = { "(" ~ string ~ sp ~ string ~ (sp ~ string ~ sp ~ string)* ~ ")" | nil}
|
||||||
|
body_type_1part = { (body_type_basic | body_type_msg | body_type_text) ~ (sp ~ body_ext_1part)? }
|
||||||
|
body_type_basic = { media_basic ~ sp ~ body_fields }
|
||||||
|
body_type_mpart = { body{1,} ~ sp ~ media_subtype ~ (sp ~ body_ext_mpart)? }
|
||||||
|
body_type_msg = { media_message ~ sp ~ body_fields ~ sp ~ envelope ~ sp ~ body ~ sp ~ body_fld_lines }
|
||||||
|
body_type_text = { media_text ~ sp ~ body_fields ~ sp ~ body_fld_lines }
|
||||||
|
capability = ${ ^"AUTH=" ~ auth_type | atom }
|
||||||
|
capability_data = { ^"CAPABILITY" ~ (sp ~ capability)* ~ sp ~ "IMAP4rev1" ~ (sp ~ capability)* }
|
||||||
|
char8 = @{ '\x01'..'\xff' }
|
||||||
|
continue_req = { "+" ~ sp ~ (resp_text | base64) ~ crlf }
|
||||||
|
date_day_fixed = { (sp ~ digit) | digit{2} }
|
||||||
|
date_month = { "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" | "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec" }
|
||||||
|
date_time = { dquote ~ date_day_fixed ~ "-" ~ date_month ~ "-" ~ date_year ~ sp ~ time ~ sp ~ zone ~ dquote }
|
||||||
|
date_year = { digit{4} }
|
||||||
|
digit_nz = @{ '\x31'..'\x39' }
|
||||||
|
env_bcc = { "(" ~ address{1,} ~ ")" | nil }
|
||||||
|
env_cc = { "(" ~ address{1,} ~ ")" | nil }
|
||||||
|
env_date = { nstring }
|
||||||
|
env_from = { "(" ~ address{1,} ~ ")" | nil }
|
||||||
|
env_in_reply_to = { nstring }
|
||||||
|
env_message_id = { nstring }
|
||||||
|
env_reply_to = { "(" ~ address{1,} ~ ")" | nil }
|
||||||
|
env_sender = { "(" ~ address{1,} ~ ")" | nil }
|
||||||
|
env_subject = { nstring }
|
||||||
|
env_to = { "(" ~ address{1,} ~ ")" | nil }
|
||||||
|
envelope = { "(" ~ env_date ~ sp ~ env_subject ~ sp ~ env_from ~ sp ~ env_sender ~ sp ~ env_reply_to ~ sp ~ env_to ~ sp ~ env_cc ~ sp ~ env_bcc ~ sp ~ env_in_reply_to ~ sp ~ env_message_id ~ ")" }
|
||||||
|
flag = { "\\Answered" | "\\Flagged" | "\\Deleted" | "\\Seen" | "\\Draft" | flag_keyword | flag_extension }
|
||||||
|
flag_extension = @{ "\\" ~ atom }
|
||||||
|
flag_fetch = { flag | "\\Recent" }
|
||||||
|
flag_keyword = @{ atom }
|
||||||
|
flag_list = { "(" ~ (flag ~ (sp ~ flag)*)? ~ ")" }
|
||||||
|
flag_perm = { flag | "\\*" }
|
||||||
|
header_fld_name = { astring }
|
||||||
|
header_list = { "(" ~ header_fld_name ~ (sp ~ header_fld_name)* ~ ")" }
|
||||||
list_wildcards = @{ "%" | "*" }
|
list_wildcards = @{ "%" | "*" }
|
||||||
|
literal = @{ "{" ~ number ~ "}" ~ crlf ~ char8* }
|
||||||
|
mailbox = { ^"INBOX" | astring }
|
||||||
|
mailbox_data = { (^"FLAGS" ~ sp ~ flag_list) | (^"LIST" ~ sp ~ mailbox_list) | (^"LSUB" ~ sp ~ mailbox_list) | (^"SEARCH" ~ (sp ~ nz_number)*) | (^"STATUS" ~ sp ~ mailbox ~ sp ~ ^"(" ~ status_att_list? ~ ^")") | (number ~ sp ~ ^"EXISTS") | (number ~ sp ~ ^"RECENT") }
|
||||||
|
mailbox_list = { "(" ~ mbx_list_flags* ~ ")" ~ sp ~ (dquote ~ quoted_char ~ dquote | nil) ~ sp ~ mailbox }
|
||||||
|
mbx_list_flags = { (mbx_list_oflag ~ sp)* ~ mbx_list_sflag ~ (sp ~ mbx_list_oflag)* | mbx_list_oflag ~ (sp ~ mbx_list_oflag)* }
|
||||||
|
mbx_list_oflag = { "\\NoInferiors" | flag_extension }
|
||||||
|
mbx_list_sflag = { "\\NoSelect" | "\\Marked" | "\\Unmarked" }
|
||||||
|
media_basic = { ((dquote ~ ("APPLICATION" | "AUDIO" | "IMAGE" | "MESSAGE" | "VIDEO") ~ dquote) | string) ~ sp ~ media_subtype }
|
||||||
|
media_message = { dquote ~ "MESSAGE" ~ dquote ~ sp ~ dquote ~ "RFC822" ~ dquote }
|
||||||
|
media_subtype = { string }
|
||||||
|
media_text = { dquote ~ "TEXT" ~ dquote ~ sp ~ media_subtype }
|
||||||
|
message_data = { nz_number ~ sp ~ (^"EXPUNGE" | (^"FETCH" ~ sp ~ msg_att)) }
|
||||||
|
msg_att = { "(" ~ (msg_att_dynamic | msg_att_static) ~ (sp ~ (msg_att_dynamic | msg_att_static))* ~ ")" }
|
||||||
|
msg_att_dynamic = { ^"FLAGS" ~ sp ~ "(" ~ (flag_fetch ~ (sp ~ flag_fetch)*)? ~ ")" }
|
||||||
|
msg_att_static = { (^"ENVELOPE" ~ sp ~ envelope) | (^"INTERNALDATE" ~ sp ~ date_time) | (^"RFC822" ~ (^".HEADER" | ^".TEXT") ~ sp ~ nstring) | (^"RFC822.SIZE" ~ sp ~ number) | (^"BODY" ~ ^"STRUCTURE"? ~ sp ~ body) | (^"BODY" ~ section ~ ("<" ~ number ~ ">")? ~ sp ~ nstring) | (^"UID" ~ sp ~ uniqueid) }
|
||||||
|
nil = { ^"NIL" }
|
||||||
|
nstring = { string | nil }
|
||||||
|
number = @{ digit{1,} }
|
||||||
|
nz_number = @{ digit_nz ~ digit* }
|
||||||
|
quoted = @{ dquote ~ quoted_char* ~ dquote }
|
||||||
|
quoted_char = @{ (!quoted_specials ~ char) | ("\\" ~ quoted_specials) }
|
||||||
quoted_specials = @{ dquote | "\\" }
|
quoted_specials = @{ dquote | "\\" }
|
||||||
|
resp_cond_bye = { ^"BYE" ~ sp ~ resp_text }
|
||||||
|
resp_cond_state = { (^"OK" | ^"NO" | ^"BAD") ~ resp_text }
|
||||||
resp_specials = @{ "]" }
|
resp_specials = @{ "]" }
|
||||||
nil = { "NIL" }
|
resp_text = { ("[" ~ resp_text_code ~ "]" ~ sp)? ~ text }
|
||||||
|
resp_text_code = { ^"ALERT" | (^"BADCHARSET" ~ (sp ~ "(" ~ astring ~ (sp ~ astring)* ~ ")")?) | capability_data | ^"PARSE" | (^"PERMANENTFLAGS" ~ sp ~ "(" ~ (flag_perm ~ (sp ~ flag_perm)*)? ~ ")") | ^"READ-ONLY" | ^"READ-WRITE" | ^"TRYCREATE" | (^"UIDNEXT" ~ sp ~ nz_number) | (^"UIDVALIDITY" ~ sp ~ nz_number) | (^"UNSEEN" ~ sp ~ nz_number) | (atom ~ (sp ~ resp_text_code_atom)?) }
|
||||||
|
resp_text_code_atom = @{ (!"]" ~ text_char){1,} }
|
||||||
|
response = { continue_req | response_data | response_done }
|
||||||
|
response_data = { "*" ~ sp ~ (resp_cond_state | resp_cond_bye | mailbox_data | message_data | capability_data) ~ crlf }
|
||||||
|
response_done = { response_tagged | response_fatal }
|
||||||
|
response_fatal = { "*" ~ sp ~ resp_cond_bye ~ crlf }
|
||||||
|
response_tagged = { tag ~ sp ~ resp_cond_state ~ crlf }
|
||||||
|
section = { "[" ~ section_spec? ~ "]" }
|
||||||
|
section_msgtext = { ^"HEADER" | (^"HEADER.FIELDS" ~ ^".NOT"? ~ sp ~ header_list) | ^"TEXT" }
|
||||||
|
section_part = { nz_number ~ ("." ~ nz_number)* }
|
||||||
|
section_spec = { section_msgtext | (section_part ~ ("." ~ section_text)?) }
|
||||||
|
section_text = { section_msgtext | "MIME" }
|
||||||
|
status_att = { ^"MESSAGES" | ^"RECENT" | ^"UIDNEXT" | ^"UIDVALIDITY" | ^"UNSEEN" }
|
||||||
|
status_att_list = { status_att ~ sp ~ number ~ (sp ~ status_att ~ sp ~ number)* }
|
||||||
|
string = @{ quoted | literal }
|
||||||
|
tag = @{ tag_char{1,} }
|
||||||
|
tag_char = @{ !"+" ~ astring_char }
|
||||||
|
text = @{ text_char{1,} }
|
||||||
|
text_char = @{ !cr ~ !lf ~ char }
|
||||||
|
time = { digit{2} ~ ":" ~ digit{2} ~ ":" ~ digit{2} }
|
||||||
|
uniqueid = { nz_number }
|
||||||
|
zone = { ("+" | "-") ~ digit{4} }
|
||||||
|
|
||||||
// core rules from https://tools.ietf.org/html/rfc2234#section-6.1
|
// core rules from https://tools.ietf.org/html/rfc2234#section-6.1
|
||||||
|
alpha = @{ '\x41'..'\x5a' | '\x61'..'\x7a' }
|
||||||
char = @{ '\x01'..'\x7f' }
|
char = @{ '\x01'..'\x7f' }
|
||||||
|
cr = @{ "\x0d" }
|
||||||
|
crlf = @{ cr ~ lf }
|
||||||
ctl = @{ '\x00'..'\x1f' | "\x7f" }
|
ctl = @{ '\x00'..'\x1f' | "\x7f" }
|
||||||
|
digit = @{ '\x30'..'\x39' }
|
||||||
dquote = @{ "\"" }
|
dquote = @{ "\"" }
|
||||||
|
lf = @{ "\x0a" }
|
||||||
sp = @{ " " }
|
sp = @{ " " }
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue