Answer WHOIS IDLE for our pseudo-clients
All checks were successful
CI / check (push) Successful in 3m54s

A two-parameter WHOIS (/whois x x — what mIRC sends when you double-click a nick,
to fetch idle time) routes the query to the target's server. For a service that
server is echo, which was dropping the resulting IDLE request as an unknown
command, so the WHOIS produced no output at all: InspIRCd only emits the whois
numerics once the IDLE reply arrives (m_spanningtree/idle.cpp), so no reply meant
total silence.

Handle the inbound IDLE request and answer it for our own services and bots:
":<target> IDLE <requester> <signon> <idle>", signon = burst time, idle 0 (a
pseudo-client is always active). A routed WHOIS of NickServ/ChanServ/etc. now
completes.
This commit is contained in:
Jean Chevronnet 2026-07-16 23:22:35 +00:00
parent 4e12531815
commit 3f11f77a81
No known key found for this signature in database
4 changed files with 59 additions and 0 deletions

View file

@ -78,6 +78,17 @@ impl Protocol for InspIrcd {
.to_string();
vec![NetEvent::Ping { token, from: source }]
}
"IDLE" => {
// `:<requester> IDLE <target>` — the idle half of a routed WHOIS.
// Multi-param IDLE is a reply (never sent to us); ignore it.
let target = tokens.next().unwrap_or("").to_string();
match source {
Some(requester) if !target.is_empty() && tokens.next().is_none() => {
vec![NetEvent::Idle { requester, target }]
}
_ => vec![],
}
}
"PRIVMSG" => {
let to = tokens.next().unwrap_or("").to_string();
vec![NetEvent::Privmsg {
@ -279,6 +290,11 @@ impl Protocol for InspIrcd {
let dest = from.clone().unwrap_or_else(|| self.sid.clone());
vec![self.sourced(format!("PONG {} {}", dest, token))]
}
// Reply to a routed-WHOIS idle request, sourced from the target client:
// `:<target> IDLE <requester> <signon> <idle>` (m_spanningtree/idle.cpp).
NetAction::IdleReply { target, requester, signon, idle } => {
vec![format!(":{target} IDLE {requester} {signon} {idle}")]
}
// insp4 UID: uuid nickts nick realhost disphost realuser dispuser ip signonts +modes :gecos
// (both a real AND a displayed user field — see m_spanningtree/uid.cpp Builder).
NetAction::IntroduceUser { uid, nick, ident, host, gecos } => vec![self.sourced(format!(