diff --git a/src/client.rs b/src/client.rs index f9c942f..c738773 100644 --- a/src/client.rs +++ b/src/client.rs @@ -58,12 +58,12 @@ fn read_messages(mut stream: TcpStream) { fn process_message(msg_bytes: &[u8]) { match msg_bytes[0] { - codes::ERROR => { + codes::ERROR => + { #[cfg(debug_assertions)] - println!("err: {:x?}", msg_bytes[1]); match msg_bytes[1] { codes::error::INVALID_ROOM => { - println!("Attempted to message or list non-existant room. Try again"); + println!("Operation Performed on an invalid room. Try again"); } codes::error::NICKNAME_COLLISION => { println!( @@ -98,11 +98,23 @@ fn process_message(msg_bytes: &[u8]) { fn disconnect() {} -fn show(stream: &mut TcpStream) {} +fn help() { + println!("Available commands:"); + println!("/quit <- Disconnect and stop the client"); + println!("/rooms <- List all of the rooms on the server"); + println!("/users <- List all of the user connected to the server"); + println!("/list [room-name] <- List all of the users in the given room"); + println!("/join [room-name] <- Join the given room. Create the room if it does not exist"); + println!( + "/leave [room-name] <- Leave the given room. Error if the you are not already in the room" + ); + println!("/show [room-name] <- Switch your focus to the given room. It is suggested to join the room first"); +} pub fn start() { println!("Starting the IRC client. No spaces allowed in nicknames or room names"); let mut nick: String; + let mut active_room: String = String::new(); loop { nick = input!("Enter your nickname : "); if nick.contains(" ") { @@ -125,47 +137,87 @@ pub fn start() { }); //try to register the nickname - let mut buf: Vec = vec![0; nick.capacity()]; - buf[0] = codes::client::REGISTER_NICK; - for i in 1..nick.len() + 1 { - buf[i] = *nick.as_bytes().get(i - 1).unwrap(); - } - stream.write(&buf).unwrap(); + one_param_op(codes::client::REGISTER_NICK, &mut stream, &nick); loop { let inp: String = input!(":"); - let cmds: Vec<_> = inp.split(" ").collect(); - match *cmds.get(0).unwrap() { - "/quit" => disconnect(), - "/rooms" => no_param_op(codes::client::LIST_ROOMS, &mut stream), - "/users" => no_param_op(codes::client::LIST_USERS, &mut stream), - "/list" => { - let room = *cmds.get(1).unwrap(); - one_param_op(codes::client::LIST_USERS_IN_ROOM, &mut stream, room); - } - "/join" => { - let room = *cmds.get(1).unwrap(); - one_param_op(codes::client::JOIN_ROOM, &mut stream, room); - } - "/show" => show(&mut stream), - "/leave" => { - let room: &str = *cmds.get(1).unwrap(); - one_param_op(codes::client::LEAVE_ROOM, &mut stream, room); - } - "/msg" => { - let room: &str = *cmds.get(1).unwrap(); - // let message = *cmds. - // two_param_op( - // codes::client::SEND_MESSAGE_TO_ROOM, - // &mut stream, - // param0, - // param1, - // ) - } + let mut args: std::str::SplitWhitespace<'_> = inp.split_whitespace(); + let command: Option<&str> = args.next(); - _ => { - println!("Not implemented"); + match command { + Some(cmd) => { + let param: Option<&str> = args.next(); + match cmd { + "/quit" => disconnect(), + "/rooms" => no_param_op(codes::client::LIST_ROOMS, &mut stream), + "/users" => no_param_op(codes::client::LIST_USERS, &mut stream), + "/list" => match param { + Some(room) => { + one_param_op(codes::client::LIST_USERS_IN_ROOM, &mut stream, room); + } + None => { + println!("Room name expected, but not provided"); + } + }, + "/join" => match param { + Some(room) => { + one_param_op(codes::client::JOIN_ROOM, &mut stream, room); + } + None => { + println!("Room name expected, but not provided"); + } + }, + "/show" => match param { + Some(room) => { + active_room = room.to_string(); + } + None => { + println!("Room name expected, but not provided") + } + }, + "/leave" => match param { + Some(room) => { + one_param_op(codes::client::LEAVE_ROOM, &mut stream, room); + } + None => { + println!("Room name expected, but not provided"); + } + }, + "/msg" => match inp.split_once(" ") { + Some((room, msg)) => { + two_param_op( + codes::client::SEND_MESSAGE_TO_ROOM, + &mut stream, + room, + msg, + ); + } + None => { + println!("Usage: /msg [room] [message]"); + } + }, + "/help" => { + help(); + } + "/" => { + println!("Invalid command"); + } + _ => { + if active_room.is_empty() { + println!("use '/show [room]' to set an active room before sending a message"); + } else { + let message: String = inp; + two_param_op( + codes::client::SEND_MESSAGE_TO_ROOM, + &mut stream, + &active_room, + &message, + ); + } + } + } } + None => {} } } } else { diff --git a/src/lib.rs b/src/lib.rs index a3fc597..5c4907f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,5 +23,11 @@ pub mod codes { pub const SERVER_FULL: u8 = 0x12; pub const ALREADY_REGISTERED: u8 = 0x13; pub const NOT_YET_REGISTERED: u8 = 0x14; + pub const MALFORMED: u8 = 0x15; + pub const ALREADY_IN_ROOM: u8 = 0x16; } } + +pub fn clear() { + print!("\x1B[2J"); +} diff --git a/src/server.rs b/src/server.rs index f33617e..f04d97d 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,6 +1,6 @@ use std::sync::{Arc, Mutex}; use std::{ - collections::{HashMap, HashSet}, + collections::HashMap, io::{Read, Write}, net::{TcpListener, TcpStream}, thread, @@ -14,28 +14,28 @@ const MAX_USERS: usize = 20; #[derive(Debug)] struct Server { - users: HashSet, + users: HashMap, rooms: HashMap>, } impl Server { fn new() -> Self { Server { - users: HashSet::new(), + users: HashMap::new(), rooms: HashMap::new(), } } } -fn send_all(op:u8, listener: TcpListener) { +fn message(room: &str, message: &str) {} + +fn send_all(op: u8, listener: TcpListener) { for tcpstream in listener.incoming() { match tcpstream { Ok(mut stream) => { stream.write_all(&[op]).unwrap(); - }, - Err(_) => { - } + Err(_) => {} } } } @@ -69,7 +69,7 @@ fn handle_client( let unlocked_server: std::sync::MutexGuard<'_, Server> = server.lock().unwrap(); let mut buf_out: Vec = Vec::new(); buf_out.extend_from_slice(&[codes::RESPONSE]); - for user in &unlocked_server.users { + for (user, _) in &unlocked_server.users { buf_out.extend_from_slice(user.as_bytes()); buf_out.extend_from_slice(&[0x20]); } @@ -99,22 +99,39 @@ fn handle_client( codes::client::JOIN_ROOM => { let p: String = String::from_utf8_lossy(param_bytes).to_string(); - let params: Vec<&str> = p.split(' ').collect(); + let params: Vec<&str> = p.split_whitespace().collect(); let room = params.get(0).unwrap(); - join_room(server, &nickname, room, stream) + join_room(server, &nickname, room, stream); } codes::client::LEAVE_ROOM => { let p: String = String::from_utf8_lossy(param_bytes).to_string(); - let params: Vec<&str> = p.split(' ').collect(); + let params: Vec<&str> = p.split_whitespace().collect(); let room = params.get(0).unwrap(); - leave_room(server, &nickname, room, stream) + leave_room(server, &nickname, room, stream); } codes::client::SEND_MESSAGE => { #[cfg(debug_assertions)] println!("SEND_MESSAGE"); } + + codes::client::SEND_MESSAGE_TO_ROOM => { + let p: String = String::from_utf8_lossy(param_bytes).to_string(); + let params: Option<(&str, &str)> = p.split_once(" "); + match params { + Some((room, msg)) => { + message(room, msg); + } + _ => { + stream + .write(&[codes::ERROR, codes::error::MALFORMED]) + .unwrap(); + } + } + #[cfg(debug_assertions)] + println!("SEND_MESSAGE_TO_ROOM, {} ", p); + } _ => { #[cfg(debug_assertions)] println!("Unspecified client Op, {:x?}", cmd_bytes); @@ -127,15 +144,16 @@ fn handle_client( fn register_nick(server: &Arc>, nickname: &str, stream: &mut TcpStream) { // Check for nickname collision let mut unlocked_server: std::sync::MutexGuard<'_, Server> = server.lock().unwrap(); - if unlocked_server.users.contains(nickname) { + if unlocked_server.users.contains_key(nickname) { #[cfg(debug_assertions)] println!("Nickname Collision, {}", nickname); stream .write_all(&[codes::ERROR, codes::error::NICKNAME_COLLISION]) .unwrap(); } else { + let stream_clone = stream.try_clone().expect("failed to clone"); // Add the user to the user list - unlocked_server.users.insert(nickname.to_string()); + unlocked_server.users.insert(nickname.to_string(), stream_clone); // Send response ok stream.write_all(&[codes::RESPONSE_OK]).unwrap(); @@ -147,6 +165,12 @@ fn join_room(server: &Arc>, user: &str, room: &str, stream: &mut T match unlocked_server.rooms.get_mut(room) { Some(l) => { + for ele in l.into_iter() { + if ele == user{ + stream.write_all(&[codes::ERROR, codes::error::ALREADY_IN_ROOM]).unwrap(); + return; + } + } l.push(user.to_string()); } None => { @@ -184,7 +208,6 @@ fn leave_room(server: &Arc>, user: &str, room: &str, stream: &mut pub fn start() { let listener: TcpListener = TcpListener::bind(SERVER_ADDRESS).expect("Failed to bind to port"); - // let incoming: &std::net::Incoming = &listener.incoming(); let server: Arc> = Arc::new(Mutex::new(Server::new())); let server_outer: Arc> = Arc::clone(&server); println!("Server listening on {}", SERVER_ADDRESS); @@ -256,7 +279,9 @@ pub fn start() { let inp: String = input!(":"); match inp.parse::() { Ok(num) => match num { - 0 => {println!("Goodbye"); }, + 0 => { + println!("Goodbye"); + } 1 => println!("Users: {:?}", server.lock().unwrap().users), 2 => println!("Rooms: {:?}", server.lock().unwrap().rooms), _ => println!("Invalid Input"),