45 lines
1.6 KiB
Rust
45 lines
1.6 KiB
Rust
//! channelban — the `j:` matching extban: match a user by another channel they
|
|
//! are in. `+b j:#lobby` bans everyone who is also in `#lobby`; an optional status
|
|
//! prefix narrows it to members at/above that rank, e.g. `+b j:@#staff` matches
|
|
//! only ops-or-higher in `#staff`. The channel part is a glob. Dispatched from the
|
|
//! channel ban matcher.
|
|
|
|
use crate::channels::{glob_match, RANK_ADMIN, RANK_HALFOP, RANK_OP, RANK_OWNER, RANK_VOICE};
|
|
use crate::server::Server;
|
|
use crate::Uid;
|
|
|
|
/// Map a leading status prefix to the minimum rank it requires; `None` if the
|
|
/// first char isn't a prefix (so the whole string is the channel glob).
|
|
fn split_prefix(mask: &str) -> (u8, &str) {
|
|
match mask.chars().next() {
|
|
Some('~') => (RANK_OWNER, &mask[1..]),
|
|
Some('&') => (RANK_ADMIN, &mask[1..]),
|
|
Some('@') => (RANK_OP, &mask[1..]),
|
|
Some('%') => (RANK_HALFOP, &mask[1..]),
|
|
Some('+') => (RANK_VOICE, &mask[1..]),
|
|
_ => (0, mask),
|
|
}
|
|
}
|
|
|
|
/// Is `uid` a member (at/above the required rank) of a channel matching the glob
|
|
/// in `mask` (the part after `j:`)?
|
|
pub fn matches(s: &Server, uid: Uid, mask: &str) -> bool {
|
|
let (min_rank, changlob) = split_prefix(mask);
|
|
let changlob = changlob.to_ascii_lowercase();
|
|
let Some(u) = s.users.get(&uid) else {
|
|
return false;
|
|
};
|
|
u.channels.iter().any(|key| {
|
|
if !glob_match(&changlob, key) {
|
|
return false;
|
|
}
|
|
if min_rank == 0 {
|
|
return true;
|
|
}
|
|
s.channels
|
|
.get(key)
|
|
.and_then(|ch| ch.members.get(&uid))
|
|
.map(|m| m.rank() >= min_rank)
|
|
.unwrap_or(false)
|
|
})
|
|
}
|