summaryrefslogtreecommitdiffstats
path: root/src/bin/client/repl.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/client/repl.rs')
-rw-r--r--src/bin/client/repl.rs126
1 files changed, 124 insertions, 2 deletions
diff --git a/src/bin/client/repl.rs b/src/bin/client/repl.rs
index 1512091..5559cfd 100644
--- a/src/bin/client/repl.rs
+++ b/src/bin/client/repl.rs
@@ -2,10 +2,18 @@ use std::io::{BufRead, BufReader, Write, stdout};
2use std::net::TcpStream; 2use std::net::TcpStream;
3use rusqlite::Connection; 3use rusqlite::Connection;
4use std::time::Instant; 4use std::time::Instant;
5use bcrypt::hash_with_salt;
5 6
6use crate::connection; 7use crate::connection;
7use crate::database; 8use crate::database;
8 9
10const SALT: [u8; 16] = *b"fixedsalt1234567";
11
12fn hash_password(password: &str) -> String {
13 let result = hash_with_salt(password, 10, SALT).unwrap();
14 result.to_string()
15}
16
9use crossterm::{event::{read, Event, KeyCode, KeyModifiers}, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType}, cursor::{MoveToColumn, Show}, execute}; 17use crossterm::{event::{read, Event, KeyCode, KeyModifiers}, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType}, cursor::{MoveToColumn, Show}, execute};
10 18
11macro_rules! println { 19macro_rules! println {
@@ -47,7 +55,7 @@ impl ClientState {
47 55
48 pub fn connect_to(&mut self, addr: &str) { 56 pub fn connect_to(&mut self, addr: &str) {
49 self.addr = Some(addr.to_string()); 57 self.addr = Some(addr.to_string());
50 match connection::connect(addr) { 58 match connection::connect(addr, &self.conn) {
51 Ok(s) => { 59 Ok(s) => {
52 self.stream = Some(s); 60 self.stream = Some(s);
53 database::save_host(&self.conn, addr); 61 database::save_host(&self.conn, addr);
@@ -166,6 +174,108 @@ impl ClientState {
166 } 174 }
167 } 175 }
168 } 176 }
177 "register" => {
178 if self.stream.is_none() {
179 println!("\nNot connected. Use 'connect <ip>:<port>' first.");
180 continue 'outer;
181 }
182 let username = read_line("\nUsername: ");
183 if username.is_empty() {
184 continue 'outer;
185 }
186 let password = read_line("Password: ");
187 if password.is_empty() {
188 continue 'outer;
189 }
190 let hashed = hash_password(&password);
191 let msg = format!("REGISTER {} {}", username, hashed);
192 let s = self.stream.as_mut().unwrap();
193 let writer_stream = s.try_clone().unwrap();
194 let mut writer = writer_stream;
195 let response_reader_stream = s.try_clone().unwrap();
196 let mut response_reader = BufReader::new(response_reader_stream);
197 if let Err(_) = writeln!(writer, "{}", msg) {
198 self.stream = None;
199 println!("\nConnection lost");
200 continue 'outer;
201 }
202 let mut response = String::new();
203 if let Err(_) = response_reader.read_line(&mut response) {
204 self.stream = None;
205 println!("\nConnection lost");
206 } else {
207 println!("\n{}", response.trim());
208 }
209 }
210 "login" => {
211 if self.stream.is_none() {
212 println!("\nNot connected. Use 'connect <ip>:<port>' first.");
213 continue 'outer;
214 }
215 loop {
216 let username = read_line("\nUsername: ");
217 if username.is_empty() {
218 break;
219 }
220 let password = read_line("Password: ");
221 if password.is_empty() {
222 break;
223 }
224 let hashed = hash_password(&password);
225 let msg = format!("AUTH {} {}", username, hashed);
226 let s = self.stream.as_mut().unwrap();
227 let writer_stream = s.try_clone().unwrap();
228 let mut writer = writer_stream;
229 let response_reader_stream = s.try_clone().unwrap();
230 let mut response_reader = BufReader::new(response_reader_stream);
231 if let Err(_) = writeln!(writer, "{}", msg) {
232 self.stream = None;
233 println!("\nConnection lost");
234 break;
235 }
236 let mut response = String::new();
237 if let Err(_) = response_reader.read_line(&mut response) {
238 self.stream = None;
239 println!("\nConnection lost");
240 break;
241 } else {
242 let resp = response.trim();
243 println!("\n{}", resp);
244 if resp.starts_with("OK") {
245 // Save credentials
246 if let Some(addr) = &self.addr {
247 if let Err(e) = database::save_credentials(&self.conn, addr, &username, &password) {
248 println!("Failed to save credentials: {}", e);
249 }
250 }
251 break;
252 } else {
253 println!("Authentication failed, try again or press Enter to cancel.");
254 }
255 }
256 }
257 }
258 "setcred" => {
259 if self.addr.is_none() {
260 println!("\nNo host connected. Use 'connect <ip>:<port>' first.");
261 continue 'outer;
262 }
263 let username = read_line("\nUsername: ");
264 if username.is_empty() {
265 continue 'outer;
266 }
267 let password = read_line("Password: ");
268 if password.is_empty() {
269 continue 'outer;
270 }
271 if let Some(addr) = &self.addr {
272 if let Err(e) = database::save_credentials(&self.conn, addr, &username, &password) {
273 println!("Failed to save credentials: {}", e);
274 } else {
275 println!("Credentials saved for {}", addr);
276 }
277 }
278 }
169 "help" => { 279 "help" => {
170 help(); 280 help();
171 } 281 }
@@ -208,6 +318,18 @@ fn redraw(current_input: &str) {
208 execute!(stdout(), MoveToColumn((2 + current_input.len()) as u16)).unwrap(); 318 execute!(stdout(), MoveToColumn((2 + current_input.len()) as u16)).unwrap();
209} 319}
210 320
321fn read_line(prompt: &str) -> String {
322 disable_raw_mode().unwrap();
323 execute!(stdout(), Show).unwrap();
324 print!("{}", prompt);
325 stdout().flush().unwrap();
326 let mut input = String::new();
327 std::io::stdin().read_line(&mut input).unwrap();
328 enable_raw_mode().unwrap();
329 execute!(stdout(), crossterm::cursor::Hide).unwrap();
330 input.trim().to_string()
331}
332
211fn help() { 333fn help() {
212 println!("Available commands: connect <ip>:<port>, send <message>, disconnect, ping, help, quit"); 334 println!("Available commands: connect <ip>:<port>, send <message>, disconnect, ping, login, register, setcred, help, quit");
213} 335}