extban: add r: realnameban, j: channelban, s: serverban matching extbans
This commit is contained in:
parent
a48b48e46f
commit
245d8d8529
6 changed files with 96 additions and 3 deletions
|
|
@ -906,6 +906,9 @@ impl Server {
|
||||||
Some(b'y') => {
|
Some(b'y') => {
|
||||||
crate::modules::reputation::score_ban_match(self, uid, &b.mask[2..])
|
crate::modules::reputation::score_ban_match(self, uid, &b.mask[2..])
|
||||||
}
|
}
|
||||||
|
Some(b'r') => crate::modules::realnameban::matches(self, uid, &b.mask[2..]),
|
||||||
|
Some(b'j') => crate::modules::channelban::matches(self, uid, &b.mask[2..]),
|
||||||
|
Some(b's') => crate::modules::serverban::matches(self, uid, &b.mask[2..]),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1114,8 +1117,10 @@ pub fn normalize_mask(m: &str) -> String {
|
||||||
pub fn normalize_ban_mask(m: &str) -> String {
|
pub fn normalize_ban_mask(m: &str) -> String {
|
||||||
let b = m.as_bytes();
|
let b = m.as_bytes();
|
||||||
if b.len() >= 2 && b[1] == b':' && (b[0] as char).is_ascii_alphabetic() {
|
if b.len() >= 2 && b[1] == b':' && (b[0] as char).is_ascii_alphabetic() {
|
||||||
// g: (security-group name) and y: (reputation score spec) aren't host masks
|
// These extbans carry a name / spec / channel / server, not a host mask,
|
||||||
if b[0] == b'g' || b[0] == b'y' {
|
// so they must not be host-normalised: g: (security group), y: (reputation
|
||||||
|
// score), r: (realname), j: (channel), s: (server name).
|
||||||
|
if matches!(b[0], b'g' | b'y' | b'r' | b'j' | b's') {
|
||||||
return m.to_string();
|
return m.to_string();
|
||||||
}
|
}
|
||||||
return format!("{}:{}", &m[..1], normalize_mask(&m[2..]));
|
return format!("{}:{}", &m[..1], normalize_mask(&m[2..]));
|
||||||
|
|
|
||||||
47
src/modules/channelban.rs
Normal file
47
src/modules/channelban.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
//! 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; the logic lives here in its own file.
|
||||||
|
//!
|
||||||
|
//! Behaviour reference: InspIRCd's `m_channelban`. Original native Rust.
|
||||||
|
|
||||||
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
pub mod antimixedutf8;
|
pub mod antimixedutf8;
|
||||||
pub mod antirandom;
|
pub mod antirandom;
|
||||||
pub mod blockamsg;
|
pub mod blockamsg;
|
||||||
|
pub mod channelban;
|
||||||
pub mod chathistory;
|
pub mod chathistory;
|
||||||
pub mod cloak;
|
pub mod cloak;
|
||||||
pub mod connectban;
|
pub mod connectban;
|
||||||
|
|
@ -19,10 +20,12 @@ pub mod metadata;
|
||||||
pub mod multiline;
|
pub mod multiline;
|
||||||
pub mod network_icon;
|
pub mod network_icon;
|
||||||
pub mod profilelink;
|
pub mod profilelink;
|
||||||
|
pub mod realnameban;
|
||||||
pub mod reputation;
|
pub mod reputation;
|
||||||
pub mod restrictcommands;
|
pub mod restrictcommands;
|
||||||
pub mod restrictmsg;
|
pub mod restrictmsg;
|
||||||
pub mod securitygroups;
|
pub mod securitygroups;
|
||||||
|
pub mod serverban;
|
||||||
pub mod snoop;
|
pub mod snoop;
|
||||||
pub mod whoisport;
|
pub mod whoisport;
|
||||||
|
|
||||||
|
|
|
||||||
18
src/modules/realnameban.rs
Normal file
18
src/modules/realnameban.rs
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
//! realnameban — the `r:` matching extban: match a user by their real name
|
||||||
|
//! (GECOS) instead of their host. `+b r:*some spammer*` bans everyone whose
|
||||||
|
//! realname matches the glob. Dispatched from the channel ban matcher; the logic
|
||||||
|
//! lives here in its own file.
|
||||||
|
//!
|
||||||
|
//! Behaviour reference: InspIRCd's `m_realnameban`. Original native Rust.
|
||||||
|
|
||||||
|
use crate::channels::glob_match;
|
||||||
|
use crate::server::Server;
|
||||||
|
use crate::Uid;
|
||||||
|
|
||||||
|
/// Does `uid`'s realname match the glob `mask` (the part after `r:`)?
|
||||||
|
pub fn matches(s: &Server, uid: Uid, mask: &str) -> bool {
|
||||||
|
s.users
|
||||||
|
.get(&uid)
|
||||||
|
.map(|u| glob_match(mask, &u.realname))
|
||||||
|
.unwrap_or(false)
|
||||||
|
}
|
||||||
20
src/modules/serverban.rs
Normal file
20
src/modules/serverban.rs
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
//! serverban — the `s:` matching extban: match a user by the name of the server
|
||||||
|
//! they are connected to. `+b s:irc.example.net` bans everyone on that server.
|
||||||
|
//! Ban matching only ever runs against local users (join happens locally), so a
|
||||||
|
//! matched user is on this server — we glob the mask against our own name.
|
||||||
|
//! Dispatched from the channel ban matcher; the logic lives here.
|
||||||
|
//!
|
||||||
|
//! Behaviour reference: InspIRCd's `m_serverban`. Original native Rust.
|
||||||
|
|
||||||
|
use crate::channels::glob_match;
|
||||||
|
use crate::server::Server;
|
||||||
|
use crate::Uid;
|
||||||
|
|
||||||
|
/// Does `uid`'s server name match the glob `mask` (the part after `s:`)?
|
||||||
|
pub fn matches(s: &Server, uid: Uid, mask: &str) -> bool {
|
||||||
|
// local users are on this server; guard on the user still existing
|
||||||
|
if !s.users.contains_key(&uid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
glob_match(mask, &s.name)
|
||||||
|
}
|
||||||
|
|
@ -449,7 +449,7 @@ impl Server {
|
||||||
uid,
|
uid,
|
||||||
RPL_ISUPPORT,
|
RPL_ISUPPORT,
|
||||||
&format!(
|
&format!(
|
||||||
"CHANTYPES=# PREFIX=(qaohv)~&@%+ CHANMODES=beIgX,k,lfjFLHBJdK,ACDGMNOPQRSTUcimnpstuz EXTBAN=,cgmny WATCH=128 MONITOR=128 SILENCE=32 CALLERID=g WHOX CHATHISTORY=256 MSGREFTYPES=timestamp,msgid UTF8ONLY CASEMAPPING=ascii NICKLEN=30 CHANNELLEN=50 NETWORK={} :are supported by this server",
|
"CHANTYPES=# PREFIX=(qaohv)~&@%+ CHANMODES=beIgX,k,lfjFLHBJdK,ACDGMNOPQRSTUcimnpstuz EXTBAN=,cgjmnrsy WATCH=128 MONITOR=128 SILENCE=32 CALLERID=g WHOX CHATHISTORY=256 MSGREFTYPES=timestamp,msgid UTF8ONLY CASEMAPPING=ascii NICKLEN=30 CHANNELLEN=50 NETWORK={} :are supported by this server",
|
||||||
self.network
|
self.network
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue