OperServ: CBAN — ban a channel name
CBAN ADD <#channel-glob> <reason> / DEL / LIST blocks users from creating
or joining a matching channel (m_cban is loaded). Another kind ("CBAN")
in the generalized X-line handler with a channel-mask normalizer; the
too-wide guard now ignores a leading channel prefix so #* is refused.
This commit is contained in:
parent
a7aacf347e
commit
90824513a7
3 changed files with 21 additions and 3 deletions
|
|
@ -63,6 +63,7 @@ impl Service for OperServ {
|
|||
Some(cmd) if cmd.eq_ignore_ascii_case("SQLINE") => xline::SQLINE.handle(me, from, args, ctx, db),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("SNLINE") => xline::SNLINE.handle(me, from, args, ctx, db),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("SHUN") => xline::SHUN.handle(me, from, args, ctx, db),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("CBAN") => xline::CBAN.handle(me, from, args, ctx, db),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("GLOBAL") => global::handle(me, from, args, ctx),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("KILL") => kill::handle(me, from, args, ctx, net),
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("KICK") => kick::handle(me, from, args, ctx, net),
|
||||
|
|
@ -87,5 +88,5 @@ impl Service for OperServ {
|
|||
}
|
||||
|
||||
fn help(me: &str, from: &Sender, ctx: &mut ServiceCtx) {
|
||||
ctx.notice(me, from.uid, "OperServ holds network operator tools (Priv::Admin): \x02AKILL\x02 ADD|DEL|LIST (user@host bans), \x02SQLINE\x02 ADD|DEL|LIST (nick bans), \x02SNLINE\x02 ADD|DEL|LIST (realname bans), \x02SHUN\x02 ADD|DEL|LIST (silence a host), \x02CHANKILL\x02 <#chan> (clear a channel), \x02GLOBAL\x02 <message> (announce to everyone), \x02KILL\x02 <nick> [reason] (disconnect a user), \x02KICK\x02 <#chan> <nick> [reason], \x02MODE\x02 <#chan> <modes> [params], \x02IGNORE\x02 ADD|DEL|LIST (silence a user from services), \x02STATS\x02 (enforcement overview), \x02SVSNICK\x02 <nick> <newnick>, \x02SVSJOIN\x02 <nick> <#chan> [key], \x02INFO\x02 ADD|DEL <target> (staff notes), \x02NEWS\x02 ADD|DEL|LIST <LOGON|OPER> (announcements), \x02OPER\x02 ADD|DEL|LIST (runtime operators), \x02SESSION\x02 LIST|VIEW + \x02EXCEPTION\x02 ADD|DEL|LIST (per-IP session limits), \x02JUPE\x02 <server> | DEL | LIST, \x02LOGSEARCH\x02 [pattern] (search the action log).");
|
||||
ctx.notice(me, from.uid, "OperServ holds network operator tools (Priv::Admin): \x02AKILL\x02 ADD|DEL|LIST (user@host bans), \x02SQLINE\x02 ADD|DEL|LIST (nick bans), \x02SNLINE\x02 ADD|DEL|LIST (realname bans), \x02SHUN\x02 ADD|DEL|LIST (silence a host), \x02CBAN\x02 ADD|DEL|LIST (ban a channel name), \x02CHANKILL\x02 <#chan> (clear a channel), \x02GLOBAL\x02 <message> (announce to everyone), \x02KILL\x02 <nick> [reason] (disconnect a user), \x02KICK\x02 <#chan> <nick> [reason], \x02MODE\x02 <#chan> <modes> [params], \x02IGNORE\x02 ADD|DEL|LIST (silence a user from services), \x02STATS\x02 (enforcement overview), \x02SVSNICK\x02 <nick> <newnick>, \x02SVSJOIN\x02 <nick> <#chan> [key], \x02INFO\x02 ADD|DEL <target> (staff notes), \x02NEWS\x02 ADD|DEL|LIST <LOGON|OPER> (announcements), \x02OPER\x02 ADD|DEL|LIST (runtime operators), \x02SESSION\x02 LIST|VIEW + \x02EXCEPTION\x02 ADD|DEL|LIST (per-IP session limits), \x02JUPE\x02 <server> | DEL | LIST, \x02LOGSEARCH\x02 [pattern] (search the action log).");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,6 +127,10 @@ pub const SNLINE: Xline = Xline { kind: "R", name: "SNLINE", target: "realname-r
|
|||
// drops their commands — a quieter alternative to an AKILL.
|
||||
pub const SHUN: Xline = Xline { kind: "SHUN", name: "SHUN", target: "user@host", normalize: norm_userhost };
|
||||
|
||||
// CBAN: a channel-name ban. Users can't join (or create) a channel matching the
|
||||
// mask, which is a channel glob like `#warez*`.
|
||||
pub const CBAN: Xline = Xline { kind: "CBAN", name: "CBAN", target: "#channel", normalize: norm_channel };
|
||||
|
||||
fn norm_userhost(input: &str) -> Option<String> {
|
||||
let body = input.rsplit('!').next().unwrap_or(input);
|
||||
let (user, host) = body.split_once('@')?;
|
||||
|
|
@ -151,10 +155,19 @@ fn norm_realname(input: &str) -> Option<String> {
|
|||
(!input.is_empty()).then(|| input.to_string())
|
||||
}
|
||||
|
||||
fn norm_channel(input: &str) -> Option<String> {
|
||||
// A channel-name glob: must carry a channel prefix and a non-empty body.
|
||||
if (!input.starts_with('#') && !input.starts_with('&')) || input.len() < 2 || input.contains(|c: char| c.is_whitespace() || c == ',') {
|
||||
return None;
|
||||
}
|
||||
Some(input.to_ascii_lowercase())
|
||||
}
|
||||
|
||||
// A mask whose every meaningful character is a wildcard would match nearly all.
|
||||
// A leading channel prefix is ignored so `#*` counts as too wide.
|
||||
fn too_wide(mask: &str) -> bool {
|
||||
let trivial = |s: &str| s.chars().all(|c| c == '*' || c == '?' || c == '.' || c == '@');
|
||||
trivial(mask)
|
||||
let body = mask.strip_prefix('#').or_else(|| mask.strip_prefix('&')).unwrap_or(mask);
|
||||
body.is_empty() || body.chars().all(|c| c == '*' || c == '?' || c == '.' || c == '@')
|
||||
}
|
||||
|
||||
fn human_secs(secs: u64) -> String {
|
||||
|
|
|
|||
|
|
@ -1593,6 +1593,7 @@ fn ban_kind_label(kind: &str) -> &'static str {
|
|||
"Q" => "nick ban",
|
||||
"R" => "realname ban",
|
||||
"SHUN" => "shun",
|
||||
"CBAN" => "channel ban",
|
||||
_ => "network ban",
|
||||
}
|
||||
}
|
||||
|
|
@ -4053,6 +4054,9 @@ mod tests {
|
|||
assert!(os(&mut e, "000AAAAAS", "SNLINE ADD .*free.money.* spambot").iter().any(|a| matches!(a, NetAction::AddLine { kind, mask, .. } if kind == "R" && mask == ".*free.money.*")), "R-line added");
|
||||
// SHUN drives a SHUN X-line on a user@host mask.
|
||||
assert!(os(&mut e, "000AAAAAS", "SHUN ADD *@noisy.host quiet down").iter().any(|a| matches!(a, NetAction::AddLine { kind, mask, .. } if kind == "SHUN" && mask == "*@noisy.host")), "shun added");
|
||||
// CBAN drives a channel-name ban; an all-wildcard channel mask is refused.
|
||||
assert!(os(&mut e, "000AAAAAS", "CBAN ADD #warez* piracy").iter().any(|a| matches!(a, NetAction::AddLine { kind, mask, .. } if kind == "CBAN" && mask == "#warez*")), "cban added");
|
||||
assert!(os(&mut e, "000AAAAAS", "CBAN ADD #* everything").iter().any(|a| matches!(a, NetAction::Notice { text, .. } if text.contains("too wide"))), "wildcard channel refused");
|
||||
|
||||
// GLOBAL fans out to every user via the $* server glob.
|
||||
let out = os(&mut e, "000AAAAAS", "GLOBAL rebooting in 5");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue