Fix list wildcards

This commit is contained in:
Alex 2022-07-13 15:26:00 +02:00
parent 15a354f949
commit c703e3e2b8
Signed by: lx
GPG Key ID: 0E496D15096376BE
5 changed files with 85 additions and 31 deletions

23
Cargo.lock generated
View File

@ -30,7 +30,6 @@ dependencies = [
"clap",
"duplexify",
"futures",
"globset",
"hex",
"im",
"imap-codec",
@ -444,15 +443,6 @@ dependencies = [
"tracing-futures",
]
[[package]]
name = "bstr"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
dependencies = [
"memchr",
]
[[package]]
name = "bumpalo"
version = "3.10.0"
@ -973,19 +963,6 @@ dependencies = [
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "globset"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a"
dependencies = [
"aho-corasick",
"bstr",
"fnv",
"log",
"regex",
]
[[package]]
name = "gloo-timers"
version = "0.2.4"

View File

@ -15,7 +15,6 @@ clap = { version = "3.1.18", features = ["derive", "env"] }
duplexify = "1.1.0"
hex = "0.4"
futures = "0.3"
globset = "0.4"
im = "15"
itertools = "0.10"
lazy_static = "1.4"

View File

@ -121,6 +121,29 @@ impl<'a> AuthenticatedContext<'a> {
));
}
let wildcard = String::try_from(mailbox_wildcard.clone())?;
if wildcard.is_empty() {
if is_lsub {
return Ok((
Response::ok("LSUB complete")?.with_body(vec![Data::Lsub {
items: vec![],
delimiter: Some(MAILBOX_HIERARCHY_DELIMITER),
mailbox: "".try_into().unwrap(),
}]),
flow::Transition::None,
));
} else {
return Ok((
Response::ok("LIST complete")?.with_body(vec![Data::List {
items: vec![],
delimiter: Some(MAILBOX_HIERARCHY_DELIMITER),
mailbox: "".try_into().unwrap(),
}]),
flow::Transition::None,
));
}
}
let mailboxes = self.user.list_mailboxes().await?;
let mut vmailboxes = BTreeMap::new();
for mb in mailboxes.iter() {
@ -135,12 +158,9 @@ impl<'a> AuthenticatedContext<'a> {
vmailboxes.insert(mb, true);
}
let wildcard = String::try_from(mailbox_wildcard.clone())?;
let wildcard_pat = globset::Glob::new(&wildcard)?.compile_matcher();
let mut ret = vec![];
for (mb, is_real) in vmailboxes.iter() {
if wildcard_pat.is_match(mb) {
if matches_wildcard(&wildcard, &mb) {
let mailbox = mb
.to_string()
.try_into()
@ -378,3 +398,49 @@ impl<'a> AuthenticatedContext<'a> {
Ok((mb, uidvalidity, uid))
}
}
fn matches_wildcard(wildcard: &str, name: &str) -> bool {
let wildcard = wildcard.chars().collect::<Vec<char>>();
let name = name.chars().collect::<Vec<char>>();
let mut matches = vec![vec![false; wildcard.len() + 1]; name.len() + 1];
for i in 0..=name.len() {
for j in 0..=wildcard.len() {
matches[i][j] = (i == 0 && j == 0)
|| (j > 0
&& matches[i][j - 1]
&& (wildcard[j - 1] == '%' || wildcard[j - 1] == '*'))
|| (i > 0
&& j > 0
&& matches[i - 1][j - 1]
&& wildcard[j - 1] == name[i - 1]
&& wildcard[j - 1] != '%'
&& wildcard[j - 1] != '*')
|| (i > 0
&& j > 0
&& matches[i - 1][j]
&& (wildcard[j - 1] == '*'
|| (wildcard[j - 1] == '%' && name[i - 1] != MAILBOX_HIERARCHY_DELIMITER)));
}
}
matches[name.len()][wildcard.len()]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_wildcard_matches() {
assert!(matches_wildcard("INBOX", "INBOX"));
assert!(matches_wildcard("*", "INBOX"));
assert!(matches_wildcard("%", "INBOX"));
assert!(!matches_wildcard("%", "Test.Azerty"));
assert!(!matches_wildcard("INBOX.*", "INBOX"));
assert!(matches_wildcard("Sent.*", "Sent.A"));
assert!(matches_wildcard("Sent.*", "Sent.A.B"));
assert!(!matches_wildcard("Sent.%", "Sent.A.B"));
}
}

View File

@ -41,8 +41,14 @@ impl State {
match (self, tr) {
(s, Transition::None) => Ok(s),
(State::NotAuthenticated, Transition::Authenticate(u)) => Ok(State::Authenticated(u)),
(State::Authenticated(u), Transition::Select(m)) => Ok(State::Selected(u, m)),
(State::Authenticated(u), Transition::Examine(m)) => Ok(State::Examined(u, m)),
(
State::Authenticated(u) | State::Selected(u, _) | State::Examined(u, _),
Transition::Select(m),
) => Ok(State::Selected(u, m)),
(
State::Authenticated(u) | State::Selected(u, _) | State::Examined(u, _),
Transition::Examine(m),
) => Ok(State::Examined(u, m)),
(State::Selected(u, _), Transition::Unselect) => Ok(State::Authenticated(u)),
(State::Examined(u, _), Transition::Unselect) => Ok(State::Authenticated(u)),
(_, Transition::Logout) => Ok(State::Logout),

View File

@ -140,7 +140,13 @@ impl Instance {
let res = match ctrl {
Ok((res, tr)) => {
//@FIXME remove unwrap
self.state = self.state.apply(tr).unwrap();
self.state = match self.state.apply(tr) {
Ok(new_state) => new_state,
Err(e) => {
tracing::error!("Invalid transition: {}, exiting", e);
break;
}
};
//@FIXME enrich here the command with some global status