Clippy sweep: clean across the workspace
- Box the oversized enum variants (Event::AccountRegistered, gossip Msg::Entry) so small events aren't sized to a full Account; serde is transparent so the wire/log format is unchanged. - Use the configured server description in the SERVER line (it was hardcoded and the config field went unread). - Derive the ChanServ SET label from the ChanSetting (drops toggle's 8th arg); rename inspircd from_us -> sourced. - allow the two framework-idiom lints with rationale (tonic's Status in authorize; the guest-rename handler's inherent arg count). - Auto-fixed map_or/contains/split_once style lints.
This commit is contained in:
parent
790ec51b28
commit
2fd5676ff2
9 changed files with 71 additions and 44 deletions
|
|
@ -42,18 +42,33 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
||||||
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some("SIGNKICK") => toggle(me, from, ctx, db, chan, ChanSetting::SignKick, args.get(3).copied(), "SIGNKICK"),
|
Some("SIGNKICK") => toggle(me, from, ctx, db, chan, ChanSetting::SignKick, args.get(3).copied()),
|
||||||
Some("PRIVATE") => toggle(me, from, ctx, db, chan, ChanSetting::Private, args.get(3).copied(), "PRIVATE"),
|
Some("PRIVATE") => toggle(me, from, ctx, db, chan, ChanSetting::Private, args.get(3).copied()),
|
||||||
Some("PEACE") => toggle(me, from, ctx, db, chan, ChanSetting::Peace, args.get(3).copied(), "PEACE"),
|
Some("PEACE") => toggle(me, from, ctx, db, chan, ChanSetting::Peace, args.get(3).copied()),
|
||||||
Some("SECUREOPS") => toggle(me, from, ctx, db, chan, ChanSetting::SecureOps, args.get(3).copied(), "SECUREOPS"),
|
Some("SECUREOPS") => toggle(me, from, ctx, db, chan, ChanSetting::SecureOps, args.get(3).copied()),
|
||||||
Some("KEEPTOPIC") => toggle(me, from, ctx, db, chan, ChanSetting::KeepTopic, args.get(3).copied(), "KEEPTOPIC"),
|
Some("KEEPTOPIC") => toggle(me, from, ctx, db, chan, ChanSetting::KeepTopic, args.get(3).copied()),
|
||||||
Some("TOPICLOCK") => toggle(me, from, ctx, db, chan, ChanSetting::TopicLock, args.get(3).copied(), "TOPICLOCK"),
|
Some("TOPICLOCK") => toggle(me, from, ctx, db, chan, ChanSetting::TopicLock, args.get(3).copied()),
|
||||||
_ => ctx.notice(me, from.uid, "Syntax: SET <#channel> FOUNDER <account> | DESC <text> | SIGNKICK {ON|OFF} | PRIVATE {ON|OFF} | PEACE {ON|OFF} | SECUREOPS {ON|OFF} | KEEPTOPIC {ON|OFF} | TOPICLOCK {ON|OFF}"),
|
_ => ctx.notice(me, from.uid, "Syntax: SET <#channel> FOUNDER <account> | DESC <text> | SIGNKICK {ON|OFF} | PRIVATE {ON|OFF} | PEACE {ON|OFF} | SECUREOPS {ON|OFF} | KEEPTOPIC {ON|OFF} | TOPICLOCK {ON|OFF}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The SET keyword for a channel option, used in its messages.
|
||||||
|
fn label(setting: ChanSetting) -> &'static str {
|
||||||
|
match setting {
|
||||||
|
ChanSetting::SignKick => "SIGNKICK",
|
||||||
|
ChanSetting::Private => "PRIVATE",
|
||||||
|
ChanSetting::Peace => "PEACE",
|
||||||
|
ChanSetting::SecureOps => "SECUREOPS",
|
||||||
|
ChanSetting::KeepTopic => "KEEPTOPIC",
|
||||||
|
ChanSetting::TopicLock => "TOPICLOCK",
|
||||||
|
ChanSetting::BotGreet => "GREET",
|
||||||
|
ChanSetting::NoBot => "NOBOT",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Flip one on/off channel option, reporting the new state.
|
// Flip one on/off channel option, reporting the new state.
|
||||||
fn toggle(me: &str, from: &Sender, ctx: &mut ServiceCtx, db: &mut dyn Store, chan: &str, setting: ChanSetting, arg: Option<&str>, name: &str) {
|
fn toggle(me: &str, from: &Sender, ctx: &mut ServiceCtx, db: &mut dyn Store, chan: &str, setting: ChanSetting, arg: Option<&str>) {
|
||||||
|
let name = label(setting);
|
||||||
let on = match arg.map(|s| s.to_ascii_uppercase()).as_deref() {
|
let on = match arg.map(|s| s.to_ascii_uppercase()).as_deref() {
|
||||||
Some("ON") => true,
|
Some("ON") => true,
|
||||||
Some("OFF") => false,
|
Some("OFF") => false,
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,18 @@ use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
pub struct InspIrcd {
|
pub struct InspIrcd {
|
||||||
sid: String,
|
sid: String,
|
||||||
name: String,
|
name: String,
|
||||||
|
description: String,
|
||||||
password: String,
|
password: String,
|
||||||
protocol: u32,
|
protocol: u32,
|
||||||
ts: u64,
|
ts: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InspIrcd {
|
impl InspIrcd {
|
||||||
pub fn new(name: String, sid: String, password: String, protocol: u32, ts: u64) -> Self {
|
pub fn new(name: String, description: String, sid: String, password: String, protocol: u32, ts: u64) -> Self {
|
||||||
Self { sid, name, password, protocol, ts }
|
Self { sid, name, description, password, protocol, ts }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_us(&self, cmd: String) -> String {
|
fn sourced(&self, cmd: String) -> String {
|
||||||
format!(":{} {}", self.sid, cmd)
|
format!(":{} {}", self.sid, cmd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -28,7 +29,7 @@ impl Protocol for InspIrcd {
|
||||||
format!("CAPAB START {}", self.protocol),
|
format!("CAPAB START {}", self.protocol),
|
||||||
format!("CAPAB CAPABILITIES :PROTOCOL={}", self.protocol),
|
format!("CAPAB CAPABILITIES :PROTOCOL={}", self.protocol),
|
||||||
"CAPAB END".to_string(),
|
"CAPAB END".to_string(),
|
||||||
format!("SERVER {} {} {} :{}", self.name, self.password, self.sid, "Federated Services"),
|
format!("SERVER {} {} {} :{}", self.name, self.password, self.sid, self.description),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,7 +37,7 @@ impl Protocol for InspIrcd {
|
||||||
// Strip an optional IRCv3 message-tag prefix (@k=v;… ) — insp tags PRIVMSGs
|
// Strip an optional IRCv3 message-tag prefix (@k=v;… ) — insp tags PRIVMSGs
|
||||||
// with time/msgid, and it sits before the :source.
|
// with time/msgid, and it sits before the :source.
|
||||||
let line = match line.strip_prefix('@') {
|
let line = match line.strip_prefix('@') {
|
||||||
Some(rest) => rest.splitn(2, ' ').nth(1).unwrap_or(""),
|
Some(rest) => rest.split_once(' ').map(|x| x.1).unwrap_or(""),
|
||||||
None => line,
|
None => line,
|
||||||
};
|
};
|
||||||
let (source, rest) = match line.strip_prefix(':') {
|
let (source, rest) = match line.strip_prefix(':') {
|
||||||
|
|
@ -227,15 +228,15 @@ impl Protocol for InspIrcd {
|
||||||
|
|
||||||
fn serialize(&mut self, action: &NetAction) -> Vec<String> {
|
fn serialize(&mut self, action: &NetAction) -> Vec<String> {
|
||||||
match action {
|
match action {
|
||||||
NetAction::Burst => vec![self.from_us(format!("BURST {}", self.ts))],
|
NetAction::Burst => vec![self.sourced(format!("BURST {}", self.ts))],
|
||||||
NetAction::EndBurst => vec![self.from_us("ENDBURST".to_string())],
|
NetAction::EndBurst => vec![self.sourced("ENDBURST".to_string())],
|
||||||
NetAction::Pong { token, from } => {
|
NetAction::Pong { token, from } => {
|
||||||
let dest = from.clone().unwrap_or_else(|| self.sid.clone());
|
let dest = from.clone().unwrap_or_else(|| self.sid.clone());
|
||||||
vec![self.from_us(format!("PONG {} {}", dest, token))]
|
vec![self.sourced(format!("PONG {} {}", dest, token))]
|
||||||
}
|
}
|
||||||
// insp4 UID: uuid nickts nick realhost disphost realuser dispuser ip signonts +modes :gecos
|
// insp4 UID: uuid nickts nick realhost disphost realuser dispuser ip signonts +modes :gecos
|
||||||
// (both a real AND a displayed user field — see m_spanningtree/uid.cpp Builder).
|
// (both a real AND a displayed user field — see m_spanningtree/uid.cpp Builder).
|
||||||
NetAction::IntroduceUser { uid, nick, ident, host, gecos } => vec![self.from_us(format!(
|
NetAction::IntroduceUser { uid, nick, ident, host, gecos } => vec![self.sourced(format!(
|
||||||
"UID {uid} {ts} {nick} {host} {host} {ident} {ident} 0.0.0.0 {ts} +i :{gecos}",
|
"UID {uid} {ts} {nick} {host} {host} {ident} {ident} 0.0.0.0 {ts} +i :{gecos}",
|
||||||
uid = uid, ts = self.ts, nick = nick, host = host, ident = ident, gecos = gecos
|
uid = uid, ts = self.ts, nick = nick, host = host, ident = ident, gecos = gecos
|
||||||
))],
|
))],
|
||||||
|
|
@ -247,7 +248,7 @@ impl Protocol for InspIrcd {
|
||||||
}
|
}
|
||||||
// ACCTREGRESULT <reqid> <kind> <account> <status> <code> :<message>
|
// ACCTREGRESULT <reqid> <kind> <account> <status> <code> :<message>
|
||||||
NetAction::AccountResponse { reqid, kind, account, status, code, message } => {
|
NetAction::AccountResponse { reqid, kind, account, status, code, message } => {
|
||||||
vec![self.from_us(format!(
|
vec![self.sourced(format!(
|
||||||
"ACCTREGRESULT {} {} {} {} {} :{}",
|
"ACCTREGRESULT {} {} {} {} {} :{}",
|
||||||
reqid, kind, account, status, code, message
|
reqid, kind, account, status, code, message
|
||||||
))]
|
))]
|
||||||
|
|
@ -259,11 +260,11 @@ impl Protocol for InspIrcd {
|
||||||
line.push(' ');
|
line.push(' ');
|
||||||
line.push_str(d);
|
line.push_str(d);
|
||||||
}
|
}
|
||||||
vec![self.from_us(line)]
|
vec![self.sourced(line)]
|
||||||
}
|
}
|
||||||
// METADATA <target> <key> :<value> — "*" is server-global metadata.
|
// METADATA <target> <key> :<value> — "*" is server-global metadata.
|
||||||
NetAction::Metadata { target, key, value } => {
|
NetAction::Metadata { target, key, value } => {
|
||||||
vec![self.from_us(format!("METADATA {} {} :{}", target, key, value))]
|
vec![self.sourced(format!("METADATA {} {} :{}", target, key, value))]
|
||||||
}
|
}
|
||||||
// SVSNICK <uid> <newnick> <nickts> — the new nick takes the current
|
// SVSNICK <uid> <newnick> <nickts> — the new nick takes the current
|
||||||
// time as its TS so it wins any collision resolution.
|
// time as its TS so it wins any collision resolution.
|
||||||
|
|
@ -273,23 +274,23 @@ impl Protocol for InspIrcd {
|
||||||
// ENCAP the target's server: CHGHOST <uid> <newhost>, to set a vhost.
|
// ENCAP the target's server: CHGHOST <uid> <newhost>, to set a vhost.
|
||||||
NetAction::SetHost { uid, host } => {
|
NetAction::SetHost { uid, host } => {
|
||||||
let target = uid.get(..3).unwrap_or(uid.as_str());
|
let target = uid.get(..3).unwrap_or(uid.as_str());
|
||||||
vec![self.from_us(format!("ENCAP {} CHGHOST {} {}", target, uid, host))]
|
vec![self.sourced(format!("ENCAP {} CHGHOST {} {}", target, uid, host))]
|
||||||
}
|
}
|
||||||
NetAction::SetIdent { uid, ident } => {
|
NetAction::SetIdent { uid, ident } => {
|
||||||
let target = uid.get(..3).unwrap_or(uid.as_str());
|
let target = uid.get(..3).unwrap_or(uid.as_str());
|
||||||
vec![self.from_us(format!("ENCAP {} CHGIDENT {} {}", target, uid, ident))]
|
vec![self.sourced(format!("ENCAP {} CHGIDENT {} {}", target, uid, ident))]
|
||||||
}
|
}
|
||||||
NetAction::ForceNick { uid, nick } => {
|
NetAction::ForceNick { uid, nick } => {
|
||||||
let now = SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(self.ts);
|
let now = SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(self.ts);
|
||||||
vec![self.from_us(format!("SVSNICK {} {} {}", uid, nick, now))]
|
vec![self.sourced(format!("SVSNICK {} {} {}", uid, nick, now))]
|
||||||
}
|
}
|
||||||
// SVSJOIN <uid> <chan> [key]: force a user into a channel, sourced from
|
// SVSJOIN <uid> <chan> [key]: force a user into a channel, sourced from
|
||||||
// the services server. Used to apply an account's auto-join list.
|
// the services server. Used to apply an account's auto-join list.
|
||||||
NetAction::ForceJoin { uid, channel, key } => {
|
NetAction::ForceJoin { uid, channel, key } => {
|
||||||
if key.is_empty() {
|
if key.is_empty() {
|
||||||
vec![self.from_us(format!("SVSJOIN {} {}", uid, channel))]
|
vec![self.sourced(format!("SVSJOIN {} {}", uid, channel))]
|
||||||
} else {
|
} else {
|
||||||
vec![self.from_us(format!("SVSJOIN {} {} {}", uid, channel, key))]
|
vec![self.sourced(format!("SVSJOIN {} {} {}", uid, channel, key))]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// FMODE <chan> <ts> <modes>. The ircd drops an FMODE whose TS is newer
|
// FMODE <chan> <ts> <modes>. The ircd drops an FMODE whose TS is newer
|
||||||
|
|
@ -299,7 +300,7 @@ impl Protocol for InspIrcd {
|
||||||
NetAction::ChannelMode { from, channel, modes } => {
|
NetAction::ChannelMode { from, channel, modes } => {
|
||||||
let cmd = format!("FMODE {} 1 {}", channel, modes);
|
let cmd = format!("FMODE {} 1 {}", channel, modes);
|
||||||
if from.is_empty() {
|
if from.is_empty() {
|
||||||
vec![self.from_us(cmd)]
|
vec![self.sourced(cmd)]
|
||||||
} else {
|
} else {
|
||||||
vec![format!(":{} {}", from, cmd)]
|
vec![format!(":{} {}", from, cmd)]
|
||||||
}
|
}
|
||||||
|
|
@ -397,7 +398,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
fn proto() -> InspIrcd {
|
fn proto() -> InspIrcd {
|
||||||
InspIrcd::new("services.test".into(), "42S".into(), "pw".into(), 1206, 1)
|
InspIrcd::new("services.test".into(), "Federated Services".into(), "42S".into(), "pw".into(), 1206, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// A remote nick change must surface as a NickChange for the source uid, or
|
// A remote nick change must surface as a NickChange for the source uid, or
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@ 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.
|
||||||
|
// Every argument is a distinct input the guest-rename needs (the guest nick and
|
||||||
|
// its sequence counter among them), so the count is inherent.
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn handle(me: &str, guest_nick: &str, guest_seq: &mut u32, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView, db: &dyn Store) {
|
pub fn handle(me: &str, guest_nick: &str, guest_seq: &mut u32, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView, db: &dyn Store) {
|
||||||
let Some(&target) = args.get(1) else {
|
let Some(&target) = args.get(1) else {
|
||||||
ctx.notice(me, from.uid, "Syntax: GHOST <nick> [password]");
|
ctx.notice(me, from.uid, "Syntax: GHOST <nick> [password]");
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
||||||
ctx.notice(me, from.uid, "You can't ungroup your main account name.");
|
ctx.notice(me, from.uid, "You can't ungroup your main account name.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if db.resolve_account(nick).map_or(true, |a| !a.eq_ignore_ascii_case(account)) {
|
if db.resolve_account(nick).is_none_or(|a| !a.eq_ignore_ascii_case(account)) {
|
||||||
ctx.notice(me, from.uid, format!("\x02{nick}\x02 isn't grouped to your account."));
|
ctx.notice(me, from.uid, format!("\x02{nick}\x02 isn't grouped to your account."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,9 @@ fn verified_default() -> bool {
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
#[serde(tag = "event")]
|
#[serde(tag = "event")]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
AccountRegistered(Account),
|
// Boxed: Account is far larger than any other variant, so this keeps every
|
||||||
|
// small event in the log from being sized to it.
|
||||||
|
AccountRegistered(Box<Account>),
|
||||||
CertAdded { account: String, fp: String },
|
CertAdded { account: String, fp: String },
|
||||||
CertRemoved { account: String, fp: String },
|
CertRemoved { account: String, fp: String },
|
||||||
AccountEmailSet { account: String, email: Option<String> },
|
AccountEmailSet { account: String, email: Option<String> },
|
||||||
|
|
@ -692,7 +694,7 @@ impl EventLog {
|
||||||
self.entries
|
self.entries
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|e| e.event.scope() == Scope::Global)
|
.filter(|e| e.event.scope() == Scope::Global)
|
||||||
.filter(|e| peer.get(&e.origin).map_or(true, |&s| e.seq > s))
|
.filter(|e| peer.get(&e.origin).is_none_or(|&s| e.seq > s))
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
@ -879,7 +881,7 @@ impl Db {
|
||||||
/// Rewrite the log to one entry per account and channel, reclaiming churn.
|
/// Rewrite the log to one entry per account and channel, reclaiming churn.
|
||||||
pub fn compact(&mut self) -> std::io::Result<()> {
|
pub fn compact(&mut self) -> std::io::Result<()> {
|
||||||
let before = self.log.len();
|
let before = self.log.len();
|
||||||
let mut snapshot: Vec<Event> = self.accounts.values().cloned().map(Event::AccountRegistered).collect();
|
let mut snapshot: Vec<Event> = self.accounts.values().cloned().map(|a| Event::AccountRegistered(Box::new(a))).collect();
|
||||||
for c in self.channels.values() {
|
for c in self.channels.values() {
|
||||||
snapshot.push(Event::ChannelRegistered { name: c.name.clone(), founder: c.founder.clone(), ts: c.ts });
|
snapshot.push(Event::ChannelRegistered { name: c.name.clone(), founder: c.founder.clone(), ts: c.ts });
|
||||||
if !c.lock_on.is_empty() || !c.lock_off.is_empty() {
|
if !c.lock_on.is_empty() || !c.lock_off.is_empty() {
|
||||||
|
|
@ -1041,7 +1043,7 @@ impl Db {
|
||||||
vhost: None,
|
vhost: None,
|
||||||
vhost_request: None,
|
vhost_request: None,
|
||||||
};
|
};
|
||||||
self.log.append(Event::AccountRegistered(account.clone())).map_err(|_| RegError::Internal)?;
|
self.log.append(Event::AccountRegistered(Box::new(account.clone()))).map_err(|_| RegError::Internal)?;
|
||||||
self.accounts.insert(key(name), account);
|
self.accounts.insert(key(name), account);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -1081,7 +1083,7 @@ impl Db {
|
||||||
/// globally unique, so this is unambiguous.
|
/// globally unique, so this is unambiguous.
|
||||||
pub fn certfp_owner(&self, fp: &str) -> Option<&str> {
|
pub fn certfp_owner(&self, fp: &str) -> Option<&str> {
|
||||||
let fp = fp.to_ascii_lowercase();
|
let fp = fp.to_ascii_lowercase();
|
||||||
self.accounts.values().find(|a| a.certfps.iter().any(|c| *c == fp)).map(|a| a.name.as_str())
|
self.accounts.values().find(|a| a.certfps.contains(&fp)).map(|a| a.name.as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The fingerprints registered to an account (empty if unknown/none).
|
/// The fingerprints registered to an account (empty if unknown/none).
|
||||||
|
|
@ -1096,7 +1098,7 @@ impl Db {
|
||||||
|
|
||||||
/// Whether `account`'s email is confirmed (true for unknown/legacy accounts).
|
/// Whether `account`'s email is confirmed (true for unknown/legacy accounts).
|
||||||
pub fn is_verified(&self, account: &str) -> bool {
|
pub fn is_verified(&self, account: &str) -> bool {
|
||||||
self.account(account).map_or(true, |a| a.verified)
|
self.account(account).is_none_or(|a| a.verified)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark `account`'s email confirmed.
|
/// Mark `account`'s email confirmed.
|
||||||
|
|
@ -1443,7 +1445,7 @@ impl Db {
|
||||||
let k = key(account);
|
let k = key(account);
|
||||||
match self.accounts.get(&k) {
|
match self.accounts.get(&k) {
|
||||||
None => return Err(CertError::NoAccount),
|
None => return Err(CertError::NoAccount),
|
||||||
Some(a) if !a.certfps.iter().any(|c| *c == fp) => return Ok(false),
|
Some(a) if !a.certfps.contains(&fp) => return Ok(false),
|
||||||
Some(_) => {}
|
Some(_) => {}
|
||||||
}
|
}
|
||||||
self.log.append(Event::CertRemoved { account: account.to_string(), fp: fp.clone() }).map_err(|_| CertError::Internal)?;
|
self.log.append(Event::CertRemoved { account: account.to_string(), fp: fp.clone() }).map_err(|_| CertError::Internal)?;
|
||||||
|
|
@ -2169,7 +2171,7 @@ fn apply(accounts: &mut HashMap<String, Account>, channels: &mut HashMap<String,
|
||||||
let k = key(&a.name);
|
let k = key(&a.name);
|
||||||
let keep_existing = accounts.get(&k).is_some_and(|cur| owns_over(cur, &a));
|
let keep_existing = accounts.get(&k).is_some_and(|cur| owns_over(cur, &a));
|
||||||
if !keep_existing {
|
if !keep_existing {
|
||||||
accounts.insert(k, a);
|
accounts.insert(k, *a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::CertAdded { account, fp } => {
|
Event::CertAdded { account, fp } => {
|
||||||
|
|
@ -2826,8 +2828,8 @@ mod tests {
|
||||||
};
|
};
|
||||||
let converge = |first: &Account, second: &Account| {
|
let converge = |first: &Account, second: &Account| {
|
||||||
let (mut acc, mut ch, mut gr, mut bo, mut hc) = (HashMap::new(), HashMap::new(), HashMap::new(), HashMap::new(), HostConfig::default());
|
let (mut acc, mut ch, mut gr, mut bo, mut hc) = (HashMap::new(), HashMap::new(), HashMap::new(), HashMap::new(), HostConfig::default());
|
||||||
apply(&mut acc, &mut ch, &mut gr, &mut bo, &mut hc, Event::AccountRegistered(first.clone()));
|
apply(&mut acc, &mut ch, &mut gr, &mut bo, &mut hc, Event::AccountRegistered(Box::new(first.clone())));
|
||||||
apply(&mut acc, &mut ch, &mut gr, &mut bo, &mut hc, Event::AccountRegistered(second.clone()));
|
apply(&mut acc, &mut ch, &mut gr, &mut bo, &mut hc, Event::AccountRegistered(Box::new(second.clone())));
|
||||||
acc["alice"].password_hash.clone()
|
acc["alice"].password_hash.clone()
|
||||||
};
|
};
|
||||||
// Earlier registration wins, regardless of which claim applies first.
|
// Earlier registration wins, regardless of which claim applies first.
|
||||||
|
|
@ -3100,7 +3102,7 @@ mod tests {
|
||||||
name: "bob".into(), password_hash: "x".into(), email: None,
|
name: "bob".into(), password_hash: "x".into(), email: None,
|
||||||
ts: 0, home: "peer".into(), scram256: None, scram512: None, certfps: vec![], verified: true, ajoin: vec![], suspension: None, memos: vec![], greet: String::new(), vhost: None, vhost_request: None,
|
ts: 0, home: "peer".into(), scram256: None, scram512: None, certfps: vec![], verified: true, ajoin: vec![], suspension: None, memos: vec![], greet: String::new(), vhost: None, vhost_request: None,
|
||||||
};
|
};
|
||||||
let entry = LogEntry { origin: "peer".into(), seq: 0, lamport: 1, event: Event::AccountRegistered(bob) };
|
let entry = LogEntry { origin: "peer".into(), seq: 0, lamport: 1, event: Event::AccountRegistered(Box::new(bob)) };
|
||||||
db.ingest(entry).unwrap();
|
db.ingest(entry).unwrap();
|
||||||
assert!(db.exists("bob"), "peer's account is present locally");
|
assert!(db.exists("bob"), "peer's account is present locally");
|
||||||
assert!(db.exists("alice"), "local account still there");
|
assert!(db.exists("alice"), "local account still there");
|
||||||
|
|
|
||||||
|
|
@ -1916,7 +1916,7 @@ mod tests {
|
||||||
name: "alice".into(), password_hash: "OTHER".into(), email: None,
|
name: "alice".into(), password_hash: "OTHER".into(), email: None,
|
||||||
ts: 0, home: "peer".into(), scram256: None, scram512: None, certfps: vec![], verified: true, ajoin: vec![], suspension: None, memos: vec![], greet: String::new(), vhost: None, vhost_request: None,
|
ts: 0, home: "peer".into(), scram256: None, scram512: None, certfps: vec![], verified: true, ajoin: vec![], suspension: None, memos: vec![], greet: String::new(), vhost: None, vhost_request: None,
|
||||||
};
|
};
|
||||||
let entry = LogEntry::for_test("peer", 0, 1, db::Event::AccountRegistered(winner));
|
let entry = LogEntry::for_test("peer", 0, 1, db::Event::AccountRegistered(Box::new(winner)));
|
||||||
e.gossip_ingest(entry).unwrap();
|
e.gossip_ingest(entry).unwrap();
|
||||||
|
|
||||||
// The local session is logged out and its orphaned channel is dropped.
|
// The local session is logged out and its orphaned channel is dropped.
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,9 @@ enum Msg {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
reply: bool,
|
reply: bool,
|
||||||
},
|
},
|
||||||
Entry { entry: LogEntry },
|
// Boxed: a LogEntry carries an Event, whose largest variant dwarfs the other
|
||||||
|
// Msg variants.
|
||||||
|
Entry { entry: Box<LogEntry> },
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the listener (if bound) and a dialer per configured peer. When TLS is
|
// Start the listener (if bound) and a dialer per configured peer. When TLS is
|
||||||
|
|
@ -203,7 +205,7 @@ where
|
||||||
loop {
|
loop {
|
||||||
match rx.recv().await {
|
match rx.recv().await {
|
||||||
Ok(entry) => {
|
Ok(entry) => {
|
||||||
if send(&tx, &Msg::Entry { entry }).await.is_err() {
|
if send(&tx, &Msg::Entry { entry: Box::new(entry) }).await.is_err() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -229,7 +231,7 @@ where
|
||||||
Ok(Msg::Digest { versions, reply }) => {
|
Ok(Msg::Digest { versions, reply }) => {
|
||||||
let missing = engine.lock().await.gossip_missing(&versions);
|
let missing = engine.lock().await.gossip_missing(&versions);
|
||||||
for entry in missing {
|
for entry in missing {
|
||||||
let _ = send(&tx, &Msg::Entry { entry }).await;
|
let _ = send(&tx, &Msg::Entry { entry: Box::new(entry) }).await;
|
||||||
}
|
}
|
||||||
if reply {
|
if reply {
|
||||||
let versions = engine.lock().await.gossip_digest();
|
let versions = engine.lock().await.gossip_digest();
|
||||||
|
|
@ -237,7 +239,7 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Msg::Entry { entry }) => {
|
Ok(Msg::Entry { entry }) => {
|
||||||
if let Err(e) = engine.lock().await.gossip_ingest(entry) {
|
if let Err(e) = engine.lock().await.gossip_ingest(*entry) {
|
||||||
tracing::warn!(%e, "gossip ingest failed");
|
tracing::warn!(%e, "gossip ingest failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,9 @@ const SUBSCRIBER_BUFFER: usize = 1024;
|
||||||
// Every RPC (both services) needs `authorization: Bearer <token>` matching the
|
// Every RPC (both services) needs `authorization: Bearer <token>` matching the
|
||||||
// configured secret. Constant-time compare — same posture as password/secret
|
// configured secret. Constant-time compare — same posture as password/secret
|
||||||
// checks elsewhere in this codebase, cheap insurance against a timing side-channel.
|
// checks elsewhere in this codebase, cheap insurance against a timing side-channel.
|
||||||
|
// Status is tonic's error type, used by every RPC — boxing it here would fight
|
||||||
|
// the framework's convention for marginal size gain.
|
||||||
|
#[allow(clippy::result_large_err)]
|
||||||
fn authorize<T>(req: &Request<T>, token: &str) -> Result<(), Status> {
|
fn authorize<T>(req: &Request<T>, token: &str) -> Result<(), Status> {
|
||||||
let got = req
|
let got = req
|
||||||
.metadata()
|
.metadata()
|
||||||
|
|
@ -409,7 +412,7 @@ mod tests {
|
||||||
vhost: None,
|
vhost: None,
|
||||||
vhost_request: None,
|
vhost_request: None,
|
||||||
};
|
};
|
||||||
let registered = LogEntry::for_test("A", 0, 1, Event::AccountRegistered(acct));
|
let registered = LogEntry::for_test("A", 0, 1, Event::AccountRegistered(Box::new(acct)));
|
||||||
let wire = to_wire(®istered).expect("account registration replicates");
|
let wire = to_wire(®istered).expect("account registration replicates");
|
||||||
match wire.kind {
|
match wire.kind {
|
||||||
Some(Kind::AccountRegistered(a)) => {
|
Some(Kind::AccountRegistered(a)) => {
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
let proto = Box::new(InspIrcd::new(
|
let proto = Box::new(InspIrcd::new(
|
||||||
cfg.server.name.clone(),
|
cfg.server.name.clone(),
|
||||||
|
cfg.server.description.clone(),
|
||||||
cfg.server.sid.clone(),
|
cfg.server.sid.clone(),
|
||||||
cfg.uplink.password.clone(),
|
cfg.uplink.password.clone(),
|
||||||
cfg.server.protocol,
|
cfg.server.protocol,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue