diff --git a/libs/clipboard/src/lib.rs b/libs/clipboard/src/lib.rs index 21ca6bfb8..bf5a0f140 100644 --- a/libs/clipboard/src/lib.rs +++ b/libs/clipboard/src/lib.rs @@ -77,7 +77,8 @@ pub fn server_msg(context: &mut Box, conn_id: ConnID, msg: ret } None => { - unreachable!() + // unreachable!() + 0 } } } diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index a20f5c5ae..c963b4bae 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -318,11 +318,13 @@ message CliprdrFormat { int32 id = 3; string format = 4; } + message CliprdrServerFormatList { int32 server_conn_id = 1; int32 remote_conn_id = 2; repeated CliprdrFormat formats = 3; } + message CliprdrServerFormatListResponse { int32 server_conn_id = 1; int32 remote_conn_id = 2; @@ -334,6 +336,7 @@ message CliprdrServerFormatDataRequest { int32 remote_conn_id = 2; int32 requested_format_id = 3; } + message CliprdrServerFormatDataResponse { int32 server_conn_id = 1; int32 remote_conn_id = 2; @@ -353,6 +356,7 @@ message CliprdrFileContentsRequest { bool have_clip_data_id = 9; int32 clip_data_id = 10; } + message CliprdrFileContentsResponse { int32 server_conn_id = 1; int32 remote_conn_id = 2; @@ -386,6 +390,7 @@ message PermissionInfo { Keyboard = 0; Clipboard = 2; Audio = 3; + File = 4; } Permission permission = 1; @@ -413,6 +418,7 @@ message OptionMessage { int32 custom_image_quality = 6; BoolOption disable_audio = 7; BoolOption disable_clipboard = 8; + BoolOption enable_file_transfer = 9; } message OptionResponse { diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index c3d7d23dc..8b1504d79 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -139,6 +139,8 @@ pub struct PeerConfig { pub disable_audio: bool, #[serde(default)] pub disable_clipboard: bool, + #[serde(default)] + pub enable_file_transfer: bool, // the other scalar value must before this #[serde(default)] diff --git a/libs/hbb_common/src/udp.rs b/libs/hbb_common/src/udp.rs index d1896bdbc..f465a0e23 100644 --- a/libs/hbb_common/src/udp.rs +++ b/libs/hbb_common/src/udp.rs @@ -84,7 +84,7 @@ impl FramedSocket { let _ = match self { Self::Direct(f) => match addr { TargetAddr::Ip(addr) => f.send((send_data, addr)).await?, - _ => unreachable!(), + _ => {} }, Self::ProxySocks(f) => f.send((send_data, addr)).await?, }; @@ -103,7 +103,7 @@ impl FramedSocket { let _ = match self { Self::Direct(f) => match addr { TargetAddr::Ip(addr) => f.send((Bytes::from(msg), addr)).await?, - _ => unreachable!(), + _ => {} }, Self::ProxySocks(f) => f.send((Bytes::from(msg), addr)).await?, }; diff --git a/src/client.rs b/src/client.rs index 4c1ea0f54..cbe99b324 100644 --- a/src/client.rs +++ b/src/client.rs @@ -765,6 +765,14 @@ impl LoginConfigHandler { BoolOption::No }) .into(); + } else if name == "enable-file-transfer" { + config.enable_file_transfer = !config.enable_file_transfer; + option.enable_file_transfer = (if config.enable_file_transfer { + BoolOption::Yes + } else { + BoolOption::No + }) + .into(); } else if name == "block-input" { option.block_input = BoolOption::Yes.into(); } else if name == "unblock-input" { @@ -827,6 +835,10 @@ impl LoginConfigHandler { msg.disable_audio = BoolOption::Yes.into(); n += 1; } + if self.get_toggle_option("enable-file-transfer") { + msg.enable_file_transfer = BoolOption::Yes.into(); + n += 1; + } if self.get_toggle_option("disable-clipboard") { msg.disable_clipboard = BoolOption::Yes.into(); n += 1; diff --git a/src/ipc.rs b/src/ipc.rs index 341599d58..351cf83c2 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -72,6 +72,7 @@ pub enum Data { keyboard: bool, clipboard: bool, audio: bool, + file: bool, }, ChatMessage { text: String, diff --git a/src/server.rs b/src/server.rs index ed7315200..8c63f055e 100644 --- a/src/server.rs +++ b/src/server.rs @@ -30,7 +30,7 @@ use std::{ mod audio_service; mod clipboard_service; #[cfg(windows)] -pub mod cliprdr_service; +pub mod clipboard_file_service; mod connection; pub mod input_service; mod service; @@ -64,7 +64,7 @@ pub fn new() -> ServerPtr { server.add_service(Box::new(video_service::new())); server.add_service(Box::new(clipboard_service::new())); #[cfg(windows)] - server.add_service(Box::new(cliprdr_service::new())); + server.add_service(Box::new(clipboard_file_service::new())); server.add_service(Box::new(input_service::new_cursor())); server.add_service(Box::new(input_service::new_pos())); Arc::new(RwLock::new(server)) diff --git a/src/server/cliprdr_service.rs b/src/server/clipboard_file_service.rs similarity index 97% rename from src/server/cliprdr_service.rs rename to src/server/clipboard_file_service.rs index 53ff10dcf..5cb967ba6 100644 --- a/src/server/cliprdr_service.rs +++ b/src/server/clipboard_file_service.rs @@ -74,7 +74,7 @@ mod listen { } } None => { - unreachable!() + // unreachable!() } } } @@ -87,7 +87,7 @@ mod listen { } } None => { - unreachable!() + // unreachable!() } } } diff --git a/src/server/connection.rs b/src/server/connection.rs index e2461e88c..4fff8fffa 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -54,6 +54,7 @@ pub struct Connection { keyboard: bool, clipboard: bool, audio: bool, + file: bool, last_test_delay: i64, image_quality: i32, lock_after_session_end: bool, @@ -62,6 +63,7 @@ pub struct Connection { ip: String, disable_clipboard: bool, // by peer disable_audio: bool, // by peer + enable_file_transfer: bool, // by peer tx_input: std_mpsc::Sender, // handle input messages video_ack_required: bool, } @@ -134,6 +136,7 @@ impl Connection { keyboard: Config::get_option("enable-keyboard").is_empty(), clipboard: Config::get_option("enable-clipboard").is_empty(), audio: Config::get_option("audio-input") != "Mute", + file: Config::get_option("enable-file-transfer").is_empty(), last_test_delay: 0, image_quality: ImageQuality::Balanced.value(), lock_after_session_end: false, @@ -141,6 +144,7 @@ impl Connection { privacy_mode: false, ip: "".to_owned(), disable_audio: false, + enable_file_transfer: false, disable_clipboard: false, tx_input, video_ack_required: false, @@ -162,6 +166,9 @@ impl Connection { if !conn.audio { conn.send_permission(Permission::Audio, false).await; } + if !conn.file { + conn.send_permission(Permission::File, false).await; + } let mut test_delay_timer = time::interval_at(Instant::now() + TEST_DELAY_TIMEOUT, TEST_DELAY_TIMEOUT); let mut last_recv_time = Instant::now(); @@ -224,10 +231,6 @@ impl Connection { s.write().unwrap().subscribe( super::clipboard_service::NAME, conn.inner.clone(), conn.clipboard_enabled() && conn.keyboard); - #[cfg(windows)] - s.write().unwrap().subscribe( - super::cliprdr_service::NAME, - conn.inner.clone(), conn.clipboard_enabled() && conn.keyboard); } } else if &name == "audio" { conn.audio = enabled; @@ -237,6 +240,15 @@ impl Connection { super::audio_service::NAME, conn.inner.clone(), conn.audio_enabled()); } + } else if &name == "file" { + conn.file = enabled; + conn.send_permission(Permission::File, enabled).await; + #[cfg(windows)] + if let Some(s) = conn.server.upgrade() { + s.write().unwrap().subscribe( + super::clipboard_file_service::NAME, + conn.inner.clone(), conn.file_transfer_enabled()); + } } } ipc::Data::RawMessage(bytes) => { @@ -604,12 +616,14 @@ impl Connection { } if !self.clipboard_enabled() || !self.keyboard { noperms.push(super::clipboard_service::NAME); - #[cfg(windows)] - noperms.push(super::cliprdr_service::NAME); } if !self.audio_enabled() { noperms.push(super::audio_service::NAME); } + if !self.file_transfer_enabled() { + #[cfg(windows)] + noperms.push(super::clipboard_file_service::NAME); + } s.write() .unwrap() .add_connection(self.inner.clone(), &noperms); @@ -625,6 +639,10 @@ impl Connection { self.audio && !self.disable_audio } + fn file_transfer_enabled(&self) -> bool { + self.file && self.enable_file_transfer + } + async fn try_start_cm(&mut self, peer_id: String, name: String, authorized: bool) { self.send_to_cm(ipc::Data::Login { id: self.inner.id(), @@ -636,6 +654,7 @@ impl Connection { keyboard: self.keyboard, clipboard: self.clipboard, audio: self.audio, + file: self.file, }); } @@ -832,8 +851,7 @@ impl Connection { } #[cfg(windows)] Some(message::Union::cliprdr(clip)) => { - log::debug!("received cliprdr msg"); - cliprdr_service::handle_serve_cliprdr_msg(self.inner.id, clip) + clipboard_file_service::handle_serve_clipboard_file_msg(self.inner.id, clip) } Some(message::Union::file_action(fa)) => { if self.file_transfer.is_some() { @@ -995,6 +1013,17 @@ impl Connection { } } } + #[cfg(windows)] + if let Ok(q) = o.enable_file_transfer.enum_value() { + if q != BoolOption::NotSet { + self.enable_file_transfer = q == BoolOption::Yes; + s.write().unwrap().subscribe( + super::clipboard_file_service::NAME, + self.inner.clone(), + self.file_transfer_enabled(), + ); + } + } if let Ok(q) = o.disable_clipboard.enum_value() { if q != BoolOption::NotSet { self.disable_clipboard = q == BoolOption::Yes; @@ -1004,12 +1033,6 @@ impl Connection { self.inner.clone(), self.clipboard_enabled() && self.keyboard, ); - #[cfg(windows)] - s.write().unwrap().subscribe( - super::cliprdr_service::NAME, - self.inner.clone(), - self.clipboard_enabled() && self.keyboard, - ); } } } diff --git a/src/ui/cm.css b/src/ui/cm.css index 59a745029..e88245beb 100644 --- a/src/ui/cm.css +++ b/src/ui/cm.css @@ -100,6 +100,10 @@ icon.audio { background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAk1BMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////ROyVeAAAAMHRSTlMAgfz08DDqCAThvraZjEcoGA751JxzbGdfTRP25NrIpaGTcEM+HAvMuKinhXhWNx9Yzm/gAAABFUlEQVQ4y82S2XLCMAxFheMsQNghCQFalkL39vz/11V4GpNk0r629+Va1pmxPFfyh1ravOP2Y1ydJmBO0lYP3r+PyQ62s2Y7fgF6VRXOYdToT++ogIuoVhCUtX7YpwJG3F8f6V8rr3WABwwUahlEvr8y3IBniGKdKYBQ5OGQpukQakBpIVcfwptIhJcf8hWGakdndAAhBInIGHbdQGJg6jjbDUgEE5EpmB+AAM4uj6gb+AQT6wdhITLvAHJ4VCtgoAlG1tpNA0gWON/f4ioHdSADc1bfgt+PZFkDlD6ojWF+kVoaHlhvFjPHuVRrefohY1GdcFm1N8JvwEyrJ/X2Th2rIoVgIi3Fo6Xf0z5k8psKu5f/oi+nHjjI92o36AAAAABJRU5ErkJggg=='); } +icon.file { + background:url('data: image/png;base64, iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAMAAADVRocKAAAAUVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////8IN+deAAAAGnRSTlMAH+CAESEN8jyZkcIb5N/ONy3vmHhmiGjUm7UwS+YAAAHZSURBVGje7dnbboMwDIBhBwgQoFAO7Ta//4NOqCAXYZQstatq4r+r5ubrgQSpg8iyC4ZURa+PlIpQYGiwrzyeHtYZjAL8T05O4H8BbbKvFgRa4NoBU8pXeYEkDDgaaLQBcwJrmeErJQB/7wes3QBWGnCIX0+AQycL1PO6BMwPa0nA4ZxbgTvOjUYMGPHRnZkQAY4mxPZBjmy53E7ukSkFKYB/D4XsWZQx64sCeYebOogGsoOBYvv6/UCb8F0IOBZ0TlP6lEYdANY350AJqB9/qPVuOI5evw4A1hgLigAlepnyxW80bcCcwN++A2s82Vcu02ta+ceq9BoL5KGTTRwQPlpqA3gCnwWU2kCDgeWRQPj2jAPCDxgCMjhI6uZnToDpvd/BJeFrJQB/fsAa02gCt3mi1wNuy8GgBNDZlysBNNSrADVSjcJl6vCpUn6jOdx0kz0q6PMhQRa4465SFKhx35cgUCBTwj2/NHwZAb71qR8GEP2H1XcmAtBPTEO67GP6FUUAIKGABbDLQ0EArhN2sAIGesRO+iyy+RMAjckVTlMCKFVAbh/4Af9OPgG61SkDVco3BQGT3GXaDAnTIAcYZDuBTwGsAGDxuBFeAQqIqwoFMlAVLrHr/wId5MPt0nilGgAAAABJRU5ErkJggg=='); +} + div.buttons { width: *; border-spacing: 0.5em; @@ -230,4 +234,4 @@ div.tab-arrows span:hover { div.tab-arrows span:active { opacity: 1; background-color: #ddd; -} \ No newline at end of file +} diff --git a/src/ui/cm.rs b/src/ui/cm.rs index 32f7fc6d1..5547c3686 100644 --- a/src/ui/cm.rs +++ b/src/ui/cm.rs @@ -76,6 +76,7 @@ impl ConnectionManager { keyboard: bool, clipboard: bool, audio: bool, + file: bool, tx: mpsc::UnboundedSender, ) { self.call( @@ -89,7 +90,8 @@ impl ConnectionManager { authorized, keyboard, clipboard, - audio + audio, + file ), ); self.write().unwrap().senders.insert(id, tx); @@ -345,9 +347,9 @@ async fn start_ipc(cm: ConnectionManager) { } Ok(Some(data)) => { match data { - Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio} => { + Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file} => { conn_id = id; - cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, tx.clone()); + cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, tx.clone()); } Data::Close => { log::info!("cm ipc connection closed from connection request"); diff --git a/src/ui/cm.tis b/src/ui/cm.tis index 8fdb3f17e..3d341c2c1 100644 --- a/src/ui/cm.tis +++ b/src/ui/cm.tis @@ -41,6 +41,7 @@ class Body: Reactor.Component
+
} {c.port_forward ?
Port Forwarding: {c.port_forward}
: ""}
@@ -94,6 +95,15 @@ class Body: Reactor.Component }); } + event click $(icon.file) { + var { cid, connection } = this; + checkClickTime(function() { + connection.file = !connection.file; + body.update(); + handler.switch_permission(cid, "file", connection.file); + }); + } + event click $(button#accept) { var { cid, connection } = this; checkClickTime(function() { @@ -243,7 +253,7 @@ function bring_to_top(idx=-1) { } } -handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio) { +handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file) { var conn; connections.map(function(c) { if (c.id == id) conn = c; @@ -259,7 +269,7 @@ handler.addConnection = function(id, is_file_transfer, port_forward, peer_id, na port_forward: port_forward, name: name, authorized: authorized, time: new Date(), keyboard: keyboard, clipboard: clipboard, msgs: [], unreaded: 0, - audio: audio, + audio: audio, file: file }); body.cur = connections.length - 1; bring_to_top(); @@ -396,7 +406,7 @@ function adjustHeader() { view.on("size", adjustHeader); -// handler.addConnection(0, false, 0, "", "test1", true, false, false, false); -// handler.addConnection(1, false, 0, "", "test2--------", true, false, false, false); -// handler.addConnection(2, false, 0, "", "test3", true, false, false, false); +handler.addConnection(0, false, 0, "", "test1", true, false, false, true, true); +// handler.addConnection(1, false, 0, "", "test2--------", true, false, false, false, false); +// handler.addConnection(2, false, 0, "", "test3", true, false, false, false, false); // handler.newMessage(0, 'h'); diff --git a/src/ui/header.tis b/src/ui/header.tis index c04e7d973..44eaa58de 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -127,6 +127,7 @@ class Header: Reactor.Component {
  • {svg_checkmark}{translate('Show remote cursor')}
  • {audio_enabled ?
  • {svg_checkmark}{translate('Mute')}
  • : ""} + {is_win && pi.platform == 'Windows' && file_enabled ?
  • {svg_checkmark}{translate('File transfer')}
  • : ""} {keyboard_enabled && clipboard_enabled ?
  • {svg_checkmark}{translate('Disable clipboard')}
  • : ""} {keyboard_enabled ?
  • {svg_checkmark}{translate('Lock after session end')}
  • : ""} {keyboard_enabled && pi.platform == "Windows" ?
  • {svg_checkmark}{translate('Privacy mode')}
  • : ""} @@ -298,7 +299,7 @@ function toggleMenuState() { for (var el in $$(menu#display-options>li)) { el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0); } - for (var id in ["show-remote-cursor", "disable-audio", "disable-clipboard", "lock-after-session-end", "privacy-mode"]) { + for (var id in ["show-remote-cursor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end", "privacy-mode"]) { var el = self.select('#' + id); if (el) { var value = handler.get_toggle_option(id); diff --git a/src/ui/remote.rs b/src/ui/remote.rs index be26b1f57..3950228ad 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -4,7 +4,9 @@ use crate::common::{ }; #[cfg(windows)] use clipboard::{ - cliprdr::CliprdrClientContext, create_cliprdr_context, get_rx_client_msg, server_msg, ConnID, + cliprdrfile::CliprdrClientContext, create_cliprdr_context as create_clipboard_file_context, + get_rx_client_msg as get_clipboard_file_rx_client_msg, server_msg as clipboard_file_msg, + ConnID as ClipboardFileConnID, }; use enigo::{self, Enigo, KeyboardControllable}; use hbb_common::{ @@ -1165,7 +1167,7 @@ async fn io_loop(handler: Handler) { }); #[cfg(windows)] - let cliprdr_context = match create_cliprdr_context(true, false) { + let clipboard_file_context = match create_clipboard_file_context(true, false) { Ok(context) => Some(context), Err(err) => { handler.msgbox("error", "Create clipboard error", &err.to_string()); @@ -1187,7 +1189,7 @@ async fn io_loop(handler: Handler) { last_update_jobs_status: (Instant::now(), Default::default()), first_frame: false, #[cfg(windows)] - cliprdr_context, + clipboard_file_context, #[cfg(windows)] pid: std::process::id(), }; @@ -1230,7 +1232,7 @@ struct Remote { last_update_jobs_status: (Instant, HashMap), first_frame: bool, #[cfg(windows)] - cliprdr_context: Option>, + clipboard_file_context: Option>, #[cfg(windows)] pid: u32, } @@ -1255,9 +1257,9 @@ impl Remote { // just build for now #[cfg(not(windows))] - let (_client_tx, mut client_rx) = mpsc::unbounded_channel::(); + let (_, mut clipboard_file_client_rx) = mpsc::unbounded_channel::(); #[cfg(windows)] - let mut client_rx = get_rx_client_msg().lock().await; + let mut clipboard_file_client_rx = get_clipboard_file_rx_client_msg().lock().await; loop { tokio::select! { @@ -1289,18 +1291,16 @@ impl Remote { } } } - msg = client_rx.recv() => { - #[cfg(not(windows))] - println!("{:?}", msg); + _msg = clipboard_file_client_rx.recv() => { #[cfg(windows)] - match msg { + match _msg { Some((conn_id, msg)) => { if conn_id.remote_conn_id == 0 || conn_id.remote_conn_id == self.pid { allow_err!(peer.send(&msg).await); } } None => { - unreachable!() + // unreachable!() } } } @@ -1675,21 +1675,18 @@ impl Remote { update_clipboard(cb, Some(&self.old_clipboard)); } } - #[allow(unused_variables)] + #[cfg(windows)] Some(message::Union::cliprdr(clip)) => { - log::debug!("received cliprdr msg"); - #[cfg(windows)] if !self.handler.lc.read().unwrap().disable_clipboard { - if let Some(context) = &mut self.cliprdr_context { - let res = server_msg( + if let Some(context) = &mut self.clipboard_file_context { + let res = clipboard_file_msg( context, - ConnID { + ClipboardFileConnID { server_conn_id: 0, remote_conn_id: self.pid, }, clip, ); - log::debug!("server msg returns {}", res); } } } @@ -1755,6 +1752,10 @@ impl Remote { self.handler .call("setPermission", &make_args!("audio", p.enabled)); } + Permission::File => { + self.handler + .call("setPermission", &make_args!("file", p.enabled)); + } } } Some(misc::Union::switch_display(s)) => { diff --git a/src/ui/remote.tis b/src/ui/remote.tis index c7d0740ae..1b74002cd 100644 --- a/src/ui/remote.tis +++ b/src/ui/remote.tis @@ -10,6 +10,7 @@ var display_scale = 1; var keyboard_enabled = true; // server side var clipboard_enabled = true; // server side var audio_enabled = true; // server side +var file_enabled = true; // server side var scroll_body = $(body); handler.setDisplay = function(x, y, w, h) { @@ -424,6 +425,7 @@ function self.closing() { handler.setPermission = function(name, enabled) { if (name == "keyboard") keyboard_enabled = enabled; if (name == "audio") audio_enabled = enabled; + if (name == "file") file_enabled = enabled; if (name == "clipboard") clipboard_enabled = enabled; input_blocked = false; header.update();