summaryrefslogtreecommitdiffstats
path: root/src/client/repl.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/repl.rs')
-rw-r--r--src/client/repl.rs335
1 files changed, 0 insertions, 335 deletions
diff --git a/src/client/repl.rs b/src/client/repl.rs
deleted file mode 100644
index 5bcb2a3..0000000
--- a/src/client/repl.rs
+++ /dev/null
@@ -1,335 +0,0 @@
1use std::io::{BufRead, BufReader, Write, stdout};
2use std::net::TcpStream;
3use rusqlite::Connection;
4use std::time::Instant;
5use bcrypt::hash_with_salt;
6
7use crate::connection;
8use crate::database;
9
10const SALT: [u8; 16] = *b"fixedsalt1234567";
11
12pub fn hash_password(password: &str) -> String {
13 let result = hash_with_salt(password, 10, SALT).unwrap();
14 result.to_string()
15}
16
17use crossterm::{event::{read, Event, KeyCode, KeyModifiers}, terminal::{disable_raw_mode, enable_raw_mode, Clear, ClearType}, cursor::{MoveToColumn, Show}, execute};
18
19macro_rules! println {
20 () => {
21 print!("\r\n")
22 };
23 ($($arg:tt)*) => {
24 print!("\r{}\n", format_args!($($arg)*))
25 };
26}
27
28pub struct ClientState {
29 pub stream: Option<TcpStream>,
30 pub conn: Connection,
31 pub addr: Option<String>,
32 pub default_port: u16,
33 pub history: Vec<String>,
34 pub history_index: usize,
35}
36
37impl ClientState {
38 pub fn new() -> Self {
39 let conn = database::init_db();
40 let default_ip = "127.0.0.1".to_string();
41 let default_port = 11525;
42 let last_host = database::get_last_host(&conn);
43 let default_host = format!("{}:{}", default_ip, default_port);
44 let addr = last_host.or(Some(default_host));
45
46 Self {
47 stream: None,
48 conn,
49 addr,
50 default_port,
51 history: vec![],
52 history_index: 0,
53 }
54 }
55
56 pub fn connect_to(&mut self, addr: &str) {
57 self.addr = Some(addr.to_string());
58 match connection::connect(addr, &self.conn) {
59 Ok(s) => {
60 self.stream = Some(s);
61 database::save_host(&self.conn, addr);
62 }
63 Err(e) => println!("Failed to connect: {}", e),
64 }
65 }
66
67 pub fn run(&mut self) {
68 enable_raw_mode().unwrap();
69 'outer: loop {
70 let mut current_input = String::new();
71 self.history_index = self.history.len();
72 redraw("");
73 loop {
74 match read().unwrap() {
75 Event::Key(key) => match key.code {
76 KeyCode::Up => {
77 if self.history_index > 0 {
78 self.history_index -= 1;
79 current_input = self.history[self.history_index].clone();
80 redraw(&current_input);
81 }
82 }
83 KeyCode::Down => {
84 if self.history_index < self.history.len() {
85 self.history_index += 1;
86 current_input = if self.history_index == self.history.len() { "".to_string() } else { self.history[self.history_index].clone() };
87 redraw(&current_input);
88 }
89 }
90 KeyCode::Enter => {
91 let line = current_input.trim().to_string();
92 if !line.is_empty() {
93 self.history.push(line.clone());
94 }
95 // Process command
96 let parts: Vec<&str> = line.splitn(2, ' ').collect();
97 match parts[0] {
98 "connect" => {
99 let addr_str = if parts.len() < 2 {
100 if let Some(h) = &self.addr {
101 h.clone()
102 } else {
103 println!("\nNo host specified and no previous connection. Usage: connect <ip>:<port>");
104 continue 'outer;
105 }
106 } else {
107 let mut a = parts[1].to_string();
108 if !a.contains(':') {
109 a = format!("{}:{}", a, self.default_port);
110 }
111 a
112 };
113 self.connect_to(&addr_str);
114 }
115 "send" => {
116 if self.stream.is_none() {
117 println!("\nNot connected. Use 'connect <ip>:<port>' first.");
118 continue 'outer;
119 }
120 if parts.len() < 2 {
121 println!("\nUsage: send <message>");
122 continue 'outer;
123 }
124 let msg = parts[1];
125 let s = self.stream.as_mut().unwrap();
126 let writer_stream = s.try_clone().unwrap();
127 let mut writer = writer_stream;
128 let response_reader_stream = s.try_clone().unwrap();
129 let mut response_reader = BufReader::new(response_reader_stream);
130 if let Err(_) = writeln!(writer, "{}", msg) {
131 self.stream = None;
132 println!("\nConnection lost");
133 continue 'outer;
134 }
135 let mut response = String::new();
136 if let Err(_) = response_reader.read_line(&mut response) {
137 self.stream = None;
138 println!("\nConnection lost");
139 } else {
140 println!("\nEcho: {}", response.trim());
141 }
142 }
143 "disconnect" => {
144 self.stream = None;
145 println!("\nDisconnected");
146 }
147 "ping" => {
148 if self.stream.is_none() {
149 println!("\nNot connected. Use 'connect <ip>:<port>' first.");
150 continue 'outer;
151 }
152 let s = self.stream.as_mut().unwrap();
153 let writer_stream = s.try_clone().unwrap();
154 let mut writer = writer_stream;
155 let start = Instant::now();
156 let response_reader_stream = s.try_clone().unwrap();
157 let mut response_reader = BufReader::new(response_reader_stream);
158 if let Err(_) = writeln!(writer, "PING") {
159 self.stream = None;
160 println!("\nConnection lost");
161 continue 'outer;
162 }
163 let mut response = String::new();
164 if let Err(_) = response_reader.read_line(&mut response) {
165 self.stream = None;
166 println!("\nConnection lost");
167 } else {
168 if response.trim() == "PING" {
169 let elapsed = start.elapsed();
170 println!("\nPong ({:?})", elapsed);
171 } else {
172 self.stream = None;
173 println!("\nConnection lost");
174 }
175 }
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 }
279 "help" => {
280 help();
281 }
282 "quit" => break 'outer,
283 _ => help(),
284 }
285 break;
286 }
287 KeyCode::Backspace => {
288 if !current_input.is_empty() {
289 current_input.pop();
290 redraw(&current_input);
291 }
292 }
293 KeyCode::Char(c) => {
294 if key.modifiers.contains(KeyModifiers::CONTROL) && c == 'c' {
295 disable_raw_mode().unwrap();
296 execute!(stdout(), Show).unwrap();
297 return;
298 } else {
299 current_input.push(c);
300 redraw(&current_input);
301 }
302 }
303 _ => {}
304 }
305 _ => {}
306 }
307 }
308 }
309 disable_raw_mode().unwrap();
310 execute!(stdout(), Show).unwrap();
311 }
312}
313
314fn redraw(current_input: &str) {
315 execute!(stdout(), Clear(ClearType::CurrentLine), MoveToColumn(0)).unwrap();
316 print!("> {}", current_input);
317 stdout().flush().unwrap();
318 execute!(stdout(), MoveToColumn((2 + current_input.len()) as u16)).unwrap();
319}
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
333fn help() {
334 println!("Available commands: connect <ip>:<port>, send <message>, disconnect, ping, login, register, setcred, help, quit");
335}