From 7ca1b5aeacd9633e5a3dca9f7596975dbc686508 Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 1 Mar 2026 14:39:56 +0100 Subject: slowly preparing for actual casino --- src/bin/client/repl.rs | 156 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 126 insertions(+), 30 deletions(-) (limited to 'src/bin/client/repl.rs') diff --git a/src/bin/client/repl.rs b/src/bin/client/repl.rs index 5559cfd..b2f6ecd 100644 --- a/src/bin/client/repl.rs +++ b/src/bin/client/repl.rs @@ -2,28 +2,13 @@ use std::io::{BufRead, BufReader, Write, stdout}; use std::net::TcpStream; use rusqlite::Connection; use std::time::Instant; -use bcrypt::hash_with_salt; use crate::connection; use crate::database; -const SALT: [u8; 16] = *b"fixedsalt1234567"; - -fn hash_password(password: &str) -> String { - let result = hash_with_salt(password, 10, SALT).unwrap(); - result.to_string() -} - use crossterm::{event::{read, Event, KeyCode, KeyModifiers}, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType}, cursor::{MoveToColumn, Show}, execute}; -macro_rules! println { - () => { - print!("\r\n") - }; - ($($arg:tt)*) => { - print!("\r{}\n", format_args!($($arg)*)) - }; -} +use sha2::{Sha256, Digest}; pub struct ClientState { pub stream: Option, @@ -32,6 +17,7 @@ pub struct ClientState { pub default_port: u16, pub history: Vec, pub history_index: usize, + pub logged_in_user: Option, } impl ClientState { @@ -50,14 +36,16 @@ impl ClientState { default_port, history: vec![], history_index: 0, + logged_in_user: None, } } pub fn connect_to(&mut self, addr: &str) { self.addr = Some(addr.to_string()); match connection::connect(addr, &self.conn) { - Ok(s) => { + Ok((s, authed_user)) => { self.stream = Some(s); + self.logged_in_user = authed_user; database::save_host(&self.conn, addr); } Err(e) => println!("Failed to connect: {}", e), @@ -142,6 +130,7 @@ impl ClientState { } "disconnect" => { self.stream = None; + self.logged_in_user = None; println!("\nDisconnected"); } "ping" => { @@ -165,7 +154,7 @@ impl ClientState { self.stream = None; println!("\nConnection lost"); } else { - if response.trim() == "PING" { + if response.trim() == "PONG" { let elapsed = start.elapsed(); println!("\nPong ({:?})", elapsed); } else { @@ -187,7 +176,7 @@ impl ClientState { if password.is_empty() { continue 'outer; } - let hashed = hash_password(&password); + let hashed = format!("{:x}", Sha256::digest(password.as_bytes())); let msg = format!("REGISTER {} {}", username, hashed); let s = self.stream.as_mut().unwrap(); let writer_stream = s.try_clone().unwrap(); @@ -204,7 +193,14 @@ impl ClientState { self.stream = None; println!("\nConnection lost"); } else { - println!("\n{}", response.trim()); + let resp = response.trim(); + if resp == format!("REGISTERED {}", username) { + println!("\nSuccessfully registered as {}", username); + } else if resp.starts_with("ERROR ") { + println!("\nRegistration failed: {}", &resp[6..]); + } else { + println!("\n{}", resp); + } } } "login" => { @@ -212,6 +208,10 @@ impl ClientState { println!("\nNot connected. Use 'connect :' first."); continue 'outer; } + if self.logged_in_user.is_some() { + println!("\nAlready logged in as {}", self.logged_in_user.as_ref().unwrap()); + continue 'outer; + } loop { let username = read_line("\nUsername: "); if username.is_empty() { @@ -221,7 +221,7 @@ impl ClientState { if password.is_empty() { break; } - let hashed = hash_password(&password); + let hashed = format!("{:x}", Sha256::digest(password.as_bytes())); let msg = format!("AUTH {} {}", username, hashed); let s = self.stream.as_mut().unwrap(); let writer_stream = s.try_clone().unwrap(); @@ -240,17 +240,21 @@ impl ClientState { break; } else { let resp = response.trim(); - println!("\n{}", resp); if resp.starts_with("OK") { // Save credentials if let Some(addr) = &self.addr { - if let Err(e) = database::save_credentials(&self.conn, addr, &username, &password) { + if let Err(e) = database::save_credentials(&self.conn, addr, &username, &hashed) { println!("Failed to save credentials: {}", e); } } + self.logged_in_user = Some(username.clone()); + println!("\nLogged in as {}", username); break; + } else if resp.starts_with("ERROR ") { + println!("\nAuthentication failed: {}", &resp[6..]); + println!("Try again or press Enter to cancel."); } else { - println!("Authentication failed, try again or press Enter to cancel."); + println!("\n{}", resp); } } } @@ -268,14 +272,54 @@ impl ClientState { if password.is_empty() { continue 'outer; } + let hashed = format!("{:x}", Sha256::digest(password.as_bytes())); if let Some(addr) = &self.addr { - if let Err(e) = database::save_credentials(&self.conn, addr, &username, &password) { + if let Err(e) = database::save_credentials(&self.conn, addr, &username, &hashed) { println!("Failed to save credentials: {}", e); } else { println!("Credentials saved for {}", addr); } } } + "logout" => { + self.logged_in_user = None; + println!("\nLogged out"); + } + "info" => { + if self.stream.is_none() { + println!("\nNot connected. Use 'connect :' first."); + continue 'outer; + } + let s = self.stream.as_mut().unwrap(); + let writer_stream = s.try_clone().unwrap(); + let mut writer = writer_stream; + let response_reader_stream = s.try_clone().unwrap(); + let mut response_reader = BufReader::new(response_reader_stream); + if let Err(_) = writeln!(writer, "INFO") { + self.stream = None; + println!("\nConnection lost"); + continue 'outer; + } + let mut response = String::new(); + if let Err(_) = response_reader.read_line(&mut response) { + self.stream = None; + println!("\nConnection lost"); + } else { + let resp = response.trim(); + if resp.starts_with("INFO ") { + let parts: Vec<&str> = resp.split_whitespace().collect(); + if parts.len() == 4 { + println!("\nLogged in as {} (ID: {})\r\nBalance: {} PLN", parts[1], parts[2], parts[3]); + } else { + println!("\n{}", resp); + } + } else if resp.starts_with("ERROR ") { + println!("\nError: {}", &resp[6..]); + } else { + println!("\n{}", resp); + } + } + } "help" => { help(); } @@ -295,11 +339,16 @@ impl ClientState { disable_raw_mode().unwrap(); execute!(stdout(), Show).unwrap(); return; + } else if c == '\t' { + self.handle_tab_completion(&mut current_input); } else { current_input.push(c); redraw(¤t_input); } } + KeyCode::Tab => { + self.handle_tab_completion(&mut current_input); + } _ => {} } _ => {} @@ -309,6 +358,58 @@ impl ClientState { disable_raw_mode().unwrap(); execute!(stdout(), Show).unwrap(); } + + fn handle_tab_completion(&mut self, current_input: &mut String) { + let prefix = current_input.trim(); + let commands = get_commands(); + let matches: Vec<_> = commands.iter().filter(|c| c.starts_with(prefix)).collect(); + if matches.len() == 1 { + *current_input = matches[0].clone(); + redraw(current_input); + } else if matches.len() > 1 { + disable_raw_mode().unwrap(); + execute!(stdout(), Show).unwrap(); + println!("\nPossible completions:"); + for m in &matches { + print!("{} ", m); + } + println!(); + enable_raw_mode().unwrap(); + redraw(current_input); + } + // else do nothing + } +} + +fn get_commands() -> Vec { + vec![ + "connect".to_string(), + "send".to_string(), + "disconnect".to_string(), + "ping".to_string(), + "login".to_string(), + "register".to_string(), + "setcred".to_string(), + "logout".to_string(), + "info".to_string(), + "help".to_string(), + "quit".to_string(), + ] +} + +fn help() { + println!("\nAvailable commands:\r + connect : - connect to the server\r + send - send a message to the server\r + disconnect - disconnect from the server\r + ping - ping the server\r + login - login to the server\r + register - register a new user\r + setcred - set credentials for a host\r + logout - logout from the server\r + info - show login info\r + help - show this message\r + quit - quit the program\n"); } fn redraw(current_input: &str) { @@ -326,10 +427,5 @@ fn read_line(prompt: &str) -> String { let mut input = String::new(); std::io::stdin().read_line(&mut input).unwrap(); enable_raw_mode().unwrap(); - execute!(stdout(), crossterm::cursor::Hide).unwrap(); input.trim().to_string() } - -fn help() { - println!("Available commands: connect :, send , disconnect, ping, login, register, setcred, help, quit"); -} -- cgit v1.2.3