From 5a8792b9fd40f80b16f8e672a87044030c92ff01 Mon Sep 17 00:00:00 2001 From: Andrea Date: Wed, 22 Jan 2025 11:48:35 +0100 Subject: [PATCH] Implement message parsing and command handling in client module --- src/client/mod.rs | 111 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 94 insertions(+), 17 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index 84830a3..7816deb 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -6,11 +6,42 @@ pub(crate) mod handlers { use base64::{engine::general_purpose::STANDARD as BASE64, Engine as _}; use log::{debug, error, info}; + use serde::Deserialize; use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader}; use tokio::net::TcpStream; use tokio::sync::broadcast; use x25519_dalek::{EphemeralSecret, PublicKey}; + /* + Specifications of the packet + 32 bytes - Command name + 512 bytes - Command argument + if command is empty then it is a message + */ + #[derive(Deserialize, Debug)] + struct Message { + command: Vec, + argument: Vec, // Changed from Vec to Vec + } + + fn parse_message(message: &str) -> Message { + let mut iter = message.split_whitespace(); + + let command: Vec = if let Some(cmd) = iter.next() { + if cmd.starts_with("/") { + vec![cmd.to_string()] + } else { + Vec::new() // Empty command means it's a regular message + } + } else { + Vec::new() + }; + + let argument: Vec = iter.map(String::from).collect(); + + Message { command, argument } + } + pub async fn handle_client( socket: TcpStream, tx: broadcast::Sender, @@ -52,7 +83,7 @@ pub(crate) mod handlers { .unwrap(); let username = String::from_utf8(decrypted)?; let username_read = username.clone(); // Clone for read task - let username_write = username.clone(); // Clone for write task + let mut username_write = username.clone(); // Clone for write task // Read task for receiving messages from the client let read_task = tokio::spawn(async move { @@ -90,25 +121,71 @@ pub(crate) mod handlers { } }; - info!( - "Received message from {}: {}", - username_read, - message.trim() - ); + info!("Parsing message"); - if message.trim() == "/quit" { - info!("Client requested to quit"); - break; - } + let parsed_message = parse_message(message.as_str()); + // Handle commands + if !parsed_message.command.is_empty() { + match parsed_message.command[0].as_str() { + "/msg" => { + if parsed_message.argument.len() < 2 { + match tx.send("Error! Invalid /msg format".to_string()) { + Ok(_) => info!( + "Error message sent to client {}", + username_write + ), + Err(e) => { + error!("Failed to send error message: {:?}", e); + break; + } + } + continue; + } + let target_user = &parsed_message.argument[0]; + let msg_content = parsed_message.argument[1..].join(" "); + info!("Private message to {}: {}", target_user, msg_content); + } - let formatted_message = format!("{}: {}", username_read, message.trim()); + "/quit" => { + info!("Client requested to quit"); + break; + } - // Broadcast the message to all clients - match tx.send(formatted_message) { - Ok(_) => info!("Message broadcast successfully"), - Err(e) => { - error!("Failed to broadcast message: {:?}", e); - break; + "/nickname" => { + if parsed_message.argument.is_empty() { + error!( + "Invalid /nickname format. Usage: /nickname new_name" + ); + continue; + } + let new_nickname = &parsed_message.argument[0]; + info!("Changing nickname to: {}", new_nickname); + username_write = new_nickname.clone(); + // Here implement your nickname change logic + } + + _ => { + error!("Unknown command: {}", parsed_message.command[0]); + } + } + } else { + // Regular message handling + info!( + "Received message from {}: {}", + username_read, + parsed_message.argument.join(" ") + ); + + let formatted_message = + format!("{}: {}", username_read, message.trim()); + + // Broadcast the message to all clients + match tx.send(formatted_message) { + Ok(_) => info!("Message broadcast successfully"), + Err(e) => { + error!("Failed to broadcast message: {:?}", e); + break; + } } } }