OperServ: SNLINE realname bans
SNLINE ADD [+expiry] <realname-regex> <reason> / DEL / LIST — a realname
R-line the ircd matches against connecting users' realnames. It's just
another kind ("R") in the generalized X-line handler: same event-sourced
list, lazy expiry, and burst re-assertion as AKILL/SQLINE, keyed by
(kind, mask). The mask is kept verbatim (a single-token regex; use . or
\\s for spaces) and an all-wildcard pattern is refused. STATS now counts
it too.
This commit is contained in:
parent
a67409e0d3
commit
78ad909706
4 changed files with 17 additions and 2 deletions
|
|
@ -53,6 +53,7 @@ impl Service for OperServ {
|
|||
match args.first().copied() {
|
||||
Some(cmd) if cmd.eq_ignore_ascii_case("AKILL") => xline::AKILL.handle(me, from, args, ctx, db),
|
||||
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("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),
|
||||
|
|
@ -72,5 +73,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), \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).");
|
||||
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), \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).");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ pub fn handle(me: &str, from: &Sender, db: &mut dyn Store, ctx: &mut ServiceCtx)
|
|||
let akills = db.akills();
|
||||
let glines = akills.iter().filter(|a| a.kind == "G").count();
|
||||
let qlines = akills.iter().filter(|a| a.kind == "Q").count();
|
||||
let rlines = akills.iter().filter(|a| a.kind == "R").count();
|
||||
let ignores = db.ignores().len();
|
||||
ctx.notice(me, from.uid, format!("Live network bans: \x02{glines}\x02 AKILL, \x02{qlines}\x02 SQLINE. Services ignores: \x02{ignores}\x02."));
|
||||
ctx.notice(me, from.uid, format!("Live network bans: \x02{glines}\x02 AKILL, \x02{qlines}\x02 SQLINE, \x02{rlines}\x02 SNLINE. Services ignores: \x02{ignores}\x02."));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,6 +119,10 @@ pub const AKILL: Xline = Xline { kind: "G", name: "AKILL", target: "user@host",
|
|||
// SQLINE: a nick Q-line. The mask is a nick glob (no '@'); wildcards allowed.
|
||||
pub const SQLINE: Xline = Xline { kind: "Q", name: "SQLINE", target: "nick", normalize: norm_nick };
|
||||
|
||||
// SNLINE: a realname R-line. The mask is a regex the ircd matches against a
|
||||
// connecting user's realname (use `.` for spaces, e.g. `.*free.money.*`).
|
||||
pub const SNLINE: Xline = Xline { kind: "R", name: "SNLINE", target: "realname-regex", normalize: norm_realname };
|
||||
|
||||
fn norm_userhost(input: &str) -> Option<String> {
|
||||
let body = input.rsplit('!').next().unwrap_or(input);
|
||||
let (user, host) = body.split_once('@')?;
|
||||
|
|
@ -137,6 +141,12 @@ fn norm_nick(input: &str) -> Option<String> {
|
|||
Some(input.to_ascii_lowercase())
|
||||
}
|
||||
|
||||
fn norm_realname(input: &str) -> Option<String> {
|
||||
// A realname regex the ircd evaluates: keep it verbatim (case matters), just
|
||||
// require it non-empty. A single token — use `.`/`\s` for spaces.
|
||||
(!input.is_empty()).then(|| input.to_string())
|
||||
}
|
||||
|
||||
// A mask whose every meaningful character is a wildcard would match nearly all.
|
||||
fn too_wide(mask: &str) -> bool {
|
||||
let trivial = |s: &str| s.chars().all(|c| c == '*' || c == '?' || c == '.' || c == '@');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue