linux virtual display, init commit

Signed-off-by: fufesou <shuanglongchen@yeah.net>
This commit is contained in:
fufesou
2023-03-22 17:01:11 +08:00
parent ddd0d6eafc
commit 10eddc139c
24 changed files with 1544 additions and 54 deletions

View File

@@ -85,8 +85,8 @@ impl Interface for Session {
handle_hash(self.lc.clone(), &pass, hash, self, peer).await;
}
async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) {
handle_login_from_ui(self.lc.clone(), password, remember, peer).await;
async fn handle_login_from_ui(&mut self, os_username: String, os_password: String, password: String, remember: bool, peer: &mut Stream) {
handle_login_from_ui(self.lc.clone(), os_username, os_password, password, remember, peer).await;
}
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) {

View File

@@ -1591,7 +1591,12 @@ impl LoginConfigHandler {
}
/// Create a [`Message`] for login.
fn create_login_msg(&self, password: Vec<u8>) -> Message {
fn create_login_msg(
&self,
os_username: String,
os_password: String,
password: Vec<u8>,
) -> Message {
#[cfg(any(target_os = "android", target_os = "ios"))]
let my_id = Config::get_id_or(crate::common::DEVICE_ID.lock().unwrap().clone());
#[cfg(not(any(target_os = "android", target_os = "ios")))]
@@ -1604,6 +1609,12 @@ impl LoginConfigHandler {
option: self.get_option_message(true).into(),
session_id: self.session_id,
version: crate::VERSION.to_string(),
os_login: Some(OSLogin {
username: os_username,
password: os_password,
..Default::default()
})
.into(),
..Default::default()
};
match self.conn_type {
@@ -1908,10 +1919,46 @@ pub fn handle_login_error(
err: &str,
interface: &impl Interface,
) -> bool {
if err == "Wrong Password" {
if err == crate::server::LOGIN_MSG_PASSWORD_EMPTY {
lc.write().unwrap().password = Default::default();
interface.msgbox("input-password", "Password Required", "", "");
true
} else if err == crate::server::LOGIN_MSG_PASSWORD_WRONG {
lc.write().unwrap().password = Default::default();
interface.msgbox("re-input-password", err, "Do you want to enter again?", "");
true
} else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY {
interface.msgbox(
"xsession-login",
"xsession is unready",
"Input linux user/password",
"",
);
true
} else if err == crate::server::LOGIN_MSG_XSESSION_FAILED {
interface.msgbox(
"xsession-re-login",
"xsession username/password is wrong",
"Do you want to enter again?",
"",
);
true
} else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY {
interface.msgbox(
"xsession-login-password",
"xsession is unready",
"Input connection password and linux user/password",
"",
);
true
} else if err == crate::server::LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG {
interface.msgbox(
"xsession-login-re-password",
"xsession is unready and password is wrong",
"Do you want to enter again?",
"",
);
true
} else if err == "No Password Access" {
lc.write().unwrap().password = Default::default();
interface.msgbox(
@@ -1944,7 +1991,7 @@ pub async fn handle_hash(
lc: Arc<RwLock<LoginConfigHandler>>,
password_preset: &str,
hash: Hash,
interface: &impl Interface,
_interface: &impl Interface,
peer: &mut Stream,
) {
lc.write().unwrap().hash = hash.clone();
@@ -1970,13 +2017,19 @@ pub async fn handle_hash(
}
if password.is_empty() {
// login without password, the remote side can click accept
send_login(lc.clone(), Vec::new(), peer).await;
interface.msgbox("input-password", "Password Required", "", "");
send_login(lc.clone(), "".to_owned(), "".to_owned(), Vec::new(), peer).await;
} else {
let mut hasher = Sha256::new();
hasher.update(&password);
hasher.update(&hash.challenge);
send_login(lc.clone(), hasher.finalize()[..].into(), peer).await;
send_login(
lc.clone(),
"".to_owned(),
"".to_owned(),
hasher.finalize()[..].into(),
peer,
)
.await;
}
lc.write().unwrap().hash = hash;
}
@@ -1986,10 +2039,21 @@ pub async fn handle_hash(
/// # Arguments
///
/// * `lc` - Login config.
/// * `os_username` - OS username.
/// * `os_password` - OS password.
/// * `password` - Password.
/// * `peer` - [`Stream`] for communicating with peer.
async fn send_login(lc: Arc<RwLock<LoginConfigHandler>>, password: Vec<u8>, peer: &mut Stream) {
let msg_out = lc.read().unwrap().create_login_msg(password);
async fn send_login(
lc: Arc<RwLock<LoginConfigHandler>>,
os_username: String,
os_password: String,
password: Vec<u8>,
peer: &mut Stream,
) {
let msg_out = lc
.read()
.unwrap()
.create_login_msg(os_username, os_password, password);
allow_err!(peer.send(&msg_out).await);
}
@@ -1998,25 +2062,40 @@ async fn send_login(lc: Arc<RwLock<LoginConfigHandler>>, password: Vec<u8>, peer
/// # Arguments
///
/// * `lc` - Login config.
/// * `os_username` - OS username.
/// * `os_password` - OS password.
/// * `password` - Password.
/// * `remember` - Whether to remember password.
/// * `peer` - [`Stream`] for communicating with peer.
pub async fn handle_login_from_ui(
lc: Arc<RwLock<LoginConfigHandler>>,
os_username: String,
os_password: String,
password: String,
remember: bool,
peer: &mut Stream,
) {
let mut hasher = Sha256::new();
hasher.update(password);
hasher.update(&lc.read().unwrap().hash.salt);
let res = hasher.finalize();
lc.write().unwrap().remember = remember;
lc.write().unwrap().password = res[..].into();
let mut hash_password = if password.is_empty() {
let mut password2 = lc.read().unwrap().password.clone();
if password2.is_empty() {
password2 = lc.read().unwrap().config.password.clone();
}
password2
} else {
let mut hasher = Sha256::new();
hasher.update(password);
hasher.update(&lc.read().unwrap().hash.salt);
let res = hasher.finalize();
lc.write().unwrap().remember = remember;
lc.write().unwrap().password = res[..].into();
res[..].into()
};
let mut hasher2 = Sha256::new();
hasher2.update(&res[..]);
hasher2.update(&hash_password[..]);
hasher2.update(&lc.read().unwrap().hash.challenge);
send_login(lc.clone(), hasher2.finalize()[..].into(), peer).await;
hash_password = hasher2.finalize()[..].to_vec();
send_login(lc.clone(), os_username, os_password, hash_password, peer).await;
}
async fn send_switch_login_request(
@@ -2030,7 +2109,7 @@ async fn send_switch_login_request(
lr: hbb_common::protobuf::MessageField::some(
lc.read()
.unwrap()
.create_login_msg(vec![])
.create_login_msg("".to_owned(), "".to_owned(), vec![])
.login_request()
.to_owned(),
),
@@ -2051,7 +2130,14 @@ pub trait Interface: Send + Clone + 'static + Sized {
self.msgbox("error", "Error", err, "");
}
async fn handle_hash(&mut self, pass: &str, hash: Hash, peer: &mut Stream);
async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream);
async fn handle_login_from_ui(
&mut self,
os_username: String,
os_password: String,
password: String,
remember: bool,
peer: &mut Stream,
);
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream);
fn get_login_config_handler(&self) -> Arc<RwLock<LoginConfigHandler>>;
@@ -2071,7 +2157,7 @@ pub trait Interface: Send + Clone + 'static + Sized {
#[derive(Clone)]
pub enum Data {
Close,
Login((String, bool)),
Login((String, String, String, bool)),
Message(Message),
SendFiles((i32, String, String, i32, bool, bool)),
RemoveDirAll((i32, String, bool, bool)),

View File

@@ -360,9 +360,9 @@ impl<T: InvokeUiSession> Remote<T> {
allow_err!(peer.send(&msg).await);
return false;
}
Data::Login((password, remember)) => {
Data::Login((os_username, os_password, password, remember)) => {
self.handler
.handle_login_from_ui(password, remember, peer)
.handle_login_from_ui(os_username, os_password, password, remember, peer)
.await;
}
Data::ToggleClipboardFile => {

View File

@@ -1,3 +1,4 @@
use super::linux_desktop::GNOME_SESSION_BINARY;
use super::{CursorData, ResultType};
use desktop::Desktop;
pub use hbb_common::platform::linux::*;

View File

@@ -0,0 +1,951 @@
use super::{linux::*, ResultType};
use hbb_common::{allow_err, bail, log, rand::prelude::*, tokio::time};
use pam;
use std::{
collections::HashMap,
os::unix::process::CommandExt,
path::Path,
process::{Child, Command},
sync::{
atomic::{AtomicBool, Ordering},
mpsc::{sync_channel, SyncSender},
Arc, Mutex,
},
time::{Duration, Instant},
};
use users::{get_user_by_name, os::unix::UserExt, User};
lazy_static::lazy_static! {
static ref DESKTOP_RUNNING: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
static ref DESKTOP_INST: Arc<Mutex<Option<Desktop>>> = Arc::new(Mutex::new(None));
}
pub const VIRTUAL_X11_DESKTOP: &str = "xfce4";
pub const VIRTUAL_X11_DESKTOP_START: &str = "startxfce4";
pub const XFCE4_PANEL: &str = "xfce4-panel";
pub const GNOME_SESSION_BINARY: &str = "gnome-session-binary";
pub const ENV_DESKTOP_PROTOCAL: &str = "RUSTDESK_PROTOCAL";
pub const ENV_DESKTOP_PROTOCAL_WAYLAND: &str = "wayland";
pub const ENV_DESKTOP_PROTOCAL__X11: &str = "x11";
pub const ENV_DESKTOP_PROTOCAL_UNKNOWN: &str = "unknown";
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Protocal {
Wayland,
X11, // Xorg
Unknown,
}
#[derive(Debug, Clone)]
pub struct DesktopEnv {
pub protocal: Protocal,
pub username: String,
pub uid: String,
pub display: String,
pub xauth: String,
}
#[derive(Debug)]
pub struct Desktop {
env: DesktopEnv,
child_exit: Arc<AtomicBool>,
is_child_running: Arc<AtomicBool>,
}
fn check_update_env() {
let mut inst = DESKTOP_INST.lock().unwrap();
if let Some(inst) = &mut (*inst) {
if !inst.is_child_running.load(Ordering::SeqCst) {
inst.child_exit.store(true, Ordering::SeqCst);
let old_env = inst.env.clone();
allow_err!(inst.env.refresh());
if !inst.env.is_same_env(&old_env) {
inst.env.update_env();
log::debug!("desktop env changed, {:?}", &inst.env);
}
}
}
}
pub fn start_xdesktop() {
std::thread::spawn(|| {
if wait_xdesktop(20) {
log::info!("Wait desktop: default");
} else {
log::info!("Wait desktop: none");
}
*DESKTOP_INST.lock().unwrap() = Some(Desktop::new());
let interval = time::Duration::from_millis(super::SERVICE_INTERVAL);
DESKTOP_RUNNING.store(true, Ordering::SeqCst);
while DESKTOP_RUNNING.load(Ordering::SeqCst) {
check_update_env();
std::thread::sleep(interval);
}
log::info!("xdesktop update thread exit");
});
}
pub fn stop_xdesktop() {
DESKTOP_RUNNING.store(false, Ordering::SeqCst);
}
pub fn get_desktop_env() -> Option<DesktopEnv> {
match &*DESKTOP_INST.lock().unwrap() {
Some(inst) => Some(inst.env.clone()),
None => None,
}
}
pub fn try_start_x_session(username: &str, password: &str) -> ResultType<DesktopEnv> {
let mut inst = DESKTOP_INST.lock().unwrap();
if let Some(inst) = &mut (*inst) {
let _ = inst.try_start_x_session(username, password)?;
log::debug!("try_start_x_session, username: {}, {:?}", &username, &inst);
Ok(inst.env.clone())
} else {
bail!(crate::server::LOGIN_MSG_XDESKTOP_NOT_INITED);
}
}
fn wait_xdesktop(timeout_secs: u64) -> bool {
let wait_begin = Instant::now();
while wait_begin.elapsed().as_secs() < timeout_secs {
let seat0 = get_values_of_seat0(&[0]);
if !seat0[0].is_empty() {
return true;
}
if let Ok(output) = run_cmds(format!(
"ps -ef | grep -v 'grep' | grep -E 'gnome-session-binary|{}'",
XFCE4_PANEL
)) {
if !output.is_empty() {
log::info!("wait xdesktop: find xclient {}", &output);
return true;
}
}
std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL));
}
false
}
impl DesktopEnv {
pub fn new() -> Self {
let xauth = get_env_var("XAUTHORITY");
Self {
protocal: Protocal::Unknown,
username: "".to_owned(),
uid: "".to_owned(),
display: "".to_owned(),
xauth: if xauth.is_empty() {
"/tmp/.Xauthority".to_owned()
} else {
xauth
},
}
}
fn update_env(&self) {
if self.is_ready() {
std::env::set_var("DISPLAY", &self.display);
std::env::set_var("XAUTHORITY", &self.xauth);
std::env::set_var(ENV_DESKTOP_PROTOCAL, &self.protocal.to_string());
} else {
std::env::set_var("DISPLAY", "");
std::env::set_var("XAUTHORITY", "");
std::env::set_var(ENV_DESKTOP_PROTOCAL, &Protocal::Unknown.to_string());
}
}
pub fn is_same_env(&self, other: &Self) -> bool {
self.protocal == other.protocal
&& self.uid == other.uid
&& self.display == other.display
&& self.xauth == other.xauth
}
#[inline(always)]
pub fn is_ready(&self) -> bool {
self.protocal == Protocal::X11
}
// The logic mainly fron https://github.com/neutrinolabs/xrdp/blob/34fe9b60ebaea59e8814bbc3ca5383cabaa1b869/sesman/session.c#L334.
fn get_avail_display() -> ResultType<u32> {
let display_range = 0..51;
for i in display_range.clone() {
if Self::is_x_server_running(i) {
continue;
}
return Ok(i);
}
bail!("No avaliable display found in range {:?}", display_range)
}
fn is_x_server_running(display: u32) -> bool {
Path::new(&format!("/tmp/.X11-unix/X{}", display)).exists()
|| Path::new(&format!("/tmp/.X{}-lock", display)).exists()
}
fn get_display(&mut self) {
self.display = get_env_tries("DISPLAY", &self.uid, GNOME_SESSION_BINARY, 10);
if self.display.is_empty() {
self.display = get_env_tries("DISPLAY", &self.uid, XFCE4_PANEL, 10);
}
if self.display.is_empty() {
self.display = Self::get_display_by_user(&self.username);
}
if self.display.is_empty() {
self.display = ":0".to_owned();
}
self.display = self
.display
.replace(&whoami::hostname(), "")
.replace("localhost", "");
}
fn get_xauth(&mut self) {
self.xauth = get_env_tries("XAUTHORITY", &self.uid, GNOME_SESSION_BINARY, 10);
if self.xauth.is_empty() {
get_env_tries("XAUTHORITY", &self.uid, XFCE4_PANEL, 10);
}
let gdm = format!("/run/user/{}/gdm/Xauthority", self.uid);
if self.xauth.is_empty() {
self.xauth = if std::path::Path::new(&gdm).exists() {
gdm
} else {
let username = &self.username;
if username == "root" {
format!("/{}/.Xauthority", username)
} else {
let tmp = format!("/home/{}/.Xauthority", username);
if std::path::Path::new(&tmp).exists() {
tmp
} else {
format!("/var/lib/{}/.Xauthority", username)
}
}
};
}
}
// fixme: reduce loginctl
fn get_env_seat0(&mut self) -> ResultType<bool> {
let output = Command::new("loginctl").output()?;
for line in String::from_utf8_lossy(&output.stdout).lines() {
if line.contains("seat0") {
if let Some(sid) = line.split_whitespace().nth(0) {
if Self::is_active(sid)? {
if let Some(uid) = line.split_whitespace().nth(1) {
self.uid = uid.to_owned();
}
if let Some(u) = line.split_whitespace().nth(2) {
self.username = u.to_owned();
}
self.protocal = Protocal::Unknown;
let type_output = Command::new("loginctl")
.args(vec!["show-session", "-p", "Type", sid])
.output()?;
let type_stdout = String::from_utf8_lossy(&type_output.stdout);
if type_stdout.contains("x11") {
self.protocal = Protocal::X11;
break;
} else if type_stdout.contains("wayland") {
self.protocal = Protocal::Wayland;
}
}
}
}
}
Ok(self.is_ready())
}
// some case, there is no seat0 https://github.com/rustdesk/rustdesk/issues/73
fn get_env_active(&mut self) -> ResultType<bool> {
let output = Command::new("loginctl").output()?;
// set active Xorg session
for line in String::from_utf8_lossy(&output.stdout).lines() {
if line.contains("sessions listed.") {
continue;
}
if let Some(sid) = line.split_whitespace().nth(0) {
if Self::is_active(sid)? {
if Self::get_display_server_of_session(sid) == ENV_DESKTOP_PROTOCAL__X11 {
if let Some(uid) = line.split_whitespace().nth(1) {
self.uid = uid.to_owned();
}
if let Some(u) = line.split_whitespace().nth(2) {
self.username = u.to_owned();
}
self.protocal = Protocal::X11;
}
}
}
}
// // set active xfce4 session
// for line in String::from_utf8_lossy(&output.stdout).lines() {
// if let Some(sid) = line.split_whitespace().nth(0) {
// if Self::is_active(sid)? {
// let tty_output = Command::new("loginctl")
// .args(vec!["show-session", "-p", "TTY", sid])
// .output()?;
// let tty: String = String::from_utf8_lossy(&tty_output.stdout)
// .replace("TTY=", "")
// .trim_end()
// .into();
// let xfce_panel_info =
// run_cmds(format!("ps -e | grep \"{}.\\\\+{}\"", tty, XFCE4_PANEL))?;
// if xfce_panel_info.trim_end().to_string() != "" {
// if let Some(uid) = line.split_whitespace().nth(1) {
// self.uid = uid.to_owned();
// }
// if let Some(u) = line.split_whitespace().nth(2) {
// self.username = u.to_owned();
// }
// }
// }
// }
// }
Ok(self.is_ready())
}
// fixme: dup
fn get_display_server_of_session(session: &str) -> String {
if let Ok(output) = Command::new("loginctl")
.args(vec!["show-session", "-p", "Type", session])
.output()
// Check session type of the session
{
let display_server = String::from_utf8_lossy(&output.stdout)
.replace("Type=", "")
.trim_end()
.into();
if display_server == "tty" {
// If the type is tty...
if let Ok(output) = Command::new("loginctl")
.args(vec!["show-session", "-p", "TTY", session])
.output()
// Get the tty number
{
let tty: String = String::from_utf8_lossy(&output.stdout)
.replace("TTY=", "")
.trim_end()
.into();
if let Ok(xorg_results) =
run_cmds(format!("ps -e | grep \"{}.\\\\+Xorg\"", tty))
// And check if Xorg is running on that tty
{
if xorg_results.trim_end().to_string() != "" {
// If it is, manually return "x11", otherwise return tty
ENV_DESKTOP_PROTOCAL__X11.to_owned()
} else {
display_server
}
} else {
// If any of these commands fail just fall back to the display server
display_server
}
} else {
display_server
}
} else {
// If the session is not a tty, then just return the type as usual
display_server
}
} else {
"".to_owned()
}
}
// fixme: remove
fn is_active(sid: &str) -> ResultType<bool> {
let output = Command::new("loginctl")
.args(vec!["show-session", "-p", "State", sid])
.output()?;
Ok(String::from_utf8_lossy(&output.stdout).contains("active"))
}
fn get_display_by_user(user: &str) -> String {
// log::debug!("w {}", &user);
if let Ok(output) = std::process::Command::new("w").arg(&user).output() {
for line in String::from_utf8_lossy(&output.stdout).lines() {
let mut iter = line.split_whitespace();
let b = iter.nth(2);
if let Some(b) = b {
if b.starts_with(":") {
return b.to_owned();
}
}
}
}
// above not work for gdm user
//log::debug!("ls -l /tmp/.X11-unix/");
let mut last = "".to_owned();
if let Ok(output) = std::process::Command::new("ls")
.args(vec!["-l", "/tmp/.X11-unix/"])
.output()
{
for line in String::from_utf8_lossy(&output.stdout).lines() {
let mut iter = line.split_whitespace();
let user_field = iter.nth(2);
if let Some(x) = iter.last() {
if x.starts_with("X") {
last = x.replace("X", ":").to_owned();
if user_field == Some(&user) {
return last;
}
}
}
}
}
last
}
fn add_xauth_cookie(
file: &str,
display: &str,
uid: u32,
gid: u32,
envs: &HashMap<&str, String>,
) -> ResultType<()> {
let randstr = (0..16)
.map(|_| format!("{:02x}", random::<u8>()))
.collect::<String>();
let output = Command::new("xauth")
.uid(uid)
.gid(gid)
.envs(envs)
.args(vec!["-q", "-f", file, "add", display, ".", &randstr])
.output()?;
// xauth run success, even the following error occurs.
// Ok(Output { status: ExitStatus(unix_wait_status(0)), stdout: "", stderr: "xauth: file .Xauthority does not exist\n" })
let errmsg = String::from_utf8_lossy(&output.stderr).to_string();
if !errmsg.is_empty() {
if !errmsg.contains("does not exist") {
bail!("Failed to launch xauth, {}", errmsg)
}
}
Ok(())
}
fn wait_x_server_running(pid: u32, display_num: u32, max_wait_secs: u64) -> ResultType<()> {
let wait_begin = Instant::now();
loop {
if run_cmds(format!("ls /proc/{}", pid))?.is_empty() {
bail!("X server exit");
}
if Self::is_x_server_running(display_num) {
return Ok(());
}
if wait_begin.elapsed().as_secs() > max_wait_secs {
bail!("Failed to wait xserver after {} seconds", max_wait_secs);
}
std::thread::sleep(Duration::from_millis(300));
}
}
fn refresh(&mut self) -> ResultType<bool> {
*self = Self::new();
if self.get_env_seat0()? || self.get_env_active()? {
self.get_display();
self.get_xauth();
Ok(true)
} else {
Ok(false)
}
}
}
impl Drop for Desktop {
fn drop(&mut self) {
self.stop_children();
}
}
impl Desktop {
fn fatal_exit() {
std::process::exit(0);
}
pub fn new() -> Self {
Self {
env: DesktopEnv::new(),
child_exit: Arc::new(AtomicBool::new(true)),
is_child_running: Arc::new(AtomicBool::new(false)),
}
}
fn try_start_x_session(&mut self, username: &str, password: &str) -> ResultType<()> {
match get_user_by_name(username) {
Some(userinfo) => {
let mut client = pam::Client::with_password(pam_get_service_name())?;
client
.conversation_mut()
.set_credentials(username, password);
match client.authenticate() {
Ok(_) => {
if self.env.is_ready() && self.env.username == username {
return Ok(());
}
self.env.username = username.to_string();
self.env.uid = userinfo.uid().to_string();
self.env.protocal = Protocal::Unknown;
match self.start_x_session(&userinfo, password) {
Ok(_) => {
log::info!("Succeeded to start x11, update env {:?}", &self.env);
self.env.update_env();
Ok(())
}
Err(e) => {
self.env = DesktopEnv::new();
self.env.update_env();
bail!("failed to start x session, {}", e);
}
}
}
Err(e) => {
bail!("failed to check user pass for {}, {}", username, e);
}
}
}
None => {
bail!("failed to get userinfo of {}", username);
}
}
}
fn start_x_session(&mut self, userinfo: &User, password: &str) -> ResultType<()> {
self.stop_children();
let display_num = DesktopEnv::get_avail_display()?;
// "xServer_ip:display_num.screen_num"
self.env.display = format!(":{}", display_num);
let uid = userinfo.uid();
let gid = userinfo.primary_group_id();
let envs = HashMap::from([
("SHELL", userinfo.shell().to_string_lossy().to_string()),
("PATH", "/sbin:/bin:/usr/bin:/usr/local/bin".to_owned()),
("USER", self.env.username.clone()),
("UID", userinfo.uid().to_string()),
("HOME", userinfo.home_dir().to_string_lossy().to_string()),
(
"XDG_RUNTIME_DIR",
format!("/run/user/{}", userinfo.uid().to_string()),
),
// ("DISPLAY", self.display.clone()),
// ("XAUTHORITY", self.xauth.clone()),
// (ENV_DESKTOP_PROTOCAL, XProtocal::X11.to_string()),
]);
let env = self.env.clone();
self.child_exit.store(false, Ordering::SeqCst);
let is_child_running = self.is_child_running.clone();
let (tx_res, rx_res) = sync_channel(1);
let password = password.to_string();
// start x11
std::thread::spawn(move || {
match Self::start_x_session_thread(
tx_res.clone(),
is_child_running,
env,
uid,
gid,
display_num,
password,
envs,
) {
Ok(_) => {}
Err(e) => {
log::error!("Failed to start x session thread");
allow_err!(tx_res.send(format!("Failed to start x session thread, {}", e)));
}
}
});
// wait x11
match rx_res.recv_timeout(Duration::from_millis(10_000)) {
Ok(res) => {
if res == "" {
self.env.protocal = Protocal::X11;
Ok(())
} else {
bail!(res)
}
}
Err(e) => {
bail!("Failed to recv x11 result {}", e)
}
}
}
fn start_x_session_thread(
tx_res: SyncSender<String>,
is_child_running: Arc<AtomicBool>,
env: DesktopEnv,
uid: u32,
gid: u32,
display_num: u32,
password: String,
envs: HashMap<&str, String>,
) -> ResultType<()> {
let mut client = pam::Client::with_password(pam_get_service_name())?;
client
.conversation_mut()
.set_credentials(&env.username, &password);
client.authenticate()?;
client.set_item(pam::PamItemType::TTY, &env.display)?;
client.open_session()?;
// fixme: FreeBSD kernel needs to login here.
// see: https://github.com/neutrinolabs/xrdp/blob/a64573b596b5fb07ca3a51590c5308d621f7214e/sesman/session.c#L556
let (child_xorg, child_wm) = Self::start_x11(&env, uid, gid, display_num, &envs)?;
is_child_running.store(true, Ordering::SeqCst);
log::info!("Start xorg and wm done, notify and wait xtop x11");
allow_err!(tx_res.send("".to_owned()));
Self::wait_stop_x11(child_xorg, child_wm);
log::info!("Wait x11 stop done");
Ok(())
}
fn wait_xorg_exit(child_xorg: &mut Child) -> ResultType<String> {
if let Ok(_) = child_xorg.kill() {
for _ in 0..3 {
match child_xorg.try_wait() {
Ok(Some(status)) => return Ok(format!("Xorg exit with {}", status)),
Ok(None) => {}
Err(e) => {
// fatal error
log::error!("Failed to wait xorg process, {}", e);
bail!("Failed to wait xorg process, {}", e)
}
}
std::thread::sleep(std::time::Duration::from_millis(1_000));
}
log::error!("Failed to wait xorg process, not exit");
bail!("Failed to wait xorg process, not exit")
} else {
Ok("Xorg is already exited".to_owned())
}
}
fn start_x11(
env: &DesktopEnv,
uid: u32,
gid: u32,
display_num: u32,
envs: &HashMap<&str, String>,
) -> ResultType<(Child, Child)> {
log::debug!("envs of user {}: {:?}", &env.username, &envs);
DesktopEnv::add_xauth_cookie(&env.xauth, &env.display, uid, gid, &envs)?;
// Start Xorg
let mut child_xorg = Self::start_x_server(&env.xauth, &env.display, uid, gid, &envs)?;
log::info!("xorg started, wait 10 secs to ensuer x server is running");
let max_wait_secs = 10;
// wait x server running
if let Err(e) =
DesktopEnv::wait_x_server_running(child_xorg.id(), display_num, max_wait_secs)
{
match Self::wait_xorg_exit(&mut child_xorg) {
Ok(msg) => log::info!("{}", msg),
Err(e) => {
log::error!("{}", e);
Self::fatal_exit();
}
}
bail!(e)
}
log::info!(
"xorg is running, start x window manager with DISPLAY: {}, XAUTHORITY: {}",
&env.display,
&env.xauth
);
std::env::set_var("DISPLAY", &env.display);
std::env::set_var("XAUTHORITY", &env.xauth);
// start window manager (startwm.sh)
let child_wm = match Self::start_x_window_manager(uid, gid, &envs) {
Ok(c) => c,
Err(e) => {
match Self::wait_xorg_exit(&mut child_xorg) {
Ok(msg) => log::info!("{}", msg),
Err(e) => {
log::error!("{}", e);
Self::fatal_exit();
}
}
bail!(e)
}
};
log::info!("x window manager is started");
Ok((child_xorg, child_wm))
}
fn try_wait_x11_child_exit(child_xorg: &mut Child, child_wm: &mut Child) -> bool {
match child_xorg.try_wait() {
Ok(Some(status)) => {
println!(
"=============================MYDEBUG Xorg exit with {}",
status
);
log::info!("Xorg exit with {}", status);
return true;
}
Ok(None) => {}
Err(e) => log::error!("Failed to wait xorg process, {}", e),
}
match child_wm.try_wait() {
Ok(Some(status)) => {
println!(
"=============================MYDEBUG: wm exit with {}",
status
);
log::info!("wm exit with {}", status);
return true;
}
Ok(None) => {}
Err(e) => log::error!("Failed to wait xorg process, {}", e),
}
false
}
fn wait_x11_children_exit(child_xorg: &mut Child, child_wm: &mut Child) {
log::debug!("Try kill child process xorg");
if let Ok(_) = child_xorg.kill() {
let mut exited = false;
for _ in 0..2 {
match child_xorg.try_wait() {
Ok(Some(status)) => {
println!(
"=============================MYDEBUG Xorg exit with {}",
status
);
log::info!("Xorg exit with {}", status);
exited = true;
break;
}
Ok(None) => {}
Err(e) => {
println!(
"=============================MYDEBUG Failed to wait xorg process, {}",
&e
);
log::error!("Failed to wait xorg process, {}", e);
Self::fatal_exit();
}
}
std::thread::sleep(std::time::Duration::from_millis(1_000));
}
if !exited {
println!(
"=============================MYDEBUG Failed to wait child xorg, after kill()"
);
log::error!("Failed to wait child xorg, after kill()");
// try kill -9?
}
}
log::debug!("Try kill child process wm");
if let Ok(_) = child_wm.kill() {
let mut exited = false;
for _ in 0..2 {
match child_wm.try_wait() {
Ok(Some(status)) => {
println!(
"=============================MYDEBUG wm exit with {}",
status
);
log::info!("wm exit with {}", status);
exited = true;
}
Ok(None) => {}
Err(e) => {
println!(
"=============================MYDEBUG Failed to wait wm process, {}",
&e
);
log::error!("Failed to wait wm process, {}", e);
Self::fatal_exit();
}
}
std::thread::sleep(std::time::Duration::from_millis(1_000));
}
if !exited {
println!(
"=============================MYDEBUG Failed to wait child xorg, after kill()"
);
log::error!("Failed to wait child xorg, after kill()");
// try kill -9?
}
}
}
fn try_wait_stop_x11(child_xorg: &mut Child, child_wm: &mut Child) -> bool {
let mut inst = DESKTOP_INST.lock().unwrap();
let mut exited = true;
if let Some(inst) = &mut (*inst) {
if inst.child_exit.load(Ordering::SeqCst) {
exited = true;
} else {
exited = Self::try_wait_x11_child_exit(child_xorg, child_wm);
}
if exited {
println!("=============================MYDEBUG begin to wait x11 children exit");
Self::wait_x11_children_exit(child_xorg, child_wm);
inst.is_child_running.store(false, Ordering::SeqCst);
inst.child_exit.store(true, Ordering::SeqCst);
}
}
exited
}
fn wait_stop_x11(mut child_xorg: Child, mut child_wm: Child) {
loop {
if Self::try_wait_stop_x11(&mut child_xorg, &mut child_wm) {
break;
}
std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL));
}
}
fn get_xorg() -> ResultType<&'static str> {
// Fedora 26 or later
let xorg = "/usr/libexec/Xorg";
if Path::new(xorg).is_file() {
return Ok(xorg);
}
// Debian 9 or later
let xorg = "/usr/lib/xorg/Xorg";
if Path::new(xorg).is_file() {
return Ok(xorg);
}
// Ubuntu 16.04 or later
let xorg = "/usr/lib/xorg/Xorg";
if Path::new(xorg).is_file() {
return Ok(xorg);
}
// Arch Linux
let xorg = "/usr/lib/xorg-server/Xorg";
if Path::new(xorg).is_file() {
return Ok(xorg);
}
// Arch Linux
let xorg = "/usr/lib/Xorg";
if Path::new(xorg).is_file() {
return Ok(xorg);
}
// CentOS 7 /usr/bin/Xorg or param=Xorg
log::warn!("Failed to find xorg, use default Xorg.\n Please add \"allowed_users=anybody\" to \"/etc/X11/Xwrapper.config\".");
Ok("Xorg")
}
fn start_x_server(
xauth: &str,
display: &str,
uid: u32,
gid: u32,
envs: &HashMap<&str, String>,
) -> ResultType<Child> {
let xorg = Self::get_xorg()?;
log::info!("Use xorg: {}", &xorg);
match Command::new(xorg)
.envs(envs)
.uid(uid)
.gid(gid)
.args(vec![
"-noreset",
"+extension",
"GLX",
"+extension",
"RANDR",
"+extension",
"RENDER",
//"-logfile",
//"/tmp/RustDesk_xorg.log",
"-config",
"rustdesk/xorg.conf",
"-auth",
xauth,
display,
])
.spawn()
{
Ok(c) => Ok(c),
Err(e) => {
bail!("Failed to start Xorg with display {}, {}", display, e);
}
}
}
fn start_x_window_manager(
uid: u32,
gid: u32,
envs: &HashMap<&str, String>,
) -> ResultType<Child> {
match Command::new("/etc/rustdesk/startwm.sh")
.envs(envs)
.uid(uid)
.gid(gid)
.spawn()
{
Ok(c) => Ok(c),
Err(e) => {
bail!("Failed to start window manager, {}", e);
}
}
}
fn stop_children(&mut self) {
self.child_exit.store(true, Ordering::SeqCst);
for _i in 1..10 {
if !self.is_child_running.load(Ordering::SeqCst) {
break;
}
std::thread::sleep(Duration::from_millis(super::SERVICE_INTERVAL));
}
if self.is_child_running.load(Ordering::SeqCst) {
log::warn!("xdesktop child is still running!");
}
}
}
fn pam_get_service_name() -> &'static str {
if Path::new("/etc/pam.d/rustdesk").is_file() {
"rustdesk"
} else {
"gdm"
}
}
impl ToString for Protocal {
fn to_string(&self) -> String {
match self {
Protocal::X11 => ENV_DESKTOP_PROTOCAL__X11.to_owned(),
Protocal::Wayland => ENV_DESKTOP_PROTOCAL_WAYLAND.to_owned(),
Protocal::Unknown => ENV_DESKTOP_PROTOCAL_UNKNOWN.to_owned(),
}
}
}

View File

@@ -17,6 +17,9 @@ pub mod delegate;
#[cfg(target_os = "linux")]
pub mod linux;
#[cfg(target_os = "linux")]
pub mod linux_desktop;
#[cfg(not(any(target_os = "android", target_os = "ios")))]
use hbb_common::{message_proto::CursorData, ResultType};
#[cfg(not(any(target_os = "macos", target_os = "android", target_os = "ios")))]

View File

@@ -199,8 +199,8 @@ async fn connect_and_login_2(
},
d = ui_receiver.recv() => {
match d {
Some(Data::Login((password, remember))) => {
interface.handle_login_from_ui(password, remember, &mut stream).await;
Some(Data::Login((os_username, os_password, password, remember))) => {
interface.handle_login_from_ui(os_username, os_password, password, remember, &mut stream).await;
}
_ => {}
}

View File

@@ -54,6 +54,8 @@ impl RendezvousMediator {
pub async fn start_all() {
let mut nat_tested = false;
check_zombie();
#[cfg(target_os = "linux")]
crate::server::check_xdesktop();
let server = new_server();
if Config::get_nat_type() == NatType::UNKNOWN_NAT as i32 {
crate::test_nat_type();
@@ -72,7 +74,10 @@ impl RendezvousMediator {
allow_err!(super::lan::start_listening());
});
}
loop {
#[cfg(target_os = "linux")]
crate::platform::linux_desktop::start_xdesktop();
SHOULD_EXIT.store(false, Ordering::SeqCst);
while !SHOULD_EXIT.load(Ordering::SeqCst) {
Config::reset_online();
if Config::get_option("stop-service").is_empty() {
if !nat_tested {
@@ -96,6 +101,8 @@ impl RendezvousMediator {
}
sleep(1.).await;
}
#[cfg(target_os = "linux")]
crate::platform::linux_desktop::stop_xdesktop();
}
pub async fn start(server: ServerPtr, host: String) -> ResultType<()> {

View File

@@ -356,6 +356,40 @@ pub fn check_zombie() {
});
}
#[cfg(target_os = "linux")]
pub fn check_xdesktop() {
std::thread::spawn(|| {
use crate::platform::linux_desktop::{get_desktop_env, DesktopEnv};
let mut desktop_env = DesktopEnv::new();
loop {
let new_env = match get_desktop_env() {
Some(env) => env,
None => DesktopEnv::new(),
};
if new_env.is_ready() {
if !desktop_env.is_ready() {
desktop_env = new_env.clone();
} else {
if !desktop_env.is_same_env(&new_env) {
log::info!("xdesktop env diff {:?} to {:?}", &new_env, desktop_env);
break;
}
}
} else {
if desktop_env.is_ready() {
log::info!("xdesktop env diff {:?} to {:?}", &new_env, desktop_env);
break;
}
}
std::thread::sleep(Duration::from_millis(300));
}
crate::RendezvousMediator::restart();
});
}
/// Start the host server that allows the remote peer to control the current machine.
///
/// # Arguments

View File

@@ -57,6 +57,16 @@ lazy_static::lazy_static! {
pub static CLICK_TIME: AtomicI64 = AtomicI64::new(0);
pub static MOUSE_MOVE_TIME: AtomicI64 = AtomicI64::new(0);
pub const LOGIN_MSG_XDESKTOP_NOT_INITED: &str = "xdesktop env is not inited";
pub const LOGIN_MSG_XSESSION_NOT_READY: &str = "xsession unready";
pub const LOGIN_MSG_XSESSION_FAILED: &str = "xsession failed";
pub const LOGIN_MSG_XSESSION_ANOTHER_USER_READTY: &str = "xsession another user login";
pub const LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY: &str = "xsession unready, password empty";
pub const LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG: &str = "xsession unready, password wrong";
pub const LOGIN_MSG_PASSWORD_EMPTY: &str = "Empty Password";
pub const LOGIN_MSG_PASSWORD_WRONG: &str = "Wrong Password";
pub const LOGIN_MSG_OFFLINE: &str = "Offline";
#[derive(Clone, Default)]
pub struct ConnInner {
id: i32,
@@ -902,7 +912,7 @@ impl Connection {
}
#[cfg(not(any(target_os = "android", target_os = "ios")))]
if self.file_transfer.is_some() {
if crate::platform::is_prelogin() || self.tx_to_cm.send(ipc::Data::Test).is_err() {
if !crate::platform::is_prelogin() || self.tx_to_cm.send(ipc::Data::Test).is_err() {
username = "".to_owned();
}
}
@@ -1061,6 +1071,47 @@ impl Connection {
self.tx_input.send(MessageInput::Key((msg, press))).ok();
}
fn try_start_desktop(_username: &str, _passsword: &str) -> String {
#[cfg(target_os = "linux")]
{
if _username.is_empty() {
match crate::platform::linux_desktop::get_desktop_env() {
Some(desktop_env) => {
if desktop_env.is_ready() {
""
} else {
LOGIN_MSG_XSESSION_NOT_READY
}
}
None => LOGIN_MSG_XDESKTOP_NOT_INITED,
}
.to_owned()
} else {
match crate::platform::linux_desktop::try_start_x_session(_username, _passsword) {
Ok(desktop_env) => {
if desktop_env.is_ready() {
if _username != desktop_env.username {
LOGIN_MSG_XSESSION_ANOTHER_USER_READTY.to_owned()
} else {
"".to_owned()
}
} else {
LOGIN_MSG_XSESSION_NOT_READY.to_owned()
}
}
Err(e) => {
log::error!("Failed to start xsession {}", e);
LOGIN_MSG_XSESSION_FAILED.to_owned()
}
}
}
}
#[cfg(not(target_os = "linux"))]
{
"".to_owned()
}
}
fn validate_one_password(&self, password: String) -> bool {
if password.len() == 0 {
return false;
@@ -1225,16 +1276,31 @@ impl Connection {
}
_ => {}
}
let desktop_err = match lr.os_login.as_ref() {
Some(os_login) => Self::try_start_desktop(&os_login.username, &os_login.password),
None => Self::try_start_desktop("", ""),
};
if !desktop_err.is_empty() && desktop_err != LOGIN_MSG_XSESSION_NOT_READY {
self.send_login_error(desktop_err).await;
return true;
}
if !hbb_common::is_ipv4_str(&lr.username) && lr.username != Config::get_id() {
self.send_login_error("Offline").await;
self.send_login_error(LOGIN_MSG_OFFLINE).await;
} else if password::approve_mode() == ApproveMode::Click
|| password::approve_mode() == ApproveMode::Both && !password::has_valid_password()
{
self.try_start_cm(lr.my_id, lr.my_name, false);
if hbb_common::get_version_number(&lr.version)
>= hbb_common::get_version_number("1.2.0")
{
self.send_login_error("No Password Access").await;
if desktop_err.is_empty() {
self.try_start_cm(lr.my_id, lr.my_name, false);
if hbb_common::get_version_number(&lr.version)
>= hbb_common::get_version_number("1.2.0")
{
self.send_login_error("No Password Access").await;
}
} else {
self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_EMPTY)
.await;
}
return true;
} else if password::approve_mode() == ApproveMode::Password
@@ -1290,16 +1356,25 @@ impl Connection {
.lock()
.unwrap()
.insert(self.ip.clone(), failure);
self.send_login_error("Wrong Password").await;
self.try_start_cm(lr.my_id, lr.my_name, false);
if desktop_err.is_empty() {
self.send_login_error(LOGIN_MSG_PASSWORD_WRONG).await;
self.try_start_cm(lr.my_id, lr.my_name, false);
} else {
self.send_login_error(LOGIN_MSG_XSESSION_NOT_READY_PASSWORD_WRONG)
.await;
}
} else {
if failure.0 != 0 {
LOGIN_FAILURES.lock().unwrap().remove(&self.ip);
}
self.try_start_cm(lr.my_id, lr.my_name, true);
self.send_logon_response().await;
if self.port_forward_socket.is_some() {
return false;
if desktop_err.is_empty() {
self.send_logon_response().await;
self.try_start_cm(lr.my_id, lr.my_name, true);
if self.port_forward_socket.is_some() {
return false;
}
} else {
self.send_login_error(desktop_err).await;
}
}
}
@@ -2072,7 +2147,7 @@ async fn start_ipc(
tx_from_cm: mpsc::UnboundedSender<ipc::Data>,
) -> ResultType<()> {
loop {
if !crate::platform::is_prelogin() {
if crate::platform::is_prelogin() {
break;
}
sleep(1.).await;

View File

@@ -221,6 +221,7 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
thread::sleep(interval - elapsed);
}
}
log::info!("Service {} exit", sp.name());
});
self.0.write().unwrap().handle = Some(thread);
}
@@ -256,6 +257,7 @@ impl<T: Subscriber + From<ConnInner>> ServiceTmpl<T> {
}
thread::sleep(time::Duration::from_millis(HIBERNATE_TIMEOUT));
}
log::info!("Service {} exit", sp.name());
});
self.0.write().unwrap().handle = Some(thread);
}

View File

@@ -142,6 +142,10 @@ div.password input {
font-size: 1.2em;
}
div.username input {
font-size: 1.2em;
}
svg {
background: none;
}

View File

@@ -252,7 +252,7 @@ function msgbox(type, title, content, link="", callback=null, height=180, width=
view.close();
return;
}
handler.login(res.password, res.remember);
handler.login("", "", res.password, res.remember);
if (!is_port_forward) {
// Specially handling file transfer for no permission hanging issue (including 60ms
// timer in setPermission.
@@ -262,6 +262,30 @@ function msgbox(type, title, content, link="", callback=null, height=180, width=
else msgbox("connecting", "Connecting...", "Logging in...");
}
};
} else if (type == "xsession-login" || type == "xsession-re-login") {
callback = function (res) {
if (!res) {
view.close();
return;
}
handler.login(res.osusername, res.ospassword, "", false);
if (!is_port_forward) {
if (is_file_transfer) handler.msgbox("connecting", "Connecting...", "Logging in...");
else msgbox("connecting", "Connecting...", "Logging in...");
}
};
} else if (type.indexOf("xsession-login") >= 0) {
callback = function (res) {
if (!res) {
view.close();
return;
}
handler.login(res.osusername, res.ospassword, res.password, res.remember);
if (!is_port_forward) {
if (is_file_transfer) handler.msgbox("connecting", "Connecting...", "Logging in...");
else msgbox("connecting", "Connecting...", "Logging in...");
}
};
} else if (type.indexOf("custom") < 0 && !is_port_forward && !callback) {
callback = function() { view.close(); }
} else if (type == 'wait-remote-accept-nook') {

View File

@@ -32,7 +32,7 @@ class MsgboxComponent: Reactor.Component {
}
function getIcon(color) {
if (this.type == "input-password") {
if (this.type == "input-password" || this.type == "xsession-login" || this.type == "xsession-login-password") {
return <svg viewBox="0 0 505 505"><circle cx="252.5" cy="252.5" r="252.5" fill={color}/><path d="M271.9 246.1c29.2 17.5 67.6 13.6 92.7-11.5 29.7-29.7 29.7-77.8 0-107.4s-77.8-29.7-107.4 0c-25.1 25.1-29 63.5-11.5 92.7L118.1 347.4l26.2 26.2 26.4 26.4 10.6-10.6-10.1-10.1 9.7-9.7 10.1 10.1 10.6-10.6-10.1-10 9.7-9.7 10.1 10.1 10.6-10.6-26.4-26.3 76.4-76.5z" fill="#fff"/><circle cx="337.4" cy="154.4" r="17.7" fill={color}/></svg>;
}
if (this.type == "connecting") {
@@ -41,7 +41,7 @@ class MsgboxComponent: Reactor.Component {
if (this.type == "success") {
return <svg viewBox="0 0 512 512"><circle cx="256" cy="256" r="256" fill={color} /><path fill="#fff" d="M235.472 392.08l-121.04-94.296 34.416-44.168 74.328 57.904 122.672-177.016 46.032 31.888z"/></svg>;
}
if (this.type.indexOf("error") >= 0 || this.type == "re-input-password") {
if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "xsession-re-login" || this.type == "xsession-login-re-password") {
return <svg viewBox="0 0 512 512"><ellipse cx="256" cy="256" rx="256" ry="255.832" fill={color}/><g fill="#fff"><path d="M376.812 337.18l-39.592 39.593-201.998-201.999 39.592-39.592z"/><path d="M376.818 174.825L174.819 376.824l-39.592-39.592 201.999-201.999z"/></g></svg>;
}
return null;
@@ -56,11 +56,36 @@ class MsgboxComponent: Reactor.Component {
</div>;
}
function getInputUserPasswordContent() {
return <div .form>
<div>{translate("OS Username")}</div>
<div .username><input name='osusername' type='text' .outline-focus /></div>
<div>{translate("OS Password")}</div>
<PasswordComponent name='ospassword' />
<div></div>
</div>;
}
function getXsessionPasswordContent() {
return <div .form>
<div>{translate("OS Username")}</div>
<div .username><input name='osusername' type='text' .outline-focus /></div>
<div>{translate("OS Password")}</div>
<PasswordComponent name='ospassword' />
<div>{translate('Please enter your password')}</div>
<PasswordComponent />
<div><button|checkbox(remember) {ts}>{translate('Remember password')}</button></div>
</div>;
}
function getContent() {
if (this.type == "input-password") {
return this.getInputPasswordContent();
}
if (this.type == "custom-os-password") {
} else if (this.type == "xsession-login") {
return this.getInputUserPasswordContent();
} else if (this.type == "xsession-login-password") {
return this.getXsessionPasswordContent();
} else if (this.type == "custom-os-password") {
var ts = this.auto_login ? { checked: true } : {};
return <div .form>
<PasswordComponent value={this.content} />
@@ -71,13 +96,13 @@ class MsgboxComponent: Reactor.Component {
}
function getColor() {
if (this.type == "input-password" || this.type == "custom-os-password") {
if (this.type == "input-password" || this.type == "custom-os-password" || this.type == "xsession-login" || this.type == "xsession-login-password") {
return "#AD448E";
}
if (this.type == "success") {
return "#32bea6";
}
if (this.type.indexOf("error") >= 0 || this.type == "re-input-password") {
if (this.type.indexOf("error") >= 0 || this.type == "re-input-password" || this.type == "xsession-re-login" || this.type == "xsession-login-re-password") {
return "#e04f5f";
}
return "#2C8CFF";
@@ -177,6 +202,16 @@ class MsgboxComponent: Reactor.Component {
this.update();
return;
}
if (this.type == "xsession-re-login") {
this.type = "xsession-login";
this.update();
return;
}
if (this.type == "xsession-login-re-password") {
this.type = "xsession-login-password";
this.update();
return;
}
var values = this.getValues();
if (this.callback) {
var self = this;
@@ -238,6 +273,21 @@ class MsgboxComponent: Reactor.Component {
return;
}
}
if (this.type == "xsession-login") {
values.osusername = (values.osusername || "").trim();
values.ospassword = (values.ospassword || "").trim();
if (!values.osusername || !values.ospassword) {
return;
}
}
if (this.type == "xsession-login-password") {
values.password = (values.password || "").trim();
values.osusername = (values.osusername || "").trim();
values.ospassword = (values.ospassword || "").trim();
if (!values.osusername || !values.ospassword || !values.password) {
return;
}
}
return values;
}

View File

@@ -398,7 +398,7 @@ impl sciter::EventHandler for SciterSession {
fn is_file_transfer();
fn is_port_forward();
fn is_rdp();
fn login(String, bool);
fn login(String, String, String, bool);
fn new_rdp();
fn send_mouse(i32, i32, i32, bool, bool, bool, bool);
fn enter();

View File

@@ -711,8 +711,14 @@ impl<T: InvokeUiSession> Session<T> {
fs::get_string(&path)
}
pub fn login(&self, password: String, remember: bool) {
self.send(Data::Login((password, remember)));
pub fn login(
&mut self,
os_username: String,
os_password: String,
password: String,
remember: bool,
) {
self.send(Data::Login((os_username, os_password, password, remember)));
}
pub fn new_rdp(&self) {
@@ -1005,8 +1011,23 @@ impl<T: InvokeUiSession> Interface for Session<T> {
handle_hash(self.lc.clone(), pass, hash, self, peer).await;
}
async fn handle_login_from_ui(&mut self, password: String, remember: bool, peer: &mut Stream) {
handle_login_from_ui(self.lc.clone(), password, remember, peer).await;
async fn handle_login_from_ui(
&mut self,
os_username: String,
os_password: String,
password: String,
remember: bool,
peer: &mut Stream,
) {
handle_login_from_ui(
self.lc.clone(),
os_username,
os_password,
password,
remember,
peer,
)
.await;
}
async fn handle_test_delay(&mut self, t: TestDelay, peer: &mut Stream) {