diff --git a/src/config.rs b/src/config.rs index d73ba45..d909d5d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -42,6 +42,24 @@ pub struct OperBlock { pub fingerprint: Option, } +/// A trusted WEBIRC gateway: after presenting `password` it may rewrite a client's +/// real host + IP. `ipmask` (empty = any) restricts which source addresses may use +/// this block. +#[derive(Clone)] +pub struct WebircGateway { + pub password: String, + pub name: String, + pub ipmask: String, +} + +/// A +G censor rule: substitute `find` -> `replace` in channel text; an empty +/// `replace` blocks the message instead of rewriting it. +#[derive(Clone)] +pub struct CensorRule { + pub find: String, + pub replace: String, +} + /// Config for the `antimixedutf8` module (blocks mixed-script look-alike spam). #[derive(Clone)] pub struct AntiMixedCfg { @@ -90,7 +108,7 @@ pub struct Config { pub bind_server: Vec, // server-to-server link listeners (repeatable) pub links: Vec, // peers we accept / dial pub conf_path: String, // where this was loaded from (for REHASH) - pub censor: Vec<(String, String)>, // +G bad words: (find, replace); empty replace = block + pub censor: Vec, // +G bad words (empty replace = block) pub amu: AntiMixedCfg, // antimixedutf8 module config pub resolve_hosts: bool, // reverse-DNS clients on connect (default on) pub use_resolved_host: bool, // put the resolved hostname in the hostmask (default on) @@ -98,7 +116,7 @@ pub struct Config { pub dnsbl_action: String, // mark | kline | gline | zline (on a hit) pub dnsbl_reason: String, // ban reason for a DNSBL hit pub sasl_server: String, // linked services server that handles SASL ("" = none) - pub webirc: Vec<(String, String, String)>, // web gateways: (password, name, ip-mask) + pub webirc: Vec, // trusted web gateways /// Every `key = value` line, captured raw so modules read their own settings /// via `Server::conf*` — no per-module field bloats this struct or `Server`. pub raw: HashMap>, @@ -235,7 +253,7 @@ impl Config { let mut it = v.splitn(2, char::is_whitespace); if let Some(find) = it.next().filter(|f| !f.is_empty()) { let replace = it.next().unwrap_or("").trim().to_string(); - c.censor.push((find.to_string(), replace)); + c.censor.push(CensorRule { find: find.to_string(), replace }); } } "antimixedutf8" | "amu" => { @@ -291,7 +309,11 @@ impl Config { if let Some(pass) = it.next() { let gw = it.next().unwrap_or("webirc").to_string(); let mask = it.next().unwrap_or("").to_string(); - c.webirc.push((pass.to_string(), gw, mask)); + c.webirc.push(WebircGateway { + password: pass.to_string(), + name: gw, + ipmask: mask, + }); } } _ => {} diff --git a/src/coremods/core_message.rs b/src/coremods/core_message.rs index 00f200b..a4f7764 100644 --- a/src/coremods/core_message.rs +++ b/src/coremods/core_message.rs @@ -105,16 +105,16 @@ fn ci_replace(hay: &str, find: &str, rep: &str) -> String { /// +G censor: replace each configured bad word in `body`. Returns `None` when a /// matched word has an empty replacement (⇒ the message must be blocked). -fn apply_censor(body: &str, censor: &[(String, String)]) -> Option { +fn apply_censor(body: &str, censor: &[crate::config::CensorRule]) -> Option { let mut out = body.to_string(); - for (find, replace) in censor { - if find.is_empty() || !ci_contains(&out, find) { + for rule in censor { + if rule.find.is_empty() || !ci_contains(&out, &rule.find) { continue; } - if replace.is_empty() { + if rule.replace.is_empty() { return None; // no replacement ⇒ block } - out = ci_replace(&out, find, replace); + out = ci_replace(&out, &rule.find, &rule.replace); } Some(out) } diff --git a/src/coremods/core_user.rs b/src/coremods/core_user.rs index 218f3c2..f23eb4b 100644 --- a/src/coremods/core_user.rs +++ b/src/coremods/core_user.rs @@ -102,8 +102,8 @@ impl Command for WebIrc { let Some(gw) = s .webirc .iter() - .find(|(p, _, mask)| p == pass && (mask.is_empty() || glob_match(mask, &from))) - .map(|(_, g, _)| g.clone()) + .find(|g| g.password == *pass && (g.ipmask.is_empty() || glob_match(&g.ipmask, &from))) + .map(|g| g.name.clone()) else { s.notice_star(uid, "WEBIRC: invalid credentials"); return CmdResult::Fail; diff --git a/src/server.rs b/src/server.rs index 8041eb6..69fd538 100644 --- a/src/server.rs +++ b/src/server.rs @@ -149,7 +149,7 @@ pub struct Server { pub xlines: Vec, // server bans (KLINE/GLINE/ZLINE) pub mode_sudo: bool, // SAMODE/SAKICK: bypass rank checks pub in_redirect: bool, // +L: guards against redirect loops - pub censor: Vec<(String, String)>, // +G bad words: (find, replace) + pub censor: Vec, // +G bad words pub amu: crate::config::AntiMixedCfg, // antimixedutf8 module config pub resolve_hosts: bool, // reverse-DNS clients on connect pub use_resolved_host: bool, // apply the resolved name to the hostmask @@ -157,7 +157,7 @@ pub struct Server { pub dnsbl_action: String, // mark | kline | gline | zline pub dnsbl_reason: String, // ban reason on a DNSBL hit pub sasl_server: String, // services server that handles SASL - pub webirc: Vec<(String, String, String)>, // web gateways: (password, name, ip-mask) + pub webirc: Vec, // trusted web gateways /// Every `key = value` line from the config, so each module reads its own /// settings via [`Server::conf`] / [`conf_all`] / [`conf_bool`] / [`conf_num`] /// — no per-module field lives on this struct (module-per-file rule).