From af5896743d5b5b79e127c4ca00a920837868b2b1 Mon Sep 17 00:00:00 2001 From: Jean Date: Sun, 19 Jul 2026 04:49:31 +0000 Subject: [PATCH] Restore the real ident when a HostServ ident@host vhost is turned off or deleted --- api/src/lib.rs | 9 +++++++++ modules/hostserv/src/del.rs | 9 +++++++++ modules/hostserv/src/off.rs | 12 ++++++++++-- src/engine/state.rs | 7 +++++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/api/src/lib.rs b/api/src/lib.rs index 071c980..5b5c8af 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -640,6 +640,13 @@ impl ServiceCtx { }); } + pub fn set_ident(&mut self, uid: &str, ident: &str) { + self.actions.push(NetAction::SetIdent { + uid: uid.to_string(), + ident: ident.to_string(), + }); + } + // Apply a vhost spec to a user: `ident@host` sets both the ident and host, // a bare `host` just the host. pub fn apply_vhost(&mut self, uid: &str, spec: &str) { @@ -2142,6 +2149,8 @@ pub trait NetView { fn uid_by_nick(&self, nick: &str) -> Option<&str>; fn nick_of(&self, uid: &str) -> Option<&str>; fn host_of(&self, uid: &str) -> Option<&str>; + // A user's real ident (username), for restoring after an `ident@host` vhost. + fn ident_of(&self, uid: &str) -> Option<&str>; fn account_of(&self, uid: &str) -> Option<&str>; // A user's identity for extban / AKICK matching, if known. fn ban_target(&self, uid: &str) -> Option>; diff --git a/modules/hostserv/src/del.rs b/modules/hostserv/src/del.rs index f15f6c8..2402b6c 100644 --- a/modules/hostserv/src/del.rs +++ b/modules/hostserv/src/del.rs @@ -10,6 +10,9 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: ctx.notice(me, from.uid, "Syntax: DEL "); return; }; + // Note whether the vhost spoofed the ident (`ident@host`) before removing it, + // so online sessions get their real ident back too, not just their host. + let had_ident = db.vhost(account).is_some_and(|v| v.host.contains('@')); match db.del_vhost(account) { Ok(true) => { for uid in net.uids_logged_into(account) { @@ -17,6 +20,12 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: let host = host.to_string(); ctx.set_host(&uid, &host); } + if had_ident { + if let Some(ident) = net.ident_of(&uid) { + let ident = ident.to_string(); + ctx.set_ident(&uid, &ident); + } + } } ctx.notice(me, from.uid, format!("Vhost for \x02{account}\x02 removed.")); } diff --git a/modules/hostserv/src/off.rs b/modules/hostserv/src/off.rs index 87aab27..e509eb0 100644 --- a/modules/hostserv/src/off.rs +++ b/modules/hostserv/src/off.rs @@ -7,14 +7,22 @@ pub fn handle(me: &str, from: &Sender, ctx: &mut ServiceCtx, net: &dyn NetView, ctx.notice(me, from.uid, "You need to identify to NickServ first."); return; }; - if db.vhost(account).is_none() { + let Some(vhost) = db.vhost(account) else { ctx.notice(me, from.uid, "You have no vhost assigned."); return; - } + }; match net.host_of(from.uid) { Some(host) => { let host = host.to_string(); ctx.set_host(from.uid, &host); + // An `ident@host` vhost spoofed the ident too; restore the real one, or + // the user keeps the vhost's username for the rest of the session. + if vhost.host.contains('@') { + if let Some(ident) = net.ident_of(from.uid) { + let ident = ident.to_string(); + ctx.set_ident(from.uid, &ident); + } + } ctx.notice(me, from.uid, "Your vhost is off; your normal host is restored."); } None => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."), diff --git a/src/engine/state.rs b/src/engine/state.rs index f0eb150..88454c1 100644 --- a/src/engine/state.rs +++ b/src/engine/state.rs @@ -249,6 +249,10 @@ impl Network { self.users.get(uid).map(|u| u.host.as_str()) } + pub fn ident_of(&self, uid: &str) -> Option<&str> { + self.users.get(uid).map(|u| u.ident.as_str()) + } + // A user's identity for extban / ban matching (nick, ident, hosts, ip, gecos, // account). `None` if we don't know the uid. pub fn ban_target<'a>(&'a self, uid: &str) -> Option> { @@ -577,6 +581,9 @@ impl NetView for Network { fn host_of(&self, uid: &str) -> Option<&str> { Network::host_of(self, uid) } + fn ident_of(&self, uid: &str) -> Option<&str> { + Network::ident_of(self, uid) + } fn account_of(&self, uid: &str) -> Option<&str> { Network::account_of(self, uid) }