From 914c3874ce4ab73325b2dd4ce883756d5ed24fc4 Mon Sep 17 00:00:00 2001 From: Jean Date: Sun, 12 Jul 2026 12:30:16 +0000 Subject: [PATCH] nickserv: accept IDENTIFY [account] IDENTIFY now takes an optional leading account name, so logging into an account whose name differs from your current nick works. Without it, the account still defaults to the current nick as before. --- modules/nickserv/nickserv.rs | 18 ++++++++++++------ src/engine/mod.rs | 19 +++++++++++++++++++ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/modules/nickserv/nickserv.rs b/modules/nickserv/nickserv.rs index fb2af92..f9359c6 100644 --- a/modules/nickserv/nickserv.rs +++ b/modules/nickserv/nickserv.rs @@ -39,12 +39,18 @@ impl Service for NickServ { nick: from.nick.to_string(), }); } - Some("IDENTIFY") => { - let Some(password) = args.get(1) else { - ctx.notice(me, from.uid, "Syntax: IDENTIFY "); - return; + Some("IDENTIFY") | Some("ID") => { + // IDENTIFY [account] — the account defaults to the + // current nick, so both the bare-password and account+password forms work. + let (account_name, password) = match (args.get(1), args.get(2)) { + (Some(account), Some(password)) => (*account, *password), + (Some(password), None) => (from.nick, *password), + _ => { + ctx.notice(me, from.uid, "Syntax: IDENTIFY [account] "); + return; + } }; - match db.authenticate(from.nick, password) { + match db.authenticate(account_name, password) { Some(account) => { // Already identified to this account: don't re-fire the login. if from.account == Some(account) { @@ -75,7 +81,7 @@ impl Service for NickServ { Some("HELP") => ctx.notice( me, from.uid, - "NickServ looks after your nickname. Commands: \x02REGISTER\x02 [email], \x02IDENTIFY\x02 , \x02LOGOUT\x02, \x02CERT\x02 ADD|DEL|LIST [fingerprint].", + "NickServ looks after your nickname. Commands: \x02REGISTER\x02 [email], \x02IDENTIFY\x02 [account] , \x02LOGOUT\x02, \x02CERT\x02 ADD|DEL|LIST [fingerprint].", ), Some(other) => ctx.notice(me, from.uid, format!("I don't know the command \x02{other}\x02. Try \x02HELP\x02.")), None => {} diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 2c77531..f8980bb 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -978,6 +978,25 @@ mod tests { assert!(out.iter().any(|a| matches!(a, NetAction::Metadata { key, value, .. } if key == "accountname" && value == "realnick")), "must log into the current nick's account: {out:?}"); } + // IDENTIFY accepts the two-arg account+password form: log into a named + // account even when the current nick differs; a wrong password is refused. + #[test] + fn identify_two_arg_account_form() { + let mut e = engine_with("twoarg", "realacct", "sesame"); + e.handle(NetEvent::UserConnect { uid: "000AAAAAB".into(), nick: "someguest".into(), host: "host.example".into() }); + let out = e.handle(NetEvent::Privmsg { + from: "000AAAAAB".into(), to: "42SAAAAAA".into(), text: "IDENTIFY realacct sesame".into(), + }); + assert!(out.iter().any(|a| matches!(a, NetAction::Metadata { key, value, .. } if key == "accountname" && value == "realacct")), "two-arg identify logs into the named account: {out:?}"); + + e.handle(NetEvent::UserConnect { uid: "000AAAAAC".into(), nick: "other".into(), host: "host.example".into() }); + let bad = e.handle(NetEvent::Privmsg { + from: "000AAAAAC".into(), to: "42SAAAAAA".into(), text: "IDENTIFY realacct wrongpw".into(), + }); + assert!(bad.iter().any(|a| matches!(a, NetAction::Notice { text, .. } if text.contains("Invalid password"))), "{bad:?}"); + assert!(!bad.iter().any(|a| matches!(a, NetAction::Metadata { .. })), "wrong password must not log in: {bad:?}"); + } + // ChanServ: registration needs identification, INFO shows the founder, and // only the founder can drop. #[test]