Restore the real ident when a HostServ ident@host vhost is turned off or deleted

This commit is contained in:
Jean Chevronnet 2026-07-19 04:49:31 +00:00
parent 384f222a22
commit af5896743d
No known key found for this signature in database
4 changed files with 35 additions and 2 deletions

View file

@ -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<BanTarget<'_>>;

View file

@ -10,6 +10,9 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net:
ctx.notice(me, from.uid, "Syntax: DEL <account>");
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."));
}

View file

@ -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."),

View file

@ -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<echo_api::BanTarget<'a>> {
@ -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)
}