100 lines
3.1 KiB
Rust
100 lines
3.1 KiB
Rust
|
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<String, tokio::io::Error> {
|
||
|
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<String> {
|
||
|
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()))
|
||
|
}
|