modules: extract fedserv-api leaf crate (protocol + service vocabulary)
Move NetEvent/NetAction/RegReply/Protocol and Sender/ServiceCtx into a dependency-free fedserv-api crate that a module builds against. Core keeps referring to them through re-export shims at crate::proto and crate::engine::service, so no consumer changes. Groundwork for external service and protocol modules.
This commit is contained in:
parent
d99eb16dab
commit
931b8727e9
6 changed files with 249 additions and 211 deletions
5
Cargo.lock
generated
5
Cargo.lock
generated
|
|
@ -284,6 +284,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argon2",
|
"argon2",
|
||||||
"base64",
|
"base64",
|
||||||
|
"fedserv-api",
|
||||||
"hmac",
|
"hmac",
|
||||||
"pbkdf2",
|
"pbkdf2",
|
||||||
"prost",
|
"prost",
|
||||||
|
|
@ -302,6 +303,10 @@ dependencies = [
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fedserv-api"
|
||||||
|
version = "0.0.1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "find-msvc-tools"
|
name = "find-msvc-tools"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,6 @@
|
||||||
|
[workspace]
|
||||||
|
members = ["api"]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "fedserv"
|
name = "fedserv"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
|
@ -5,6 +8,7 @@ edition = "2021"
|
||||||
description = "Federated IRC services daemon (protocol-agnostic core, InspIRCd link first)"
|
description = "Federated IRC services daemon (protocol-agnostic core, InspIRCd link first)"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
fedserv-api = { path = "api" }
|
||||||
tokio = { version = "1", features = ["net", "io-util", "rt-multi-thread", "macros", "time", "sync", "process"] }
|
tokio = { version = "1", features = ["net", "io-util", "rt-multi-thread", "macros", "time", "sync", "process"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|
|
||||||
7
api/Cargo.toml
Normal file
7
api/Cargo.toml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
[package]
|
||||||
|
name = "fedserv-api"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = "2021"
|
||||||
|
description = "Stable module SDK: the traits and vocabulary a service or protocol module builds against."
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
225
api/src/lib.rs
Normal file
225
api/src/lib.rs
Normal file
|
|
@ -0,0 +1,225 @@
|
||||||
|
//! The module SDK. A service or protocol module depends only on this crate:
|
||||||
|
//! it carries the traits a module implements and the normalized vocabulary the
|
||||||
|
//! engine speaks, with no storage or runtime dependencies of its own.
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Protocol vocabulary
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Normalized inbound facts, translated from the uplink's raw lines.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum NetEvent {
|
||||||
|
Registered,
|
||||||
|
EndBurst,
|
||||||
|
Ping { token: String, from: Option<String> },
|
||||||
|
Privmsg { from: String, to: String, text: String },
|
||||||
|
UserConnect { uid: String, nick: String, host: String },
|
||||||
|
NickChange { uid: String, nick: String },
|
||||||
|
// A channel was created or bursted (an FJOIN). Subsequent single joins arrive
|
||||||
|
// as IJOIN and are not surfaced.
|
||||||
|
ChannelCreate { channel: String },
|
||||||
|
// A user joined a channel (an FJOIN member or an IJOIN), for auto-op. `op` is
|
||||||
|
// whether they hold channel-operator status at that point (an FJOIN prefix).
|
||||||
|
Join { uid: String, channel: String, op: bool },
|
||||||
|
// A user left a channel (PART) or was removed (KICK), for membership tracking.
|
||||||
|
Part { uid: String, channel: String },
|
||||||
|
// A user's channel-operator status changed (FMODE +o/-o), for live op tracking.
|
||||||
|
ChannelOp { channel: String, uid: String, op: bool },
|
||||||
|
// A channel's modes changed (FMODE), for enforcing mode locks. Our own
|
||||||
|
// changes are filtered out by the protocol layer.
|
||||||
|
ChannelModeChange { channel: String, modes: String },
|
||||||
|
// A channel's key (+k/-k) changed, tracked so GETKEY can report it.
|
||||||
|
ChannelKey { channel: String, key: Option<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 },
|
||||||
|
// An ircd relaying a SASL exchange step to us (the SASL agent). mode = H/S/C/D.
|
||||||
|
Sasl { client: String, agent: String, mode: String, data: Vec<String> },
|
||||||
|
Unknown { line: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalized outbound intents the engine wants performed on the network.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum NetAction {
|
||||||
|
Burst,
|
||||||
|
EndBurst,
|
||||||
|
Pong { token: String, from: Option<String> },
|
||||||
|
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 },
|
||||||
|
// A SASL exchange step back to the ircd, sourced from our SASL agent. mode = C/D.
|
||||||
|
Sasl { agent: String, client: String, mode: String, data: Vec<String> },
|
||||||
|
// Publish network state to the uplink: target "*" is server-global (e.g. the
|
||||||
|
// advertised SASL mechanism list), otherwise a user uid (e.g. their account).
|
||||||
|
Metadata { target: String, key: String, value: String },
|
||||||
|
// Force a user's nick (SVSNICK), e.g. renaming to a guest nick on logout.
|
||||||
|
// The protocol stamps the new nick's timestamp.
|
||||||
|
ForceNick { uid: String, nick: String },
|
||||||
|
// Set channel modes from services, e.g. +r on a registered channel. `from` is
|
||||||
|
// the pseudoclient uid to source it from (empty = the services server). The
|
||||||
|
// protocol stamps a timestamp the ircd will accept.
|
||||||
|
ChannelMode { from: String, channel: String, modes: String },
|
||||||
|
// Kick a user from a channel, sourced from pseudoclient `from`.
|
||||||
|
Kick { from: String, channel: String, uid: String, reason: String },
|
||||||
|
// Set a channel's topic, sourced from pseudoclient `from`.
|
||||||
|
Topic { from: String, channel: String, topic: String },
|
||||||
|
// Invite a user to a channel, sourced from pseudoclient `from`.
|
||||||
|
Invite { from: String, uid: String, channel: String },
|
||||||
|
Raw(String),
|
||||||
|
// Internal only, never serialized to the wire: a registration whose password
|
||||||
|
// still needs its (expensive) key derivation. The link layer runs the
|
||||||
|
// derivation off-thread, then calls Engine::complete_register.
|
||||||
|
DeferRegister { account: String, password: String, email: Option<String>, reply: RegReply },
|
||||||
|
// Internal only: a password change awaiting the same off-thread derivation.
|
||||||
|
// The link layer derives, then calls Engine::complete_password_change.
|
||||||
|
DeferPassword { account: String, password: String, agent: String, uid: String },
|
||||||
|
// Internal only: send an email (plaintext + optional HTML). The link layer
|
||||||
|
// pipes it to the configured mail command off-thread; never serialized.
|
||||||
|
SendEmail { to: String, subject: String, text: String, html: Option<String> },
|
||||||
|
}
|
||||||
|
|
||||||
|
// How to answer a registration once its credentials have been derived.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum RegReply {
|
||||||
|
// IRCv3 account-registration relay: answer the requesting ircd.
|
||||||
|
Relay { reqid: String, kind: String },
|
||||||
|
// NickServ REGISTER: NOTICE the requesting user, logging them in on success.
|
||||||
|
NickServ { agent: String, uid: String, nick: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ircd link layer. The engine only ever sees NetEvent / NetAction; raw
|
||||||
|
// server-to-server lines live entirely behind a Protocol impl, so a new ircd is
|
||||||
|
// one new module and the engine is untouched.
|
||||||
|
pub trait Protocol: Send {
|
||||||
|
/// Lines to send immediately on connect (auth / capability negotiation).
|
||||||
|
fn handshake(&mut self) -> Vec<String>;
|
||||||
|
/// One raw inbound line -> zero or more normalized events.
|
||||||
|
fn parse(&mut self, line: &str) -> Vec<NetEvent>;
|
||||||
|
/// One normalized action -> the raw line(s) that realise it.
|
||||||
|
fn serialize(&mut self, action: &NetAction) -> Vec<String>;
|
||||||
|
/// Our own server id, used as the source prefix for server-sourced lines.
|
||||||
|
fn sid(&self) -> &str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Service vocabulary
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Who sent the command, resolved by the engine (UID + current nick + the
|
||||||
|
// account they are identified to, if any).
|
||||||
|
pub struct Sender<'a> {
|
||||||
|
pub uid: &'a str,
|
||||||
|
pub nick: &'a str,
|
||||||
|
pub account: Option<&'a str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// The intent sink a service writes to. A service never mutates the network or
|
||||||
|
// the store itself; it pushes normalized actions the engine drains and applies.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct ServiceCtx {
|
||||||
|
pub actions: Vec<NetAction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServiceCtx {
|
||||||
|
pub fn notice(&mut self, from: &str, to: &str, text: impl Into<String>) {
|
||||||
|
self.actions.push(NetAction::Notice {
|
||||||
|
from: from.to_string(),
|
||||||
|
to: to.to_string(),
|
||||||
|
text: text.into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hand a registration to the engine to finish: its password derivation runs
|
||||||
|
// off the reactor, then the engine commits it and answers `reply`.
|
||||||
|
pub fn defer_register(&mut self, account: impl Into<String>, password: impl Into<String>, email: Option<String>, reply: RegReply) {
|
||||||
|
self.actions.push(NetAction::DeferRegister {
|
||||||
|
account: account.into(),
|
||||||
|
password: password.into(),
|
||||||
|
email,
|
||||||
|
reply,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send an email (the link layer pipes it to the configured mail command).
|
||||||
|
pub fn send_email(&mut self, to: impl Into<String>, subject: impl Into<String>, text: impl Into<String>, html: Option<String>) {
|
||||||
|
self.actions.push(NetAction::SendEmail { to: to.into(), subject: subject.into(), text: text.into(), html });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hand a password change to the engine to finish: its derivation runs off the
|
||||||
|
// reactor, then the engine commits it and notices `uid`, sourced from `agent`.
|
||||||
|
pub fn defer_password(&mut self, account: impl Into<String>, password: impl Into<String>, agent: impl Into<String>, uid: impl Into<String>) {
|
||||||
|
self.actions.push(NetAction::DeferPassword {
|
||||||
|
account: account.into(),
|
||||||
|
password: password.into(),
|
||||||
|
agent: agent.into(),
|
||||||
|
uid: uid.into(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log a user into an account: sets the accountname the ircd turns into
|
||||||
|
// RPL_LOGGEDIN (900) and exposes to account-tag / WHOX, the same login the
|
||||||
|
// SASL agent applies. Used after a successful REGISTER / IDENTIFY.
|
||||||
|
pub fn login(&mut self, uid: &str, account: &str) {
|
||||||
|
self.actions.push(NetAction::Metadata {
|
||||||
|
target: uid.to_string(),
|
||||||
|
key: "accountname".to_string(),
|
||||||
|
value: account.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log a user out: clearing the accountname the ircd turns into RPL_LOGGEDOUT
|
||||||
|
// (901) and drops from account-tag / WHOX. The inverse of `login`.
|
||||||
|
pub fn logout(&mut self, uid: &str) {
|
||||||
|
self.actions.push(NetAction::Metadata {
|
||||||
|
target: uid.to_string(),
|
||||||
|
key: "accountname".to_string(),
|
||||||
|
value: String::new(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force a user's nick (SVSNICK), e.g. to a guest nick after logout.
|
||||||
|
pub fn force_nick(&mut self, uid: &str, nick: &str) {
|
||||||
|
self.actions.push(NetAction::ForceNick {
|
||||||
|
uid: uid.to_string(),
|
||||||
|
nick: nick.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set channel modes, sourced from pseudoclient `from` (e.g. ChanServ).
|
||||||
|
pub fn channel_mode(&mut self, from: &str, channel: &str, modes: &str) {
|
||||||
|
self.actions.push(NetAction::ChannelMode {
|
||||||
|
from: from.to_string(),
|
||||||
|
channel: channel.to_string(),
|
||||||
|
modes: modes.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kick a user, sourced from pseudoclient `from`.
|
||||||
|
pub fn kick(&mut self, from: &str, channel: &str, uid: &str, reason: &str) {
|
||||||
|
self.actions.push(NetAction::Kick {
|
||||||
|
from: from.to_string(),
|
||||||
|
channel: channel.to_string(),
|
||||||
|
uid: uid.to_string(),
|
||||||
|
reason: reason.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a channel's topic, sourced from pseudoclient `from`.
|
||||||
|
pub fn topic(&mut self, from: &str, channel: &str, topic: &str) {
|
||||||
|
self.actions.push(NetAction::Topic {
|
||||||
|
from: from.to_string(),
|
||||||
|
channel: channel.to_string(),
|
||||||
|
topic: topic.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invite a user to a channel, sourced from pseudoclient `from`.
|
||||||
|
pub fn invite(&mut self, from: &str, uid: &str, channel: &str) {
|
||||||
|
self.actions.push(NetAction::Invite {
|
||||||
|
from: from.to_string(),
|
||||||
|
uid: uid.to_string(),
|
||||||
|
channel: channel.to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,97 +1,6 @@
|
||||||
// Protocol abstraction. The engine only ever sees NetEvent / NetAction; raw
|
// Protocol layer. The normalized vocabulary (NetEvent / NetAction / RegReply)
|
||||||
// server-to-server lines live entirely behind a Protocol impl. Adding another
|
// and the Protocol trait live in the fedserv-api SDK crate; re-exported here so
|
||||||
// ircd later means one new module, engine untouched.
|
// the engine and the ircd modules keep referring to them as `crate::proto::*`.
|
||||||
pub mod inspircd;
|
pub mod inspircd;
|
||||||
|
|
||||||
// Normalized inbound facts, translated from the uplink's raw lines.
|
pub use fedserv_api::{NetAction, NetEvent, Protocol, RegReply};
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum NetEvent {
|
|
||||||
Registered,
|
|
||||||
EndBurst,
|
|
||||||
Ping { token: String, from: Option<String> },
|
|
||||||
Privmsg { from: String, to: String, text: String },
|
|
||||||
UserConnect { uid: String, nick: String, host: String },
|
|
||||||
NickChange { uid: String, nick: String },
|
|
||||||
// A channel was created or bursted (InspIRCd FJOIN). Subsequent single joins
|
|
||||||
// arrive as IJOIN and are not surfaced.
|
|
||||||
ChannelCreate { channel: String },
|
|
||||||
// A user joined a channel (an FJOIN member or an IJOIN), for auto-op. `op` is
|
|
||||||
// whether they hold channel-operator status at that point (an FJOIN prefix).
|
|
||||||
Join { uid: String, channel: String, op: bool },
|
|
||||||
// A user left a channel (PART) or was removed (KICK), for membership tracking.
|
|
||||||
Part { uid: String, channel: String },
|
|
||||||
// A user's channel-operator status changed (FMODE +o/-o), for live op tracking.
|
|
||||||
ChannelOp { channel: String, uid: String, op: bool },
|
|
||||||
// A channel's modes changed (FMODE), for enforcing mode locks. Our own
|
|
||||||
// changes are filtered out by the protocol layer.
|
|
||||||
ChannelModeChange { channel: String, modes: String },
|
|
||||||
// A channel's key (+k/-k) changed, tracked so GETKEY can report it.
|
|
||||||
ChannelKey { channel: String, key: Option<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 },
|
|
||||||
// An ircd relaying a SASL exchange step to us (the SASL agent). mode = H/S/C/D.
|
|
||||||
Sasl { client: String, agent: String, mode: String, data: Vec<String> },
|
|
||||||
Unknown { line: String },
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalized outbound intents the engine wants performed on the network.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum NetAction {
|
|
||||||
Burst,
|
|
||||||
EndBurst,
|
|
||||||
Pong { token: String, from: Option<String> },
|
|
||||||
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 },
|
|
||||||
// A SASL exchange step back to the ircd, sourced from our SASL agent. mode = C/D.
|
|
||||||
Sasl { agent: String, client: String, mode: String, data: Vec<String> },
|
|
||||||
// Publish network state to the uplink: target "*" is server-global (e.g. the
|
|
||||||
// advertised SASL mechanism list), otherwise a user uid (e.g. their account).
|
|
||||||
Metadata { target: String, key: String, value: String },
|
|
||||||
// Force a user's nick (SVSNICK), e.g. renaming to a guest nick on logout.
|
|
||||||
// The protocol stamps the new nick's timestamp.
|
|
||||||
ForceNick { uid: String, nick: String },
|
|
||||||
// Set channel modes from services, e.g. +r on a registered channel. `from` is
|
|
||||||
// the pseudoclient uid to source it from (empty = the services server). The
|
|
||||||
// protocol stamps a timestamp the ircd will accept.
|
|
||||||
ChannelMode { from: String, channel: String, modes: String },
|
|
||||||
// Kick a user from a channel, sourced from pseudoclient `from`.
|
|
||||||
Kick { from: String, channel: String, uid: String, reason: String },
|
|
||||||
// Set a channel's topic, sourced from pseudoclient `from`.
|
|
||||||
Topic { from: String, channel: String, topic: String },
|
|
||||||
// Invite a user to a channel, sourced from pseudoclient `from`.
|
|
||||||
Invite { from: String, uid: String, channel: String },
|
|
||||||
Raw(String),
|
|
||||||
// Internal only, never serialized to the wire: a registration whose password
|
|
||||||
// still needs its (expensive) key derivation. The link layer runs the
|
|
||||||
// derivation off-thread, then calls Engine::complete_register.
|
|
||||||
DeferRegister { account: String, password: String, email: Option<String>, reply: RegReply },
|
|
||||||
// Internal only: a password change awaiting the same off-thread derivation.
|
|
||||||
// The link layer derives, then calls Engine::complete_password_change.
|
|
||||||
DeferPassword { account: String, password: String, agent: String, uid: String },
|
|
||||||
// Internal only: send an email (plaintext + optional HTML). The link layer
|
|
||||||
// pipes it to the configured mail command off-thread; never serialized.
|
|
||||||
SendEmail { to: String, subject: String, text: String, html: Option<String> },
|
|
||||||
}
|
|
||||||
|
|
||||||
// How to answer a registration once its credentials have been derived.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum RegReply {
|
|
||||||
// IRCv3 account-registration relay: answer the requesting ircd.
|
|
||||||
Relay { reqid: String, kind: String },
|
|
||||||
// NickServ REGISTER: NOTICE the requesting user, logging them in on success.
|
|
||||||
NickServ { agent: String, uid: String, nick: String },
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Protocol: Send {
|
|
||||||
/// Lines to send immediately on connect (auth / capability negotiation).
|
|
||||||
fn handshake(&mut self) -> Vec<String>;
|
|
||||||
/// One raw inbound line -> zero or more normalized events.
|
|
||||||
fn parse(&mut self, line: &str) -> Vec<NetEvent>;
|
|
||||||
/// One normalized action -> the raw line(s) that realise it.
|
|
||||||
fn serialize(&mut self, action: &NetAction) -> Vec<String>;
|
|
||||||
/// Our own server id, used as the source prefix for server-sourced lines.
|
|
||||||
fn sid(&self) -> &str;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,10 @@
|
||||||
use crate::engine::db::Db;
|
use crate::engine::db::Db;
|
||||||
use crate::engine::state::Network;
|
use crate::engine::state::Network;
|
||||||
use crate::proto::{NetAction, RegReply};
|
|
||||||
|
|
||||||
// Who sent the command, resolved by the engine (UID + current nick + the
|
// Sender + ServiceCtx (the command context a module receives) live in the
|
||||||
// account they are identified to, if any).
|
// fedserv-api SDK crate; re-exported so modules keep using
|
||||||
pub struct Sender<'a> {
|
// `crate::engine::service::{Sender, ServiceCtx}`.
|
||||||
pub uid: &'a str,
|
pub use fedserv_api::{Sender, ServiceCtx};
|
||||||
pub nick: &'a str,
|
|
||||||
pub account: Option<&'a str>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// A pseudo-client (NickServ, ChanServ, ...). Introduced at burst, receives the
|
// A pseudo-client (NickServ, ChanServ, ...). Introduced at burst, receives the
|
||||||
// commands users message it, reads/writes the account store, and pushes actions.
|
// commands users message it, reads/writes the account store, and pushes actions.
|
||||||
|
|
@ -31,111 +27,3 @@ pub trait Service: Send {
|
||||||
}
|
}
|
||||||
fn on_command(&mut self, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &Network, db: &mut Db);
|
fn on_command(&mut self, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &Network, db: &mut Db);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct ServiceCtx {
|
|
||||||
pub actions: Vec<NetAction>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ServiceCtx {
|
|
||||||
pub fn notice(&mut self, from: &str, to: &str, text: impl Into<String>) {
|
|
||||||
self.actions.push(NetAction::Notice {
|
|
||||||
from: from.to_string(),
|
|
||||||
to: to.to_string(),
|
|
||||||
text: text.into(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hand a registration to the engine to finish: its password derivation runs
|
|
||||||
// off the reactor, then the engine commits it and answers `reply`.
|
|
||||||
pub fn defer_register(&mut self, account: impl Into<String>, password: impl Into<String>, email: Option<String>, reply: RegReply) {
|
|
||||||
self.actions.push(NetAction::DeferRegister {
|
|
||||||
account: account.into(),
|
|
||||||
password: password.into(),
|
|
||||||
email,
|
|
||||||
reply,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send an email (the link layer pipes it to the configured mail command).
|
|
||||||
pub fn send_email(&mut self, to: impl Into<String>, subject: impl Into<String>, text: impl Into<String>, html: Option<String>) {
|
|
||||||
self.actions.push(NetAction::SendEmail { to: to.into(), subject: subject.into(), text: text.into(), html });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hand a password change to the engine to finish: its derivation runs off the
|
|
||||||
// reactor, then the engine commits it and notices `uid`, sourced from `agent`.
|
|
||||||
pub fn defer_password(&mut self, account: impl Into<String>, password: impl Into<String>, agent: impl Into<String>, uid: impl Into<String>) {
|
|
||||||
self.actions.push(NetAction::DeferPassword {
|
|
||||||
account: account.into(),
|
|
||||||
password: password.into(),
|
|
||||||
agent: agent.into(),
|
|
||||||
uid: uid.into(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log a user into an account: sets the accountname the ircd turns into
|
|
||||||
// RPL_LOGGEDIN (900) and exposes to account-tag / WHOX, the same login the
|
|
||||||
// SASL agent applies. Used after a successful REGISTER / IDENTIFY.
|
|
||||||
pub fn login(&mut self, uid: &str, account: &str) {
|
|
||||||
self.actions.push(NetAction::Metadata {
|
|
||||||
target: uid.to_string(),
|
|
||||||
key: "accountname".to_string(),
|
|
||||||
value: account.to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log a user out: clearing the accountname the ircd turns into RPL_LOGGEDOUT
|
|
||||||
// (901) and drops from account-tag / WHOX. The inverse of `login`.
|
|
||||||
pub fn logout(&mut self, uid: &str) {
|
|
||||||
self.actions.push(NetAction::Metadata {
|
|
||||||
target: uid.to_string(),
|
|
||||||
key: "accountname".to_string(),
|
|
||||||
value: String::new(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force a user's nick (SVSNICK), e.g. to a guest nick after logout.
|
|
||||||
pub fn force_nick(&mut self, uid: &str, nick: &str) {
|
|
||||||
self.actions.push(NetAction::ForceNick {
|
|
||||||
uid: uid.to_string(),
|
|
||||||
nick: nick.to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set channel modes, sourced from pseudoclient `from` (e.g. ChanServ).
|
|
||||||
pub fn channel_mode(&mut self, from: &str, channel: &str, modes: &str) {
|
|
||||||
self.actions.push(NetAction::ChannelMode {
|
|
||||||
from: from.to_string(),
|
|
||||||
channel: channel.to_string(),
|
|
||||||
modes: modes.to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Kick a user, sourced from pseudoclient `from`.
|
|
||||||
pub fn kick(&mut self, from: &str, channel: &str, uid: &str, reason: &str) {
|
|
||||||
self.actions.push(NetAction::Kick {
|
|
||||||
from: from.to_string(),
|
|
||||||
channel: channel.to_string(),
|
|
||||||
uid: uid.to_string(),
|
|
||||||
reason: reason.to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set a channel's topic, sourced from pseudoclient `from`.
|
|
||||||
pub fn topic(&mut self, from: &str, channel: &str, topic: &str) {
|
|
||||||
self.actions.push(NetAction::Topic {
|
|
||||||
from: from.to_string(),
|
|
||||||
channel: channel.to_string(),
|
|
||||||
topic: topic.to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Invite a user to a channel, sourced from pseudoclient `from`.
|
|
||||||
pub fn invite(&mut self, from: &str, uid: &str, channel: &str) {
|
|
||||||
self.actions.push(NetAction::Invite {
|
|
||||||
from: from.to_string(),
|
|
||||||
uid: uid.to_string(),
|
|
||||||
channel: channel.to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue