diff --git a/flutter/lib/desktop/widgets/remote_menubar.dart b/flutter/lib/desktop/widgets/remote_menubar.dart index 4f9a227bd..bdf9c1f1d 100644 --- a/flutter/lib/desktop/widgets/remote_menubar.dart +++ b/flutter/lib/desktop/widgets/remote_menubar.dart @@ -598,6 +598,7 @@ class _ControlMenu extends StatelessWidget { hoverColor: _MenubarTheme.hoverBlueColor, ffi: ffi, menuChildren: [ + requestElevation(), osPassword(), transferFile(context), tcpTunneling(context), @@ -611,6 +612,15 @@ class _ControlMenu extends StatelessWidget { ]); } + requestElevation() { + final visible = ffi.elevationModel.showRequestMenu; + if (!visible) return Offstage(); + return _MenuItemButton( + child: Text(translate('Request Elevation')), + ffi: ffi, + onPressed: () => showRequestElevationDialog(id, ffi.dialogManager)); + } + osPassword() { return _MenuItemButton( child: Text(translate('OS Password')), diff --git a/flutter/lib/mobile/widgets/dialog.dart b/flutter/lib/mobile/widgets/dialog.dart index 7e9a9879c..931999382 100644 --- a/flutter/lib/mobile/widgets/dialog.dart +++ b/flutter/lib/mobile/widgets/dialog.dart @@ -374,8 +374,7 @@ void showWaitUacDialog( )); } -void _showRequestElevationDialog( - String id, OverlayDialogManager dialogManager) { +void showRequestElevationDialog(String id, OverlayDialogManager dialogManager) { RxString groupValue = ''.obs; RxString errUser = ''.obs; RxString errPwd = ''.obs; @@ -531,7 +530,7 @@ void showOnBlockDialog( dialogManager.show(tag: '$id-$type', (setState, close) { void submit() { close(); - _showRequestElevationDialog(id, dialogManager); + showRequestElevationDialog(id, dialogManager); } return CustomAlertDialog( @@ -553,7 +552,7 @@ void showElevationError(String id, String type, String title, String text, dialogManager.show(tag: '$id-$type', (setState, close) { void submit() { close(); - _showRequestElevationDialog(id, dialogManager); + showRequestElevationDialog(id, dialogManager); } return CustomAlertDialog( diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index f4efe2f08..eae41679f 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -203,6 +203,8 @@ class FfiModel with ChangeNotifier { final peer_id = evt['peer_id'].toString(); await bind.sessionSwitchSides(id: peer_id); closeConnection(id: peer_id); + } else if (name == 'portable_service_running') { + parent.target?.elevationModel.onPortableServiceRunning(evt); } else if (name == "on_url_scheme_received") { final url = evt['url'].toString(); parseRustdeskUri(url); @@ -439,6 +441,7 @@ class FfiModel with ChangeNotifier { Map features = json.decode(evt['features']); _pi.features.privacyMode = features['privacy_mode'] == 1; handleResolutions(peerId, evt["resolutions"]); + parent.target?.elevationModel.onPeerInfo(_pi); } notifyListeners(); } @@ -1395,6 +1398,21 @@ class RecordingModel with ChangeNotifier { } } +class ElevationModel with ChangeNotifier { + WeakReference parent; + ElevationModel(this.parent); + bool _running = false; + bool _canElevate = false; + bool get showRequestMenu => _canElevate && !_running; + onPeerInfo(PeerInfo pi) { + _canElevate = pi.platform == kPeerPlatformWindows && pi.sasEnabled == false; + } + + onPortableServiceRunning(Map evt) { + _running = evt['running'] == 'true'; + } +} + enum ConnType { defaultConn, fileTransfer, portForward, rdp } /// Flutter state manager and data communication with the Rust core. @@ -1420,6 +1438,7 @@ class FFI { late final QualityMonitorModel qualityMonitorModel; // session late final RecordingModel recordingModel; // session late final InputModel inputModel; // session + late final ElevationModel elevationModel; // session FFI() { imageModel = ImageModel(WeakReference(this)); @@ -1436,6 +1455,7 @@ class FFI { qualityMonitorModel = QualityMonitorModel(WeakReference(this)); recordingModel = RecordingModel(WeakReference(this)); inputModel = InputModel(WeakReference(this)); + elevationModel = ElevationModel(WeakReference(this)); } /// Start with the given [id]. Only transfer file if [isFileTransfer], only port forward if [isPortForward]. diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs index b51c481a5..1c7788193 100644 --- a/src/client/io_loop.rs +++ b/src/client/io_loop.rs @@ -56,6 +56,7 @@ pub struct Remote { data_count: Arc, frame_count: Arc, video_format: CodecFormat, + elevation_requested: bool, } impl Remote { @@ -87,6 +88,7 @@ impl Remote { video_format: CodecFormat::Unknown, stop_voice_call_sender: None, voice_call_request_timestamp: None, + elevation_requested: false, } } @@ -686,6 +688,7 @@ impl Remote { let mut msg = Message::new(); msg.set_misc(misc); allow_err!(peer.send(&msg).await); + self.elevation_requested = true; } Data::ElevateWithLogon(username, password) => { let mut request = ElevationRequest::new(); @@ -699,6 +702,7 @@ impl Remote { let mut msg = Message::new(); msg.set_misc(misc); allow_err!(peer.send(&msg).await); + self.elevation_requested = true; } Data::NewVoiceCall => { let msg = new_voice_call_request(true); @@ -1181,7 +1185,8 @@ impl Remote { } } Some(misc::Union::PortableServiceRunning(b)) => { - if b { + self.handler.portable_service_running(b); + if self.elevation_requested && b { self.handler.msgbox( "custom-nocancel-success", "Successful", @@ -1253,14 +1258,12 @@ impl Remote { } } } - Some(message::Union::PeerInfo(pi)) => { - match pi.conn_id { - crate::SYNC_PEER_INFO_DISPLAYS => { - self.handler.set_displays(&pi.displays); - } - _ => {} + Some(message::Union::PeerInfo(pi)) => match pi.conn_id { + crate::SYNC_PEER_INFO_DISPLAYS => { + self.handler.set_displays(&pi.displays); } - } + _ => {} + }, _ => {} } } diff --git a/src/flutter.rs b/src/flutter.rs index ea73eb925..2f660775f 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -572,6 +572,13 @@ impl InvokeUiSession for FlutterHandler { self.push_event("switch_back", [("peer_id", peer_id)].into()); } + fn portable_service_running(&self, running: bool) { + self.push_event( + "portable_service_running", + [("running", running.to_string().as_str())].into(), + ); + } + fn on_voice_call_started(&self) { self.push_event("on_voice_call_started", [].into()); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 85fcb676b..b2e198681 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1552,7 +1552,6 @@ impl Connection { .err() .map_or("".to_string(), |e| e.to_string()); } - self.portable.elevation_requested = err.is_empty(); let mut misc = Misc::new(); misc.set_elevation_response(err); let mut msg = Message::new(); @@ -1571,7 +1570,6 @@ impl Connection { .err() .map_or("".to_string(), |e| e.to_string()); } - self.portable.elevation_requested = err.is_empty(); let mut misc = Misc::new(); misc.set_elevation_response(err); let mut msg = Message::new(); @@ -1936,13 +1934,11 @@ impl Connection { let p = &mut self.portable; if running != p.last_running { p.last_running = running; - if running && p.elevation_requested { - let mut misc = Misc::new(); - misc.set_portable_service_running(running); - let mut msg = Message::new(); - msg.set_misc(misc); - self.inner.send(msg.into()); - } + let mut misc = Misc::new(); + misc.set_portable_service_running(running); + let mut msg = Message::new(); + msg.set_misc(misc); + self.inner.send(msg.into()); } let uac = crate::video_service::IS_UAC_RUNNING.lock().unwrap().clone(); if p.last_uac != uac { @@ -2166,7 +2162,6 @@ pub struct PortableState { pub last_foreground_window_elevated: bool, pub last_running: bool, pub is_installed: bool, - pub elevation_requested: bool, } #[cfg(windows)] @@ -2177,7 +2172,6 @@ impl Default for PortableState { last_uac: Default::default(), last_foreground_window_elevated: Default::default(), last_running: Default::default(), - elevation_requested: Default::default(), } } } diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 7b31c84e9..c6e0229b2 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -277,6 +277,8 @@ impl InvokeUiSession for SciterHandler { fn switch_back(&self, _id: &str) {} + fn portable_service_running(&self, _running: bool) {} + fn on_voice_call_started(&self) { self.call("onVoiceCallStart", &make_args!()); } diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index fd5a7d9c0..f726ed526 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -805,6 +805,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default { fn clipboard(&self, content: String); fn cancel_msgbox(&self, tag: &str); fn switch_back(&self, id: &str); + fn portable_service_running(&self, running: bool); fn on_voice_call_started(&self); fn on_voice_call_closed(&self, reason: &str); fn on_voice_call_waiting(&self);