Skip to content

Commit f6c618d

Browse files
urkledjc
authored andcommitted
Add support for RFC 4314/2086 ACL extension to imap-proto
ACL response LISTRIGHTS response MYRIGHTS response
1 parent 332de09 commit f6c618d

File tree

6 files changed

+633
-3
lines changed

6 files changed

+633
-3
lines changed

imap-proto/src/parser/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pub mod gmail;
88
pub mod rfc2087;
99
pub mod rfc2971;
1010
pub mod rfc3501;
11+
pub mod rfc4314;
1112
pub mod rfc4315;
1213
pub mod rfc4551;
1314
pub mod rfc5161;

imap-proto/src/parser/rfc3501/mod.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ use nom::{
1919

2020
use crate::{
2121
parser::{
22-
core::*, rfc2087, rfc2971, rfc3501::body::*, rfc3501::body_structure::*, rfc4315, rfc4551,
23-
rfc5161, rfc5256, rfc5464, rfc7162,
22+
core::*, rfc2087, rfc2971, rfc3501::body::*, rfc3501::body_structure::*, rfc4314, rfc4315,
23+
rfc4551, rfc5161, rfc5256, rfc5464, rfc7162,
2424
},
2525
types::*,
2626
};
@@ -54,7 +54,7 @@ fn status(i: &[u8]) -> IResult<&[u8], Status> {
5454
alt((status_ok, status_no, status_bad, status_preauth, status_bye))(i)
5555
}
5656

57-
fn mailbox(i: &[u8]) -> IResult<&[u8], &str> {
57+
pub(crate) fn mailbox(i: &[u8]) -> IResult<&[u8], &str> {
5858
map(astring_utf8, |s| {
5959
if s.eq_ignore_ascii_case("INBOX") {
6060
"INBOX"
@@ -680,6 +680,9 @@ pub(crate) fn response_data(i: &[u8]) -> IResult<&[u8], Response> {
680680
rfc2087::quota,
681681
rfc2087::quota_root,
682682
rfc2971::resp_id,
683+
rfc4314::acl,
684+
rfc4314::list_rights,
685+
rfc4314::my_rights,
683686
)),
684687
tag(b"\r\n"),
685688
)(i)

imap-proto/src/parser/rfc4314.rs

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//!
2+
//! Current
3+
//! https://tools.ietf.org/html/rfc4314
4+
//!
5+
//! Original
6+
//! https://tools.ietf.org/html/rfc2086
7+
//!
8+
//! The IMAP ACL Extension
9+
//!
10+
11+
use std::borrow::Cow;
12+
13+
use nom::{
14+
bytes::streaming::tag_no_case,
15+
character::complete::{space0, space1},
16+
combinator::map,
17+
multi::separated_list0,
18+
sequence::{preceded, separated_pair, tuple},
19+
IResult,
20+
};
21+
22+
use crate::parser::core::astring_utf8;
23+
use crate::parser::rfc3501::mailbox;
24+
use crate::types::*;
25+
26+
/// 3.6. ACL Response
27+
/// ```ignore
28+
/// acl_response ::= "ACL" SP mailbox SP acl_list
29+
/// ```
30+
pub(crate) fn acl(i: &[u8]) -> IResult<&[u8], Response> {
31+
let (rest, (_, _, mailbox, acls)) = tuple((
32+
tag_no_case("ACL"),
33+
space1,
34+
map(mailbox, Cow::Borrowed),
35+
acl_list,
36+
))(i)?;
37+
38+
Ok((rest, Response::Acl(Acl { mailbox, acls })))
39+
}
40+
41+
/// ```ignore
42+
/// acl_list ::= *(SP acl_entry)
43+
/// ```
44+
fn acl_list(i: &[u8]) -> IResult<&[u8], Vec<AclEntry>> {
45+
preceded(space0, separated_list0(space1, acl_entry))(i)
46+
}
47+
48+
/// ```ignore
49+
/// acl_entry ::= SP identifier SP rights
50+
/// ```
51+
fn acl_entry(i: &[u8]) -> IResult<&[u8], AclEntry> {
52+
let (rest, (identifier, rights)) = separated_pair(
53+
map(astring_utf8, Cow::Borrowed),
54+
space1,
55+
map(astring_utf8, map_text_to_rights),
56+
)(i)?;
57+
58+
Ok((rest, AclEntry { identifier, rights }))
59+
}
60+
61+
/// 3.7. LISTRIGHTS Response
62+
/// ```ignore
63+
/// list_rights_response ::= "LISTRIGHTS" SP mailbox SP identifier SP required_rights *(SP optional_rights)
64+
/// ```
65+
pub(crate) fn list_rights(i: &[u8]) -> IResult<&[u8], Response> {
66+
let (rest, (_, _, mailbox, _, identifier, _, required, optional)) = tuple((
67+
tag_no_case("LISTRIGHTS"),
68+
space1,
69+
map(mailbox, Cow::Borrowed),
70+
space1,
71+
map(astring_utf8, Cow::Borrowed),
72+
space1,
73+
map(astring_utf8, map_text_to_rights),
74+
list_rights_optional,
75+
))(i)?;
76+
77+
Ok((
78+
rest,
79+
Response::ListRights(ListRights {
80+
mailbox,
81+
identifier,
82+
required,
83+
optional,
84+
}),
85+
))
86+
}
87+
88+
fn list_rights_optional(i: &[u8]) -> IResult<&[u8], Vec<AclRight>> {
89+
let (rest, items) = preceded(space0, separated_list0(space1, astring_utf8))(i)?;
90+
91+
Ok((
92+
rest,
93+
items
94+
.into_iter()
95+
.flat_map(|s| s.chars().map(|c| c.into()))
96+
.collect(),
97+
))
98+
}
99+
100+
/// 3.7. MYRIGHTS Response
101+
/// ```ignore
102+
/// my_rights_response ::= "MYRIGHTS" SP mailbox SP rights
103+
/// ```
104+
pub(crate) fn my_rights(i: &[u8]) -> IResult<&[u8], Response> {
105+
let (rest, (_, _, mailbox, _, rights)) = tuple((
106+
tag_no_case("MYRIGHTS"),
107+
space1,
108+
map(mailbox, Cow::Borrowed),
109+
space1,
110+
map(astring_utf8, map_text_to_rights),
111+
))(i)?;
112+
113+
Ok((rest, Response::MyRights(MyRights { mailbox, rights })))
114+
}
115+
116+
/// helper routine to map a string to a vec of AclRights
117+
fn map_text_to_rights(i: &str) -> Vec<AclRight> {
118+
i.chars().map(|c| c.into()).collect()
119+
}

0 commit comments

Comments
 (0)