user modes +W showwhois / +c commonchans
This commit is contained in:
parent
37ea5d277d
commit
835c1cf099
5 changed files with 65 additions and 2 deletions
|
|
@ -119,6 +119,7 @@ impl Command for Whois {
|
||||||
signon,
|
signon,
|
||||||
certfp,
|
certfp,
|
||||||
swhois,
|
swhois,
|
||||||
|
showwhois,
|
||||||
) = {
|
) = {
|
||||||
let u = &s.users[&tuid];
|
let u = &s.users[&tuid];
|
||||||
(
|
(
|
||||||
|
|
@ -140,6 +141,7 @@ impl Command for Whois {
|
||||||
u.ext
|
u.ext
|
||||||
.get::<crate::coremods::core_oper::Swhois>()
|
.get::<crate::coremods::core_oper::Swhois>()
|
||||||
.map(|w| w.0.clone()),
|
.map(|w| w.0.clone()),
|
||||||
|
u.flags.showwhois,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
let chans: Vec<String> = keys
|
let chans: Vec<String> = keys
|
||||||
|
|
@ -221,6 +223,14 @@ impl Command for Whois {
|
||||||
RPL_WHOISIDLE,
|
RPL_WHOISIDLE,
|
||||||
&format!("{nick} {idle} {signon} :seconds idle, signon time"),
|
&format!("{nick} {idle} {signon} :seconds idle, signon time"),
|
||||||
);
|
);
|
||||||
|
// +W showwhois — tell the target that someone looked them up
|
||||||
|
if showwhois && !is_self {
|
||||||
|
let by = s.users.get(&uid).map(|u| u.prefix()).unwrap_or_default();
|
||||||
|
s.send(
|
||||||
|
tuid,
|
||||||
|
format!(":{} NOTICE {nick} :*** {by} did a /WHOIS on you", s.name),
|
||||||
|
);
|
||||||
|
}
|
||||||
s.numeric(uid, RPL_ENDOFWHOIS, &format!("{nick} :End of /WHOIS list"));
|
s.numeric(uid, RPL_ENDOFWHOIS, &format!("{nick} :End of /WHOIS list"));
|
||||||
CmdResult::Ok
|
CmdResult::Ok
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -360,6 +360,34 @@ pub(crate) fn deliver(s: &mut Server, uid: Uid, params: &[String], notice: bool)
|
||||||
// propagate to linked servers that have members in this channel
|
// propagate to linked servers that have members in this channel
|
||||||
s.send_channel_to_links(uid, &key, target, cmd, &body);
|
s.send_channel_to_links(uid, &key, target, cmd, &body);
|
||||||
} else if let Some(tuid) = s.find_nick(target) {
|
} else if let Some(tuid) = s.find_nick(target) {
|
||||||
|
// user +c (commonchans): only users sharing a channel may PM them (opers exempt)
|
||||||
|
if s.users
|
||||||
|
.get(&tuid)
|
||||||
|
.map(|u| u.flags.deny_uncommon)
|
||||||
|
.unwrap_or(false)
|
||||||
|
&& uid != tuid
|
||||||
|
&& !s.is_oper(uid)
|
||||||
|
{
|
||||||
|
let common = match (s.users.get(&uid), s.users.get(&tuid)) {
|
||||||
|
(Some(a), Some(b)) => a.channels.intersection(&b.channels).next().is_some(),
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if !common {
|
||||||
|
if !notice {
|
||||||
|
let tn = s
|
||||||
|
.users
|
||||||
|
.get(&tuid)
|
||||||
|
.map(|u| u.nick.clone())
|
||||||
|
.unwrap_or_default();
|
||||||
|
s.numeric(
|
||||||
|
uid,
|
||||||
|
ERR_CANTSENDTOUSER,
|
||||||
|
&format!("{tn} :You must share a channel to message this user (+c)"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return CmdResult::Fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
// user +R (regdeaf): drop messages from users not logged into an account
|
// user +R (regdeaf): drop messages from users not logged into an account
|
||||||
if s.users
|
if s.users
|
||||||
.get(&tuid)
|
.get(&tuid)
|
||||||
|
|
|
||||||
18
src/mode.rs
18
src/mode.rs
|
|
@ -931,6 +931,8 @@ static USER_MODES: &[&(dyn UserMode + Sync)] = &[
|
||||||
&SSLPM,
|
&SSLPM,
|
||||||
&SNOMASK,
|
&SNOMASK,
|
||||||
&CALLERID,
|
&CALLERID,
|
||||||
|
&SHOWWHOIS,
|
||||||
|
&COMMONCHANS,
|
||||||
];
|
];
|
||||||
|
|
||||||
/// A simple boolean user flag (+i / +w).
|
/// A simple boolean user flag (+i / +w).
|
||||||
|
|
@ -973,6 +975,12 @@ fn set_sslpm(f: &mut UserFlags, v: bool) {
|
||||||
fn set_callerid(f: &mut UserFlags, v: bool) {
|
fn set_callerid(f: &mut UserFlags, v: bool) {
|
||||||
f.callerid = v;
|
f.callerid = v;
|
||||||
}
|
}
|
||||||
|
fn set_showwhois(f: &mut UserFlags, v: bool) {
|
||||||
|
f.showwhois = v;
|
||||||
|
}
|
||||||
|
fn set_commonchans(f: &mut UserFlags, v: bool) {
|
||||||
|
f.deny_uncommon = v;
|
||||||
|
}
|
||||||
static BOT: UFlag = UFlag {
|
static BOT: UFlag = UFlag {
|
||||||
ch: 'B',
|
ch: 'B',
|
||||||
set: set_bot,
|
set: set_bot,
|
||||||
|
|
@ -1001,6 +1009,14 @@ static CALLERID: UFlag = UFlag {
|
||||||
ch: 'g',
|
ch: 'g',
|
||||||
set: set_callerid,
|
set: set_callerid,
|
||||||
};
|
};
|
||||||
|
static SHOWWHOIS: UFlag = UFlag {
|
||||||
|
ch: 'W',
|
||||||
|
set: set_showwhois,
|
||||||
|
};
|
||||||
|
static COMMONCHANS: UFlag = UFlag {
|
||||||
|
ch: 'c',
|
||||||
|
set: set_commonchans,
|
||||||
|
};
|
||||||
|
|
||||||
impl UserMode for UFlag {
|
impl UserMode for UFlag {
|
||||||
fn letter(&self) -> char {
|
fn letter(&self) -> char {
|
||||||
|
|
@ -1141,7 +1157,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn registry_covers_all_user_modes() {
|
fn registry_covers_all_user_modes() {
|
||||||
for c in "iwoxBDIHrRzsg".chars() {
|
for c in "iwoxBDIHrRzsgWc".chars() {
|
||||||
assert!(user_mode(c).is_some(), "missing umode +{c}");
|
assert!(user_mode(c).is_some(), "missing umode +{c}");
|
||||||
}
|
}
|
||||||
assert!(user_mode('Q').is_none());
|
assert!(user_mode('Q').is_none());
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ pub const ERR_WASNOSUCHNICK: u16 = 406;
|
||||||
pub const ERR_UNAVAILRESOURCE: u16 = 437; // channel temporarily unavailable (+j)
|
pub const ERR_UNAVAILRESOURCE: u16 = 437; // channel temporarily unavailable (+j)
|
||||||
pub const ERR_LINKCHANNEL: u16 = 470; // +L — you were redirected to another channel
|
pub const ERR_LINKCHANNEL: u16 = 470; // +L — you were redirected to another channel
|
||||||
pub const ERR_DELAYREJOIN: u16 = 495; // +J — must wait before rejoining after a kick
|
pub const ERR_DELAYREJOIN: u16 = 495; // +J — must wait before rejoining after a kick
|
||||||
|
pub const ERR_CANTSENDTOUSER: u16 = 531; // +c — no shared channel with the target
|
||||||
pub const RPL_ENDOFSPAMFILTER: u16 = 940; // end of the +g word-filter list
|
pub const RPL_ENDOFSPAMFILTER: u16 = 940; // end of the +g word-filter list
|
||||||
pub const RPL_SPAMFILTER: u16 = 941; // one +g word-filter entry
|
pub const RPL_SPAMFILTER: u16 = 941; // one +g word-filter entry
|
||||||
pub const RPL_KNOCK: u16 = 710; // channel gets the knock
|
pub const RPL_KNOCK: u16 = 710; // channel gets the knock
|
||||||
|
|
|
||||||
10
src/users.rs
10
src/users.rs
|
|
@ -30,6 +30,8 @@ pub struct UserFlags {
|
||||||
pub ssl_pm: bool, // +z (only accept PMs from TLS users)
|
pub ssl_pm: bool, // +z (only accept PMs from TLS users)
|
||||||
pub snomask: bool, // +s (oper: receive server notices)
|
pub snomask: bool, // +s (oper: receive server notices)
|
||||||
pub callerid: bool, // +g (only accept PMs from users on the ACCEPT list)
|
pub callerid: bool, // +g (only accept PMs from users on the ACCEPT list)
|
||||||
|
pub showwhois: bool, // +W (get a notice when someone WHOISes you)
|
||||||
|
pub deny_uncommon: bool, // +c (only users sharing a channel may PM you)
|
||||||
pub away: Option<String>, // AWAY message, if set
|
pub away: Option<String>, // AWAY message, if set
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -75,6 +77,12 @@ impl UserFlags {
|
||||||
if self.callerid {
|
if self.callerid {
|
||||||
s.push('g');
|
s.push('g');
|
||||||
}
|
}
|
||||||
|
if self.showwhois {
|
||||||
|
s.push('W');
|
||||||
|
}
|
||||||
|
if self.deny_uncommon {
|
||||||
|
s.push('c');
|
||||||
|
}
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -409,7 +417,7 @@ impl Server {
|
||||||
uid,
|
uid,
|
||||||
RPL_MYINFO,
|
RPL_MYINFO,
|
||||||
&format!(
|
&format!(
|
||||||
"{} echoircd-{VERSION} iowxsgBDIHrRz qaohvbeIklimnpstzCTcSNORMfjFLgGuBQAPJ",
|
"{} echoircd-{VERSION} iowxsgBDIHrRzWc qaohvbeIklimnpstzCTcSNORMfjFLgGuBQAPJ",
|
||||||
self.name
|
self.name
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue