snomasks: make +s a parametric snomask mode with the standard category letters (acdfgjklnoqrtuvwx), route each server notice by category, RPL_SNOMASKIS 008; opers default to all and narrow with +s -c etc.
This commit is contained in:
parent
309201d7fb
commit
546f6279e7
16 changed files with 131 additions and 29 deletions
|
|
@ -236,6 +236,68 @@ pub fn svs_set_chan_modes(s: &mut Server, target: &str, modestring: &str, args:
|
|||
true
|
||||
}
|
||||
|
||||
/// Apply a client `+s`/`-s` snomask change. Oper-only. `+s` with a mask edits the
|
||||
/// subscribed categories (`+cq` adds, `-c` removes, `*` all); `+s` with no mask
|
||||
/// subscribes to everything; `-s` clears it. Emits `RPL_SNOMASKIS` (008) and returns
|
||||
/// whether the `s` mode char should appear in the MODE echo.
|
||||
fn apply_snomask(s: &mut Server, uid: Uid, adding: bool, param: Option<&str>) -> bool {
|
||||
if !s.is_oper(uid) {
|
||||
s.numeric(
|
||||
uid,
|
||||
crate::numeric::ERR_NOPRIVILEGES,
|
||||
":Permission denied - only operators may set a server notice mask",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
let mut cats: std::collections::BTreeSet<char> = s
|
||||
.users
|
||||
.get(&uid)
|
||||
.map(|u| u.flags.snomask_cats.chars().collect())
|
||||
.unwrap_or_default();
|
||||
let all = || crate::users::DEFAULT_SNOMASK.chars().collect::<std::collections::BTreeSet<char>>();
|
||||
if !adding {
|
||||
cats.clear();
|
||||
} else {
|
||||
match param {
|
||||
None => cats = all(),
|
||||
Some(p) => {
|
||||
let mut sign = '+';
|
||||
for c in p.chars() {
|
||||
match c {
|
||||
'+' => sign = '+',
|
||||
'-' => sign = '-',
|
||||
'*' => cats = if sign == '+' { all() } else { Default::default() },
|
||||
c if crate::users::DEFAULT_SNOMASK.contains(c) => {
|
||||
if sign == '+' {
|
||||
cats.insert(c);
|
||||
} else {
|
||||
cats.remove(&c);
|
||||
}
|
||||
}
|
||||
_ => {} // ignore unknown snomask letters
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mask: String = cats.iter().collect();
|
||||
let on = !mask.is_empty();
|
||||
if let Some(u) = s.users.get_mut(&uid) {
|
||||
u.flags.snomask = on;
|
||||
u.flags.snomask_cats = mask.clone();
|
||||
}
|
||||
s.numeric(
|
||||
uid,
|
||||
crate::numeric::RPL_SNOMASKIS,
|
||||
&format!("+{mask} :Server notice mask"),
|
||||
);
|
||||
if adding {
|
||||
on
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// User modes: dispatched to the [`crate::mode`] `UserMode` handler objects.
|
||||
fn apply_user_modes(s: &mut Server, uid: Uid, target: &str, params: &[String]) -> CmdResult {
|
||||
let me = s
|
||||
|
|
@ -264,12 +326,29 @@ fn apply_user_modes(s: &mut Server, uid: Uid, target: &str, params: &[String]) -
|
|||
let mut sign = '+';
|
||||
let mut applied = String::new();
|
||||
let mut last = ' ';
|
||||
let mut argi = 2usize; // params[2..] are mode arguments (the +s snomask mask)
|
||||
for c in modestring.chars() {
|
||||
if c == '+' || c == '-' {
|
||||
sign = c;
|
||||
continue;
|
||||
}
|
||||
let adding = sign == '+';
|
||||
// +s is a parametric snomask mode: it consumes the following mask argument
|
||||
if c == 's' {
|
||||
let param = if adding {
|
||||
let p = params.get(argi).cloned();
|
||||
if p.is_some() {
|
||||
argi += 1;
|
||||
}
|
||||
p
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if apply_snomask(s, uid, adding, param.as_deref()) {
|
||||
emit(&mut applied, &mut last, sign, c);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let Some(handler) = user_mode(c) else {
|
||||
s.numeric(
|
||||
uid,
|
||||
|
|
|
|||
|
|
@ -868,7 +868,7 @@ impl Command for NickLock {
|
|||
u.flags.nick_locked = true;
|
||||
}
|
||||
let by = oper_nick(s, uid);
|
||||
s.snotice(&format!("{by} used NICKLOCK on {newnick}"));
|
||||
s.snotice_c('v', &format!("{by} used NICKLOCK on {newnick}"));
|
||||
CmdResult::Ok
|
||||
}
|
||||
}
|
||||
|
|
@ -893,7 +893,7 @@ impl Command for NickUnlock {
|
|||
u.flags.nick_locked = false;
|
||||
}
|
||||
let by = oper_nick(s, uid);
|
||||
s.snotice(&format!("{by} used NICKUNLOCK on {}", params[0]));
|
||||
s.snotice_c('v', &format!("{by} used NICKUNLOCK on {}", params[0]));
|
||||
CmdResult::Ok
|
||||
}
|
||||
}
|
||||
|
|
@ -933,7 +933,7 @@ impl Command for Connect {
|
|||
let max_line = s.conf_num("max_line", crate::socketengine::DEFAULT_MAX_LINE);
|
||||
std::thread::spawn(move || crate::socketengine::connect_link(&addr, tx, counter, max_line));
|
||||
let by = oper_nick(s, uid);
|
||||
s.snotice(&format!(
|
||||
s.snotice_c('l', &format!(
|
||||
"{by} used CONNECT to {} ({}:{})",
|
||||
b.name, b.ip, b.port
|
||||
));
|
||||
|
|
@ -996,7 +996,7 @@ impl Command for ChgHost {
|
|||
};
|
||||
s.change_host_ident(t, None, Some(¶ms[1]));
|
||||
let by = oper_nick(s, uid);
|
||||
s.snotice(&format!(
|
||||
s.snotice_c('v', &format!(
|
||||
"{by} used CHGHOST on {}: {}",
|
||||
params[0], params[1]
|
||||
));
|
||||
|
|
@ -1048,7 +1048,7 @@ impl Command for ChgIdent {
|
|||
};
|
||||
s.change_host_ident(t, Some(¶ms[1]), None);
|
||||
let by = oper_nick(s, uid);
|
||||
s.snotice(&format!(
|
||||
s.snotice_c('v', &format!(
|
||||
"{by} used CHGIDENT on {}: {}",
|
||||
params[0], params[1]
|
||||
));
|
||||
|
|
@ -1095,7 +1095,7 @@ impl Command for SaMode {
|
|||
let r = apply_mode(s, uid, params);
|
||||
s.mode_sudo = false;
|
||||
let by = oper_nick(s, uid);
|
||||
s.snotice(&format!("{by} used SAMODE: {}", params.join(" ")));
|
||||
s.snotice_c('v', &format!("{by} used SAMODE: {}", params.join(" ")));
|
||||
r
|
||||
}
|
||||
}
|
||||
|
|
@ -1134,7 +1134,7 @@ impl Command for SaTopic {
|
|||
s.to_channel(&key, &format!(":{prefix} TOPIC {chan} :{text}"), None);
|
||||
s.propagate_topic(uid, chan, &text);
|
||||
let by = oper_nick(s, uid);
|
||||
s.snotice(&format!("{by} used SATOPIC on {chan}"));
|
||||
s.snotice_c('v', &format!("{by} used SATOPIC on {chan}"));
|
||||
CmdResult::Ok
|
||||
}
|
||||
}
|
||||
|
|
@ -1195,7 +1195,7 @@ impl Command for SaKick {
|
|||
s.events
|
||||
.push_back(Hook::Part(tuid, key, "kicked".to_string()));
|
||||
let by = oper_nick(s, uid);
|
||||
s.snotice(&format!("{by} used SAKICK on {victim} in {chan}"));
|
||||
s.snotice_c('v', &format!("{by} used SAKICK on {victim} in {chan}"));
|
||||
CmdResult::Ok
|
||||
}
|
||||
}
|
||||
|
|
@ -1224,7 +1224,7 @@ impl Command for SaQuit {
|
|||
s.send(tuid, format!("ERROR :Closing link: (SAQUIT: {reason})"));
|
||||
s.remove_user(tuid, &format!("Quit: {reason}"));
|
||||
let by = oper_nick(s, uid);
|
||||
s.snotice(&format!("{by} used SAQUIT on {}: {reason}", params[0]));
|
||||
s.snotice_c('v', &format!("{by} used SAQUIT on {}: {reason}", params[0]));
|
||||
CmdResult::Ok
|
||||
}
|
||||
}
|
||||
|
|
@ -1260,7 +1260,7 @@ impl Command for ChgName {
|
|||
}
|
||||
s.notify_peers(t, &line, |c| c.setname);
|
||||
let by = oper_nick(s, uid);
|
||||
s.snotice(&format!("{by} used CHGNAME on {}: {realname}", params[0]));
|
||||
s.snotice_c('v', &format!("{by} used CHGNAME on {}: {realname}", params[0]));
|
||||
CmdResult::Ok
|
||||
}
|
||||
}
|
||||
|
|
@ -1317,7 +1317,7 @@ impl Command for ClearChan {
|
|||
}
|
||||
s.channels.retain(|_, c| c.keep_alive());
|
||||
let by = oper_nick(s, uid);
|
||||
s.snotice(&format!("{by} used CLEARCHAN on {chan}"));
|
||||
s.snotice_c('v', &format!("{by} used CLEARCHAN on {chan}"));
|
||||
CmdResult::Ok
|
||||
}
|
||||
}
|
||||
|
|
@ -1434,7 +1434,7 @@ impl Command for SwhoisCmd {
|
|||
}
|
||||
}
|
||||
let by = oper_nick(s, uid);
|
||||
s.snotice(&format!("{by} used SWHOIS on {}: {text}", params[0]));
|
||||
s.snotice_c('v', &format!("{by} used SWHOIS on {}: {text}", params[0]));
|
||||
CmdResult::Ok
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue