From 201c4c723ef2e501c200ee7713556bf6886e84e3 Mon Sep 17 00:00:00 2001 From: Andrew Baldwin Date: Fri, 19 Apr 2024 18:01:40 -0700 Subject: [PATCH 1/3] Handle `ERR_NEEDREGGEDNICK` (aka `ERR_NOCHANMODES`). --- data/src/client.rs | 48 +++++++++++++++++++++++++++++++--------- irc/proto/src/command.rs | 2 ++ 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/data/src/client.rs b/data/src/client.rs index cb3a95b0c..579df1643 100644 --- a/data/src/client.rs +++ b/data/src/client.rs @@ -87,6 +87,7 @@ pub struct Client { supports_labels: bool, supports_away_notify: bool, highlight_blackout: HighlightBlackout, + registration_required_channels: Vec, } impl fmt::Debug for Client { @@ -135,6 +136,7 @@ impl Client { supports_labels: false, supports_away_notify: false, highlight_blackout: HighlightBlackout::Blackout(Instant::now()), + registration_required_channels: vec![], } } @@ -443,6 +445,14 @@ impl Client { } Command::Numeric(RPL_LOGGEDIN, _) => { log::info!("[{}] logged in", self.server); + + if !self.registration_required_channels.is_empty() { + for message in group_joins(&self.config, &self.registration_required_channels) { + let _ = self.handle.try_send(message); + } + + self.registration_required_channels.clear(); + } } Command::PRIVMSG(channel, text) | Command::NOTICE(channel, text) => { if let Some(user) = message.user() { @@ -606,7 +616,7 @@ impl Client { } // Send JOIN - for message in group_joins(&self.config) { + for message in group_joins(&self.config, &self.config.channels) { let _ = self.handle.try_send(message); } } @@ -816,6 +826,22 @@ impl Client { #[cfg(feature = "dev")] return None; } + Command::Numeric(ERR_NOCHANMODES, args) => { + let channel = args.get(1)?; + + // If the channel has not been joined but is in the configured channels, + // then interpret this numeric as ERR_NEEDREGGEDNICK (which has the + // same number as ERR_NOCHANMODES) + if self.chanmap.get(channel).is_none() + && self + .config + .channels + .iter() + .any(|config_channel| config_channel == channel) + { + self.registration_required_channels.push(channel.clone()); + } + } _ => {} } @@ -1175,17 +1201,19 @@ pub enum WhoStatus { } /// Group channels together into as few JOIN messages as possible -fn group_joins(config: &config::Server) -> impl Iterator + '_ { +fn group_joins<'a>( + config: &'a config::Server, + channels: &'a [String], +) -> impl Iterator + 'a { const MAX_LEN: usize = proto::format::BYTE_LIMIT - b"JOIN \r\n".len(); - let (without_keys, with_keys): (Vec<_>, Vec<_>) = - config.channels.iter().partition_map(|channel| { - config - .channel_keys - .get(channel) - .map(|key| Either::Right((channel, key))) - .unwrap_or(Either::Left(channel)) - }); + let (without_keys, with_keys): (Vec<_>, Vec<_>) = channels.iter().partition_map(|channel| { + config + .channel_keys + .get(channel) + .map(|key| Either::Right((channel, key))) + .unwrap_or(Either::Left(channel)) + }); let joins_without_keys = without_keys .into_iter() diff --git a/irc/proto/src/command.rs b/irc/proto/src/command.rs index ed2f05120..3640b56ea 100644 --- a/irc/proto/src/command.rs +++ b/irc/proto/src/command.rs @@ -394,6 +394,7 @@ pub enum Numeric { ERR_BANNEDFROMCHAN = 474, ERR_BADCHANNELKEY = 475, ERR_BADCHANMASK = 476, + ERR_NOCHANMODES = 477, ERR_NOPRIVILEGES = 481, ERR_CHANOPRIVSNEEDED = 482, ERR_CANTKILLSERVER = 483, @@ -533,6 +534,7 @@ impl TryFrom for Numeric { 474 => ERR_BANNEDFROMCHAN, 475 => ERR_BADCHANNELKEY, 476 => ERR_BADCHANMASK, + 477 => ERR_NOCHANMODES, 481 => ERR_NOPRIVILEGES, 482 => ERR_CHANOPRIVSNEEDED, 483 => ERR_CANTKILLSERVER, From e5fb7c081c7f98464c005d0abac271a25b5ad9e5 Mon Sep 17 00:00:00 2001 From: Andrew Baldwin Date: Fri, 19 Apr 2024 18:14:16 -0700 Subject: [PATCH 2/3] Changelog update. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6be82b1d4..c801d5112 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Changed: - Simplified onboarding experience for users without a `config.toml` file - MacOS will now also look in `$HOME/.config/halloy` for `config.toml`. - Context menus can be dismissed by pressing Escape +- Join channels that report themselves as requiring registration after logging in # 2024.6 (2024-04-05) From cc87a044f78467b8fb7d918457e34b2cc64d9873 Mon Sep 17 00:00:00 2001 From: Andrew Baldwin Date: Tue, 23 Apr 2024 13:26:54 -0700 Subject: [PATCH 3/3] Improve `group_joins` interface. --- data/src/client.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/data/src/client.rs b/data/src/client.rs index 579df1643..ce7fed521 100644 --- a/data/src/client.rs +++ b/data/src/client.rs @@ -447,7 +447,10 @@ impl Client { log::info!("[{}] logged in", self.server); if !self.registration_required_channels.is_empty() { - for message in group_joins(&self.config, &self.registration_required_channels) { + for message in group_joins( + &self.registration_required_channels, + &self.config.channel_keys, + ) { let _ = self.handle.try_send(message); } @@ -616,7 +619,7 @@ impl Client { } // Send JOIN - for message in group_joins(&self.config, &self.config.channels) { + for message in group_joins(&self.config.channels, &self.config.channel_keys) { let _ = self.handle.try_send(message); } } @@ -1202,15 +1205,13 @@ pub enum WhoStatus { /// Group channels together into as few JOIN messages as possible fn group_joins<'a>( - config: &'a config::Server, channels: &'a [String], + keys: &'a HashMap, ) -> impl Iterator + 'a { const MAX_LEN: usize = proto::format::BYTE_LIMIT - b"JOIN \r\n".len(); let (without_keys, with_keys): (Vec<_>, Vec<_>) = channels.iter().partition_map(|channel| { - config - .channel_keys - .get(channel) + keys.get(channel) .map(|key| Either::Right((channel, key))) .unwrap_or(Either::Left(channel)) });