feat(irc): SASL PLAIN login and IRCv3 server-time

Registration now opens with CAP LS 302 (servers without IRCv3 ignore it
and register as before). From the offered capabilities (multi-line LS
lists included) the client requests server-time and, when the profile
enables it, sasl; then AUTHENTICATE PLAIN with the account (defaulting
to the nick) and a stored SASL password (irc-sasl-password), base64 in
400-byte chunks, and CAP END on success or failure — failures are shown
on the Server tab and the connection carries on (NickServ still works).
server-time tags stamp events with the server's clock, so replayed
history shows real times.

Tested against the fake IRC server: multi-line LS, REQ/ACK, the PLAIN
exchange, 903, CAP END, and a tagged message's timestamp.
This commit is contained in:
Andy Dixon
2026-10-12 19:11:16 +01:00
parent 0fc9451583
commit 32890c6415
7 changed files with 159 additions and 6 deletions
+9
View File
@@ -143,6 +143,13 @@ impl Manager {
} else {
String::new()
};
let (sasl_account, sasl_password) = if ic.sasl {
let account = if ic.sasl_account.trim().is_empty() { ic.nick.clone() } else { ic.sasl_account.trim().to_string() };
let pw = self.secret_fn(id, &p.id, SecretKind::IrcSaslPassword, ic.sasl_mode, prompt.clone())().map_err(|e| format!("SASL password: {e}"))?;
(account, pw.to_string())
} else {
(String::new(), String::new())
};
let mut rooms = ic.auto_join_rooms.clone();
if !p.id.is_empty() {
let last = self.store.irc_last_rooms(&p.id).map_err(|e| format!("loading remembered IRC rooms: {e}"))?;
@@ -206,6 +213,8 @@ impl Manager {
nickserv_identify: ic.nick_serv_identify,
nickserv_account: ic.nick_serv_account.clone(),
nickserv_password: ns_pass,
sasl_account,
sasl_password,
connector,
connect_timeout: Duration::from_secs(p.connect_timeout.max(1) as u64),
on_event,