Feat: Windows connect to a specific user session (#6825)
* feat windows connect to specific user session Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix import Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix multiple user session fields Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix build Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix file transfer Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix text color on light theme Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * feat windows connect to specific user session code changes and sciter support Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * update texts Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix sciter selected user session Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * add translations Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * Use Y,N options * feat windows specific user code changes Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * Update dialog.dart * Update connection.rs * Update connection.rs * feat windows specific user code changes Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix sciter Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use lr.union Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove unused peer options Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * select user only when authorised and no existing connection Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * check for multiple users only once Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * optimise and add check for client version Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * use misc option message Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * update rdp user session proto Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix show cm on user session Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * Update pl.rs * update on_message Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix cm Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * remove user_session_id Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix cm Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> * fix multiple connections Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com> --------- Signed-off-by: Sahil Yeole <sahilyeole93@gmail.com>
This commit is contained in:
parent
236687ae53
commit
4bf3764b5d
@ -1861,3 +1861,106 @@ void enter2FaDialog(
|
||||
onCancel: cancel);
|
||||
});
|
||||
}
|
||||
|
||||
void showWindowsSessionsDialog(
|
||||
String type,
|
||||
String title,
|
||||
String text,
|
||||
OverlayDialogManager dialogManager,
|
||||
SessionID sessionId,
|
||||
String peerId,
|
||||
String sessions) {
|
||||
List<String> sessionsList = sessions.split(',');
|
||||
Map<String, String> sessionMap = {};
|
||||
for (var session in sessionsList) {
|
||||
var sessionInfo = session.split('-');
|
||||
if (sessionInfo.isNotEmpty) {
|
||||
sessionMap[sessionInfo[0]] = sessionInfo[1];
|
||||
}
|
||||
}
|
||||
String selectedUserValue = sessionMap.keys.first;
|
||||
dialogManager.dismissAll();
|
||||
dialogManager.show((setState, close, context) {
|
||||
onConnect() {
|
||||
bind.sessionReconnect(
|
||||
sessionId: sessionId,
|
||||
forceRelay: false,
|
||||
userSessionId: selectedUserValue);
|
||||
dialogManager.dismissAll();
|
||||
dialogManager.showLoading(translate('Connecting...'),
|
||||
onCancel: closeConnection);
|
||||
}
|
||||
|
||||
return CustomAlertDialog(
|
||||
title: null,
|
||||
content: msgboxContent(type, title, text),
|
||||
actions: [
|
||||
SessionsDropdown(peerId, sessionId, sessionMap, (value) {
|
||||
setState(() {
|
||||
selectedUserValue = value;
|
||||
});
|
||||
}),
|
||||
dialogButton('Connect', onPressed: onConnect, isOutline: false),
|
||||
],
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
class SessionsDropdown extends StatefulWidget {
|
||||
final String peerId;
|
||||
final SessionID sessionId;
|
||||
final Map<String, String> sessions;
|
||||
final Function(String) onValueChanged;
|
||||
|
||||
SessionsDropdown(
|
||||
this.peerId, this.sessionId, this.sessions, this.onValueChanged);
|
||||
|
||||
@override
|
||||
_SessionsDropdownState createState() => _SessionsDropdownState();
|
||||
}
|
||||
|
||||
class _SessionsDropdownState extends State<SessionsDropdown> {
|
||||
late String selectedValue;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
selectedValue = widget.sessions.keys.first;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 300,
|
||||
child: DropdownButton<String>(
|
||||
value: selectedValue,
|
||||
isExpanded: true,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
items: widget.sessions.entries.map((entry) {
|
||||
return DropdownMenuItem(
|
||||
value: entry.key,
|
||||
child: Text(
|
||||
entry.value,
|
||||
style: TextStyle(
|
||||
color: MyTheme.currentThemeMode() == ThemeMode.dark
|
||||
? Colors.white
|
||||
: MyTheme.dark,
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
selectedValue = value;
|
||||
});
|
||||
widget.onValueChanged(value);
|
||||
}
|
||||
},
|
||||
style: TextStyle(
|
||||
fontSize: 16.0,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_breadcrumb/flutter_breadcrumb.dart';
|
||||
import 'package:flutter_hbb/models/file_model.dart';
|
||||
import 'package:flutter_hbb/models/platform_model.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:toggle_switch/toggle_switch.dart';
|
||||
import 'package:wakelock_plus/wakelock_plus.dart';
|
||||
|
@ -245,6 +245,8 @@ class FfiModel with ChangeNotifier {
|
||||
var name = evt['name'];
|
||||
if (name == 'msgbox') {
|
||||
handleMsgBox(evt, sessionId, peerId);
|
||||
} else if (name == 'set_multiple_user_session') {
|
||||
handleMultipleUserSession(evt, sessionId, peerId);
|
||||
} else if (name == 'peer_info') {
|
||||
handlePeerInfo(evt, peerId, false);
|
||||
} else if (name == 'sync_peer_info') {
|
||||
@ -488,6 +490,19 @@ class FfiModel with ChangeNotifier {
|
||||
dialogManager.dismissByTag(tag);
|
||||
}
|
||||
|
||||
handleMultipleUserSession(
|
||||
Map<String, dynamic> evt, SessionID sessionId, String peerId) {
|
||||
if (parent.target == null) return;
|
||||
final dialogManager = parent.target!.dialogManager;
|
||||
final sessions = evt['user_sessions'];
|
||||
final title = translate('Multiple active user sessions found');
|
||||
final text = translate('Please select the user you want to connect to');
|
||||
final type = "";
|
||||
|
||||
showWindowsSessionsDialog(
|
||||
type, title, text, dialogManager, sessionId, peerId, sessions);
|
||||
}
|
||||
|
||||
/// Handle the message box event based on [evt] and [id].
|
||||
handleMsgBox(Map<String, dynamic> evt, SessionID sessionId, String peerId) {
|
||||
if (parent.target == null) return;
|
||||
@ -549,7 +564,8 @@ class FfiModel with ChangeNotifier {
|
||||
|
||||
void reconnect(OverlayDialogManager dialogManager, SessionID sessionId,
|
||||
bool forceRelay) {
|
||||
bind.sessionReconnect(sessionId: sessionId, forceRelay: forceRelay);
|
||||
bind.sessionReconnect(
|
||||
sessionId: sessionId, forceRelay: forceRelay, userSessionId: "");
|
||||
clearPermissions();
|
||||
dialogManager.dismissAll();
|
||||
dialogManager.showLoading(translate('Connecting...'),
|
||||
|
@ -124,6 +124,11 @@ message PeerInfo {
|
||||
string platform_additions = 12;
|
||||
}
|
||||
|
||||
message RdpUserSession {
|
||||
string user_session_id = 1;
|
||||
string user_name = 2;
|
||||
}
|
||||
|
||||
message LoginResponse {
|
||||
oneof union {
|
||||
string error = 1;
|
||||
@ -589,6 +594,7 @@ message OptionMessage {
|
||||
BoolOption disable_keyboard = 12;
|
||||
// Position 13 is used for Resolution. Remove later.
|
||||
// Resolution custom_resolution = 13;
|
||||
string user_session = 14;
|
||||
}
|
||||
|
||||
message TestDelay {
|
||||
@ -703,6 +709,10 @@ message PluginFailure {
|
||||
string msg = 3;
|
||||
}
|
||||
|
||||
message RdpUserSessions {
|
||||
repeated RdpUserSession rdp_user_sessions = 1;
|
||||
}
|
||||
|
||||
message Misc {
|
||||
oneof union {
|
||||
ChatMessage chat_message = 4;
|
||||
@ -734,6 +744,7 @@ message Misc {
|
||||
ToggleVirtualDisplay toggle_virtual_display = 32;
|
||||
TogglePrivacyMode toggle_privacy_mode = 33;
|
||||
SupportedEncoding supported_encoding = 34;
|
||||
RdpUserSessions rdp_user_sessions = 35;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1149,6 +1149,7 @@ pub struct LoginConfigHandler {
|
||||
pub custom_fps: Arc<Mutex<Option<usize>>>,
|
||||
pub adapter_luid: Option<i64>,
|
||||
pub mark_unsupported: Vec<CodecFormat>,
|
||||
pub selected_user_session_id: String,
|
||||
}
|
||||
|
||||
impl Deref for LoginConfigHandler {
|
||||
@ -1235,6 +1236,7 @@ impl LoginConfigHandler {
|
||||
self.received = false;
|
||||
self.switch_uuid = switch_uuid;
|
||||
self.adapter_luid = adapter_luid;
|
||||
self.selected_user_session_id = "".to_owned();
|
||||
}
|
||||
|
||||
/// Check if the client should auto login.
|
||||
@ -1511,14 +1513,17 @@ impl LoginConfigHandler {
|
||||
///
|
||||
/// * `ignore_default` - If `true`, ignore the default value of the option.
|
||||
fn get_option_message(&self, ignore_default: bool) -> Option<OptionMessage> {
|
||||
if self.conn_type.eq(&ConnType::FILE_TRANSFER)
|
||||
|| self.conn_type.eq(&ConnType::PORT_FORWARD)
|
||||
|| self.conn_type.eq(&ConnType::RDP)
|
||||
{
|
||||
if self.conn_type.eq(&ConnType::PORT_FORWARD) || self.conn_type.eq(&ConnType::RDP) {
|
||||
return None;
|
||||
}
|
||||
let mut n = 0;
|
||||
let mut msg = OptionMessage::new();
|
||||
msg.user_session = self.selected_user_session_id.clone();
|
||||
n += 1;
|
||||
|
||||
if self.conn_type.eq(&ConnType::FILE_TRANSFER) {
|
||||
return Some(msg);
|
||||
}
|
||||
let q = self.image_quality.clone();
|
||||
if let Some(q) = self.get_image_quality_enum(&q, ignore_default) {
|
||||
msg.image_quality = q.into();
|
||||
@ -1581,7 +1586,6 @@ impl LoginConfigHandler {
|
||||
&self.mark_unsupported,
|
||||
));
|
||||
n += 1;
|
||||
|
||||
if n > 0 {
|
||||
Some(msg)
|
||||
} else {
|
||||
@ -2740,6 +2744,7 @@ pub trait Interface: Send + Clone + 'static + Sized {
|
||||
fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str);
|
||||
fn handle_login_error(&self, err: &str) -> bool;
|
||||
fn handle_peer_info(&self, pi: PeerInfo);
|
||||
fn set_multiple_user_sessions(&self, sessions: Vec<hbb_common::message_proto::RdpUserSession>);
|
||||
fn on_error(&self, err: &str) {
|
||||
self.msgbox("error", "Error", err, "");
|
||||
}
|
||||
|
@ -1314,6 +1314,13 @@ impl<T: InvokeUiSession> Remote<T> {
|
||||
}
|
||||
}
|
||||
Some(message::Union::Misc(misc)) => match misc.union {
|
||||
Some(misc::Union::RdpUserSessions(sessions)) => {
|
||||
if !sessions.rdp_user_sessions.is_empty() {
|
||||
self.handler
|
||||
.set_multiple_user_session(sessions.rdp_user_sessions);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Some(misc::Union::AudioFormat(f)) => {
|
||||
self.audio_sender.send(MediaData::AudioFormat(f)).ok();
|
||||
}
|
||||
|
@ -826,6 +826,18 @@ impl InvokeUiSession for FlutterHandler {
|
||||
)
|
||||
}
|
||||
|
||||
fn set_multiple_user_session(&self, sessions: Vec<hbb_common::message_proto::RdpUserSession>) {
|
||||
let formatted_sessions: Vec<String> = sessions
|
||||
.iter()
|
||||
.map(|session| format!("{}-{}", session.user_session_id, session.user_name))
|
||||
.collect();
|
||||
let sessions = formatted_sessions.join(",");
|
||||
self.push_event(
|
||||
"set_multiple_user_session",
|
||||
vec![("user_sessions", &sessions)],
|
||||
);
|
||||
}
|
||||
|
||||
fn on_connected(&self, _conn_type: ConnType) {}
|
||||
|
||||
fn msgbox(&self, msgtype: &str, title: &str, text: &str, link: &str, retry: bool) {
|
||||
|
@ -217,9 +217,9 @@ pub fn session_record_status(session_id: SessionID, status: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn session_reconnect(session_id: SessionID, force_relay: bool) {
|
||||
pub fn session_reconnect(session_id: SessionID, force_relay: bool, user_session_id: String) {
|
||||
if let Some(session) = sessions::get_session_by_session_id(&session_id) {
|
||||
session.reconnect(force_relay);
|
||||
session.reconnect(force_relay, user_session_id);
|
||||
}
|
||||
session_on_waiting_for_image_dialog_show(session_id);
|
||||
}
|
||||
|
@ -187,6 +187,7 @@ pub enum Data {
|
||||
Authorize,
|
||||
Close,
|
||||
SAS,
|
||||
UserSid(Option<u32>),
|
||||
OnlineStatus(Option<(i64, bool)>),
|
||||
Config((String, Option<String>)),
|
||||
Options(Option<HashMap<String, String>>),
|
||||
@ -917,6 +918,13 @@ pub fn close_all_instances() -> ResultType<bool> {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
pub async fn connect_to_user_session(usid: Option<u32>) -> ResultType<()> {
|
||||
let mut stream = crate::ipc::connect(1000, crate::POSTFIX_SERVICE).await?;
|
||||
timeout(1000, stream.send(&crate::ipc::Data::UserSid(usid))).await??;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", "双重认证"),
|
||||
("Email verification code must be 6 characters.", "Email 验证码必须是 6 个字符。"),
|
||||
("2FA code must be 6 digits.", "双重认证代码必须是 6 位数字。"),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", "Dvoufaktorová autentizace"),
|
||||
("Email verification code must be 6 characters.", "E-mailový ověřovací kód musí mít 6 znaků."),
|
||||
("2FA code must be 6 digits.", "Kód 2FA musí mít 6 číslic."),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", "Zwei-Faktor-Authentifizierung"),
|
||||
("Email verification code must be 6 characters.", "Der E-Mail-Verifizierungscode muss aus 6 Zeichen bestehen."),
|
||||
("2FA code must be 6 digits.", "Der 2FA-Code muss 6 Ziffern haben."),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", "Autenticación en dos pasos"),
|
||||
("Email verification code must be 6 characters.", "El código de verificación por mail debe tener 6 caracteres"),
|
||||
("2FA code must be 6 digits.", "El cóidigo 2FA debe tener 6 dígitos"),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", "احراز هویت دو مرحله ای"),
|
||||
("Email verification code must be 6 characters.", "کد تأیید ایمیل باید 6 کاراکتر باشد"),
|
||||
("2FA code must be 6 digits.", "کد احراز هویت دو مرحله ای باید 6 رقم باشد"),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", "Autenticazione a due fattori"),
|
||||
("Email verification code must be 6 characters.", "Il codice di verifica email deve contenere 6 caratteri."),
|
||||
("2FA code must be 6 digits.", "Il codice 2FA deve essere composto da 6 cifre."),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", "Divu faktoru autentifikācija"),
|
||||
("Email verification code must be 6 characters.", "E-pasta verifikācijas kodam jābūt ar 6 rakstzīmēm."),
|
||||
("2FA code must be 6 digits.", "2FA kodam ir jābūt ar 6 cipariem."),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -585,5 +585,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", "geef-2fa-titel in"),
|
||||
("Email verification code must be 6 characters.", "E-mailverificatiecode moet 6 tekens lang zijn."),
|
||||
("2FA code must be 6 digits.", "2FA-code moet 6 cijfers lang zijn."),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", "Autoryzacja dwuskładnikowa"),
|
||||
("Email verification code must be 6 characters.", "Kod weryfikacyjny wysłany e-mailem musi mieć 6 znaków."),
|
||||
("2FA code must be 6 digits.", "Kod 2FA musi zawierać 6 cyfr."),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", "Двухфакторная аутентификация"),
|
||||
("Email verification code must be 6 characters.", "Код подтверждения электронной почты должен состоять из 6 символов."),
|
||||
("2FA code must be 6 digits.", "Код двухфакторной аутентификации должен состоять из 6 цифр."),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", "Dvojfaktorové overenie"),
|
||||
("Email verification code must be 6 characters.", "Overovací kód e-mailu musí mať 6 znakov."),
|
||||
("2FA code must be 6 digits.", "Kód 2FA musí obsahovať 6 číslic."),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", "二步驟驗證"),
|
||||
("Email verification code must be 6 characters.", "Email 驗證碼必須是 6 個字元。"),
|
||||
("2FA code must be 6 digits.", "二步驟驗證碼必須是 6 位數字。"),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -586,5 +586,7 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("enter-2fa-title", ""),
|
||||
("Email verification code must be 6 characters.", ""),
|
||||
("2FA code must be 6 digits.", ""),
|
||||
("Multiple active user sessions found", ""),
|
||||
("Please select the user you want to connect to", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <shlobj.h> // NOLINT(build/include_order)
|
||||
#include <userenv.h>
|
||||
#include <versionhelpers.h>
|
||||
#include <vector>
|
||||
|
||||
void flog(char const *fmt, ...)
|
||||
{
|
||||
@ -433,6 +434,74 @@ extern "C"
|
||||
return nout;
|
||||
}
|
||||
|
||||
uint32_t get_current_process_session_id()
|
||||
{
|
||||
DWORD sessionId = 0;
|
||||
HANDLE hProcess = GetCurrentProcess();
|
||||
if (hProcess) {
|
||||
ProcessIdToSessionId(GetCurrentProcessId(), &sessionId);
|
||||
CloseHandle(hProcess);
|
||||
}
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
uint32_t get_session_user_info(PWSTR bufin, uint32_t nin, BOOL rdp, uint32_t id)
|
||||
{
|
||||
uint32_t nout = 0;
|
||||
PWSTR buf = NULL;
|
||||
DWORD n = 0;
|
||||
if (WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, id, WTSUserName, &buf, &n))
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
nout = min(nin, n);
|
||||
memcpy(bufin, buf, nout);
|
||||
WTSFreeMemory(buf);
|
||||
}
|
||||
}
|
||||
return nout;
|
||||
}
|
||||
|
||||
void get_available_session_ids(PWSTR buf, uint32_t bufSize, BOOL include_rdp) {
|
||||
std::vector<std::wstring> sessionIds;
|
||||
PWTS_SESSION_INFOA pInfos;
|
||||
DWORD count;
|
||||
|
||||
if (WTSEnumerateSessionsA(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pInfos, &count)) {
|
||||
for (DWORD i = 0; i < count; i++) {
|
||||
auto info = pInfos[i];
|
||||
auto rdp = "rdp";
|
||||
auto nrdp = strlen(rdp);
|
||||
if (info.State == WTSActive) {
|
||||
if (info.pWinStationName == NULL)
|
||||
continue;
|
||||
if (info.SessionId == 65536 || info.SessionId == 655)
|
||||
continue;
|
||||
|
||||
if (!stricmp(info.pWinStationName, "console")){
|
||||
sessionIds.push_back(std::wstring(L"Console:") + std::to_wstring(info.SessionId));
|
||||
}
|
||||
else if (include_rdp && !strnicmp(info.pWinStationName, rdp, nrdp)) {
|
||||
sessionIds.push_back(std::wstring(L"RDP:") + std::to_wstring(info.SessionId));
|
||||
}
|
||||
}
|
||||
}
|
||||
WTSFreeMemory(pInfos);
|
||||
}
|
||||
|
||||
std::wstring tmpStr;
|
||||
for (size_t i = 0; i < sessionIds.size(); i++) {
|
||||
if (i > 0) {
|
||||
tmpStr += L",";
|
||||
}
|
||||
tmpStr += sessionIds[i];
|
||||
}
|
||||
|
||||
if (buf && !tmpStr.empty() && tmpStr.size() < bufSize) {
|
||||
memcpy(buf, tmpStr.c_str(), (tmpStr.size() + 1) * sizeof(wchar_t));
|
||||
}
|
||||
}
|
||||
|
||||
BOOL has_rdp_service()
|
||||
{
|
||||
PWTS_SESSION_INFOA pInfos;
|
||||
|
@ -5,6 +5,7 @@ use crate::{
|
||||
license::*,
|
||||
privacy_mode::win_topmost_window::{self, WIN_TOPMOST_INJECTED_PROCESS_EXE},
|
||||
};
|
||||
use hbb_common::libc::{c_int, wchar_t};
|
||||
use hbb_common::{
|
||||
allow_err,
|
||||
anyhow::anyhow,
|
||||
@ -508,7 +509,16 @@ async fn run_service(_arguments: Vec<OsString>) -> ResultType<()> {
|
||||
log::info!("session id {}", session_id);
|
||||
let mut h_process = launch_server(session_id, true).await.unwrap_or(NULL);
|
||||
let mut incoming = ipc::new_listener(crate::POSTFIX_SERVICE).await?;
|
||||
let mut stored_usid = None;
|
||||
loop {
|
||||
let sids = get_all_active_session_ids();
|
||||
if !sids.contains(&format!("{}", session_id)) || !is_share_rdp() {
|
||||
let current_active_session = unsafe { get_current_session(share_rdp()) };
|
||||
if session_id != current_active_session {
|
||||
session_id = current_active_session;
|
||||
h_process = launch_server(session_id, true).await.unwrap_or(NULL);
|
||||
}
|
||||
}
|
||||
let res = timeout(super::SERVICE_INTERVAL, incoming.next()).await;
|
||||
match res {
|
||||
Ok(res) => match res {
|
||||
@ -523,6 +533,21 @@ async fn run_service(_arguments: Vec<OsString>) -> ResultType<()> {
|
||||
ipc::Data::SAS => {
|
||||
send_sas();
|
||||
}
|
||||
ipc::Data::UserSid(usid) => {
|
||||
if let Some(usid) = usid {
|
||||
if session_id != usid {
|
||||
log::info!(
|
||||
"session changed from {} to {}",
|
||||
session_id,
|
||||
usid
|
||||
);
|
||||
session_id = usid;
|
||||
stored_usid = Some(session_id);
|
||||
h_process =
|
||||
launch_server(session_id, true).await.unwrap_or(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -537,7 +562,7 @@ async fn run_service(_arguments: Vec<OsString>) -> ResultType<()> {
|
||||
continue;
|
||||
}
|
||||
let mut close_sent = false;
|
||||
if tmp != session_id {
|
||||
if tmp != session_id && stored_usid != Some(session_id) {
|
||||
log::info!("session changed from {} to {}", session_id, tmp);
|
||||
session_id = tmp;
|
||||
send_close_async("").await.ok();
|
||||
@ -603,13 +628,16 @@ async fn launch_server(session_id: DWORD, close_first: bool) -> ResultType<HANDL
|
||||
Ok(h)
|
||||
}
|
||||
|
||||
pub fn run_as_user(arg: Vec<&str>) -> ResultType<Option<std::process::Child>> {
|
||||
pub fn run_as_user(arg: Vec<&str>, usid: Option<u32>) -> ResultType<Option<std::process::Child>> {
|
||||
let cmd = format!(
|
||||
"\"{}\" {}",
|
||||
std::env::current_exe()?.to_str().unwrap_or(""),
|
||||
arg.join(" "),
|
||||
);
|
||||
let session_id = unsafe { get_current_session(share_rdp()) };
|
||||
let mut session_id = get_current_process_session_id();
|
||||
if let Some(usid) = usid {
|
||||
session_id = usid;
|
||||
}
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
let wstr: Vec<u16> = std::ffi::OsStr::new(&cmd)
|
||||
.encode_wide()
|
||||
@ -684,10 +712,10 @@ pub fn try_change_desktop() -> bool {
|
||||
}
|
||||
|
||||
fn share_rdp() -> BOOL {
|
||||
if get_reg("share_rdp") != "true" {
|
||||
FALSE
|
||||
} else {
|
||||
if get_reg("share_rdp") != "false" {
|
||||
TRUE
|
||||
} else {
|
||||
FALSE
|
||||
}
|
||||
}
|
||||
|
||||
@ -705,6 +733,13 @@ pub fn set_share_rdp(enable: bool) {
|
||||
run_cmds(cmd, false, "share_rdp").ok();
|
||||
}
|
||||
|
||||
pub fn get_current_process_session_id() -> u32 {
|
||||
extern "C" {
|
||||
fn get_current_process_session_id() -> u32;
|
||||
}
|
||||
unsafe { get_current_process_session_id() }
|
||||
}
|
||||
|
||||
pub fn get_active_username() -> String {
|
||||
if !is_root() {
|
||||
return crate::username();
|
||||
@ -727,6 +762,76 @@ pub fn get_active_username() -> String {
|
||||
.to_owned()
|
||||
}
|
||||
|
||||
pub fn get_all_active_sessions() -> Vec<Vec<String>> {
|
||||
let sids = get_all_active_session_ids_with_station();
|
||||
let mut out = Vec::new();
|
||||
for sid in sids.split(',') {
|
||||
let username = get_session_username(sid.to_owned());
|
||||
if !username.is_empty() {
|
||||
let sid_split = sid.split(':').collect::<Vec<_>>()[1];
|
||||
let v = vec![sid_split.to_owned(), username];
|
||||
out.push(v);
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
pub fn get_session_username(session_id_with_station_name: String) -> String {
|
||||
let mut session_id = session_id_with_station_name.split(':');
|
||||
let station = session_id.next().unwrap_or("");
|
||||
let session_id = session_id.next().unwrap_or("");
|
||||
if session_id == "" {
|
||||
return "".to_owned();
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn get_session_user_info(path: *mut u16, n: u32, rdp: bool, session_id: u32) -> u32;
|
||||
}
|
||||
let buff_size = 256;
|
||||
let mut buff: Vec<u16> = Vec::with_capacity(buff_size);
|
||||
buff.resize(buff_size, 0);
|
||||
let n = unsafe {
|
||||
get_session_user_info(
|
||||
buff.as_mut_ptr(),
|
||||
buff_size as _,
|
||||
true,
|
||||
session_id.parse::<u32>().unwrap(),
|
||||
)
|
||||
};
|
||||
if n == 0 {
|
||||
return "".to_owned();
|
||||
}
|
||||
let sl = unsafe { std::slice::from_raw_parts(buff.as_ptr(), n as _) };
|
||||
let out = String::from_utf16(sl)
|
||||
.unwrap_or("".to_owned())
|
||||
.trim_end_matches('\0')
|
||||
.to_owned();
|
||||
station.to_owned() + ": " + &out
|
||||
}
|
||||
|
||||
pub fn get_all_active_session_ids_with_station() -> String {
|
||||
extern "C" {
|
||||
fn get_available_session_ids(buf: *mut wchar_t, buf_size: c_int, include_rdp: bool);
|
||||
}
|
||||
const BUF_SIZE: c_int = 1024;
|
||||
let mut buf: Vec<wchar_t> = vec![0; BUF_SIZE as usize];
|
||||
|
||||
unsafe {
|
||||
get_available_session_ids(buf.as_mut_ptr(), BUF_SIZE, true);
|
||||
let session_ids = String::from_utf16_lossy(&buf);
|
||||
session_ids.trim_matches(char::from(0)).trim().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_all_active_session_ids() -> String {
|
||||
let out = get_all_active_session_ids_with_station()
|
||||
.split(',')
|
||||
.map(|x| x.split(':').nth(1).unwrap_or(""))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
out.trim_matches(char::from(0)).trim().to_string()
|
||||
}
|
||||
|
||||
pub fn get_active_user_home() -> Option<PathBuf> {
|
||||
let username = get_active_username();
|
||||
if !username.is_empty() {
|
||||
|
@ -237,6 +237,8 @@ pub struct Connection {
|
||||
file_remove_log_control: FileRemoveLogControl,
|
||||
#[cfg(feature = "gpucodec")]
|
||||
supported_encoding_flag: (bool, Option<bool>),
|
||||
user_session_id: Option<u32>,
|
||||
checked_multiple_session: bool,
|
||||
}
|
||||
|
||||
impl ConnInner {
|
||||
@ -384,6 +386,8 @@ impl Connection {
|
||||
file_remove_log_control: FileRemoveLogControl::new(id),
|
||||
#[cfg(feature = "gpucodec")]
|
||||
supported_encoding_flag: (false, None),
|
||||
user_session_id: None,
|
||||
checked_multiple_session: false,
|
||||
};
|
||||
let addr = hbb_common::try_into_v4(addr);
|
||||
if !conn.on_open(addr).await {
|
||||
@ -1491,8 +1495,50 @@ impl Connection {
|
||||
self.video_ack_required = lr.video_ack_required;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
async fn handle_multiple_user_sessions(&mut self, usid: Option<u32>) -> bool {
|
||||
if self.port_forward_socket.is_some() {
|
||||
return true;
|
||||
} else {
|
||||
let active_sessions = crate::platform::get_all_active_sessions();
|
||||
if active_sessions.len() <= 1 {
|
||||
return true;
|
||||
}
|
||||
let current_process_usid = crate::platform::get_current_process_session_id();
|
||||
if usid.is_none() {
|
||||
let mut res = Misc::new();
|
||||
let mut rdp = Vec::new();
|
||||
for session in active_sessions {
|
||||
let u_sid = &session[0];
|
||||
let u_name = &session[1];
|
||||
let mut rdp_session = RdpUserSession::new();
|
||||
rdp_session.user_session_id = u_sid.clone();
|
||||
rdp_session.user_name = u_name.clone();
|
||||
rdp.push(rdp_session);
|
||||
}
|
||||
res.set_rdp_user_sessions(RdpUserSessions {
|
||||
rdp_user_sessions: rdp,
|
||||
..Default::default()
|
||||
});
|
||||
let mut msg_out = Message::new();
|
||||
msg_out.set_misc(res);
|
||||
self.send(msg_out).await;
|
||||
return true;
|
||||
}
|
||||
if usid != Some(current_process_usid) {
|
||||
self.on_close("Reconnecting...", false).await;
|
||||
std::thread::spawn(move || {
|
||||
let _ = ipc::connect_to_user_session(usid);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
fn try_start_cm_ipc(&mut self) {
|
||||
let usid = self.user_session_id;
|
||||
if let Some(p) = self.start_cm_ipc_para.take() {
|
||||
tokio::spawn(async move {
|
||||
#[cfg(windows)]
|
||||
@ -1502,6 +1548,7 @@ impl Connection {
|
||||
p.tx_from_cm,
|
||||
p.rx_desktop_ready,
|
||||
p.tx_cm_stream_ready,
|
||||
usid.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
@ -1513,9 +1560,9 @@ impl Connection {
|
||||
}
|
||||
});
|
||||
#[cfg(all(windows, feature = "flutter"))]
|
||||
std::thread::spawn(|| {
|
||||
std::thread::spawn(move || {
|
||||
if crate::is_server() && !crate::check_process("--tray", false) {
|
||||
crate::platform::run_as_user(vec!["--tray"]).ok();
|
||||
crate::platform::run_as_user(vec!["--tray"], usid).ok();
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1523,6 +1570,19 @@ impl Connection {
|
||||
|
||||
async fn on_message(&mut self, msg: Message) -> bool {
|
||||
if let Some(message::Union::LoginRequest(lr)) = msg.union {
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
if !self.checked_multiple_session {
|
||||
let usid;
|
||||
match lr.option.user_session.parse::<u32>() {
|
||||
Ok(n) => usid = Some(n),
|
||||
Err(..) => usid = None,
|
||||
}
|
||||
if usid.is_some() {
|
||||
self.user_session_id = usid;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.handle_login_request_without_validation(&lr).await;
|
||||
if self.authorized {
|
||||
return true;
|
||||
@ -1761,6 +1821,22 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
} else if self.authorized {
|
||||
#[cfg(target_os = "windows")]
|
||||
if !self.checked_multiple_session {
|
||||
self.checked_multiple_session = true;
|
||||
if crate::platform::is_installed()
|
||||
&& crate::platform::is_share_rdp()
|
||||
&& !(*CONN_COUNT.lock().unwrap() > 1)
|
||||
&& get_version_number(&self.lr.version) >= get_version_number("1.2.4")
|
||||
{
|
||||
if !self
|
||||
.handle_multiple_user_sessions(self.user_session_id)
|
||||
.await
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
match msg.union {
|
||||
Some(message::Union::MouseEvent(me)) => {
|
||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||
@ -3010,6 +3086,7 @@ async fn start_ipc(
|
||||
tx_from_cm: mpsc::UnboundedSender<ipc::Data>,
|
||||
mut _rx_desktop_ready: mpsc::Receiver<()>,
|
||||
tx_stream_ready: mpsc::Sender<()>,
|
||||
user_session_id: Option<u32>,
|
||||
) -> ResultType<()> {
|
||||
use hbb_common::anyhow::anyhow;
|
||||
|
||||
@ -3057,7 +3134,7 @@ async fn start_ipc(
|
||||
if crate::platform::is_root() {
|
||||
let mut res = Ok(None);
|
||||
for _ in 0..10 {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
|
||||
{
|
||||
log::debug!("Start cm");
|
||||
res = crate::platform::run_as_user(args.clone());
|
||||
@ -3071,6 +3148,11 @@ async fn start_ipc(
|
||||
None::<(&str, &str)>,
|
||||
);
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
log::debug!("Start cm");
|
||||
res = crate::platform::run_as_user(args.clone(), user_session_id);
|
||||
}
|
||||
if res.is_ok() {
|
||||
break;
|
||||
}
|
||||
|
@ -461,3 +461,18 @@ div#msgbox div.set-password input {
|
||||
div#msgbox #error {
|
||||
color: red;
|
||||
}
|
||||
|
||||
div.user-session .title {
|
||||
font-size: 1.2em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
div.user-session select {
|
||||
width: 98%;
|
||||
height: 2em;
|
||||
border-radius: 0.5em;
|
||||
border: color(border) solid 1px;
|
||||
background: color(bg);
|
||||
color: color(text);
|
||||
padding-left: 0.5em;
|
||||
}
|
@ -304,7 +304,21 @@ function msgbox(type, title, content, link="", callback=null, height=180, width=
|
||||
return;
|
||||
}
|
||||
};
|
||||
}
|
||||
} else if (type === "multiple-sessions") {
|
||||
var parts = content.split("-");
|
||||
var ids = parts[0].split(",");
|
||||
var names = parts[1].split(",");
|
||||
var sessionData = [];
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
sessionData.push({ id: ids[i], name: names[i] });
|
||||
}
|
||||
content = <MultipleSessionComponent sessions={sessionData} />;
|
||||
callback = function () {
|
||||
retryConnect();
|
||||
return;
|
||||
};
|
||||
height += 50;
|
||||
}
|
||||
last_msgbox_tag = type + "-" + title + "-" + content + "-" + link;
|
||||
$(#msgbox).content(<MsgboxComponent width={width} height={height} autoLogin={autoLogin} type={type} title={title} content={content} link={link} remember={remember} callback={callback} contentStyle={contentStyle} hasRetry={hasRetry} />);
|
||||
}
|
||||
@ -339,7 +353,7 @@ handler.msgbox_retry = function(type, title, text, link, hasRetry) {
|
||||
function retryConnect(cancelTimer=false) {
|
||||
if (cancelTimer) self.timer(0, retryConnect);
|
||||
if (!is_port_forward) connecting();
|
||||
handler.reconnect(false);
|
||||
handler.reconnect(false, "");
|
||||
}
|
||||
/******************** end of msgbox ****************************************/
|
||||
|
||||
@ -458,3 +472,37 @@ function awake() {
|
||||
view.focus = self;
|
||||
}
|
||||
|
||||
class MultipleSessionComponent extends Reactor.Component {
|
||||
this var sessions = [];
|
||||
this var selectedSessionId = null;
|
||||
this var sessionlength = 0;
|
||||
this var messageText = translate("Please select the user you want to connect to");
|
||||
|
||||
function this(params) {
|
||||
if (params && params.sessions) {
|
||||
this.sessions = params.sessions;
|
||||
this.selectedSessionId = params.sessions[0].id;
|
||||
this.sessions.map(session => {
|
||||
this.sessionlength += session.name.length;
|
||||
});
|
||||
}
|
||||
handler.set_selected_user_session_id(this.selectedSessionId);
|
||||
}
|
||||
|
||||
function render() {
|
||||
return <div .user-session>
|
||||
<div .title>{this.messageText}</div>
|
||||
<select>
|
||||
{this.sessions.map(session =>
|
||||
<option value={session.id}>{session.name}</option>
|
||||
)}
|
||||
</select>
|
||||
</div>;
|
||||
}
|
||||
|
||||
event change {
|
||||
var selectedSessionName = this.value.substr(this.messageText.length + this.sessionlength);
|
||||
this.selectedSessionId = this.sessions.find(session => session.name == selectedSessionName).id;
|
||||
handler.set_selected_user_session_id(this.selectedSessionId);
|
||||
}
|
||||
}
|
@ -527,6 +527,10 @@ handler.updateDisplays = function(v) {
|
||||
}
|
||||
}
|
||||
|
||||
handler.setMultipleUserSession = function(usid,uname) {
|
||||
msgbox("multiple-sessions", translate("Multiple active user sessions found"), usid+"-"+uname, "", function(res) {});
|
||||
}
|
||||
|
||||
function updatePrivacyMode() {
|
||||
var el = $(li#privacy-mode);
|
||||
if (el) {
|
||||
|
@ -259,6 +259,15 @@ impl InvokeUiSession for SciterHandler {
|
||||
// Ignore for sciter version.
|
||||
}
|
||||
|
||||
fn set_multiple_user_session(&self, sessions: Vec<hbb_common::message_proto::RdpUserSession>) {
|
||||
let formatted_sessions: Vec<String> = sessions.iter()
|
||||
.map(|session| format!("{}-{}", session.user_session_id, session.user_name))
|
||||
.collect();
|
||||
let u_sids: String = formatted_sessions.iter().map(|s| s.split("-").next().unwrap().to_string()).collect::<Vec<String>>().join(",");
|
||||
let u_names:String = formatted_sessions.iter().map(|s| s.split("-").nth(1).unwrap().to_string()).collect::<Vec<String>>().join(",");
|
||||
self.call("setMultipleUserSession", &make_args!(u_sids, u_names));
|
||||
}
|
||||
|
||||
fn on_connected(&self, conn_type: ConnType) {
|
||||
match conn_type {
|
||||
ConnType::RDP => {}
|
||||
@ -346,6 +355,7 @@ impl sciter::EventHandler for SciterSession {
|
||||
}
|
||||
|
||||
fn detached(&mut self, _root: HELEMENT) {
|
||||
self.set_selected_user_session_id("".to_string());
|
||||
*self.element.lock().unwrap() = None;
|
||||
self.sender.write().unwrap().take().map(|sender| {
|
||||
sender.send(Data::Close).ok();
|
||||
@ -376,7 +386,7 @@ impl sciter::EventHandler for SciterSession {
|
||||
let site = AssetPtr::adopt(ptr as *mut video_destination);
|
||||
log::debug!("[video] start video");
|
||||
*VIDEO.lock().unwrap() = Some(site);
|
||||
self.reconnect(false);
|
||||
self.reconnect(false, "".to_string());
|
||||
}
|
||||
}
|
||||
BEHAVIOR_EVENTS::VIDEO_INITIALIZED => {
|
||||
@ -426,7 +436,7 @@ impl sciter::EventHandler for SciterSession {
|
||||
fn transfer_file();
|
||||
fn tunnel();
|
||||
fn lock_screen();
|
||||
fn reconnect(bool);
|
||||
fn reconnect(bool, String);
|
||||
fn get_chatbox();
|
||||
fn get_icon();
|
||||
fn get_home_dir();
|
||||
@ -477,6 +487,7 @@ impl sciter::EventHandler for SciterSession {
|
||||
fn request_voice_call();
|
||||
fn close_voice_call();
|
||||
fn version_cmp(String, String);
|
||||
fn set_selected_user_session_id(String);
|
||||
}
|
||||
}
|
||||
|
||||
@ -580,6 +591,10 @@ impl SciterSession {
|
||||
log::info!("size saved");
|
||||
}
|
||||
|
||||
fn set_selected_user_session_id(&mut self, u_sid: String) {
|
||||
self.lc.write().unwrap().selected_user_session_id = u_sid;
|
||||
}
|
||||
|
||||
fn get_port_forwards(&mut self) -> Value {
|
||||
let port_forwards = self.lc.read().unwrap().port_forwards.clone();
|
||||
let mut v = Value::array(0);
|
||||
|
@ -1003,7 +1003,7 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reconnect(&self, force_relay: bool) {
|
||||
pub fn reconnect(&self, force_relay: bool, user_session_id: String) {
|
||||
// 1. If current session is connecting, do not reconnect.
|
||||
// 2. If the connection is established, send `Data::Close`.
|
||||
// 3. If the connection is disconnected, do nothing.
|
||||
@ -1023,6 +1023,9 @@ impl<T: InvokeUiSession> Session<T> {
|
||||
if true == force_relay {
|
||||
self.lc.write().unwrap().force_relay = true;
|
||||
}
|
||||
if !user_session_id.is_empty() {
|
||||
self.lc.write().unwrap().selected_user_session_id = user_session_id;
|
||||
}
|
||||
let mut lock = self.thread.lock().unwrap();
|
||||
// No need to join the previous thread, because it will exit automatically.
|
||||
// And the previous thread will not change important states.
|
||||
@ -1310,6 +1313,7 @@ pub trait InvokeUiSession: Send + Sync + Clone + 'static + Sized + Default {
|
||||
fn next_rgba(&self, display: usize);
|
||||
#[cfg(all(feature = "gpucodec", feature = "flutter"))]
|
||||
fn on_texture(&self, display: usize, texture: *mut c_void);
|
||||
fn set_multiple_user_session(&self, sessions: Vec<hbb_common::message_proto::RdpUserSession>);
|
||||
}
|
||||
|
||||
impl<T: InvokeUiSession> Deref for Session<T> {
|
||||
@ -1351,6 +1355,10 @@ impl<T: InvokeUiSession> Interface for Session<T> {
|
||||
handle_login_error(self.lc.clone(), err, self)
|
||||
}
|
||||
|
||||
fn set_multiple_user_sessions(&self, sessions: Vec<hbb_common::message_proto::RdpUserSession>) {
|
||||
self.ui_handler.set_multiple_user_session(sessions);
|
||||
}
|
||||
|
||||
fn handle_peer_info(&self, mut pi: PeerInfo) {
|
||||
log::debug!("handle_peer_info :{:?}", pi);
|
||||
pi.username = self.lc.read().unwrap().get_username(&pi);
|
||||
|
Loading…
Reference in New Issue
Block a user