operprefix + ojoin: server oper prefix (!/mode y, above owner) auto-granted to opers + OJOIN command

This commit is contained in:
Jean Chevronnet 2026-08-10 10:05:37 +00:00
parent 8ebb106f97
commit 1dd7f77ca8
106 changed files with 687 additions and 711 deletions

View file

@ -1,13 +1,13 @@
//! rpc ban provider — `xline.list`, `xline.add`, `xline.del`. InspIRCd's
//! `m_rpc_ban`. Covers every echoIRCd x-line kind (K/G/Z/E/SHUN/Q/CBAN) through the
//! same `add_xline`/`remove_xline` primitives the oper commands use.
//! X-line RPC provider: `xline.list`, `xline.add`, `xline.del`. Covers every
//! x-line kind (K/G/Z/E/SHUN/Q/CBAN) via the same `add_xline`/`remove_xline`
//! primitives the oper commands use.
use super::json::{self, obj, qstr};
use super::RpcError;
use crate::server::Server;
use crate::xline::{parse_duration, XKind};
/// Map a request `type` (letter or unreal-ish name) to an `XKind`.
/// Map a request `type` (letter tag or full name like `KLINE`) to an `XKind`.
fn kind_of(t: &str) -> Option<XKind> {
let up = t.to_ascii_uppercase();
XKind::from_tag(&up).or_else(|| {

View file

@ -1,6 +1,5 @@
//! rpc channel provider — `channel.list`, `channel.get`, and the mutators
//! `channel.kick`, `channel.set_topic`. InspIRCd's `m_rpc_channel`. (`channel.set_mode`
//! lands with the shared server-side mode applier in a later pass.)
//! Channel RPC provider: `channel.list`, `channel.get`, and the mutators
//! `channel.kick`, `channel.set_topic`, `channel.set_mode`.
use super::json::{obj, qstr};
use super::RpcError;

View file

@ -1,6 +1,5 @@
//! rpc core provider — introspection: `rpc.methods` (list the interface),
//! `rpc.info` (identity + methods), and `server.info` / `stats.get` (identity +
//! network counts). InspIRCd's `m_rpc_core` + the legacy `stats.get`.
//! Core RPC introspection: `rpc.methods` (list the interface), `rpc.info`
//! (identity + methods), and `server.info` / `stats.get` (identity + network counts).
use super::json::{obj, qstr};
use super::{RpcError, ALL_METHODS};

View file

@ -1,8 +1,7 @@
//! The RPC HTTP server: a blocking listener thread that accepts a connection,
//! reads one HTTP request, authenticates it, and forwards the JSON-RPC body to the
//! core as `Event::RpcRequest` — then writes back whatever the core replies. Low
//! volume (admin tooling), so thread-per-connection is fine. Native `TcpStream`
//! only; no `unsafe`, no new crate.
//! RPC HTTP server: a blocking listener thread that accepts a connection, reads
//! one HTTP request, authenticates it, forwards the JSON-RPC body to the core as
//! `Event::RpcRequest`, and writes back the core's reply. Low volume (admin
//! tooling), so thread-per-connection is fine.
use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
@ -68,9 +67,9 @@ fn handle(
stream.set_read_timeout(Some(IO_TIMEOUT))?;
stream.set_write_timeout(Some(IO_TIMEOUT))?;
// read until we have the full header block, then the declared body — whether
// it's Content-Length-framed or Transfer-Encoding: chunked (like InspIRCd's
// http_parser handles). Body starts 4 bytes past the header terminator.
// Read until the full header block, then the declared body — whether
// Content-Length-framed or Transfer-Encoding: chunked. Body starts 4 bytes
// past the header terminator.
let mut buf = Vec::new();
let mut chunk = [0u8; 8192];
let mut head_end = None;

View file

@ -1,8 +1,7 @@
//! Minimal JSON for the RPC subsystem — no serde (openssl+mio-only crate policy).
//! Two jobs: pull a named field out of a flat-ish request object (`get_*`), and
//! escape strings when *building* result JSON with `format!`. The scanners respect
//! nesting and string escapes, so `get_raw` only ever matches a **top-level** key
//! (a `"nick"` buried inside a nested value or another string won't false-match).
//! Minimal JSON for the RPC subsystem. Two jobs: pull a named field out of a
//! request object (`get_*`), and escape strings when building result JSON. The
//! scanners respect nesting and string escapes, so `get_raw` only matches a
//! top-level key (a `"nick"` buried in a nested value or a string won't false-match).
/// Given `b[i] == b'"'`, return the index just past the closing quote.
fn scan_string(b: &[u8], mut i: usize) -> Option<usize> {

View file

@ -1,6 +1,6 @@
//! rpc log provider — `log.tail` and `log.events`. InspIRCd's `m_rpc_log` /
//! `m_jsonrpclog`. echoIRCd has no log *file* (it logs to journald), so both read
//! the in-memory server-log ring that `Server::snotice` feeds (`Server.log`).
//! Log RPC provider: `log.tail` and `log.events`. There is no log file (logging
//! goes to journald), so both read the in-memory server-log ring that
//! `Server::snotice` feeds (`Server.log`).
use super::json::{self, obj, qstr};
use super::RpcError;

View file

@ -1,6 +1,5 @@
//! rpc message provider — `message.send_notice`. InspIRCd's `m_rpc_message`.
//! Sends a server NOTICE to a channel (`#…`), a single user (nick), or every
//! local user (`*` / `$*`).
//! Message RPC provider: `message.send_notice`. Sends a server NOTICE to a
//! channel (`#…`), a single user (nick), or every local user (`*` / `$*`).
use super::json::{self, obj};
use super::RpcError;

View file

@ -1,11 +1,10 @@
//! rpc — a JSON-RPC 2.0 control interface over a small native HTTP server, the
//! echoIRCd analogue of InspIRCd's `m_httpd` + `m_jsonrpc` + `m_rpc_*`. Admin tools
//! call it to introspect and drive the ircd (list/kill users, manage bans, rehash…).
//! JSON-RPC 2.0 control interface over a small HTTP server. Admin tools call it to
//! introspect and drive the ircd (list/kill users, manage bans, rehash…).
//!
//! Layering (each provider is its own file, per [[echoircd-module-per-file]]):
//! Layering (each provider is its own file):
//! * [`httpd`] — the listener thread: accept, parse HTTP, authenticate, and hand
//! the JSON-RPC body to the core as `Event::RpcRequest`.
//! * [`json`] — native JSON scan/build (no serde).
//! * [`json`] — JSON scan/build.
//! * `core` / `user` / `channel` / `server` / `stats` / `ban` / `message` /
//! `whowas` / `spamfilter` / `log` — the method providers, called on the core
//! thread with `&mut Server`.
@ -128,7 +127,7 @@ pub fn dispatch(s: &mut Server, method: &str, params: &str, id: &str) -> String
}
/// Wrap a provider result (or error) in the JSON-RPC 2.0 response envelope, echoing
/// the method and id (InspIRCd includes the method in its responses too).
/// the method and id.
pub fn envelope(method: &str, id: &str, result: Result<String, RpcError>) -> String {
let id = if id.trim().is_empty() { "null" } else { id };
match result {

View file

@ -1,6 +1,5 @@
//! rpc server provider — `server.list`, `server.rehash`, `server.disconnect`.
//! InspIRCd's `m_rpc_server`. (`server.connect` needs the socketengine's dialer,
//! which isn't reachable from the core thread — use the `CONNECT` command instead.)
//! Server RPC provider: `server.list`, `server.rehash`, `server.connect`,
//! `server.disconnect`.
use super::json::{obj, qstr};
use super::RpcError;

View file

@ -1,7 +1,7 @@
//! rpc spamfilter provider — `spamfilter.list`, `spamfilter.add`, `spamfilter.del`.
//! InspIRCd's `m_rpc_spamfilter`. Operates on the same [`crate::modules::filter`]
//! rule set (stored in `Server.ext`) that the `FILTER` command and the enforcement
//! hook use, so a rule added here takes effect immediately.
//! Spamfilter RPC provider: `spamfilter.list`, `spamfilter.add`, `spamfilter.del`.
//! Operates on the same [`crate::modules::filter`] rule set (in `Server.ext`) that
//! the `FILTER` command and the enforcement hook use, so a rule added here takes
//! effect immediately.
use super::json::{self, obj, qstr};
use super::RpcError;

View file

@ -1,5 +1,5 @@
//! rpc stats provider — read-only introspection: `module.list`, `oper.list`,
//! `security_group.list`. InspIRCd's `m_rpc_stats`.
//! Stats RPC provider: read-only introspection via `module.list`, `oper.list`,
//! `security_group.list`.
use super::json::{obj, qstr};
use super::RpcError;

View file

@ -1,7 +1,7 @@
//! rpc user provider — `user.list`, `user.get`, and the mutators `user.kill`,
//! `user.set_mode`, `user.set_vhost`, `user.set_nick`, `user.set_oper`. InspIRCd's
//! `m_rpc_user`. Mutators route through the same `Server` primitives the commands
//! use, so behaviour and side-effects (QUIT/CHGHOST/MODE broadcasts) stay identical.
//! User RPC provider: `user.list`, `user.get`, and the mutators `user.kill`,
//! `user.set_mode`, `user.set_vhost`, `user.set_nick`, `user.set_oper`. Mutators
//! route through the same `Server` primitives the commands use, so behaviour and
//! side-effects (QUIT/CHGHOST/MODE broadcasts) stay identical.
use super::json::{self, obj, qstr};
use super::RpcError;

View file

@ -1,5 +1,5 @@
//! rpc whowas provider — `whowas.get`. InspIRCd's `m_rpc_whowas`. Returns the
//! recent-nick-history entries the ircd keeps for `WHOWAS`.
//! Whowas RPC provider: `whowas.get`. Returns the recent-nick-history entries
//! the ircd keeps for `WHOWAS`.
use super::json::{self, obj, qstr};
use super::RpcError;