modules: port maphide and tline

This commit is contained in:
Jean Chevronnet 2026-08-09 20:06:54 +00:00
parent 140554719b
commit 3186af07e8
3 changed files with 111 additions and 0 deletions

44
src/modules/maphide.rs Normal file
View file

@ -0,0 +1,44 @@
//! maphide — hide the server map (`LINKS` / `MAP`) from ordinary users, so the
//! network topology isn't exposed to non-operators. Off unless `maphide = yes`.
//!
//! Behaviour reference: InspIRCd's `m_maphide`. Original native Rust.
use crate::module::{ModResult, Module};
use crate::server::Server;
use crate::Uid;
pub struct MapHide;
impl Module for MapHide {
fn name(&self) -> &'static str {
"maphide"
}
fn on_pre_command(
&mut self,
srv: &mut Server,
uid: Uid,
cmd: &str,
_params: &[String],
) -> ModResult {
if !srv.conf_bool("maphide", false) || srv.is_oper(uid) {
return ModResult::Passthru;
}
if cmd.eq_ignore_ascii_case("LINKS") || cmd.eq_ignore_ascii_case("MAP") {
let nick = srv
.users
.get(&uid)
.map(|u| u.nick.clone())
.unwrap_or_default();
srv.send(
uid,
format!(
":{} NOTICE {nick} :The server map is hidden; ask an operator.",
srv.name
),
);
return ModResult::Deny;
}
ModResult::Passthru
}
}

View file

@ -27,6 +27,7 @@ pub mod hidewhois;
pub mod irccloudtags; pub mod irccloudtags;
pub mod jsonlog; pub mod jsonlog;
pub mod jwt; pub mod jwt;
pub mod maphide;
pub mod markread; pub mod markread;
pub mod metadata; pub mod metadata;
pub mod multiline; pub mod multiline;
@ -45,6 +46,7 @@ pub mod securelist;
pub mod securitygroups; pub mod securitygroups;
pub mod serverban; pub mod serverban;
pub mod snoop; pub mod snoop;
pub mod tline;
pub mod whoisport; pub mod whoisport;
use crate::command::Command; use crate::command::Command;
@ -76,6 +78,7 @@ pub fn default_modules() -> Vec<Box<dyn Module>> {
Box::new(irccloudtags::IrcCloudTags), Box::new(irccloudtags::IrcCloudTags),
Box::new(randquote::RandQuote), Box::new(randquote::RandQuote),
Box::new(disable::Disable), Box::new(disable::Disable),
Box::new(maphide::MapHide),
] ]
} }
@ -105,5 +108,6 @@ pub fn module_commands() -> Vec<Box<dyn Command>> {
.chain(extjwt::commands()) .chain(extjwt::commands())
.chain(filehost::commands()) .chain(filehost::commands())
.chain(extended_isupport::commands()) .chain(extended_isupport::commands())
.chain(tline::commands())
.collect() .collect()
} }

63
src/modules/tline.rs Normal file
View file

@ -0,0 +1,63 @@
//! tline — `TLINE <mask>`, an oper command that reports how many currently-connected
//! local users a would-be K/G/Z-line mask matches, so you can gauge the blast radius
//! before actually setting the ban.
//!
//! Behaviour reference: InspIRCd's `m_tline`. Original native Rust.
use crate::channels::glob_match;
use crate::command::{CmdResult, Command};
use crate::numeric::ERR_NOPRIVILEGES;
use crate::server::Server;
use crate::Uid;
pub fn commands() -> Vec<Box<dyn Command>> {
vec![Box::new(TLine)]
}
struct TLine;
impl Command for TLine {
fn name(&self) -> &'static str {
"TLINE"
}
fn min_params(&self) -> usize {
1
}
fn handle(&self, s: &mut Server, uid: Uid, params: &[String]) -> CmdResult {
if !s.is_oper(uid) {
s.numeric(
uid,
ERR_NOPRIVILEGES,
":Permission Denied- You're not an IRC operator",
);
return CmdResult::Fail;
}
let mask = &params[0];
let total = s.users.len();
let matched = s
.users
.values()
.filter(|u| {
let forms = [
format!("{}!{}@{}", u.nick, u.ident, u.host_display()),
format!("{}@{}", u.ident, u.host),
format!("{}@{}", u.ident, u.addr.ip()),
];
forms.iter().any(|f| glob_match(mask, f))
})
.count();
let pct = (matched * 100).checked_div(total).unwrap_or(0);
let nick = s
.users
.get(&uid)
.map(|u| u.nick.clone())
.unwrap_or_default();
s.send(
uid,
format!(
":{} NOTICE {nick} :*** TLINE: {mask} matches {matched} of {total} local users ({pct}%)",
s.name
),
);
CmdResult::Ok
}
}