From f7c1b8d88fa4786e4e63e08a2f8be3362340657a Mon Sep 17 00:00:00 2001 From: dignow Date: Mon, 31 Jul 2023 19:57:35 +0800 Subject: [PATCH 1/5] fix, win file clipboard, when authrized by the remote Signed-off-by: dignow --- src/ui_cm_interface.rs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index 9a7556d25..8316139a5 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -413,6 +413,10 @@ impl IpcTaskRunner { if stop { ContextSend::set_is_stopped(); } else { + if !self.authorized { + log::debug!("Clipboard message from client peer, but not authorized"); + continue; + } let conn_id = self.conn_id; ContextSend::proc(|context: &mut Box| -> u32 { clipboard::server_clip_file(context, conn_id, _clip) @@ -454,16 +458,25 @@ impl IpcTaskRunner { } } Some(data) = self.rx.recv() => { - if let Data::SwitchPermission{name: _name, enabled: _enabled} = &data { - #[cfg(windows)] - if _name == "file" { - self.file_transfer_enabled = *_enabled; - } - } if self.stream.send(&data).await.is_err() { break; } - } + match &data { + Data::SwitchPermission{name: _name, enabled: _enabled} => { + #[cfg(windows)] + if _name == "file" { + self.file_transfer_enabled = *_enabled; + } + } + Data::Authorize => { + self.authorized = true; + self.running = true; + break; + } + _ => { + } + } + }, clip_file = rx_clip.recv() => match clip_file { Some(_clip) => { #[cfg(windows)] From e143493d7b9ba4df08e3709f9070a935ee7123b9 Mon Sep 17 00:00:00 2001 From: dignow Date: Mon, 31 Jul 2023 20:02:01 +0800 Subject: [PATCH 2/5] simple changes Signed-off-by: dignow --- flutter/lib/models/server_model.dart | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/flutter/lib/models/server_model.dart b/flutter/lib/models/server_model.dart index 3f5fb43f9..6867aa6fe 100644 --- a/flutter/lib/models/server_model.dart +++ b/flutter/lib/models/server_model.dart @@ -410,18 +410,25 @@ class ServerModel with ChangeNotifier { updateClientState([String? json]) async { if (isTest) return; var res = await bind.cmGetClientsState(); + List clientsJson; try { - final List clientsJson = jsonDecode(res); - _clients.clear(); - tabController.state.value.tabs.clear(); - for (var clientJson in clientsJson) { + clientsJson = jsonDecode(res); + } catch (e) { + debugPrint("Failed to decode clientsJson: '$res', error $e"); + return; + } + + _clients.clear(); + tabController.state.value.tabs.clear(); + + for (var clientJson in clientsJson) { + try { final client = Client.fromJson(clientJson); _clients.add(client); _addTab(client); + } catch (e) { + debugPrint("Failed to decode clientJson '$clientJson', error $e"); } - notifyListeners(); - } catch (e) { - debugPrint("Failed to updateClientState:$e"); } } From 416cdac1fed7667e37ae375ccc064f2d0db685f3 Mon Sep 17 00:00:00 2001 From: dignow Date: Mon, 31 Jul 2023 20:33:19 +0800 Subject: [PATCH 3/5] fix, win clipboard, remove duplicated state Signed-off-by: dignow --- src/ui_cm_interface.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/ui_cm_interface.rs b/src/ui_cm_interface.rs index 8316139a5..78949b0b5 100644 --- a/src/ui_cm_interface.rs +++ b/src/ui_cm_interface.rs @@ -68,7 +68,6 @@ struct IpcTaskRunner { rx: mpsc::UnboundedReceiver, close: bool, running: bool, - authorized: bool, conn_id: i32, #[cfg(windows)] file_transfer_enabled: bool, @@ -162,6 +161,16 @@ impl ConnectionManager { self.ui_handler.add_connection(&client); } + #[inline] + fn is_authorized(&self, id: i32) -> bool { + CLIENTS + .read() + .unwrap() + .get(&id) + .map(|c| c.authorized) + .unwrap_or(false) + } + fn remove_connection(&self, id: i32, close: bool) { if close { CLIENTS.write().unwrap().remove(&id); @@ -316,13 +325,14 @@ impl IpcTaskRunner { // for tmp use, without real conn id let mut write_jobs: Vec = Vec::new(); + let is_authorized = self.cm.is_authorized(self.conn_id); #[cfg(windows)] let rx_clip1; let mut rx_clip; let _tx_clip; #[cfg(windows)] - if self.conn_id > 0 && self.authorized { + if is_authorized { rx_clip1 = clipboard::get_rx_cliprdr_server(self.conn_id); rx_clip = rx_clip1.lock().await; } else { @@ -361,7 +371,6 @@ impl IpcTaskRunner { Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled: _file_transfer_enabled, restart, recording, from_switch} => { log::debug!("conn_id: {}", id); self.cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, restart, recording, from_switch,self.tx.clone()); - self.authorized = authorized; self.conn_id = id; #[cfg(windows)] { @@ -413,7 +422,7 @@ impl IpcTaskRunner { if stop { ContextSend::set_is_stopped(); } else { - if !self.authorized { + if !is_authorized { log::debug!("Clipboard message from client peer, but not authorized"); continue; } @@ -469,7 +478,6 @@ impl IpcTaskRunner { } } Data::Authorize => { - self.authorized = true; self.running = true; break; } @@ -514,7 +522,6 @@ impl IpcTaskRunner { rx, close: true, running: true, - authorized: false, conn_id: 0, #[cfg(windows)] file_transfer_enabled: false, From 433059f8a0c0f0f37b939b8bcba518e1c9b19aa1 Mon Sep 17 00:00:00 2001 From: dignow Date: Mon, 31 Jul 2023 20:56:48 +0800 Subject: [PATCH 4/5] fix, win clipboard, send clipboard message only after first frame is received Signed-off-by: dignow --- src/client/io_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index b4e311227..bb79a2d5e 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -204,7 +204,7 @@ impl Remote { let is_stopping_allowed = clip.is_stopping_allowed(); let server_file_transfer_enabled = *self.handler.server_file_transfer_enabled.read().unwrap(); let file_transfer_enabled = self.handler.lc.read().unwrap().enable_file_transfer.v; - let stop = is_stopping_allowed && !(server_file_transfer_enabled && file_transfer_enabled); + let stop = is_stopping_allowed && (!self.first_frame || !(server_file_transfer_enabled && file_transfer_enabled)); log::debug!("Process clipboard message from system, stop: {}, is_stopping_allowed: {}, server_file_transfer_enabled: {}, file_transfer_enabled: {}", stop, is_stopping_allowed, server_file_transfer_enabled, file_transfer_enabled); if stop { ContextSend::set_is_stopped(); From 4009fd77e853f29808e1bca86482adf29c05ed37 Mon Sep 17 00:00:00 2001 From: dignow Date: Mon, 31 Jul 2023 21:40:55 +0800 Subject: [PATCH 5/5] init sync file clipboard, local to remote Signed-off-by: dignow --- libs/clipboard/src/lib.rs | 7 ++++ src/client/io_loop.rs | 76 ++++++++++++++++++++++++++----------- src/platform/mod.rs | 5 +++ src/ui_session_interface.rs | 2 +- 4 files changed, 66 insertions(+), 24 deletions(-) diff --git a/libs/clipboard/src/lib.rs b/libs/clipboard/src/lib.rs index 7a5941029..497ce54c8 100644 --- a/libs/clipboard/src/lib.rs +++ b/libs/clipboard/src/lib.rs @@ -70,6 +70,7 @@ struct MsgChannel { lazy_static::lazy_static! { static ref VEC_MSG_CHANNEL: RwLock> = Default::default(); static ref CLIENT_CONN_ID_COUNTER: Mutex = Mutex::new(0); + static ref LAST_FILE_FORMAT_LIST: Arc>> = Default::default(); } impl ClipboardFile { @@ -90,6 +91,11 @@ impl ClipboardFile { } } +#[inline] +pub fn get_last_file_format_list() -> Option { + LAST_FILE_FORMAT_LIST.lock().unwrap().clone() +} + pub fn get_client_conn_id(session_uuid: &SessionID) -> Option { VEC_MSG_CHANNEL .read() @@ -561,6 +567,7 @@ extern "C" fn client_format_list( } log::debug!("client_format_list called, client id: {}, format_list: {:?}", conn_id, &format_list); let data = ClipboardFile::FormatList { format_list }; + *LAST_FILE_FORMAT_LIST.lock().unwrap() = Some(data.clone()); // no need to handle result here if conn_id == 0 { // msg_channel is used for debug, VEC_MSG_CHANNEL cannot be inspected by the debugger. diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index bb79a2d5e..498d022f1 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -6,7 +6,9 @@ use std::sync::{ }; #[cfg(windows)] -use clipboard::{cliprdr::CliprdrClientContext, empty_clipboard, ContextSend}; +use clipboard::{ + cliprdr::CliprdrClientContext, empty_clipboard, get_last_file_format_list, ContextSend, +}; use crossbeam_queue::ArrayQueue; use hbb_common::config::{PeerConfig, TransferSerde}; use hbb_common::fs::{ @@ -56,6 +58,7 @@ pub struct Remote { remove_jobs: HashMap, timer: Interval, last_update_jobs_status: (Instant, HashMap), + is_connected: bool, first_frame: bool, #[cfg(windows)] client_conn_id: i32, // used for file clipboard @@ -90,6 +93,7 @@ impl Remote { remove_jobs: Default::default(), timer: time::interval(SEC30), last_update_jobs_status: (Instant::now(), Default::default()), + is_connected: false, first_frame: false, #[cfg(windows)] client_conn_id: 0, @@ -195,28 +199,7 @@ impl Remote { } _msg = rx_clip_client.recv() => { #[cfg(windows)] - match _msg { - Some(clip) => match clip { - clipboard::ClipboardFile::NotifyCallback{r#type, title, text} => { - self.handler.msgbox(&r#type, &title, &text, ""); - } - _ => { - let is_stopping_allowed = clip.is_stopping_allowed(); - let server_file_transfer_enabled = *self.handler.server_file_transfer_enabled.read().unwrap(); - let file_transfer_enabled = self.handler.lc.read().unwrap().enable_file_transfer.v; - let stop = is_stopping_allowed && (!self.first_frame || !(server_file_transfer_enabled && file_transfer_enabled)); - log::debug!("Process clipboard message from system, stop: {}, is_stopping_allowed: {}, server_file_transfer_enabled: {}, file_transfer_enabled: {}", stop, is_stopping_allowed, server_file_transfer_enabled, file_transfer_enabled); - if stop { - ContextSend::set_is_stopped(); - } else { - allow_err!(peer.send(&crate::clipboard_file::clip_2_msg(clip)).await); - } - } - } - None => { - // unreachable!() - } - } + self.handle_local_clipboard_msg(&mut peer, _msg).await; } _ = self.timer.tick() => { if last_recv_time.elapsed() >= SEC30 { @@ -277,6 +260,44 @@ impl Remote { } } + #[cfg(windows)] + async fn handle_local_clipboard_msg( + &self, + peer: &mut crate::client::FramedStream, + msg: Option, + ) { + match msg { + Some(clip) => match clip { + clipboard::ClipboardFile::NotifyCallback { + r#type, + title, + text, + } => { + self.handler.msgbox(&r#type, &title, &text, ""); + } + _ => { + let is_stopping_allowed = clip.is_stopping_allowed(); + let server_file_transfer_enabled = + *self.handler.server_file_transfer_enabled.read().unwrap(); + let file_transfer_enabled = + self.handler.lc.read().unwrap().enable_file_transfer.v; + let stop = is_stopping_allowed + && (!self.is_connected + || !(server_file_transfer_enabled && file_transfer_enabled)); + log::debug!("Process clipboard message from system, stop: {}, is_stopping_allowed: {}, server_file_transfer_enabled: {}, file_transfer_enabled: {}", stop, is_stopping_allowed, server_file_transfer_enabled, file_transfer_enabled); + if stop { + ContextSend::set_is_stopped(); + } else { + allow_err!(peer.send(&crate::clipboard_file::clip_2_msg(clip)).await); + } + } + }, + None => { + // unreachable!() + } + } + } + fn handle_job_status(&mut self, id: i32, file_num: i32, err: Option) { if let Some(job) = self.remove_jobs.get_mut(&id) { if job.no_confirm { @@ -1030,6 +1051,15 @@ impl Remote { if self.handler.is_file_transfer() { self.handler.load_last_jobs(); } + + self.is_connected = true; + #[cfg(target_os = "windows")] + if self.handler.peer_platform() == crate::platform::PLATFORM_WINDOWS { + if let Some(last_file_format_list) = get_last_file_format_list() { + self.handle_local_clipboard_msg(peer, Some(last_file_format_list)) + .await; + } + } } _ => {} }, diff --git a/src/platform/mod.rs b/src/platform/mod.rs index e962ef9d5..6492e5e25 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -26,6 +26,11 @@ use hbb_common::{message_proto::CursorData, ResultType}; #[cfg(not(any(target_os = "macos", target_os = "android", target_os = "ios")))] const SERVICE_INTERVAL: u64 = 300; +pub const PLATFORM_WINDOWS: &str = "Windows"; +pub const PLATFORM_LINUX: &str = "Linux"; +pub const PLATFORM_MACOS: &str = "Mac OS"; +pub const PLATFORM_ANDROID: &str = "Android"; + pub fn is_xfce() -> bool { #[cfg(target_os = "linux")] { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index a88a02edb..be0185d30 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -412,7 +412,7 @@ impl Session { pub fn get_path_sep(&self, is_remote: bool) -> &'static str { let p = self.get_platform(is_remote); - if &p == "Windows" { + if &p == crate::platform::PLATFORM_WINDOWS { return "\\"; } else { return "/";