Scaffold the federated services daemon
Protocol-agnostic core: a Protocol trait maps raw server-to-server lines to a normalized event/action model, so the engine never touches a raw line and a new ircd is one new module. Adds event-log state (state is a fold over the log) and a service framework. First protocol impl links to an InspIRCd (insp4) uplink — completes the CAPAB/SERVER handshake, bursts, and introduces NickServ.
This commit is contained in:
commit
f82ab0c70a
15 changed files with 890 additions and 0 deletions
104
src/proto/inspircd.rs
Normal file
104
src/proto/inspircd.rs
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
// InspIRCd spanning-tree link protocol. Handshake + UID + PING mirror the
|
||||
// sequence in Network-Links (protocols/inspircd.py); mode/burst details get
|
||||
// firmed up against a live insp4 uplink.
|
||||
use super::{NetAction, NetEvent, Protocol};
|
||||
|
||||
pub struct InspIrcd {
|
||||
sid: String,
|
||||
name: String,
|
||||
password: String,
|
||||
protocol: u32,
|
||||
ts: u64,
|
||||
}
|
||||
|
||||
impl InspIrcd {
|
||||
pub fn new(name: String, sid: String, password: String, protocol: u32, ts: u64) -> Self {
|
||||
Self { sid, name, password, protocol, ts }
|
||||
}
|
||||
|
||||
fn from_us(&self, cmd: String) -> String {
|
||||
format!(":{} {}", self.sid, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
impl Protocol for InspIrcd {
|
||||
fn handshake(&mut self) -> Vec<String> {
|
||||
vec![
|
||||
format!("CAPAB START {}", self.protocol),
|
||||
format!("CAPAB CAPABILITIES :PROTOCOL={}", self.protocol),
|
||||
"CAPAB END".to_string(),
|
||||
format!("SERVER {} {} {} :{}", self.name, self.password, self.sid, "Federated Services"),
|
||||
]
|
||||
}
|
||||
|
||||
fn parse(&mut self, line: &str) -> Vec<NetEvent> {
|
||||
let (source, rest) = match line.strip_prefix(':') {
|
||||
Some(s) => {
|
||||
let mut it = s.splitn(2, ' ');
|
||||
(Some(it.next().unwrap_or("").to_string()), it.next().unwrap_or(""))
|
||||
}
|
||||
None => (None, line),
|
||||
};
|
||||
let mut tokens = rest.split(' ');
|
||||
let cmd = match tokens.next() {
|
||||
Some(c) => c,
|
||||
None => return vec![],
|
||||
};
|
||||
match cmd.to_ascii_uppercase().as_str() {
|
||||
"SERVER" => vec![NetEvent::Registered],
|
||||
"ENDBURST" => vec![NetEvent::EndBurst],
|
||||
"PING" => {
|
||||
let token = tokens
|
||||
.next()
|
||||
.unwrap_or("")
|
||||
.trim_start_matches(':')
|
||||
.to_string();
|
||||
vec![NetEvent::Ping { token, from: source }]
|
||||
}
|
||||
"PRIVMSG" => {
|
||||
let to = tokens.next().unwrap_or("").to_string();
|
||||
vec![NetEvent::Privmsg {
|
||||
from: source.unwrap_or_default(),
|
||||
to,
|
||||
text: trailing(rest),
|
||||
}]
|
||||
}
|
||||
"QUIT" => vec![NetEvent::Quit { uid: source.unwrap_or_default() }],
|
||||
_ => vec![NetEvent::Unknown { line: line.to_string() }],
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize(&mut self, action: &NetAction) -> Vec<String> {
|
||||
match action {
|
||||
NetAction::Burst => vec![self.from_us(format!("BURST {}", self.ts))],
|
||||
NetAction::EndBurst => vec![self.from_us("ENDBURST".to_string())],
|
||||
NetAction::Pong { token, from } => {
|
||||
let dest = from.clone().unwrap_or_else(|| self.sid.clone());
|
||||
vec![self.from_us(format!("PONG {} {}", dest, token))]
|
||||
}
|
||||
NetAction::IntroduceUser { uid, nick, ident, host, gecos } => vec![self.from_us(format!(
|
||||
"UID {uid} {ts} {nick} {host} {host} {ident} 0.0.0.0 {ts} +ioS :{gecos}",
|
||||
uid = uid, ts = self.ts, nick = nick, host = host, ident = ident, gecos = gecos
|
||||
))],
|
||||
NetAction::Privmsg { from, to, text } => {
|
||||
vec![format!(":{} PRIVMSG {} :{}", from, to, text)]
|
||||
}
|
||||
NetAction::Notice { from, to, text } => {
|
||||
vec![format!(":{} NOTICE {} :{}", from, to, text)]
|
||||
}
|
||||
NetAction::Raw(s) => vec![s.clone()],
|
||||
}
|
||||
}
|
||||
|
||||
fn sid(&self) -> &str {
|
||||
&self.sid
|
||||
}
|
||||
}
|
||||
|
||||
// The IRC "trailing" argument: everything after the first " :", else the last word.
|
||||
fn trailing(rest: &str) -> String {
|
||||
match rest.find(" :") {
|
||||
Some(i) => rest[i + 2..].to_string(),
|
||||
None => rest.rsplit(' ').next().unwrap_or("").to_string(),
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue