Просмотр исходного кода

darkirc: Implement user modes and set +v for encrypted messages.

parazyd 3 лет назад
Родитель
Сommit
d5b4cd9645
4 измененных файлов с 111 добавлено и 12 удалено
  1. 2 2
      bin/darkirc/src/crypto.rs
  2. 20 6
      bin/darkirc/src/irc/client.rs
  3. 87 3
      bin/darkirc/src/settings.rs
  4. 2 1
      src/util/pcg.rs

+ 2 - 2
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
             }

+ 20 - 6
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<C: AsyncRead + AsyncWrite + Send + Unpin + 'static> IrcClient<C> {
                 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<C: AsyncRead + AsyncWrite + Send + Unpin + 'static> IrcClient<C> {
                     self.irc_config.nickname,
                     RPL::NameReply as u32,
                     chan,
-                    chan_info.names.join(" ")
+                    chan_info.names()
                 );
 
                 self.reply(&names_reply).await?;

+ 87 - 3
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<String> {
+        if self.mode == mode {
+            return None
+        }
+
+        self.mode = mode;
+        Some(format!("+{}", mode))
+    }
+
+    pub fn unset_mode(&mut self, mode: UserMode) -> Option<String> {
+        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<String> for Nick {
+    fn from(name: String) -> Self {
+        Self { name, mode: UserMode::None }
+    }
+}
+
+impl std::hash::Hash for Nick {
+    fn hash<H: core::hash::Hasher>(&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<String>,
+    pub names: HashSet<Nick>,
 }
 
 impl ChannelInfo {
     pub fn new() -> Result<Self> {
-        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::<Vec<String>>().join(" ")
     }
 }
 

+ 2 - 1
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(())
     }
 }