From d5b4cd96459c51094069e4944c686b10e1910073 Mon Sep 17 00:00:00 2001 From: parazyd Date: Wed, 19 Jul 2023 18:23:32 +0200 Subject: [PATCH] darkirc: Implement user modes and set +v for encrypted messages. --- bin/darkirc/src/crypto.rs | 4 +- bin/darkirc/src/irc/client.rs | 26 +++++++--- bin/darkirc/src/settings.rs | 90 +++++++++++++++++++++++++++++++++-- src/util/pcg.rs | 3 +- 4 files changed, 111 insertions(+), 12 deletions(-) diff --git a/bin/darkirc/src/crypto.rs b/bin/darkirc/src/crypto.rs index c4a7f0014..e7fbefeae 100644 --- a/bin/darkirc/src/crypto.rs +++ b/bin/darkirc/src/crypto.rs @@ -93,7 +93,7 @@ pub fn decrypt_target( } if let Some(salt_box) = &chan_info.salt_box { - let decrypted_target = try_decrypt(&salt_box, &privmsg.target); + let decrypted_target = try_decrypt(salt_box, &privmsg.target); if decrypted_target.is_none() { continue } @@ -111,7 +111,7 @@ pub fn decrypt_target( let cnt_info = configured_contacts.get(cnt_name).unwrap(); if let Some(salt_box) = &cnt_info.salt_box { - let decrypted_target = try_decrypt(&salt_box, &privmsg.target); + let decrypted_target = try_decrypt(salt_box, &privmsg.target); if decrypted_target.is_none() { continue } diff --git a/bin/darkirc/src/irc/client.rs b/bin/darkirc/src/irc/client.rs index 776f06090..c7b2bfdef 100644 --- a/bin/darkirc/src/irc/client.rs +++ b/bin/darkirc/src/irc/client.rs @@ -35,7 +35,7 @@ use darkfi::{ use crate::{ crypto::{decrypt_privmsg, decrypt_target, encrypt_privmsg}, settings, - settings::RPL, + settings::{Nick, UserMode, RPL}, ChannelInfo, PrivMsgEvent, }; @@ -152,15 +152,29 @@ impl IrcClient { return Ok(()) } + let mut encrypted = false; if let Some(salt_box) = &chan_info.salt_box { decrypt_privmsg(salt_box, &mut msg); + encrypted = true; debug!("[P2P] Decrypted received message: {:?}", msg); } - // add the nickname to the channel's names - if !chan_info.names.contains(&msg.nick) { - chan_info.names.push(msg.nick.clone()); - } + // Add the nickname to the channel's names + let mut nick: Nick = msg.nick.clone().into(); + let _mode_change = if chan_info.names.contains(&nick) { + let mut n = chan_info.names.get(&nick).unwrap().clone(); + let mode_change = if encrypted { + n.set_mode(UserMode::Voice) + } else { + n.unset_mode(UserMode::Voice) + }; + chan_info.names.insert(n); + mode_change + } else { + let mode_change = if encrypted { nick.set_mode(UserMode::Voice) } else { None }; + chan_info.names.insert(nick); + mode_change + }; self.reply(&msg.to_string()).await?; } else if self.irc_config.is_cap_end && self.irc_config.is_nick_init { @@ -453,7 +467,7 @@ impl IrcClient { self.irc_config.nickname, RPL::NameReply as u32, chan, - chan_info.names.join(" ") + chan_info.names() ); self.reply(&names_reply).await?; diff --git a/bin/darkirc/src/settings.rs b/bin/darkirc/src/settings.rs index ca4d0428d..5024675ea 100644 --- a/bin/darkirc/src/settings.rs +++ b/bin/darkirc/src/settings.rs @@ -20,7 +20,7 @@ use async_std::sync::Arc; use crypto_box::ChaChaBox; use log::{info, warn}; use serde::{self, Deserialize}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use structopt::StructOpt; use structopt_toml::StructOptToml; use toml::Value; @@ -119,6 +119,86 @@ impl ContactInfo { } } +/// Defined user modes +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] +pub enum UserMode { + None, + Op, + Voice, + HalfOp, + Admin, + Owner, +} + +impl std::fmt::Display for UserMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + match self { + Self::None => write!(f, ""), + Self::Op => write!(f, "@"), + Self::Voice => write!(f, "+"), + Self::HalfOp => write!(f, "%"), + Self::Admin => write!(f, "&"), + Self::Owner => write!(f, "~"), + } + } +} + +/// This struct holds info about a specific nickname within a channel. +/// We usually use it to implement modes. +#[derive(Debug, Clone, Eq)] +pub struct Nick { + name: String, + mode: UserMode, +} + +impl Nick { + pub fn new(name: String) -> Self { + Self { name, mode: UserMode::None } + } + + pub fn set_mode(&mut self, mode: UserMode) -> Option { + if self.mode == mode { + return None + } + + self.mode = mode; + Some(format!("+{}", mode)) + } + + pub fn unset_mode(&mut self, mode: UserMode) -> Option { + if self.mode != mode { + return None + } + + self.mode = mode; + Some(format!("-{}", mode)) + } +} + +impl PartialEq for Nick { + fn eq(&self, other: &Self) -> bool { + self.name == other.name + } +} + +impl From for Nick { + fn from(name: String) -> Self { + Self { name, mode: UserMode::None } + } +} + +impl std::hash::Hash for Nick { + fn hash(&self, state: &mut H) { + state.write(&self.name.clone().into_bytes()); + } +} + +impl std::fmt::Display for Nick { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { + write!(f, "{}{}", self.mode, self.name) + } +} + /// This struct holds information about preconfigured channels. /// In the TOML configuration file, we can configure channels as such: /// ```toml @@ -141,12 +221,16 @@ pub struct ChannelInfo { /// Flag indicates whether the user has joined the channel or not pub joined: bool, /// All nicknames which are visible on the channel - pub names: Vec, + pub names: HashSet, } impl ChannelInfo { pub fn new() -> Result { - Ok(Self { topic: None, salt_box: None, joined: false, names: vec![] }) + Ok(Self { topic: None, salt_box: None, joined: false, names: HashSet::new() }) + } + + pub fn names(&self) -> String { + self.names.iter().map(|n| n.to_string()).collect::>().join(" ") } } diff --git a/src/util/pcg.rs b/src/util/pcg.rs index c414e1b1f..cd0f07ad1 100644 --- a/src/util/pcg.rs +++ b/src/util/pcg.rs @@ -70,7 +70,8 @@ impl RngCore for Pcg32 { } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - Ok(self.fill_bytes(dest)) + self.fill_bytes(dest); + Ok(()) } }