refact: terminal, win, run as admin (#12300)
Signed-off-by: fufesou <linlong1266@gmail.com>
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -2218,9 +2218,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "filedescriptor"
|
name = "filedescriptor"
|
||||||
version = "0.8.3"
|
version = "0.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/rustdesk-org/wezterm?branch=rustdesk/pty_based_0.8.1#80174f8009f41565f0fa8c66dab90d4f9211ae16"
|
||||||
checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"thiserror 1.0.61",
|
"thiserror 1.0.61",
|
||||||
@@ -5233,8 +5232,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "portable-pty"
|
name = "portable-pty"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/rustdesk-org/wezterm?branch=rustdesk/pty_based_0.8.1#80174f8009f41565f0fa8c66dab90d4f9211ae16"
|
||||||
checksum = "806ee80c2a03dbe1a9fb9534f8d19e4c0546b790cde8fd1fea9d6390644cb0be"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ ctrlc = "3.2"
|
|||||||
# arboard = { version = "3.4", features = ["wayland-data-control"] }
|
# arboard = { version = "3.4", features = ["wayland-data-control"] }
|
||||||
arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control"] }
|
arboard = { git = "https://github.com/rustdesk-org/arboard", features = ["wayland-data-control"] }
|
||||||
clipboard-master = { git = "https://github.com/rustdesk-org/clipboard-master" }
|
clipboard-master = { git = "https://github.com/rustdesk-org/clipboard-master" }
|
||||||
portable-pty = "0.8.1" # higher version not work on rustc 1.75
|
portable-pty = { git = "https://github.com/rustdesk-org/wezterm", branch = "rustdesk/pty_based_0.8.1", package = "portable-pty" }
|
||||||
|
|
||||||
system_shutdown = "4.0"
|
system_shutdown = "4.0"
|
||||||
qrcode-generator = "4.1"
|
qrcode-generator = "4.1"
|
||||||
|
|||||||
@@ -2124,6 +2124,10 @@ enum UriLinkType {
|
|||||||
terminal,
|
terminal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setEnvTerminalAdmin() {
|
||||||
|
bind.mainSetEnv(key: 'IS_TERMINAL_ADMIN', value: 'Y');
|
||||||
|
}
|
||||||
|
|
||||||
// uri link handler
|
// uri link handler
|
||||||
bool handleUriLink({List<String>? cmdArgs, Uri? uri, String? uriString}) {
|
bool handleUriLink({List<String>? cmdArgs, Uri? uri, String? uriString}) {
|
||||||
List<String>? args;
|
List<String>? args;
|
||||||
@@ -2191,6 +2195,12 @@ bool handleUriLink({List<String>? cmdArgs, Uri? uri, String? uriString}) {
|
|||||||
id = args[i + 1];
|
id = args[i + 1];
|
||||||
i++;
|
i++;
|
||||||
break;
|
break;
|
||||||
|
case '--terminal-admin':
|
||||||
|
setEnvTerminalAdmin();
|
||||||
|
type = UriLinkType.terminal;
|
||||||
|
id = args[i + 1];
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
case '--password':
|
case '--password':
|
||||||
password = args[i + 1];
|
password = args[i + 1];
|
||||||
i++;
|
i++;
|
||||||
@@ -2264,7 +2274,8 @@ List<String>? urlLinkToCmdArgs(Uri uri) {
|
|||||||
"view-camera",
|
"view-camera",
|
||||||
"port-forward",
|
"port-forward",
|
||||||
"rdp",
|
"rdp",
|
||||||
"terminal"
|
"terminal",
|
||||||
|
"terminal-admin",
|
||||||
];
|
];
|
||||||
if (uri.authority.isEmpty &&
|
if (uri.authority.isEmpty &&
|
||||||
uri.path.split('').every((char) => char == '/')) {
|
uri.path.split('').every((char) => char == '/')) {
|
||||||
@@ -2334,6 +2345,10 @@ List<String>? urlLinkToCmdArgs(Uri uri) {
|
|||||||
} else if (command == '--terminal') {
|
} else if (command == '--terminal') {
|
||||||
connect(Get.context!, id,
|
connect(Get.context!, id,
|
||||||
isTerminal: true, forceRelay: forceRelay, password: password);
|
isTerminal: true, forceRelay: forceRelay, password: password);
|
||||||
|
} else if (command == 'terminal-admin') {
|
||||||
|
setEnvTerminalAdmin();
|
||||||
|
connect(Get.context!, id,
|
||||||
|
isTerminal: true, forceRelay: forceRelay, password: password);
|
||||||
} else {
|
} else {
|
||||||
// Default to remote desktop for '--connect', '--play', or direct connection
|
// Default to remote desktop for '--connect', '--play', or direct connection
|
||||||
connect(Get.context!, id, forceRelay: forceRelay, password: password);
|
connect(Get.context!, id, forceRelay: forceRelay, password: password);
|
||||||
|
|||||||
@@ -819,23 +819,33 @@ void enterPasswordDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void enterUserLoginDialog(
|
void enterUserLoginDialog(
|
||||||
SessionID sessionId, OverlayDialogManager dialogManager) async {
|
SessionID sessionId,
|
||||||
|
OverlayDialogManager dialogManager,
|
||||||
|
String osAccountDescTip,
|
||||||
|
bool canRememberAccount) async {
|
||||||
await _connectDialog(
|
await _connectDialog(
|
||||||
sessionId,
|
sessionId,
|
||||||
dialogManager,
|
dialogManager,
|
||||||
osUsernameController: TextEditingController(),
|
osUsernameController: TextEditingController(),
|
||||||
osPasswordController: TextEditingController(),
|
osPasswordController: TextEditingController(),
|
||||||
|
osAccountDescTip: osAccountDescTip,
|
||||||
|
canRememberAccount: canRememberAccount,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void enterUserLoginAndPasswordDialog(
|
void enterUserLoginAndPasswordDialog(
|
||||||
SessionID sessionId, OverlayDialogManager dialogManager) async {
|
SessionID sessionId,
|
||||||
|
OverlayDialogManager dialogManager,
|
||||||
|
String osAccountDescTip,
|
||||||
|
bool canRememberAccount) async {
|
||||||
await _connectDialog(
|
await _connectDialog(
|
||||||
sessionId,
|
sessionId,
|
||||||
dialogManager,
|
dialogManager,
|
||||||
osUsernameController: TextEditingController(),
|
osUsernameController: TextEditingController(),
|
||||||
osPasswordController: TextEditingController(),
|
osPasswordController: TextEditingController(),
|
||||||
passwordController: TextEditingController(),
|
passwordController: TextEditingController(),
|
||||||
|
osAccountDescTip: osAccountDescTip,
|
||||||
|
canRememberAccount: canRememberAccount,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -845,17 +855,28 @@ _connectDialog(
|
|||||||
TextEditingController? osUsernameController,
|
TextEditingController? osUsernameController,
|
||||||
TextEditingController? osPasswordController,
|
TextEditingController? osPasswordController,
|
||||||
TextEditingController? passwordController,
|
TextEditingController? passwordController,
|
||||||
|
String? osAccountDescTip,
|
||||||
|
bool canRememberAccount = true,
|
||||||
}) async {
|
}) async {
|
||||||
|
final errUsername = ''.obs;
|
||||||
var rememberPassword = false;
|
var rememberPassword = false;
|
||||||
if (passwordController != null) {
|
if (passwordController != null) {
|
||||||
rememberPassword =
|
rememberPassword =
|
||||||
await bind.sessionGetRemember(sessionId: sessionId) ?? false;
|
await bind.sessionGetRemember(sessionId: sessionId) ?? false;
|
||||||
}
|
}
|
||||||
var rememberAccount = false;
|
var rememberAccount = false;
|
||||||
if (osUsernameController != null) {
|
if (canRememberAccount && osUsernameController != null) {
|
||||||
rememberAccount =
|
rememberAccount =
|
||||||
await bind.sessionGetRemember(sessionId: sessionId) ?? false;
|
await bind.sessionGetRemember(sessionId: sessionId) ?? false;
|
||||||
}
|
}
|
||||||
|
if (osUsernameController != null) {
|
||||||
|
osUsernameController.addListener(() {
|
||||||
|
if (errUsername.value.isNotEmpty) {
|
||||||
|
errUsername.value = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
dialogManager.dismissAll();
|
dialogManager.dismissAll();
|
||||||
dialogManager.show((setState, close, context) {
|
dialogManager.show((setState, close, context) {
|
||||||
cancel() {
|
cancel() {
|
||||||
@@ -864,6 +885,13 @@ _connectDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
submit() {
|
submit() {
|
||||||
|
if (osUsernameController != null) {
|
||||||
|
if (osUsernameController.text.trim().isEmpty) {
|
||||||
|
errUsername.value = translate('Empty Username');
|
||||||
|
setState(() {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
final osUsername = osUsernameController?.text.trim() ?? '';
|
final osUsername = osUsernameController?.text.trim() ?? '';
|
||||||
final osPassword = osPasswordController?.text.trim() ?? '';
|
final osPassword = osPasswordController?.text.trim() ?? '';
|
||||||
final password = passwordController?.text.trim() ?? '';
|
final password = passwordController?.text.trim() ?? '';
|
||||||
@@ -927,26 +955,39 @@ _connectDialog(
|
|||||||
}
|
}
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
descWidget(translate('login_linux_tip')),
|
if (osAccountDescTip != null) descWidget(translate(osAccountDescTip)),
|
||||||
DialogTextField(
|
DialogTextField(
|
||||||
title: translate(DialogTextField.kUsernameTitle),
|
title: translate(DialogTextField.kUsernameTitle),
|
||||||
controller: osUsernameController,
|
controller: osUsernameController,
|
||||||
prefixIcon: DialogTextField.kUsernameIcon,
|
prefixIcon: DialogTextField.kUsernameIcon,
|
||||||
errorText: null,
|
errorText: null,
|
||||||
),
|
),
|
||||||
|
if (errUsername.value.isNotEmpty)
|
||||||
|
Align(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
child: SelectableText(
|
||||||
|
errUsername.value,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.left,
|
||||||
|
).paddingOnly(left: 12, bottom: 2),
|
||||||
|
),
|
||||||
PasswordWidget(
|
PasswordWidget(
|
||||||
controller: osPasswordController,
|
controller: osPasswordController,
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
),
|
),
|
||||||
rememberWidget(
|
if (canRememberAccount)
|
||||||
translate('remember_account_tip'),
|
rememberWidget(
|
||||||
rememberAccount,
|
translate('remember_account_tip'),
|
||||||
(v) {
|
rememberAccount,
|
||||||
if (v != null) {
|
(v) {
|
||||||
setState(() => rememberAccount = v);
|
if (v != null) {
|
||||||
}
|
setState(() => rememberAccount = v);
|
||||||
},
|
}
|
||||||
),
|
},
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -492,6 +492,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
|||||||
bool isTcpTunneling = false,
|
bool isTcpTunneling = false,
|
||||||
bool isRDP = false,
|
bool isRDP = false,
|
||||||
bool isTerminal = false,
|
bool isTerminal = false,
|
||||||
|
bool isTerminalRunAsAdmin = false,
|
||||||
}) {
|
}) {
|
||||||
return MenuEntryButton<String>(
|
return MenuEntryButton<String>(
|
||||||
childBuilder: (TextStyle? style) => Text(
|
childBuilder: (TextStyle? style) => Text(
|
||||||
@@ -499,6 +500,9 @@ abstract class BasePeerCard extends StatelessWidget {
|
|||||||
style: style,
|
style: style,
|
||||||
),
|
),
|
||||||
proc: () {
|
proc: () {
|
||||||
|
if (isTerminalRunAsAdmin) {
|
||||||
|
setEnvTerminalAdmin();
|
||||||
|
}
|
||||||
connectInPeerTab(
|
connectInPeerTab(
|
||||||
context,
|
context,
|
||||||
peer,
|
peer,
|
||||||
@@ -507,7 +511,7 @@ abstract class BasePeerCard extends StatelessWidget {
|
|||||||
isViewCamera: isViewCamera,
|
isViewCamera: isViewCamera,
|
||||||
isTcpTunneling: isTcpTunneling,
|
isTcpTunneling: isTcpTunneling,
|
||||||
isRDP: isRDP,
|
isRDP: isRDP,
|
||||||
isTerminal: isTerminal,
|
isTerminal: isTerminal || isTerminalRunAsAdmin,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
padding: menuPadding,
|
padding: menuPadding,
|
||||||
@@ -552,6 +556,15 @@ abstract class BasePeerCard extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@protected
|
||||||
|
MenuEntryBase<String> _terminalRunAsAdminAction(BuildContext context) {
|
||||||
|
return _connectCommonAction(
|
||||||
|
context,
|
||||||
|
translate('Terminal (Run as administrator)'),
|
||||||
|
isTerminalRunAsAdmin: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
MenuEntryBase<String> _tcpTunnelingAction(BuildContext context) {
|
MenuEntryBase<String> _tcpTunnelingAction(BuildContext context) {
|
||||||
return _connectCommonAction(
|
return _connectCommonAction(
|
||||||
@@ -906,6 +919,10 @@ class RecentPeerCard extends BasePeerCard {
|
|||||||
_terminalAction(context),
|
_terminalAction(context),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (peer.platform == kPeerPlatformWindows) {
|
||||||
|
menuItems.add(_terminalRunAsAdminAction(context));
|
||||||
|
}
|
||||||
|
|
||||||
final List favs = (await bind.mainGetFav()).toList();
|
final List favs = (await bind.mainGetFav()).toList();
|
||||||
|
|
||||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||||
@@ -966,6 +983,11 @@ class FavoritePeerCard extends BasePeerCard {
|
|||||||
_viewCameraAction(context),
|
_viewCameraAction(context),
|
||||||
_terminalAction(context),
|
_terminalAction(context),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (peer.platform == kPeerPlatformWindows) {
|
||||||
|
menuItems.add(_terminalRunAsAdminAction(context));
|
||||||
|
}
|
||||||
|
|
||||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||||
menuItems.add(_tcpTunnelingAction(context));
|
menuItems.add(_tcpTunnelingAction(context));
|
||||||
}
|
}
|
||||||
@@ -1022,6 +1044,10 @@ class DiscoveredPeerCard extends BasePeerCard {
|
|||||||
_terminalAction(context),
|
_terminalAction(context),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (peer.platform == kPeerPlatformWindows) {
|
||||||
|
menuItems.add(_terminalRunAsAdminAction(context));
|
||||||
|
}
|
||||||
|
|
||||||
final List favs = (await bind.mainGetFav()).toList();
|
final List favs = (await bind.mainGetFav()).toList();
|
||||||
|
|
||||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||||
@@ -1076,6 +1102,11 @@ class AddressBookPeerCard extends BasePeerCard {
|
|||||||
_viewCameraAction(context),
|
_viewCameraAction(context),
|
||||||
_terminalAction(context),
|
_terminalAction(context),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (peer.platform == kPeerPlatformWindows) {
|
||||||
|
menuItems.add(_terminalRunAsAdminAction(context));
|
||||||
|
}
|
||||||
|
|
||||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||||
menuItems.add(_tcpTunnelingAction(context));
|
menuItems.add(_tcpTunnelingAction(context));
|
||||||
}
|
}
|
||||||
@@ -1212,6 +1243,11 @@ class MyGroupPeerCard extends BasePeerCard {
|
|||||||
_viewCameraAction(context),
|
_viewCameraAction(context),
|
||||||
_terminalAction(context),
|
_terminalAction(context),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (peer.platform == kPeerPlatformWindows) {
|
||||||
|
menuItems.add(_terminalRunAsAdminAction(context));
|
||||||
|
}
|
||||||
|
|
||||||
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
if (isDesktop && peer.platform != kPeerPlatformAndroid) {
|
||||||
menuItems.add(_tcpTunnelingAction(context));
|
menuItems.add(_tcpTunnelingAction(context));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,6 +230,12 @@ class WebHomePage extends StatelessWidget {
|
|||||||
id = args[i + 1];
|
id = args[i + 1];
|
||||||
i++;
|
i++;
|
||||||
break;
|
break;
|
||||||
|
case '--terminal-admin':
|
||||||
|
setEnvTerminalAdmin();
|
||||||
|
isTerminal = true;
|
||||||
|
id = args[i + 1];
|
||||||
|
i++;
|
||||||
|
break;
|
||||||
case '--password':
|
case '--password':
|
||||||
password = args[i + 1];
|
password = args[i + 1];
|
||||||
i++;
|
i++;
|
||||||
|
|||||||
@@ -836,10 +836,16 @@ class FfiModel with ChangeNotifier {
|
|||||||
} else if (type == 'input-password') {
|
} else if (type == 'input-password') {
|
||||||
enterPasswordDialog(sessionId, dialogManager);
|
enterPasswordDialog(sessionId, dialogManager);
|
||||||
} else if (type == 'session-login' || type == 'session-re-login') {
|
} else if (type == 'session-login' || type == 'session-re-login') {
|
||||||
enterUserLoginDialog(sessionId, dialogManager);
|
enterUserLoginDialog(sessionId, dialogManager, 'login_linux_tip', true);
|
||||||
} else if (type == 'session-login-password' ||
|
} else if (type == 'session-login-password') {
|
||||||
type == 'session-login-password') {
|
enterUserLoginAndPasswordDialog(
|
||||||
enterUserLoginAndPasswordDialog(sessionId, dialogManager);
|
sessionId, dialogManager, 'login_linux_tip', true);
|
||||||
|
} else if (type == 'terminal-admin-login') {
|
||||||
|
enterUserLoginDialog(
|
||||||
|
sessionId, dialogManager, 'terminal-admin-login-tip', false);
|
||||||
|
} else if (type == 'terminal-admin-login-password') {
|
||||||
|
enterUserLoginAndPasswordDialog(
|
||||||
|
sessionId, dialogManager, 'terminal-admin-login-tip', false);
|
||||||
} else if (type == 'restarting') {
|
} else if (type == 'restarting') {
|
||||||
showMsgBox(sessionId, type, title, text, link, false, dialogManager,
|
showMsgBox(sessionId, type, title, text, link, false, dialogManager,
|
||||||
hasCancel: false);
|
hasCancel: false);
|
||||||
|
|||||||
@@ -1611,6 +1611,7 @@ struct ConnToken {
|
|||||||
pub struct LoginConfigHandler {
|
pub struct LoginConfigHandler {
|
||||||
id: String,
|
id: String,
|
||||||
pub conn_type: ConnType,
|
pub conn_type: ConnType,
|
||||||
|
pub is_terminal_admin: bool,
|
||||||
hash: Hash,
|
hash: Hash,
|
||||||
password: Vec<u8>, // remember password for reconnect
|
password: Vec<u8>, // remember password for reconnect
|
||||||
pub remember: bool,
|
pub remember: bool,
|
||||||
@@ -1736,6 +1737,7 @@ impl LoginConfigHandler {
|
|||||||
self.other_server = Some((real_id.to_owned(), server.to_owned(), other_server_key));
|
self.other_server = Some((real_id.to_owned(), server.to_owned(), other_server_key));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.direct = None;
|
self.direct = None;
|
||||||
self.received = false;
|
self.received = false;
|
||||||
self.switch_uuid = switch_uuid;
|
self.switch_uuid = switch_uuid;
|
||||||
@@ -1744,6 +1746,11 @@ impl LoginConfigHandler {
|
|||||||
self.shared_password = shared_password;
|
self.shared_password = shared_password;
|
||||||
self.record_state = false;
|
self.record_state = false;
|
||||||
self.record_permission = true;
|
self.record_permission = true;
|
||||||
|
|
||||||
|
// `std::env::remove_var("IS_TERMINAL_ADMIN");` is called in `session_add_sync()` - `flutter_ffi.rs`.
|
||||||
|
let is_terminal_admin = conn_type == ConnType::TERMINAL
|
||||||
|
&& std::env::var("IS_TERMINAL_ADMIN").map_or(false, |v| v == "Y");
|
||||||
|
self.is_terminal_admin = is_terminal_admin;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if the client should auto login.
|
/// Check if the client should auto login.
|
||||||
@@ -1956,7 +1963,7 @@ impl LoginConfigHandler {
|
|||||||
.into();
|
.into();
|
||||||
} else if name == keys::OPTION_TERMINAL_PERSISTENT {
|
} else if name == keys::OPTION_TERMINAL_PERSISTENT {
|
||||||
config.terminal_persistent.v = !config.terminal_persistent.v;
|
config.terminal_persistent.v = !config.terminal_persistent.v;
|
||||||
option.terminal_persistent = (if config.terminal_persistent.v {
|
option.terminal_persistent = (if config.terminal_persistent.v {
|
||||||
BoolOption::Yes
|
BoolOption::Yes
|
||||||
} else {
|
} else {
|
||||||
BoolOption::No
|
BoolOption::No
|
||||||
@@ -3274,6 +3281,19 @@ pub async fn handle_hash(
|
|||||||
}
|
}
|
||||||
|
|
||||||
lc.write().unwrap().password = password.clone();
|
lc.write().unwrap().password = password.clone();
|
||||||
|
|
||||||
|
let is_terminal_admin = lc.read().unwrap().is_terminal_admin;
|
||||||
|
let is_terminal = lc.read().unwrap().conn_type.eq(&ConnType::TERMINAL);
|
||||||
|
if is_terminal && is_terminal_admin {
|
||||||
|
if password.is_empty() {
|
||||||
|
interface.msgbox("terminal-admin-login-password", "", "", "");
|
||||||
|
} else {
|
||||||
|
interface.msgbox("terminal-admin-login", "", "", "");
|
||||||
|
}
|
||||||
|
lc.write().unwrap().hash = hash;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let password = if password.is_empty() {
|
let password = if password.is_empty() {
|
||||||
// login without password, the remote side can click accept
|
// login without password, the remote side can click accept
|
||||||
interface.msgbox("input-password", "Password Required", "", "");
|
interface.msgbox("input-password", "Password Required", "", "");
|
||||||
@@ -3285,8 +3305,15 @@ pub async fn handle_hash(
|
|||||||
hasher.finalize()[..].into()
|
hasher.finalize()[..].into()
|
||||||
};
|
};
|
||||||
|
|
||||||
let os_username = lc.read().unwrap().get_option("os-username");
|
let is_terminal = lc.read().unwrap().conn_type.eq(&ConnType::TERMINAL);
|
||||||
let os_password = lc.read().unwrap().get_option("os-password");
|
let (os_username, os_password) = if is_terminal {
|
||||||
|
("".to_owned(), "".to_owned())
|
||||||
|
} else {
|
||||||
|
(
|
||||||
|
lc.read().unwrap().get_option("os-username"),
|
||||||
|
lc.read().unwrap().get_option("os-password"),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
send_login(lc.clone(), os_username, os_password, password, peer).await;
|
send_login(lc.clone(), os_username, os_password, password, peer).await;
|
||||||
lc.write().unwrap().hash = hash;
|
lc.write().unwrap().hash = hash;
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ pub fn session_add_sync(
|
|||||||
is_shared_password: bool,
|
is_shared_password: bool,
|
||||||
conn_token: Option<String>,
|
conn_token: Option<String>,
|
||||||
) -> SyncReturn<String> {
|
) -> SyncReturn<String> {
|
||||||
if let Err(e) = session_add(
|
let add_res = session_add(
|
||||||
&session_id,
|
&session_id,
|
||||||
&id,
|
&id,
|
||||||
is_file_transfer,
|
is_file_transfer,
|
||||||
@@ -151,7 +151,14 @@ pub fn session_add_sync(
|
|||||||
password,
|
password,
|
||||||
is_shared_password,
|
is_shared_password,
|
||||||
conn_token,
|
conn_token,
|
||||||
) {
|
);
|
||||||
|
// We can't put the remove call together with `std::env::var("IS_TERMINAL_ADMIN")`.
|
||||||
|
// Because there are some `bail!` in `session_add()`, we must make sure `IS_TERMINAL_ADMIN` is removed at last.
|
||||||
|
if is_terminal {
|
||||||
|
std::env::remove_var("IS_TERMINAL_ADMIN");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = add_res {
|
||||||
SyncReturn(format!("Failed to add session with id {}, {}", &id, e))
|
SyncReturn(format!("Failed to add session with id {}, {}", &id, e))
|
||||||
} else {
|
} else {
|
||||||
SyncReturn("".to_owned())
|
SyncReturn("".to_owned())
|
||||||
@@ -1067,6 +1074,35 @@ pub fn main_get_env(key: String) -> SyncReturn<String> {
|
|||||||
SyncReturn(std::env::var(key).unwrap_or_default())
|
SyncReturn(std::env::var(key).unwrap_or_default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dart does not support changing environment variables.
|
||||||
|
// `Platform.environment['MY_VAR'] = 'VAR';` will throw an error
|
||||||
|
// `Unsupported operation: Cannot modify unmodifiable map`.
|
||||||
|
//
|
||||||
|
// And we need to share the environment variables between rust and dart isolates sometimes.
|
||||||
|
pub fn main_set_env(key: String, value: Option<String>) -> SyncReturn<()> {
|
||||||
|
let is_valid_key = !key.is_empty() && !key.contains('=') && !key.contains('\0');
|
||||||
|
debug_assert!(is_valid_key, "Invalid environment variable key: {}", key);
|
||||||
|
if !is_valid_key {
|
||||||
|
log::error!("Invalid environment variable key: {}", key);
|
||||||
|
return SyncReturn(());
|
||||||
|
}
|
||||||
|
|
||||||
|
match value {
|
||||||
|
Some(v) => {
|
||||||
|
let is_valid_value = !v.contains('\0');
|
||||||
|
debug_assert!(is_valid_value, "Invalid environment variable value: {}", v);
|
||||||
|
if !is_valid_value {
|
||||||
|
log::error!("Invalid environment variable value: {}", v);
|
||||||
|
return SyncReturn(());
|
||||||
|
}
|
||||||
|
std::env::set_var(key, v);
|
||||||
|
}
|
||||||
|
None => std::env::remove_var(key),
|
||||||
|
}
|
||||||
|
|
||||||
|
SyncReturn(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main_set_local_option(key: String, value: String) {
|
pub fn main_set_local_option(key: String, value: String) {
|
||||||
let is_texture_render_key = key.eq(config::keys::OPTION_TEXTURE_RENDER);
|
let is_texture_render_key = key.eq(config::keys::OPTION_TEXTURE_RENDER);
|
||||||
let is_d3d_render_key = key.eq(config::keys::OPTION_ALLOW_D3D_RENDER);
|
let is_d3d_render_key = key.eq(config::keys::OPTION_ALLOW_D3D_RENDER);
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", "تمكين الطرفية"),
|
("Enable terminal", "تمكين الطرفية"),
|
||||||
("New tab", "تبويب جديد"),
|
("New tab", "تبويب جديد"),
|
||||||
("Keep terminal sessions on disconnect", "الاحتفاظ بجلسات الطرفية عند قطع الاتصال"),
|
("Keep terminal sessions on disconnect", "الاحتفاظ بجلسات الطرفية عند قطع الاتصال"),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", "启用终端"),
|
("Enable terminal", "启用终端"),
|
||||||
("New tab", "新建选项卡"),
|
("New tab", "新建选项卡"),
|
||||||
("Keep terminal sessions on disconnect", "断开连接时保持终端会话"),
|
("Keep terminal sessions on disconnect", "断开连接时保持终端会话"),
|
||||||
|
("Terminal (Run as administrator)", "终端(以管理员身份运行)"),
|
||||||
|
("terminal-admin-login-tip", "请输入被控端的管理员账号密码。"),
|
||||||
|
("Failed to get user token.", "获取用户令牌时出错。"),
|
||||||
|
("Incorrect username or password.", "用户名或密码不正确。"),
|
||||||
|
("The user is not an administrator.", "用户不是管理员。"),
|
||||||
|
("Failed to check if the user is an administrator.", "检查用户是否为管理员时出错。"),
|
||||||
|
("Supported only by the installation version.", "仅安装版本支持。"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", "Terminal zulassen"),
|
("Enable terminal", "Terminal zulassen"),
|
||||||
("New tab", "Neuer Tab"),
|
("New tab", "Neuer Tab"),
|
||||||
("Keep terminal sessions on disconnect", "Terminalsitzungen beim Trennen der Verbindung beibehalten"),
|
("Keep terminal sessions on disconnect", "Terminalsitzungen beim Trennen der Verbindung beibehalten"),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -256,5 +256,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("download-new-version-failed-tip", "Download failed. You can try again or click the \"Download\" button to download from the release page and upgrade manually."),
|
("download-new-version-failed-tip", "Download failed. You can try again or click the \"Download\" button to download from the release page and upgrade manually."),
|
||||||
("update-failed-check-msi-tip", "Installation method check failed. Please click the \"Download\" button to download from the release page and upgrade manually."),
|
("update-failed-check-msi-tip", "Installation method check failed. Please click the \"Download\" button to download from the release page and upgrade manually."),
|
||||||
("websocket_tip", "When using WebSocket, only relay connections are supported."),
|
("websocket_tip", "When using WebSocket, only relay connections are supported."),
|
||||||
|
("terminal-admin-login-tip", "Please input the administrator username and password of the controlled side."),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", "فعالسازی ترمینال"),
|
("Enable terminal", "فعالسازی ترمینال"),
|
||||||
("New tab", "زبانه جدید"),
|
("New tab", "زبانه جدید"),
|
||||||
("Keep terminal sessions on disconnect", "حفظ جلسات ترمینال پس از قطع اتصال"),
|
("Keep terminal sessions on disconnect", "حفظ جلسات ترمینال پس از قطع اتصال"),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", "Activer le terminal"),
|
("Enable terminal", "Activer le terminal"),
|
||||||
("New tab", "Nouvel onglet"),
|
("New tab", "Nouvel onglet"),
|
||||||
("Keep terminal sessions on disconnect", "Maintenir les sessions du terminal lors de la déconnexion"),
|
("Keep terminal sessions on disconnect", "Maintenir les sessions du terminal lors de la déconnexion"),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", "Terminál engedélyezése"),
|
("Enable terminal", "Terminál engedélyezése"),
|
||||||
("New tab", "Új lap"),
|
("New tab", "Új lap"),
|
||||||
("Keep terminal sessions on disconnect", "Terminál munkamenetek megtartása leválasztáskor"),
|
("Keep terminal sessions on disconnect", "Terminál munkamenetek megtartása leválasztáskor"),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", "Abilita terminale"),
|
("Enable terminal", "Abilita terminale"),
|
||||||
("New tab", "Nuova scheda"),
|
("New tab", "Nuova scheda"),
|
||||||
("Keep terminal sessions on disconnect", "Quando disconetti mantieni attiva sessione terminale"),
|
("Keep terminal sessions on disconnect", "Quando disconetti mantieni attiva sessione terminale"),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", "터미널 사용함"),
|
("Enable terminal", "터미널 사용함"),
|
||||||
("New tab", "새 탭"),
|
("New tab", "새 탭"),
|
||||||
("Keep terminal sessions on disconnect", "터미널 세션 연결 해제 상태 유지"),
|
("Keep terminal sessions on disconnect", "터미널 세션 연결 해제 상태 유지"),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", "Iespējot termināli"),
|
("Enable terminal", "Iespējot termināli"),
|
||||||
("New tab", "Jauna cilne"),
|
("New tab", "Jauna cilne"),
|
||||||
("Keep terminal sessions on disconnect", "Atvienojoties saglabāt termināļa sesijas"),
|
("Keep terminal sessions on disconnect", "Atvienojoties saglabāt termināļa sesijas"),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", "Terminal inschakelen"),
|
("Enable terminal", "Terminal inschakelen"),
|
||||||
("New tab", "Nieuw tabblad"),
|
("New tab", "Nieuw tabblad"),
|
||||||
("Keep terminal sessions on disconnect", "Terminalsessies bij verbreking van de verbinding behouden"),
|
("Keep terminal sessions on disconnect", "Terminalsessies bij verbreking van de verbinding behouden"),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", "Включить терминал"),
|
("Enable terminal", "Включить терминал"),
|
||||||
("New tab", "Новая вкладка"),
|
("New tab", "Новая вкладка"),
|
||||||
("Keep terminal sessions on disconnect", "Сохранять сеансы терминала при отключении"),
|
("Keep terminal sessions on disconnect", "Сохранять сеансы терминала при отключении"),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -703,5 +703,12 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Enable terminal", ""),
|
("Enable terminal", ""),
|
||||||
("New tab", ""),
|
("New tab", ""),
|
||||||
("Keep terminal sessions on disconnect", ""),
|
("Keep terminal sessions on disconnect", ""),
|
||||||
|
("Terminal (Run as administrator)", ""),
|
||||||
|
("terminal-admin-login-tip", ""),
|
||||||
|
("Failed to get user token.", ""),
|
||||||
|
("Incorrect username or password.", ""),
|
||||||
|
("The user is not an administrator.", ""),
|
||||||
|
("Failed to check if the user is an administrator.", ""),
|
||||||
|
("Supported only by the installation version.", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ use winapi::{
|
|||||||
shared::{minwindef::*, ntdef::NULL, windef::*, winerror::*},
|
shared::{minwindef::*, ntdef::NULL, windef::*, winerror::*},
|
||||||
um::{
|
um::{
|
||||||
errhandlingapi::GetLastError,
|
errhandlingapi::GetLastError,
|
||||||
handleapi::CloseHandle,
|
handleapi::{CloseHandle, INVALID_HANDLE_VALUE},
|
||||||
libloaderapi::{
|
libloaderapi::{
|
||||||
GetProcAddress, LoadLibraryA, LoadLibraryExA, LOAD_LIBRARY_SEARCH_SYSTEM32,
|
GetProcAddress, LoadLibraryA, LoadLibraryExA, LOAD_LIBRARY_SEARCH_SYSTEM32,
|
||||||
},
|
},
|
||||||
@@ -49,15 +49,19 @@ use winapi::{
|
|||||||
GetCurrentProcess, GetCurrentProcessId, GetExitCodeProcess, OpenProcess,
|
GetCurrentProcess, GetCurrentProcessId, GetExitCodeProcess, OpenProcess,
|
||||||
OpenProcessToken, ProcessIdToSessionId, PROCESS_INFORMATION, STARTUPINFOW,
|
OpenProcessToken, ProcessIdToSessionId, PROCESS_INFORMATION, STARTUPINFOW,
|
||||||
},
|
},
|
||||||
securitybaseapi::GetTokenInformation,
|
securitybaseapi::{
|
||||||
|
AllocateAndInitializeSid, DuplicateToken, EqualSid, FreeSid, GetTokenInformation,
|
||||||
|
},
|
||||||
shellapi::ShellExecuteW,
|
shellapi::ShellExecuteW,
|
||||||
sysinfoapi::{GetNativeSystemInfo, SYSTEM_INFO},
|
sysinfoapi::{GetNativeSystemInfo, SYSTEM_INFO},
|
||||||
winbase::*,
|
winbase::*,
|
||||||
wingdi::*,
|
wingdi::*,
|
||||||
winnt::{
|
winnt::{
|
||||||
TokenElevation, ES_AWAYMODE_REQUIRED, ES_CONTINUOUS, ES_DISPLAY_REQUIRED,
|
SecurityImpersonation, TokenElevation, TokenGroups, TokenImpersonation, TokenType,
|
||||||
|
DOMAIN_ALIAS_RID_ADMINS, ES_AWAYMODE_REQUIRED, ES_CONTINUOUS, ES_DISPLAY_REQUIRED,
|
||||||
ES_SYSTEM_REQUIRED, HANDLE, PROCESS_ALL_ACCESS, PROCESS_QUERY_LIMITED_INFORMATION,
|
ES_SYSTEM_REQUIRED, HANDLE, PROCESS_ALL_ACCESS, PROCESS_QUERY_LIMITED_INFORMATION,
|
||||||
TOKEN_ELEVATION, TOKEN_QUERY,
|
PSID, SECURITY_BUILTIN_DOMAIN_RID, SECURITY_NT_AUTHORITY, SID_IDENTIFIER_AUTHORITY,
|
||||||
|
TOKEN_ELEVATION, TOKEN_GROUPS, TOKEN_QUERY, TOKEN_TYPE,
|
||||||
},
|
},
|
||||||
winreg::HKEY_CURRENT_USER,
|
winreg::HKEY_CURRENT_USER,
|
||||||
winspool::{
|
winspool::{
|
||||||
@@ -521,6 +525,10 @@ extern "C" {
|
|||||||
fn is_service_running_w(svc_name: *const u16) -> bool;
|
fn is_service_running_w(svc_name: *const u16) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_current_session_id(share_rdp: bool) -> DWORD {
|
||||||
|
unsafe { get_current_session(if share_rdp { TRUE } else { FALSE }) }
|
||||||
|
}
|
||||||
|
|
||||||
extern "system" {
|
extern "system" {
|
||||||
fn BlockInput(v: BOOL) -> BOOL;
|
fn BlockInput(v: BOOL) -> BOOL;
|
||||||
}
|
}
|
||||||
@@ -2158,6 +2166,177 @@ pub fn send_message_to_hnwd(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_logon_user_token(user: &str, pwd: &str) -> ResultType<HANDLE> {
|
||||||
|
let user_split = user.split("\\").collect::<Vec<&str>>();
|
||||||
|
let wuser = wide_string(user_split.get(1).unwrap_or(&user));
|
||||||
|
let wpc = wide_string(user_split.get(0).unwrap_or(&""));
|
||||||
|
let wpwd = wide_string(pwd);
|
||||||
|
let mut ph_token: HANDLE = std::ptr::null_mut();
|
||||||
|
let res = unsafe {
|
||||||
|
LogonUserW(
|
||||||
|
wuser.as_ptr(),
|
||||||
|
wpc.as_ptr(),
|
||||||
|
wpwd.as_ptr(),
|
||||||
|
LOGON32_LOGON_INTERACTIVE,
|
||||||
|
LOGON32_PROVIDER_DEFAULT,
|
||||||
|
&mut ph_token as _,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if res == FALSE {
|
||||||
|
bail!(
|
||||||
|
"Failed to log on user {}: {}",
|
||||||
|
user,
|
||||||
|
std::io::Error::last_os_error()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if ph_token.is_null() {
|
||||||
|
bail!(
|
||||||
|
"Failed to log on user {}: {}",
|
||||||
|
user,
|
||||||
|
std::io::Error::last_os_error()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(ph_token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the token returned is a primary token.
|
||||||
|
// If the provided token is an impersonation token, it duplicates it to a primary token.
|
||||||
|
// If the provided token is already a primary token, it returns it as is.
|
||||||
|
// The caller is responsible for closing the returned token handle.
|
||||||
|
pub fn ensure_primary_token(user_token: HANDLE) -> ResultType<HANDLE> {
|
||||||
|
if user_token.is_null() || user_token == INVALID_HANDLE_VALUE {
|
||||||
|
bail!("Invalid user token provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let mut token_type: TOKEN_TYPE = 0;
|
||||||
|
let mut return_length: DWORD = 0;
|
||||||
|
|
||||||
|
if GetTokenInformation(
|
||||||
|
user_token,
|
||||||
|
TokenType,
|
||||||
|
&mut token_type as *mut _ as *mut _,
|
||||||
|
std::mem::size_of::<TOKEN_TYPE>() as DWORD,
|
||||||
|
&mut return_length,
|
||||||
|
) == FALSE
|
||||||
|
{
|
||||||
|
bail!(
|
||||||
|
"Failed to get token type, error {}",
|
||||||
|
io::Error::last_os_error()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if token_type == TokenImpersonation {
|
||||||
|
let mut duplicate_token: HANDLE = std::ptr::null_mut();
|
||||||
|
let dup_res = DuplicateToken(user_token, SecurityImpersonation, &mut duplicate_token);
|
||||||
|
CloseHandle(user_token);
|
||||||
|
if dup_res == FALSE {
|
||||||
|
bail!(
|
||||||
|
"Failed to duplicate token, error {}",
|
||||||
|
io::Error::last_os_error()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(duplicate_token)
|
||||||
|
} else {
|
||||||
|
Ok(user_token)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_user_token_admin(user_token: HANDLE) -> ResultType<bool> {
|
||||||
|
if user_token.is_null() || user_token == INVALID_HANDLE_VALUE {
|
||||||
|
bail!("Invalid user token provided");
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let mut dw_size: DWORD = 0;
|
||||||
|
GetTokenInformation(
|
||||||
|
user_token,
|
||||||
|
TokenGroups,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
0,
|
||||||
|
&mut dw_size,
|
||||||
|
);
|
||||||
|
|
||||||
|
let last_error = GetLastError();
|
||||||
|
if last_error != ERROR_INSUFFICIENT_BUFFER {
|
||||||
|
bail!(
|
||||||
|
"Failed to get token groups buffer size, error: {}",
|
||||||
|
last_error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if dw_size == 0 {
|
||||||
|
bail!("Token groups buffer size is zero");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buffer = vec![0u8; dw_size as usize];
|
||||||
|
if GetTokenInformation(
|
||||||
|
user_token,
|
||||||
|
TokenGroups,
|
||||||
|
buffer.as_mut_ptr() as *mut _,
|
||||||
|
dw_size,
|
||||||
|
&mut dw_size,
|
||||||
|
) == FALSE
|
||||||
|
{
|
||||||
|
bail!(
|
||||||
|
"Failed to get token groups information, error: {}",
|
||||||
|
io::Error::last_os_error()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let p_token_groups = buffer.as_ptr() as *const TOKEN_GROUPS;
|
||||||
|
let group_count = (*p_token_groups).GroupCount;
|
||||||
|
|
||||||
|
if group_count == 0 {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut nt_authority: SID_IDENTIFIER_AUTHORITY = SID_IDENTIFIER_AUTHORITY {
|
||||||
|
Value: SECURITY_NT_AUTHORITY,
|
||||||
|
};
|
||||||
|
let mut administrators_group: PSID = std::ptr::null_mut();
|
||||||
|
if AllocateAndInitializeSid(
|
||||||
|
&mut nt_authority,
|
||||||
|
2,
|
||||||
|
SECURITY_BUILTIN_DOMAIN_RID,
|
||||||
|
DOMAIN_ALIAS_RID_ADMINS,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&mut administrators_group,
|
||||||
|
) == FALSE
|
||||||
|
{
|
||||||
|
bail!(
|
||||||
|
"Failed to allocate administrators group SID, error: {}",
|
||||||
|
io::Error::last_os_error()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if administrators_group.is_null() {
|
||||||
|
bail!("Failed to create administrators group SID");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut is_admin = false;
|
||||||
|
let groups =
|
||||||
|
std::slice::from_raw_parts((*p_token_groups).Groups.as_ptr(), group_count as usize);
|
||||||
|
for group in groups {
|
||||||
|
if EqualSid(administrators_group, group.Sid) == TRUE {
|
||||||
|
is_admin = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !administrators_group.is_null() {
|
||||||
|
FreeSid(administrators_group);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(is_admin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn create_process_with_logon(user: &str, pwd: &str, exe: &str, arg: &str) -> ResultType<()> {
|
pub fn create_process_with_logon(user: &str, pwd: &str, exe: &str, arg: &str) -> ResultType<()> {
|
||||||
let last_error_table = HashMap::from([
|
let last_error_table = HashMap::from([
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ use std::{
|
|||||||
};
|
};
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
use system_shutdown;
|
use system_shutdown;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
use windows::Win32::Foundation::{CloseHandle, HANDLE};
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use crate::virtual_display_manager;
|
use crate::virtual_display_manager;
|
||||||
@@ -172,6 +174,22 @@ pub enum AuthConnType {
|
|||||||
Terminal,
|
Terminal,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
enum TerminalUserToken {
|
||||||
|
SelfUser,
|
||||||
|
CurrentLogonUser(crate::terminal_service::UserToken),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
impl TerminalUserToken {
|
||||||
|
fn to_terminal_service_token(&self) -> Option<crate::terminal_service::UserToken> {
|
||||||
|
match self {
|
||||||
|
TerminalUserToken::SelfUser => None,
|
||||||
|
TerminalUserToken::CurrentLogonUser(token) => Some(*token),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
pub struct Connection {
|
pub struct Connection {
|
||||||
inner: ConnInner,
|
inner: ConnInner,
|
||||||
display_idx: usize,
|
display_idx: usize,
|
||||||
@@ -254,6 +272,11 @@ pub struct Connection {
|
|||||||
tx_post_seq: mpsc::UnboundedSender<(String, Value)>,
|
tx_post_seq: mpsc::UnboundedSender<(String, Value)>,
|
||||||
terminal_service_id: String,
|
terminal_service_id: String,
|
||||||
terminal_persistent: bool,
|
terminal_persistent: bool,
|
||||||
|
// The user token must be set when terminal is enabled.
|
||||||
|
// 0 indicates SYSTEM user
|
||||||
|
// other values indicate current user
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
terminal_user_token: Option<TerminalUserToken>,
|
||||||
terminal_generic_service: Option<Box<GenericService>>,
|
terminal_generic_service: Option<Box<GenericService>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,6 +441,8 @@ impl Connection {
|
|||||||
tx_post_seq,
|
tx_post_seq,
|
||||||
terminal_service_id: "".to_owned(),
|
terminal_service_id: "".to_owned(),
|
||||||
terminal_persistent: false,
|
terminal_persistent: false,
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
|
terminal_user_token: None,
|
||||||
terminal_generic_service: None,
|
terminal_generic_service: None,
|
||||||
};
|
};
|
||||||
let addr = hbb_common::try_into_v4(addr);
|
let addr = hbb_common::try_into_v4(addr);
|
||||||
@@ -1415,12 +1440,19 @@ impl Connection {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.insert(self.lr.my_id.clone(), self.tx_input.clone());
|
.insert(self.lr.my_id.clone(), self.tx_input.clone());
|
||||||
|
|
||||||
|
// Terminal feature is supported on desktop only
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut terminal = cfg!(not(any(target_os = "android", target_os = "ios")));
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
{
|
||||||
|
terminal = terminal && portable_pty::win::check_support().is_ok();
|
||||||
|
}
|
||||||
pi.username = username;
|
pi.username = username;
|
||||||
pi.sas_enabled = sas_enabled;
|
pi.sas_enabled = sas_enabled;
|
||||||
pi.features = Some(Features {
|
pi.features = Some(Features {
|
||||||
privacy_mode: privacy_mode::is_privacy_mode_supported(),
|
privacy_mode: privacy_mode::is_privacy_mode_supported(),
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
terminal: true, // Terminal feature is supported on desktop only
|
terminal,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.into();
|
.into();
|
||||||
@@ -1429,7 +1461,9 @@ impl Connection {
|
|||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut wait_session_id_confirm = false;
|
let mut wait_session_id_confirm = false;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
self.handle_windows_specific_session(&mut pi, &mut wait_session_id_confirm);
|
if !self.terminal {
|
||||||
|
self.handle_windows_specific_session(&mut pi, &mut wait_session_id_confirm);
|
||||||
|
}
|
||||||
if self.file_transfer.is_some() || self.terminal {
|
if self.file_transfer.is_some() || self.terminal {
|
||||||
res.set_peer_info(pi);
|
res.set_peer_info(pi);
|
||||||
} else if self.view_camera {
|
} else if self.view_camera {
|
||||||
@@ -1933,12 +1967,28 @@ impl Connection {
|
|||||||
sleep(1.).await;
|
sleep(1.).await;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
if !lr.os_login.username.is_empty() && !crate::platform::is_installed() {
|
||||||
|
self.send_login_error("Supported only by the installation version.")
|
||||||
|
.await;
|
||||||
|
sleep(1.).await;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
self.terminal = true;
|
self.terminal = true;
|
||||||
if let Some(o) = self.options_in_login.as_ref() {
|
if let Some(o) = self.options_in_login.as_ref() {
|
||||||
self.terminal_persistent =
|
self.terminal_persistent =
|
||||||
o.terminal_persistent.enum_value() == Ok(BoolOption::Yes);
|
o.terminal_persistent.enum_value() == Ok(BoolOption::Yes);
|
||||||
}
|
}
|
||||||
self.terminal_service_id = terminal.service_id;
|
self.terminal_service_id = terminal.service_id;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
if let Some(msg) =
|
||||||
|
self.fill_terminal_user_token(&lr.os_login.username, &lr.os_login.password)
|
||||||
|
{
|
||||||
|
self.send_login_error(msg).await;
|
||||||
|
sleep(1.).await;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Some(login_request::Union::PortForward(mut pf)) => {
|
Some(login_request::Union::PortForward(mut pf)) => {
|
||||||
if !Connection::permission("enable-tunnel") {
|
if !Connection::permission("enable-tunnel") {
|
||||||
@@ -2893,6 +2943,94 @@ impl Connection {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to fill user token for terminal connection.
|
||||||
|
// If username is empty, use the user token of the current session.
|
||||||
|
// If username is not empty, try to logon and check if the user is an administrator.
|
||||||
|
// If the user is an administrator, use the user token of current process (SYSTEM).
|
||||||
|
// If the user is not an administrator, return an error message.
|
||||||
|
// Note: Only local and domain users are supported, Microsoft account (online account) not supported for now.
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn fill_terminal_user_token(&mut self, username: &str, password: &str) -> Option<&'static str> {
|
||||||
|
// No need to check if the password is empty.
|
||||||
|
if !username.is_empty() {
|
||||||
|
return self.handle_administrator_check(username, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
if crate::platform::is_prelogin() {
|
||||||
|
self.terminal_user_token = None;
|
||||||
|
return Some("No active console user logged on, please connect and logon first.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if crate::platform::is_installed() {
|
||||||
|
return self.handle_installed_user();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.terminal_user_token = Some(TerminalUserToken::SelfUser);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn handle_administrator_check(
|
||||||
|
&mut self,
|
||||||
|
username: &str,
|
||||||
|
password: &str,
|
||||||
|
) -> Option<&'static str> {
|
||||||
|
let check_admin_res =
|
||||||
|
crate::platform::get_logon_user_token(username, password).map(|token| {
|
||||||
|
let is_token_admin = crate::platform::is_user_token_admin(token);
|
||||||
|
unsafe {
|
||||||
|
hbb_common::allow_err!(CloseHandle(HANDLE(token as _)));
|
||||||
|
};
|
||||||
|
is_token_admin
|
||||||
|
});
|
||||||
|
match check_admin_res {
|
||||||
|
Ok(Ok(b)) => {
|
||||||
|
if b {
|
||||||
|
self.terminal_user_token = Some(TerminalUserToken::SelfUser);
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some("The user is not an administrator.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(Err(e)) => {
|
||||||
|
log::error!("Failed to check if the user is an administrator: {}", e);
|
||||||
|
Some("Failed to check if the user is an administrator.")
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to get logon user token: {}", e);
|
||||||
|
Some("Incorrect username or password.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn handle_installed_user(&mut self) -> Option<&'static str> {
|
||||||
|
let session_id = crate::platform::get_current_session_id(true);
|
||||||
|
if session_id == 0xFFFFFFFF {
|
||||||
|
return Some("Failed to get current session id.");
|
||||||
|
}
|
||||||
|
let token = crate::platform::get_user_token(session_id, true);
|
||||||
|
if !token.is_null() {
|
||||||
|
match crate::platform::ensure_primary_token(token) {
|
||||||
|
Ok(t) => {
|
||||||
|
self.terminal_user_token = Some(TerminalUserToken::CurrentLogonUser(t as _));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to ensure primary token: {}", e);
|
||||||
|
self.terminal_user_token =
|
||||||
|
Some(TerminalUserToken::CurrentLogonUser(token as _));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
log::error!(
|
||||||
|
"Failed to get user token for terminal action, {}",
|
||||||
|
std::io::Error::last_os_error()
|
||||||
|
);
|
||||||
|
Some("Failed to get user token.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn update_failure(&self, (mut failure, time): ((i32, i32, i32), i32), remove: bool, i: usize) {
|
fn update_failure(&self, (mut failure, time): ((i32, i32, i32), i32), remove: bool, i: usize) {
|
||||||
if remove {
|
if remove {
|
||||||
if failure.0 != 0 {
|
if failure.0 != 0 {
|
||||||
@@ -3833,12 +3971,19 @@ impl Connection {
|
|||||||
|
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
async fn init_terminal_service(&mut self) {
|
async fn init_terminal_service(&mut self) {
|
||||||
|
debug_assert!(self.terminal_user_token.is_some());
|
||||||
|
let Some(user_token) = self.terminal_user_token.clone() else {
|
||||||
|
// unreachable, but keep it for safety
|
||||||
|
log::error!("Terminal user token is not set.");
|
||||||
|
return;
|
||||||
|
};
|
||||||
if self.terminal_service_id.is_empty() {
|
if self.terminal_service_id.is_empty() {
|
||||||
self.terminal_service_id = terminal_service::generate_service_id();
|
self.terminal_service_id = terminal_service::generate_service_id();
|
||||||
}
|
}
|
||||||
let s = Box::new(terminal_service::new(
|
let s = Box::new(terminal_service::new(
|
||||||
self.terminal_service_id.clone(),
|
self.terminal_service_id.clone(),
|
||||||
self.terminal_persistent,
|
self.terminal_persistent,
|
||||||
|
user_token.to_terminal_service_token(),
|
||||||
));
|
));
|
||||||
s.on_subscribe(self.inner.clone());
|
s.on_subscribe(self.inner.clone());
|
||||||
self.terminal_generic_service = Some(s);
|
self.terminal_generic_service = Some(s);
|
||||||
@@ -3846,9 +3991,15 @@ impl Connection {
|
|||||||
|
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
async fn handle_terminal_action(&mut self, action: TerminalAction) -> ResultType<()> {
|
async fn handle_terminal_action(&mut self, action: TerminalAction) -> ResultType<()> {
|
||||||
|
debug_assert!(self.terminal_user_token.is_some());
|
||||||
|
let Some(user_token) = self.terminal_user_token.clone() else {
|
||||||
|
// unreacheable, but keep it for safety
|
||||||
|
bail!("Terminal user token is not set.");
|
||||||
|
};
|
||||||
let mut proxy = terminal_service::TerminalServiceProxy::new(
|
let mut proxy = terminal_service::TerminalServiceProxy::new(
|
||||||
self.terminal_service_id.clone(),
|
self.terminal_service_id.clone(),
|
||||||
Some(self.terminal_persistent),
|
Some(self.terminal_persistent),
|
||||||
|
user_token.to_terminal_service_token(),
|
||||||
);
|
);
|
||||||
|
|
||||||
match proxy.handle_action(&action) {
|
match proxy.handle_action(&action) {
|
||||||
@@ -4249,6 +4400,15 @@ impl Drop for Connection {
|
|||||||
if let Some(s) = self.terminal_generic_service.as_ref() {
|
if let Some(s) = self.terminal_generic_service.as_ref() {
|
||||||
s.join();
|
s.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
if let Some(TerminalUserToken::CurrentLogonUser(token)) = self.terminal_user_token.take() {
|
||||||
|
if token != 0 {
|
||||||
|
unsafe {
|
||||||
|
hbb_common::allow_err!(CloseHandle(HANDLE(token as _)));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use portable_pty::{Child, CommandBuilder, PtySize};
|
|||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, VecDeque},
|
collections::{HashMap, VecDeque},
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
sync::{
|
sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
mpsc::{self, Receiver, SyncSender},
|
mpsc::{self, Receiver, SyncSender},
|
||||||
@@ -271,17 +272,51 @@ pub fn get_terminal_session_count(include_zombie_tasks: bool) -> usize {
|
|||||||
c
|
c
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(service_id: String, is_persistent: bool) -> GenericService {
|
pub type UserToken = u64;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TerminalService {
|
||||||
|
sp: GenericService,
|
||||||
|
user_token: Option<UserToken>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for TerminalService {
|
||||||
|
type Target = ServiceTmpl<ConnInner>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.sp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for TerminalService {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
&mut self.sp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_service_name(source: VideoSource, idx: usize) -> String {
|
||||||
|
format!("{}{}", source.service_name_prefix(), idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(
|
||||||
|
service_id: String,
|
||||||
|
is_persistent: bool,
|
||||||
|
user_token: Option<UserToken>,
|
||||||
|
) -> GenericService {
|
||||||
// Create the service with initial persistence setting
|
// Create the service with initial persistence setting
|
||||||
allow_err!(get_or_create_service(service_id.clone(), is_persistent));
|
allow_err!(get_or_create_service(service_id.clone(), is_persistent));
|
||||||
let svc = EmptyExtraFieldService::new(service_id.clone(), false);
|
let svc = TerminalService {
|
||||||
|
sp: GenericService::new(service_id.clone(), false),
|
||||||
|
user_token,
|
||||||
|
};
|
||||||
GenericService::run(&svc.clone(), move |sp| run(sp, service_id.clone()));
|
GenericService::run(&svc.clone(), move |sp| run(sp, service_id.clone()));
|
||||||
svc.sp
|
svc.sp
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(sp: EmptyExtraFieldService, service_id: String) -> ResultType<()> {
|
fn run(sp: TerminalService, service_id: String) -> ResultType<()> {
|
||||||
while sp.ok() {
|
while sp.ok() {
|
||||||
let responses = TerminalServiceProxy::new(service_id.clone(), None).read_outputs();
|
let responses = TerminalServiceProxy::new(service_id.clone(), None, sp.user_token.clone())
|
||||||
|
.read_outputs();
|
||||||
for response in responses {
|
for response in responses {
|
||||||
let mut msg_out = Message::new();
|
let mut msg_out = Message::new();
|
||||||
msg_out.set_terminal_response(response);
|
msg_out.set_terminal_response(response);
|
||||||
@@ -451,6 +486,7 @@ impl TerminalSession {
|
|||||||
}
|
}
|
||||||
drop(input_tx);
|
drop(input_tx);
|
||||||
}
|
}
|
||||||
|
self.output_rx = None;
|
||||||
|
|
||||||
// Wait for threads to finish
|
// Wait for threads to finish
|
||||||
// The reader thread should join before the writer thread on Windows.
|
// The reader thread should join before the writer thread on Windows.
|
||||||
@@ -544,6 +580,8 @@ impl PersistentTerminalService {
|
|||||||
pub struct TerminalServiceProxy {
|
pub struct TerminalServiceProxy {
|
||||||
service_id: String,
|
service_id: String,
|
||||||
is_persistent: bool,
|
is_persistent: bool,
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
user_token: Option<UserToken>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_persistent(service_id: &str, is_persistent: bool) -> Result<()> {
|
pub fn set_persistent(service_id: &str, is_persistent: bool) -> Result<()> {
|
||||||
@@ -556,7 +594,11 @@ pub fn set_persistent(service_id: &str, is_persistent: bool) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TerminalServiceProxy {
|
impl TerminalServiceProxy {
|
||||||
pub fn new(service_id: String, is_persistent: Option<bool>) -> Self {
|
pub fn new(
|
||||||
|
service_id: String,
|
||||||
|
is_persistent: Option<bool>,
|
||||||
|
_user_token: Option<UserToken>,
|
||||||
|
) -> Self {
|
||||||
// Get persistence from the service if it exists
|
// Get persistence from the service if it exists
|
||||||
let is_persistent =
|
let is_persistent =
|
||||||
is_persistent.unwrap_or(if let Some(service) = get_service(&service_id) {
|
is_persistent.unwrap_or(if let Some(service) = get_service(&service_id) {
|
||||||
@@ -567,6 +609,8 @@ impl TerminalServiceProxy {
|
|||||||
TerminalServiceProxy {
|
TerminalServiceProxy {
|
||||||
service_id,
|
service_id,
|
||||||
is_persistent,
|
is_persistent,
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
user_token: _user_token,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -670,7 +714,14 @@ impl TerminalServiceProxy {
|
|||||||
// Use default shell for the platform
|
// Use default shell for the platform
|
||||||
let shell = get_default_shell();
|
let shell = get_default_shell();
|
||||||
log::debug!("Using shell: {}", shell);
|
log::debug!("Using shell: {}", shell);
|
||||||
let cmd = CommandBuilder::new(&shell);
|
|
||||||
|
#[allow(unused_mut)]
|
||||||
|
let mut cmd = CommandBuilder::new(&shell);
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
if let Some(token) = &self.user_token {
|
||||||
|
cmd.set_user_token(*token as _);
|
||||||
|
}
|
||||||
|
|
||||||
log::debug!("Spawning shell process...");
|
log::debug!("Spawning shell process...");
|
||||||
let child = pty_pair
|
let child = pty_pair
|
||||||
|
|||||||
Reference in New Issue
Block a user