akick: match extbans (account/realname/realmask/unauthed) using the ident, real host and real name the UID carries but was discarding
All checks were successful
CI / check (push) Successful in 4m0s

This commit is contained in:
Jean Chevronnet 2026-07-17 19:47:34 +00:00
parent bc4fada354
commit 426fc694a2
No known key found for this signature in database
8 changed files with 202 additions and 26 deletions

View file

@ -2,7 +2,9 @@ use echo_api::Store;
use echo_api::{Sender, ServiceCtx};
// AKICK <#channel> ADD <mask> [reason] | DEL <mask> | LIST
// Masks are nick!user@host globs; matching users are banned and kicked on join.
// Masks are nick!user@host globs or an extban echo can match: account:<glob>,
// realname:<glob>, realmask:<host>+<realname>, or unauthed:<host>. Matching users
// are banned and kicked on join.
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
let Some(&chan) = args.get(1) else {
ctx.notice(me, from.uid, "Syntax: AKICK <#channel> ADD <mask> [reason] | DEL <mask> | LIST");
@ -24,6 +26,12 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
ctx.notice(me, from.uid, "Syntax: AKICK <#channel> ADD <mask> [reason]");
return;
};
// Reject extbans echo has no per-user data to match (country, class, …),
// so we never store an akick that can't be enforced.
if let echo_api::AkickMask::Other(name) = echo_api::AkickMask::parse(mask) {
ctx.notice(me, from.uid, format!("Can't auto-kick by the \x02{name}\x02 extban. Use a host mask or \x02account:\x02 / \x02realname:\x02 / \x02realmask:\x02 / \x02unauthed:\x02."));
return;
}
if !super::require_op(me, from, chan, ctx, db) {
return;
}

View file

@ -21,9 +21,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net:
match net.account_of(&uid).and_then(|a| info.join_mode(a)) {
Some(m) => ctx.channel_mode(me, chan, &status_mode(m, &uid)),
None => {
let nick = net.nick_of(&uid).unwrap_or("*");
let host = net.host_of(&uid).unwrap_or("*");
if let Some(k) = info.akick_match(&format!("{nick}!*@{host}")) {
if let Some(k) = net.ban_target(&uid).and_then(|t| info.akick_match(&t)) {
ctx.channel_mode(me, chan, &format!("+b {}", k.mask));
let reason = if k.reason.is_empty() { "You are banned from this channel." } else { &k.reason };
ctx.kick(me, chan, &uid, reason);