diff --git a/client-gui/src/gui_app.rs b/client-gui/src/gui_app.rs index 5e606fe..b271593 100644 --- a/client-gui/src/gui_app.rs +++ b/client-gui/src/gui_app.rs @@ -1,17 +1,16 @@ use client_network::{ ChunkNode, MerkleNode, MerkleTree, NetworkCommand, NetworkEvent, NodeHash, - big_or_chunk_to_file, filename_to_string, generate_base_tree, node_hash_to_hex_string, - node_to_file, remove_null_bytes, + big_or_chunk_to_file, generate_base_tree, node_hash_to_hex_string, remove_null_bytes, }; use crossbeam_channel::{Receiver, Sender}; use egui::{ - Align, CentralPanel, CollapsingHeader, Color32, Context, CornerRadius, Frame, Layout, Response, - ScrollArea, SidePanel, Stroke, TopBottomPanel, Ui, ViewportCommand, + Align, CentralPanel, CollapsingHeader, Color32, Context, CornerRadius, Frame, Id, Layout, + ProgressBar, ScrollArea, SidePanel, Stroke, TopBottomPanel, Ui, ViewportCommand, }; +use std::collections::HashMap; use std::collections::HashSet; -use std::{collections::HashMap, fmt::format, io::Seek}; -use std::fs::{File, OpenOptions, create_dir}; +use std::fs::{OpenOptions, create_dir}; enum ServerStatus { Loading, @@ -20,24 +19,19 @@ enum ServerStatus { ConnectedHandshake, } -// --- Main Application Struct --- pub struct P2PClientApp { remaining: std::time::Duration, // temps restant last_update: std::time::Instant, // pour calculer delta timer_started: bool, - // Communication channels network_cmd_tx: Sender, network_event_rx: Receiver, - // GUI State - status_message: String, known_peers: Vec<(String, bool)>, loading_peers: Vec, connect_address_input: String, connected_address: String, connect_name_input: String, - // Key: Parent Directory Hash (String), Value: List of children FileNode loaded_fs: HashMap, shared_tree: MerkleTree, @@ -46,25 +40,28 @@ pub struct P2PClientApp { server_status: ServerStatus, - show_network_popup: bool, // gérer selon besoin - error_message: Option<(String, String)>, // Some(message) -> afficher, None -> rien success_message: Option<(String, String)>, // Some(message) -> afficher, None -> rien active_server: String, current_downloading_file_map: MerkleTree, remaining_chunks: HashSet<[u8; 32]>, + + // total number of chunks expected for the current download (set when download starts) + current_total_chunks: Option, + + // number of chunks received so far (count of removed remaining_chunks) + current_received_chunks: usize, + root_downloading_file: String, + show_network_window: bool, + show_choose_server_window: bool, } impl P2PClientApp { pub fn new(cmd_tx: Sender, event_rx: Receiver) -> Self { - //let (root_hash, tree_content) = MerkleNode::generate_base_tree(); - - let mut loaded_fs = HashMap::new(); - let mut current_downloading_file_map = MerkleTree::new(HashMap::new(), [0; 32]); - //let tree = MerkleTree::new(tree_content, root_hash); - //loaded_fs.insert("bob".to_string(), tree); + let loaded_fs = HashMap::new(); + let current_downloading_file_map = MerkleTree::new(HashMap::new(), [0; 32]); Self { remaining: std::time::Duration::from_secs(0), @@ -72,7 +69,6 @@ impl P2PClientApp { last_update: std::time::Instant::now(), network_cmd_tx: cmd_tx, network_event_rx: event_rx, - status_message: "Client Initialized. Awaiting network status...".to_string(), known_peers: Vec::new(), loading_peers: Vec::new(), connect_address_input: "https://jch.irif.fr:8443".to_string(), @@ -80,15 +76,18 @@ impl P2PClientApp { loaded_fs, active_peer: None, server_status: ServerStatus::NotConnected, - show_network_popup: false, error_message: None, success_message: None, connect_name_input: "bob".to_string(), active_server: "".to_string(), shared_tree: generate_base_tree(), current_downloading_file_map: current_downloading_file_map, + current_total_chunks: None, + current_received_chunks: 0, root_downloading_file: "".to_string(), remaining_chunks: HashSet::new(), + show_network_window: false, + show_choose_server_window: false, } } pub fn show_error(&mut self, msg: impl Into, peer_username: impl Into) { @@ -103,9 +102,15 @@ impl P2PClientApp { pub fn clear_success(&mut self) { self.success_message = None; } -} -// --- eframe::App Trait Implementation --- + fn set_current_total_chunks(&mut self, len: Option) { + self.current_total_chunks = len + } + + fn set_current_received_chunks(&mut self, arg: usize) { + self.current_received_chunks = arg + } +} impl eframe::App for P2PClientApp { fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) { @@ -115,7 +120,6 @@ impl eframe::App for P2PClientApp { self.timer_started = true; } - // in update (every frame) let now = std::time::Instant::now(); let delta = now.saturating_duration_since(self.last_update); self.last_update = now; @@ -126,18 +130,8 @@ impl eframe::App for P2PClientApp { self.remaining = self.remaining.saturating_sub(delta); } - // 1. Process incoming Network Events - // We poll the channel and update the GUI state for every event received. while let Ok(event) = self.network_event_rx.try_recv() { match event { - NetworkEvent::PeerConnected(addr) => { - todo!(); - - self.status_message = format!("✅ Peer connected: {}", addr); - if !self.known_peers.contains(&(addr, true)) { - self.known_peers.push((addr, true)); - } - } NetworkEvent::RootRequest(addr) => { let root = self.shared_tree.root; let _ = self @@ -163,8 +157,6 @@ impl eframe::App for P2PClientApp { } } NetworkEvent::PeerListUpdated(peers) => { - //todo!(); - self.known_peers = peers; } @@ -204,14 +196,6 @@ impl eframe::App for P2PClientApp { } } NetworkEvent::FileTreeRootReceived(peer_id, root_hash) => { - // todo!(); - - /*self.status_message = format!( - "🔄 Received Merkle Root from {}: {}", - peer_id, - &root_hash[..8] - );*/ - if let Ok(chunknode) = ChunkNode::new(Vec::new()) { let data_map: HashMap = HashMap::new(); //data_map.insert(root_hash, MerkleNode::Chunk(chunknode)); @@ -230,17 +214,11 @@ impl eframe::App for P2PClientApp { println!("tree created"); } - - //self.active_peer_id = Some(peer_id.clone()); - - // Request the content of the root directory immediately - /*let _ = self - .network_cmd_tx - .send(NetworkCommand::RequestDirectoryContent(peer_id, root_hash));*/ } NetworkEvent::Connected(ip) => { self.server_status = ServerStatus::Connected; self.connected_address = ip.clone(); + self.show_choose_server_window = true; let _ = self.network_cmd_tx.send(NetworkCommand::FetchPeerList( self.connected_address.clone(), )); @@ -292,10 +270,11 @@ impl eframe::App for P2PClientApp { true, )); self.remaining_chunks.insert(entry); + self.current_total_chunks = Some(self.remaining_chunks.len()); } self.remaining_chunks.remove(&hash); } - MerkleNode::Chunk(chunk) => { + MerkleNode::Chunk(_) => { self.remaining_chunks.remove(&hash); } _ => {} @@ -328,6 +307,18 @@ impl eframe::App for P2PClientApp { eprintln!("error root absent"); } println!("bigfile téléchargé {}", self.root_downloading_file); + + if let Some(total) = self.current_total_chunks { + // recompute received (safer than incrementing) + let received = total.saturating_sub(self.remaining_chunks.len()); + self.current_received_chunks = received; + } + + if self.remaining_chunks.is_empty() { + self.current_total_chunks = None; + self.current_received_chunks = 0; + println!("bigfile téléchargé"); + } } } NetworkEvent::Success(msg, peer_username) => { @@ -340,26 +331,115 @@ impl eframe::App for P2PClientApp { self.server_status = ServerStatus::NotConnected; let err_msg = format!("Failed to connect to the server: {}", err); self.show_error(err_msg, ""); - let res = self.network_cmd_tx.send(NetworkCommand::ResetServerPeer()); + match self.network_cmd_tx.send(NetworkCommand::ResetServerPeer()) { + Ok(_) => {} + Err(err) => { + println!("GUI Error : {}", err.to_string()); + } + }; } } } + if self.show_choose_server_window { + let full_rect = ctx.input(|i| i.content_rect()); + let modal_size = egui::vec2(400.0, 160.0); + + egui::Area::new(Id::new("modal_blocker_bg")) + .order(egui::Order::Background) + .show(ctx, |ui| { + let painter = ui.painter(); + painter.rect_filled(full_rect, 0.0, egui::Color32::from_black_alpha(160)); + + let sense = egui::Sense::click_and_drag(); + ui.allocate_exact_size(full_rect.size(), sense); + }); + egui::Window::new("Choose the server") + .resizable(false) + .collapsible(false) + .title_bar(true) + .anchor(egui::Align2::CENTER_CENTER, egui::Vec2::ZERO) + .fixed_size(modal_size) + .show(ctx, |ui| { + ScrollArea::vertical() + .auto_shrink([false; 2]) + .show(ui, |ui| { + ui.style_mut().visuals.widgets.inactive.bg_fill = + ui.style().visuals.widgets.inactive.bg_fill; // no-op to get mutable borrow + if self.known_peers.is_empty() { + ui.add_space(10.0); + ui.label("No active peers."); + } else { + for peer in &self.known_peers { + let is_active = + self.active_peer.as_ref().map_or(false, |id| id == &peer.0); // if peer.id == self.active_peer_id + + // place spinner to the right of the label + ui.horizontal(|ui| { + // Use same width for the label widget as the selectable we already created: + // Recreate selectable inline so both label and spinner share the same row. + let resp = if &self.active_server == &peer.0 { + // draw with frame inline + let frame = Frame { + fill: Color32::DARK_BLUE, + stroke: Stroke::default(), + corner_radius: CornerRadius::from(0.5), + ..Default::default() + }; + frame + .show(ui, |ui| { + ui.selectable_label( + is_active, + format!("{}", peer.0), + ) + }) + .inner + } else { + ui.selectable_label(is_active, format!("{}", peer.0)) + }; + + ui.add_space(4.0); // small gap + + // use resp (click handling etc.) + if resp.clicked() { + self.active_server = peer.0.to_string(); + match self.network_cmd_tx.send( + NetworkCommand::ServerHandshake( + peer.0.to_string(), + self.connected_address.clone(), + ), + ) { + Ok(_) => {} + Err(e) => { + println!("GUI Error : {}", e.to_string()); + } + }; + ui.close(); + self.show_choose_server_window = false; + } + }); + } + } + }); + }); + } + // 2. Menu Bar TopBottomPanel::top("top_panel").show(ctx, |ui| { egui::MenuBar::new().ui(ui, |ui| { ui.menu_button("File", |ui| { - if ui.button("Settings").clicked() { - //show settings - } + if ui.button("Settings").clicked() {} if ui.button("Quit").clicked() { - // Use ViewportCommand to request a close ctx.send_viewport_cmd(ViewportCommand::Close); } }); - ui.menu_button("Network", |ui| { + if ui.button("Network").clicked() { + self.show_network_window = true; + } + + if self.show_network_window { match self.server_status { ServerStatus::Connected | ServerStatus::ConnectedHandshake => { let desired = egui::vec2(300.0, 0.0); // width 300, auto-height if 0 @@ -371,62 +451,77 @@ impl eframe::App for P2PClientApp { self.server_status = ServerStatus::NotConnected; self.remaining = std::time::Duration::from_secs(0); self.timer_started = false; - ui.close(); + self.show_network_window = false; } }); } ServerStatus::NotConnected => { - let desired = egui::vec2(0.0, 0.0); // width 300, auto-height if 0 - ui.set_min_size(desired); - ui.vertical(|ui| { - ui.horizontal(|ui| { - ui.label("Server IP:"); - ui.text_edit_singleline(&mut self.connect_address_input); + egui::Window::new("Network") + .resizable(false) + .show(ctx, |ui| { + ui.horizontal(|ui| { + ui.label("Server IP:"); + ui.text_edit_singleline(&mut self.connect_address_input); + }); + ui.horizontal(|ui| { + ui.label("Name:"); + ui.text_edit_singleline(&mut self.connect_name_input); + }); + if ui.button("Connect").clicked() { + let addr = self.connect_address_input.clone(); + let name = self.connect_name_input.clone(); + let _ = self.network_cmd_tx.send( + NetworkCommand::ConnectToServerPut( + addr, + name.to_string(), + ), + ); + self.server_status = ServerStatus::Loading; + ui.close(); + self.show_network_window = false; + self.loaded_fs + .insert(name.to_string(), self.shared_tree.clone()); + } }); - ui.horizontal(|ui| { - ui.label("Name:"); - ui.text_edit_singleline(&mut self.connect_name_input); - }); - if ui.button("Connect").clicked() { - let addr = self.connect_address_input.clone(); - let name = self.connect_name_input.clone(); - let _ = self - .network_cmd_tx - .send(NetworkCommand::ConnectToServerPut(addr, name)); - self.server_status = ServerStatus::Loading; - ui.close(); - } - }); } _ => {} } - }); + } }); }); TopBottomPanel::bottom("bottom_panel").show(ctx, |ui| { ui.horizontal(|ui| { match self.server_status { - ServerStatus::Loading => { - ui.spinner(); - } - ServerStatus::Connected => { - ui.label("Registered but no server peer chosen..."); - } - ServerStatus::NotConnected => { - ui.label("No connection.."); - } - ServerStatus::ConnectedHandshake => { - let str = format!("📡"); - ui.label(str); - } + ServerStatus::Loading => ui.spinner(), + ServerStatus::Connected => ui.label("Registered but no server peer chosen..."), + ServerStatus::NotConnected => ui.label("No connection.."), + ServerStatus::ConnectedHandshake => ui.label("📡"), + }; + + ui.add_space(8.0); // small gap + + // desired progress bar width + let bar_width = 220.0f32; + // push it to the right by adding space equal to remaining width minus bar width + let push = (ui.available_width() - bar_width).max(0.0); + ui.add_space(push); + + if let Some(total) = self.current_total_chunks { + let received = self.current_received_chunks; + let frac = if total == 0 { + 1.0 + } else { + received as f32 / total as f32 + }; + + ui.add( + ProgressBar::new(frac) + .show_percentage() + .animate(true) + .desired_height(10.0), + ); } - ui.add_space(ui.available_width() - 30.0); - // formater mm:ss - let secs = self.remaining.as_secs(); - let minutes = secs / 60; - let seconds = secs % 60; - ui.label(format!("{:02}:{:02}", minutes, seconds)); }); }); @@ -457,93 +552,81 @@ impl eframe::App for P2PClientApp { }); ui.separator(); - ScrollArea::vertical().show(ui, |ui| { - if self.known_peers.is_empty() { - ui.add_space(10.0); - ui.label("No active peers."); - } else { - for peer in &self.known_peers { - let is_active = - self.active_peer.as_ref().map_or(false, |id| id == &peer.0); // if peer.id == self.active_peer_id + ScrollArea::vertical() + .auto_shrink([false; 2]) + .show(ui, |ui| { + ui.style_mut().visuals.widgets.inactive.bg_fill = + ui.style().visuals.widgets.inactive.bg_fill; // no-op to get mutable borrow + if self.known_peers.is_empty() { + ui.add_space(10.0); + ui.label("No active peers."); + } else { + for peer in &self.known_peers { + let is_active = + self.active_peer.as_ref().map_or(false, |id| id == &peer.0); // if peer.id == self.active_peer_id + // + if peer.0.eq(&self.connect_name_input) { + ui.horizontal(|ui| { + let resp = ui.selectable_label( + is_active, + format!("{} (you)", peer.0), + ); - let selectable: Response; - // if &self.active_server == &peer.0 { - // // Create a frame with green background and render the selectable inside it. - // // Adjust rounding, padding and stroke as desired. - // let frame = Frame { - // fill: Color32::DARK_BLUE, - // stroke: Stroke::default(), - // corner_radius: CornerRadius::from(0.5), - // ..Default::default() - // }; - // let internal = frame.show(ui, |ui| { - // // horizontal row: label on the left, spinner on the right - // ui.horizontal(|ui| { - // // let selectable label take remaining space - // ui.with_layout(Layout::left_to_right(Align::Center), |ui| { - // ui.add_space(0.0); // ensure layout established - // return ui.selectable_label( - // is_active, - // format!("{}", peer.0), - // ); - // }) - // .inner // return Response from the inner closure if your egui version does so - // }) - // }); - // selectable = internal.inner.inner; - // } else { - // selectable = ui.selectable_label(is_active, format!("{}", peer.0)); - // } - - // place spinner to the right of the label - ui.horizontal(|ui| { - // Use same width for the label widget as the selectable we already created: - // Recreate selectable inline so both label and spinner share the same row. - let resp = if &self.active_server == &peer.0 { - // draw with frame inline - let frame = Frame { - fill: Color32::DARK_BLUE, - stroke: Stroke::default(), - corner_radius: CornerRadius::from(0.5), - ..Default::default() - }; - frame - .show(ui, |ui| { - ui.selectable_label(is_active, format!("{}", peer.0)) - }) - .inner - } else { - ui.selectable_label(is_active, format!("{}", peer.0)) - }; - - ui.add_space(4.0); // small gap - - if self.loading_peers.contains(&peer.0) { - // push spinner to right by expanding a spacer before it - ui.with_layout(Layout::right_to_left(Align::Center), |ui| { - ui.spinner(); + if resp.clicked() { + self.active_peer = Some(peer.0.clone()); + } }); - } + } else { + // place spinner to the right of the label + ui.horizontal(|ui| { + let resp = if &self.active_server == &peer.0 { + let frame = Frame { + fill: Color32::DARK_BLUE, + stroke: Stroke::default(), + corner_radius: CornerRadius::from(0.5), + ..Default::default() + }; + frame + .show(ui, |ui| { + ui.selectable_label( + is_active, + format!("{}", peer.0), + ) + }) + .inner + } else { + ui.selectable_label(is_active, format!("{}", peer.0)) + }; - // use resp (click handling etc.) - if resp.clicked() { - // switch to displaying this peer's tree - self.active_peer = Some(peer.0.clone()); - // Request root content if not loaded - if !self - .loaded_fs - .contains_key(self.active_peer.as_ref().unwrap()) - { - //todo!(); - let _ = self.network_cmd_tx.send(NetworkCommand::Discover( - peer.0.clone(), - "root".to_string(), - self.connected_address.clone(), - )); - } - } - resp.context_menu(|ui| { - // ... action + ui.add_space(4.0); // small gap + + if self.loading_peers.contains(&peer.0) { + ui.with_layout( + Layout::right_to_left(Align::Center), + |ui| { + ui.spinner(); + }, + ); + } + + if resp.clicked() { + self.active_peer = Some(peer.0.clone()); + // Request root content if not loaded + if !self + .loaded_fs + .contains_key(self.active_peer.as_ref().unwrap()) + { + //todo!(); + let _ = self.network_cmd_tx.send( + NetworkCommand::Discover( + peer.0.clone(), + "root".to_string(), + self.connected_address.clone(), + ), + ); + } + } + resp.context_menu(|ui| { match self.server_status { ServerStatus::Connected => { if ui @@ -551,12 +634,17 @@ impl eframe::App for P2PClientApp { .clicked() { self.active_server = peer.0.to_string(); - let res = self.network_cmd_tx.send( + match self.network_cmd_tx.send( NetworkCommand::ServerHandshake( peer.0.to_string(), self.connected_address.clone(), ), - ); + ) { + Ok(_) => {} + Err(e) => { + println!("GUI Error : {}", e.to_string()); + }, + }; } } _ => {} @@ -593,78 +681,11 @@ impl eframe::App for P2PClientApp { // ... autres boutons }); - }); - - // if self.loading_peers.contains(&peer.0) { - // ui.spinner(); - // } - - //if selectable.clicked() { - // switch to displaying this peer's tree - //self.active_peer = Some(peer.0.clone()); - //// Request root content if not loaded - //if !self - // .loaded_fs - // .contains_key(self.active_peer.as_ref().unwrap()) - //{ - // //todo!(); - // let _ = self.network_cmd_tx.send(NetworkCommand::Discover( - // peer.0.clone(), - // "root".to_string(), - // self.connected_address.clone(), - // )); - //} - //} - //selectable.context_menu(|ui| { - // // ... action - // match self.server_status { - // ServerStatus::Connected => { - // if ui - // .button("Utiliser le peer en tant que serveur") - // .clicked() - // { - // self.active_server = peer.0.to_string(); - // let res = self.network_cmd_tx.send( - // NetworkCommand::ServerHandshake( - // peer.0.to_string(), - // self.connected_address.clone(), - // ), - // ); - // } - // } - // _ => {} - // } - // if ui.button("Send Ping").clicked() { - // let res = self.network_cmd_tx.send(NetworkCommand::Ping( - // peer.0.to_string(), - // self.connected_address.clone(), - // )); - - // self.loading_peers.push(peer.0.to_owned()); - // } - // if ui.button("Send Nat Traversal Request").clicked() { - // match self.network_cmd_tx.send(NetworkCommand::NatTraversal( - // peer.0.to_string(), - // self.connected_address.clone(), - // )) { - // Ok(_) => { - // print!("[+] successfully sent nat traversal request") - // } - // Err(_) => { - // print!("[-] failed to send nat traversal request") - // } - // } - // } - // if ui.button("Infos").clicked() { - // // action 3 - // ui.close(); - // } - - // // ... autres boutons - //}); + }); + } + } } - } - }); + }); }); CentralPanel::default().show(ctx, |ui| { @@ -678,13 +699,17 @@ impl eframe::App for P2PClientApp { ui.separator(); if let Some(active_peer) = &self.active_peer { - if let Some(tree) = self.loaded_fs.get(active_peer) { + if let Some(tree) = self.loaded_fs.clone().get(active_peer) { ScrollArea::vertical().show(ui, |ui| { // Start drawing the tree from the root hash self.draw_file_tree(ui, tree); }); } else { - ui.label(format!("Loading root for peer: {}", active_peer)); + ui.horizontal(|ui| { + ui.label(format!("Loading root for peer: {}", active_peer)); + ui.add_space(8.0); + ui.spinner(); // or conditional: if is_loading { ui.spinner(); } + }); } } else { ui.label("Connect to a peer to view a file tree."); @@ -734,7 +759,7 @@ impl eframe::App for P2PClientApp { // --- Helper for Drawing the Recursive File Tree --- impl P2PClientApp { - fn draw_file_tree(&self, ui: &mut Ui, tree: &MerkleTree) { + fn draw_file_tree(&mut self, ui: &mut Ui, tree: &MerkleTree) { assert!(self.active_peer.is_some()); assert!( self.loaded_fs @@ -751,7 +776,7 @@ impl P2PClientApp { } fn draw_file_node( - &self, + &mut self, ui: &mut Ui, to_draw: NodeHash, tree: &MerkleTree, diff --git a/client-gui/src/main.rs b/client-gui/src/main.rs index ed372a1..5384f17 100644 --- a/client-gui/src/main.rs +++ b/client-gui/src/main.rs @@ -32,7 +32,7 @@ async fn main() -> eframe::Result<()> { eframe::run_native( "p2p-merkle client", options, - Box::new(|cc| { + Box::new(|_| { let app = P2PClientApp::new(network_cmd_tx, network_event_rx); Ok(Box::new(app)) }), diff --git a/client-network/src/lib.rs b/client-network/src/lib.rs index 215fd4c..6cf1057 100644 --- a/client-network/src/lib.rs +++ b/client-network/src/lib.rs @@ -196,7 +196,6 @@ pub enum NetworkEvent { Disconnected(), Error(String, String), Success(String, String), - PeerConnected(String), PeerListUpdated(Vec<(String, bool)>), FileTreeReceived([u8; 32], MerkleNode, String), // peer_id, content DataReceived([u8; 32], MerkleNode, String), @@ -340,9 +339,53 @@ pub fn start_p2p_executor( sd.messages_list(), sd.handshake_peers.get_username_peerinfo_map(), ); + let server_address = { + match get_server_address(username.to_owned(), ip.to_owned()).await { + Some(addr) => addr.to_string(), + None => { + match event_tx.send(NetworkEvent::Error( + "Couldn't fetch server socket address.".to_owned(), + username.to_owned(), + )) { + Ok(_) => {} + Err(e) => { + println!("Network Event Error : {}", e.to_string()); + } + } + "".to_owned() + } + } + }; + if server_address.to_owned().eq(&"".to_owned()) { + continue; + } - let res = - perform_handshake(&sd, username, ip, event_tx.clone(), true).await; + sd.set_servername(username.to_owned()); + sd.set_serveraddress(server_address.to_string()); + println!("SET SERVERADDRESS"); + + match perform_handshake( + &sd, + username.to_owned(), + ip, + event_tx.clone(), + (true, server_address.to_string()), + ) + .await + { + true => { + match event_tx.send(NetworkEvent::Success( + "Handshake established ✔️".to_string(), + username.to_owned(), + )) { + Ok(_) => {} + Err(err) => { + println!("Network Event Error : {}", err.to_string()); + } + }; + } + false => {} + }; } else { println!("no shared data"); } @@ -360,7 +403,9 @@ pub fn start_p2p_executor( NetworkCommand::Discover(username, hash, ip) => { // envoie un handshake au peer, puis un root request if let Some(sd) = shared_data.as_ref() { - let res = sd.handshake_peers.get_peer_info_username(username.clone()); + let res = sd + .handshake_peers + .get_peer_info_username(username.to_owned()); match res { Some(peerinfo) => { let id = generate_id(); @@ -395,14 +440,31 @@ pub fn start_p2p_executor( } None => { // envoyer un handshake - let res = perform_handshake( + match perform_handshake( &sd, - username, + username.to_owned(), ip, event_tx.clone(), - false, + (false, "".to_string()), ) - .await; + .await + { + true => { + match event_tx.send(NetworkEvent::Success( + "Handshake established ✔️".to_string(), + username.to_owned(), + )) { + Ok(_) => {} + Err(err) => { + println!( + "Network Event Error : {}", + err.to_string() + ); + } + }; + } + false => {} + } } } } else { @@ -514,14 +576,21 @@ pub fn start_p2p_executor( current.push(i); } } - let res = - event_tx.send(NetworkEvent::PeerListUpdated(peers)); + match event_tx.send(NetworkEvent::PeerListUpdated(peers)) { + Ok(_) => {} + Err(err) => { + println!( + "Network Event Error : {}", + err.to_string() + ); + } + }; } Err(e) => { eprintln!("invalid UTF-8 in socket address bytes: {}", e); } }, - Err(e) => println!("error"), + Err(e) => println!("error : {}", e), } } } @@ -533,25 +602,10 @@ pub fn start_p2p_executor( if let Some(sd) = shared_data.as_ref() { let id = generate_id(); sd.add_message(id, EventType::Ping); - let pingrequest = - construct_message(PING, Vec::new(), id, sd.cryptopair_ref()); let peer_address = get_socket_address(str.to_owned(), ip, shared_data.as_ref()).await; match peer_address { Ok(addr) => { - //if let Some(ping) = pingrequest { - // sd.senders_ref().add_message_to_retry_queue( - // ping.clone(), - // addr.to_string(), - // false, - // ); - // sd.senders_ref().send_dispatch( - // ping, - // addr.to_string(), - // false, - // sd.messages_list(), - // ); - //} match event_tx.send(NetworkEvent::Success( format!( "Successfully sent ping message to {}.", @@ -756,7 +810,16 @@ pub async fn get_socket_address( } }; - let addresses = parse_addresses(&s); // assumes parse_addresses: &str -> Vec + let addresses: Vec = { + let temp = parse_addresses(&s); + temp.iter() + .filter_map(|a| match a { + SocketAddr::V4(_) => Some(*a), + SocketAddr::V6(_) => None, + }) + .collect() + }; + if addresses.is_empty() { return Err(FetchSocketAddressError::NoRegisteredAddresses); } else if !addresses.iter().any(|a| matches!(a, SocketAddr::V4(_))) { diff --git a/client-network/src/message_handling.rs b/client-network/src/message_handling.rs index 2ba2fb3..3365e0a 100644 --- a/client-network/src/message_handling.rs +++ b/client-network/src/message_handling.rs @@ -314,7 +314,7 @@ pub fn parse_message( ERROR => { if let Ok(err_received) = - String::from_utf8(received_message[LENGTH..(msg_length + LENGTH + 4)].to_vec()) + String::from_utf8(received_message[LENGTH..(msg_length + LENGTH)].to_vec()) { let err_msg = format!("Error received from peer {} : {}", ip, err_received); let _ = cmd_tx_clone.send(NetworkEvent::Error(err_msg, "".to_owned())); diff --git a/client-network/src/registration.rs b/client-network/src/registration.rs index 5f24264..12fb34f 100644 --- a/client-network/src/registration.rs +++ b/client-network/src/registration.rs @@ -2,6 +2,7 @@ use crate::NetworkEvent; use crate::P2PSharedData; use crate::cryptographic_signature::CryptographicSignature; use crate::get_server_address; +use crate::get_socket_address; use crate::message_handling::EventType; use crate::messages_structure::construct_message; use crate::server_communication::generate_id; @@ -50,53 +51,77 @@ pub fn parse_addresses(input: &String) -> Vec { /// /// registers the IP addresses by sending a Hello request to the server. /// + pub async fn perform_handshake( sd: &P2PSharedData, username: String, ip: String, event_tx: Sender, - is_server_handshake: bool, -) { + is_server_handshake: (bool, String), +) -> bool { println!("username: {}, ip: {}", username.clone(), ip.clone()); let crypto_pair = sd.cryptopair_ref(); let senders = sd.senders_ref(); let id = generate_id(); - let server_addr_query = get_server_address(username.clone(), ip.clone()); - match server_addr_query.await { - Some(sockaddr_bytes) => { - sd.set_servername(username); - // first: &SocketAddr - let mut payload = Vec::new(); - payload.extend_from_slice(&0u32.to_be_bytes()); - payload.extend_from_slice(&crypto_pair.username.clone().as_bytes()); - let hello_handshake = construct_message(1, payload, id, crypto_pair); - if is_server_handshake { - sd.add_message(id, EventType::Hello); - sd.set_serveraddress(sockaddr_bytes.to_string()); - } else { - println!("hello the nroot"); - sd.add_message(id, EventType::HelloThenRootRequest); - } - match hello_handshake { - Some(handshake_message) => { - senders.send_dispatch( - handshake_message, - sockaddr_bytes.to_string(), - is_server_handshake, - sd.messages_list(), - ); + let address = { + if is_server_handshake.0 { + is_server_handshake.1 + } else { + let server_addr_query = + get_socket_address(username.clone(), ip.clone(), Some(sd)).await; + + match server_addr_query { + Ok(sockaddr_bytes) => sockaddr_bytes.to_string(), + Err(err_msg) => { + match event_tx.send(NetworkEvent::Error( + err_msg.to_string(), + username.to_owned(), + )) { + Ok(_) => {} + Err(err) => { + println!("Network Event Error : {}", err.to_string()); + } + } + "".to_string() } - None => {} } } - None => { - let err_msg = format!("failed to retreive socket address:").to_string(); - let res = event_tx.send(NetworkEvent::Error(err_msg, "".to_owned())); - } + }; + + if address.eq(&"".to_string()) { + return false; } + let mut payload = Vec::new(); + payload.extend_from_slice(&0u32.to_be_bytes()); + payload.extend_from_slice(&crypto_pair.username.clone().as_bytes()); + let hello_handshake = construct_message(1, payload, id, crypto_pair); + if is_server_handshake.0 { + sd.add_message(id, EventType::Hello); + } else { + sd.add_message(id, EventType::HelloThenRootRequest); + } + + match hello_handshake { + Some(handshake_message) => { + senders.send_dispatch( + handshake_message, + address, + is_server_handshake.0, + sd.messages_list(), + ); + } + None => {} + } + + //let server_addr_query = get_socket_address(username.clone(), ip.clone(), Some(sd)).await; + //match server_addr_query { + // Ok(sockaddr_bytes) => {} + // Err(err_msg) => {} + //} + /*let mut list = messages_list.lock().expect("Failed to lock messages_list"); match list.get(&id) { Some(_) => { @@ -113,37 +138,5 @@ pub async fn perform_handshake( let hello_handshake_received = UDPMessage::parse(buf.to_vec()); hello_handshake_received.display();*/ //TODO -} - -#[cfg(test)] -mod tests { - - /*/// - /// does the procedure to register with the server - /// - #[tokio::test] - async fn registering_with_server() { - let username = String::from("gameixtreize"); - let server_uri = String::from("https://jch.irif.fr:8443"); - let crypto_pair = CryptographicSignature::new(username); - if let Err(e) = register_with_the_server(crypto_pair, server_uri).await { - eprintln!("Error during registration: {}", e); - } - }*/ - - /*/// - /// retreives the socket address of a given peer - /// - #[tokio::test] - async fn retreive_socket_addr() { - let username = String::from("ipjkndqfshjldfsjlbsdfjhhj"); - match get_socket_address(username).await { - Ok(body) => { - println!("{:?}", body); - } - Err(e) => { - eprintln!("Erreur HTTP: {}", e); - } - } - }*/ + return true; } diff --git a/rapport.txt b/rapport.txt new file mode 100644 index 0000000..d2c1184 --- /dev/null +++ b/rapport.txt @@ -0,0 +1 @@ +https://docs.google.com/document/d/1emhrAfjJyJTWpBYx4IJGcCz0_iLVjDRAAdq2EZFchKo/edit?usp=sharing