Compare commits

...

10 Commits

Author SHA1 Message Date
21pages
5439ec38b6 Revert "fix web break introduced in 38f130071 fix(linux): enable mouse side buttons in remote sessions (#14848)" (#14973)
This reverts commit d5d0b01266.
2026-05-06 20:20:17 +08:00
rustdesk
8b8a64f870 revert hbb_common to old one 2026-05-06 19:40:52 +08:00
rustdesk
92509f8e8a update hbb_common 2026-05-06 19:35:47 +08:00
Lynilia
0221634a4d Update fr.rs (#14955) 2026-05-06 19:32:59 +08:00
Mr-Update
9d1f86fbc6 Update de.rs (#14953) 2026-05-06 19:32:41 +08:00
rustdesk
f29dec7b13 harden switch side 2026-05-06 19:27:56 +08:00
rustdesk
d5d0b01266 fix web break introduced in 38f130071 fix(linux): enable mouse side buttons in remote sessions (#14848) 2026-05-06 18:27:34 +08:00
bovirus
5abae617dc Italian language update (#14949) 2026-05-04 16:50:42 +08:00
bilimiyorum
52d62da002 Update tr.rs (#14948)
1- New string entry
2- A minor improvement for terminological consistency
2026-05-04 16:50:23 +08:00
solokot
253d632709 Update ru.rs (#14947) 2026-05-04 16:49:49 +08:00
12 changed files with 159 additions and 20 deletions

View File

@@ -1745,6 +1745,9 @@ pub struct LoginConfigHandler {
pub direct: Option<bool>,
pub received: bool,
switch_uuid: Option<String>,
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
switch_back_allowed: bool,
pub save_ab_password_to_recent: bool, // true: connected with ab password
pub other_server: Option<(String, String, String)>,
pub custom_fps: Arc<Mutex<Option<usize>>>,
@@ -1861,6 +1864,11 @@ impl LoginConfigHandler {
self.direct = None;
self.received = false;
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
self.switch_back_allowed = false;
}
self.switch_uuid = switch_uuid;
self.adapter_luid = adapter_luid;
self.selected_windows_session_id = None;
@@ -1874,6 +1882,23 @@ impl LoginConfigHandler {
self.is_terminal_admin = is_terminal_admin;
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn allow_switch_back_once(&mut self) {
self.switch_back_allowed = true;
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn consume_switch_back_permission(&mut self) -> bool {
if self.switch_back_allowed {
self.switch_back_allowed = false;
true
} else {
false
}
}
/// Check if the client should auto login.
/// Return password if the client should auto login, otherwise return empty string.
pub fn should_auto_login(&self) -> String {
@@ -3377,6 +3402,36 @@ pub fn handle_login_error(
}
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
async fn consume_local_switch_sides_uuid(id: &str, uuid: &Uuid) -> bool {
let Ok(mut conn) = crate::ipc::connect(1000, "").await else {
return false;
};
let uuid = uuid.to_string();
if conn
.send(&crate::ipc::Data::SwitchSidesUuid(
uuid.clone(),
id.to_owned(),
None,
))
.await
.is_err()
{
return false;
}
match conn.next_timeout(1000).await {
Ok(Some(crate::ipc::Data::SwitchSidesUuid(
returned_uuid,
returned_id,
Some(true),
))) => {
returned_uuid == uuid && returned_id == id
}
_ => false,
}
}
/// Handle hash message sent by peer.
/// Hash will be used for login.
///
@@ -3397,12 +3452,22 @@ pub async fn handle_hash(
// Take care of password application order
// switch_uuid
let uuid = lc.write().unwrap().switch_uuid.take();
if let Some(uuid) = uuid {
if let Ok(uuid) = uuid::Uuid::from_str(&uuid) {
send_switch_login_request(lc.clone(), peer, uuid).await;
lc.write().unwrap().password_source = Default::default();
return;
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
let uuid = lc.write().unwrap().switch_uuid.take();
if let Some(uuid) = uuid {
if let Ok(uuid) = uuid::Uuid::from_str(&uuid) {
let id = lc.read().unwrap().id.clone();
if !consume_local_switch_sides_uuid(&id, &uuid).await {
log::warn!("Ignored untrusted switch_uuid");
} else {
lc.write().unwrap().allow_switch_back_once();
send_switch_login_request(lc.clone(), peer, uuid).await;
lc.write().unwrap().password_source = Default::default();
return;
}
}
}
}
// last password

View File

@@ -1923,9 +1923,23 @@ impl<T: InvokeUiSession> Remote<T> {
);
}
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
Some(misc::Union::SwitchBack(_)) => {
#[cfg(feature = "flutter")]
self.handler.switch_back(&self.handler.get_id());
let allow_switch_back = self
.handler
.lc
.write()
.unwrap()
.consume_switch_back_permission();
if allow_switch_back {
self.handler.switch_back(&self.handler.get_id());
} else {
log::warn!(
"Ignored unsolicited SwitchBack from {}",
self.handler.get_id()
);
}
}
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
#[cfg(not(any(target_os = "android", target_os = "ios")))]

View File

@@ -2213,7 +2213,7 @@ pub fn cm_elevate_portable(conn_id: i32) {
}
pub fn cm_switch_back(conn_id: i32) {
#[cfg(not(any(target_os = "ios")))]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
crate::ui_cm_interface::switch_back(conn_id);
}

View File

@@ -285,7 +285,14 @@ pub enum Data {
Empty,
Disconnected,
DataPortableService(DataPortableService),
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
SwitchSidesRequest(String),
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
SwitchSidesUuid(String, String, Option<bool>),
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
SwitchSidesBack,
UrlLink(String),
VoiceCallIncoming,
@@ -771,6 +778,8 @@ async fn handle(data: Data, stream: &mut Connection) {
Data::TestRendezvousServer => {
crate::test_rendezvous_server();
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
Data::SwitchSidesRequest(id) => {
let uuid = uuid::Uuid::new_v4();
crate::server::insert_switch_sides_uuid(id, uuid.clone());
@@ -780,6 +789,19 @@ async fn handle(data: Data, stream: &mut Connection) {
.await
);
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
Data::SwitchSidesUuid(uuid, id, None) => {
let allowed = uuid
.parse::<uuid::Uuid>()
.map(|uuid| crate::server::remove_pending_switch_sides_uuid(&id, &uuid))
.unwrap_or(false);
allow_err!(
stream
.send(&Data::SwitchSidesUuid(uuid, id, Some(allowed)))
.await
);
}
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
Data::Plugin(plugin) => crate::plugin::ipc::handle_plugin(plugin, stream).await,

View File

@@ -743,6 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Display Name", "Anzeigename"),
("password-hidden-tip", "Ein permanentes Passwort wurde festgelegt (ausgeblendet)."),
("preset-password-in-use-tip", "Das voreingestellte Passwort wird derzeit verwendet."),
("Enable privacy mode", ""),
("Enable privacy mode", "Datenschutzmodus aktivieren"),
].iter().cloned().collect();
}

View File

@@ -743,6 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Display Name", "Nom daffichage"),
("password-hidden-tip", "Le mot de passe permanent est défini (masqué)."),
("preset-password-in-use-tip", "Le mot de passe prédéfini est actuellement utilisé."),
("Enable privacy mode", ""),
("Enable privacy mode", "Activer le mode de confidentialité"),
].iter().cloned().collect();
}

View File

@@ -743,6 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Display Name", "Visualizza nome"),
("password-hidden-tip", "È impostata una password permanente (nascosta)."),
("preset-password-in-use-tip", "È attualmente in uso la password preimpostata."),
("Enable privacy mode", ""),
("Enable privacy mode", "Abilita modalità privacy"),
].iter().cloned().collect();
}

View File

@@ -743,6 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("Display Name", "Отображаемое имя"),
("password-hidden-tip", "Установлен постоянный пароль (скрытый)."),
("preset-password-in-use-tip", "Установленный пароль сейчас используется."),
("Enable privacy mode", ""),
("Enable privacy mode", "Использовать режим конфиденциальности"),
].iter().cloned().collect();
}

View File

@@ -741,8 +741,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
("keep-awake-during-incoming-sessions-label", "Gelen oturumlar süresince ekranıık tutun"),
("Continue with {}", "{} ile devam et"),
("Display Name", "Görünen Ad"),
("password-hidden-tip", "Şifre gizli"),
("preset-password-in-use-tip", "Önceden ayarlanmış şifre kullanılıyor"),
("Enable privacy mode", ""),
("password-hidden-tip", "Parola gizli"),
("preset-password-in-use-tip", "Önceden ayarlanmış parola kullanılıyor"),
("Enable privacy mode", "Gizlilik modunu etkinleştir"),
].iter().cloned().collect();
}

View File

@@ -73,11 +73,17 @@ lazy_static::lazy_static! {
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()));
static ref WAKELOCK_KEEP_AWAKE_OPTION: Arc::<Mutex<Option<bool>>> = Default::default();
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
lazy_static::lazy_static! {
static ref SWITCH_SIDES_UUID: Arc::<Mutex<HashMap<String, (Instant, uuid::Uuid)>>> = Default::default();
static ref PENDING_SWITCH_SIDES_UUID: Arc::<Mutex<HashMap<String, (Instant, uuid::Uuid)>>> = Default::default();
}
fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
@@ -775,6 +781,8 @@ impl Connection {
log::error!("Failed to start portable service from cm: {:?}", e);
}
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
ipc::Data::SwitchSidesBack => {
let mut misc = Misc::new();
misc.set_switch_back(SwitchBack::default());
@@ -2579,6 +2587,7 @@ impl Connection {
}
} else if let Some(message::Union::SwitchSidesResponse(_s)) = msg.union {
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if let Some(lr) = _s.lr.clone().take() {
self.handle_login_request_without_validation(&lr).await;
SWITCH_SIDES_UUID
@@ -3294,8 +3303,13 @@ impl Connection {
}
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
Some(misc::Union::SwitchSidesRequest(s)) => {
if let Ok(uuid) = uuid::Uuid::from_slice(&s.uuid.to_vec()[..]) {
crate::server::insert_pending_switch_sides_uuid(
self.lr.my_id.clone(),
uuid.clone(),
);
crate::run_me(vec![
"--connect",
&self.lr.my_id,
@@ -4938,6 +4952,8 @@ impl Connection {
}
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) {
SWITCH_SIDES_UUID
.lock()
@@ -4945,6 +4961,27 @@ pub fn insert_switch_sides_uuid(id: String, uuid: uuid::Uuid) {
.insert(id, (tokio::time::Instant::now(), uuid));
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn insert_pending_switch_sides_uuid(id: String, uuid: uuid::Uuid) {
let mut uuids = PENDING_SWITCH_SIDES_UUID.lock().unwrap();
uuids.retain(|_, (instant, _)| instant.elapsed() < Duration::from_secs(10));
uuids.insert(id, (tokio::time::Instant::now(), uuid));
}
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn remove_pending_switch_sides_uuid(id: &str, uuid: &uuid::Uuid) -> bool {
let mut uuids = PENDING_SWITCH_SIDES_UUID.lock().unwrap();
uuids.retain(|_, (instant, _)| instant.elapsed() < Duration::from_secs(10));
if uuids.get(id).map(|(_, stored_uuid)| stored_uuid == uuid) == Some(true) {
uuids.remove(id);
true
} else {
false
}
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
async fn start_ipc(
mut rx_to_cm: mpsc::UnboundedReceiver<ipc::Data>,

View File

@@ -464,7 +464,7 @@ pub fn has_active_clients() -> bool {
#[inline]
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "ios")))]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
pub fn switch_back(id: i32) {
if let Some(client) = CLIENTS.read().unwrap().get(&id) {
allow_err!(client.tx.send(Data::SwitchSidesBack));

View File

@@ -1464,10 +1464,11 @@ impl<T: InvokeUiSession> Session<T> {
self.send(Data::ElevateWithLogon(username, password));
}
#[cfg(any(target_os = "ios"))]
#[cfg(any(target_os = "android", target_os = "ios", not(feature = "flutter")))]
pub fn switch_sides(&self) {}
#[cfg(not(any(target_os = "ios")))]
#[cfg(feature = "flutter")]
#[cfg(not(any(target_os = "android", target_os = "ios")))]
#[tokio::main(flavor = "current_thread")]
pub async fn switch_sides(&self) {
match crate::ipc::connect(1000, "").await {