Skip to content

Commit

Permalink
fixed server ip:port validation #7
Browse files Browse the repository at this point in the history
  • Loading branch information
alembiq committed Jul 2, 2024
1 parent d94af30 commit 27f2444
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 72 deletions.
26 changes: 11 additions & 15 deletions lesson16/src/bin/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@ use std::thread::{self, JoinHandle};
use eyre::{anyhow, bail, Context, Result};

use lesson16::{
create_directory, current_time, filename_from_input, image_to_png, incoming_message,
outgoing_message, read_file, server_address, MessageType, DIRECTORY_FILES, DIRECTORY_IMAGES,
create_directory, filename_from_input, image_to_png, incoming_message, outgoing_message,
read_file, server_address, timestamp, MessageType, DIRECTORY_FILES, DIRECTORY_IMAGES,
};

fn main() -> Result<()> {
let server_address: String = server_address(env::args().collect());

println!(
"{} Client connecting to {}!",
current_time(),
server_address
);
println!("{} Client connecting to {}!", timestamp(), server_address);

let outgoing_stream = match TcpStream::connect(server_address) {
Ok(s) => s,
Expand Down Expand Up @@ -63,7 +59,7 @@ fn outgoing(mut stream: TcpStream) -> Result<JoinHandle<Result<()>>> {
let message: MessageType = {
match trimmed_input.split_whitespace().next().unwrap_or_default() {
".quit" => {
println!("{} Exiting!", current_time(),);
println!("{} Exiting!", timestamp(),);
process::exit(0)
}
".file" => MessageType::File(
Expand All @@ -79,7 +75,7 @@ fn outgoing(mut stream: TcpStream) -> Result<JoinHandle<Result<()>>> {
if let Err(e) = outgoing_message(&mut stream, &message) {
eprintln!(
"{} Failed to broadcast message: {message:?} -> {e}",
current_time()
timestamp()
);
}
};
Expand All @@ -95,34 +91,34 @@ fn incoming(mut stream: TcpStream) -> JoinHandle<()> {
let message = match incoming_message(&mut stream) {
Ok(res) => res,
Err(e) => {
eprintln!("{} Stream inter: {e}", current_time());
eprintln!("{} Stream inter: {e}", timestamp());
//FIXME infinite loop
process::exit(1);
}
};

match message {
MessageType::Text(text) => {
println!("{} {text:?}", current_time());
println!("{} {text:?}", timestamp());
}
MessageType::File(name, content) => {
//TODO unable to save
//TODO file already exist
fs::write(format!("{}/{}", DIRECTORY_FILES, name), content)
.expect("Could not write file");
println!("{} Receiving {name}", current_time());
println!("{} Receiving {name}", timestamp());
}
MessageType::Image(image) => {
//TODO unable to save
//TODO file already exist
let timestamp: String = std::time::UNIX_EPOCH
let filename: String = std::time::UNIX_EPOCH
.elapsed()
.unwrap()
.as_secs()
.to_string();
fs::write(format!("{}/{}.png", DIRECTORY_IMAGES, timestamp), &image)
fs::write(format!("{}/{}.png", DIRECTORY_IMAGES, filename), &image)
.expect("Could not write file");
println!("{} Receiving {timestamp}.png", current_time());
println!("{} Receiving {filename}.png", timestamp());
}
}
})
Expand Down
23 changes: 10 additions & 13 deletions lesson16/src/bin/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ use std::thread;
use eyre::{bail, Result};
use parking_lot::Mutex;

use lesson16::{current_time, incoming_message, outgoing_message, server_address, MessageType};
use lesson16::{incoming_message, outgoing_message, server_address, timestamp, MessageType};

fn main() -> Result<()> {
let server_address: String = server_address(env::args().collect());
println!("{} Starting server on {}!", current_time(), server_address);
println!("{} Starting server on {}!", timestamp(), server_address);
listen_and_accept(server_address)?;
Ok(())
}
Expand All @@ -20,7 +20,7 @@ fn listen_and_accept(address: String) -> Result<()> {
let listener = match TcpListener::bind(address) {
Ok(l) => l,
Err(e) => {
bail!("{} Unable to listen: {e}", current_time())
bail!("{} Unable to listen: {e}", timestamp())
}
};

Expand All @@ -29,7 +29,7 @@ fn listen_and_accept(address: String) -> Result<()> {
for stream in listener.incoming() {
let mut stream = stream.unwrap();
let addr = stream.peer_addr().unwrap();
println!("{} {} stream started", current_time(), addr);
println!("{} {} stream started", timestamp(), addr);

{
clients.lock().insert(addr, stream.try_clone().unwrap());
Expand All @@ -41,7 +41,7 @@ fn listen_and_accept(address: String) -> Result<()> {
let message = match incoming_message(&mut stream) {
Ok(msg) => msg,
Err(e) => {
eprintln!("{} {addr} stream interrupted: {e}", current_time());
eprintln!("{} {addr} stream interrupted: {e}", timestamp());
break;
}
};
Expand All @@ -55,10 +55,7 @@ fn listen_and_accept(address: String) -> Result<()> {
}

if let Err(e) = outgoing_message(peer_stream, &message) {
eprintln!(
"{} failed to send message: {message:?} -> {e}",
current_time()
);
eprintln!("{} failed to send message: {message:?} -> {e}", timestamp());
peers_to_remove.push(*peer_addr);
}
}
Expand All @@ -72,18 +69,18 @@ fn listen_and_accept(address: String) -> Result<()> {
//MESSAGE SNEAKPEAK
match message {
MessageType::Text(text) => {
println!("{} {addr}: {text:?}", current_time());
println!("{} {addr}: {text:?}", timestamp());
}
MessageType::File(name, _content) => {
println!("{} {addr} sending: {}", current_time(), name);
println!("{} {addr} sending: {}", timestamp(), name);
}
MessageType::Image(_image) => {
let timestamp: String = std::time::UNIX_EPOCH
let filename: String = std::time::UNIX_EPOCH
.elapsed()
.unwrap()
.as_secs()
.to_string();
println!("{} {addr} sending: {}.png", current_time(), timestamp);
println!("{} {addr} sending: {}.png", timestamp(), filename);
}
}
});
Expand Down
55 changes: 41 additions & 14 deletions lesson16/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
#![warn(missing_docs)]
#![doc(issue_tracker_base_url = "https://github.com/alembiq/rust-developer/issues/")]
//! Just a simple CLI chat
//!
//! Able to send messages, files and images (always converted to png)
//! created for a `RUST` training course with [robot_dreams](https://robot-dreams-rust.mag.wiki/)
use std::error::Error;
use std::fs::{self};
use std::io::{Cursor, Read, Write};
Expand All @@ -10,10 +17,15 @@ use image::ImageEncoder;
use serde::{Deserialize, Serialize};
use thiserror::Error;

/// Default server 127.0.0.1:11111
pub static DEFAULT_ADDRESS: &str = "127.0.0.1:11111";
/// Default folder for storing incoming `files`
pub static DIRECTORY_FILES: &str = "files";
/// Default folder for storing incoming `images`
pub static DIRECTORY_IMAGES: &str = "images";

#[doc(hidden)]
/// Error messages used by [thiserror](https://docs.rs/thiserror/latest/thiserror/)
#[derive(Error, Debug)]
pub enum ErrorMessage {
#[error("File {0} not found.")]
Expand All @@ -30,41 +42,52 @@ pub enum ErrorMessage {
InvalidMessageFormat(#[from] ciborium::de::Error<std::io::Error>),
}

pub fn current_time() -> String {
/// Returns timestamp as String
pub fn timestamp() -> String {
std::time::UNIX_EPOCH
.elapsed()
.unwrap()
.as_secs()
.to_string()
}

/// CONNECTIVITY
pub fn is_valid_ip(ip: &str) -> bool {
ip.parse::<IpAddr>().is_ok()
}

/// Return server address
/// eighter one from the CLI parameter or a default one
///
/// # Example
/// ```
/// # use lesson16::server_address;
/// # use std::env;
/// let server_address: String = server_address(env::args().collect());
/// println!("{}",server_address);
/// ```
pub fn server_address(args: Vec<String>) -> String {
if args.len() > 1 && args[1] == "help" {
println!("=============== USAGE ===============");
println!("{} IPaddress:port", args[0]);
process::exit(0)
} else if args.len() > 1 && args[1].parse::<IpAddr>().is_ok() {
} else if args.len() > 1 {
let ip: Vec<&str> = args[1].split(':').collect();
assert!(ip[0].parse::<IpAddr>().is_ok());
assert!((1..65535).contains(&ip[1].parse::<i32>().unwrap()));
args[1].clone()
} else {
DEFAULT_ADDRESS.to_string()
}
}

/// MESSAGE HANDLING maybe redo as implementations of MessageType
/// Define types of messages
#[derive(Serialize, Deserialize, Debug)]
pub enum MessageType {
File(String, Vec<u8>), // Filename and its content as bytes
/// File as name and content.
File(String, Vec<u8>),
/// 'PNG' image as content.
Image(Vec<u8>),
/// Text.
Text(String),
}

/// Process incomming [MessageType]
pub fn incoming_message(stream: &mut TcpStream) -> Result<MessageType, ErrorMessage> {
let mut len_bytes = [0; 4];
stream.read_exact(&mut len_bytes)?;
Expand All @@ -75,6 +98,7 @@ pub fn incoming_message(stream: &mut TcpStream) -> Result<MessageType, ErrorMess
Ok(ciborium::from_reader(&mut &buffer[..])?)
}

/// Send [MessageType]
pub fn outgoing_message(
stream: &mut TcpStream,
message: &MessageType,
Expand All @@ -87,21 +111,22 @@ pub fn outgoing_message(
Ok(())
}

/// FILE HANDLING
/// Reads file to be send
pub fn read_file(input: String) -> Vec<u8> {
let mut filename = input.split_whitespace();
let filename: &str = filename.nth(1).expect("missing filename");
std::fs::read(format!("./{}", filename)).unwrap()
}

/// Creates directory for files/images to be stored
pub fn create_directory(directory: &str) {
if !Path::new(directory).is_dir() {
fs::create_dir(directory).unwrap();
println!("{} creating {} directory", current_time(), { directory });
println!("{} creating {} directory", timestamp(), { directory });
}
}

/// Reads filename from a user command
pub fn filename_from_input(user_input: &str) -> Result<&str, ErrorMessage> {
let filename = user_input.split(' ').nth(1).expect("missing filename");
if filename.is_empty() {
Expand All @@ -110,6 +135,8 @@ pub fn filename_from_input(user_input: &str) -> Result<&str, ErrorMessage> {
Ok(filename)
}

/// Converts image to `PNG`
/// - meant to be used on sender side
pub fn image_to_png(file: &str) -> Vec<u8> {
let img = image::open(file).unwrap();
let mut output = Cursor::new(Vec::new());
Expand Down
26 changes: 11 additions & 15 deletions lesson18/src/bin/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,14 @@ use std::thread::{self, JoinHandle};
use eyre::{anyhow, bail, Context, Result};

use lesson18::{
create_directory, current_time, filename_from_input, image_to_png, incoming_message,
outgoing_message, read_file, server_address, MessageType, DIRECTORY_FILES, DIRECTORY_IMAGES,
create_directory, filename_from_input, image_to_png, incoming_message, outgoing_message,
read_file, server_address, timestamp, MessageType, DIRECTORY_FILES, DIRECTORY_IMAGES,
};

fn main() -> Result<()> {
let server_address: String = server_address(env::args().collect());

println!(
"{} Client connecting to {}!",
current_time(),
server_address
);
println!("{} Client connecting to {}!", timestamp(), server_address);

let outgoing_stream = match TcpStream::connect(server_address) {
Ok(s) => s,
Expand Down Expand Up @@ -63,7 +59,7 @@ fn outgoing(mut stream: TcpStream) -> Result<JoinHandle<Result<()>>> {
let message: MessageType = {
match trimmed_input.split_whitespace().next().unwrap_or_default() {
".quit" => {
println!("{} Exiting!", current_time(),);
println!("{} Exiting!", timestamp(),);
process::exit(0)
}
".file" => MessageType::File(
Expand All @@ -79,7 +75,7 @@ fn outgoing(mut stream: TcpStream) -> Result<JoinHandle<Result<()>>> {
if let Err(e) = outgoing_message(&mut stream, &message) {
eprintln!(
"{} Failed to broadcast message: {message:?} -> {e}",
current_time()
timestamp()
);
}
};
Expand All @@ -95,34 +91,34 @@ fn incoming(mut stream: TcpStream) -> JoinHandle<()> {
let message = match incoming_message(&mut stream) {
Ok(res) => res,
Err(e) => {
eprintln!("{} Stream inter: {e}", current_time());
eprintln!("{} Stream inter: {e}", timestamp());
//FIXME infinite loop
process::exit(1);
}
};

match message {
MessageType::Text(text) => {
println!("{} {text:?}", current_time());
println!("{} {text:?}", timestamp());
}
MessageType::File(name, content) => {
//TODO unable to save
//TODO file already exist
fs::write(format!("{}/{}", DIRECTORY_FILES, name), content)
.expect("Could not write file");
println!("{} Receiving {name}", current_time());
println!("{} Receiving {name}", timestamp());
}
MessageType::Image(image) => {
//TODO unable to save
//TODO file already exist
let timestamp: String = std::time::UNIX_EPOCH
let imagename: String = std::time::UNIX_EPOCH
.elapsed()
.unwrap()
.as_secs()
.to_string();
fs::write(format!("{}/{}.png", DIRECTORY_IMAGES, timestamp), &image)
fs::write(format!("{}/{}.png", DIRECTORY_IMAGES, imagename), &image)
.expect("Could not write file");
println!("{} Receiving {timestamp}.png", current_time());
println!("{} Receiving {imagename}.png", timestamp());
}
}
})
Expand Down
Loading

0 comments on commit 27f2444

Please sign in to comment.