Allow configuring remote control permissions for different users (#13974)

Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
21pages
2026-01-09 00:21:28 +08:00
committed by GitHub
parent 4d3ccc62e8
commit 3a9084006f
11 changed files with 353 additions and 60 deletions

View File

@@ -3039,10 +3039,21 @@ Future<void> start_service(bool is_start) async {
}
Future<bool> canBeBlocked() async {
var access_mode = await bind.mainGetOption(key: kOptionAccessMode);
// First check control permission
final controlPermission = await bind.mainGetCommon(
key: "is-remote-modify-enabled-by-control-permissions");
if (controlPermission == "true") {
return false;
} else if (controlPermission == "false") {
return true;
}
// Check local settings
var accessMode = await bind.mainGetOption(key: kOptionAccessMode);
var isCustomAccessMode = accessMode != 'full' && accessMode != 'view';
var option = option2bool(kOptionAllowRemoteConfigModification,
await bind.mainGetOption(key: kOptionAllowRemoteConfigModification));
return access_mode == 'view' || (access_mode.isEmpty && !option);
return accessMode == 'view' || (isCustomAccessMode && !option);
}
// to-do: web not implemented

View File

@@ -2277,6 +2277,28 @@ pub fn str2color(s: &str, alpha: u8) -> u32 {
(alpha as u32) << 24 | rgb
}
/// Check control permission state from a u64 bitmap.
/// Each permission uses 2 bits: 0 = not set, 1 = disable, 2 = enable, 3 = invalid (treated as not set)
/// Returns: Some(true) = enabled, Some(false) = disabled, None = not set or invalid
pub fn get_control_permission(
permissions: u64,
permission: hbb_common::rendezvous_proto::control_permissions::Permission,
) -> Option<bool> {
use hbb_common::protobuf::Enum;
let index = permission.value();
if index >= 0 && index < 32 {
let shift = index * 2;
let value = (permissions >> shift) & 0b11;
match value {
1 => Some(false), // disable
2 => Some(true), // enable
_ => None, // 0 = not set, 3 = invalid
}
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@@ -2600,6 +2600,13 @@ pub fn main_get_common(key: String) -> String {
return false.to_string();
} else if key == "transfer-job-id" {
return hbb_common::fs::get_next_job_id().to_string();
} else if key == "is-remote-modify-enabled-by-control-permissions" {
return match is_remote_modify_enabled_by_control_permissions() {
Some(true) => "true",
Some(false) => "false",
None => "",
}
.to_string();
} else {
if key.starts_with("download-data-") {
let id = key.replace("download-data-", "");

View File

@@ -23,7 +23,11 @@ pub use clipboard::ClipboardFile;
use hbb_common::{
allow_err, bail, bytes,
bytes_codec::BytesCodec,
config::{self, keys::OPTION_ALLOW_WEBSOCKET, Config, Config2},
config::{
self,
keys::{self, OPTION_ALLOW_WEBSOCKET},
Config, Config2,
},
futures::StreamExt as _,
futures_util::sink::SinkExt,
log, password_security as password, timeout,
@@ -384,6 +388,9 @@ pub enum Data {
SocksWs(Option<Box<(Option<config::Socks5Server>, String)>>),
#[cfg(not(any(target_os = "android", target_os = "ios")))]
Whiteboard((String, crate::whiteboard::CustomEvent)),
ControlPermissionsRemoteModify(Option<bool>),
#[cfg(target_os = "windows")]
FileTransferEnabledState(Option<bool>),
}
#[tokio::main(flavor = "current_thread")]
@@ -862,6 +869,31 @@ async fn handle(data: Data, stream: &mut Connection) {
// Port forward session count is only a get value.
}
},
Data::ControlPermissionsRemoteModify(_) => {
use hbb_common::rendezvous_proto::control_permissions::Permission;
let state =
crate::server::get_control_permission_state(Permission::remote_modify, true);
allow_err!(
stream
.send(&Data::ControlPermissionsRemoteModify(state))
.await
);
}
#[cfg(target_os = "windows")]
Data::FileTransferEnabledState(_) => {
use hbb_common::rendezvous_proto::control_permissions::Permission;
let state = crate::server::get_control_permission_state(Permission::file, false);
let enabled = state.unwrap_or_else(|| {
crate::server::Connection::is_permission_enabled_locally(
config::keys::OPTION_ENABLE_FILE_TRANSFER,
)
});
allow_err!(
stream
.send(&Data::FileTransferEnabledState(Some(enabled)))
.await
);
}
_ => {}
}
}

View File

@@ -427,6 +427,7 @@ impl RendezvousMediator {
rr.secure,
false,
Default::default(),
rr.control_permissions.clone().into_option(),
)
.await
}
@@ -440,6 +441,7 @@ impl RendezvousMediator {
secure: bool,
initiate: bool,
socket_addr_v6: bytes::Bytes,
control_permissions: Option<ControlPermissions>,
) -> ResultType<()> {
let peer_addr = AddrMangle::decode(&socket_addr);
log::info!(
@@ -473,6 +475,7 @@ impl RendezvousMediator {
peer_addr,
secure,
is_ipv4(&self.addr),
control_permissions,
)
.await;
Ok(())
@@ -491,7 +494,13 @@ impl RendezvousMediator {
let relay = use_ws() || Config::is_proxy();
let mut socket_addr_v6 = Default::default();
if peer_addr_v6.port() > 0 && !relay {
socket_addr_v6 = start_ipv6(peer_addr_v6, addr, server.clone()).await;
socket_addr_v6 = start_ipv6(
peer_addr_v6,
addr,
server.clone(),
fla.control_permissions.clone().into_option(),
)
.await;
}
if is_ipv4(&self.addr) && !relay && !config::is_disable_tcp_listen() {
if let Err(err) = self
@@ -517,6 +526,7 @@ impl RendezvousMediator {
true,
true,
socket_addr_v6,
fla.control_permissions.into_option(),
)
.await
}
@@ -547,7 +557,14 @@ impl RendezvousMediator {
});
let bytes = msg_out.write_to_bytes()?;
socket.send_raw(bytes).await?;
crate::accept_connection(server.clone(), socket, peer_addr, true).await;
crate::accept_connection(
server.clone(),
socket,
peer_addr,
true,
fla.control_permissions.into_option(),
)
.await;
Ok(())
}
@@ -562,8 +579,15 @@ impl RendezvousMediator {
let peer_addr_v6 = hbb_common::AddrMangle::decode(&ph.socket_addr_v6);
let relay = use_ws() || Config::is_proxy() || ph.force_relay;
let mut socket_addr_v6 = Default::default();
let control_permissions = ph.control_permissions.into_option();
if peer_addr_v6.port() > 0 && !relay {
socket_addr_v6 = start_ipv6(peer_addr_v6, peer_addr, server.clone()).await;
socket_addr_v6 = start_ipv6(
peer_addr_v6,
peer_addr,
server.clone(),
control_permissions.clone(),
)
.await;
}
let relay_server = self.get_relay_server(ph.relay_server);
// for ensure, websocket go relay directly
@@ -582,6 +606,7 @@ impl RendezvousMediator {
true,
true,
socket_addr_v6.clone(),
control_permissions,
)
.await;
}
@@ -598,7 +623,8 @@ impl RendezvousMediator {
};
if ph.udp_port > 0 {
peer_addr.set_port(ph.udp_port as u16);
self.punch_udp_hole(peer_addr, server, msg_punch).await?;
self.punch_udp_hole(peer_addr, server, msg_punch, control_permissions)
.await?;
return Ok(());
}
log::debug!("Punch tcp hole to {:?}", peer_addr);
@@ -614,7 +640,8 @@ impl RendezvousMediator {
msg_out.set_punch_hole_sent(msg_punch);
let bytes = msg_out.write_to_bytes()?;
socket.send_raw(bytes).await?;
crate::accept_connection(server.clone(), socket, peer_addr, true).await;
crate::accept_connection(server.clone(), socket, peer_addr, true, control_permissions)
.await;
Ok(())
}
@@ -623,6 +650,7 @@ impl RendezvousMediator {
peer_addr: SocketAddr,
server: ServerPtr,
msg_punch: PunchHoleSent,
control_permissions: Option<ControlPermissions>,
) -> ResultType<()> {
let mut msg_out = Message::new();
msg_out.set_punch_hole_sent(msg_punch);
@@ -637,7 +665,14 @@ impl RendezvousMediator {
socket.send_to(&data, addr).await.ok();
}
});
udp_nat_listen(socket_cloned.clone(), peer_addr, peer_addr, server).await?;
udp_nat_listen(
socket_cloned.clone(),
peer_addr,
peer_addr,
server,
control_permissions,
)
.await?;
Ok(())
}
@@ -778,6 +813,7 @@ async fn direct_server(server: ServerPtr) {
hbb_common::Stream::from(stream, local_addr),
addr,
false,
None, // Direct connections don't have control_permissions
)
.await
);
@@ -809,12 +845,22 @@ async fn start_ipv6(
peer_addr_v6: SocketAddr,
peer_addr_v4: SocketAddr,
server: ServerPtr,
control_permissions: Option<ControlPermissions>,
) -> bytes::Bytes {
crate::test_ipv6().await;
if let Some((socket, local_addr_v6)) = crate::get_ipv6_socket().await {
let server = server.clone();
tokio::spawn(async move {
allow_err!(udp_nat_listen(socket.clone(), peer_addr_v6, peer_addr_v4, server).await);
allow_err!(
udp_nat_listen(
socket.clone(),
peer_addr_v6,
peer_addr_v4,
server,
control_permissions
)
.await
);
});
return local_addr_v6;
}
@@ -826,6 +872,7 @@ async fn udp_nat_listen(
peer_addr: SocketAddr,
peer_addr_v4: SocketAddr,
server: ServerPtr,
control_permissions: Option<ControlPermissions>,
) -> ResultType<()> {
let tm = Instant::now();
let socket_cloned = socket.clone();
@@ -838,7 +885,14 @@ async fn udp_nat_listen(
res,
)
.await?;
crate::server::create_tcp_connection(server, stream.1, peer_addr_v4, true).await?;
crate::server::create_tcp_connection(
server,
stream.1,
peer_addr_v4,
true,
control_permissions,
)
.await?;
Ok(())
};
func.await.map_err(|e: anyhow::Error| {

View File

@@ -154,18 +154,30 @@ pub fn new() -> ServerPtr {
Arc::new(RwLock::new(server))
}
async fn accept_connection_(server: ServerPtr, socket: Stream, secure: bool) -> ResultType<()> {
async fn accept_connection_(
server: ServerPtr,
socket: Stream,
secure: bool,
control_permissions: Option<ControlPermissions>,
) -> ResultType<()> {
let local_addr = socket.local_addr();
drop(socket);
// even we drop socket, below still may fail if not use reuse_addr,
// there is TIME_WAIT before socket really released, so sometimes we
// see Only one usage of each socket address is normally permitted on windows sometimes,
// see "Only one usage of each socket address is normally permitted" on windows sometimes,
let listener = new_listener(local_addr, true).await?;
log::info!("Server listening on: {}", &listener.local_addr()?);
if let Ok((stream, addr)) = timeout(CONNECT_TIMEOUT, listener.accept()).await? {
stream.set_nodelay(true).ok();
let stream_addr = stream.local_addr()?;
create_tcp_connection(server, Stream::from(stream, stream_addr), addr, secure).await?;
create_tcp_connection(
server,
Stream::from(stream, stream_addr),
addr,
secure,
control_permissions,
)
.await?;
}
Ok(())
}
@@ -175,6 +187,7 @@ pub async fn create_tcp_connection(
stream: Stream,
addr: SocketAddr,
secure: bool,
control_permissions: Option<ControlPermissions>,
) -> ResultType<()> {
let mut stream = stream;
let id = server.write().unwrap().get_new_id();
@@ -242,7 +255,14 @@ pub async fn create_tcp_connection(
}
log::info!("wake up macos");
}
Connection::start(addr, stream, id, Arc::downgrade(&server)).await;
Connection::start(
addr,
stream,
id,
Arc::downgrade(&server),
control_permissions,
)
.await;
Ok(())
}
@@ -251,8 +271,9 @@ pub async fn accept_connection(
socket: Stream,
peer_addr: SocketAddr,
secure: bool,
control_permissions: Option<ControlPermissions>,
) {
if let Err(err) = accept_connection_(server, socket, secure).await {
if let Err(err) = accept_connection_(server, socket, secure, control_permissions).await {
log::warn!("Failed to accept connection from {}: {}", peer_addr, err);
}
}
@@ -264,9 +285,18 @@ pub async fn create_relay_connection(
peer_addr: SocketAddr,
secure: bool,
ipv4: bool,
control_permissions: Option<ControlPermissions>,
) {
if let Err(err) =
create_relay_connection_(server, relay_server, uuid.clone(), peer_addr, secure, ipv4).await
if let Err(err) = create_relay_connection_(
server,
relay_server,
uuid.clone(),
peer_addr,
secure,
ipv4,
control_permissions,
)
.await
{
log::error!(
"Failed to create relay connection for {} with uuid {}: {}",
@@ -284,6 +314,7 @@ async fn create_relay_connection_(
peer_addr: SocketAddr,
secure: bool,
ipv4: bool,
control_permissions: Option<ControlPermissions>,
) -> ResultType<()> {
let mut stream = socket_client::connect_tcp(
socket_client::ipv4_to_ipv6(crate::check_port(relay_server, RELAY_PORT), ipv4),
@@ -298,7 +329,7 @@ async fn create_relay_connection_(
..Default::default()
});
stream.send(&msg_out).await?;
create_tcp_connection(server, stream, peer_addr, secure).await?;
create_tcp_connection(server, stream, peer_addr, secure, control_permissions).await?;
Ok(())
}

View File

@@ -71,6 +71,7 @@ lazy_static::lazy_static! {
static ref SESSIONS: Arc::<Mutex<HashMap<SessionKey, Session>>> = Default::default();
static ref ALIVE_CONNS: Arc::<Mutex<Vec<i32>>> = Default::default();
pub static ref AUTHED_CONNS: Arc::<Mutex<Vec<AuthedConn>>> = Default::default();
pub static ref CONTROL_PERMISSIONS_ARRAY: Arc::<Mutex<Vec<(i32, ControlPermissions)>>> = Default::default();
static ref SWITCH_SIDES_UUID: Arc::<Mutex<HashMap<String, (Instant, uuid::Uuid)>>> = Default::default();
static ref WAKELOCK_SENDER: Arc::<Mutex<std::sync::mpsc::Sender<(usize, usize)>>> = Arc::new(Mutex::new(start_wakelock_thread()));
}
@@ -226,6 +227,7 @@ pub struct Connection {
restart: bool,
recording: bool,
block_input: bool,
control_permissions: Option<ControlPermissions>,
last_test_delay: Option<Instant>,
network_delay: u32,
lock_after_session_end: bool,
@@ -349,8 +351,14 @@ impl Connection {
stream: super::Stream,
id: i32,
server: super::ServerPtrWeak,
control_permissions: Option<ControlPermissions>,
) {
// Android is not supported yet, so we always set control_permissions to None.
#[cfg(target_os = "android")]
let control_permissions = None;
let _raii_id = raii::ConnectionID::new(id);
let _raii_control_permissions_id =
raii::ControlPermissionsID::new(id, &control_permissions);
let hash = Hash {
salt: Config::get_salt(),
challenge: Config::get_auto_password(6),
@@ -401,14 +409,15 @@ impl Connection {
port_forward_address: "".to_owned(),
tx_to_cm,
authorized: false,
keyboard: Connection::permission("enable-keyboard"),
clipboard: Connection::permission("enable-clipboard"),
audio: Connection::permission("enable-audio"),
keyboard: Self::permission(keys::OPTION_ENABLE_KEYBOARD, &control_permissions),
clipboard: Self::permission(keys::OPTION_ENABLE_CLIPBOARD, &control_permissions),
audio: Self::permission(keys::OPTION_ENABLE_AUDIO, &control_permissions),
// to-do: make sure is the option correct here
file: Connection::permission(keys::OPTION_ENABLE_FILE_TRANSFER),
restart: Connection::permission("enable-remote-restart"),
recording: Connection::permission("enable-record-session"),
block_input: Connection::permission("enable-block-input"),
file: Self::permission(keys::OPTION_ENABLE_FILE_TRANSFER, &control_permissions),
restart: Self::permission(keys::OPTION_ENABLE_REMOTE_RESTART, &control_permissions),
recording: Self::permission(keys::OPTION_ENABLE_RECORD_SESSION, &control_permissions),
block_input: Self::permission(keys::OPTION_ENABLE_BLOCK_INPUT, &control_permissions),
control_permissions,
last_test_delay: None,
network_delay: 0,
lock_after_session_end: false,
@@ -885,7 +894,7 @@ impl Connection {
match data {
#[cfg(all(target_os = "windows", feature = "flutter"))]
ipc::Data::PrinterData(data) => {
if config::Config::get_bool_option(config::keys::OPTION_ENABLE_REMOTE_PRINTER) {
if Self::permission(keys::OPTION_ENABLE_REMOTE_PRINTER, &conn.control_permissions) {
conn.send_printer_request(data).await;
} else {
conn.send_remote_printing_disallowed().await;
@@ -1942,7 +1951,8 @@ impl Connection {
false
}
pub fn permission(enable_prefix_option: &str) -> bool {
#[inline]
pub fn is_permission_enabled_locally(enable_prefix_option: &str) -> bool {
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
@@ -1959,6 +1969,37 @@ impl Connection {
)
}
fn permission(
enable_prefix_option: &str,
control_permissions: &Option<ControlPermissions>,
) -> bool {
use hbb_common::rendezvous_proto::control_permissions::Permission;
if let Some(control_permissions) = control_permissions {
let permission = match enable_prefix_option {
keys::OPTION_ENABLE_KEYBOARD => Some(Permission::keyboard),
keys::OPTION_ENABLE_REMOTE_PRINTER => Some(Permission::remote_printer),
keys::OPTION_ENABLE_CLIPBOARD => Some(Permission::clipboard),
keys::OPTION_ENABLE_FILE_TRANSFER => Some(Permission::file),
keys::OPTION_ENABLE_AUDIO => Some(Permission::audio),
keys::OPTION_ENABLE_CAMERA => Some(Permission::camera),
keys::OPTION_ENABLE_TERMINAL => Some(Permission::terminal),
keys::OPTION_ENABLE_TUNNEL => Some(Permission::tunnel),
keys::OPTION_ENABLE_REMOTE_RESTART => Some(Permission::restart),
keys::OPTION_ENABLE_RECORD_SESSION => Some(Permission::recording),
keys::OPTION_ENABLE_BLOCK_INPUT => Some(Permission::block_input),
_ => None,
};
if let Some(permission) = permission {
if let Some(enabled) =
crate::get_control_permission(control_permissions.permissions, permission)
{
return enabled;
}
}
}
Self::is_permission_enabled_locally(enable_prefix_option)
}
fn update_codec_on_login(&self) {
use scrap::codec::{Encoder, EncodingUpdate::*};
if let Some(o) = self.lr.clone().option.as_ref() {
@@ -2054,7 +2095,10 @@ impl Connection {
}
match lr.union {
Some(login_request::Union::FileTransfer(ft)) => {
if !Connection::permission(keys::OPTION_ENABLE_FILE_TRANSFER) {
if !Self::permission(
keys::OPTION_ENABLE_FILE_TRANSFER,
&self.control_permissions,
) {
self.send_login_error("No permission of file transfer")
.await;
sleep(1.).await;
@@ -2063,7 +2107,7 @@ impl Connection {
self.file_transfer = Some((ft.dir, ft.show_hidden));
}
Some(login_request::Union::ViewCamera(_vc)) => {
if !Connection::permission(keys::OPTION_ENABLE_CAMERA) {
if !Self::permission(keys::OPTION_ENABLE_CAMERA, &self.control_permissions) {
self.send_login_error("No permission of viewing camera")
.await;
sleep(1.).await;
@@ -2072,7 +2116,7 @@ impl Connection {
self.view_camera = true;
}
Some(login_request::Union::Terminal(terminal)) => {
if !Connection::permission(keys::OPTION_ENABLE_TERMINAL) {
if !Self::permission(keys::OPTION_ENABLE_TERMINAL, &self.control_permissions) {
self.send_login_error("No permission of terminal").await;
sleep(1.).await;
return false;
@@ -2120,7 +2164,7 @@ impl Connection {
}
}
Some(login_request::Union::PortForward(mut pf)) => {
if !Connection::permission("enable-tunnel") {
if !Self::permission(keys::OPTION_ENABLE_TUNNEL, &self.control_permissions) {
self.send_login_error("No permission of IP tunneling").await;
sleep(1.).await;
return false;
@@ -5167,6 +5211,41 @@ impl Retina {
}
}
/// Get control permission state from CONTROL_PERMISSIONS_ARRAY.
/// Returns: Some(false) if any disable, Some(true) if any enable (and no disable), None if not set.
pub fn get_control_permission_state(
permission: hbb_common::rendezvous_proto::control_permissions::Permission,
disable_if_has_disabled: bool,
) -> Option<bool> {
let control_permissions = CONTROL_PERMISSIONS_ARRAY.lock().unwrap();
let mut has_enable = false;
let mut has_disable = false;
for (_, cp) in control_permissions.iter() {
match crate::get_control_permission(cp.permissions, permission) {
Some(false) => has_disable = true,
Some(true) => has_enable = true,
None => {}
}
}
if disable_if_has_disabled {
if has_disable {
Some(false)
} else if has_enable {
Some(true)
} else {
None
}
} else {
if has_enable {
Some(true)
} else if has_disable {
Some(false)
} else {
None
}
}
}
pub struct AuthedConn {
pub conn_id: i32,
pub conn_type: AuthConnType,
@@ -5178,6 +5257,7 @@ pub struct AuthedConn {
mod raii {
// ALIVE_CONNS: all connections, including unauthorized connections
// AUTHED_CONNS: all authorized connections
// CONTROL_PERMISSIONS_ARRAY: all non-None control permissions
use super::*;
pub struct ConnectionID(i32);
@@ -5368,6 +5448,34 @@ mod raii {
}
}
}
pub struct ControlPermissionsID {
id: i32,
control_permissions: Option<ControlPermissions>,
}
impl Drop for ControlPermissionsID {
fn drop(&mut self) {
if self.control_permissions.is_some() {
let mut lock = CONTROL_PERMISSIONS_ARRAY.lock().unwrap();
lock.retain(|(conn_id, _)| *conn_id != self.id);
}
}
}
impl ControlPermissionsID {
pub fn new(id: i32, control_permissions: &Option<ControlPermissions>) -> Self {
if let Some(s) = control_permissions {
CONTROL_PERMISSIONS_ARRAY
.lock()
.unwrap()
.push((id, s.clone()));
}
Self {
id,
control_permissions: control_permissions.clone(),
}
}
}
}
mod test {

View File

@@ -699,6 +699,15 @@ impl UI {
fn get_builtin_option(&self, key: String) -> String {
crate::ui_interface::get_builtin_option(&key)
}
fn is_remote_modify_enabled_by_control_permissions(&self) -> String {
match crate::ui_interface::is_remote_modify_enabled_by_control_permissions() {
Some(true) => "true",
Some(false) => "false",
None => "",
}
.to_string()
}
}
impl sciter::EventHandler for UI {
@@ -801,6 +810,7 @@ impl sciter::EventHandler for UI {
fn verify_login(String, String);
fn is_option_fixed(String);
fn get_builtin_option(String);
fn is_remote_modify_enabled_by_control_permissions();
}
}

View File

@@ -1396,7 +1396,16 @@ function self.onMouse(evt) {
}
function check_if_overlay() {
if (handler.get_option('allow-remote-config-modification') != 'Y') {
var enabled;
var is_enabled_by_control_permissions = handler.is_remote_modify_enabled_by_control_permissions();
if (is_enabled_by_control_permissions == "true") {
enabled = true;
} else if (is_enabled_by_control_permissions == "false") {
enabled = false;
} else {
enabled = handler.get_option('allow-remote-config-modification') == 'Y';
}
if (!enabled) {
var time0 = getTime();
handler.check_mouse_time();
self.timer(120ms, function() {

View File

@@ -772,7 +772,14 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
#[tokio::main(flavor = "current_thread")]
pub async fn start_ipc<T: InvokeUiCM>(cm: ConnectionManager<T>) {
#[cfg(target_os = "windows")]
ContextSend::enable(crate::Connection::permission(OPTION_ENABLE_FILE_TRANSFER));
{
let enabled = crate::Connection::is_permission_enabled_locally(OPTION_ENABLE_FILE_TRANSFER);
let mut lock = crate::ui_interface::IS_FILE_TRANSFER_ENABLED
.lock()
.unwrap();
ContextSend::enable(enabled);
*lock = Some(enabled);
}
match ipc::new_listener("_cm").await {
Ok(mut incoming) => {
while let Some(result) = incoming.next().await {

View File

@@ -69,6 +69,7 @@ lazy_static::lazy_static! {
static ref ASYNC_JOB_STATUS : Arc<Mutex<String>> = Default::default();
static ref ASYNC_HTTP_STATUS : Arc<Mutex<HashMap<String, String>>> = Arc::new(Mutex::new(HashMap::new()));
static ref TEMPORARY_PASSWD : Arc<Mutex<String>> = Arc::new(Mutex::new("".to_owned()));
static ref IS_REMOTE_MODIFY_ENABLED_BY_CONTROL_PERMISSIONS : Arc<Mutex<Option<bool>>> = Arc::new(Mutex::new(None));
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
@@ -79,6 +80,11 @@ lazy_static::lazy_static! {
static ref CHILDREN : Children = Default::default();
}
#[cfg(target_os = "windows")]
lazy_static::lazy_static! {
pub static ref IS_FILE_TRANSFER_ENABLED: Arc<Mutex<Option<bool>>> = Arc::new(Mutex::new(None));
}
const INIT_ASYNC_JOB_STATUS: &str = " ";
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
@@ -1166,10 +1172,6 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
let mut video_conn_count = 0;
#[cfg(not(feature = "flutter"))]
let mut id = "".to_owned();
#[cfg(target_os = "windows")]
let mut access_mode = "".to_owned();
#[cfg(target_os = "windows")]
let mut enable_file_transfer = "".to_owned();
let is_cm = crate::common::is_cm();
loop {
@@ -1194,28 +1196,6 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
Ok(Some(ipc::Data::Options(Some(v)))) => {
*OPTIONS.lock().unwrap() = v;
*OPTION_SYNCED.lock().unwrap() = true;
#[cfg(target_os = "windows")]
{
let (ft, am) = {
let lock = OPTIONS.lock().unwrap();
(
lock.get(OPTION_ENABLE_FILE_TRANSFER).map(|x| x.to_string()).unwrap_or_default(),
lock.get(OPTION_ACCESS_MODE).map(|x| x.to_string()).unwrap_or_default(),
)
};
if ft != enable_file_transfer || am != access_mode {
let access_mode_enabled = match am.as_str() {
"full" => Some(true),
"view" => Some(false),
_ => None,
};
let enabled = access_mode_enabled.unwrap_or(config::option2bool(OPTION_ENABLE_FILE_TRANSFER, &ft));
clipboard::ContextSend::enable(enabled);
enable_file_transfer = ft;
access_mode = am;
}
}
}
Ok(Some(ipc::Data::Config((name, Some(value))))) => {
if name == "id" {
@@ -1251,6 +1231,19 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
video_conn_count,
};
}
Ok(Some(ipc::Data::ControlPermissionsRemoteModify(v))) => {
*IS_REMOTE_MODIFY_ENABLED_BY_CONTROL_PERMISSIONS.lock().unwrap() = v;
}
#[cfg(target_os = "windows")]
Ok(Some(ipc::Data::FileTransferEnabledState(v))) => {
if let Some(enabled) = v {
let mut lock = IS_FILE_TRANSFER_ENABLED.lock().unwrap();
if *lock != v {
clipboard::ContextSend::enable(enabled);
*lock = v;
}
}
}
_ => {}
}
}
@@ -1264,6 +1257,9 @@ async fn check_connect_status_(reconnect: bool, rx: mpsc::UnboundedReceiver<ipc:
c.send(&ipc::Data::Config(("temporary-password".to_owned(), None))).await.ok();
#[cfg(feature = "flutter")]
c.send(&ipc::Data::VideoConnCount(None)).await.ok();
c.send(&ipc::Data::ControlPermissionsRemoteModify(None)).await.ok();
#[cfg(target_os = "windows")]
c.send(&ipc::Data::FileTransferEnabledState(None)).await.ok();
}
}
}
@@ -1556,3 +1552,9 @@ pub fn clear_trusted_devices() {
pub fn max_encrypt_len() -> usize {
hbb_common::config::ENCRYPT_MAX_LEN
}
pub fn is_remote_modify_enabled_by_control_permissions() -> Option<bool> {
*IS_REMOTE_MODIFY_ENABLED_BY_CONTROL_PERMISSIONS
.lock()
.unwrap()
}