mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-05-02 10:16:28 +02:00
feat: option, enable-privacy-mode & enable-perm-change-in-accept-window (#14875)
* feat: option, privacy mode Signed-off-by: fufesou <linlong1266@gmail.com> * feat(privacy mode): update libs/hbb_common Signed-off-by: fufesou <linlong1266@gmail.com> * feat(privacy mode): turn off on disable privacy mode Signed-off-by: fufesou <linlong1266@gmail.com> * feat(privacy mode): better check if supported Signed-off-by: fufesou <linlong1266@gmail.com> * feat(option): enable perm change in accept window Signed-off-by: fufesou <linlong1266@gmail.com> --------- Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
@@ -759,9 +759,18 @@ List<TToggleMenu> toolbarPrivacyMode(
|
|||||||
final ffiModel = ffi.ffiModel;
|
final ffiModel = ffi.ffiModel;
|
||||||
final pi = ffiModel.pi;
|
final pi = ffiModel.pi;
|
||||||
final sessionId = ffi.sessionId;
|
final sessionId = ffi.sessionId;
|
||||||
|
final hasPrivacyModePermission = ffiModel.permissions['privacy_mode'] != false;
|
||||||
|
|
||||||
|
// Backend revocation already attempts to turn privacy mode off.
|
||||||
|
// Still keep this menu when privacy mode is active, so users can turn it off
|
||||||
|
// if there is a sync delay, version mismatch, or off attempt failure.
|
||||||
|
if (!hasPrivacyModePermission && privacyModeState.isEmpty) {
|
||||||
|
return []; // No permission and not active, hide options.
|
||||||
|
}
|
||||||
|
|
||||||
getDefaultMenu(Future<void> Function(SessionID sid, String opt) toggleFunc) {
|
getDefaultMenu(Future<void> Function(SessionID sid, String opt) toggleFunc) {
|
||||||
final enabled = !ffi.ffiModel.viewOnly;
|
final enabled =
|
||||||
|
!ffiModel.viewOnly && (hasPrivacyModePermission || privacyModeState.isNotEmpty);
|
||||||
return TToggleMenu(
|
return TToggleMenu(
|
||||||
value: privacyModeState.isNotEmpty,
|
value: privacyModeState.isNotEmpty,
|
||||||
onChanged: enabled
|
onChanged: enabled
|
||||||
@@ -810,18 +819,29 @@ List<TToggleMenu> toolbarPrivacyMode(
|
|||||||
})
|
})
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
return privacyModeImpls.map((e) {
|
final visibleImpls = hasPrivacyModePermission
|
||||||
|
? privacyModeImpls
|
||||||
|
: privacyModeImpls.where((e) {
|
||||||
|
final implKey = (e as List<dynamic>)[0] as String;
|
||||||
|
return privacyModeState.value == implKey;
|
||||||
|
}).toList();
|
||||||
|
return visibleImpls.map((e) {
|
||||||
final implKey = (e as List<dynamic>)[0] as String;
|
final implKey = (e as List<dynamic>)[0] as String;
|
||||||
final implName = (e)[1] as String;
|
final implName = (e)[1] as String;
|
||||||
|
final enabled = !ffiModel.viewOnly &&
|
||||||
|
(hasPrivacyModePermission || privacyModeState.value == implKey);
|
||||||
return TToggleMenu(
|
return TToggleMenu(
|
||||||
child: Text(translate(implName)),
|
child: Text(translate(implName)),
|
||||||
value: privacyModeState.value == implKey,
|
value: privacyModeState.value == implKey,
|
||||||
onChanged: (value) {
|
onChanged: enabled
|
||||||
if (value == null) return;
|
? (value) {
|
||||||
togglePrivacyModeTime = DateTime.now();
|
if (value == null) return;
|
||||||
bind.sessionTogglePrivacyMode(
|
if (value && !hasPrivacyModePermission) return;
|
||||||
sessionId: sessionId, implKey: implKey, on: value);
|
togglePrivacyModeTime = DateTime.now();
|
||||||
});
|
bind.sessionTogglePrivacyMode(
|
||||||
|
sessionId: sessionId, implKey: implKey, on: value);
|
||||||
|
}
|
||||||
|
: null);
|
||||||
}).toList();
|
}).toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,6 +114,9 @@ const String kOptionTerminalPersistent = "terminal-persistent";
|
|||||||
const String kOptionEnableTunnel = "enable-tunnel";
|
const String kOptionEnableTunnel = "enable-tunnel";
|
||||||
const String kOptionEnableRemoteRestart = "enable-remote-restart";
|
const String kOptionEnableRemoteRestart = "enable-remote-restart";
|
||||||
const String kOptionEnableBlockInput = "enable-block-input";
|
const String kOptionEnableBlockInput = "enable-block-input";
|
||||||
|
const String kOptionEnablePrivacyMode = "enable-privacy-mode";
|
||||||
|
const String kOptionEnablePermChangeInAcceptWindow =
|
||||||
|
"enable-perm-change-in-accept-window";
|
||||||
const String kOptionAllowRemoteConfigModification =
|
const String kOptionAllowRemoteConfigModification =
|
||||||
"allow-remote-config-modification";
|
"allow-remote-config-modification";
|
||||||
const String kOptionVerificationMethod = "verification-method";
|
const String kOptionVerificationMethod = "verification-method";
|
||||||
|
|||||||
@@ -1062,6 +1062,10 @@ class _SafetyState extends State<_Safety> with AutomaticKeepAliveClientMixin {
|
|||||||
_OptionCheckBox(context, 'Enable blocking user input',
|
_OptionCheckBox(context, 'Enable blocking user input',
|
||||||
kOptionEnableBlockInput,
|
kOptionEnableBlockInput,
|
||||||
enabled: enabled, fakeValue: fakeValue),
|
enabled: enabled, fakeValue: fakeValue),
|
||||||
|
if (bind.mainSupportedPrivacyModeImpls() != '[]')
|
||||||
|
_OptionCheckBox(
|
||||||
|
context, 'Enable privacy mode', kOptionEnablePrivacyMode,
|
||||||
|
enabled: enabled, fakeValue: fakeValue),
|
||||||
_OptionCheckBox(context, 'Enable remote configuration modification',
|
_OptionCheckBox(context, 'Enable remote configuration modification',
|
||||||
kOptionAllowRemoteConfigModification,
|
kOptionAllowRemoteConfigModification,
|
||||||
enabled: enabled, fakeValue: fakeValue),
|
enabled: enabled, fakeValue: fakeValue),
|
||||||
|
|||||||
@@ -610,19 +610,24 @@ class _PrivilegeBoard extends StatefulWidget {
|
|||||||
class _PrivilegeBoardState extends State<_PrivilegeBoard> {
|
class _PrivilegeBoardState extends State<_PrivilegeBoard> {
|
||||||
late final client = widget.client;
|
late final client = widget.client;
|
||||||
Widget buildPermissionIcon(bool enabled, IconData iconData,
|
Widget buildPermissionIcon(bool enabled, IconData iconData,
|
||||||
Function(bool)? onTap, String tooltipText) {
|
Function(bool)? onTap, String tooltipText,
|
||||||
|
{required bool canModify}) {
|
||||||
return Tooltip(
|
return Tooltip(
|
||||||
message: "$tooltipText: ${enabled ? "ON" : "OFF"}",
|
message: "$tooltipText: ${enabled ? "ON" : "OFF"}",
|
||||||
waitDuration: Duration.zero,
|
waitDuration: Duration.zero,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: enabled ? MyTheme.accent : Colors.grey[700],
|
color: enabled
|
||||||
|
? (canModify ? MyTheme.accent : MyTheme.accent.withOpacity(0.6))
|
||||||
|
: Colors.grey[700],
|
||||||
borderRadius: BorderRadius.circular(10.0),
|
borderRadius: BorderRadius.circular(10.0),
|
||||||
),
|
),
|
||||||
padding: EdgeInsets.all(8.0),
|
padding: EdgeInsets.all(8.0),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () =>
|
onTap: canModify
|
||||||
checkClickTime(widget.client.id, () => onTap?.call(!enabled)),
|
? () =>
|
||||||
|
checkClickTime(widget.client.id, () => onTap?.call(!enabled))
|
||||||
|
: null,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
children: [
|
children: [
|
||||||
@@ -643,6 +648,9 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final crossAxisCount = 4;
|
final crossAxisCount = 4;
|
||||||
final spacing = 10.0;
|
final spacing = 10.0;
|
||||||
|
final canModifyPermission =
|
||||||
|
bind.mainGetBuildinOption(key: kOptionEnablePermChangeInAcceptWindow) !=
|
||||||
|
'N';
|
||||||
return Container(
|
return Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
height: 160.0,
|
height: 160.0,
|
||||||
@@ -689,6 +697,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
translate('Enable audio'),
|
translate('Enable audio'),
|
||||||
|
canModify: canModifyPermission,
|
||||||
),
|
),
|
||||||
buildPermissionIcon(
|
buildPermissionIcon(
|
||||||
client.recording,
|
client.recording,
|
||||||
@@ -703,6 +712,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
translate('Enable recording session'),
|
translate('Enable recording session'),
|
||||||
|
canModify: canModifyPermission,
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
@@ -719,6 +729,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
translate('Enable keyboard/mouse'),
|
translate('Enable keyboard/mouse'),
|
||||||
|
canModify: canModifyPermission,
|
||||||
),
|
),
|
||||||
buildPermissionIcon(
|
buildPermissionIcon(
|
||||||
client.clipboard,
|
client.clipboard,
|
||||||
@@ -733,6 +744,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
translate('Enable clipboard'),
|
translate('Enable clipboard'),
|
||||||
|
canModify: canModifyPermission,
|
||||||
),
|
),
|
||||||
buildPermissionIcon(
|
buildPermissionIcon(
|
||||||
client.audio,
|
client.audio,
|
||||||
@@ -747,6 +759,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
translate('Enable audio'),
|
translate('Enable audio'),
|
||||||
|
canModify: canModifyPermission,
|
||||||
),
|
),
|
||||||
buildPermissionIcon(
|
buildPermissionIcon(
|
||||||
client.file,
|
client.file,
|
||||||
@@ -761,6 +774,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
translate('Enable file copy and paste'),
|
translate('Enable file copy and paste'),
|
||||||
|
canModify: canModifyPermission,
|
||||||
),
|
),
|
||||||
buildPermissionIcon(
|
buildPermissionIcon(
|
||||||
client.restart,
|
client.restart,
|
||||||
@@ -775,6 +789,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
translate('Enable remote restart'),
|
translate('Enable remote restart'),
|
||||||
|
canModify: canModifyPermission,
|
||||||
),
|
),
|
||||||
buildPermissionIcon(
|
buildPermissionIcon(
|
||||||
client.recording,
|
client.recording,
|
||||||
@@ -789,6 +804,7 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
translate('Enable recording session'),
|
translate('Enable recording session'),
|
||||||
|
canModify: canModifyPermission,
|
||||||
),
|
),
|
||||||
// only windows support block input
|
// only windows support block input
|
||||||
if (isWindows)
|
if (isWindows)
|
||||||
@@ -805,6 +821,23 @@ class _PrivilegeBoardState extends State<_PrivilegeBoard> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
translate('Enable blocking user input'),
|
translate('Enable blocking user input'),
|
||||||
|
canModify: canModifyPermission,
|
||||||
|
),
|
||||||
|
if (bind.mainSupportedPrivacyModeImpls() != '[]')
|
||||||
|
buildPermissionIcon(
|
||||||
|
client.privacyMode,
|
||||||
|
Icons.visibility_off,
|
||||||
|
(enabled) {
|
||||||
|
bind.cmSwitchPermission(
|
||||||
|
connId: client.id,
|
||||||
|
name: "privacy_mode",
|
||||||
|
enabled: enabled);
|
||||||
|
setState(() {
|
||||||
|
client.privacyMode = enabled;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
translate('Enable privacy mode'),
|
||||||
|
canModify: canModifyPermission,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -996,10 +996,10 @@ class _DisplayMenuState extends State<_DisplayMenu> {
|
|||||||
toggles(),
|
toggles(),
|
||||||
];
|
];
|
||||||
// privacy mode
|
// privacy mode
|
||||||
|
final privacyModeState = PrivacyModeState.find(id);
|
||||||
if (ffi.connType == ConnType.defaultConn &&
|
if (ffi.connType == ConnType.defaultConn &&
|
||||||
ffiModel.keyboard &&
|
(pi.features.privacyMode || privacyModeState.isNotEmpty) &&
|
||||||
pi.features.privacyMode) {
|
(ffiModel.keyboard || privacyModeState.isNotEmpty)) {
|
||||||
final privacyModeState = PrivacyModeState.find(id);
|
|
||||||
final privacyModeList =
|
final privacyModeList =
|
||||||
toolbarPrivacyMode(privacyModeState, context, id, ffi);
|
toolbarPrivacyMode(privacyModeState, context, id, ffi);
|
||||||
if (privacyModeList.length == 1) {
|
if (privacyModeList.length == 1) {
|
||||||
|
|||||||
@@ -1183,7 +1183,8 @@ void showOptions(
|
|||||||
List<TToggleMenu> privacyModeList = [];
|
List<TToggleMenu> privacyModeList = [];
|
||||||
// privacy mode
|
// privacy mode
|
||||||
final privacyModeState = PrivacyModeState.find(id);
|
final privacyModeState = PrivacyModeState.find(id);
|
||||||
if (gFFI.ffiModel.keyboard && gFFI.ffiModel.pi.features.privacyMode) {
|
if ((gFFI.ffiModel.pi.features.privacyMode && gFFI.ffiModel.keyboard) ||
|
||||||
|
privacyModeState.isNotEmpty) {
|
||||||
privacyModeList = toolbarPrivacyMode(privacyModeState, context, id, gFFI);
|
privacyModeList = toolbarPrivacyMode(privacyModeState, context, id, gFFI);
|
||||||
if (privacyModeList.length == 1) {
|
if (privacyModeList.length == 1) {
|
||||||
displayToggles.add(privacyModeList[0]);
|
displayToggles.add(privacyModeList[0]);
|
||||||
|
|||||||
@@ -583,9 +583,16 @@ class _PermissionCheckerState extends State<PermissionChecker> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final serverModel = Provider.of<ServerModel>(context);
|
final serverModel = Provider.of<ServerModel>(context);
|
||||||
final hasAudioPermission = androidVersion >= 30;
|
final hasAudioPermission = androidVersion >= 30;
|
||||||
final hideStopService =
|
final hideStopService = isAndroid &&
|
||||||
isAndroid &&
|
bind.mainGetBuildinOption(key: kOptionHideStopService) == 'Y';
|
||||||
bind.mainGetBuildinOption(key: kOptionHideStopService) == 'Y';
|
final allowPermChangeInAcceptWindow = option2bool(
|
||||||
|
kOptionEnablePermChangeInAcceptWindow,
|
||||||
|
bind.mainGetBuildinOption(
|
||||||
|
key: kOptionEnablePermChangeInAcceptWindow,
|
||||||
|
));
|
||||||
|
final permissionChangeLocked = isAndroid &&
|
||||||
|
serverModel.clients.any((c) => !c.disconnected) &&
|
||||||
|
!allowPermChangeInAcceptWindow;
|
||||||
return PaddingCard(
|
return PaddingCard(
|
||||||
title: translate("Permissions"),
|
title: translate("Permissions"),
|
||||||
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
@@ -608,13 +615,21 @@ class _PermissionCheckerState extends State<PermissionChecker> {
|
|||||||
bind.mainGetLocalOption(key: "show-scam-warning") != "N"
|
bind.mainGetLocalOption(key: "show-scam-warning") != "N"
|
||||||
? () => showScamWarning(context, serverModel)
|
? () => showScamWarning(context, serverModel)
|
||||||
: serverModel.toggleService),
|
: serverModel.toggleService),
|
||||||
PermissionRow(translate("Input Control"), serverModel.inputOk,
|
PermissionRow(
|
||||||
serverModel.toggleInput),
|
translate("Input Control"),
|
||||||
PermissionRow(translate("Transfer file"), serverModel.fileOk,
|
serverModel.inputOk,
|
||||||
serverModel.toggleFile),
|
serverModel.toggleInput,
|
||||||
|
),
|
||||||
|
PermissionRow(
|
||||||
|
translate("Transfer file"),
|
||||||
|
serverModel.fileOk,
|
||||||
|
serverModel.toggleFile,
|
||||||
|
enabled: !permissionChangeLocked,
|
||||||
|
),
|
||||||
hasAudioPermission
|
hasAudioPermission
|
||||||
? PermissionRow(translate("Audio Capture"), serverModel.audioOk,
|
? PermissionRow(translate("Audio Capture"), serverModel.audioOk,
|
||||||
serverModel.toggleAudio)
|
serverModel.toggleAudio,
|
||||||
|
enabled: !permissionChangeLocked)
|
||||||
: Row(children: [
|
: Row(children: [
|
||||||
Icon(Icons.info_outline).marginOnly(right: 15),
|
Icon(Icons.info_outline).marginOnly(right: 15),
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -623,19 +638,25 @@ class _PermissionCheckerState extends State<PermissionChecker> {
|
|||||||
style: const TextStyle(color: MyTheme.darkGray),
|
style: const TextStyle(color: MyTheme.darkGray),
|
||||||
))
|
))
|
||||||
]),
|
]),
|
||||||
PermissionRow(translate("Enable clipboard"), serverModel.clipboardOk,
|
PermissionRow(
|
||||||
serverModel.toggleClipboard),
|
translate("Enable clipboard"),
|
||||||
|
serverModel.clipboardOk,
|
||||||
|
serverModel.toggleClipboard,
|
||||||
|
enabled: !permissionChangeLocked,
|
||||||
|
),
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PermissionRow extends StatelessWidget {
|
class PermissionRow extends StatelessWidget {
|
||||||
const PermissionRow(this.name, this.isOk, this.onPressed, {Key? key})
|
const PermissionRow(this.name, this.isOk, this.onPressed,
|
||||||
|
{Key? key, this.enabled = true})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
final String name;
|
final String name;
|
||||||
final bool isOk;
|
final bool isOk;
|
||||||
final VoidCallback onPressed;
|
final VoidCallback onPressed;
|
||||||
|
final bool enabled;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -644,9 +665,11 @@ class PermissionRow extends StatelessWidget {
|
|||||||
contentPadding: EdgeInsets.all(0),
|
contentPadding: EdgeInsets.all(0),
|
||||||
title: Text(name),
|
title: Text(name),
|
||||||
value: isOk,
|
value: isOk,
|
||||||
onChanged: (bool value) {
|
onChanged: enabled
|
||||||
onPressed();
|
? (bool value) {
|
||||||
});
|
onPressed();
|
||||||
|
}
|
||||||
|
: null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggleAudio() async {
|
toggleAudio() async {
|
||||||
if (clients.isNotEmpty) {
|
if (clients.any((c) => !c.disconnected)) {
|
||||||
await showClientsMayNotBeChangedAlert(parent.target);
|
await showClientsMayNotBeChangedAlert(parent.target);
|
||||||
}
|
}
|
||||||
if (!_audioOk && !await AndroidPermissionManager.check(kRecordAudio)) {
|
if (!_audioOk && !await AndroidPermissionManager.check(kRecordAudio)) {
|
||||||
@@ -316,7 +316,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggleFile() async {
|
toggleFile() async {
|
||||||
if (clients.isNotEmpty) {
|
if (clients.any((c) => !c.disconnected)) {
|
||||||
await showClientsMayNotBeChangedAlert(parent.target);
|
await showClientsMayNotBeChangedAlert(parent.target);
|
||||||
}
|
}
|
||||||
if (!_fileOk &&
|
if (!_fileOk &&
|
||||||
@@ -345,7 +345,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggleInput() async {
|
toggleInput() async {
|
||||||
if (clients.isNotEmpty) {
|
if (clients.any((c) => !c.disconnected)) {
|
||||||
await showClientsMayNotBeChangedAlert(parent.target);
|
await showClientsMayNotBeChangedAlert(parent.target);
|
||||||
}
|
}
|
||||||
if (_inputOk) {
|
if (_inputOk) {
|
||||||
@@ -549,10 +549,19 @@ class ServerModel with ChangeNotifier {
|
|||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
_clients.add(client);
|
_clients.add(client);
|
||||||
} else {
|
} else {
|
||||||
|
if (_clients[index].authorized) {
|
||||||
|
_clients[index].privacyMode = client.privacyMode;
|
||||||
|
notifyListeners();
|
||||||
|
return;
|
||||||
|
}
|
||||||
_clients[index].authorized = true;
|
_clients[index].authorized = true;
|
||||||
|
_clients[index].privacyMode = client.privacyMode;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (_clients.any((c) => c.id == client.id)) {
|
final index = _clients.indexWhere((c) => c.id == client.id);
|
||||||
|
if (index >= 0) {
|
||||||
|
_clients[index].privacyMode = client.privacyMode;
|
||||||
|
notifyListeners();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_clients.add(client);
|
_clients.add(client);
|
||||||
@@ -818,6 +827,7 @@ class Client {
|
|||||||
bool restart = false;
|
bool restart = false;
|
||||||
bool recording = false;
|
bool recording = false;
|
||||||
bool blockInput = false;
|
bool blockInput = false;
|
||||||
|
bool privacyMode = false;
|
||||||
bool disconnected = false;
|
bool disconnected = false;
|
||||||
bool fromSwitch = false;
|
bool fromSwitch = false;
|
||||||
bool inVoiceCall = false;
|
bool inVoiceCall = false;
|
||||||
@@ -846,6 +856,7 @@ class Client {
|
|||||||
restart = json['restart'];
|
restart = json['restart'];
|
||||||
recording = json['recording'];
|
recording = json['recording'];
|
||||||
blockInput = json['block_input'];
|
blockInput = json['block_input'];
|
||||||
|
privacyMode = json['privacy_mode'] ?? privacyMode;
|
||||||
disconnected = json['disconnected'];
|
disconnected = json['disconnected'];
|
||||||
fromSwitch = json['from_switch'];
|
fromSwitch = json['from_switch'];
|
||||||
inVoiceCall = json['in_voice_call'];
|
inVoiceCall = json['in_voice_call'];
|
||||||
@@ -870,6 +881,7 @@ class Client {
|
|||||||
data['restart'] = restart;
|
data['restart'] = restart;
|
||||||
data['recording'] = recording;
|
data['recording'] = recording;
|
||||||
data['block_input'] = blockInput;
|
data['block_input'] = blockInput;
|
||||||
|
data['privacy_mode'] = privacyMode;
|
||||||
data['disconnected'] = disconnected;
|
data['disconnected'] = disconnected;
|
||||||
data['from_switch'] = fromSwitch;
|
data['from_switch'] = fromSwitch;
|
||||||
data['in_voice_call'] = inVoiceCall;
|
data['in_voice_call'] = inVoiceCall;
|
||||||
|
|||||||
@@ -1729,7 +1729,7 @@ class RustdeskImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String mainSupportedPrivacyModeImpls({dynamic hint}) {
|
String mainSupportedPrivacyModeImpls({dynamic hint}) {
|
||||||
throw UnimplementedError("mainSupportedPrivacyModeImpls");
|
return '[]';
|
||||||
}
|
}
|
||||||
|
|
||||||
String mainSupportedInputSource({dynamic hint}) {
|
String mainSupportedInputSource({dynamic hint}) {
|
||||||
|
|||||||
Submodule libs/hbb_common updated: 87b11a7959...3e31a94939
@@ -1797,6 +1797,9 @@ impl<T: InvokeUiSession> Remote<T> {
|
|||||||
Ok(Permission::BlockInput) => {
|
Ok(Permission::BlockInput) => {
|
||||||
self.handler.set_permission("block_input", p.enabled);
|
self.handler.set_permission("block_input", p.enabled);
|
||||||
}
|
}
|
||||||
|
Ok(Permission::PrivacyMode) => {
|
||||||
|
self.handler.set_permission("privacy_mode", p.enabled);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -972,6 +972,27 @@ pub fn main_show_option(_key: String) -> SyncReturn<bool> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_set_option(key: String, value: String) {
|
pub fn main_set_option(key: String, value: String) {
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
{
|
||||||
|
let is_permission_option = key.eq(config::keys::OPTION_ENABLE_CLIPBOARD)
|
||||||
|
|| key.eq(config::keys::OPTION_ENABLE_FILE_TRANSFER)
|
||||||
|
|| key.eq(config::keys::OPTION_ENABLE_AUDIO);
|
||||||
|
let allow_perm_change_in_accept_window = config::option2bool(
|
||||||
|
config::keys::OPTION_ENABLE_PERM_CHANGE_IN_ACCEPT_WINDOW,
|
||||||
|
&crate::get_builtin_option(config::keys::OPTION_ENABLE_PERM_CHANGE_IN_ACCEPT_WINDOW),
|
||||||
|
);
|
||||||
|
if is_permission_option
|
||||||
|
&& !allow_perm_change_in_accept_window
|
||||||
|
&& crate::ui_cm_interface::has_active_clients()
|
||||||
|
{
|
||||||
|
log::info!(
|
||||||
|
"blocked main_set_option by policy, key={}, value={}",
|
||||||
|
key,
|
||||||
|
value
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
if key.eq(config::keys::OPTION_ENABLE_KEYBOARD) {
|
if key.eq(config::keys::OPTION_ENABLE_KEYBOARD) {
|
||||||
crate::ui_cm_interface::switch_permission_all(
|
crate::ui_cm_interface::switch_permission_all(
|
||||||
@@ -1019,7 +1040,29 @@ pub fn main_get_options_sync() -> SyncReturn<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn main_set_options(json: String) {
|
pub fn main_set_options(json: String) {
|
||||||
let map: HashMap<String, String> = serde_json::from_str(&json).unwrap_or(HashMap::new());
|
let mut map: HashMap<String, String> = serde_json::from_str(&json).unwrap_or(HashMap::new());
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
{
|
||||||
|
let allow_perm_change_in_accept_window = config::option2bool(
|
||||||
|
config::keys::OPTION_ENABLE_PERM_CHANGE_IN_ACCEPT_WINDOW,
|
||||||
|
&crate::get_builtin_option(config::keys::OPTION_ENABLE_PERM_CHANGE_IN_ACCEPT_WINDOW),
|
||||||
|
);
|
||||||
|
if !allow_perm_change_in_accept_window && crate::ui_cm_interface::has_active_clients() {
|
||||||
|
for key in [
|
||||||
|
config::keys::OPTION_ENABLE_CLIPBOARD,
|
||||||
|
config::keys::OPTION_ENABLE_FILE_TRANSFER,
|
||||||
|
config::keys::OPTION_ENABLE_AUDIO,
|
||||||
|
] {
|
||||||
|
if let Some(value) = map.remove(key) {
|
||||||
|
log::info!(
|
||||||
|
"blocked main_set_options item by policy, key={}, value={}",
|
||||||
|
key,
|
||||||
|
value
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if !map.is_empty() {
|
if !map.is_empty() {
|
||||||
set_options(map)
|
set_options(map)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -237,6 +237,7 @@ pub enum Data {
|
|||||||
restart: bool,
|
restart: bool,
|
||||||
recording: bool,
|
recording: bool,
|
||||||
block_input: bool,
|
block_input: bool,
|
||||||
|
privacy_mode: bool,
|
||||||
from_switch: bool,
|
from_switch: bool,
|
||||||
},
|
},
|
||||||
ChatMessage {
|
ChatMessage {
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "اسم العرض"),
|
("Display Name", "اسم العرض"),
|
||||||
("password-hidden-tip", "كلمة المرور مخفية"),
|
("password-hidden-tip", "كلمة المرور مخفية"),
|
||||||
("preset-password-in-use-tip", "كلمة المرور المحددة مسبقًا قيد الاستخدام"),
|
("preset-password-in-use-tip", "كلمة المرور المحددة مسبقًا قيد الاستخدام"),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "Імя для адлюстравання"),
|
("Display Name", "Імя для адлюстравання"),
|
||||||
("password-hidden-tip", "Зададзены пастаянны пароль (скрыты)."),
|
("password-hidden-tip", "Зададзены пастаянны пароль (скрыты)."),
|
||||||
("preset-password-in-use-tip", "Пададзены пароль цяпер выкарыстоўваецца"),
|
("preset-password-in-use-tip", "Пададзены пароль цяпер выкарыстоўваецца"),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "显示名称"),
|
("Display Name", "显示名称"),
|
||||||
("password-hidden-tip", "永久密码已设置(已隐藏)"),
|
("password-hidden-tip", "永久密码已设置(已隐藏)"),
|
||||||
("preset-password-in-use-tip", "当前使用预设密码"),
|
("preset-password-in-use-tip", "当前使用预设密码"),
|
||||||
|
("Enable privacy mode", "允许隐私模式"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "Anzeigename"),
|
("Display Name", "Anzeigename"),
|
||||||
("password-hidden-tip", "Ein permanentes Passwort wurde festgelegt (ausgeblendet)."),
|
("password-hidden-tip", "Ein permanentes Passwort wurde festgelegt (ausgeblendet)."),
|
||||||
("preset-password-in-use-tip", "Das voreingestellte Passwort wird derzeit verwendet."),
|
("preset-password-in-use-tip", "Das voreingestellte Passwort wird derzeit verwendet."),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "Εμφανιζόμενο όνομα"),
|
("Display Name", "Εμφανιζόμενο όνομα"),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "Nom d’affichage"),
|
("Display Name", "Nom d’affichage"),
|
||||||
("password-hidden-tip", "Le mot de passe permanent est défini (masqué)."),
|
("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é."),
|
("preset-password-in-use-tip", "Le mot de passe prédéfini est actuellement utilisé."),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -742,5 +742,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "ડિસ્પ્લે નામ"),
|
("Display Name", "ડિસ્પ્લે નામ"),
|
||||||
("password-hidden-tip", "સુરક્ષા માટે પાસવર્ડ છુપાવેલ છે."),
|
("password-hidden-tip", "સુરક્ષા માટે પાસવર્ડ છુપાવેલ છે."),
|
||||||
("preset-password-in-use-tip", "પ્રીસેટ પાસવર્ડ વપરાશમાં છે."),
|
("preset-password-in-use-tip", "પ્રીસેટ પાસવર્ડ વપરાશમાં છે."),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "Kijelző név"),
|
("Display Name", "Kijelző név"),
|
||||||
("password-hidden-tip", "Állandó jelszó lett beállítva (rejtett)."),
|
("password-hidden-tip", "Állandó jelszó lett beállítva (rejtett)."),
|
||||||
("preset-password-in-use-tip", "Jelenleg az alapértelmezett jelszót használja."),
|
("preset-password-in-use-tip", "Jelenleg az alapértelmezett jelszót használja."),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "Visualizza nome"),
|
("Display Name", "Visualizza nome"),
|
||||||
("password-hidden-tip", "È impostata una password permanente (nascosta)."),
|
("password-hidden-tip", "È impostata una password permanente (nascosta)."),
|
||||||
("preset-password-in-use-tip", "È attualmente in uso la password preimpostata."),
|
("preset-password-in-use-tip", "È attualmente in uso la password preimpostata."),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "表示名"),
|
("Display Name", "表示名"),
|
||||||
("password-hidden-tip", "永続的なパスワードが設定されています (非表示)"),
|
("password-hidden-tip", "永続的なパスワードが設定されています (非表示)"),
|
||||||
("preset-password-in-use-tip", "プリセットパスワードが現在使用されています"),
|
("preset-password-in-use-tip", "プリセットパスワードが現在使用されています"),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "표시 이름"),
|
("Display Name", "표시 이름"),
|
||||||
("password-hidden-tip", "영구 비밀번호가 설정되었습니다 (숨김)."),
|
("password-hidden-tip", "영구 비밀번호가 설정되었습니다 (숨김)."),
|
||||||
("preset-password-in-use-tip", "현재 사전 설정된 비밀번호가 사용 중입니다."),
|
("preset-password-in-use-tip", "현재 사전 설정된 비밀번호가 사용 중입니다."),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "Naam Weergeven"),
|
("Display Name", "Naam Weergeven"),
|
||||||
("password-hidden-tip", "Er is een permanent wachtwoord ingesteld (verborgen)."),
|
("password-hidden-tip", "Er is een permanent wachtwoord ingesteld (verborgen)."),
|
||||||
("preset-password-in-use-tip", "Het basis wachtwoord is momenteel in gebruik."),
|
("preset-password-in-use-tip", "Het basis wachtwoord is momenteel in gebruik."),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "Nazwa wyświetlana"),
|
("Display Name", "Nazwa wyświetlana"),
|
||||||
("password-hidden-tip", "Ustawiono (ukryto) stare hasło."),
|
("password-hidden-tip", "Ustawiono (ukryto) stare hasło."),
|
||||||
("preset-password-in-use-tip", "Obecnie używane jest hasło domyślne."),
|
("preset-password-in-use-tip", "Obecnie używane jest hasło domyślne."),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "Nume afișat"),
|
("Display Name", "Nume afișat"),
|
||||||
("password-hidden-tip", "Parola este ascunsă din motive de securitate. Fă clic pe pictograma ochiului pentru a o afișa."),
|
("password-hidden-tip", "Parola este ascunsă din motive de securitate. Fă clic pe pictograma ochiului pentru a o afișa."),
|
||||||
("preset-password-in-use-tip", "Se folosește o parolă prestabilită. Se recomandă setarea unei parole personalizate pentru securitate sporită."),
|
("preset-password-in-use-tip", "Se folosește o parolă prestabilită. Se recomandă setarea unei parole personalizate pentru securitate sporită."),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "Отображаемое имя"),
|
("Display Name", "Отображаемое имя"),
|
||||||
("password-hidden-tip", "Установлен постоянный пароль (скрытый)."),
|
("password-hidden-tip", "Установлен постоянный пароль (скрытый)."),
|
||||||
("preset-password-in-use-tip", "Установленный пароль сейчас используется."),
|
("preset-password-in-use-tip", "Установленный пароль сейчас используется."),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "Görünen Ad"),
|
("Display Name", "Görünen Ad"),
|
||||||
("password-hidden-tip", "Şifre gizli"),
|
("password-hidden-tip", "Şifre gizli"),
|
||||||
("preset-password-in-use-tip", "Önceden ayarlanmış şifre kullanılıyor"),
|
("preset-password-in-use-tip", "Önceden ayarlanmış şifre kullanılıyor"),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", "顯示名稱"),
|
("Display Name", "顯示名稱"),
|
||||||
("password-hidden-tip", "固定密碼已設定(已隱藏)"),
|
("password-hidden-tip", "固定密碼已設定(已隱藏)"),
|
||||||
("preset-password-in-use-tip", "目前正在使用預設密碼"),
|
("preset-password-in-use-tip", "目前正在使用預設密碼"),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -743,5 +743,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Display Name", ""),
|
("Display Name", ""),
|
||||||
("password-hidden-tip", ""),
|
("password-hidden-tip", ""),
|
||||||
("preset-password-in-use-tip", ""),
|
("preset-password-in-use-tip", ""),
|
||||||
|
("Enable privacy mode", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -241,6 +241,7 @@ pub struct Connection {
|
|||||||
restart: bool,
|
restart: bool,
|
||||||
recording: bool,
|
recording: bool,
|
||||||
block_input: bool,
|
block_input: bool,
|
||||||
|
privacy_mode: bool,
|
||||||
control_permissions: Option<ControlPermissions>,
|
control_permissions: Option<ControlPermissions>,
|
||||||
last_test_delay: Option<Instant>,
|
last_test_delay: Option<Instant>,
|
||||||
network_delay: u32,
|
network_delay: u32,
|
||||||
@@ -431,6 +432,7 @@ impl Connection {
|
|||||||
restart: Self::permission(keys::OPTION_ENABLE_REMOTE_RESTART, &control_permissions),
|
restart: Self::permission(keys::OPTION_ENABLE_REMOTE_RESTART, &control_permissions),
|
||||||
recording: Self::permission(keys::OPTION_ENABLE_RECORD_SESSION, &control_permissions),
|
recording: Self::permission(keys::OPTION_ENABLE_RECORD_SESSION, &control_permissions),
|
||||||
block_input: Self::permission(keys::OPTION_ENABLE_BLOCK_INPUT, &control_permissions),
|
block_input: Self::permission(keys::OPTION_ENABLE_BLOCK_INPUT, &control_permissions),
|
||||||
|
privacy_mode: Self::permission(keys::OPTION_ENABLE_PRIVACY_MODE, &control_permissions),
|
||||||
control_permissions,
|
control_permissions,
|
||||||
last_test_delay: None,
|
last_test_delay: None,
|
||||||
network_delay: 0,
|
network_delay: 0,
|
||||||
@@ -527,6 +529,9 @@ impl Connection {
|
|||||||
if !conn.block_input {
|
if !conn.block_input {
|
||||||
conn.send_permission(Permission::BlockInput, false).await;
|
conn.send_permission(Permission::BlockInput, false).await;
|
||||||
}
|
}
|
||||||
|
if !conn.privacy_mode {
|
||||||
|
conn.send_permission(Permission::PrivacyMode, false).await;
|
||||||
|
}
|
||||||
let mut test_delay_timer =
|
let mut test_delay_timer =
|
||||||
crate::rustdesk_interval(time::interval_at(Instant::now(), TEST_DELAY_TIMEOUT));
|
crate::rustdesk_interval(time::interval_at(Instant::now(), TEST_DELAY_TIMEOUT));
|
||||||
let mut last_recv_time = Instant::now();
|
let mut last_recv_time = Instant::now();
|
||||||
@@ -674,6 +679,46 @@ impl Connection {
|
|||||||
} else if &name == "block_input" {
|
} else if &name == "block_input" {
|
||||||
conn.block_input = enabled;
|
conn.block_input = enabled;
|
||||||
conn.send_permission(Permission::BlockInput, enabled).await;
|
conn.send_permission(Permission::BlockInput, enabled).await;
|
||||||
|
} else if &name == "privacy_mode" {
|
||||||
|
// Keep permission state and runtime state consistent:
|
||||||
|
// when revoking the permission, try to leave privacy mode first.
|
||||||
|
// Otherwise we could end up in an inconsistent state where
|
||||||
|
// permission looks disabled while privacy mode is still active.
|
||||||
|
if !enabled && privacy_mode::is_in_privacy_mode() {
|
||||||
|
if let Some(conn_id) = privacy_mode::get_privacy_mode_conn_id() {
|
||||||
|
if conn_id == conn.inner.id() {
|
||||||
|
let impl_key =
|
||||||
|
privacy_mode::get_cur_impl_key().unwrap_or_default();
|
||||||
|
let turn_off_res =
|
||||||
|
privacy_mode::turn_off_privacy(conn_id, None);
|
||||||
|
match turn_off_res {
|
||||||
|
Some(Ok(_)) => {
|
||||||
|
let msg_out = crate::common::make_privacy_mode_msg(
|
||||||
|
back_notification::PrivacyModeState::PrvOffByPeer,
|
||||||
|
impl_key.clone(),
|
||||||
|
);
|
||||||
|
conn.send(msg_out).await;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let msg_out = Self::turn_off_privacy_result_to_msg(
|
||||||
|
turn_off_res,
|
||||||
|
impl_key,
|
||||||
|
);
|
||||||
|
conn.send(msg_out).await;
|
||||||
|
// Turn-off failed, so revert CM's optimistic toggle
|
||||||
|
// and keep the previous permission value.
|
||||||
|
conn.send_to_cm(ipc::Data::SwitchPermission {
|
||||||
|
name: "privacy_mode".to_owned(),
|
||||||
|
enabled: conn.privacy_mode,
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
conn.privacy_mode = enabled;
|
||||||
|
conn.send_permission(Permission::PrivacyMode, enabled).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ipc::Data::RawMessage(bytes) => {
|
ipc::Data::RawMessage(bytes) => {
|
||||||
@@ -978,7 +1023,7 @@ impl Connection {
|
|||||||
|
|
||||||
if let Some(video_privacy_conn_id) = privacy_mode::get_privacy_mode_conn_id() {
|
if let Some(video_privacy_conn_id) = privacy_mode::get_privacy_mode_conn_id() {
|
||||||
if video_privacy_conn_id == id {
|
if video_privacy_conn_id == id {
|
||||||
let _ = Self::turn_off_privacy_to_msg(id);
|
let _ = Self::turn_off_privacy_to_msg(id, String::new());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
|
#[cfg(all(feature = "flutter", feature = "plugin_framework"))]
|
||||||
@@ -1900,6 +1945,7 @@ impl Connection {
|
|||||||
restart: self.restart,
|
restart: self.restart,
|
||||||
recording: self.recording,
|
recording: self.recording,
|
||||||
block_input: self.block_input,
|
block_input: self.block_input,
|
||||||
|
privacy_mode: self.privacy_mode,
|
||||||
from_switch: self.from_switch,
|
from_switch: self.from_switch,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2175,6 +2221,7 @@ impl Connection {
|
|||||||
keys::OPTION_ENABLE_REMOTE_RESTART => Some(Permission::restart),
|
keys::OPTION_ENABLE_REMOTE_RESTART => Some(Permission::restart),
|
||||||
keys::OPTION_ENABLE_RECORD_SESSION => Some(Permission::recording),
|
keys::OPTION_ENABLE_RECORD_SESSION => Some(Permission::recording),
|
||||||
keys::OPTION_ENABLE_BLOCK_INPUT => Some(Permission::block_input),
|
keys::OPTION_ENABLE_BLOCK_INPUT => Some(Permission::block_input),
|
||||||
|
keys::OPTION_ENABLE_PRIVACY_MODE => Some(Permission::privacy_mode),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some(permission) = permission {
|
if let Some(permission) = permission {
|
||||||
@@ -4145,6 +4192,15 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn turn_on_privacy(&mut self, impl_key: String) {
|
async fn turn_on_privacy(&mut self, impl_key: String) {
|
||||||
|
if !self.is_authed_remote_conn() || !self.privacy_mode {
|
||||||
|
let msg_out = crate::common::make_privacy_mode_msg(
|
||||||
|
back_notification::PrivacyModeState::PrvOnFailedDenied,
|
||||||
|
impl_key,
|
||||||
|
);
|
||||||
|
self.send(msg_out).await;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let msg_out = if !privacy_mode::is_privacy_mode_supported() {
|
let msg_out = if !privacy_mode::is_privacy_mode_supported() {
|
||||||
crate::common::make_privacy_mode_msg_with_details(
|
crate::common::make_privacy_mode_msg_with_details(
|
||||||
back_notification::PrivacyModeState::PrvNotSupported,
|
back_notification::PrivacyModeState::PrvNotSupported,
|
||||||
@@ -4186,7 +4242,7 @@ impl Connection {
|
|||||||
"Check privacy mode failed: {}, turn off privacy mode.",
|
"Check privacy mode failed: {}, turn off privacy mode.",
|
||||||
&err_msg
|
&err_msg
|
||||||
);
|
);
|
||||||
let _ = Self::turn_off_privacy_to_msg(self.inner.id);
|
let _ = Self::turn_off_privacy_to_msg(self.inner.id, String::new());
|
||||||
crate::common::make_privacy_mode_msg_with_details(
|
crate::common::make_privacy_mode_msg_with_details(
|
||||||
back_notification::PrivacyModeState::PrvOnFailed,
|
back_notification::PrivacyModeState::PrvOnFailed,
|
||||||
err_msg,
|
err_msg,
|
||||||
@@ -4205,6 +4261,7 @@ impl Connection {
|
|||||||
if privacy_mode::is_in_privacy_mode() {
|
if privacy_mode::is_in_privacy_mode() {
|
||||||
let _ = Self::turn_off_privacy_to_msg(
|
let _ = Self::turn_off_privacy_to_msg(
|
||||||
privacy_mode::INVALID_PRIVACY_MODE_CONN_ID,
|
privacy_mode::INVALID_PRIVACY_MODE_CONN_ID,
|
||||||
|
String::new(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
crate::common::make_privacy_mode_msg_with_details(
|
crate::common::make_privacy_mode_msg_with_details(
|
||||||
@@ -4232,14 +4289,23 @@ impl Connection {
|
|||||||
impl_key,
|
impl_key,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Self::turn_off_privacy_to_msg(self.inner.id)
|
Self::turn_off_privacy_to_msg(self.inner.id, impl_key)
|
||||||
};
|
};
|
||||||
self.send(msg_out).await;
|
self.send(msg_out).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn turn_off_privacy_to_msg(_conn_id: i32) -> Message {
|
pub fn turn_off_privacy_to_msg(_conn_id: i32, impl_key: String) -> Message {
|
||||||
let impl_key = "".to_owned();
|
Self::turn_off_privacy_result_to_msg(
|
||||||
match privacy_mode::turn_off_privacy(_conn_id, None) {
|
privacy_mode::turn_off_privacy(_conn_id, None),
|
||||||
|
impl_key,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_off_privacy_result_to_msg(
|
||||||
|
turn_off_res: Option<hbb_common::ResultType<()>>,
|
||||||
|
impl_key: String,
|
||||||
|
) -> Message {
|
||||||
|
match turn_off_res {
|
||||||
Some(Ok(_)) => crate::common::make_privacy_mode_msg(
|
Some(Ok(_)) => crate::common::make_privacy_mode_msg(
|
||||||
back_notification::PrivacyModeState::PrvOffSucceeded,
|
back_notification::PrivacyModeState::PrvOffSucceeded,
|
||||||
impl_key,
|
impl_key,
|
||||||
|
|||||||
@@ -372,6 +372,11 @@ impl UI {
|
|||||||
is_installed()
|
is_installed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_supported_privacy_mode_impls(&self) -> String {
|
||||||
|
serde_json::to_string(&crate::privacy_mode::get_supported_privacy_mode_impl())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
fn is_root(&self) -> bool {
|
fn is_root(&self) -> bool {
|
||||||
is_root()
|
is_root()
|
||||||
}
|
}
|
||||||
@@ -752,6 +757,7 @@ impl sciter::EventHandler for UI {
|
|||||||
fn get_icon();
|
fn get_icon();
|
||||||
fn install_me(String, String);
|
fn install_me(String, String);
|
||||||
fn is_installed();
|
fn is_installed();
|
||||||
|
fn get_supported_privacy_mode_impls();
|
||||||
fn is_root();
|
fn is_root();
|
||||||
fn is_release();
|
fn is_release();
|
||||||
fn set_socks(String, String, String);
|
fn set_socks(String, String, String);
|
||||||
|
|||||||
@@ -93,6 +93,13 @@ div.permissions > div:active {
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.permissions.locked,
|
||||||
|
div.permissions.locked *,
|
||||||
|
div.permissions.locked > div:active {
|
||||||
|
cursor: default !important;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
icon.keyboard {
|
icon.keyboard {
|
||||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAgVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9d3yJTAAAAKnRSTlMA0Gd/0y8ILZgbJffDPUwV2nvzt+TMqZxyU7CMb1pYQyzsvKunkXE4AwJnNC24AAAA+0lEQVQ4y83O2U7DMBCF4ZMxk9rZk26kpQs7nPd/QJy4EiLbLf01N5Y/2YP/qxDFQvGB5NPC/ZpVnfJx4b5xyGfF95rkHvNCWH1u+N6J6T0sC7gqRy8uGPfBLEbozPXUjlkQKwGaFPNizwQbwkx0TDvhCii34ExZCSQVBdzIOEOyeclSHgBGXkpeygXSQgStACtWx4Z8rr8COHOvfEP/IbbsQAToFUAAV1M408IIjIGYAPoCSNRP7DQutfQTqxuAiH7UUg1FaJR2AGrrx52sK2ye28LZ0wBAEyR6y8X+NADhm1B4fgiiHXbRrTrxpwEY9RdM9wsepnvFHfUDwYEeiwAJr/gAAAAASUVORK5CYII=');
|
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAgVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9d3yJTAAAAKnRSTlMA0Gd/0y8ILZgbJffDPUwV2nvzt+TMqZxyU7CMb1pYQyzsvKunkXE4AwJnNC24AAAA+0lEQVQ4y83O2U7DMBCF4ZMxk9rZk26kpQs7nPd/QJy4EiLbLf01N5Y/2YP/qxDFQvGB5NPC/ZpVnfJx4b5xyGfF95rkHvNCWH1u+N6J6T0sC7gqRy8uGPfBLEbozPXUjlkQKwGaFPNizwQbwkx0TDvhCii34ExZCSQVBdzIOEOyeclSHgBGXkpeygXSQgStACtWx4Z8rr8COHOvfEP/IbbsQAToFUAAV1M408IIjIGYAPoCSNRP7DQutfQTqxuAiH7UUg1FaJR2AGrrx52sK2ye28LZ0wBAEyR6y8X+NADhm1B4fgiiHXbRrTrxpwEY9RdM9wsepnvFHfUDwYEeiwAJr/gAAAAASUVORK5CYII=');
|
||||||
}
|
}
|
||||||
@@ -121,6 +128,10 @@ icon.block_input {
|
|||||||
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAjdJREFUWEe1V8tNAzEQfXOHAx2QG0UgQSqBFIIgHdABoQqOhBq4cCMlcMh90FvZq/HEXtvJxlKUZNceP783no+gY6jqNYBHAHcA+JufXTDBb37eRWTbalZqE82mz7W55v0ABMBGRCLA7PJJAKr6AiC3sT11NHyf2SEyQjvtAMKp3wBYo9VTGbYegjxxU65d5tg4YEBVbwF8ALgw2lLX4in80QqyZUEkAMLCb7P5n4hcdWifTA32Pg0bByA8AE4+oL3n9A1s7ERkEeeNAJzD/QC4OVaCAgjrU7wdK86zAHREJSKqyvvORRxVb67JFOT4NfYGpxwAqCo34oYcKxHZhOdzg7D2BhYigHj6RJ+5QbjrPezlqR61sZTOKYfztSUBWPoXpdA5FwjnC2sCGK+eiNRC8yw+oap0RiayLQHEPwf65zx7DibMoXcEEB0wq/85QJQAbEVkWbvP8f0pTFi/65ZgjtuRyJ7QYWL0OZnwTmiLDobH5nLqGDlUlcmON49jQwnsg/Wxma/VJ1zcGQIR7+OYJGyqbJWhhwlDPxh3JpNRL4Ba7nAsJckoYaFUv7UCyslBvQ3TNDWEfVsPJGH2FCkKTPAxD8ox+poFwJfZqqX15H6eYyK+TgJeriidLCJ7wAQHZ4Udy7u9iFxaG7mynEx4EF1leZDANzV7AE8i8joJICz2cvBxbExIYTZYTTQmxTxTzP+VnvC8rZlLOLEj7m5OW6JqtTs2US6247Hvy7XnX0OV05FP/gHde5fLZaGS8AAAAABJRU5ErkJggg==');
|
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAjdJREFUWEe1V8tNAzEQfXOHAx2QG0UgQSqBFIIgHdABoQqOhBq4cCMlcMh90FvZq/HEXtvJxlKUZNceP783no+gY6jqNYBHAHcA+JufXTDBb37eRWTbalZqE82mz7W55v0ABMBGRCLA7PJJAKr6AiC3sT11NHyf2SEyQjvtAMKp3wBYo9VTGbYegjxxU65d5tg4YEBVbwF8ALgw2lLX4in80QqyZUEkAMLCb7P5n4hcdWifTA32Pg0bByA8AE4+oL3n9A1s7ERkEeeNAJzD/QC4OVaCAgjrU7wdK86zAHREJSKqyvvORRxVb67JFOT4NfYGpxwAqCo34oYcKxHZhOdzg7D2BhYigHj6RJ+5QbjrPezlqR61sZTOKYfztSUBWPoXpdA5FwjnC2sCGK+eiNRC8yw+oap0RiayLQHEPwf65zx7DibMoXcEEB0wq/85QJQAbEVkWbvP8f0pTFi/65ZgjtuRyJ7QYWL0OZnwTmiLDobH5nLqGDlUlcmON49jQwnsg/Wxma/VJ1zcGQIR7+OYJGyqbJWhhwlDPxh3JpNRL4Ba7nAsJckoYaFUv7UCyslBvQ3TNDWEfVsPJGH2FCkKTPAxD8ox+poFwJfZqqX15H6eYyK+TgJeriidLCJ7wAQHZ4Udy7u9iFxaG7mynEx4EF1leZDANzV7AE8i8joJICz2cvBxbExIYTZYTTQmxTxTzP+VnvC8rZlLOLEj7m5OW6JqtTs2US6247Hvy7XnX0OV05FP/gHde5fLZaGS8AAAAABJRU5ErkJggg==');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
icon.privacy_mode {
|
||||||
|
background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAB7UlEQVR4AdyTrVYDMRCFuyjqiiuuOJA46sCVR6jDgQTXN+CgQIJCgkOCA0cduOLAgaOOuuW7czYhyWY5FcXQc28n85O5m9nsUuuPf/9IoCzLLnxd9MTCET3SvNckQnwL7lfcpnYueIGiKNbY8QYjERo+wZK4HuAcK94rVvGSWCO8gCqKjAixTXLPsAl7ldBxriASqAo6lfUnqUTaWAP5FajTYjxGCNXeYSRAwSflToBlKxSZKSCiMoUa6Uh+QNW/B37LC9D8lkTYHNegTf7JqNP8b5RB5AT7AkPoNqqXxUyATT28AUzhRuFFaLpDUYc9V1ihr7+EA/JdxUyAxQTWQDM3CuVSEWugGiUztJ5OIJPPhlKRbFEVXJZ1Anph8iNyTCsieA0dvIgCQY3ckBtyTIBjfuDcwRR2TPJDElkRcrpd6XcyJm7X2ATY3CKwi1UxxkNPeyiP/BAa8LVZObtdBMOPcYbvX7wXYJNE2lidBuNxyhgm0I1LCdcgFXmguXqoxhgJKELBKvYMhljH+ULEwDr8mEIRXWHSP6gJKIXIESxYh3PHzWJK1IuwjpAVcBWIhHPX0x2QE/vkHGofIzUevwr4KhZ003wvsOKYkAcxXfPoxbvk3AJuQ5MNRNwFsNKFCaibRGB0CxcqIJGU3wAAAP//8GtoDAAAAAZJREFUAwCJJuAxFVNbWwAAAABJRU5ErkJggg==');
|
||||||
|
}
|
||||||
|
|
||||||
div.outer_buttons {
|
div.outer_buttons {
|
||||||
flow:vertical;
|
flow:vertical;
|
||||||
border-spacing:8;
|
border-spacing:8;
|
||||||
|
|||||||
14
src/ui/cm.rs
14
src/ui/cm.rs
@@ -36,7 +36,8 @@ impl InvokeUiCM for SciterHandler {
|
|||||||
client.file,
|
client.file,
|
||||||
client.restart,
|
client.restart,
|
||||||
client.recording,
|
client.recording,
|
||||||
client.block_input
|
client.block_input,
|
||||||
|
client.privacy_mode
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -157,9 +158,18 @@ impl SciterConnectionManager {
|
|||||||
crate::ui_interface::get_option(key)
|
crate::ui_interface::get_option(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_builtin_option(&self, key: String) -> String {
|
||||||
|
crate::ui_interface::get_builtin_option(&key)
|
||||||
|
}
|
||||||
|
|
||||||
fn hide_cm(&self) -> bool {
|
fn hide_cm(&self) -> bool {
|
||||||
*crate::ui::cm::HIDE_CM.lock().unwrap()
|
*crate::ui::cm::HIDE_CM.lock().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_supported_privacy_mode_impls(&self) -> String {
|
||||||
|
serde_json::to_string(&crate::privacy_mode::get_supported_privacy_mode_impl())
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl sciter::EventHandler for SciterConnectionManager {
|
impl sciter::EventHandler for SciterConnectionManager {
|
||||||
@@ -181,6 +191,8 @@ impl sciter::EventHandler for SciterConnectionManager {
|
|||||||
fn can_elevate();
|
fn can_elevate();
|
||||||
fn elevate_portable(i32);
|
fn elevate_portable(i32);
|
||||||
fn get_option(String);
|
fn get_option(String);
|
||||||
|
fn get_builtin_option(String);
|
||||||
fn hide_cm();
|
fn hide_cm();
|
||||||
|
fn get_supported_privacy_mode_impls();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ var body;
|
|||||||
var connections = [];
|
var connections = [];
|
||||||
var show_chat = false;
|
var show_chat = false;
|
||||||
var show_elevation = true;
|
var show_elevation = true;
|
||||||
|
var is_privacy_mode_supported = handler.get_supported_privacy_mode_impls() != '[]';
|
||||||
|
var allow_perm_change_in_accept_window =
|
||||||
|
handler.get_builtin_option('enable-perm-change-in-accept-window') != 'N';
|
||||||
var svg_elevate = <svg t="1667992597853" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1850" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M892.761 160.724v426.504c0 25.588-6.419 51.036-19.177 76.339-12.798 25.336-29.547 49.86-50.254 73.627-20.707 23.79-44.372 46.296-70.97 67.516-26.589 21.244-53.543 40.177-80.921 56.768-27.363 16.623-53.968 30.461-79.801 41.438-25.809 11.008-48.433 18.547-67.871 22.64l-9.203 1.53-8.43-1.53c-19.958-4.093-43.094-11.632-69.432-22.64-26.337-10.969-53.708-24.816-82.080-41.438-28.388-16.591-56.256-35.524-83.618-56.768-27.378-21.219-51.776-43.725-73.265-67.516-21.488-23.759-38.868-48.291-52.155-73.627-13.319-25.305-19.974-50.759-19.974-76.339v-426.504l31.455-4.629 352.892-65.97 359.784 65.97 23.017 4.629zM510.028 151.884l-4.211-0.844-302.89 51.476v269.101h307.102v-319.734zM815.434 471.634h-305.406v383.031c19.682-4.51 41.052-11.411 64.141-20.692 23.033-9.249 45.815-20.234 68.304-32.867 22.513-12.672 44.159-26.739 64.969-42.203 20.818-15.472 39.23-32.047 55.277-49.797 16.024-17.703 28.822-36.131 38.386-55.222 9.549-19.131 14.328-38.553 14.328-58.235v-124.015z" p-id="1851" fill="#ffffff"></path></svg>;
|
var svg_elevate = <svg t="1667992597853" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1850" width="16" height="16" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M892.761 160.724v426.504c0 25.588-6.419 51.036-19.177 76.339-12.798 25.336-29.547 49.86-50.254 73.627-20.707 23.79-44.372 46.296-70.97 67.516-26.589 21.244-53.543 40.177-80.921 56.768-27.363 16.623-53.968 30.461-79.801 41.438-25.809 11.008-48.433 18.547-67.871 22.64l-9.203 1.53-8.43-1.53c-19.958-4.093-43.094-11.632-69.432-22.64-26.337-10.969-53.708-24.816-82.080-41.438-28.388-16.591-56.256-35.524-83.618-56.768-27.378-21.219-51.776-43.725-73.265-67.516-21.488-23.759-38.868-48.291-52.155-73.627-13.319-25.305-19.974-50.759-19.974-76.339v-426.504l31.455-4.629 352.892-65.97 359.784 65.97 23.017 4.629zM510.028 151.884l-4.211-0.844-302.89 51.476v269.101h307.102v-319.734zM815.434 471.634h-305.406v383.031c19.682-4.51 41.052-11.411 64.141-20.692 23.033-9.249 45.815-20.234 68.304-32.867 22.513-12.672 44.159-26.739 64.969-42.203 20.818-15.472 39.23-32.047 55.277-49.797 16.024-17.703 28.822-36.131 38.386-55.222 9.549-19.131 14.328-38.553 14.328-58.235v-124.015z" p-id="1851" fill="#ffffff"></path></svg>;
|
||||||
|
|
||||||
var hide_cm = undefined;
|
var hide_cm = undefined;
|
||||||
@@ -35,6 +38,7 @@ class Body: Reactor.Component
|
|||||||
me.sendMsg(msg);
|
me.sendMsg(msg);
|
||||||
};
|
};
|
||||||
var right_style = show_chat ? "" : "display: none";
|
var right_style = show_chat ? "" : "display: none";
|
||||||
|
var permissions_locked = !allow_perm_change_in_accept_window;
|
||||||
var disconnected = c.disconnected;
|
var disconnected = c.disconnected;
|
||||||
var show_elevation_btn = handler.can_elevate() && show_elevation && !c.is_file_transfer && !c.is_view_camera && !c.is_terminal && c.port_forward.length == 0;
|
var show_elevation_btn = handler.can_elevate() && show_elevation && !c.is_file_transfer && !c.is_view_camera && !c.is_terminal && c.port_forward.length == 0;
|
||||||
var show_accept_btn = handler.get_option('approve-mode') != 'password';
|
var show_accept_btn = handler.get_option('approve-mode') != 'password';
|
||||||
@@ -58,15 +62,16 @@ class Body: Reactor.Component
|
|||||||
</div>
|
</div>
|
||||||
<div />
|
<div />
|
||||||
{c.is_file_transfer || c.is_terminal || c.port_forward || disconnected ? "" : <div>{translate('Permissions')}</div>}
|
{c.is_file_transfer || c.is_terminal || c.port_forward || disconnected ? "" : <div>{translate('Permissions')}</div>}
|
||||||
{c.is_file_transfer || c.is_terminal || c.port_forward || disconnected ? "" : <div> <div .permissions>
|
{c.is_file_transfer || c.is_terminal || c.port_forward || disconnected ? "" : <div> <div class={permissions_locked ? "permissions locked" : "permissions"} style={permissions_locked ? "opacity:0.6;" : ""}>
|
||||||
<div class={!c.keyboard ? "disabled" : ""} title={translate('Enable keyboard/mouse')}><icon .keyboard /></div>
|
<div class={!c.keyboard ? "disabled" : ""} title={translate('Enable keyboard/mouse')}><icon .keyboard /></div>
|
||||||
<div class={!c.clipboard ? "disabled" : ""} title={translate('Enable clipboard')}><icon .clipboard /></div>
|
<div class={!c.clipboard ? "disabled" : ""} title={translate('Enable clipboard')}><icon .clipboard /></div>
|
||||||
<div class={!c.audio ? "disabled" : ""} title={translate('Enable audio')}><icon .audio /></div>
|
<div class={!c.audio ? "disabled" : ""} title={translate('Enable audio')}><icon .audio /></div>
|
||||||
<div class={!c.file ? "disabled" : ""} title={translate('Enable file copy and paste')}><icon .file /></div>
|
<div class={!c.file ? "disabled" : ""} title={translate('Enable file copy and paste')}><icon .file /></div>
|
||||||
<div class={!c.restart ? "disabled" : ""} title={translate('Enable remote restart')}><icon .restart /></div>
|
<div class={!c.restart ? "disabled" : ""} title={translate('Enable remote restart')}><icon .restart /></div>
|
||||||
</div> <div .permissions style="margin-top:8px;" >
|
</div> <div class={permissions_locked ? "permissions locked" : "permissions"} style={permissions_locked ? "margin-top:8px;opacity:0.6;" : "margin-top:8px;"} >
|
||||||
<div class={!c.recording ? "disabled" : ""} title={translate('Enable recording session')}><icon .recording /></div>
|
<div class={!c.recording ? "disabled" : ""} title={translate('Enable recording session')}><icon .recording /></div>
|
||||||
<div class={!c.block_input ? "disabled" : ""} title={translate('Enable blocking user input')} style={is_win ? "" : "display:none;"}><icon .block_input /></div>
|
<div class={!c.block_input ? "disabled" : ""} title={translate('Enable blocking user input')} style={is_win ? "" : "display:none;"}><icon .block_input /></div>
|
||||||
|
<div class={!c.privacy_mode ? "disabled" : ""} title={translate('Enable privacy mode')} style={is_privacy_mode_supported ? "" : "display:none;"}><icon .privacy_mode /></div>
|
||||||
</div></div>
|
</div></div>
|
||||||
}
|
}
|
||||||
{c.is_file_transfer ? <div>{translate('Transfer file')}</div> : ""}
|
{c.is_file_transfer ? <div>{translate('Transfer file')}</div> : ""}
|
||||||
@@ -103,6 +108,7 @@ class Body: Reactor.Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
event click $(icon.keyboard) (e) {
|
event click $(icon.keyboard) (e) {
|
||||||
|
if (!allow_perm_change_in_accept_window) return;
|
||||||
var { cid, connection } = this;
|
var { cid, connection } = this;
|
||||||
checkClickTime(function() {
|
checkClickTime(function() {
|
||||||
connection.keyboard = !connection.keyboard;
|
connection.keyboard = !connection.keyboard;
|
||||||
@@ -112,6 +118,7 @@ class Body: Reactor.Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
event click $(icon.clipboard) {
|
event click $(icon.clipboard) {
|
||||||
|
if (!allow_perm_change_in_accept_window) return;
|
||||||
var { cid, connection } = this;
|
var { cid, connection } = this;
|
||||||
checkClickTime(function() {
|
checkClickTime(function() {
|
||||||
connection.clipboard = !connection.clipboard;
|
connection.clipboard = !connection.clipboard;
|
||||||
@@ -121,6 +128,7 @@ class Body: Reactor.Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
event click $(icon.audio) {
|
event click $(icon.audio) {
|
||||||
|
if (!allow_perm_change_in_accept_window) return;
|
||||||
var { cid, connection } = this;
|
var { cid, connection } = this;
|
||||||
checkClickTime(function() {
|
checkClickTime(function() {
|
||||||
connection.audio = !connection.audio;
|
connection.audio = !connection.audio;
|
||||||
@@ -130,6 +138,7 @@ class Body: Reactor.Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
event click $(icon.file) {
|
event click $(icon.file) {
|
||||||
|
if (!allow_perm_change_in_accept_window) return;
|
||||||
var { cid, connection } = this;
|
var { cid, connection } = this;
|
||||||
checkClickTime(function() {
|
checkClickTime(function() {
|
||||||
connection.file = !connection.file;
|
connection.file = !connection.file;
|
||||||
@@ -139,6 +148,7 @@ class Body: Reactor.Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
event click $(icon.restart) {
|
event click $(icon.restart) {
|
||||||
|
if (!allow_perm_change_in_accept_window) return;
|
||||||
var { cid, connection } = this;
|
var { cid, connection } = this;
|
||||||
checkClickTime(function() {
|
checkClickTime(function() {
|
||||||
connection.restart = !connection.restart;
|
connection.restart = !connection.restart;
|
||||||
@@ -148,6 +158,7 @@ class Body: Reactor.Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
event click $(icon.recording) {
|
event click $(icon.recording) {
|
||||||
|
if (!allow_perm_change_in_accept_window) return;
|
||||||
var { cid, connection } = this;
|
var { cid, connection } = this;
|
||||||
checkClickTime(function() {
|
checkClickTime(function() {
|
||||||
connection.recording = !connection.recording;
|
connection.recording = !connection.recording;
|
||||||
@@ -157,6 +168,7 @@ class Body: Reactor.Component
|
|||||||
}
|
}
|
||||||
|
|
||||||
event click $(icon.block_input) {
|
event click $(icon.block_input) {
|
||||||
|
if (!allow_perm_change_in_accept_window) return;
|
||||||
var { cid, connection } = this;
|
var { cid, connection } = this;
|
||||||
checkClickTime(function() {
|
checkClickTime(function() {
|
||||||
connection.block_input = !connection.block_input;
|
connection.block_input = !connection.block_input;
|
||||||
@@ -165,6 +177,16 @@ class Body: Reactor.Component
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event click $(icon.privacy_mode) {
|
||||||
|
if (!allow_perm_change_in_accept_window) return;
|
||||||
|
var { cid, connection } = this;
|
||||||
|
checkClickTime(function() {
|
||||||
|
connection.privacy_mode = !connection.privacy_mode;
|
||||||
|
body.update();
|
||||||
|
handler.switch_permission(cid, "privacy_mode", connection.privacy_mode);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
event click $(button#accept) {
|
event click $(button#accept) {
|
||||||
var { cid, connection } = this;
|
var { cid, connection } = this;
|
||||||
checkClickTime(function() {
|
checkClickTime(function() {
|
||||||
@@ -368,7 +390,7 @@ function bring_to_top(idx=-1) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handler.addConnection = function(id, is_file_transfer, is_view_camera, is_terminal, port_forward, peer_id, name, avatar, authorized, keyboard, clipboard, audio, file, restart, recording, block_input) {
|
handler.addConnection = function(id, is_file_transfer, is_view_camera, is_terminal, port_forward, peer_id, name, avatar, authorized, keyboard, clipboard, audio, file, restart, recording, block_input, privacy_mode) {
|
||||||
stdout.println("new connection #" + id + ": " + peer_id);
|
stdout.println("new connection #" + id + ": " + peer_id);
|
||||||
var conn;
|
var conn;
|
||||||
connections.map(function(c) {
|
connections.map(function(c) {
|
||||||
@@ -376,6 +398,7 @@ handler.addConnection = function(id, is_file_transfer, is_view_camera, is_termin
|
|||||||
});
|
});
|
||||||
if (conn) {
|
if (conn) {
|
||||||
conn.authorized = authorized;
|
conn.authorized = authorized;
|
||||||
|
conn.privacy_mode = privacy_mode;
|
||||||
update();
|
update();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -391,7 +414,7 @@ handler.addConnection = function(id, is_file_transfer, is_view_camera, is_termin
|
|||||||
name: name, authorized: authorized, time: new Date(), now: new Date(),
|
name: name, authorized: authorized, time: new Date(), now: new Date(),
|
||||||
keyboard: keyboard, clipboard: clipboard, msgs: [], unreaded: 0,
|
keyboard: keyboard, clipboard: clipboard, msgs: [], unreaded: 0,
|
||||||
audio: audio, file: file, restart: restart, recording: recording,
|
audio: audio, file: file, restart: restart, recording: recording,
|
||||||
block_input:block_input,
|
block_input:block_input, privacy_mode:privacy_mode,
|
||||||
disconnected: false
|
disconnected: false
|
||||||
};
|
};
|
||||||
if (idx < 0) {
|
if (idx < 0) {
|
||||||
@@ -480,15 +503,21 @@ function getElapsed(time, now) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ui_status_cache = [""];
|
var ui_status_cache = ["", ""];
|
||||||
function check_update_ui() {
|
function check_update_ui() {
|
||||||
self.timer(1s, function() {
|
self.timer(1s, function() {
|
||||||
var approve_mode = handler.get_option('approve-mode');
|
var approve_mode = handler.get_option('approve-mode');
|
||||||
|
var allow_perm_change = handler.get_builtin_option('enable-perm-change-in-accept-window');
|
||||||
var changed = false;
|
var changed = false;
|
||||||
if (ui_status_cache[0] != approve_mode) {
|
if (ui_status_cache[0] != approve_mode) {
|
||||||
ui_status_cache[0] = approve_mode;
|
ui_status_cache[0] = approve_mode;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
if (ui_status_cache[1] != allow_perm_change) {
|
||||||
|
ui_status_cache[1] = allow_perm_change;
|
||||||
|
allow_perm_change_in_accept_window = allow_perm_change != 'N';
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
if (changed) update();
|
if (changed) update();
|
||||||
check_update_ui();
|
check_update_ui();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -218,7 +218,7 @@ class Header: Reactor.Component {
|
|||||||
{is_file_copy_paste_supported && file_enabled ? <li #enable-file-copy-paste .toggle-option><span>{svg_checkmark}</span>{translate('Enable file copy and paste')}</li> : ""}
|
{is_file_copy_paste_supported && file_enabled ? <li #enable-file-copy-paste .toggle-option><span>{svg_checkmark}</span>{translate('Enable file copy and paste')}</li> : ""}
|
||||||
{keyboard_enabled && clipboard_enabled ? <li #disable-clipboard .toggle-option><span>{svg_checkmark}</span>{translate('Disable clipboard')}</li> : ""}
|
{keyboard_enabled && clipboard_enabled ? <li #disable-clipboard .toggle-option><span>{svg_checkmark}</span>{translate('Disable clipboard')}</li> : ""}
|
||||||
{keyboard_enabled ? <li #lock-after-session-end .toggle-option><span>{svg_checkmark}</span>{translate('Lock after session end')}</li> : ""}
|
{keyboard_enabled ? <li #lock-after-session-end .toggle-option><span>{svg_checkmark}</span>{translate('Lock after session end')}</li> : ""}
|
||||||
{keyboard_enabled && pi.platform == "Windows" ? <li #privacy-mode><span>{svg_checkmark}</span>{translate('Privacy mode')}</li> : ""}
|
{(pi.platform == "Windows" || pi.platform == "Mac OS") && (handler.get_toggle_option("privacy-mode") || (keyboard_enabled && privacy_mode_enabled)) ? <li #privacy-mode><span>{svg_checkmark}</span>{translate('Privacy mode')}</li> : ""}
|
||||||
{keyboard_enabled && ((is_osx && pi.platform != "Mac OS") || (!is_osx && pi.platform == "Mac OS")) ? <li #allow_swap_key .toggle-option><span>{svg_checkmark}</span>{translate('Swap control-command key')}</li> : ""}
|
{keyboard_enabled && ((is_osx && pi.platform != "Mac OS") || (!is_osx && pi.platform == "Mac OS")) ? <li #allow_swap_key .toggle-option><span>{svg_checkmark}</span>{translate('Swap control-command key')}</li> : ""}
|
||||||
{handler.version_cmp(pi.version, '1.2.4') >= 0 ? <li #i444><span>{svg_checkmark}</span>{translate('True color (4:4:4)')}</li> : ""}
|
{handler.version_cmp(pi.version, '1.2.4') >= 0 ? <li #i444><span>{svg_checkmark}</span>{translate('True color (4:4:4)')}</li> : ""}
|
||||||
</menu>
|
</menu>
|
||||||
|
|||||||
@@ -521,6 +521,7 @@ class MyIdMenu: Reactor.Component {
|
|||||||
{!disable_settings && <li #enable-remote-restart><span>{svg_checkmark}</span>{translate('Enable remote restart')}</li>}
|
{!disable_settings && <li #enable-remote-restart><span>{svg_checkmark}</span>{translate('Enable remote restart')}</li>}
|
||||||
{!disable_settings && <li #enable-tunnel><span>{svg_checkmark}</span>{translate('Enable TCP tunneling')}</li>}
|
{!disable_settings && <li #enable-tunnel><span>{svg_checkmark}</span>{translate('Enable TCP tunneling')}</li>}
|
||||||
{!disable_settings && is_win ? <li #enable-block-input><span>{svg_checkmark}</span>{translate('Enable blocking user input')}</li> : ""}
|
{!disable_settings && is_win ? <li #enable-block-input><span>{svg_checkmark}</span>{translate('Enable blocking user input')}</li> : ""}
|
||||||
|
{!disable_settings && (handler.get_supported_privacy_mode_impls() != '[]') && <li #enable-privacy-mode><span>{svg_checkmark}</span>{translate('Enable privacy mode')}</li>}
|
||||||
{!disable_settings && <li #enable-lan-discovery><span>{svg_checkmark}</span>{translate('Enable LAN discovery')}</li>}
|
{!disable_settings && <li #enable-lan-discovery><span>{svg_checkmark}</span>{translate('Enable LAN discovery')}</li>}
|
||||||
<AudioInputs />
|
<AudioInputs />
|
||||||
<Enhancements />
|
<Enhancements />
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ var audio_enabled = true; // server side
|
|||||||
var file_enabled = true; // server side
|
var file_enabled = true; // server side
|
||||||
var restart_enabled = true; // server side
|
var restart_enabled = true; // server side
|
||||||
var recording_enabled = true; // server side
|
var recording_enabled = true; // server side
|
||||||
|
var privacy_mode_enabled = true; // server side
|
||||||
var scroll_body = $(body);
|
var scroll_body = $(body);
|
||||||
var peer_platform = "";
|
var peer_platform = "";
|
||||||
|
|
||||||
@@ -588,6 +589,7 @@ handler.setPermission = function(name, enabled) {
|
|||||||
if (name == "clipboard") clipboard_enabled = enabled;
|
if (name == "clipboard") clipboard_enabled = enabled;
|
||||||
if (name == "restart") restart_enabled = enabled;
|
if (name == "restart") restart_enabled = enabled;
|
||||||
if (name == "recording") recording_enabled = enabled;
|
if (name == "recording") recording_enabled = enabled;
|
||||||
|
if (name == "privacy_mode") privacy_mode_enabled = enabled;
|
||||||
input_blocked = false;
|
input_blocked = false;
|
||||||
header.update();
|
header.update();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ use hbb_common::fs::serialize_transfer_job;
|
|||||||
use hbb_common::tokio::sync::mpsc::unbounded_channel;
|
use hbb_common::tokio::sync::mpsc::unbounded_channel;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
allow_err, bail,
|
allow_err, bail,
|
||||||
config::{keys::OPTION_FILE_TRANSFER_MAX_FILES, Config},
|
config::{
|
||||||
|
keys::{OPTION_ENABLE_PERM_CHANGE_IN_ACCEPT_WINDOW, OPTION_FILE_TRANSFER_MAX_FILES},
|
||||||
|
option2bool, Config,
|
||||||
|
},
|
||||||
fs::{self, get_string, is_write_need_confirmation, new_send_confirm, DigestCheckResult},
|
fs::{self, get_string, is_write_need_confirmation, new_send_confirm, DigestCheckResult},
|
||||||
log,
|
log,
|
||||||
message_proto::*,
|
message_proto::*,
|
||||||
@@ -25,10 +28,7 @@ use hbb_common::{
|
|||||||
ResultType,
|
ResultType,
|
||||||
};
|
};
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
use hbb_common::{
|
use hbb_common::{config::keys::*, tokio::sync::Mutex as TokioMutex};
|
||||||
config::{keys::*, option2bool},
|
|
||||||
tokio::sync::Mutex as TokioMutex,
|
|
||||||
};
|
|
||||||
use serde_derive::Serialize;
|
use serde_derive::Serialize;
|
||||||
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
#[cfg(any(target_os = "android", target_os = "ios", feature = "flutter"))]
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
@@ -143,6 +143,7 @@ pub struct Client {
|
|||||||
pub restart: bool,
|
pub restart: bool,
|
||||||
pub recording: bool,
|
pub recording: bool,
|
||||||
pub block_input: bool,
|
pub block_input: bool,
|
||||||
|
pub privacy_mode: bool,
|
||||||
pub from_switch: bool,
|
pub from_switch: bool,
|
||||||
pub in_voice_call: bool,
|
pub in_voice_call: bool,
|
||||||
pub incoming_voice_call: bool,
|
pub incoming_voice_call: bool,
|
||||||
@@ -230,6 +231,7 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
|
|||||||
restart: bool,
|
restart: bool,
|
||||||
recording: bool,
|
recording: bool,
|
||||||
block_input: bool,
|
block_input: bool,
|
||||||
|
privacy_mode: bool,
|
||||||
from_switch: bool,
|
from_switch: bool,
|
||||||
#[cfg(not(any(target_os = "ios")))] tx: mpsc::UnboundedSender<Data>,
|
#[cfg(not(any(target_os = "ios")))] tx: mpsc::UnboundedSender<Data>,
|
||||||
) {
|
) {
|
||||||
@@ -251,6 +253,7 @@ impl<T: InvokeUiCM> ConnectionManager<T> {
|
|||||||
restart,
|
restart,
|
||||||
recording,
|
recording,
|
||||||
block_input,
|
block_input,
|
||||||
|
privacy_mode,
|
||||||
from_switch,
|
from_switch,
|
||||||
#[cfg(not(any(target_os = "ios")))]
|
#[cfg(not(any(target_os = "ios")))]
|
||||||
tx,
|
tx,
|
||||||
@@ -392,6 +395,23 @@ pub fn send_chat(id: i32, text: String) {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(not(any(target_os = "ios")))]
|
#[cfg(not(any(target_os = "ios")))]
|
||||||
pub fn switch_permission(id: i32, name: String, enabled: bool) {
|
pub fn switch_permission(id: i32, name: String, enabled: bool) {
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
let is_keyboard_permission = name == "keyboard";
|
||||||
|
#[cfg(not(target_os = "android"))]
|
||||||
|
let is_keyboard_permission = false;
|
||||||
|
if !option2bool(
|
||||||
|
OPTION_ENABLE_PERM_CHANGE_IN_ACCEPT_WINDOW,
|
||||||
|
&crate::get_builtin_option(OPTION_ENABLE_PERM_CHANGE_IN_ACCEPT_WINDOW),
|
||||||
|
) && !is_keyboard_permission
|
||||||
|
{
|
||||||
|
log::info!(
|
||||||
|
"blocked cm switch_permission by policy, conn_id={}, permission={}, enabled={}",
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
enabled
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if let Some(client) = CLIENTS.read().unwrap().get(&id) {
|
if let Some(client) = CLIENTS.read().unwrap().get(&id) {
|
||||||
allow_err!(client.tx.send(Data::SwitchPermission { name, enabled }));
|
allow_err!(client.tx.send(Data::SwitchPermission { name, enabled }));
|
||||||
};
|
};
|
||||||
@@ -400,6 +420,19 @@ pub fn switch_permission(id: i32, name: String, enabled: bool) {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
pub fn switch_permission_all(name: String, enabled: bool) {
|
pub fn switch_permission_all(name: String, enabled: bool) {
|
||||||
|
if name != "keyboard"
|
||||||
|
&& !option2bool(
|
||||||
|
OPTION_ENABLE_PERM_CHANGE_IN_ACCEPT_WINDOW,
|
||||||
|
&crate::get_builtin_option(OPTION_ENABLE_PERM_CHANGE_IN_ACCEPT_WINDOW),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
log::info!(
|
||||||
|
"blocked cm switch_permission_all by policy, permission={}, enabled={}",
|
||||||
|
name,
|
||||||
|
enabled
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (_, client) in CLIENTS.read().unwrap().iter() {
|
for (_, client) in CLIENTS.read().unwrap().iter() {
|
||||||
allow_err!(client.tx.send(Data::SwitchPermission {
|
allow_err!(client.tx.send(Data::SwitchPermission {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
@@ -422,6 +455,13 @@ pub fn get_clients_length() -> usize {
|
|||||||
clients.len()
|
clients.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
pub fn has_active_clients() -> bool {
|
||||||
|
let clients = CLIENTS.read().unwrap();
|
||||||
|
clients.values().any(|c| !c.disconnected)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(feature = "flutter")]
|
#[cfg(feature = "flutter")]
|
||||||
#[cfg(not(any(target_os = "ios")))]
|
#[cfg(not(any(target_os = "ios")))]
|
||||||
@@ -503,9 +543,9 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
|||||||
}
|
}
|
||||||
Ok(Some(data)) => {
|
Ok(Some(data)) => {
|
||||||
match data {
|
match data {
|
||||||
Data::Login{id, is_file_transfer, is_view_camera, is_terminal, port_forward, peer_id, name, avatar, authorized, keyboard, clipboard, audio, file, file_transfer_enabled: _file_transfer_enabled, restart, recording, block_input, from_switch} => {
|
Data::Login{id, is_file_transfer, is_view_camera, is_terminal, port_forward, peer_id, name, avatar, authorized, keyboard, clipboard, audio, file, file_transfer_enabled: _file_transfer_enabled, restart, recording, block_input, privacy_mode, from_switch} => {
|
||||||
log::debug!("conn_id: {}", id);
|
log::debug!("conn_id: {}", id);
|
||||||
self.cm.add_connection(id, is_file_transfer, is_view_camera, is_terminal, port_forward, peer_id, name, avatar, authorized, keyboard, clipboard, audio, file, restart, recording, block_input, from_switch, self.tx.clone());
|
self.cm.add_connection(id, is_file_transfer, is_view_camera, is_terminal, port_forward, peer_id, name, avatar, authorized, keyboard, clipboard, audio, file, restart, recording, block_input, privacy_mode, from_switch, self.tx.clone());
|
||||||
self.conn_id = id;
|
self.conn_id = id;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
@@ -533,6 +573,26 @@ impl<T: InvokeUiCM> IpcTaskRunner<T> {
|
|||||||
Data::ChatMessage { text } => {
|
Data::ChatMessage { text } => {
|
||||||
self.cm.new_message(self.conn_id, text);
|
self.cm.new_message(self.conn_id, text);
|
||||||
}
|
}
|
||||||
|
Data::SwitchPermission { name, enabled } => {
|
||||||
|
// Keep this branch scoped to privacy mode rollback.
|
||||||
|
// Other CM permission toggles are updated optimistically by the UI itself.
|
||||||
|
// The backend currently sends SwitchPermission back to CM only when
|
||||||
|
// privacy-mode turn-off fails and the UI state must be restored.
|
||||||
|
if name == "privacy_mode" {
|
||||||
|
let client = {
|
||||||
|
let mut clients = CLIENTS.write().unwrap();
|
||||||
|
clients.get_mut(&self.conn_id).map(|c| {
|
||||||
|
c.privacy_mode = enabled;
|
||||||
|
c.clone()
|
||||||
|
})
|
||||||
|
};
|
||||||
|
if let Some(client) = client {
|
||||||
|
// This reuses add_connection(), and cm.tis only selectively updates
|
||||||
|
// existing rows (authorized/privacy_mode) for this fallback path.
|
||||||
|
self.cm.ui_handler.add_connection(&client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Data::FS(mut fs) => {
|
Data::FS(mut fs) => {
|
||||||
if let ipc::FS::WriteBlock { id, file_num, data: _, compressed } = fs {
|
if let ipc::FS::WriteBlock { id, file_num, data: _, compressed } = fs {
|
||||||
if let Ok(bytes) = self.stream.next_raw().await {
|
if let Ok(bytes) = self.stream.next_raw().await {
|
||||||
@@ -835,6 +895,7 @@ pub async fn start_listen<T: InvokeUiCM>(
|
|||||||
restart,
|
restart,
|
||||||
recording,
|
recording,
|
||||||
block_input,
|
block_input,
|
||||||
|
privacy_mode,
|
||||||
from_switch,
|
from_switch,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
@@ -856,6 +917,7 @@ pub async fn start_listen<T: InvokeUiCM>(
|
|||||||
restart,
|
restart,
|
||||||
recording,
|
recording,
|
||||||
block_input,
|
block_input,
|
||||||
|
privacy_mode,
|
||||||
from_switch,
|
from_switch,
|
||||||
tx.clone(),
|
tx.clone(),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user