Bläddra i källkod

darkirc: NickServ account registration

This currently just creates a sled tree for the account and generates a keypair.
parazyd 2 år sedan
förälder
incheckning
a9a6cb4ef9
3 ändrade filer med 129 tillägg och 11 borttagningar
  1. 3 0
      bin/darkirc/Makefile
  2. 3 1
      bin/darkirc/src/irc/client.rs
  3. 123 10
      bin/darkirc/src/irc/services/nickserv.rs

+ 3 - 0
bin/darkirc/Makefile

@@ -64,6 +64,9 @@ docker-android:
 _aarch64-android:
 	cargo build --release --target aarch64-linux-android --package darkirc
 
+clippy: all
+	RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) clippy --target=$(RUST_TARGET) --release --package $(BIN) --tests
+
 clean:
 	RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) clean --target=$(RUST_TARGET) --release --package $(BIN)
 	rm -f $(BIN) ../../$(BIN) $(BIN).android64 $(BIN).android32

+ 3 - 1
bin/darkirc/src/irc/client.rs

@@ -119,7 +119,9 @@ impl Client {
             realname: RwLock::new(String::from("*")),
             caps: RwLock::new(caps),
             seen: OnceCell::new(),
-            nickserv: Arc::new(NickServ::new(username.clone(), nickname.clone(), server.clone())),
+            nickserv: Arc::new(
+                NickServ::new(username.clone(), nickname.clone(), server.clone()).await?,
+            ),
         })
     }
 

+ 123 - 10
bin/darkirc/src/irc/services/nickserv.rs

@@ -16,21 +16,30 @@
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
 
-use std::sync::Arc;
+use std::{str::SplitAsciiWhitespace, sync::Arc};
 
 use darkfi::Result;
+use darkfi_sdk::crypto::SecretKey;
+use darkfi_serial::serialize_async;
+use rand::rngs::OsRng;
 use smol::lock::RwLock;
 
 use super::super::{client::ReplyType, rpl::*};
 use crate::IrcServer;
 
+const ACCOUNTS_DB_PREFIX: &str = "darkirc_account_";
+const ACCOUNTS_KEY_SECRET: &[u8] = b"secret_key";
+
 const NICKSERV_USAGE: &str = r#"***** NickServ Help ***** 
 
 NickServ allows a client to perform account management on DarkIRC.
 
 The following commands are available:
 
-  REGISTER      Register an account
+  INFO          Displays information on registrations.
+  REGISTER      Register an account.
+  DEREGISTER    Deregister an account.
+  SET           Select an account to use.
 
 For more information on a NickServ command, type:
 /msg NickServ HELP <command>
@@ -49,14 +58,14 @@ pub struct NickServ {
 }
 
 impl NickServ {
-    /// Instantiate a new `NickServ` for a client.
-    /// This is called from `Client::new()`
-    pub fn new(
+    /// Instantiate a new `NickServ` for a client. This should be called after
+    /// the user/nick are successfully registered.
+    pub async fn new(
         username: Arc<RwLock<String>>,
         nickname: Arc<RwLock<String>>,
         server: Arc<IrcServer>,
-    ) -> Self {
-        Self { username, nickname, server }
+    ) -> Result<Self> {
+        Ok(Self { username, nickname, server })
     }
 
     /// Handle a `NickServ` query. This is the main command handler.
@@ -73,13 +82,99 @@ impl NickServ {
         };
 
         match command.to_uppercase().as_str() {
-            "HELP" => self.reply_help(&nick).await,
-            _x => todo!(),
+            "INFO" => self.handle_info(&nick, &mut tokens).await,
+            "REGISTER" => self.handle_register(&nick, &mut tokens).await,
+            "DEREGISTER" => self.handle_deregister(&nick, &mut tokens).await,
+            "SET" => self.handle_set(&nick, &mut tokens).await,
+            "HELP" => self.handle_help(&nick).await,
+            _ => self.handle_invalid(&nick).await,
+        }
+    }
+
+    /// Handle the INFO command
+    pub async fn handle_info(
+        &self,
+        _nick: &str,
+        _tokens: &mut SplitAsciiWhitespace<'_>,
+    ) -> Result<Vec<ReplyType>> {
+        todo!()
+    }
+
+    /// Handle the REGISTER command
+    pub async fn handle_register(
+        &self,
+        nick: &str,
+        tokens: &mut SplitAsciiWhitespace<'_>,
+    ) -> Result<Vec<ReplyType>> {
+        let Some(account_name) = tokens.next() else {
+            return Ok(vec![ReplyType::Notice((
+                "NickServ".to_string(),
+                nick.to_string(),
+                "Invalid syntax. Use `REGISTER <account_name>`.".to_string(),
+            ))])
+        };
+
+        // Open the sled tree
+        let db = self
+            .server
+            .darkirc
+            .sled
+            .open_tree(format!("{}{}", ACCOUNTS_DB_PREFIX, account_name))?;
+
+        if !db.is_empty() {
+            return Ok(vec![ReplyType::Notice((
+                "NickServ".to_string(),
+                nick.to_string(),
+                "This account is already registered.".to_string(),
+            ))])
         }
+
+        // Create a new secret key and insert it into the db.
+        let secret = SecretKey::random(&mut OsRng);
+        db.insert(ACCOUNTS_KEY_SECRET, serialize_async(&secret).await)?;
+
+        Ok(vec![ReplyType::Notice((
+            "NickServ".to_string(),
+            nick.to_string(),
+            format!("Successfully registered account \"{}\"", account_name),
+        ))])
+    }
+
+    /// Handle the DEREGISTER command
+    pub async fn handle_deregister(
+        &self,
+        nick: &str,
+        tokens: &mut SplitAsciiWhitespace<'_>,
+    ) -> Result<Vec<ReplyType>> {
+        let Some(account_name) = tokens.next() else {
+            return Ok(vec![ReplyType::Notice((
+                "NickServ".to_string(),
+                nick.to_string(),
+                "Invalid syntax. Use `DEREGISTER <account_name>`.".to_string(),
+            ))])
+        };
+
+        // Drop the tree
+        self.server.darkirc.sled.drop_tree(format!("{}{}", ACCOUNTS_DB_PREFIX, account_name))?;
+
+        Ok(vec![ReplyType::Notice((
+            "NickServ".to_string(),
+            nick.to_string(),
+            format!("Successfully deregistered account \"{}\"", account_name),
+        ))])
+    }
+
+    /// Handle the SET command
+    pub async fn handle_set(
+        &self,
+        _nick: &str,
+        _tokens: &mut SplitAsciiWhitespace<'_>,
+    ) -> Result<Vec<ReplyType>> {
+        todo!()
     }
 
     /// Reply to the HELP command
-    pub async fn reply_help(&self, nick: &str) -> Result<Vec<ReplyType>> {
+    pub async fn handle_help(&self, nick: &str) -> Result<Vec<ReplyType>> {
         let replies = NICKSERV_USAGE
             .lines()
             .map(|x| ReplyType::Notice(("NickServ".to_string(), nick.to_string(), x.to_string())))
@@ -87,4 +182,22 @@ impl NickServ {
 
         Ok(replies)
     }
+
+    /// Reply to an invalid command
+    pub async fn handle_invalid(&self, nick: &str) -> Result<Vec<ReplyType>> {
+        let replies = vec![
+            ReplyType::Notice((
+                "NickServ".to_string(),
+                nick.to_string(),
+                "Invalid NickServ command.".to_string(),
+            )),
+            ReplyType::Notice((
+                "NickServ".to_string(),
+                nick.to_string(),
+                "Use /msg NickServ HELP for a NickServ command listing.".to_string(),
+            )),
+        ];
+
+        Ok(replies)
+    }
 }