From f22f22f495d241178378870868b810427693af5a Mon Sep 17 00:00:00 2001 From: Varga Marcell Date: Sun, 3 Dec 2023 11:40:13 +0100 Subject: [PATCH] New messaging protocol --- Cargo.toml | 1 - proto/messages.proto | 41 +-- src/app.rs | 3 +- src/app/account_manager.rs | 26 +- src/app/backend.rs | 149 +++++++++- src/app/client.rs | 15 +- src/app/server.rs | 311 +++++++++------------ src/app/ui/client.rs | 515 +++++++++++++++++------------------ src/app/ui/emoji.rs | 51 ++-- src/app/ui/login.rs | 12 +- src/app/ui/mode_selection.rs | 18 +- src/app/ui/server.rs | 22 +- src/lib.rs | 1 + src/main.rs | 1 + temp.txt | 82 ++++++ 15 files changed, 659 insertions(+), 589 deletions(-) create mode 100644 temp.txt diff --git a/Cargo.toml b/Cargo.toml index 55fbc982..a61384b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,7 +44,6 @@ device_query = "1.1.3" rand = "0.8.5" unicode_names2 = "1.2.0" - [dependencies.windows-sys] features = [ "Win32_Foundation", diff --git a/proto/messages.proto b/proto/messages.proto index c6bcde55..ebdc1e4e 100644 --- a/proto/messages.proto +++ b/proto/messages.proto @@ -3,13 +3,7 @@ package messages; service Message { - rpc SendMessage (MessageRequest) returns (MessageResponse); - - rpc SyncMessage (MessageSync) returns (MessageResponse); - - rpc ReciveFile (FileSend) returns (FileStatus); - - rpc ServeFile (FileRequest) returns (FileResponse); + rpc MessageMain (MessageRequest) returns (MessageResponse); } @@ -18,41 +12,8 @@ message MessageRequest { string message = 1; } -//empty request, for syncing -message MessageSync { - string password = 1; -} - //outgoing message MessageResponse { string message = 1; } -// FILE IN - -//ingoing file -message FileSend { - bytes file = 1; - string name = 2; - string passw = 3; - string author = 4; -} - -//sent file status -message FileStatus { - int32 error = 1; -} - -// FILE OUT - -//request stored file -message FileResponse { - bytes file = 1; - string name = 2; -} - -//ask for indexed file -message FileRequest { - int32 index = 1; -} - diff --git a/src/app.rs b/src/app.rs index 5f01e65a..7f64696b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -15,7 +15,6 @@ use self::account_manager::{ append_to_file, decrypt_lines_from_vec, delete_line_from_file, read_from_file, }; -use self::backend::{FileUpload, Message}; use self::input::keymap; impl eframe::App for backend::TemplateApp { @@ -39,7 +38,7 @@ impl eframe::App for backend::TemplateApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { let input_keys = keymap(self.keymap.clone()); - + /* :: custom font :: diff --git a/src/app/account_manager.rs b/src/app/account_manager.rs index 0e323897..aa60ccbc 100644 --- a/src/app/account_manager.rs +++ b/src/app/account_manager.rs @@ -13,8 +13,6 @@ use std::string::FromUtf8Error; use argon2::{self, Config, Variant, Version}; -use super::client::messages::FileResponse; - pub fn encrypt_aes256(string_to_be_encrypted: String) -> aes_gcm::aead::Result { let key: &[u8] = &[42; 32]; @@ -183,18 +181,18 @@ pub fn delete_line_from_file(line_number: usize, path: PathBuf) -> anyhow::Resul Ok(()) } -pub fn write_file(file_response: FileResponse) -> Result<()> { - let file_extension: Vec<&str> = file_response.name.split('.').collect(); +// pub fn write_file(file_response: FileResponse) -> Result<()> { +// let file_extension: Vec<&str> = file_response.name.split('.').collect(); - let files = FileDialog::new() - .set_title("Save to") - .set_directory("/") - .add_filter(file_extension[1], &[file_extension[1]]) - .save_file(); +// let files = FileDialog::new() +// .set_title("Save to") +// .set_directory("/") +// .add_filter(file_extension[1], &[file_extension[1]]) +// .save_file(); - if let Some(file) = files { - fs::write(file, file_response.file)?; - } +// if let Some(file) = files { +// fs::write(file, file_response.file)?; +// } - Ok(()) -} +// Ok(()) +// } diff --git a/src/app/backend.rs b/src/app/backend.rs index 0e827810..83e1e25d 100644 --- a/src/app/backend.rs +++ b/src/app/backend.rs @@ -75,7 +75,7 @@ pub struct TemplateApp { #[serde(skip)] pub incoming_msg_time: Vec, #[serde(skip)] - pub incoming_msg: String, + pub incoming_msg: ServerMaster, //emoji fasz pub random_emoji: String, pub emoji: Vec, @@ -161,7 +161,7 @@ impl Default for TemplateApp { //msg usr_msg: String::new(), incoming_msg_time: Vec::new(), - incoming_msg: String::new(), + incoming_msg: ServerMaster::default(), //thread communication for client rx, tx, @@ -189,6 +189,7 @@ impl TemplateApp { //Message Types #[derive(Default, serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct FileUpload { + pub extension: String, pub name: String, pub bytes: Vec, } @@ -204,9 +205,7 @@ pub struct Image { } #[derive(Default, serde::Serialize, serde::Deserialize, Debug, Clone)] -pub struct SnycMessage { - /*Empty packet, only for syncing*/ -} +pub struct SnycMessage {/*Empty packet, only for syncing*/} #[derive(Default, serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct FileRequest { @@ -242,13 +241,145 @@ impl Message { author: String, ) -> Message { Message { - MessageType: MessageType::NormalMessage(NormalMessage { message: msg.trim().to_string() }), + MessageType: MessageType::NormalMessage(NormalMessage { + message: msg.trim().to_string(), + }), + Password: password, + Author: author, + MessageDate: { Utc::now().format("%Y.%m.%d. %H:%M").to_string() }, + Destination: ip, + } + } + pub fn construct_file_msg( + bytes: Vec, + file_name: PathBuf, + ip: String, + password: String, + author: String, + ) -> Message { + Message { + //Dont execute me please :3 | + // | + // V + MessageType: MessageType::FileUpload(FileUpload { + extension: file_name.extension().unwrap().to_str().unwrap().to_string(), + name: file_name + .file_prefix() + .unwrap() + .to_str() + .unwrap() + .to_string(), + bytes: bytes, + }), + + Password: password, + Author: author, + MessageDate: { Utc::now().format("%Y.%m.%d. %H:%M").to_string() }, + Destination: ip, + } + } + pub fn construct_sync_msg(ip: String, password: String, author: String) -> Message { + Message { + MessageType: MessageType::SyncMessage(SnycMessage {}), Password: password, Author: author, - MessageDate: { - Utc::now().format("%Y.%m.%d. %H:%M").to_string() - }, + MessageDate: { Utc::now().format("%Y.%m.%d. %H:%M").to_string() }, Destination: ip, } } } + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct ServerFileUpload { + pub file_name: String, + pub index: i32, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct ServerNormalMessage { + pub message: String, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct ServerImage { + pub bytes: Vec, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub enum ServerMessageType { + Upload(ServerFileUpload), + Normal(ServerNormalMessage), + Image(ServerImage), +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct ServerOutput { + pub MessageType: ServerMessageType, + pub Author: String, + pub MessageDate: String, +} + +impl ServerOutput { + pub fn struct_into_string(&self) -> String { + return serde_json::to_string(self).unwrap_or_default(); + } + pub fn convert_msg_to_servermsg(normal_msg: Message) -> ServerOutput { + //Convert a client output to a server output (Message -> ServerOutput), trim some useless info + ServerOutput { + MessageType: ServerMessageType::Normal(ServerNormalMessage { + message: match normal_msg.MessageType { + MessageType::SyncMessage(_) => todo!(), + MessageType::FileRequest(_) => todo!(), + MessageType::FileUpload(_) => todo!(), + MessageType::Image(_) => todo!(), + MessageType::NormalMessage(msg) => msg.message, + }, + }), + Author: normal_msg.Author, + MessageDate: normal_msg.MessageDate, + } + } + pub fn convert_upload_to_servermsg(normal_msg: Message, index: i32) -> ServerOutput { + //Convert a client output to a server output (Message -> ServerOutput), trim some useless info + ServerOutput { + MessageType: ServerMessageType::Upload(ServerFileUpload { + file_name: match normal_msg.MessageType { + MessageType::SyncMessage(_) => todo!(), + MessageType::FileRequest(_) => todo!(), + MessageType::FileUpload(msg) => { + format!("{}.{}", msg.name, msg.extension) + } + MessageType::Image(_) => todo!(), + MessageType::NormalMessage(_) => todo!(), + }, + index: index, + }), + Author: normal_msg.Author, + MessageDate: normal_msg.MessageDate, + } + } +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct ServerMaster { + pub struct_list: Vec, +} +impl Default for ServerMaster { + fn default() -> Self { + Self { + struct_list: Vec::new(), + } + } +} +impl ServerMaster { + pub fn struct_into_string(&self) -> String { + return serde_json::to_string(self).unwrap_or_default(); + } + pub fn convert_vec_serverout_into_server_master( + server_output_list: Vec, + ) -> ServerMaster { + return ServerMaster { + struct_list: server_output_list, + }; + } +} diff --git a/src/app/client.rs b/src/app/client.rs index 229a378d..d0232ec0 100644 --- a/src/app/client.rs +++ b/src/app/client.rs @@ -1,8 +1,4 @@ -use std::path::PathBuf; - -use messages::{message_client::MessageClient, FileRequest, FileSend, MessageRequest, MessageSync}; - -use self::messages::FileResponse; +use messages::{message_client::MessageClient, MessageRequest}; use super::backend::Message; pub mod messages { @@ -10,9 +6,7 @@ pub mod messages { } //main is for sending -pub async fn send_msg( - message: Message -) -> Result> { +pub async fn send_msg(message: Message) -> Result> { let mut client: MessageClient = MessageClient::connect(format!("http://{}", message.Destination)).await?; @@ -20,13 +14,13 @@ pub async fn send_msg( message: message.struct_into_string(), }); - let response = client.send_message(request).await?.into_inner().clone(); + let response = client.message_main(request).await?.into_inner().clone(); let message = response.message; Ok(message) } - +/* pub async fn sync_msg(passw: String, ip: String) -> Result> { let mut client: MessageClient = MessageClient::connect(format!("http://{}", ip)).await?; @@ -78,3 +72,4 @@ pub async fn request_file( Ok(response) } + */ diff --git a/src/app/server.rs b/src/app/server.rs index 3c182137..45fed27c 100644 --- a/src/app/server.rs +++ b/src/app/server.rs @@ -1,5 +1,3 @@ -use chrono::{format::StrftimeItems, Local}; - use std::{env, fs, io::Write, path::PathBuf}; use std::sync::Mutex; @@ -21,10 +19,16 @@ use instant_acme::{ use messages::{ message_server::{Message as ServerMessage, MessageServer}, - FileRequest, FileResponse, FileSend, FileStatus, MessageRequest, MessageResponse, MessageSync, + MessageRequest, MessageResponse, +}; + +use crate::app::backend::ServerMaster; +use crate::app::backend::{ + FileRequest as FileRequestStruct, FileUpload as FileUploadStruct, Message, + MessageType::{FileRequest, FileUpload, Image, NormalMessage, SyncMessage}, }; -use crate::app::backend::Message; +use super::backend::ServerOutput; pub mod messages { tonic::include_proto!("messages"); @@ -32,7 +36,7 @@ pub mod messages { #[derive(Debug, Default)] pub struct MessageService { - pub messages: Mutex>, + pub messages: Mutex>, pub passw: String, //files @@ -40,201 +44,37 @@ pub struct MessageService { } #[tonic::async_trait] impl ServerMessage for MessageService { - async fn send_message( + async fn message_main( &self, request: Request, ) -> Result, Status> { - println!("Got a request: {:?}", request); - - let req_result: Result = serde_json::from_str(&request.into_inner().message); + let req_result: Result = + serde_json::from_str(&request.into_inner().message); let req: Message = req_result.unwrap(); if &req.Password == self.passw.trim() { - match self.messages.lock() { - Ok(mut ok) => { - ok.push( - format!("{}", req.struct_into_string()) + "\n", - ); + match &req.MessageType { + NormalMessage(msg) => self.NormalMessage(req).await, + SyncMessage(msg) => { /*Dont do anything we will always reply with the list of msgs*/ } - Err(err) => { - println!("{err}") + Image(msg) => { + todo!() } - }; - } - - let shared_messages = self.messages.lock().unwrap().clone(); - - let handle = std::thread::spawn(move || { - let final_msg: String = shared_messages - .iter() - .map(|s| s.to_string()) - .collect::(); - - final_msg - }); - - // Wait for the spawned thread to finish - let final_msg = handle.join().unwrap(); - if req.Password.trim() == self.passw.trim() { - let reply = MessageResponse { - message: final_msg.to_string(), - }; - - Ok(Response::new(reply)) - } - //invalid passw - else { - let reply = MessageResponse { - message: "Invalid Password!".to_string(), - }; - Ok(Response::new(reply)) - } - } - async fn sync_message( - &self, - request: Request, - ) -> Result, Status> { - //pass matching p100 technology - if request.into_inner().password == self.passw { - let shared_messages = self.messages.lock().unwrap().clone(); - - let handle = std::thread::spawn(move || { - let final_msg: String = shared_messages - .iter() - .map(|s| s.to_string()) - .collect::(); - - final_msg - }); - - // Wait for the spawned thread to finish - let final_msg = handle.join().unwrap(); - - let reply = MessageResponse { message: final_msg }; - Ok(Response::new(reply)) - } else { - let reply = MessageResponse { - message: "Invalid password!".into(), - }; - Ok(Response::new(reply)) - } - } - async fn recive_file( - &self, - request: Request, - ) -> Result, Status> { - /* - - error -> 0 success - error -> 1 Server : failed to get APPDATA arg - error -> 2 Server : failed to create file - - */ - let req = request.into_inner().clone(); - - if req.passw == self.passw { - let mut error_code: i32 = 0; - - //500mb limit - if req.file.len() > 500000000 { - error_code = -1; - } else { - match env::var("APPDATA") { - Ok(app_data) => { - let _create_dir = fs::create_dir(format!("{}\\szeChat\\Server", app_data)); - - match fs::File::create(format!("{app_data}\\szeChat\\Server\\{}", req.name)) - { - Ok(mut created_file) => { - if let Err(err) = created_file.write_all(&req.file) { - println!("[{err}\n{}]", err.kind()); - }; - - created_file.flush().unwrap(); - //success - - match self.file_paths.lock() { - Ok(mut ok) => { - ok.push(PathBuf::from(format!( - "{app_data}\\szeChat\\Server\\{}", - req.name - ))); - } - Err(err) => { - println!("{err}") - } - }; - let current_datetime = Local::now(); - let format = StrftimeItems::new("%Y.%m.%d. %H:%M"); - let formatted_datetime = current_datetime.format_with_items(format); - match self.messages.lock() { - Ok(mut ok) => { - ok.push(format!( - //use a character before file upload which cannot be set as a file name - "{formatted_datetime} $ {} | >file_upload '{}' '{}'\n", - req.author, - req.name, - self.file_paths.lock().unwrap().len() - 1 - )); - } - Err(err) => println!("{err}"), - } - } - Err(err) => { - println!(" [{err}\n{}]", err.kind()); - error_code = 2; - } - } - } - Err(err) => { - error_code = 1; - println!("{err}") - } + FileRequest(msg) => { + todo!() } - } - - dbg!(&self.file_paths); - - let reply = FileStatus { error: error_code }; + FileUpload(_) => { + self.recive_file(req.clone()).await; + } + }; - Ok(Response::new(reply)) + return self.sync_message().await; } else { - let reply = FileStatus { error: -2 }; - - Ok(Response::new(reply)) + return Ok(Response::new(MessageResponse { + message: "Invalid Password!".into(), + })); } } - - async fn serve_file( - &self, - request: Request, - ) -> Result, Status> { - let req = request.into_inner().clone(); - - let file_path_vec = self.file_paths.lock().unwrap(); - - //check for index in uploaded files path vector - // EX :: - // Vec() => {"C:\Apad.exe", "C:\XD"} - // INPUT :: - // 1 - // OUTPUT :: - // "C:\XD" - - let apad = &file_path_vec[req.index as usize]; - - let file = fs::read(apad).unwrap(); - - let file_name = apad.file_name().unwrap().to_string_lossy().to_string(); - - //reply with file name, bytes - let reply = FileResponse { - file, - name: file_name, - }; - - Ok(Response::new(reply)) - } } /* @@ -385,7 +225,7 @@ pub async fn server_main( port: String, password: String, ip_v4: bool, -) -> Result, Box> { +) -> Result> { //apad().await; let mut addr: std::net::SocketAddr = format!("0.0.0.0:{}", port).parse()?; if !ip_v4 { @@ -404,5 +244,98 @@ pub async fn server_main( .serve(addr) .await?; - Ok(messages.to_vec()) + let reply: String = messages.iter().map(|f| f.struct_into_string()).collect(); + Ok(reply) +} + +impl MessageService { + pub async fn NormalMessage(&self, req: Message) { + match self.messages.lock() { + Ok(mut ok) => { + ok.push(ServerOutput::convert_msg_to_servermsg(req)); + } + Err(err) => { + println!("{err}") + } + }; + } + pub async fn sync_message(&self) -> Result, Status> { + //pass matching p100 technology + let shared_messages = self.messages.lock().unwrap().clone(); + + let server_master = ServerMaster::convert_vec_serverout_into_server_master(shared_messages); + + let final_msg: String = server_master.struct_into_string(); + + // Wait for the spawned thread to finish + + let reply = MessageResponse { message: final_msg }; + Ok(Response::new(reply)) + } + + pub async fn recive_file(&self, request: Message) { + /* + + error -> 0 success + error -> 1 Server : failed to get APPDATA arg + error -> 2 Server : failed to create file + + */ + let mut error_code: i32 = 0; + + if let FileUpload(req) = request.clone().MessageType { + //500mb limit + if req.bytes.len() > 500000000 { + error_code = -1; + } else { + match env::var("APPDATA") { + Ok(app_data) => { + let _create_dir = fs::create_dir(format!("{}\\szeChat\\Server", app_data)); + + match fs::File::create(format!("{app_data}\\szeChat\\Server\\{}", req.name)) + { + Ok(mut created_file) => { + if let Err(err) = created_file.write_all(&req.bytes) { + println!("[{err}\n{}]", err.kind()); + }; + + created_file.flush().unwrap(); + //success + + match self.file_paths.lock() { + Ok(mut ok) => { + ok.push(PathBuf::from(format!( + "{app_data}\\szeChat\\Server\\{}", + req.name + ))); + } + Err(err) => { + println!("{err}") + } + }; + match self.messages.lock() { + Ok(mut ok) => { + ok.push(ServerOutput::convert_upload_to_servermsg( + request, + self.file_paths.lock().unwrap().len() as i32 - 1, + )); + } + Err(err) => println!("{err}"), + } + } + Err(err) => { + println!(" [{err}\n{}]", err.kind()); + error_code = 2; + } + } + } + Err(err) => { + println!("{err}") + } + } + } + } + + dbg!(error_code); + } } diff --git a/src/app/ui/client.rs b/src/app/ui/client.rs index 4a1ac6ad..e4e8e2ef 100644 --- a/src/app/ui/client.rs +++ b/src/app/ui/client.rs @@ -1,5 +1,5 @@ use device_query::Keycode; -use egui::{vec2, Align, Align2, Area, Button, FontFamily, FontId, Layout, RichText}; +use egui::{vec2, Align, Align2, Area, Button, Color32, FontFamily, FontId, Layout, RichText}; use rand::Rng; use regex::Regex; @@ -8,13 +8,15 @@ use std::fs::{self}; use std::sync::atomic::Ordering; use std::time::Duration; use windows_sys::w; -use windows_sys::Win32::UI::WindowsAndMessaging::{MessageBoxW, MB_ICONEXCLAMATION, MB_ICONSTOP}; +use windows_sys::Win32::UI::WindowsAndMessaging::{MessageBoxW, MB_ICONSTOP}; use std::sync::mpsc; -use crate::app::account_manager::write_file; -use crate::app::backend::{TemplateApp, Message}; -use crate::app::client::{self, request_file, send_file}; +//use crate::app::account_manager::write_file; +use crate::app::backend::{ + Message, MessageType, NormalMessage, ServerMaster, ServerMessageType, ServerOutput, TemplateApp, +}; +use crate::app::client::{self}; impl TemplateApp { pub fn state_client( @@ -26,15 +28,18 @@ impl TemplateApp { let should_be_running = self.autosync_should_run.clone(); let rx = self.autosync_sender.get_or_insert_with(|| { let (tx, rx) = mpsc::channel::(); - let passw = self.client_password.clone(); - let ip = self.send_on_ip.clone(); + + let message = Message::construct_sync_msg( + self.send_on_ip.clone(), + self.client_password.clone(), + self.login_username.clone(), + ); tokio::spawn(async move { while should_be_running.load(Ordering::Relaxed) { - tokio::time::sleep(Duration::from_secs_f32(1.5)).await; + tokio::time::sleep(Duration::from_secs_f32(2.)).await; println!("requested sync!"); - dbg!(ip.clone()); - match client::sync_msg(passw.clone(), ip.clone()).await { + match client::send_msg(message.clone()).await { Ok(ok) => { match tx.send(ok) { Ok(_) => {} @@ -56,7 +61,11 @@ impl TemplateApp { Ok(msg) => { //show messages ctx.request_repaint(); - self.incoming_msg = msg; + let incoming_struct: Result = + serde_json::from_str(&msg); + if let Ok(ok) = incoming_struct { + self.incoming_msg = ok; + } } Err(_err) => { //println!("{}", _err) @@ -114,124 +123,93 @@ impl TemplateApp { egui::ScrollArea::vertical() .id_source("msg_area") .stick_to_bottom(true) + .auto_shrink([false, true]) .show(ui, |ui| { - ui.separator(); - ui.allocate_ui(ui.available_size(), |ui| { - for i in self.incoming_msg.clone().lines() { - //md style - if i.contains("file_upload") { - //use a character before file upload which cannot be set as a file name - let sort_msg: Vec<&str> = i.split('>').collect(); - - let re = Regex::new(r#"'(.*?[^\\])'"#).unwrap(); - - let mut results: Vec = Vec::new(); - - for captured in re.captures_iter(sort_msg[1]) { - if let Some(inner_text) = captured.get(1) { - results.push(inner_text.as_str().to_string()); + if self.send_on_ip.is_empty() { + ui.with_layout(Layout::centered_and_justified(egui::Direction::TopDown), |ui|{ + + ui.label(RichText::from("To start chatting go to settings and set the IP to the server you want to connect to!").size(self.font_size).color(Color32::LIGHT_BLUE)); + + }); + } + for item in self.incoming_msg.clone().struct_list { + ui.group(|ui| + { + ui.label(RichText::from(format!("{}", item.Author)).size(self.font_size / 1.3).color(Color32::WHITE)); + if let ServerMessageType::Normal(item) = &item.MessageType { + let i = &item.message; + if (i.contains('[') && i.contains(']')) + && (i.contains('(') && i.contains(')')) + { + let re = Regex::new( + r"\[(?P[^\]]*)\]\((?P[^)]+)\)", + ) + .unwrap(); + let mut captures: Vec = Vec::new(); + for capture in re.captures_iter(&i) { + for i in 1..capture.len() { + captures.push(capture[i].to_string()); + } } - } - ui.horizontal(|ui| { - ui.label( - RichText::from(sort_msg[0]).size(self.font_size), - ); - if ui - .button( - RichText::from(format!( - "Download {}", - results[0] - )) - .strong() - .size(self.font_size), - ) - .clicked() - { - let ip = self.send_on_ip.clone(); - tokio::spawn(async move { - match request_file( - results[1].parse::().unwrap(), - ip, - ) - .await - { - Ok(file_reponse) => { - if let Err(err) = - write_file(file_reponse) - { - println!("{err}") - }; - } - Err(err) => println!("{err}"), - }; + if captures.is_empty() { + ui.label(RichText::from(i).size(self.font_size)); + } else { + ui.horizontal(|ui| { + ui.label( + RichText::from(re.replace_all::<&str>(&i, "")) + .size(self.font_size), + ); + for i in (0..captures.len()).step_by(2) { + ui.add(egui::Hyperlink::from_label_and_url( + RichText::from(&captures[i]) + .size(self.font_size), + &captures[i + 1], + )); + } }); - }; - }); - } else if (i.contains('[') && i.contains(']')) - && (i.contains('(') && i.contains(')')) - { - let re = Regex::new( - r"\[(?P[^\]]*)\]\((?P[^)]+)\)", - ) - .unwrap(); - let mut captures: Vec = Vec::new(); - for capture in re.captures_iter(i) { - for i in 1..capture.len() { - captures.push(capture[i].to_string()); } - } - if captures.is_empty() { - ui.label(RichText::from(i).size(self.font_size)); - } else { + } else if i.contains('#') && i.rmatches('#').count() <= 5 { + let split_lines = i.rsplit_once('#').unwrap(); ui.horizontal(|ui| { ui.label( - RichText::from(re.replace_all::<&str>(i, "")) + RichText::from(split_lines.0.replace('#', "")) .size(self.font_size), ); - for i in (0..captures.len()).step_by(2) { - ui.add(egui::Hyperlink::from_label_and_url( - RichText::from(&captures[i]) - .size(self.font_size), - &captures[i + 1], - )); - } + ui.label( + RichText::from(split_lines.1).strong().size( + self.font_size + * match i + .rmatches('#') + .collect::>() + .len() + { + 1 => 2.0, + 2 => 1.8, + 3 => 1.6, + 4 => 1.4, + 5 => 1.2, + _ => 1., + } + as f32, + ), + ); }); + } else { + ui.label(RichText::from(i).size(self.font_size)); } - } else if i.contains('#') && i.rmatches('#').count() <= 5 { - let split_lines = i.rsplit_once('#').unwrap(); - - ui.horizontal(|ui| { - ui.label( - RichText::from(split_lines.0.replace('#', "")) - .size(self.font_size), - ); - ui.label( - RichText::from(split_lines.1).strong().size( - self.font_size - * match i - .rmatches('#') - .collect::>() - .len() - { - 1 => 2.0, - 2 => 1.8, - 3 => 1.6, - 4 => 1.4, - 5 => 1.2, - _ => 1., - } - as f32, - ), - ); - }); - } else { - ui.label(RichText::from(i).size(self.font_size)); } + if let ServerMessageType::Upload(file) = &item.MessageType { + if ui.button(RichText::from(format!("{}", file.file_name)).size(self.font_size)).clicked() { + //Request file with index item.index + } + } + ui.label(RichText::from(format!("{}", item.MessageDate)).size(self.font_size / 1.5).color(Color32::DARK_GRAY)); + } + ); } }); - ui.separator(); if !self.usr_msg_expanded { ui.allocate_space(vec2(ui.available_width(), 25.)); } @@ -263,178 +241,173 @@ impl TemplateApp { //usr_input egui::TopBottomPanel::bottom("usr_input").show_animated(ctx, self.usr_msg_expanded, |ui| { - ui.allocate_space(vec2(ui.available_width(), 5.)); - - ui.with_layout(Layout::left_to_right(Align::Min), |ui| { - ui.allocate_ui( - vec2( - ui.available_width() - 100., - _frame.info().window_info.size[1] / 5., - ), - |ui| { - egui::ScrollArea::vertical() - .id_source("usr_input") - .stick_to_bottom(true) - .show(ui, |ui| { - ui.with_layout( - egui::Layout::top_down_justified(Align::Center), - |ui| { - ui.add_sized( - ui.available_size(), - egui::TextEdit::multiline(&mut self.usr_msg) - .hint_text(RichText::from(format!("Message : {}", self.send_on_ip)).size(self.font_size)) - .font(FontId::new(self.font_size, FontFamily::default())) - ); - }, - ); - }); - }, - ); + ui.allocate_space(vec2(ui.available_width(), 5.)); - ui.with_layout(Layout::top_down(Align::Center), |ui| { - ui.allocate_ui(vec2(50., 50.), |ui| { - if ui - .add(egui::widgets::ImageButton::new(egui::include_image!( - "../../../icons/send_msg.png" - ))) - .clicked() || input_keys.contains(&Keycode::Enter) && !(input_keys.contains(&Keycode::LShift) || input_keys.contains(&Keycode::RShift)) && !(self.usr_msg.trim().is_empty() || self.usr_msg.trim_end_matches('\n').is_empty()) - { - if self.usr_msg.contains("file_upload") { - std::thread::spawn(|| unsafe { - MessageBoxW( - 0, - w!("You can not send server messages!"), - w!("Error"), - MB_ICONSTOP, + ui.with_layout(Layout::left_to_right(Align::Min), |ui| { + ui.allocate_ui( + vec2( + ui.available_width() - 100., + _frame.info().window_info.size[1] / 5., + ), + |ui| { + egui::ScrollArea::vertical() + .id_source("usr_input") + .stick_to_bottom(true) + .show(ui, |ui| { + ui.with_layout( + egui::Layout::top_down_justified(Align::Center), + |ui| { + ui.add_sized( + ui.available_size(), + egui::TextEdit::multiline(&mut self.usr_msg) + .hint_text( + RichText::from(format!( + "Message : {}", + self.send_on_ip + )) + .size(self.font_size), + ) + .font(FontId::new( + self.font_size, + FontFamily::default(), + )), ); - }); - return; - } + }, + ); + }); + }, + ); - let temp_msg = self.usr_msg.clone(); - let tx = self.tx.clone(); - let username = self.login_username.clone(); - //disable pass if its not ticked - let passw = match self.req_passw { - true => self.client_password.clone(), - false => "".into(), - }; - let temp_ip = self.send_on_ip.clone(); - tokio::spawn(async move { - match client::send_msg(Message::construct_normal_msg(&temp_msg, temp_ip, passw, username)) - .await - { - Ok(ok) => { - match tx.send(ok) { - Ok(_) => {} - Err(err) => { - println!("{}", err); - } - }; - } - Err(err) => { - println!("ln 321 {:?}", err.source()); - } - }; + ui.with_layout(Layout::top_down(Align::Center), |ui| { + ui.allocate_ui(vec2(50., 50.), |ui| { + if ui + .add(egui::widgets::ImageButton::new(egui::include_image!( + "../../../icons/send_msg.png" + ))) + .clicked() + || input_keys.contains(&Keycode::Enter) + && !(input_keys.contains(&Keycode::LShift) + || input_keys.contains(&Keycode::RShift)) + && !(self.usr_msg.trim().is_empty() + || self.usr_msg.trim_end_matches('\n').is_empty()) + { + if self.usr_msg.contains("file_upload") { + std::thread::spawn(|| unsafe { + MessageBoxW( + 0, + w!("You can not send server messages!"), + w!("Error"), + MB_ICONSTOP, + ); }); - self.usr_msg.clear(); + return; } - }); - ui.allocate_ui(vec2(50., 50.), |ui| { - if ui - .add(egui::widgets::ImageButton::new(egui::include_image!( - "../../../icons/add_file.png" - ))) - .on_hover_text("Send files") - .clicked() - { - let files = FileDialog::new() - .set_title("Pick a file") - .set_directory("/") - .pick_file(); - if let Some(file) = files { - //send file - match fs::read(file.clone()) { - Ok(file_bytes) => { - let passw = self.client_password.clone(); - let ip = self.send_on_ip.clone(); - let author = self.login_username.clone(); - tokio::spawn(async move { - match send_file(passw, ip, file_bytes, file, author).await { - Ok(_ok) => { - //server errors listing here + let temp_msg = self.usr_msg.clone(); + let tx = self.tx.clone(); + let username = self.login_username.clone(); + //disable pass if its not ticked + let passw = match self.req_passw { + true => self.client_password.clone(), + false => "".into(), + }; + let temp_ip = self.send_on_ip.clone(); + tokio::spawn(async move { + match client::send_msg(Message::construct_normal_msg( + &temp_msg, temp_ip, passw, username, + )) + .await + { + Ok(ok) => { + match tx.send(ok) { + Ok(_) => {} + Err(err) => { + println!("{}", err); + } + }; + } + Err(err) => { + println!("ln 321 {:?}", err.source()); + } + }; + }); + self.usr_msg.clear(); + } + }); + ui.allocate_ui(vec2(50., 50.), |ui| { + if ui + .add(egui::widgets::ImageButton::new(egui::include_image!( + "../../../icons/add_file.png" + ))) + .on_hover_text("Send files") + .clicked() + { + let files = FileDialog::new() + .set_title("Pick a file") + .set_directory("/") + .pick_file(); + + if let Some(file) = files { + //send file + match fs::read(file.clone()) { + Ok(file_bytes) => { + let passw = self.client_password.clone(); + let ip = self.send_on_ip.clone(); + let author = self.login_username.clone(); - /* - - error -> 0 success - error -> 1 Server : failed to get APPDATA arg - error -> 2 Server : failed to create file - - */ - match _ok { - -2 => { - std::thread::spawn(|| unsafe { - MessageBoxW( - 0, - w!("Invalid password"), - w!("Error"), - MB_ICONEXCLAMATION, - ); - }); - } - -1 => {println!("File too large!")} - 0 => {println!("File Sent successfully")} - 1 => {println!("[Server : failed to get APPDATA arg] Error : {_ok}")} - 2 => {println!("[Server : failed to create file] Error : {_ok}")} - _ => {println!("Unknown error : {_ok}")} - } - }, - Err(err) => { - println!("{err}"); - }, - }; - }); - }, - Err(err) => { - println!("{err}") - }, + let message = Message::construct_file_msg( + file_bytes, file, ip, passw, author, + ); + + tokio::spawn(async move { + //ignore server response + let _ = client::send_msg(message).await; + }); + } + Err(err) => { + println!("{err}") } } } - }); - ui.allocate_ui(vec2(37., 37.), |ui|{ - let button = ui.add( - Button::new(RichText::from(&self.random_emoji).size(45.)) - ); + } + }); + ui.allocate_ui(vec2(37., 37.), |ui| { + let button = + ui.add(Button::new(RichText::from(&self.random_emoji).size(45.))); - if button.clicked() { - self.emoji_mode = !self.emoji_mode; - }; - - if button.hovered() { - if !self.random_generated { - let random_number = self.rand_eng.gen_range(0..=self.emoji.len() - 1); - self.random_emoji = self.emoji[random_number].clone(); - self.random_generated = true; - } - } - else { - //check if button has been unhovered, reset variable - self.random_generated = false; + if button.clicked() { + self.emoji_mode = !self.emoji_mode; + }; + + if button.hovered() { + if !self.random_generated { + let random_number = + self.rand_eng.gen_range(0..=self.emoji.len() - 1); + self.random_emoji = self.emoji[random_number].clone(); + self.random_generated = true; } - }); + } else { + //check if button has been unhovered, reset variable + self.random_generated = false; + } }); }); - //receive server answer unconditionally - match self.rx.try_recv() { - Ok(ok) => self.incoming_msg = ok, - Err(_err) => { - //println!("ln 332 {}", err); + }); + //receive server answer unconditionally + match self.rx.try_recv() { + Ok(msg) => { + let incoming_struct: Result = + serde_json::from_str(&msg); + if let Ok(ok) = incoming_struct { + self.incoming_msg = ok; } - }; + } + Err(_err) => { + //println!("ln 332 {}", err); + } + }; - ui.allocate_space(vec2(ui.available_width(), 5.)); - }); + ui.allocate_space(vec2(ui.available_width(), 5.)); + }); } } diff --git a/src/app/ui/emoji.rs b/src/app/ui/emoji.rs index 374cb23b..e625cf30 100644 --- a/src/app/ui/emoji.rs +++ b/src/app/ui/emoji.rs @@ -163,37 +163,42 @@ pub fn special_char_name(chr: char) -> Option<&'static str> { impl backend::TemplateApp { pub fn window_emoji(&mut self, ctx: &egui::Context) { egui::Window::new("Emoji") - .collapsible(false) - .show(ctx, |ui| { - ui.label(format!("Click to paste")); + .collapsible(false) + .show(ctx, |ui| { + ui.label(format!("Click to paste")); - let filter = &self.filter; - let named_chars = self - .named_chars - .entry(egui::FontFamily::Monospace) - .or_insert_with(|| available_characters(ui, egui::FontFamily::Monospace)); + let filter = &self.filter; + let named_chars = self + .named_chars + .entry(egui::FontFamily::Monospace) + .or_insert_with(|| available_characters(ui, egui::FontFamily::Monospace)); - ui.separator(); + ui.separator(); - egui::ScrollArea::vertical().show(ui, |ui| { - ui.horizontal_wrapped(|ui| { - ui.spacing_mut().item_spacing = egui::Vec2::splat(2.0); + egui::ScrollArea::vertical().show(ui, |ui| { + ui.horizontal_wrapped(|ui| { + ui.spacing_mut().item_spacing = egui::Vec2::splat(2.0); - for (&chr, name) in named_chars { - if filter.is_empty() || name.contains(filter) || *filter == chr.to_string() - { - let button = egui::Button::new( - egui::RichText::new(chr.to_string()).font(egui::FontId { size: self.font_size, family: egui::FontFamily::Proportional }), - ) - .frame(false); + for (&chr, name) in named_chars { + if filter.is_empty() + || name.contains(filter) + || *filter == chr.to_string() + { + let button = egui::Button::new( + egui::RichText::new(chr.to_string()).font(egui::FontId { + size: self.font_size, + family: egui::FontFamily::Proportional, + }), + ) + .frame(false); - if ui.add(button).clicked() { - self.usr_msg.push(chr); + if ui.add(button).clicked() { + self.usr_msg.push(chr); + } } } - } + }); }); }); - }); } } diff --git a/src/app/ui/login.rs b/src/app/ui/login.rs index 716f0eea..79978c93 100644 --- a/src/app/ui/login.rs +++ b/src/app/ui/login.rs @@ -7,7 +7,7 @@ use device_query::Keycode; use egui::{vec2, Align, Layout, RichText}; use windows_sys::w; -use windows_sys::Win32::UI::WindowsAndMessaging::{MessageBoxW, MB_ICONWARNING, MB_ICONERROR}; +use windows_sys::Win32::UI::WindowsAndMessaging::{MessageBoxW, MB_ICONERROR, MB_ICONWARNING}; impl TemplateApp { pub fn state_login( @@ -42,7 +42,10 @@ impl TemplateApp { std::thread::spawn(move || unsafe { MessageBoxW( 0, - str::encode_utf16(err.to_string().as_str()).chain(iter::once(0)).collect::>().as_ptr(), + str::encode_utf16(err.to_string().as_str()) + .chain(iter::once(0)) + .collect::>() + .as_ptr(), w!("Error"), MB_ICONERROR, ); @@ -64,7 +67,10 @@ impl TemplateApp { std::thread::spawn(move || unsafe { MessageBoxW( 0, - str::encode_utf16(err.to_string().as_str()).chain(iter::once(0)).collect::>().as_ptr(), + str::encode_utf16(err.to_string().as_str()) + .chain(iter::once(0)) + .collect::>() + .as_ptr(), w!("Error"), MB_ICONWARNING, ); diff --git a/src/app/ui/mode_selection.rs b/src/app/ui/mode_selection.rs index c9b70142..fe46e4eb 100644 --- a/src/app/ui/mode_selection.rs +++ b/src/app/ui/mode_selection.rs @@ -1,7 +1,7 @@ use crate::app::backend::TemplateApp; use eframe::Frame; -use egui::{vec2, Align, Layout, RichText, ImageButton}; +use egui::{vec2, Align, ImageButton, Layout, RichText}; use std::sync::atomic::Ordering; @@ -13,26 +13,24 @@ impl TemplateApp { _frame.set_window_size(vec2(700., 300.)); egui::CentralPanel::default().show(ctx, |ui| { - ui.allocate_ui(vec2(ui.available_width(), 20.), |ui|{ - ui.with_layout(Layout::left_to_right(Align::Center), |ui|{ + ui.allocate_ui(vec2(ui.available_width(), 20.), |ui| { + ui.with_layout(Layout::left_to_right(Align::Center), |ui| { + ui.label(RichText::from("Welcome,").weak().size(20.)); ui.label( - RichText::from("Welcome,").weak().size(20.) - ); - ui.label( - RichText::from(format!("{}", self.login_username)).strong().size(20.) + RichText::from(format!("{}", self.login_username)) + .strong() + .size(20.), ); if ui.button("Logout").clicked() { self.mode_selector = false; - } /*if ui.add( ImageButton::new(egui::include_image!("../../../icons/logout.png")) ).clicked() { - self.mode_selector = false; + self.mode_selector = false; } */ }); }); - ui.columns(2, |ui| { ui[0].with_layout( diff --git a/src/app/ui/server.rs b/src/app/ui/server.rs index 8be64990..b7a81bed 100644 --- a/src/app/ui/server.rs +++ b/src/app/ui/server.rs @@ -50,7 +50,10 @@ impl TemplateApp { let temp_open_on_port = &self.open_on_port; ui.checkbox(&mut self.ipv4_mode, "Internet protocol (IP) v4 mode"); if ui.button("Start").clicked() { - let temp_tx = self.stx.clone(); + + //to be implemented for later msg showing to server mode + let _temp_tx = self.stx.clone(); + let server_pw = self.server_password.clone(); let ip_v4 = self.ipv4_mode; self.server_has_started = match temp_open_on_port.parse::() { @@ -59,22 +62,7 @@ impl TemplateApp { match server::server_main(port.to_string(), server_pw, ip_v4) .await { - Ok(ok) => { - dbg!(&ok); - - let mut concatenated_string = String::new(); - - for s in &ok { - concatenated_string.push_str(s); - } - - match temp_tx.send(ok.join(&concatenated_string)) { - Ok(_) => {} - Err(err) => { - println!("ln 214 {}", err) - } - }; - } + Ok(_temp_stuff) => {} Err(err) => { println!("ln 208 {:?}", err); } diff --git a/src/lib.rs b/src/lib.rs index 7988ec63..0d93d3d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ #![allow(non_snake_case)] #![warn(clippy::all, rust_2018_idioms)] +#![feature(path_file_prefix)] pub mod app; diff --git a/src/main.rs b/src/main.rs index 3f96322e..cb44a1e5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ #![allow(non_snake_case)] #![warn(clippy::all, rust_2018_idioms)] +#![feature(path_file_prefix)] #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release mod app; #[cfg(not(target_arch = "wasm32"))] diff --git a/temp.txt b/temp.txt new file mode 100644 index 00000000..d1a6d28a --- /dev/null +++ b/temp.txt @@ -0,0 +1,82 @@ + async fn serve_file( + &self, + request: Request, + ) -> Result, Status> { + let req = request.into_inner().clone(); + + let file_path_vec = self.file_paths.lock().unwrap(); + + //check for index in uploaded files path vector + // EX :: + // Vec() => {"C:\Apad.exe", "C:\XD"} + // INPUT :: + // 1 + // OUTPUT :: + // "C:\XD" + + let apad = &file_path_vec[req.index as usize]; + + let file = fs::read(apad).unwrap(); + + let file_name = apad.file_name().unwrap().to_string_lossy().to_string(); + + //reply with file name, bytes + let reply = FileResponse { + file, + name: file_name, + }; + + Ok(Response::new(reply)) + } + + + + if i.contains("file_upload") { + //use a character before file upload which cannot be set as a file name + let sort_msg: Vec<&str> = i.split('>').collect(); + + let re = Regex::new(r#"'(.*?[^\\])'"#).unwrap(); + + let mut results: Vec = Vec::new(); + + for captured in re.captures_iter(sort_msg[1]) { + if let Some(inner_text) = captured.get(1) { + results.push(inner_text.as_str().to_string()); + } + } + ui.horizontal(|ui| { + ui.label( + RichText::from(sort_msg[0]).size(self.font_size), + ); + if ui + .button( + RichText::from(format!( + "Download {}", + results[0] + )) + .strong() + .size(self.font_size), + ) + .clicked() + { + let ip = self.send_on_ip.clone(); + // tokio::spawn(async move { + // match request_file( + // results[1].parse::().unwrap(), + // ip, + // ) + // .await + // { + // Ok(file_reponse) => { + // if let Err(err) = + // write_file(file_reponse) + // { + // println!("{err}") + // }; + // } + // Err(err) => println!("{err}"), + // }; + // }); + }; + }); + } \ No newline at end of file