modules: split services and the ircd link into external crates
nickserv, chanserv and the InspIRCd protocol move out of the binary into their own workspace crates (fedserv-nickserv, fedserv-chanserv, fedserv-inspircd), each depending only on fedserv-api. human_time and the branded account emails move into the SDK crate so a module needs nothing from core; the engine keeps its own inherent methods and builds emails via fedserv-api too. The bin now constructs each module from its crate instead of an in-tree #[path] include. Proves the SDK is self-sufficient: a third-party module is the same shape.
This commit is contained in:
parent
8ed1a9ab70
commit
596630df53
52 changed files with 197 additions and 162 deletions
24
Cargo.lock
generated
24
Cargo.lock
generated
|
|
@ -285,6 +285,9 @@ dependencies = [
|
||||||
"argon2",
|
"argon2",
|
||||||
"base64",
|
"base64",
|
||||||
"fedserv-api",
|
"fedserv-api",
|
||||||
|
"fedserv-chanserv",
|
||||||
|
"fedserv-inspircd",
|
||||||
|
"fedserv-nickserv",
|
||||||
"hmac",
|
"hmac",
|
||||||
"pbkdf2",
|
"pbkdf2",
|
||||||
"prost",
|
"prost",
|
||||||
|
|
@ -307,6 +310,27 @@ dependencies = [
|
||||||
name = "fedserv-api"
|
name = "fedserv-api"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fedserv-chanserv"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"fedserv-api",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fedserv-inspircd"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"fedserv-api",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fedserv-nickserv"
|
||||||
|
version = "0.0.1"
|
||||||
|
dependencies = [
|
||||||
|
"fedserv-api",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "find-msvc-tools"
|
name = "find-msvc-tools"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["api"]
|
members = ["api", "inspircd", "chanserv", "nickserv"]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "fedserv"
|
name = "fedserv"
|
||||||
|
|
@ -9,6 +9,9 @@ description = "Federated IRC services daemon (protocol-agnostic core, InspIRCd l
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
fedserv-api = { path = "api" }
|
fedserv-api = { path = "api" }
|
||||||
|
fedserv-inspircd = { path = "inspircd" }
|
||||||
|
fedserv-chanserv = { path = "chanserv" }
|
||||||
|
fedserv-nickserv = { path = "nickserv" }
|
||||||
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"
|
||||||
|
|
|
||||||
26
README.md
26
README.md
|
|
@ -7,19 +7,25 @@ derived from any project's source.
|
||||||
|
|
||||||
## Design
|
## Design
|
||||||
|
|
||||||
The core lives in `src/`; the pluggable parts live in `modules/` (see
|
The core lives in `src/`. The pluggable parts are separate crates in a Cargo
|
||||||
`modules/README.md`).
|
workspace, each depending only on the `fedserv-api` SDK crate.
|
||||||
|
|
||||||
|
- **`api/`** (`fedserv-api`) the module SDK: the `Service`, `Protocol`, `Store`
|
||||||
|
and `NetView` traits a module implements or is handed, plus the normalized
|
||||||
|
`NetEvent`/`NetAction` vocabulary and the read views. It has no storage or
|
||||||
|
runtime dependencies, so a third-party module builds against it alone.
|
||||||
- **`src/engine/`** the services engine: live network `state`, the account store,
|
- **`src/engine/`** the services engine: live network `state`, the account store,
|
||||||
and the `Service` trait. The store is event-sourced: every change is an `Event`
|
and the log. The store is event-sourced: every change is an `Event` appended to
|
||||||
appended to a per-node log, and state is a fold over that log.
|
a per-node log, and state is a fold over that log. A service touches it only
|
||||||
|
through the `Store`/`NetView` traits, so the log, gossip and credential material
|
||||||
|
stay out of a module's reach.
|
||||||
- **`src/gossip.rs`** node-to-node replication over the logs.
|
- **`src/gossip.rs`** node-to-node replication over the logs.
|
||||||
- **`modules/protocol/`** the ircd link layer. A `Protocol` trait maps raw
|
- **`inspircd/`** (`fedserv-inspircd`) the ircd link layer. A `Protocol` impl maps
|
||||||
server-to-server lines to and from a normalized `NetEvent`/`NetAction` model, so
|
raw server-to-server lines to and from the normalized model, so the engine never
|
||||||
the engine never touches a raw line and a new ircd is one new module. InspIRCd is
|
touches a raw line and a new ircd is one new crate.
|
||||||
the first.
|
- **`nickserv/`, `chanserv/`** (`fedserv-nickserv`, `fedserv-chanserv`) the
|
||||||
- **`modules/nickserv/`, `modules/chanserv/`** the pseudo-clients, one directory
|
pseudo-clients, each a crate implementing `Service`. OperServ and others follow
|
||||||
each. OperServ and others follow the same shape.
|
the same shape.
|
||||||
|
|
||||||
## Replication
|
## Replication
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@
|
||||||
//! it carries the traits a module implements and the normalized vocabulary the
|
//! it carries the traits a module implements and the normalized vocabulary the
|
||||||
//! engine speaks, with no storage or runtime dependencies of its own.
|
//! engine speaks, with no storage or runtime dependencies of its own.
|
||||||
|
|
||||||
|
// Branded account emails (confirm / reset), shared by the engine and modules.
|
||||||
|
pub mod email;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Protocol vocabulary
|
// Protocol vocabulary
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
@ -448,3 +451,22 @@ fn glob_match(pattern: &str, text: &str) -> bool {
|
||||||
}
|
}
|
||||||
pi == p.len()
|
pi == p.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Format a Unix timestamp (seconds) as "YYYY-MM-DD HH:MM:SS UTC", using Howard
|
||||||
|
// Hinnant's civil-from-days algorithm so no date crate is needed.
|
||||||
|
pub fn human_time(ts: u64) -> String {
|
||||||
|
let days = (ts / 86400) as i64;
|
||||||
|
let rem = ts % 86400;
|
||||||
|
let (hh, mm, ss) = (rem / 3600, (rem % 3600) / 60, rem % 60);
|
||||||
|
let z = days + 719468;
|
||||||
|
let era = (if z >= 0 { z } else { z - 146096 }) / 146097;
|
||||||
|
let doe = z - era * 146097;
|
||||||
|
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
||||||
|
let y = yoe + era * 400;
|
||||||
|
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
||||||
|
let mp = (5 * doy + 2) / 153;
|
||||||
|
let day = doy - (153 * mp + 2) / 5 + 1;
|
||||||
|
let month = if mp < 10 { mp + 3 } else { mp - 9 };
|
||||||
|
let year = y + i64::from(month <= 2);
|
||||||
|
format!("{year:04}-{month:02}-{day:02} {hh:02}:{mm:02}:{ss:02} UTC")
|
||||||
|
}
|
||||||
|
|
|
||||||
8
chanserv/Cargo.toml
Normal file
8
chanserv/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "fedserv-chanserv"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = "2021"
|
||||||
|
description = "ChanServ channel-registration service module for fedserv."
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
fedserv-api = { path = "../api" }
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// ACCESS <#channel> LIST | ADD <account> <op|voice> | DEL <account>
|
// ACCESS <#channel> LIST | ADD <account> <op|voice> | DEL <account>
|
||||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// AKICK <#channel> ADD <mask> [reason] | DEL <mask> | LIST
|
// AKICK <#channel> ADD <mask> [reason] | DEL <mask> | LIST
|
||||||
// Masks are nick!user@host globs; matching users are banned and kicked on join.
|
// Masks are nick!user@host globs; matching users are banned and kicked on join.
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
use crate::engine::state::NetView;
|
use fedserv_api::NetView;
|
||||||
|
|
||||||
// BAN <#channel> <nick> [reason]: ban *!*@host and kick the user.
|
// BAN <#channel> <nick> [reason]: ban *!*@host and kick the user.
|
||||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView, db: &dyn Store) {
|
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView, db: &dyn Store) {
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// CLONE <source> <target>: copy a channel's settings (mode lock, access,
|
// CLONE <source> <target>: copy a channel's settings (mode lock, access,
|
||||||
// auto-kick, description, entry message) into another. Founder of both.
|
// auto-kick, description, entry message) into another. Founder of both.
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
use crate::engine::state::NetView;
|
use fedserv_api::NetView;
|
||||||
|
|
||||||
// ENFORCE <#channel>: re-apply the channel's settings to everyone present —
|
// ENFORCE <#channel>: re-apply the channel's settings to everyone present —
|
||||||
// the mode lock, access status modes, and the auto-kick list.
|
// the mode lock, access status modes, and the auto-kick list.
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// ENTRYMSG <#channel> [CLEAR | <text>]: message noticed to users as they join.
|
// ENTRYMSG <#channel> [CLEAR | <text>]: message noticed to users as they join.
|
||||||
// With no argument, show the current message.
|
// With no argument, show the current message.
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
use crate::engine::state::NetView;
|
use fedserv_api::NetView;
|
||||||
|
|
||||||
// GETKEY <#channel>: report the channel key (+k), for ops who need to let
|
// GETKEY <#channel>: report the channel key (+k), for ops who need to let
|
||||||
// someone in.
|
// someone in.
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
use crate::engine::state::NetView;
|
use fedserv_api::NetView;
|
||||||
|
|
||||||
// INVITE <#channel> [nick]: invite a user (self if no nick) into the channel.
|
// INVITE <#channel> [nick]: invite a user (self if no nick) into the channel.
|
||||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView, db: &dyn Store) {
|
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView, db: &dyn Store) {
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
use crate::engine::state::NetView;
|
use fedserv_api::NetView;
|
||||||
|
|
||||||
// KICK <#channel> <nick> [reason]: kick a user.
|
// KICK <#channel> <nick> [reason]: kick a user.
|
||||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView, db: &dyn Store) {
|
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView, db: &dyn Store) {
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::engine::db::{ChanError, ChannelView, Store};
|
use fedserv_api::{ChanError, ChannelView, Store};
|
||||||
use crate::engine::service::{Sender, Service, ServiceCtx};
|
use fedserv_api::{Sender, Service, ServiceCtx};
|
||||||
use crate::engine::state::NetView;
|
use fedserv_api::NetView;
|
||||||
|
|
||||||
#[path = "mode.rs"]
|
#[path = "mode.rs"]
|
||||||
mod mode;
|
mod mode;
|
||||||
|
|
@ -100,7 +100,7 @@ impl Service for ChanServ {
|
||||||
if !info.desc.is_empty() {
|
if !info.desc.is_empty() {
|
||||||
ctx.notice(me, from.uid, format!(" Description: {}", info.desc));
|
ctx.notice(me, from.uid, format!(" Description: {}", info.desc));
|
||||||
}
|
}
|
||||||
ctx.notice(me, from.uid, format!(" Registered : {}", crate::engine::db::human_time(info.ts)));
|
ctx.notice(me, from.uid, format!(" Registered : {}", fedserv_api::human_time(info.ts)));
|
||||||
}
|
}
|
||||||
None => ctx.notice(me, from.uid, format!("\x02{chan}\x02 isn't registered.")),
|
None => ctx.notice(me, from.uid, format!("\x02{chan}\x02 isn't registered.")),
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// LIST: show all registered channels.
|
// LIST: show all registered channels.
|
||||||
pub fn handle(me: &str, from: &Sender, _args: &[&str], ctx: &mut ServiceCtx, db: &dyn Store) {
|
pub fn handle(me: &str, from: &Sender, _args: &[&str], ctx: &mut ServiceCtx, db: &dyn Store) {
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// MODE <#channel> <modes>: the founder sets channel modes via ChanServ.
|
// MODE <#channel> <modes>: the founder sets channel modes via ChanServ.
|
||||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
use crate::engine::state::NetView;
|
use fedserv_api::NetView;
|
||||||
|
|
||||||
// OP/DEOP/VOICE/DEVOICE <#channel> [nick]: set a status mode on a user (self if
|
// OP/DEOP/VOICE/DEVOICE <#channel> [nick]: set a status mode on a user (self if
|
||||||
// no nick given). `mode` is the mode to apply, e.g. "+o".
|
// no nick given). `mode` is the mode to apply, e.g. "+o".
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
use crate::engine::state::NetView;
|
use fedserv_api::NetView;
|
||||||
|
|
||||||
// SEEN <nick>: when a nick was last seen, and doing what.
|
// SEEN <nick>: when a nick was last seen, and doing what.
|
||||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView) {
|
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView) {
|
||||||
|
|
@ -12,7 +12,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
match net.last_seen(nick) {
|
match net.last_seen(nick) {
|
||||||
Some(s) => ctx.notice(me, from.uid, format!("\x02{}\x02 was last seen {} ({}).", s.nick, crate::engine::db::human_time(s.ts), s.what)),
|
Some(s) => ctx.notice(me, from.uid, format!("\x02{}\x02 was last seen {} ({}).", s.nick, fedserv_api::human_time(s.ts), s.what)),
|
||||||
None => ctx.notice(me, from.uid, format!("I have no record of \x02{nick}\x02.")),
|
None => ctx.notice(me, from.uid, format!("I have no record of \x02{nick}\x02.")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// SET <#channel> FOUNDER <account> | DESC <text>: founder-only channel settings.
|
// SET <#channel> FOUNDER <account> | DESC <text>: founder-only channel settings.
|
||||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
use crate::engine::state::NetView;
|
use fedserv_api::NetView;
|
||||||
|
|
||||||
// STATUS <#channel> [nick]: show a user's access level (self if no nick).
|
// STATUS <#channel> [nick]: show a user's access level (self if no nick).
|
||||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView, db: &dyn Store) {
|
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView, db: &dyn Store) {
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// TOPIC <#channel> <text>: set the channel topic (empty clears it).
|
// TOPIC <#channel> <text>: set the channel topic (empty clears it).
|
||||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &dyn Store) {
|
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &dyn Store) {
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
use crate::engine::state::NetView;
|
use fedserv_api::NetView;
|
||||||
|
|
||||||
// UNBAN <#channel> [nick]: remove the *!*@host ban of a user (self if no nick).
|
// UNBAN <#channel> [nick]: remove the *!*@host ban of a user (self if no nick).
|
||||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView, db: &dyn Store) {
|
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView, db: &dyn Store) {
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// AOP/SOP/VOP <#channel> ADD <account> | DEL <account> | LIST — tiered
|
// AOP/SOP/VOP <#channel> ADD <account> | DEL <account> | LIST — tiered
|
||||||
// shortcuts over the access list. `level` is the access level they map to
|
// shortcuts over the access list. `level` is the access level they map to
|
||||||
8
inspircd/Cargo.toml
Normal file
8
inspircd/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "fedserv-inspircd"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = "2021"
|
||||||
|
description = "InspIRCd server-to-server protocol module for fedserv."
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
fedserv-api = { path = "../api" }
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// InspIRCd spanning-tree link protocol. Handshake + UID + PING mirror the
|
// InspIRCd spanning-tree link protocol. Handshake + UID + PING mirror the
|
||||||
// sequence in Network-Links (protocols/inspircd.py); mode/burst details get
|
// sequence in Network-Links (protocols/inspircd.py); mode/burst details get
|
||||||
// firmed up against a live insp4 uplink.
|
// firmed up against a live insp4 uplink.
|
||||||
use super::{NetAction, NetEvent, Protocol};
|
use fedserv_api::{NetAction, NetEvent, Protocol};
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
pub struct InspIrcd {
|
pub struct InspIrcd {
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
# modules/
|
|
||||||
|
|
||||||
The pluggable parts of fedserv: one directory per protocol and per pseudoclient.
|
|
||||||
The core (engine, event log, gossip, link loop) stays in `../src/`.
|
|
||||||
|
|
||||||
```
|
|
||||||
modules/
|
|
||||||
protocol/ ircd link protocols (inspircd.rs)
|
|
||||||
nickserv/ NickServ pseudoclient
|
|
||||||
chanserv/ ChanServ pseudoclient
|
|
||||||
```
|
|
||||||
|
|
||||||
`src/main.rs` pulls each in with `#[path = "../modules/..."]`, so crate paths
|
|
||||||
stay flat (`crate::proto`, `crate::nickserv`, `crate::chanserv`).
|
|
||||||
|
|
||||||
## Naming
|
|
||||||
|
|
||||||
A service directory holds its pseudoclient file plus, as commands are split out,
|
|
||||||
one file per command: `register.rs`, `mode.rs`, etc.
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
// Protocol layer. The normalized vocabulary (NetEvent / NetAction / RegReply)
|
|
||||||
// and the Protocol trait live in the fedserv-api SDK crate; re-exported here so
|
|
||||||
// the engine and the ircd modules keep referring to them as `crate::proto::*`.
|
|
||||||
pub mod inspircd;
|
|
||||||
|
|
||||||
pub use fedserv_api::{NetAction, NetEvent, Protocol, RegReply};
|
|
||||||
8
nickserv/Cargo.toml
Normal file
8
nickserv/Cargo.toml
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "fedserv-nickserv"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = "2021"
|
||||||
|
description = "NickServ account-registration service module for fedserv."
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
fedserv-api = { path = "../api" }
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// ALIST: list the channels the sender's account founds or has access on.
|
// ALIST: list the channels the sender's account founds or has access on.
|
||||||
pub fn handle(me: &str, from: &Sender, ctx: &mut ServiceCtx, db: &dyn Store) {
|
pub fn handle(me: &str, from: &Sender, ctx: &mut ServiceCtx, db: &dyn Store) {
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::{CertError, Store};
|
use fedserv_api::{CertError, Store};
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// CERT ADD|DEL|LIST <password> [fingerprint]: manage the TLS certificate
|
// CERT ADD|DEL|LIST <password> [fingerprint]: manage the TLS certificate
|
||||||
// fingerprints that may log in to your account via SASL EXTERNAL. Each
|
// fingerprints that may log in to your account via SASL EXTERNAL. Each
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::{CodeKind, Store};
|
use fedserv_api::{CodeKind, Store};
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// CONFIRM <code>: confirm your account's email with the code you were emailed.
|
// CONFIRM <code>: confirm your account's email with the code you were emailed.
|
||||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
use crate::engine::state::NetView;
|
use fedserv_api::NetView;
|
||||||
|
|
||||||
// DROP <password>: delete your account. Re-authenticates as confirmation, releases
|
// DROP <password>: delete your account. Re-authenticates as confirmation, releases
|
||||||
// and drops the channels you found, and logs you out.
|
// and drops the channels you found, and logs you out.
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
use crate::engine::state::NetView;
|
use fedserv_api::NetView;
|
||||||
|
|
||||||
// GHOST/RECOVER <nick> [password]: rename off a session using a nick you own,
|
// GHOST/RECOVER <nick> [password]: rename off a session using a nick you own,
|
||||||
// either by being identified to its account or giving that account's password.
|
// either by being identified to its account or giving that account's password.
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// GLIST: list the nicks grouped to your account.
|
// GLIST: list the nicks grouped to your account.
|
||||||
pub fn handle(me: &str, from: &Sender, ctx: &mut ServiceCtx, db: &dyn Store) {
|
pub fn handle(me: &str, from: &Sender, ctx: &mut ServiceCtx, db: &dyn Store) {
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// GROUP <account> <password>: link your current nick to an existing account, so
|
// GROUP <account> <password>: link your current nick to an existing account, so
|
||||||
// you can identify to it under this nick too.
|
// you can identify to it under this nick too.
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// IDENTIFY [account] <password>: log in. The account defaults to the current
|
// IDENTIFY [account] <password>: log in. The account defaults to the current
|
||||||
// nick, so both the bare-password and account+password forms work.
|
// nick, so both the bare-password and account+password forms work.
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::{human_time, Store};
|
use fedserv_api::{human_time, Store};
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// INFO [account]: show an account's registration details. The email is shown
|
// INFO [account]: show an account's registration details. The email is shown
|
||||||
// only to the account's own owner.
|
// only to the account's own owner.
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, Service, ServiceCtx};
|
use fedserv_api::{Sender, Service, ServiceCtx};
|
||||||
use crate::engine::state::NetView;
|
use fedserv_api::NetView;
|
||||||
|
|
||||||
#[path = "register.rs"]
|
#[path = "register.rs"]
|
||||||
mod register;
|
mod register;
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// LOGOUT: log out and rename to a guest nick (prefix + a per-logout sequence).
|
// LOGOUT: log out and rename to a guest nick (prefix + a per-logout sequence).
|
||||||
pub fn handle(me: &str, guest_nick: &str, guest_seq: &mut u32, from: &Sender, ctx: &mut ServiceCtx) {
|
pub fn handle(me: &str, guest_nick: &str, guest_seq: &mut u32, from: &Sender, ctx: &mut ServiceCtx) {
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
use crate::proto::RegReply;
|
use fedserv_api::RegReply;
|
||||||
|
|
||||||
// REGISTER <password> [email]: register the sender's current nick. The engine
|
// REGISTER <password> [email]: register the sender's current nick. The engine
|
||||||
// derives the password off-thread, commits, and answers.
|
// derives the password off-thread, commits, and answers.
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::{CodeKind, Store};
|
use fedserv_api::{CodeKind, Store};
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// RESETPASS <account>: email a reset code to the address on file.
|
// RESETPASS <account>: email a reset code to the address on file.
|
||||||
// RESETPASS <account> <code> <newpassword>: complete the reset with that code.
|
// RESETPASS <account> <code> <newpassword>: complete the reset with that code.
|
||||||
|
|
@ -19,7 +19,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let code = db.issue_code(&canonical, CodeKind::Reset);
|
let code = db.issue_code(&canonical, CodeKind::Reset);
|
||||||
let mail = crate::email::reset(db.email_brand(), db.email_accent(), db.email_logo(), &canonical, &code);
|
let mail = fedserv_api::email::reset(db.email_brand(), db.email_accent(), db.email_logo(), &canonical, &code);
|
||||||
ctx.send_email(email, mail.subject, mail.text, Some(mail.html));
|
ctx.send_email(email, mail.subject, mail.text, Some(mail.html));
|
||||||
ctx.notice(me, from.uid, format!("A reset code has been emailed to the address on file for \x02{canonical}\x02."));
|
ctx.notice(me, from.uid, format!("A reset code has been emailed to the address on file for \x02{canonical}\x02."));
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// SET PASSWORD <newpassword> | SET EMAIL [address]: change your account settings.
|
// SET PASSWORD <newpassword> | SET EMAIL [address]: change your account settings.
|
||||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::engine::db::Store;
|
use fedserv_api::Store;
|
||||||
use crate::engine::service::{Sender, ServiceCtx};
|
use fedserv_api::{Sender, ServiceCtx};
|
||||||
|
|
||||||
// UNGROUP [nick]: remove a nick grouped to your account (defaults to your current nick).
|
// UNGROUP [nick]: remove a nick grouped to your account (defaults to your current nick).
|
||||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
||||||
|
|
@ -471,25 +471,6 @@ fn now() -> u64 {
|
||||||
SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0)
|
SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format a Unix timestamp (seconds) as "YYYY-MM-DD HH:MM:SS UTC", using Howard
|
|
||||||
// Hinnant's civil-from-days algorithm so no date crate is needed.
|
|
||||||
pub(crate) fn human_time(ts: u64) -> String {
|
|
||||||
let days = (ts / 86400) as i64;
|
|
||||||
let rem = ts % 86400;
|
|
||||||
let (hh, mm, ss) = (rem / 3600, (rem % 3600) / 60, rem % 60);
|
|
||||||
let z = days + 719468;
|
|
||||||
let era = (if z >= 0 { z } else { z - 146096 }) / 146097;
|
|
||||||
let doe = z - era * 146097;
|
|
||||||
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
|
||||||
let y = yoe + era * 400;
|
|
||||||
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
|
||||||
let mp = (5 * doy + 2) / 153;
|
|
||||||
let day = doy - (153 * mp + 2) / 5 + 1;
|
|
||||||
let month = if mp < 10 { mp + 3 } else { mp - 9 };
|
|
||||||
let year = y + i64::from(month <= 2);
|
|
||||||
format!("{year:04}-{month:02}-{day:02} {hh:02}:{mm:02}:{ss:02} UTC")
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Db {
|
impl Db {
|
||||||
pub fn open(path: impl Into<PathBuf>, origin: impl Into<String>) -> Self {
|
pub fn open(path: impl Into<PathBuf>, origin: impl Into<String>) -> Self {
|
||||||
let (log, events) = EventLog::open(path.into(), origin.into());
|
let (log, events) = EventLog::open(path.into(), origin.into());
|
||||||
|
|
@ -1325,8 +1306,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn formats_unix_time_as_utc() {
|
fn formats_unix_time_as_utc() {
|
||||||
assert_eq!(human_time(0), "1970-01-01 00:00:00 UTC");
|
assert_eq!(fedserv_api::human_time(0), "1970-01-01 00:00:00 UTC");
|
||||||
assert_eq!(human_time(1783844590), "2026-07-12 08:23:10 UTC");
|
assert_eq!(fedserv_api::human_time(1783844590), "2026-07-12 08:23:10 UTC");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ impl Engine {
|
||||||
if status == AuthorityStatus::Ok && !self.db.is_verified(name) {
|
if status == AuthorityStatus::Ok && !self.db.is_verified(name) {
|
||||||
if let Some(addr) = addr {
|
if let Some(addr) = addr {
|
||||||
let code = self.db.issue_code(name, db::CodeKind::Confirm);
|
let code = self.db.issue_code(name, db::CodeKind::Confirm);
|
||||||
let mail = crate::email::confirm(self.db.email_brand(), self.db.email_accent(), self.db.email_logo(), name, &code);
|
let mail = fedserv_api::email::confirm(self.db.email_brand(), self.db.email_accent(), self.db.email_logo(), name, &code);
|
||||||
self.emit_irc(NetAction::SendEmail { to: addr, subject: mail.subject, text: mail.text, html: Some(mail.html) });
|
self.emit_irc(NetAction::SendEmail { to: addr, subject: mail.subject, text: mail.text, html: Some(mail.html) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -691,7 +691,7 @@ impl Engine {
|
||||||
if ok && !self.db.is_verified(account) {
|
if ok && !self.db.is_verified(account) {
|
||||||
if let (Some(addr), RegReply::NickServ { agent, uid, .. }) = (addr, &reply) {
|
if let (Some(addr), RegReply::NickServ { agent, uid, .. }) = (addr, &reply) {
|
||||||
let code = self.db.issue_code(account, db::CodeKind::Confirm);
|
let code = self.db.issue_code(account, db::CodeKind::Confirm);
|
||||||
let mail = crate::email::confirm(self.db.email_brand(), self.db.email_accent(), self.db.email_logo(), account, &code);
|
let mail = fedserv_api::email::confirm(self.db.email_brand(), self.db.email_accent(), self.db.email_logo(), account, &code);
|
||||||
out.push(NetAction::SendEmail { to: addr, subject: mail.subject, text: mail.text, html: Some(mail.html) });
|
out.push(NetAction::SendEmail { to: addr, subject: mail.subject, text: mail.text, html: Some(mail.html) });
|
||||||
out.push(NetAction::Notice { from: agent.clone(), to: uid.clone(), text: "A confirmation code has been emailed to you. Confirm with \x02CONFIRM <code>\x02.".to_string() });
|
out.push(NetAction::Notice { from: agent.clone(), to: uid.clone(), text: "A confirmation code has been emailed to you. Confirm with \x02CONFIRM <code>\x02.".to_string() });
|
||||||
}
|
}
|
||||||
|
|
@ -830,7 +830,7 @@ impl RegLimiter {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::nickserv::NickServ;
|
use fedserv_nickserv::NickServ;
|
||||||
|
|
||||||
fn plain(authzid: &[u8], authcid: &[u8], passwd: &[u8]) -> String {
|
fn plain(authzid: &[u8], authcid: &[u8], passwd: &[u8]) -> String {
|
||||||
let mut payload = Vec::new();
|
let mut payload = Vec::new();
|
||||||
|
|
@ -1222,7 +1222,7 @@ mod tests {
|
||||||
// notifies them over the services-initiated outbound path.
|
// notifies them over the services-initiated outbound path.
|
||||||
#[test]
|
#[test]
|
||||||
fn lost_conflict_logs_out_local_session() {
|
fn lost_conflict_logs_out_local_session() {
|
||||||
use crate::chanserv::ChanServ;
|
use fedserv_chanserv::ChanServ;
|
||||||
let path = std::env::temp_dir().join("fedserv-lostconf.jsonl");
|
let path = std::env::temp_dir().join("fedserv-lostconf.jsonl");
|
||||||
let _ = std::fs::remove_file(&path);
|
let _ = std::fs::remove_file(&path);
|
||||||
let mut db = Db::open(&path, "test");
|
let mut db = Db::open(&path, "test");
|
||||||
|
|
@ -1271,7 +1271,7 @@ mod tests {
|
||||||
// lists the channels the account founds or has access on.
|
// lists the channels the account founds or has access on.
|
||||||
#[test]
|
#[test]
|
||||||
fn nickserv_info_and_alist() {
|
fn nickserv_info_and_alist() {
|
||||||
use crate::chanserv::ChanServ;
|
use fedserv_chanserv::ChanServ;
|
||||||
let path = std::env::temp_dir().join("fedserv-nsinfo.jsonl");
|
let path = std::env::temp_dir().join("fedserv-nsinfo.jsonl");
|
||||||
let _ = std::fs::remove_file(&path);
|
let _ = std::fs::remove_file(&path);
|
||||||
let mut db = Db::open(&path, "test");
|
let mut db = Db::open(&path, "test");
|
||||||
|
|
@ -1473,7 +1473,7 @@ mod tests {
|
||||||
// only the founder can drop.
|
// only the founder can drop.
|
||||||
#[test]
|
#[test]
|
||||||
fn chanserv_register_info_drop() {
|
fn chanserv_register_info_drop() {
|
||||||
use crate::chanserv::ChanServ;
|
use fedserv_chanserv::ChanServ;
|
||||||
let path = std::env::temp_dir().join("fedserv-chanserv.jsonl");
|
let path = std::env::temp_dir().join("fedserv-chanserv.jsonl");
|
||||||
let _ = std::fs::remove_file(&path);
|
let _ = std::fs::remove_file(&path);
|
||||||
let mut db = Db::open(&path, "42S");
|
let mut db = Db::open(&path, "42S");
|
||||||
|
|
@ -1533,7 +1533,7 @@ mod tests {
|
||||||
// ChanServ moderation: an op can op/kick/ban users; a non-op is refused.
|
// ChanServ moderation: an op can op/kick/ban users; a non-op is refused.
|
||||||
#[test]
|
#[test]
|
||||||
fn chanserv_moderation() {
|
fn chanserv_moderation() {
|
||||||
use crate::chanserv::ChanServ;
|
use fedserv_chanserv::ChanServ;
|
||||||
let path = std::env::temp_dir().join("fedserv-cs-mod.jsonl");
|
let path = std::env::temp_dir().join("fedserv-cs-mod.jsonl");
|
||||||
let _ = std::fs::remove_file(&path);
|
let _ = std::fs::remove_file(&path);
|
||||||
let mut db = Db::open(&path, "42S");
|
let mut db = Db::open(&path, "42S");
|
||||||
|
|
@ -1577,7 +1577,7 @@ mod tests {
|
||||||
// ChanServ topic, invite, auto-kick (with enforcement on join), list and status.
|
// ChanServ topic, invite, auto-kick (with enforcement on join), list and status.
|
||||||
#[test]
|
#[test]
|
||||||
fn chanserv_topic_invite_akick() {
|
fn chanserv_topic_invite_akick() {
|
||||||
use crate::chanserv::ChanServ;
|
use fedserv_chanserv::ChanServ;
|
||||||
let path = std::env::temp_dir().join("fedserv-cs-tia.jsonl");
|
let path = std::env::temp_dir().join("fedserv-cs-tia.jsonl");
|
||||||
let _ = std::fs::remove_file(&path);
|
let _ = std::fs::remove_file(&path);
|
||||||
let mut db = Db::open(&path, "42S");
|
let mut db = Db::open(&path, "42S");
|
||||||
|
|
@ -1618,7 +1618,7 @@ mod tests {
|
||||||
// ChanServ SET: description and founder transfer, founder-gated.
|
// ChanServ SET: description and founder transfer, founder-gated.
|
||||||
#[test]
|
#[test]
|
||||||
fn chanserv_set() {
|
fn chanserv_set() {
|
||||||
use crate::chanserv::ChanServ;
|
use fedserv_chanserv::ChanServ;
|
||||||
let path = std::env::temp_dir().join("fedserv-cs-set.jsonl");
|
let path = std::env::temp_dir().join("fedserv-cs-set.jsonl");
|
||||||
let _ = std::fs::remove_file(&path);
|
let _ = std::fs::remove_file(&path);
|
||||||
let mut db = Db::open(&path, "42S");
|
let mut db = Db::open(&path, "42S");
|
||||||
|
|
@ -1653,7 +1653,7 @@ mod tests {
|
||||||
// ChanServ entrymsg, enforce, getkey, seen, clone and the xop lists.
|
// ChanServ entrymsg, enforce, getkey, seen, clone and the xop lists.
|
||||||
#[test]
|
#[test]
|
||||||
fn chanserv_extended() {
|
fn chanserv_extended() {
|
||||||
use crate::chanserv::ChanServ;
|
use fedserv_chanserv::ChanServ;
|
||||||
let path = std::env::temp_dir().join("fedserv-cs-ext.jsonl");
|
let path = std::env::temp_dir().join("fedserv-cs-ext.jsonl");
|
||||||
let _ = std::fs::remove_file(&path);
|
let _ = std::fs::remove_file(&path);
|
||||||
let mut db = Db::open(&path, "42S");
|
let mut db = Db::open(&path, "42S");
|
||||||
|
|
|
||||||
|
|
@ -263,7 +263,7 @@ async fn send(tx: &mpsc::Sender<String>, msg: &Msg) -> Result<(), ()> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::engine::db::Db;
|
use crate::engine::db::Db;
|
||||||
use crate::nickserv::NickServ;
|
use fedserv_nickserv::NickServ;
|
||||||
|
|
||||||
fn engine(origin: &str, tag: &str) -> (Shared, Outbound) {
|
fn engine(origin: &str, tag: &str) -> (Shared, Outbound) {
|
||||||
let path = std::env::temp_dir().join(format!("fedserv-gossip-{tag}.jsonl"));
|
let path = std::env::temp_dir().join(format!("fedserv-gossip-{tag}.jsonl"));
|
||||||
|
|
|
||||||
|
|
@ -316,7 +316,7 @@ pub async fn run(engine: Shared, cfg: GrpcCfg, outbound: broadcast::Sender<LogEn
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::engine::db::Db;
|
use crate::engine::db::Db;
|
||||||
use crate::nickserv::NickServ;
|
use fedserv_nickserv::NickServ;
|
||||||
use tokio_stream::StreamExt;
|
use tokio_stream::StreamExt;
|
||||||
use tonic::metadata::MetadataValue;
|
use tonic::metadata::MetadataValue;
|
||||||
|
|
||||||
|
|
|
||||||
16
src/main.rs
16
src/main.rs
|
|
@ -1,26 +1,22 @@
|
||||||
// Core lives in src/; pluggable modules live in ../modules/.
|
// Core lives in src/; the pseudo-clients and the ircd link are external module
|
||||||
|
// crates (fedserv-nickserv, fedserv-chanserv, fedserv-inspircd), each depending
|
||||||
|
// only on the fedserv-api SDK.
|
||||||
mod config;
|
mod config;
|
||||||
mod email;
|
|
||||||
mod engine;
|
mod engine;
|
||||||
mod gossip;
|
mod gossip;
|
||||||
mod grpc;
|
mod grpc;
|
||||||
mod link;
|
mod link;
|
||||||
#[path = "../modules/protocol/mod.rs"]
|
|
||||||
mod proto;
|
mod proto;
|
||||||
#[path = "../modules/nickserv/nickserv.rs"]
|
|
||||||
mod nickserv;
|
|
||||||
#[path = "../modules/chanserv/chanserv.rs"]
|
|
||||||
mod chanserv;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use chanserv::ChanServ;
|
|
||||||
use engine::Engine;
|
use engine::Engine;
|
||||||
use nickserv::NickServ;
|
use fedserv_chanserv::ChanServ;
|
||||||
use proto::inspircd::InspIrcd;
|
use fedserv_inspircd::InspIrcd;
|
||||||
|
use fedserv_nickserv::NickServ;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
|
|
||||||
4
src/proto.rs
Normal file
4
src/proto.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// The normalized protocol vocabulary lives in the fedserv-api SDK crate;
|
||||||
|
// re-exported so the engine keeps referring to it as `crate::proto::*`. The
|
||||||
|
// concrete ircd link (InspIRCd) is an external module crate, not part of core.
|
||||||
|
pub use fedserv_api::{NetAction, NetEvent, Protocol, RegReply};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue