use std::io; use std::net::SocketAddrV4; use tokio::net::{TcpListener, TcpStream}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tauri::{AppHandle, Manager}; mod errors; use errors::RequestError; pub async fn serve(addr: SocketAddrV4, app_handle: AppHandle) -> io::Result<()> { let listener = TcpListener::bind(&addr).await?; println!("Listening on {addr}"); loop { let new_handle = app_handle.app_handle(); match listener.accept().await { Ok((stream, _)) => { tokio::spawn(async { if let Err(e) = handle(stream, new_handle).await { eprintln!("{e}"); } }); }, Err(e) => { println!("Error accepting connection: {e}"); } } } } // it doesn't really return a String, we just need to placate the compiler async fn stall(stream: &mut TcpStream) -> Result { let delay = std::time::Duration::from_secs(1); loop { tokio::time::sleep(delay).await; stream.write(b"x").await?; } } async fn handle(mut stream: TcpStream, app_handle: AppHandle) -> Result<(), RequestError> { let mut buf = [0; 8192]; // it's what tokio's BufReader uses let mut n = 0; loop { n += stream.read(&mut buf[n..]).await?; if &buf[(n - 4)..n] == b"\r\n\r\n" {break;} if n == buf.len() {return Err(RequestError::RequestTooLarge);} } println!("{}", std::str::from_utf8(&buf).unwrap()); stream.write(b"HTTP/1.0 200 OK\r\n").await?; stream.write(b"Content-Type: application/json\r\n").await?; stream.write(b"X-Creddy-delaying-tactic: ").await?; let creds = tokio::select!{ r = stall(&mut stream) => r?, // this will never return Ok, just Err if it can't write to the stream c = get_creds(&app_handle) => c?, }; stream.write(b"\r\nContent-Length: ").await?; stream.write(creds.as_bytes().len().to_string().as_bytes()).await?; stream.write(b"\r\n\r\n").await?; stream.write(creds.as_bytes()).await?; stream.write(b"\r\n\r\n").await?; Ok(()) } use tokio::io::{stdin, stdout, BufReader, AsyncBufReadExt}; use crate::storage; use tokio::sync::oneshot; async fn get_creds(app_handle: &AppHandle) -> io::Result { app_handle.emit_all("credentials-request", ()).unwrap(); // let mut out = stdout(); // out.write_all(b"Enter passphrase: ").await?; // out.flush().await?; let (tx, rx) = oneshot::channel(); app_handle.once_global("passphrase-entered", |event| { match event.payload() { Some(p) => {tx.send(p.to_string());} None => {tx.send("".to_string());} // will fail decryption, we just need to unblock the outer function } }); // Error is only returned if the rx is closed/dropped before receiving, which should never happen let passphrase = rx.await.unwrap(); // let mut passphrase = String::new(); // let mut reader = BufReader::new(stdin()); // reader.read_line(&mut passphrase).await?; Ok(storage::load(&passphrase.trim())) }