use std::io::{BufRead, BufReader, Write}; use std::net::TcpStream; use std::sync::mpsc; use std::thread; use std::time::{Duration, Instant}; use rusqlite::Connection; use bcrypt::hash_with_salt; 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() } pub fn connect(addr: &str, conn: &Connection) -> Result> { let (tx, rx) = mpsc::channel(); let addr_str = addr.to_string(); thread::spawn(move || { match TcpStream::connect(&addr_str) { Ok(s) => tx.send(Ok(s)).unwrap(), Err(e) => tx.send(Err(e)).unwrap(), } }); match rx.recv_timeout(Duration::from_secs(3)) { Ok(Ok(s)) => { let mut ping_writer = s.try_clone().unwrap(); let mut ping_reader = BufReader::new(s.try_clone().unwrap()); let start = Instant::now(); writeln!(ping_writer, "PING").unwrap(); let mut response = String::new(); ping_reader.read_line(&mut response).unwrap(); let elapsed = start.elapsed(); println!("\n\rConnected to {} with ping delay: {:?}", addr, elapsed); // Check for credentials and auto-auth if let Some((username, password)) = database::get_credentials(conn, addr) { let hashed = hash_password(&password); let auth_msg = format!("AUTH {} {}", username, hashed); writeln!(ping_writer, "{}", auth_msg).unwrap(); let mut auth_response = String::new(); ping_reader.read_line(&mut auth_response).unwrap(); if auth_response.trim().starts_with("OK") { println!("Authenticated as {}", username); } else { println!("Authentication failed: {}", auth_response.trim()); // Do not fail, allow manual login } } Ok(s) } Ok(Err(e)) => Err(Box::new(e)), Err(mpsc::RecvTimeoutError::Timeout) => Err("Connection timed out after 3 seconds".into()), Err(_) => Err("Connection attempt failed".into()), } }