server handshake handling
This commit is contained in:
@@ -7,7 +7,7 @@ use egui::{
|
|||||||
Align, Align2, Button, CentralPanel, CollapsingHeader, Context, Id, LayerId, Layout, Order,
|
Align, Align2, Button, CentralPanel, CollapsingHeader, Context, Id, LayerId, Layout, Order,
|
||||||
Popup, ScrollArea, SidePanel, TextStyle, TopBottomPanel, Ui, ViewportCommand,
|
Popup, ScrollArea, SidePanel, TextStyle, TopBottomPanel, Ui, ViewportCommand,
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, fmt::format};
|
||||||
|
|
||||||
enum ServerStatus {
|
enum ServerStatus {
|
||||||
Loading,
|
Loading,
|
||||||
@@ -43,6 +43,8 @@ pub struct P2PClientApp {
|
|||||||
show_network_popup: bool, // gérer selon besoin
|
show_network_popup: bool, // gérer selon besoin
|
||||||
|
|
||||||
error_message: Option<String>, // Some(message) -> afficher, None -> rien
|
error_message: Option<String>, // Some(message) -> afficher, None -> rien
|
||||||
|
//
|
||||||
|
active_server: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl P2PClientApp {
|
impl P2PClientApp {
|
||||||
@@ -69,6 +71,7 @@ impl P2PClientApp {
|
|||||||
show_network_popup: false,
|
show_network_popup: false,
|
||||||
error_message: None,
|
error_message: None,
|
||||||
connect_name_input: "bob".to_string(),
|
connect_name_input: "bob".to_string(),
|
||||||
|
active_server: "".to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn show_error(&mut self, msg: impl Into<String>) {
|
pub fn show_error(&mut self, msg: impl Into<String>) {
|
||||||
@@ -149,6 +152,7 @@ impl eframe::App for P2PClientApp {
|
|||||||
self.server_status = ServerStatus::ConnectedHandshake;
|
self.server_status = ServerStatus::ConnectedHandshake;
|
||||||
}
|
}
|
||||||
NetworkEvent::Disconnected() => {
|
NetworkEvent::Disconnected() => {
|
||||||
|
self.active_server = "".to_string();
|
||||||
self.connected_address = "".to_string();
|
self.connected_address = "".to_string();
|
||||||
self.known_peers.clear();
|
self.known_peers.clear();
|
||||||
self.server_status = ServerStatus::NotConnected;
|
self.server_status = ServerStatus::NotConnected;
|
||||||
@@ -157,6 +161,14 @@ impl eframe::App for P2PClientApp {
|
|||||||
self.show_error(err);
|
self.show_error(err);
|
||||||
}
|
}
|
||||||
NetworkEvent::DataReceived(_, merkle_node) => todo!(),
|
NetworkEvent::DataReceived(_, merkle_node) => todo!(),
|
||||||
|
NetworkEvent::HandshakeFailed() => {}
|
||||||
|
NetworkEvent::ServerHandshakeFailed(err) => {
|
||||||
|
self.active_server = "".to_string();
|
||||||
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,7 +301,8 @@ impl eframe::App for P2PClientApp {
|
|||||||
ui.label("No connection..");
|
ui.label("No connection..");
|
||||||
}
|
}
|
||||||
ServerStatus::ConnectedHandshake => {
|
ServerStatus::ConnectedHandshake => {
|
||||||
ui.label("📡");
|
let str = format!("📡 {}", self.active_server);
|
||||||
|
ui.label(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui.add_space(ui.available_width() - 30.0);
|
ui.add_space(ui.available_width() - 30.0);
|
||||||
@@ -355,6 +368,7 @@ impl eframe::App for P2PClientApp {
|
|||||||
.button("Utiliser le peer en tant que serveur")
|
.button("Utiliser le peer en tant que serveur")
|
||||||
.clicked()
|
.clicked()
|
||||||
{
|
{
|
||||||
|
self.active_server = peer.to_string();
|
||||||
let res = self.network_cmd_tx.send(
|
let res = self.network_cmd_tx.send(
|
||||||
NetworkCommand::ServerHandshake(
|
NetworkCommand::ServerHandshake(
|
||||||
peer.to_string(),
|
peer.to_string(),
|
||||||
|
|||||||
@@ -30,10 +30,14 @@ pub struct P2PSharedData {
|
|||||||
shared_cryptopair: Arc<CryptographicSignature>,
|
shared_cryptopair: Arc<CryptographicSignature>,
|
||||||
shared_messageslist: Arc<Mutex<HashMap<i32, EventType>>>,
|
shared_messageslist: Arc<Mutex<HashMap<i32, EventType>>>,
|
||||||
shared_senders: Arc<MultipleSenders>,
|
shared_senders: Arc<MultipleSenders>,
|
||||||
|
server_name: Arc<Mutex<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl P2PSharedData {
|
impl P2PSharedData {
|
||||||
pub fn new(username: String) -> Result<P2PSharedData, Error> {
|
pub fn new(
|
||||||
|
username: String,
|
||||||
|
cmd_tx: crossbeam_channel::Sender<NetworkEvent>,
|
||||||
|
) -> Result<P2PSharedData, Error> {
|
||||||
let messages_list = HashMap::<i32, EventType>::new();
|
let messages_list = HashMap::<i32, EventType>::new();
|
||||||
let username = String::from(username);
|
let username = String::from(username);
|
||||||
let crypto_pair = CryptographicSignature::new(username);
|
let crypto_pair = CryptographicSignature::new(username);
|
||||||
@@ -42,13 +46,15 @@ impl P2PSharedData {
|
|||||||
let shared_cryptopair = Arc::new(crypto_pair);
|
let shared_cryptopair = Arc::new(crypto_pair);
|
||||||
let shared_messageslist = Arc::new(Mutex::new(messages_list));
|
let shared_messageslist = Arc::new(Mutex::new(messages_list));
|
||||||
|
|
||||||
let senders = MultipleSenders::new(1, &shared_socket);
|
let senders = MultipleSenders::new(1, &shared_socket, cmd_tx);
|
||||||
let shared_senders = Arc::new(senders);
|
let shared_senders = Arc::new(senders);
|
||||||
|
let server_name = Arc::new(Mutex::new("".to_string()));
|
||||||
Ok(P2PSharedData {
|
Ok(P2PSharedData {
|
||||||
shared_socket: shared_socket,
|
shared_socket: shared_socket,
|
||||||
shared_cryptopair: shared_cryptopair,
|
shared_cryptopair: shared_cryptopair,
|
||||||
shared_messageslist: shared_messageslist,
|
shared_messageslist: shared_messageslist,
|
||||||
shared_senders: shared_senders,
|
shared_senders: shared_senders,
|
||||||
|
server_name: server_name,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub fn socket(&self) -> Arc<UdpSocket> {
|
pub fn socket(&self) -> Arc<UdpSocket> {
|
||||||
@@ -61,6 +67,14 @@ impl P2PSharedData {
|
|||||||
pub fn messages_list(&self) -> Arc<Mutex<HashMap<i32, EventType>>> {
|
pub fn messages_list(&self) -> Arc<Mutex<HashMap<i32, EventType>>> {
|
||||||
self.shared_messageslist.clone()
|
self.shared_messageslist.clone()
|
||||||
}
|
}
|
||||||
|
pub fn servername(&self) -> String {
|
||||||
|
let guard = self.server_name.lock().unwrap();
|
||||||
|
guard.to_string()
|
||||||
|
}
|
||||||
|
pub fn set_servername(&self, new: String) {
|
||||||
|
let mut guard = self.server_name.lock().unwrap();
|
||||||
|
*guard = new
|
||||||
|
}
|
||||||
pub fn senders(&self) -> Arc<MultipleSenders> {
|
pub fn senders(&self) -> Arc<MultipleSenders> {
|
||||||
self.shared_senders.clone()
|
self.shared_senders.clone()
|
||||||
}
|
}
|
||||||
@@ -98,6 +112,7 @@ pub enum NetworkCommand {
|
|||||||
RequestDirectoryContent(String, String),
|
RequestDirectoryContent(String, String),
|
||||||
RequestChunk(String, String),
|
RequestChunk(String, String),
|
||||||
Disconnect(),
|
Disconnect(),
|
||||||
|
ResetServerPeer(),
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +127,8 @@ pub enum NetworkEvent {
|
|||||||
FileTreeReceived(String, Vec<MerkleNode>), // peer_id, content
|
FileTreeReceived(String, Vec<MerkleNode>), // peer_id, content
|
||||||
DataReceived(String, MerkleNode),
|
DataReceived(String, MerkleNode),
|
||||||
FileTreeRootReceived(String, String),
|
FileTreeRootReceived(String, String),
|
||||||
|
HandshakeFailed(),
|
||||||
|
ServerHandshakeFailed(String),
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,7 +172,7 @@ pub fn start_p2p_executor(
|
|||||||
NetworkCommand::ServerHandshake(username, ip) => {
|
NetworkCommand::ServerHandshake(username, ip) => {
|
||||||
if let Some(sd) = shared_data.as_ref() {
|
if let Some(sd) = shared_data.as_ref() {
|
||||||
println!("username:{}, ip:{}", username, ip);
|
println!("username:{}, ip:{}", username, ip);
|
||||||
let server_addr_query = get_socket_address(username, ip);
|
let server_addr_query = get_socket_address(username.clone(), ip);
|
||||||
|
|
||||||
match server_addr_query.await {
|
match server_addr_query.await {
|
||||||
Ok(sockaddr_bytes) => {
|
Ok(sockaddr_bytes) => {
|
||||||
@@ -163,6 +180,7 @@ pub fn start_p2p_executor(
|
|||||||
Ok(s) => {
|
Ok(s) => {
|
||||||
let addresses = parse_addresses(&s);
|
let addresses = parse_addresses(&s);
|
||||||
if let Some(first) = addresses.first() {
|
if let Some(first) = addresses.first() {
|
||||||
|
sd.set_servername(username);
|
||||||
// first: &SocketAddr
|
// first: &SocketAddr
|
||||||
start_receving_thread(
|
start_receving_thread(
|
||||||
sd,
|
sd,
|
||||||
@@ -230,7 +248,7 @@ pub fn start_p2p_executor(
|
|||||||
|
|
||||||
// Actual server connection
|
// Actual server connection
|
||||||
|
|
||||||
shared_data = match P2PSharedData::new(name.clone()) {
|
shared_data = match P2PSharedData::new(name.clone(), event_tx.clone()) {
|
||||||
Ok(sd) => Some(sd),
|
Ok(sd) => Some(sd),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
let mut err_msg = String::from("failed to initialize socket: ");
|
let mut err_msg = String::from("failed to initialize socket: ");
|
||||||
@@ -311,6 +329,13 @@ pub fn start_p2p_executor(
|
|||||||
println!("no p2p data");
|
println!("no p2p data");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
NetworkCommand::ResetServerPeer() => {
|
||||||
|
if let Some(sd) = shared_data.as_ref() {
|
||||||
|
sd.set_servername("".to_string());
|
||||||
|
} else {
|
||||||
|
println!("no p2p data");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
|
NetworkEvent,
|
||||||
cryptographic_signature::{
|
cryptographic_signature::{
|
||||||
CryptographicSignature, get_peer_key, sign_message, verify_signature,
|
CryptographicSignature, get_peer_key, sign_message, verify_signature,
|
||||||
},
|
},
|
||||||
@@ -45,6 +46,7 @@ pub fn handle_recevied_message(
|
|||||||
crypto_pair: &CryptographicSignature,
|
crypto_pair: &CryptographicSignature,
|
||||||
socket_addr: &SocketAddr,
|
socket_addr: &SocketAddr,
|
||||||
senders: &MultipleSenders,
|
senders: &MultipleSenders,
|
||||||
|
server_name: &String,
|
||||||
) {
|
) {
|
||||||
if recevied_message.len() < 4 {
|
if recevied_message.len() < 4 {
|
||||||
return;
|
return;
|
||||||
@@ -73,6 +75,7 @@ pub fn handle_recevied_message(
|
|||||||
|
|
||||||
// Handle handshake
|
// Handle handshake
|
||||||
if message_type == 1 {
|
if message_type == 1 {
|
||||||
|
let mut resp_to_serv = false;
|
||||||
println!("verify the signature");
|
println!("verify the signature");
|
||||||
let parsed_received_message = HandshakeMessage::parse(recevied_message.to_vec());
|
let parsed_received_message = HandshakeMessage::parse(recevied_message.to_vec());
|
||||||
let received_name = String::from_utf8(parsed_received_message.name).expect("error");
|
let received_name = String::from_utf8(parsed_received_message.name).expect("error");
|
||||||
@@ -81,12 +84,17 @@ pub fn handle_recevied_message(
|
|||||||
.block_on(get_peer_key(&received_name))
|
.block_on(get_peer_key(&received_name))
|
||||||
.expect("failed to retrieve public key");
|
.expect("failed to retrieve public key");
|
||||||
|
|
||||||
|
if received_name == server_name.to_string() {
|
||||||
|
resp_to_serv = true;
|
||||||
|
}
|
||||||
|
|
||||||
if !verify_signature(peer_pubkey, recevied_message) {
|
if !verify_signature(peer_pubkey, recevied_message) {
|
||||||
println!(
|
println!(
|
||||||
"incorrect signature from given peer: {}, ignoring message {}",
|
"incorrect signature from given peer: {}, ignoring message {}",
|
||||||
&received_name, id
|
&received_name, id
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
// verify if this is a server handshake request
|
||||||
let username_size = crypto_pair.username.len();
|
let username_size = crypto_pair.username.len();
|
||||||
let hello_handshake = HandshakeMessage::helloReply(
|
let hello_handshake = HandshakeMessage::helloReply(
|
||||||
id as u32,
|
id as u32,
|
||||||
@@ -96,7 +104,7 @@ pub fn handle_recevied_message(
|
|||||||
//HandshakeMessage::display(&hello_handshake);
|
//HandshakeMessage::display(&hello_handshake);
|
||||||
let hello_handshake_serialized = hello_handshake.serialize();
|
let hello_handshake_serialized = hello_handshake.serialize();
|
||||||
let message_signed = sign_message(crypto_pair, &hello_handshake_serialized);
|
let message_signed = sign_message(crypto_pair, &hello_handshake_serialized);
|
||||||
senders.send_via(0, message_signed, socket_addr.to_string());
|
senders.send_via(0, message_signed, socket_addr.to_string(), resp_to_serv);
|
||||||
let mut list = messages_list.lock().expect("Failed to lock messages_list");
|
let mut list = messages_list.lock().expect("Failed to lock messages_list");
|
||||||
match list.get(&id) {
|
match list.get(&id) {
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
|
|||||||
@@ -13,13 +13,17 @@ use std::thread;
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
use crate::NetworkEvent;
|
||||||
|
|
||||||
pub struct MultipleSenders {
|
pub struct MultipleSenders {
|
||||||
senders: Vec<Sender<Message>>,
|
senders: Vec<Sender<Message>>,
|
||||||
|
response_channel: crossbeam_channel::Sender<NetworkEvent>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
payload: Vec<u8>,
|
payload: Vec<u8>,
|
||||||
address: String,
|
address: String,
|
||||||
|
is_resp_to_server_handshake: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RetryMessage {
|
struct RetryMessage {
|
||||||
@@ -68,12 +72,17 @@ impl MultipleSenders {
|
|||||||
MultipleSenders { senders }
|
MultipleSenders { senders }
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
pub fn new(num_channels: usize, socket: &Arc<UdpSocket>) -> Self {
|
pub fn new(
|
||||||
|
num_channels: usize,
|
||||||
|
socket: &Arc<UdpSocket>,
|
||||||
|
cmd_tx: crossbeam_channel::Sender<NetworkEvent>,
|
||||||
|
) -> Self {
|
||||||
let mut senders = Vec::new();
|
let mut senders = Vec::new();
|
||||||
|
|
||||||
for i in 0..num_channels {
|
for i in 0..num_channels {
|
||||||
let (tx, rx) = mpsc::channel::<Message>();
|
let (tx, rx) = mpsc::channel::<Message>();
|
||||||
let sock_clone = Arc::clone(&socket);
|
let sock_clone = Arc::clone(&socket);
|
||||||
|
let cmd_tx_clone = cmd_tx.clone();
|
||||||
|
|
||||||
senders.push(tx);
|
senders.push(tx);
|
||||||
|
|
||||||
@@ -91,6 +100,10 @@ impl MultipleSenders {
|
|||||||
let mut item = queue.pop_front().unwrap();
|
let mut item = queue.pop_front().unwrap();
|
||||||
match sock_clone.send_to(&item.msg.payload, &item.msg.address) {
|
match sock_clone.send_to(&item.msg.payload, &item.msg.address) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
if (&item).msg.is_resp_to_server_handshake {
|
||||||
|
let res =
|
||||||
|
cmd_tx_clone.send(NetworkEvent::ConnectedHandshake());
|
||||||
|
}
|
||||||
let message_id: [u8; 4] =
|
let message_id: [u8; 4] =
|
||||||
item.msg.payload[0..4].try_into().expect("size error");
|
item.msg.payload[0..4].try_into().expect("size error");
|
||||||
let id = i32::from_be_bytes(message_id);
|
let id = i32::from_be_bytes(message_id);
|
||||||
@@ -103,10 +116,14 @@ impl MultipleSenders {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
item.attempts += 1;
|
item.attempts += 1;
|
||||||
if item.attempts >= max_attempts {
|
if item.attempts >= max_attempts {
|
||||||
eprintln!(
|
let str = format!(
|
||||||
"Abandon du message après {} tentatives sur canal {}: {}, address: {}",
|
"Abandon du message après {} tentatives sur canal {}: {}, address: {}",
|
||||||
item.attempts, i, e, item.msg.address
|
item.attempts, i, e, item.msg.address
|
||||||
);
|
);
|
||||||
|
if (&item).msg.is_resp_to_server_handshake {
|
||||||
|
let res = cmd_tx_clone
|
||||||
|
.send(NetworkEvent::ServerHandshakeFailed(str));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Backoff exponentiel simple
|
// Backoff exponentiel simple
|
||||||
let backoff = Duration::from_millis(
|
let backoff = Duration::from_millis(
|
||||||
@@ -131,6 +148,10 @@ impl MultipleSenders {
|
|||||||
// On tente d'envoyer immédiatement
|
// On tente d'envoyer immédiatement
|
||||||
match sock_clone.send_to(&msg.payload, &msg.address) {
|
match sock_clone.send_to(&msg.payload, &msg.address) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
if msg.is_resp_to_server_handshake {
|
||||||
|
let res =
|
||||||
|
cmd_tx_clone.send(NetworkEvent::ConnectedHandshake());
|
||||||
|
}
|
||||||
let message_id: [u8; 4] =
|
let message_id: [u8; 4] =
|
||||||
msg.payload[0..4].try_into().expect("size error");
|
msg.payload[0..4].try_into().expect("size error");
|
||||||
let id = i32::from_be_bytes(message_id);
|
let id = i32::from_be_bytes(message_id);
|
||||||
@@ -171,15 +192,29 @@ impl MultipleSenders {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
MultipleSenders { senders }
|
MultipleSenders {
|
||||||
|
senders,
|
||||||
|
response_channel: cmd_tx.clone(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Envoie un message via un canal spécifique (round-robin ou index précis)
|
/// Envoie un message via un canal spécifique (round-robin ou index précis)
|
||||||
pub fn send_via(&self, channel_idx: usize, data: Vec<u8>, remote_addr: String) {
|
pub fn send_via(
|
||||||
|
&self,
|
||||||
|
channel_idx: usize,
|
||||||
|
data: Vec<u8>,
|
||||||
|
remote_addr: String,
|
||||||
|
is_resp_to_server_handshake: bool,
|
||||||
|
) {
|
||||||
|
println!(
|
||||||
|
"is_resp_to_server_handshake {}",
|
||||||
|
is_resp_to_server_handshake
|
||||||
|
);
|
||||||
if let Some(sender) = self.senders.get(channel_idx) {
|
if let Some(sender) = self.senders.get(channel_idx) {
|
||||||
let _ = sender.send(Message {
|
let _ = sender.send(Message {
|
||||||
payload: data,
|
payload: data,
|
||||||
address: remote_addr,
|
address: remote_addr,
|
||||||
|
is_resp_to_server_handshake,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,6 +258,7 @@ pub fn start_receving_thread(shared_data: &P2PSharedData, socket_addr: SocketAdd
|
|||||||
let cryptopair_clone = shared_data.cryptopair();
|
let cryptopair_clone = shared_data.cryptopair();
|
||||||
let senders_clone = shared_data.senders();
|
let senders_clone = shared_data.senders();
|
||||||
let messages_clone = shared_data.messages_list();
|
let messages_clone = shared_data.messages_list();
|
||||||
|
let servername_clone = shared_data.servername();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut buf = [0u8; 1024];
|
let mut buf = [0u8; 1024];
|
||||||
loop {
|
loop {
|
||||||
@@ -237,6 +273,7 @@ pub fn start_receving_thread(shared_data: &P2PSharedData, socket_addr: SocketAdd
|
|||||||
&cryptopair_clone,
|
&cryptopair_clone,
|
||||||
&socket_addr,
|
&socket_addr,
|
||||||
&senders_clone,
|
&senders_clone,
|
||||||
|
&servername_clone,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Err(e) => eprintln!("Erreur de réception: {}", e),
|
Err(e) => eprintln!("Erreur de réception: {}", e),
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ pub fn register_ip_addresses(
|
|||||||
//HandshakeMessage::display(&hello_handshake);
|
//HandshakeMessage::display(&hello_handshake);
|
||||||
let hello_handshake_serialized = hello_handshake.serialize();
|
let hello_handshake_serialized = hello_handshake.serialize();
|
||||||
let message_signed = sign_message(crypto_pair, &hello_handshake_serialized);
|
let message_signed = sign_message(crypto_pair, &hello_handshake_serialized);
|
||||||
senders.send_via(0, message_signed, server_uri);
|
senders.send_via(0, message_signed, server_uri, false);
|
||||||
let mut list = messages_list.lock().expect("Failed to lock messages_list");
|
let mut list = messages_list.lock().expect("Failed to lock messages_list");
|
||||||
match list.get(&id) {
|
match list.get(&id) {
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user