diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 1fc2777..288343c 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -3,7 +3,7 @@ pub mod service; pub mod state; use crate::proto::{NetAction, NetEvent}; -use db::Db; +use db::{Db, RegError}; use service::{Sender, Service, ServiceCtx}; use state::Network; @@ -46,10 +46,35 @@ impl Engine { Vec::new() } NetEvent::Privmsg { from, to, text } => self.dispatch(&from, &to, &text), + NetEvent::AccountRequest { reqid, kind, account, p2, p3, .. } => { + self.account_request(reqid, kind, account, p2, p3) + } _ => Vec::new(), } } + // Authority side of the IRCv3 account-registration relay: create the account + // (same store as classic NickServ REGISTER) and answer the requesting ircd. + fn account_request(&mut self, reqid: String, kind: String, account: String, p2: String, p3: String) -> Vec { + if !kind.eq_ignore_ascii_case("REGISTER") { + return Vec::new(); // VERIFY / RESEND / STATUS: later + } + let email = if p2.is_empty() || p2 == "*" { None } else { Some(p2) }; + let (status, code, message) = match self.db.register(&account, &p3, email) { + Ok(()) => ("success", "*", "Account registered."), + Err(RegError::Exists) => ("error", "ACCOUNT_EXISTS", "That account name is already registered."), + Err(RegError::Internal) => ("error", "TEMPORARILY_UNAVAILABLE", "Registration is unavailable, try again later."), + }; + vec![NetAction::AccountResponse { + reqid, + kind, + account, + status: status.to_string(), + code: code.to_string(), + message: message.to_string(), + }] + } + // Route a PRIVMSG addressed to a service (by uid or nick) into that service, // handing it the sender's resolved nick and the account store. fn dispatch(&mut self, from: &str, to: &str, text: &str) -> Vec { diff --git a/src/proto/inspircd.rs b/src/proto/inspircd.rs index f2f4f04..166a202 100644 --- a/src/proto/inspircd.rs +++ b/src/proto/inspircd.rs @@ -81,6 +81,23 @@ impl Protocol for InspIrcd { } } "QUIT" => vec![NetEvent::Quit { uid: source.unwrap_or_default() }], + // account-registration relay from an ircd: + // ACCTREGISTER : + "ACCTREGISTER" => { + let a: Vec<&str> = tokens.by_ref().take(5).collect(); + if a.len() == 5 { + vec![NetEvent::AccountRequest { + reqid: a[0].to_string(), + origin: a[1].to_string(), + kind: a[2].to_string(), + account: a[3].to_string(), + p2: a[4].to_string(), + p3: trailing(rest), + }] + } else { + vec![] + } + } _ => vec![NetEvent::Unknown { line: line.to_string() }], } } @@ -105,6 +122,13 @@ impl Protocol for InspIrcd { NetAction::Notice { from, to, text } => { vec![format!(":{} NOTICE {} :{}", from, to, text)] } + // ACCTREGRESULT : + NetAction::AccountResponse { reqid, kind, account, status, code, message } => { + vec![self.from_us(format!( + "ACCTREGRESULT {} {} {} {} {} :{}", + reqid, kind, account, status, code, message + ))] + } NetAction::Raw(s) => vec![s.clone()], } } diff --git a/src/proto/mod.rs b/src/proto/mod.rs index 96ac572..4bdd21d 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -12,6 +12,8 @@ pub enum NetEvent { Privmsg { from: String, to: String, text: String }, UserConnect { uid: String, nick: String }, Quit { uid: String }, + // An ircd relaying an IRCv3 account-registration request to us as the authority. + AccountRequest { reqid: String, origin: String, kind: String, account: String, p2: String, p3: String }, Unknown { line: String }, } @@ -24,6 +26,7 @@ pub enum NetAction { IntroduceUser { uid: String, nick: String, ident: String, host: String, gecos: String }, Privmsg { from: String, to: String, text: String }, Notice { from: String, to: String, text: String }, + AccountResponse { reqid: String, kind: String, account: String, status: String, code: String, message: String }, Raw(String), }