diff --git a/Cargo.lock b/Cargo.lock index bbbebdd..df4de42 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,7 @@ version = "0.1.0" dependencies = [ "aes-gcm", "async-trait", + "async-tungstenite", "aws-lc-rs", "axum", "base64", @@ -138,6 +139,22 @@ dependencies = [ "syn", ] +[[package]] +name = "async-tungstenite" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f89c129ab749940f95509d84950c62092c8b4bc6e386ddb162229037a6ec91" +dependencies = [ + "atomic-waker", + "futures-core", + "futures-io", + "futures-task", + "futures-util", + "log", + "pin-project-lite", + "tungstenite", +] + [[package]] name = "atomic-waker" version = "1.1.2" diff --git a/Cargo.toml b/Cargo.toml index 96ba88e..8373371 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ serde = { version = "1.0.219", features = ["derive"] } sha2 = "*" sys-info = "*" sysinfo = "0.30" -tokio = { version = "*", features = ["full"] } +tokio = { version = "1.48.0", features = ["full"] } tokio-util = { version = "*", features = ["full"] } tokio-tungstenite = { version = "*", features = ["native-tls"] } tower = "*" @@ -49,3 +49,4 @@ tokio-rustls = "0.26.4" rustls-pemfile = "2.2.0" async-trait = "0.1.89" sha1 = "0.10.6" +async-tungstenite = "0.32.0" diff --git a/src/auth/local_auth.rs b/src/auth/local_auth.rs new file mode 100644 index 0000000..cdc7adb --- /dev/null +++ b/src/auth/local_auth.rs @@ -0,0 +1,19 @@ +use json::JsonValue; +use uuid::Uuid; + +use crate::util::file_util::load_file; + +pub fn is_private_key_valid(user_id: &Uuid, key_hash: &str) -> bool { + let file_contents = load_file("", "users.json"); + + let users = json::parse(&file_contents).unwrap(); + if let JsonValue::Array(users_array) = users { + for user in users_array { + if user["uuid"] == user_id.to_string() && user["private_key_hash"] == key_hash { + return true; + } + } + } + + false +} diff --git a/src/auth/mod.rs b/src/auth/mod.rs index 8eef5c3..ba5d612 100644 --- a/src/auth/mod.rs +++ b/src/auth/mod.rs @@ -1,2 +1,3 @@ pub mod auth_connector; pub mod crypto_helper; +pub mod local_auth; diff --git a/src/data/communication.rs b/src/data/communication.rs index 386895a..c9d2312 100644 --- a/src/data/communication.rs +++ b/src/data/communication.rs @@ -10,6 +10,9 @@ use uuid::Uuid; pub enum DataTypes { error_type, accepted_ids, + uuid, + settings, + settings_name, chat_partner_id, iota_id, user_id, @@ -84,6 +87,9 @@ impl DataTypes { match normalized.as_str() { "errortype" => DataTypes::error_type, "chatpartnerid" => DataTypes::chat_partner_id, + "uuid" => DataTypes::uuid, + "settings" => DataTypes::settings, + "settingsname" => DataTypes::settings_name, "iotaid" => DataTypes::iota_id, "userid" => DataTypes::user_id, "userids" => DataTypes::user_ids, @@ -163,6 +169,9 @@ pub enum CommunicationType { error_invalid_secret, error_invalid_private_key, success, + settings_save, + settings_load, + settings_list, message, message_send, message_live, @@ -191,6 +200,7 @@ pub enum CommunicationType { iota_closed, client_changed, client_connected, + client_disconnected, client_closed, public_key, private_key, @@ -212,6 +222,9 @@ impl CommunicationType { match normalized.as_str() { "error" => CommunicationType::error, + "settingssave" => CommunicationType::settings_save, + "settingsload" => CommunicationType::settings_load, + "settingslist" => CommunicationType::settings_list, "success" => CommunicationType::success, "message" => CommunicationType::message, "messagelive" => CommunicationType::message_live, @@ -241,6 +254,7 @@ impl CommunicationType { "iotaclosed" => CommunicationType::iota_closed, "clientchanged" => CommunicationType::client_changed, "clientconnected" => CommunicationType::client_connected, + "clientdisconnected" => CommunicationType::client_disconnected, "clientclosed" => CommunicationType::client_closed, "publickey" => CommunicationType::public_key, "privatekey" => CommunicationType::private_key, diff --git a/src/main.rs b/src/main.rs index fc5b744..241d418 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,6 +24,7 @@ use crate::gui::log_panel::{log_message, log_message_trans}; use crate::gui::{log_panel, ratatui_interface}; use crate::langu::language_creator; use crate::langu::language_manager::format; +use crate::omikron::omikron_connection::OMIKRON_CONNECTION; use crate::omikron::omikron_connection::OmikronConnection; use crate::server::server::start; use crate::users::user_manager; @@ -31,6 +32,7 @@ use crate::util::config_util::CONFIG; pub static APP_STATE: LazyLock>> = LazyLock::new(|| Arc::new(Mutex::new(AppState::new()))); + #[tokio::main(flavor = "multi_thread", worker_threads = 8)] #[allow(unused_must_use, dead_code)] async fn main() { @@ -115,7 +117,7 @@ async fn main() { } loop { - let omikron: OmikronConnection = OmikronConnection::new(); + let omikron: Arc = Arc::new(OmikronConnection::new()); omikron.connect().await; omikron .send_message( @@ -131,6 +133,8 @@ async fn main() { .to_string(), ) .await; + let mut omikron_connection = OMIKRON_CONNECTION.write().await; + *omikron_connection = Some(omikron.clone()); log_message_trans("setup_completed"); loop { if !omikron.is_connected().await { diff --git a/src/omikron/omikron_connection.rs b/src/omikron/omikron_connection.rs index 9093586..bbda44f 100644 --- a/src/omikron/omikron_connection.rs +++ b/src/omikron/omikron_connection.rs @@ -1,3 +1,4 @@ +use crate::auth::local_auth; use crate::data::communication::{CommunicationType, CommunicationValue, DataTypes}; use crate::gui::log_panel::{log_cv, log_message, log_message_trans}; use crate::langu::language_manager::format; @@ -5,33 +6,39 @@ use crate::users::contact::Contact; use crate::users::user_community_util::UserCommunityUtil; use crate::util::chat_files; use crate::util::chats_util::{get_user, get_users, mod_user}; +use crate::util::file_util::{get_children, load_file, save_file}; +use futures::Stream; +use futures::stream::{SplitSink, SplitStream}; +use futures_util::sink::Sink; use futures_util::{SinkExt, StreamExt}; +use hyper::upgrade::Upgraded; +use hyper_util::rt::TokioIo; use json::JsonValue; use std::collections::HashMap; use std::str::FromStr; -use std::sync::Arc; +use std::sync::{Arc, LazyLock}; use std::time::{SystemTime, UNIX_EPOCH}; -use tokio::net::TcpStream; -use tokio::sync::Mutex; +use tokio::sync::{Mutex, RwLock}; use tokio::time::{Duration, Instant, sleep}; -use tokio_tungstenite::{ - MaybeTlsStream, WebSocketStream, connect_async, tungstenite::protocol::Message, -}; +use tokio_tungstenite::{connect_async, tungstenite::protocol::Message}; use tungstenite::Utf8Bytes; use uuid::Uuid; +pub static OMIKRON_CONNECTION: LazyLock>>>> = + LazyLock::new(|| Arc::new(RwLock::new(None))); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ConnectionVariant { + Omikron, + ClientUnauthenticated, + ClientAuthenticated, +} #[derive(Clone)] pub struct OmikronConnection { - pub(crate) writer: Arc< - Mutex< - Option< - futures_util::stream::SplitSink< - WebSocketStream>, - Message, - >, - >, - >, - >, + pub variant: Arc>, + pub user_id: Arc>>, + pub(crate) writer: + Arc + Send + Unpin>>>>, waiting: Arc>>>, // waiting for responses pingpong: Arc>>>, // ping-pong handler pub last_ping: Arc>, @@ -42,6 +49,8 @@ pub struct OmikronConnection { impl OmikronConnection { pub fn new() -> Self { Self { + variant: Arc::new(RwLock::new(ConnectionVariant::Omikron)), + user_id: Arc::new(RwLock::new(None)), writer: Arc::new(Mutex::new(None)), waiting: Arc::new(Mutex::new(HashMap::new())), pingpong: Arc::new(Mutex::new(None)), @@ -50,17 +59,42 @@ impl OmikronConnection { is_connected: Arc::new(Mutex::new(false)), } } + pub async fn client( + writer: SplitSink>, Message>, + reader: SplitStream>>, + ) -> Arc { + let connection = Arc::new(Self { + variant: Arc::new(RwLock::new(ConnectionVariant::ClientUnauthenticated)), + user_id: Arc::new(RwLock::new(None)), + writer: Arc::new(Mutex::new(Some(Box::new(writer) + as Box + Send + Unpin>))), + waiting: Arc::new(Mutex::new(HashMap::new())), + pingpong: Arc::new(Mutex::new(None)), + last_ping: Arc::new(Mutex::new(-1)), + message_send_times: Arc::new(Mutex::new(HashMap::new())), + is_connected: Arc::new(Mutex::new(false)), + }); + let boxed_reader: Box< + dyn Stream> + Send + Unpin, + > = Box::new(reader); + + connection.spawn_listener(boxed_reader).await; + connection + } pub async fn is_connected(&self) -> bool { *self.is_connected.lock().await } /// Connect loop with retry - pub async fn connect(&self) { + pub async fn connect(self: &Arc) { loop { match connect_async("wss://app.tensamin.net/ws/iota/").await { Ok((ws_stream, _)) => { let (write_half, read_half) = ws_stream.split(); - *self.writer.lock().await = Some(write_half); - self.spawn_listener(read_half).await; + *self.writer.lock().await = Some(Box::new(write_half)); + let boxed_reader: Box< + dyn Stream> + Send + Unpin, + > = Box::new(read_half); + self.clone().spawn_listener(boxed_reader).await; let cloned_self = self.clone(); let handle = tokio::spawn(async move { loop { @@ -85,23 +119,32 @@ impl OmikronConnection { Self::send_message_static(&self.writer, msg).await } + pub async fn set_variant(self: &Arc, variant: ConnectionVariant) { + *self.variant.write().await = variant; + } + pub async fn set_user_id(self: &Arc, user_id: Uuid) { + *self.user_id.write().await = Some(user_id); + } + /// Listener for all incoming messages async fn spawn_listener( - &self, - mut read_half: futures_util::stream::SplitStream< - WebSocketStream>, - >, + self: &Arc, + mut read_half: Box> + Send + Unpin>, ) { let waiting_out = self.waiting.clone(); let writer_out = self.writer.clone(); let is_connected_out = self.is_connected.clone(); let sel_out = self.clone(); + let variant = self.variant.clone(); + let sel_arc_out = self.clone(); tokio::spawn(async move { while let Some(msg) = read_half.next().await { let waiting = waiting_out.clone(); let writer = writer_out.clone(); let is_connected = is_connected_out.clone(); let sel = sel_out.clone(); + let variant = variant.clone(); + let sel_arc = sel_arc_out.clone(); tokio::spawn(async move { match msg { Ok(Message::Close(Some(frame))) => { @@ -115,6 +158,96 @@ impl OmikronConnection { sel.handle_pong(&cv, true).await; return; } + let com = variant.read().await.clone(); + if com == ConnectionVariant::ClientUnauthenticated { + if cv.is_type(CommunicationType::identification) { + // Extract user ID + let user_id = match cv.get_data(DataTypes::user_id) { + Some(id_str) => { + match Uuid::parse_str(&id_str.to_string()) { + Ok(id) => id, + Err(_) => { + sel_arc.send_message( + CommunicationValue::new(CommunicationType::error_invalid_user_id) + .with_id(cv.get_id()) + .to_json() + .to_string() + ) + .await; + return; + } + } + } + None => { + sel_arc + .send_message( + CommunicationValue::new( + CommunicationType::error_invalid_user_id, + ) + .with_id(cv.get_id()) + .to_json() + .to_string(), + ) + .await; + return; + } + }; + + // Validate private key + if let Some(private_key_hash) = + cv.get_data(DataTypes::private_key_hash) + { + log_message(format!( + "private_key_hash: {}", + private_key_hash + )); + let is_valid = local_auth::is_private_key_valid( + &user_id, + &private_key_hash.to_string(), + ); + + if !is_valid { + log_message("Invalid private key"); + sel_arc.send_message( + CommunicationValue::new( + CommunicationType::error_invalid_private_key, + ) + .with_id(cv.get_id()) + .to_json() + .to_string(), + ) + .await; + return; + } + } else { + log_message("Missing private key"); + sel_arc + .send_message( + CommunicationValue::new( + CommunicationType::error_invalid_private_key, + ) + .with_id(cv.get_id()) + .to_json() + .to_string(), + ) + .await; + return; + } + + // Set identification data + + sel_arc.set_user_id(user_id).await; + sel_arc + .set_variant(ConnectionVariant::ClientAuthenticated) + .await; + + let response = CommunicationValue::new( + CommunicationType::identification_response, + ) + .with_id(cv.get_id()); + sel_arc.send_message(response.to_json().to_string()).await; + } + } // ************************************************ // // Direct messages // // ************************************************ // @@ -326,6 +459,79 @@ impl OmikronConnection { .await; return; } + + if cv.is_type(CommunicationType::settings_save) { + let my_id = cv.get_sender().unwrap(); + let settings_name = + cv.get_data(DataTypes::settings_name).unwrap().to_string(); + let settings_value = + cv.get_data(DataTypes::payload).unwrap().to_string(); + + save_file( + &format!("users/{}/settings/", my_id), + &format!("{}.settings", settings_name), + &settings_value, + ); + + let response = + CommunicationValue::new(CommunicationType::settings_save) + .with_receiver(my_id) + .with_id(cv.get_id()); + + Self::send_message_static( + &writer.clone(), + response.to_json().to_string(), + ) + .await; + return; + } + if cv.is_type(CommunicationType::settings_load) { + let my_id = cv.get_sender().unwrap(); + let settings_name = + cv.get_data(DataTypes::settings_name).unwrap().to_string(); + let settings_value_str = load_file( + &format!("users/{}/settings/", my_id), + &format!("{}.settings", settings_name), + ); + let settings_value_json = JsonValue::from(settings_value_str); + let response = + CommunicationValue::new(CommunicationType::settings_load) + .with_id(cv.get_id()) + .with_receiver(my_id) + .add_data(DataTypes::payload, settings_value_json) + .add_data_str(DataTypes::settings_name, settings_name); + + Self::send_message_static( + &writer.clone(), + response.to_json().to_string(), + ) + .await; + return; + } + if cv.is_type(CommunicationType::settings_list) { + let my_id = cv.get_sender().unwrap(); + let settings = get_children(&format!("users/{}/settings/", my_id)); + let mut settings_json = JsonValue::new_array(); + for s in settings { + let s = s.replace(".settings", ""); + if s.is_empty() { + continue; + } + let _ = settings_json.push(JsonValue::String(s)); + } + let response = + CommunicationValue::new(CommunicationType::settings_list) + .with_id(cv.get_id()) + .with_receiver(my_id) + .add_data(DataTypes::settings, settings_json); + + Self::send_message_static( + &writer.clone(), + response.to_json().to_string(), + ) + .await; + return; + } } Err(e) => { log_message(format!("[Omikron] Error: {}", e)); @@ -340,14 +546,7 @@ impl OmikronConnection { } pub async fn send_message_static( writer: &Arc< - Mutex< - Option< - futures_util::stream::SplitSink< - WebSocketStream>, - Message, - >, - >, - >, + Mutex + Send + Unpin>>>, >, msg: String, ) { diff --git a/src/server/server.rs b/src/server/server.rs index ff0b7cd..38827c8 100644 --- a/src/server/server.rs +++ b/src/server/server.rs @@ -9,16 +9,18 @@ use hyper::{ }; use hyper_util::rt::tokio::TokioIo; use hyper_util::service::TowerToHyperService; +// FIX: Add necessary rustls imports for builder in minimal-feature environment use rustls::ServerConfig; use rustls::pki_types::{CertificateDer, PrivateKeyDer}; use sha1::{Digest, Sha1}; use std::error::Error; use std::io::ErrorKind; use std::io::{self, BufReader}; +use std::net::{IpAddr, SocketAddr}; use std::result::Result::Ok; use std::sync::Arc; use std::{future::Future, pin::Pin, time::Duration}; -use tokio::net::TcpListener; +use tokio::net::{TcpListener, TcpStream}; use tokio_tungstenite::WebSocketStream; use tower::Service; @@ -26,8 +28,11 @@ use crate::gui::log_panel::log_message; use crate::server::socket::handle; use crate::util::file_util::load_file_buf; use tokio_rustls::TlsAcceptor; + #[derive(Clone)] -struct HttpService; +struct HttpService { + peer_addr: SocketAddr, +} impl Service> for HttpService { type Response = HttpResponse>; @@ -46,6 +51,9 @@ impl Service> for HttpService { let headers = req.headers().clone(); let upgrades = upgrade::on(req); + let peer_ip = self.peer_addr.ip(); + let is_local = is_local_network(peer_ip); + let fut = async move { if path.starts_with("/ws") && headers @@ -104,17 +112,14 @@ impl Service> for HttpService { let (status, body_text) = match path.as_str() { "/" => ( StatusCode::OK, - "Server: Try connecting to WebSocket at ws://:/ws or check /status.", + "Server: Try connecting to WebSocket at ws[s]://:/ws or check /status.", ), - "/status" => (StatusCode::OK, "HTTP Server Status: Online"), + "/status" => (StatusCode::OK, "Server Status: Online"), + "/index" => (StatusCode::OK, include_str!("../../static/web/index.html")), _ => (StatusCode::NOT_FOUND, "404 Not Found"), }; let body = Full::new(Bytes::from(body_text.to_string())); - let response = HttpResponse::builder() - .status(status) - .header(hyper::header::CONTENT_TYPE, "text/plain") - .body(body) - .unwrap(); + let response = HttpResponse::builder().status(status).body(body).unwrap(); Ok(response) } }; @@ -128,15 +133,84 @@ impl Service> for HttpService { } } -pub async fn start(port: u16) -> bool { - let tls_config = match load_tls_config() { - Ok(config) => config, - Err(e) => { - log_message(format!("Failed to load TLS configuration: {}", e)); - log_message("Server stopped. Ensure 'certs/cert.pem' and 'certs/key.pem' exist."); - return false; +fn is_local_network(addr: IpAddr) -> bool { + // 1. Check for standard private ranges (RFC 1918) and loopback + if addr.is_loopback() { + return true; + } + + // 2. Check for Link-Local Addresses (169.254.x.x) + if let IpAddr::V4(ipv4) = addr { + if ipv4.octets()[0] == 169 && ipv4.octets()[1] == 254 { + return true; } - }; + } + + // 3. Check for IPv6 Unique Local Addresses (fc00::/7) + if let IpAddr::V6(ipv6) = addr { + if (ipv6.segments()[0] & 0xfe00) == 0xfc00 { + return true; + } + } + + false +} + +/// Runs the standard, unencrypted HTTP/WS server loop. +async fn run_http_server(port: u16) -> bool { + // Bind to the port + let listener = TcpListener::bind(format!("0.0.0.0:{}", port)).await; + if let Err(e) = listener { + log_message(format!("Failed to bind to port {}: {:?}", port, e)); + return false; + } + let listener = listener.unwrap(); + log_message(format!( + "Standard Server listening for HTTP and WS on 0.0.0.0:{}", + port + )); + + tokio::spawn(async move { + loop { + match listener.accept().await { + std::result::Result::Ok((stream, addr)) => { + let service = HttpService { peer_addr: addr }; + let io = TokioIo::new(stream); + + tokio::spawn(async move { + if let Err(err) = http1::Builder::new() + .preserve_header_case(true) + .title_case_headers(true) + .serve_connection(io, TowerToHyperService::new(service)) + .with_upgrades() + .await + { + if let Some(io_err) = + err.source().and_then(|e| e.downcast_ref::()) + { + if io_err.kind() != io::ErrorKind::ConnectionReset + && io_err.kind() != io::ErrorKind::BrokenPipe + { + log_message(format!("Error serving connection: {:?}", err)); + } + } else { + log_message(format!("Error serving connection: {:?}", err)); + } + } + }); + } + Err(e) => { + log_message(format!("Error accepting connection: {:?}", e)); + tokio::time::sleep(Duration::from_millis(500)).await; + } + } + } + }); + true +} + +/// Runs the encrypted HTTPS/WSS server loop using the provided TLS config. +async fn run_tls_server(port: u16, tls_config: Arc) -> bool { let acceptor = TlsAcceptor::from(tls_config); // Bind to the port @@ -147,18 +221,19 @@ pub async fn start(port: u16) -> bool { } let listener = listener.unwrap(); log_message(format!( - "Server listening for HTTP and WS on 0.0.0.0:{}", + "Encrypted Server listening for HTTPS and WSS on 0.0.0.0:{}", port )); tokio::spawn(async move { loop { match listener.accept().await { - std::result::Result::Ok((stream, _addr)) => { - let service = HttpService; + std::result::Result::Ok((stream, addr)) => { + let service = HttpService { peer_addr: addr }; let acceptor = acceptor.clone(); tokio::spawn(async move { + // Perform TLS handshake let tls_stream = match acceptor.accept(stream).await { Ok(s) => s, Err(e) => { @@ -201,6 +276,26 @@ pub async fn start(port: u16) -> bool { }); true } + +pub async fn start(port: u16) -> bool { + let tls_result = load_tls_config(); + + match tls_result { + Ok(Some(tls_config)) => { + // Certificates found and config loaded successfully, run the TLS server + run_tls_server(port, tls_config).await + } + Ok(None) => { + // Certificates not found, run the standard HTTP server + run_http_server(port).await + } + Err(e) => { + log_message(format!("Fatal error during TLS config load: {}", e)); + // Error, server cannot start + false + } + } +} fn calculate_accept_key(key: &str) -> String { let websocket_guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; let mut sha1 = Sha1::new(); @@ -209,30 +304,54 @@ fn calculate_accept_key(key: &str) -> String { let result = sha1.finalize(); STANDARD.encode(result) // Base64 encode the result } -fn load_tls_config() -> Result, Box> { - // Load certificate file - let mut cert_file = BufReader::new(load_file_buf("certs", "cert.pem")?); - let cert_ders = rustls_pemfile::certs(&mut cert_file) + +/// Loads TLS config. Returns Ok(None) if cert files are not found, and an error if parsing fails. +fn load_tls_config() -> Result>, Box> { + let cert_file_res = load_file_buf("certs", "cert.pem"); + let key_file_res = load_file_buf("certs", "cert.key"); + + // Check if certificate files are present. If not, return None. + let cert_file_buf = match cert_file_res { + Ok(b) => b, + Err(e) if e.kind() == ErrorKind::NotFound => { + log_message("TLS certificate 'certs/cert.pem' not found."); + return Ok(None); + } + Err(e) => return Err(e.into()), // Other IO error + }; + + let key_file_buf = match key_file_res { + Ok(b) => b, + Err(e) if e.kind() == ErrorKind::NotFound => { + log_message("TLS key 'certs/cert.key' not found."); + return Ok(None); + } + Err(e) => return Err(e.into()), // Other IO error + }; + + // Continue with configuration if both files were found + let mut cert_reader = BufReader::new(cert_file_buf); + let cert_ders = rustls_pemfile::certs(&mut cert_reader) .collect::, io::Error>>()?; // PKCS8 - let mut key_file = BufReader::new(load_file_buf("certs", "cert.key")?); - let mut key_ders = rustls_pemfile::pkcs8_private_keys(&mut key_file) + let mut key_reader = BufReader::new(key_file_buf); + let mut key_ders = rustls_pemfile::pkcs8_private_keys(&mut key_reader) .map(|r| r.map(Into::into)) // Explicit conversion .collect::, io::Error>>()?; if key_ders.is_empty() { // RSA - key_file = BufReader::new(load_file_buf("certs", "cert.key")?); - key_ders = rustls_pemfile::rsa_private_keys(&mut key_file) + key_reader = BufReader::new(load_file_buf("certs", "cert.key")?); // Re-read key file + key_ders = rustls_pemfile::rsa_private_keys(&mut key_reader) .map(|r| r.map(Into::into)) .collect::, io::Error>>()?; } if key_ders.is_empty() { // EC - key_file = BufReader::new(load_file_buf("certs", "cert.key")?); - key_ders = rustls_pemfile::ec_private_keys(&mut key_file) + key_reader = BufReader::new(load_file_buf("certs", "cert.key")?); // Re-read key file + key_ders = rustls_pemfile::ec_private_keys(&mut key_reader) .map(|r| r.map(Into::into)) .collect::, io::Error>>()?; } @@ -246,5 +365,5 @@ fn load_tls_config() -> Result, Box> { .with_single_cert(cert_ders, key_ders.remove(0)) .map_err(|e| io::Error::new(ErrorKind::Other, e.to_string()))?; - Ok(Arc::new(config)) + Ok(Some(Arc::new(config))) } diff --git a/src/server/socket.rs b/src/server/socket.rs index c81f743..034ea24 100644 --- a/src/server/socket.rs +++ b/src/server/socket.rs @@ -1,5 +1,6 @@ use crate::communities::{community_connection::CommunityConnection, community_manager}; use crate::gui::log_panel::log_message; +use crate::omikron::omikron_connection::OmikronConnection; use futures::StreamExt; use futures::stream::SplitSink; @@ -15,7 +16,9 @@ pub fn handle( reader: SplitStream>>, ) { tokio::spawn(async move { - if path.starts_with("/ws/community/") { + if path.starts_with("/ws/users/") { + OmikronConnection::client(writer, reader).await; + } else if path.starts_with("/ws/community/") { let community_id = path.split("/").nth(3).unwrap(); log_message(format!("Community: {}", community_id)); if let Some(community) = community_manager::get_community(community_id).await { diff --git a/src/util/file_util.rs b/src/util/file_util.rs index 9bc03d5..3a60b5a 100644 --- a/src/util/file_util.rs +++ b/src/util/file_util.rs @@ -49,7 +49,6 @@ pub fn load_file_buf(path: &str, name: &str) -> io::Result> { // Ensure the directory exists, create if necessary if !dir.exists() { if let Err(e) = fs::create_dir_all(&dir) { - println!("[IMPORTANT] Couldn't create directories: {}", e); return Err(io::Error::new( io::ErrorKind::NotFound, "Directory creation failed", @@ -59,13 +58,10 @@ pub fn load_file_buf(path: &str, name: &str) -> io::Result> { // Create the file if it doesn't exist if !file_path.exists() { - if let Err(e) = File::create(&file_path) { - println!("[IMPORTANT] Couldn't create file: {}", e); - return Err(io::Error::new( - io::ErrorKind::NotFound, - "File creation failed", - )); - } + return Err(io::Error::new( + io::ErrorKind::NotFound, + "File creation failed", + )); } // Open the file and return a BufReader for efficient reading diff --git a/static/web/index.html b/static/web/index.html new file mode 100644 index 0000000..a55ec54 --- /dev/null +++ b/static/web/index.html @@ -0,0 +1,10 @@ + + + + Iota + + + +

Hallo

+ +