mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-24 12:29:04 +08:00
commit
f1fd09f189
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2087,6 +2087,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"mac_address",
|
||||
"machine-uid",
|
||||
"protobuf",
|
||||
"protobuf-codegen",
|
||||
"quinn",
|
||||
|
@ -34,6 +34,7 @@ tokio-socks = { git = "https://github.com/open-trade/tokio-socks" }
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||
mac_address = "1.1"
|
||||
machine-uid = "0.2"
|
||||
|
||||
[features]
|
||||
quic = []
|
||||
|
@ -63,6 +63,7 @@ message LoginRequest {
|
||||
PortForward port_forward = 8;
|
||||
}
|
||||
bool video_ack_required = 9;
|
||||
uint64 session_id = 10;
|
||||
}
|
||||
|
||||
message ChatMessage { string text = 1; }
|
||||
|
@ -1,4 +1,11 @@
|
||||
use crate::log;
|
||||
use crate::{
|
||||
log,
|
||||
password_security::config::{
|
||||
decrypt_str_or_original, decrypt_vec_or_original, encrypt_str_or_original,
|
||||
encrypt_vec_or_original,
|
||||
},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use directories_next::ProjectDirs;
|
||||
use rand::Rng;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
@ -17,6 +24,7 @@ pub const CONNECT_TIMEOUT: u64 = 18_000;
|
||||
pub const REG_INTERVAL: i64 = 12_000;
|
||||
pub const COMPRESS_LEVEL: i32 = 3;
|
||||
const SERIAL: i32 = 3;
|
||||
const PASSWORD_ENC_VERSION: &'static str = "00";
|
||||
// 128x128
|
||||
#[cfg(target_os = "macos")] // 128x128 on 160x160 canvas, then shrink to 128, mac looks better with padding
|
||||
pub const ICON: &str = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAyVBMVEUAAAAAcf8Acf8Acf8Acv8Acf8Acf8Acf8Acf8AcP8Acf8Ab/8AcP8Acf////8AaP/z+f/o8v/k7v/5/v/T5f8AYP/u9v/X6f+hx/+Kuv95pP8Aef/B1/+TwP9xoP8BdP/g6P+Irv9ZmP8Bgf/E3f98q/9sn/+01f+Es/9nm/9Jif8hhv8off/M4P+syP+avP86iP/c7f+xy/9yqf9Om/9hk/9Rjv+60P99tv9fpf88lv8yjf8Tgf8deP+kvP8BiP8NeP8hkP80gP8oj2VLAAAADXRSTlMA7o7qLvnaxZ1FOxYPjH9HWgAABHJJREFUeNrtm+tW4jAQgBfwuu7MtIUWsOUiCCioIIgLiqvr+z/UHq/LJKVkmwTcc/r9E2nzlU4mSTP9lpGRkZGR8VX5cZjfL+yCEXYL+/nDH//U/Pd8DgyTy39Xbv7oIAcWyB0cqbW/sweW2NtRaj8H1sgpGOwUIAH7Bkd7YJW9dXFwAJY5WNP/cmCZQnJvzIN18on5LwfWySXlxEPYAIcad8D6PdiHDbCfIFCADVBIENiFDbCbIACKPPXrZ+cP8E6/0znvP4EymgIEravIRcTxu8HxNSJ60a8W0AYECKrlAN+YwAthCd9wm1Ug6wKzIn5SgRduXfwkqDasCjx0XFzi9PV6zwNcIuhcWBOg+ikySq8C9UD4dEKWBCoOcspvAuLHTo9sCDQiFPHotRM48j8G5gVur1FdAN2uaYEuiz7xFsgEJ2RUoMUakXuBTHHoGxQYOBhHjeUBAefEnMAowFhaLBOKuOemBBbxLRQrH2PBCgMvNCPQGMeevTb9zLrPxz2Mo+QbEaijzPUcOOHMQZkKGRAIPem39+bypREMPTkQW/oCfk866zAkiIFG4yIKRE/aAnfiSd0WrORY6pFdXQEqi9mvAQm0RIOSnoCcZ8vJoz3diCnjRk+g8VP4/fuQDJ2Lxr6WwG0gXs9aTpDzW0vgDBlVUpixR8gYk44AD8FrUKHr8JQJGgIDnoDqoALxmWPQSi9AVVzm8gKUuEPGr/QCvptwJkbSYT/TC4S8C96DGjTj86aHtAI0x2WaBIq0eSYYpRa4EsdWVVwWu9O0Aj6f6dyBMnwEraeOgSYu0wZlauzA47QCbT7DgAQSE+hZWoEBF/BBmWOewNMK3BsSqKUW4MGcWqCSVmDkbvkXGKQOwg6PAUO9oL3xXhA20yaiCjuwYygRVQlUOTWTCf2SuNJTxeFjgaHByGuAIvd8ItdPLTDhS7IuqEE1YSKVOgbayLhSFQhMzYh8hwfBs1r7c505YVIQYEdNoKwxK06MJiyrpUFHiF0NAfCQUVHoiRclIXJIR6C2fqG37pBHvcWpgwzvAtYwkR5UGV2e42UISdBJETl3mg8ouo54Rcnti1/vaT+iuUQBt500Cgo4U10BeHSkk57FB0JjWkKRMWgLUA0lLodtImAQdaMiiri3+gIAPZQoutHNsgKF1aaDMhMyIdBf8Th+Bh8MTjGWCpl5Wv43tDmnF+IUVMrcZgRoiAxhtrloYizNkZaAnF5leglbNhj0wYCAbCDvGb0mP4nib7O7ZlcYQ2m1gPtIZgVgGNNMeaVAaWR+57TrqgtUnm3sHQ+kYeE6fufUubG1ez50FXbPnWgBlgSABmN3TTcsRl2yWkHRrwbiunvk/W2+Mg1hPZplPDeXRbZzStFH15s1QIVd3UImP5z/bHpeeQLvRJ7XLFUffQIlCvqlXETQbgN9/rlYABGosv+Vi9m2Xs639YLGrZd0br+odetlvdsvbN56abfd4vbCzv9Q3v/ygoOV21A4OPpfXvH4Ai+5ZGRkZGRkbJA/t/I0QMzoMiEAAAAASUVORK5CYII=
|
||||
@ -114,7 +122,7 @@ pub struct Config2 {
|
||||
pub options: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct PeerConfig {
|
||||
#[serde(default)]
|
||||
pub password: Vec<u8>,
|
||||
@ -168,7 +176,7 @@ pub struct PeerInfoSerde {
|
||||
pub platform: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct TransferSerde {
|
||||
#[serde(default)]
|
||||
pub write_jobs: Vec<String>,
|
||||
@ -207,7 +215,16 @@ fn patch(path: PathBuf) -> PathBuf {
|
||||
|
||||
impl Config2 {
|
||||
fn load() -> Config2 {
|
||||
Config::load_::<Config2>("2")
|
||||
let mut config = Config::load_::<Config2>("2");
|
||||
if let Some(mut socks) = config.socks {
|
||||
let (password, store) = decrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION);
|
||||
socks.password = password;
|
||||
config.socks = Some(socks);
|
||||
if store {
|
||||
config.store();
|
||||
}
|
||||
}
|
||||
config
|
||||
}
|
||||
|
||||
pub fn file() -> PathBuf {
|
||||
@ -215,7 +232,12 @@ impl Config2 {
|
||||
}
|
||||
|
||||
fn store(&self) {
|
||||
Config::store_(self, "2");
|
||||
let mut config = self.clone();
|
||||
if let Some(mut socks) = config.socks {
|
||||
socks.password = encrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION);
|
||||
config.socks = Some(socks);
|
||||
}
|
||||
Config::store_(&config, "2");
|
||||
}
|
||||
|
||||
pub fn get() -> Config2 {
|
||||
@ -267,11 +289,19 @@ impl Config {
|
||||
}
|
||||
|
||||
fn load() -> Config {
|
||||
Config::load_::<Config>("")
|
||||
let mut config = Config::load_::<Config>("");
|
||||
let (password, store) = decrypt_str_or_original(&config.password, PASSWORD_ENC_VERSION);
|
||||
config.password = password;
|
||||
if store {
|
||||
config.store();
|
||||
}
|
||||
config
|
||||
}
|
||||
|
||||
fn store(&self) {
|
||||
Config::store_(self, "");
|
||||
let mut config = self.clone();
|
||||
config.password = encrypt_str_or_original(&config.password, PASSWORD_ENC_VERSION);
|
||||
Config::store_(&config, "");
|
||||
}
|
||||
|
||||
pub fn file() -> PathBuf {
|
||||
@ -627,7 +657,7 @@ impl Config {
|
||||
log::info!("id updated from {} to {}", id, new_id);
|
||||
}
|
||||
|
||||
pub fn set_password(password: &str) {
|
||||
pub fn set_security_password(password: &str) {
|
||||
let mut config = CONFIG.write().unwrap();
|
||||
if password == config.password {
|
||||
return;
|
||||
@ -636,13 +666,8 @@ impl Config {
|
||||
config.store();
|
||||
}
|
||||
|
||||
pub fn get_password() -> String {
|
||||
let mut password = CONFIG.read().unwrap().password.clone();
|
||||
if password.is_empty() {
|
||||
password = Config::get_auto_password();
|
||||
Config::set_password(&password);
|
||||
}
|
||||
password
|
||||
pub fn get_security_password() -> String {
|
||||
CONFIG.read().unwrap().password.clone()
|
||||
}
|
||||
|
||||
pub fn set_salt(salt: &str) {
|
||||
@ -714,7 +739,28 @@ impl PeerConfig {
|
||||
pub fn load(id: &str) -> PeerConfig {
|
||||
let _ = CONFIG.read().unwrap(); // for lock
|
||||
match confy::load_path(&Self::path(id)) {
|
||||
Ok(config) => config,
|
||||
Ok(config) => {
|
||||
let mut config: PeerConfig = config;
|
||||
let mut store = false;
|
||||
let (password, store2) =
|
||||
decrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION);
|
||||
config.password = password;
|
||||
store = store || store2;
|
||||
config.options.get_mut("rdp_password").map(|v| {
|
||||
let (password, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION);
|
||||
*v = password;
|
||||
store = store || store2;
|
||||
});
|
||||
config.options.get_mut("os-password").map(|v| {
|
||||
let (password, store2) = decrypt_str_or_original(v, PASSWORD_ENC_VERSION);
|
||||
*v = password;
|
||||
store = store || store2;
|
||||
});
|
||||
if store {
|
||||
config.store(id);
|
||||
}
|
||||
config
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Failed to load config: {}", err);
|
||||
Default::default()
|
||||
@ -724,7 +770,17 @@ impl PeerConfig {
|
||||
|
||||
pub fn store(&self, id: &str) {
|
||||
let _ = CONFIG.read().unwrap(); // for lock
|
||||
if let Err(err) = confy::store_path(Self::path(id), self) {
|
||||
let mut config = self.clone();
|
||||
config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION);
|
||||
config
|
||||
.options
|
||||
.get_mut("rdp_password")
|
||||
.map(|v| *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION));
|
||||
config
|
||||
.options
|
||||
.get_mut("os-password")
|
||||
.map(|v| *v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION));
|
||||
if let Err(err) = confy::store_path(Self::path(id), config) {
|
||||
log::error!("Failed to store config: {}", err);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ pub mod protos;
|
||||
pub use protos::message as message_proto;
|
||||
pub use protos::rendezvous as rendezvous_proto;
|
||||
pub use bytes;
|
||||
use config::Config;
|
||||
pub use futures;
|
||||
pub use protobuf;
|
||||
use std::{
|
||||
@ -26,6 +27,7 @@ pub use anyhow::{self, bail};
|
||||
pub use futures_util;
|
||||
pub mod config;
|
||||
pub mod fs;
|
||||
pub use lazy_static;
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
pub use mac_address;
|
||||
pub use rand;
|
||||
@ -34,7 +36,7 @@ pub use sodiumoxide;
|
||||
pub use tokio_socks;
|
||||
pub use tokio_socks::IntoTargetAddr;
|
||||
pub use tokio_socks::TargetAddr;
|
||||
pub use lazy_static;
|
||||
pub mod password_security;
|
||||
|
||||
#[cfg(feature = "quic")]
|
||||
pub type Stream = quic::Connection;
|
||||
@ -199,6 +201,14 @@ pub fn get_modified_time(path: &std::path::Path) -> SystemTime {
|
||||
.unwrap_or(UNIX_EPOCH)
|
||||
}
|
||||
|
||||
pub fn get_uuid() -> Vec<u8> {
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
if let Ok(id) = machine_uid::get() {
|
||||
return id.into();
|
||||
}
|
||||
Config::get_key_pair().1
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
330
libs/hbb_common/src/password_security.rs
Normal file
330
libs/hbb_common/src/password_security.rs
Normal file
@ -0,0 +1,330 @@
|
||||
pub mod password {
|
||||
use crate::config::Config;
|
||||
use std::{
|
||||
fmt::Display,
|
||||
str::FromStr,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref RANDOM_PASSWORD:Arc<RwLock<String>> = Arc::new(RwLock::new(Config::get_auto_password()));
|
||||
}
|
||||
|
||||
const SECURITY_ENABLED: &'static str = "security-password-enabled";
|
||||
const RANDOM_ENABLED: &'static str = "random-password-enabled";
|
||||
const ONETIME_ENABLED: &'static str = "onetime-password-enabled";
|
||||
const ONETIME_ACTIVATED: &'static str = "onetime-password-activated";
|
||||
const UPDATE_METHOD: &'static str = "random-password-update-method";
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum UpdateMethod {
|
||||
KEEP,
|
||||
UPDATE,
|
||||
DISABLE,
|
||||
}
|
||||
|
||||
impl FromStr for UpdateMethod {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s == "KEEP" {
|
||||
Ok(Self::KEEP)
|
||||
} else if s == "UPDATE" {
|
||||
Ok(Self::UPDATE)
|
||||
} else if s == "DISABLE" {
|
||||
Ok(Self::DISABLE)
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for UpdateMethod {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
UpdateMethod::KEEP => write!(f, "KEEP"),
|
||||
UpdateMethod::UPDATE => write!(f, "UPDATE"),
|
||||
UpdateMethod::DISABLE => write!(f, "DISABLE"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_random_password(password: &str) {
|
||||
*RANDOM_PASSWORD.write().unwrap() = password.to_owned();
|
||||
}
|
||||
|
||||
pub fn random_password() -> String {
|
||||
let mut password = RANDOM_PASSWORD.read().unwrap().clone();
|
||||
if password.is_empty() {
|
||||
password = Config::get_auto_password();
|
||||
set_random_password(&password);
|
||||
}
|
||||
password
|
||||
}
|
||||
|
||||
pub fn random_password_valid() -> bool {
|
||||
if random_enabled() {
|
||||
onetime_password_activated() || !onetime_password_enabled()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn passwords() -> Vec<String> {
|
||||
let mut v = vec![];
|
||||
if random_password_valid() {
|
||||
v.push(random_password());
|
||||
}
|
||||
if security_enabled() {
|
||||
v.push(Config::get_security_password());
|
||||
}
|
||||
v
|
||||
}
|
||||
|
||||
pub fn after_session(authorized: bool) {
|
||||
if authorized && random_enabled() {
|
||||
UpdateMethod::from_str(&update_method())
|
||||
.map(|method| match method {
|
||||
UpdateMethod::KEEP => {}
|
||||
UpdateMethod::UPDATE => set_random_password(&Config::get_auto_password()),
|
||||
UpdateMethod::DISABLE => set_random_enabled(false),
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_method() -> String {
|
||||
let mut method = Config::get_option(UPDATE_METHOD);
|
||||
if UpdateMethod::from_str(&method).is_err() {
|
||||
method = UpdateMethod::KEEP.to_string(); // default is keep
|
||||
set_update_method(&method);
|
||||
}
|
||||
method
|
||||
}
|
||||
|
||||
pub fn set_update_method(method: &str) {
|
||||
Config::set_option(UPDATE_METHOD.to_owned(), method.to_owned());
|
||||
}
|
||||
|
||||
pub fn random_enabled() -> bool {
|
||||
str2bool(RANDOM_ENABLED, true, || {
|
||||
set_onetime_password_activated(false);
|
||||
set_random_password(&Config::get_auto_password());
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_random_enabled(enabled: bool) {
|
||||
if enabled != random_enabled() {
|
||||
Config::set_option(RANDOM_ENABLED.to_owned(), bool2str(enabled));
|
||||
set_onetime_password_activated(false);
|
||||
if enabled {
|
||||
set_random_password(&Config::get_auto_password());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn security_enabled() -> bool {
|
||||
str2bool(SECURITY_ENABLED, true, || {})
|
||||
}
|
||||
|
||||
pub fn set_security_enabled(enabled: bool) {
|
||||
if enabled != security_enabled() {
|
||||
Config::set_option(SECURITY_ENABLED.to_owned(), bool2str(enabled));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn onetime_password_enabled() -> bool {
|
||||
str2bool(ONETIME_ENABLED, false, || {
|
||||
set_onetime_password_activated(false);
|
||||
set_random_password(&Config::get_auto_password());
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_onetime_password_enabled(enabled: bool) {
|
||||
if enabled != onetime_password_enabled() {
|
||||
Config::set_option(ONETIME_ENABLED.to_owned(), bool2str(enabled));
|
||||
set_onetime_password_activated(false);
|
||||
set_random_password(&Config::get_auto_password());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn onetime_password_activated() -> bool {
|
||||
str2bool(ONETIME_ACTIVATED, false, || {})
|
||||
}
|
||||
|
||||
pub fn set_onetime_password_activated(activated: bool) {
|
||||
if activated != onetime_password_activated() {
|
||||
Config::set_option(ONETIME_ACTIVATED.to_owned(), bool2str(activated));
|
||||
if activated {
|
||||
set_random_password(&Config::get_auto_password());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// notice: Function nesting
|
||||
fn str2bool(key: &str, default: bool, default_set: impl Fn()) -> bool {
|
||||
let option = Config::get_option(key);
|
||||
if option == "Y" {
|
||||
true
|
||||
} else if option == "N" {
|
||||
false
|
||||
} else {
|
||||
Config::set_option(key.to_owned(), bool2str(default));
|
||||
default_set();
|
||||
default
|
||||
}
|
||||
}
|
||||
|
||||
fn bool2str(option: bool) -> String {
|
||||
if option { "Y" } else { "N" }.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
pub mod config {
|
||||
use super::base64::decrypt as decrypt00;
|
||||
use super::base64::encrypt as encrypt00;
|
||||
|
||||
const VERSION_LEN: usize = 2;
|
||||
|
||||
pub fn encrypt_str_or_original(s: &str, version: &str) -> String {
|
||||
if version.len() == VERSION_LEN {
|
||||
if version == "00" {
|
||||
if let Ok(s) = encrypt00(s.as_bytes()) {
|
||||
return version.to_owned() + &s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s.to_owned()
|
||||
}
|
||||
|
||||
// bool: whether should store to re-encrypt when load
|
||||
pub fn decrypt_str_or_original(s: &str, current_version: &str) -> (String, bool) {
|
||||
if s.len() > VERSION_LEN {
|
||||
let version = &s[..VERSION_LEN];
|
||||
if version == "00" {
|
||||
if let Ok(v) = decrypt00(&s[VERSION_LEN..].as_bytes()) {
|
||||
return (
|
||||
String::from_utf8_lossy(&v).to_string(),
|
||||
version != current_version,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(s.to_owned(), !s.is_empty())
|
||||
}
|
||||
|
||||
pub fn encrypt_vec_or_original(v: &[u8], version: &str) -> Vec<u8> {
|
||||
if version.len() == VERSION_LEN {
|
||||
if version == "00" {
|
||||
if let Ok(s) = encrypt00(v) {
|
||||
let mut version = version.to_owned().into_bytes();
|
||||
version.append(&mut s.into_bytes());
|
||||
return version;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v.to_owned()
|
||||
}
|
||||
|
||||
// bool: whether should store to re-encrypt when load
|
||||
pub fn decrypt_vec_or_original(v: &[u8], current_version: &str) -> (Vec<u8>, bool) {
|
||||
if v.len() > VERSION_LEN {
|
||||
let version = String::from_utf8_lossy(&v[..VERSION_LEN]);
|
||||
if version == "00" {
|
||||
if let Ok(v) = decrypt00(&v[VERSION_LEN..]) {
|
||||
return (v, version != current_version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(v.to_owned(), !v.is_empty())
|
||||
}
|
||||
|
||||
mod test {
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use crate::password_security::config::*;
|
||||
|
||||
println!("test str");
|
||||
let data = "Hello World";
|
||||
let encrypted = encrypt_str_or_original(data, "00");
|
||||
let (decrypted, store) = decrypt_str_or_original(&encrypted, "00");
|
||||
println!("data: {}", data);
|
||||
println!("encrypted: {}", encrypted);
|
||||
println!("decrypted: {}", decrypted);
|
||||
assert_eq!(data, decrypted);
|
||||
assert_eq!("00", &encrypted[..2]);
|
||||
assert_eq!(store, false);
|
||||
let (_, store2) = decrypt_str_or_original(&encrypted, "01");
|
||||
assert_eq!(store2, true);
|
||||
|
||||
println!("test vec");
|
||||
let data: Vec<u8> = vec![1, 2, 3, 4];
|
||||
let encrypted = encrypt_vec_or_original(&data, "00");
|
||||
let (decrypted, store) = decrypt_vec_or_original(&encrypted, "00");
|
||||
println!("data: {:?}", data);
|
||||
println!("encrypted: {:?}", encrypted);
|
||||
println!("decrypted: {:?}", decrypted);
|
||||
assert_eq!(data, decrypted);
|
||||
assert_eq!("00".as_bytes(), &encrypted[..2]);
|
||||
assert_eq!(store, false);
|
||||
let (_, store2) = decrypt_vec_or_original(&encrypted, "01");
|
||||
assert_eq!(store2, true);
|
||||
|
||||
println!("test old");
|
||||
let data = "00Hello World";
|
||||
let (decrypted, store) = decrypt_str_or_original(&data, "00");
|
||||
assert_eq!(data, decrypted);
|
||||
assert_eq!(store, true);
|
||||
let data: Vec<u8> = vec!['0' as u8, '0' as u8, 1, 2, 3, 4];
|
||||
let (decrypted, store) = decrypt_vec_or_original(&data, "00");
|
||||
assert_eq!(data, decrypted);
|
||||
assert_eq!(store, true);
|
||||
let (_, store) = decrypt_str_or_original("", "00");
|
||||
assert_eq!(store, false);
|
||||
let (_, store) = decrypt_vec_or_original(&vec![], "00");
|
||||
assert_eq!(store, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod base64 {
|
||||
use super::symmetric_crypt;
|
||||
use sodiumoxide::base64;
|
||||
|
||||
pub fn encrypt(v: &[u8]) -> Result<String, ()> {
|
||||
if v.len() > 0 {
|
||||
symmetric_crypt(v, true).map(|v| base64::encode(v, base64::Variant::Original))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt(v: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
if v.len() > 0 {
|
||||
base64::decode(v, base64::Variant::Original).and_then(|v| symmetric_crypt(&v, false))
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn symmetric_crypt(data: &[u8], encrypt: bool) -> Result<Vec<u8>, ()> {
|
||||
use sodiumoxide::crypto::secretbox;
|
||||
use std::convert::TryInto;
|
||||
|
||||
let mut keybuf = crate::get_uuid();
|
||||
keybuf.resize(secretbox::KEYBYTES, 0);
|
||||
let key = secretbox::Key(keybuf.try_into().map_err(|_| ())?);
|
||||
let nonce = secretbox::Nonce([0; secretbox::NONCEBYTES]);
|
||||
|
||||
if encrypt {
|
||||
Ok(secretbox::seal(data, &nonce, &key))
|
||||
} else {
|
||||
secretbox::open(data, &nonce, &key)
|
||||
}
|
||||
}
|
@ -28,6 +28,7 @@ use hbb_common::{
|
||||
log,
|
||||
message_proto::{option_message::BoolOption, *},
|
||||
protobuf::Message as _,
|
||||
rand,
|
||||
rendezvous_proto::*,
|
||||
socket_client,
|
||||
sodiumoxide::crypto::{box_, secretbox, sign},
|
||||
@ -782,6 +783,7 @@ pub struct LoginConfigHandler {
|
||||
pub version: i64,
|
||||
pub conn_id: i32,
|
||||
features: Option<Features>,
|
||||
session_id: u64,
|
||||
}
|
||||
|
||||
impl Deref for LoginConfigHandler {
|
||||
@ -805,6 +807,7 @@ impl LoginConfigHandler {
|
||||
let config = self.load_config();
|
||||
self.remember = !config.password.is_empty();
|
||||
self.config = config;
|
||||
self.session_id = rand::random();
|
||||
}
|
||||
|
||||
pub fn should_auto_login(&self) -> String {
|
||||
@ -1140,6 +1143,7 @@ impl LoginConfigHandler {
|
||||
my_id,
|
||||
my_name: crate::username(),
|
||||
option: self.get_option_message(true).into(),
|
||||
session_id: self.session_id,
|
||||
..Default::default()
|
||||
};
|
||||
if self.is_file_transfer {
|
||||
|
@ -537,14 +537,6 @@ pub fn is_setup(name: &str) -> bool {
|
||||
name.to_lowercase().ends_with("setdown.exe") || name.to_lowercase().ends_with("安装.exe")
|
||||
}
|
||||
|
||||
pub fn get_uuid() -> Vec<u8> {
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
if let Ok(id) = machine_uid::get() {
|
||||
return id.into();
|
||||
}
|
||||
Config::get_key_pair().1
|
||||
}
|
||||
|
||||
pub fn get_custom_rendezvous_server(custom: String) -> String {
|
||||
if !custom.is_empty() {
|
||||
return custom;
|
||||
|
207
src/ipc.rs
207
src/ipc.rs
@ -7,7 +7,9 @@ use hbb_common::{
|
||||
config::{self, Config, Config2},
|
||||
futures::StreamExt as _,
|
||||
futures_util::sink::SinkExt,
|
||||
log, timeout, tokio,
|
||||
log,
|
||||
password_security::password,
|
||||
timeout, tokio,
|
||||
tokio::io::{AsyncRead, AsyncWrite},
|
||||
tokio_util::codec::Framed,
|
||||
ResultType,
|
||||
@ -20,6 +22,16 @@ use std::{collections::HashMap, sync::atomic::Ordering};
|
||||
#[cfg(not(windows))]
|
||||
use std::{fs::File, io::prelude::*};
|
||||
|
||||
const STR_RANDOM_PASSWORD: &'static str = "random-password";
|
||||
const STR_SECURITY_PASSWORD: &'static str = "security-password";
|
||||
const STR_RANDOM_PASSWORD_UPDATE_METHOD: &'static str = "random-password-update-method";
|
||||
const STR_RANDOM_PASSWORD_ENABLED: &'static str = "random-password-enabled";
|
||||
const STR_SECURITY_PASSWORD_ENABLED: &'static str = "security-password-enabled";
|
||||
const STR_ONETIME_PASSWORD_ENABLED: &'static str = "onetime-password-enabled";
|
||||
const STR_ONETIME_PASSWORD_ACTIVATED: &'static str = "onetime-password-activated";
|
||||
const STR_RANDOM_PASSWORD_VALID: &'static str = "random-password-valid";
|
||||
pub const STR_PASSWORD_DESCRIPTION: &'static str = "password-description";
|
||||
|
||||
// State with timestamp, because std::time::Instant cannot be serialized
|
||||
#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
|
||||
#[serde(tag = "t", content = "c")]
|
||||
@ -128,6 +140,7 @@ pub enum Data {
|
||||
ClipboardFileEnabled(bool),
|
||||
PrivacyModeState((i32, PrivacyModeState)),
|
||||
TestRendezvousServer,
|
||||
Bool((String, Option<bool>)),
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
@ -282,8 +295,20 @@ async fn handle(data: Data, stream: &mut Connection) {
|
||||
let value;
|
||||
if name == "id" {
|
||||
value = Some(Config::get_id());
|
||||
} else if name == "password" {
|
||||
value = Some(Config::get_password());
|
||||
} else if name == STR_RANDOM_PASSWORD {
|
||||
value = Some(password::random_password());
|
||||
} else if name == STR_SECURITY_PASSWORD {
|
||||
value = Some(Config::get_security_password());
|
||||
} else if name == STR_RANDOM_PASSWORD_UPDATE_METHOD {
|
||||
value = Some(password::update_method().to_string());
|
||||
} else if name == STR_PASSWORD_DESCRIPTION {
|
||||
value = Some(
|
||||
password::random_password()
|
||||
+ &password::security_enabled().to_string()
|
||||
+ &password::random_enabled().to_string()
|
||||
+ &password::onetime_password_enabled().to_string()
|
||||
+ &password::onetime_password_activated().to_string(),
|
||||
);
|
||||
} else if name == "salt" {
|
||||
value = Some(Config::get_salt());
|
||||
} else if name == "rendezvous_server" {
|
||||
@ -303,8 +328,12 @@ async fn handle(data: Data, stream: &mut Connection) {
|
||||
if name == "id" {
|
||||
Config::set_key_confirmed(false);
|
||||
Config::set_id(&value);
|
||||
} else if name == "password" {
|
||||
Config::set_password(&value);
|
||||
} else if name == STR_RANDOM_PASSWORD {
|
||||
password::set_random_password(&value);
|
||||
} else if name == STR_SECURITY_PASSWORD {
|
||||
Config::set_security_password(&value);
|
||||
} else if name == STR_RANDOM_PASSWORD_UPDATE_METHOD {
|
||||
password::set_update_method(&value);
|
||||
} else if name == "salt" {
|
||||
Config::set_salt(&value);
|
||||
} else {
|
||||
@ -344,6 +373,36 @@ async fn handle(data: Data, stream: &mut Connection) {
|
||||
Data::TestRendezvousServer => {
|
||||
crate::test_rendezvous_server();
|
||||
}
|
||||
Data::Bool((name, value)) => match value {
|
||||
None => {
|
||||
let value;
|
||||
if name == STR_SECURITY_PASSWORD_ENABLED {
|
||||
value = Some(password::security_enabled());
|
||||
} else if name == STR_RANDOM_PASSWORD_ENABLED {
|
||||
value = Some(password::random_enabled());
|
||||
} else if name == STR_ONETIME_PASSWORD_ENABLED {
|
||||
value = Some(password::onetime_password_enabled());
|
||||
} else if name == STR_ONETIME_PASSWORD_ACTIVATED {
|
||||
value = Some(password::onetime_password_activated());
|
||||
} else if name == STR_RANDOM_PASSWORD_VALID {
|
||||
value = Some(password::random_password_valid());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
allow_err!(stream.send(&Data::Bool((name, value))).await);
|
||||
}
|
||||
Some(value) => {
|
||||
if name == STR_SECURITY_PASSWORD_ENABLED {
|
||||
password::set_security_enabled(value);
|
||||
} else if name == STR_RANDOM_PASSWORD_ENABLED {
|
||||
password::set_random_enabled(value);
|
||||
} else if name == STR_ONETIME_PASSWORD_ENABLED {
|
||||
password::set_onetime_password_enabled(value);
|
||||
} else if name == STR_ONETIME_PASSWORD_ACTIVATED {
|
||||
password::set_onetime_password_activated(value);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -426,6 +485,10 @@ where
|
||||
.await
|
||||
}
|
||||
|
||||
async fn send_bool(&mut self, name: &str, value: bool) -> ResultType<()> {
|
||||
self.send(&Data::Bool((name.to_owned(), Some(value)))).await
|
||||
}
|
||||
|
||||
pub async fn next_timeout(&mut self, ms_timeout: u64) -> ResultType<Option<Data>> {
|
||||
Ok(timeout(ms_timeout, self.next()).await??)
|
||||
}
|
||||
@ -497,9 +560,128 @@ pub async fn set_config(name: &str, value: String) -> ResultType<()> {
|
||||
set_config_async(name, value).await
|
||||
}
|
||||
|
||||
pub fn set_password(v: String) -> ResultType<()> {
|
||||
Config::set_password(&v);
|
||||
set_config("password", v)
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
async fn get_bool(name: &str) -> ResultType<Option<bool>> {
|
||||
get_bool_async(name, 1_000).await
|
||||
}
|
||||
|
||||
async fn get_bool_async(name: &str, ms_timeout: u64) -> ResultType<Option<bool>> {
|
||||
let mut c = connect(ms_timeout, "").await?;
|
||||
c.send(&Data::Bool((name.to_owned(), None))).await?;
|
||||
if let Some(Data::Bool((name2, value))) = c.next_timeout(ms_timeout).await? {
|
||||
if name == name2 {
|
||||
return Ok(value);
|
||||
}
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
pub async fn set_bool_async(name: &str, value: bool) -> ResultType<()> {
|
||||
let mut c = connect(1000, "").await?;
|
||||
c.send_bool(name, value).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main(flavor = "current_thread")]
|
||||
pub async fn set_bool(name: &str, value: bool) -> ResultType<()> {
|
||||
set_bool_async(name, value).await
|
||||
}
|
||||
|
||||
pub fn get_random_password() -> String {
|
||||
if let Ok(Some(password)) = get_config(STR_RANDOM_PASSWORD) {
|
||||
password::set_random_password(&password);
|
||||
password
|
||||
} else {
|
||||
password::random_password()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_random_password(v: String) -> ResultType<()> {
|
||||
password::set_random_password(&v);
|
||||
set_config(STR_RANDOM_PASSWORD, v)
|
||||
}
|
||||
|
||||
pub fn set_security_password(v: String) -> ResultType<()> {
|
||||
Config::set_security_password(&v);
|
||||
set_config(STR_SECURITY_PASSWORD, v)
|
||||
}
|
||||
|
||||
pub fn random_password_update_method() -> String {
|
||||
if let Ok(Some(method)) = get_config(STR_RANDOM_PASSWORD_UPDATE_METHOD) {
|
||||
password::set_update_method(&method);
|
||||
method
|
||||
} else {
|
||||
password::update_method()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_random_password_update_method(method: String) -> ResultType<()> {
|
||||
password::set_update_method(&method);
|
||||
set_config(STR_RANDOM_PASSWORD_UPDATE_METHOD, method)
|
||||
}
|
||||
|
||||
pub fn is_random_password_enabled() -> bool {
|
||||
if let Ok(Some(enabled)) = get_bool(STR_RANDOM_PASSWORD_ENABLED) {
|
||||
password::set_random_enabled(enabled);
|
||||
enabled
|
||||
} else {
|
||||
password::random_enabled()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_random_password_enabled(enabled: bool) -> ResultType<()> {
|
||||
password::set_random_enabled(enabled);
|
||||
set_bool(STR_RANDOM_PASSWORD_ENABLED, enabled)
|
||||
}
|
||||
|
||||
pub fn is_security_password_enabled() -> bool {
|
||||
if let Ok(Some(enabled)) = get_bool(STR_SECURITY_PASSWORD_ENABLED) {
|
||||
password::set_security_enabled(enabled);
|
||||
enabled
|
||||
} else {
|
||||
password::security_enabled()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_security_password_enabled(enabled: bool) -> ResultType<()> {
|
||||
password::set_security_enabled(enabled);
|
||||
set_bool(STR_SECURITY_PASSWORD_ENABLED, enabled)
|
||||
}
|
||||
|
||||
pub fn is_onetime_password_enabled() -> bool {
|
||||
if let Ok(Some(enabled)) = get_bool(STR_ONETIME_PASSWORD_ENABLED) {
|
||||
password::set_onetime_password_enabled(enabled);
|
||||
enabled
|
||||
} else {
|
||||
password::onetime_password_enabled()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_onetime_password_enabled(enabled: bool) -> ResultType<()> {
|
||||
password::set_onetime_password_enabled(enabled);
|
||||
set_bool(STR_ONETIME_PASSWORD_ENABLED, enabled)
|
||||
}
|
||||
|
||||
pub fn is_onetime_password_activated() -> bool {
|
||||
if let Ok(Some(activated)) = get_bool(STR_ONETIME_PASSWORD_ACTIVATED) {
|
||||
password::set_onetime_password_activated(activated);
|
||||
activated
|
||||
} else {
|
||||
password::onetime_password_activated()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_onetime_password_activated(activated: bool) -> ResultType<()> {
|
||||
password::set_onetime_password_activated(activated);
|
||||
set_bool(STR_ONETIME_PASSWORD_ACTIVATED, activated)
|
||||
}
|
||||
|
||||
pub fn is_random_password_valid() -> bool {
|
||||
if let Ok(Some(valid)) = get_bool(STR_RANDOM_PASSWORD_VALID) {
|
||||
valid
|
||||
} else {
|
||||
password::random_password_valid()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_id() -> String {
|
||||
@ -518,15 +700,6 @@ pub fn get_id() -> String {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_password() -> String {
|
||||
if let Ok(Some(v)) = get_config("password") {
|
||||
Config::set_password(&v);
|
||||
v
|
||||
} else {
|
||||
Config::get_password()
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_rendezvous_server(ms_timeout: u64) -> (String, Vec<String>) {
|
||||
if let Ok(Some(v)) = get_config_async("rendezvous_server", ms_timeout).await {
|
||||
let mut urls = v.split(",");
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", "保持RustDesk后台服务"),
|
||||
("Ignore Battery Optimizations", "忽略电池优化"),
|
||||
("android_open_battery_optimizations_tip", "如需关闭此功能,请在接下来的RustDesk应用设置页面中,找到并进入 [电源] 页面,取消勾选 [不受限制]"),
|
||||
("Random Password After Session", "会话结束更新随机密码"),
|
||||
("Keep", "保持"),
|
||||
("Update", "更新"),
|
||||
("Disable", "禁用"),
|
||||
("Onetime Password", "一次性口令"),
|
||||
("Verification Method", "密码验证方式"),
|
||||
("Enable security password", "启用安全密码"),
|
||||
("Enable random password", "启用随机密码"),
|
||||
("Enable onetime password", "启用一次性访问功能"),
|
||||
("Disable onetime password", "禁用一次性访问功能"),
|
||||
("Activate onetime password", "激活一次性访问功能"),
|
||||
("Set security password", "设置安全密码"),
|
||||
("Connection not allowed", "对方不允许连接"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", ""),
|
||||
("Ignore Battery Optimizations", ""),
|
||||
("android_open_battery_optimizations_tip", ""),
|
||||
("Random Password After Session", ""),
|
||||
("Keep", ""),
|
||||
("Update", ""),
|
||||
("Disable", ""),
|
||||
("Onetime Password", ""),
|
||||
("Verification Method", ""),
|
||||
("Enable security password", ""),
|
||||
("Enable random password", ""),
|
||||
("Enable onetime password", ""),
|
||||
("Disable onetime password", ""),
|
||||
("Activate onetime password", ""),
|
||||
("Set security password", ""),
|
||||
("Connection not allowed", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", ""),
|
||||
("Ignore Battery Optimizations", ""),
|
||||
("android_open_battery_optimizations_tip", ""),
|
||||
("Random Password After Session", ""),
|
||||
("Keep", ""),
|
||||
("Update", ""),
|
||||
("Disable", ""),
|
||||
("Onetime Password", ""),
|
||||
("Verification Method", ""),
|
||||
("Enable security password", ""),
|
||||
("Enable random password", ""),
|
||||
("Enable onetime password", ""),
|
||||
("Disable onetime password", ""),
|
||||
("Activate onetime password", ""),
|
||||
("Set security password", ""),
|
||||
("Connection not allowed", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", ""),
|
||||
("Ignore Battery Optimizations", ""),
|
||||
("android_open_battery_optimizations_tip", ""),
|
||||
("Random Password After Session", ""),
|
||||
("Keep", ""),
|
||||
("Update", ""),
|
||||
("Disable", ""),
|
||||
("Onetime Password", ""),
|
||||
("Verification Method", ""),
|
||||
("Enable security password", ""),
|
||||
("Enable random password", ""),
|
||||
("Enable onetime password", ""),
|
||||
("Disable onetime password", ""),
|
||||
("Activate onetime password", ""),
|
||||
("Set security password", ""),
|
||||
("Connection not allowed", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", ""),
|
||||
("Ignore Battery Optimizations", ""),
|
||||
("android_open_battery_optimizations_tip", ""),
|
||||
("Random Password After Session", ""),
|
||||
("Keep", ""),
|
||||
("Update", ""),
|
||||
("Disable", ""),
|
||||
("Onetime Password", ""),
|
||||
("Verification Method", ""),
|
||||
("Enable security password", ""),
|
||||
("Enable random password", ""),
|
||||
("Enable onetime password", ""),
|
||||
("Disable onetime password", ""),
|
||||
("Activate onetime password", ""),
|
||||
("Set security password", ""),
|
||||
("Connection not allowed", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", ""),
|
||||
("Ignore Battery Optimizations", ""),
|
||||
("android_open_battery_optimizations_tip", ""),
|
||||
("Random Password After Session", ""),
|
||||
("Keep", ""),
|
||||
("Update", ""),
|
||||
("Disable", ""),
|
||||
("Onetime Password", ""),
|
||||
("Verification Method", ""),
|
||||
("Enable security password", ""),
|
||||
("Enable random password", ""),
|
||||
("Enable onetime password", ""),
|
||||
("Disable onetime password", ""),
|
||||
("Activate onetime password", ""),
|
||||
("Set security password", ""),
|
||||
("Connection not allowed", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", ""),
|
||||
("Ignore Battery Optimizations", ""),
|
||||
("android_open_battery_optimizations_tip", ""),
|
||||
("Random Password After Session", ""),
|
||||
("Keep", ""),
|
||||
("Update", ""),
|
||||
("Disable", ""),
|
||||
("Onetime Password", ""),
|
||||
("Verification Method", ""),
|
||||
("Enable security password", ""),
|
||||
("Enable random password", ""),
|
||||
("Enable onetime password", ""),
|
||||
("Disable onetime password", ""),
|
||||
("Activate onetime password", ""),
|
||||
("Set security password", ""),
|
||||
("Connection not allowed", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", ""),
|
||||
("Ignore Battery Optimizations", ""),
|
||||
("android_open_battery_optimizations_tip", ""),
|
||||
("Random Password After Session", ""),
|
||||
("Keep", ""),
|
||||
("Update", ""),
|
||||
("Disable", ""),
|
||||
("Onetime Password", ""),
|
||||
("Verification Method", ""),
|
||||
("Enable security password", ""),
|
||||
("Enable random password", ""),
|
||||
("Enable onetime password", ""),
|
||||
("Disable onetime password", ""),
|
||||
("Activate onetime password", ""),
|
||||
("Set security password", ""),
|
||||
("Connection not allowed", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", ""),
|
||||
("Ignore Battery Optimizations", ""),
|
||||
("android_open_battery_optimizations_tip", ""),
|
||||
("Random Password After Session", ""),
|
||||
("Keep", ""),
|
||||
("Update", ""),
|
||||
("Disable", ""),
|
||||
("Onetime Password", ""),
|
||||
("Verification Method", ""),
|
||||
("Enable security password", ""),
|
||||
("Enable random password", ""),
|
||||
("Enable onetime password", ""),
|
||||
("Disable onetime password", ""),
|
||||
("Activate onetime password", ""),
|
||||
("Set security password", ""),
|
||||
("Connection not allowed", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", ""),
|
||||
("Ignore Battery Optimizations", ""),
|
||||
("android_open_battery_optimizations_tip", ""),
|
||||
("Random Password After Session", ""),
|
||||
("Keep", ""),
|
||||
("Update", ""),
|
||||
("Disable", ""),
|
||||
("Onetime Password", ""),
|
||||
("Verification Method", ""),
|
||||
("Enable security password", ""),
|
||||
("Enable random password", ""),
|
||||
("Enable onetime password", ""),
|
||||
("Disable onetime password", ""),
|
||||
("Activate onetime password", ""),
|
||||
("Set security password", ""),
|
||||
("Connection not allowed", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", ""),
|
||||
("Ignore Battery Optimizations", ""),
|
||||
("android_open_battery_optimizations_tip", ""),
|
||||
("Random Password After Session", ""),
|
||||
("Keep", ""),
|
||||
("Update", ""),
|
||||
("Disable", ""),
|
||||
("Onetime Password", ""),
|
||||
("Verification Method", ""),
|
||||
("Enable security password", ""),
|
||||
("Enable random password", ""),
|
||||
("Enable onetime password", ""),
|
||||
("Disable onetime password", ""),
|
||||
("Activate onetime password", ""),
|
||||
("Set security password", ""),
|
||||
("Connection not allowed", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", ""),
|
||||
("Ignore Battery Optimizations", ""),
|
||||
("android_open_battery_optimizations_tip", ""),
|
||||
("Random Password After Session", ""),
|
||||
("Keep", ""),
|
||||
("Update", ""),
|
||||
("Disable", ""),
|
||||
("Onetime Password", ""),
|
||||
("Verification Method", ""),
|
||||
("Enable security password", ""),
|
||||
("Enable random password", ""),
|
||||
("Enable onetime password", ""),
|
||||
("Disable onetime password", ""),
|
||||
("Activate onetime password", ""),
|
||||
("Set security password", ""),
|
||||
("Connection not allowed", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", ""),
|
||||
("Ignore Battery Optimizations", ""),
|
||||
("android_open_battery_optimizations_tip", ""),
|
||||
("Random Password After Session", ""),
|
||||
("Keep", ""),
|
||||
("Update", ""),
|
||||
("Disable", ""),
|
||||
("Onetime Password", ""),
|
||||
("Verification Method", ""),
|
||||
("Enable security password", ""),
|
||||
("Enable random password", ""),
|
||||
("Enable onetime password", ""),
|
||||
("Disable onetime password", ""),
|
||||
("Activate onetime password", ""),
|
||||
("Set security password", ""),
|
||||
("Connection not allowed", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", ""),
|
||||
("Ignore Battery Optimizations", ""),
|
||||
("android_open_battery_optimizations_tip", ""),
|
||||
("Random Password After Session", ""),
|
||||
("Keep", ""),
|
||||
("Update", ""),
|
||||
("Disable", ""),
|
||||
("Onetime Password", ""),
|
||||
("Verification Method", ""),
|
||||
("Enable security password", ""),
|
||||
("Enable random password", ""),
|
||||
("Enable onetime password", ""),
|
||||
("Disable onetime password", ""),
|
||||
("Activate onetime password", ""),
|
||||
("Set security password", ""),
|
||||
("Connection not allowed", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", ""),
|
||||
("Ignore Battery Optimizations", ""),
|
||||
("android_open_battery_optimizations_tip", ""),
|
||||
("Random Password After Session", ""),
|
||||
("Keep", ""),
|
||||
("Update", ""),
|
||||
("Disable", ""),
|
||||
("Onetime Password", ""),
|
||||
("Verification Method", ""),
|
||||
("Enable security password", ""),
|
||||
("Enable random password", ""),
|
||||
("Enable onetime password", ""),
|
||||
("Disable onetime password", ""),
|
||||
("Activate onetime password", ""),
|
||||
("Set security password", ""),
|
||||
("Connection not allowed", ""),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -287,5 +287,18 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
||||
("Keep RustDesk background service", "保持RustDesk後台服務"),
|
||||
("Ignore Battery Optimizations", "忽略電池優化"),
|
||||
("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的RustDesk應用設置頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"),
|
||||
("Random Password After Session", "會話結束更新隨機密碼"),
|
||||
("Keep", "保持"),
|
||||
("Update", "更新"),
|
||||
("Disable", "禁用"),
|
||||
("Onetime Password", "一次性口令"),
|
||||
("Verification Method", "密碼驗證方式"),
|
||||
("Enable security password", "啟用安全密碼"),
|
||||
("Enable random password", "啟用隨機密碼"),
|
||||
("Enable onetime password", "啟用一次性訪問功能"),
|
||||
("Disable onetime password", "禁用一次性訪問功能"),
|
||||
("Activate onetime password", "激活一次性訪問功能"),
|
||||
("Set security password", "設置安全密碼"),
|
||||
("Connection not allowed", "對方不允許連接"),
|
||||
].iter().cloned().collect();
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ fn main() {
|
||||
return;
|
||||
} else if args[0] == "--password" {
|
||||
if args.len() == 2 {
|
||||
ipc::set_password(args[1].to_owned()).unwrap();
|
||||
ipc::set_security_password(args[1].to_owned()).unwrap();
|
||||
}
|
||||
return;
|
||||
} else if args[0] == "--check-hwcodec-config" {
|
||||
|
@ -389,7 +389,7 @@ impl RendezvousMediator {
|
||||
async fn register_pk(&mut self, socket: &mut FramedSocket) -> ResultType<()> {
|
||||
let mut msg_out = Message::new();
|
||||
let pk = Config::get_key_pair().1;
|
||||
let uuid = crate::get_uuid();
|
||||
let uuid = hbb_common::get_uuid();
|
||||
let id = Config::get_id();
|
||||
self.last_id_pk_registry = id.clone();
|
||||
msg_out.set_register_pk(RegisterPk {
|
||||
|
@ -8,6 +8,7 @@ use crate::video_service;
|
||||
use crate::{common::MOBILE_INFO2, mobile::connection_manager::start_channel};
|
||||
use crate::{ipc, VERSION};
|
||||
use hbb_common::fs::can_enable_overwrite_detection;
|
||||
use hbb_common::password_security::password;
|
||||
use hbb_common::{
|
||||
config::Config,
|
||||
fs,
|
||||
@ -35,6 +36,7 @@ pub type Sender = mpsc::UnboundedSender<(Instant, Arc<Message>)>;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref LOGIN_FAILURES: Arc::<Mutex<HashMap<String, (i32, i32, i32)>>> = Default::default();
|
||||
static ref SESSIONS: Arc::<Mutex<HashMap<String, Session>>> = Default::default();
|
||||
}
|
||||
pub static CLICK_TIME: AtomicI64 = AtomicI64::new(0);
|
||||
pub static MOUSE_MOVE_TIME: AtomicI64 = AtomicI64::new(0);
|
||||
@ -53,6 +55,14 @@ enum MessageInput {
|
||||
BlockOff,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Session {
|
||||
name: String,
|
||||
session_id: u64,
|
||||
last_recv_time: Arc<Mutex<Instant>>,
|
||||
random_password: String,
|
||||
}
|
||||
|
||||
pub struct Connection {
|
||||
inner: ConnInner,
|
||||
stream: super::Stream,
|
||||
@ -80,6 +90,8 @@ pub struct Connection {
|
||||
video_ack_required: bool,
|
||||
peer_info: (String, String),
|
||||
api_server: String,
|
||||
lr: LoginRequest,
|
||||
last_recv_time: Arc<Mutex<Instant>>,
|
||||
}
|
||||
|
||||
impl Subscriber for ConnInner {
|
||||
@ -111,6 +123,7 @@ const H1: Duration = Duration::from_secs(3600);
|
||||
const MILLI1: Duration = Duration::from_millis(1);
|
||||
const SEND_TIMEOUT_VIDEO: u64 = 12_000;
|
||||
const SEND_TIMEOUT_OTHER: u64 = SEND_TIMEOUT_VIDEO * 10;
|
||||
const SESSION_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
impl Connection {
|
||||
pub async fn start(
|
||||
@ -164,6 +177,8 @@ impl Connection {
|
||||
video_ack_required: false,
|
||||
peer_info: Default::default(),
|
||||
api_server: "".to_owned(),
|
||||
lr: Default::default(),
|
||||
last_recv_time: Arc::new(Mutex::new(Instant::now())),
|
||||
};
|
||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||
tokio::spawn(async move {
|
||||
@ -223,6 +238,7 @@ impl Connection {
|
||||
msg_out.set_misc(misc);
|
||||
conn.send(msg_out).await;
|
||||
conn.on_close("Close requested from connection manager", false);
|
||||
SESSIONS.lock().unwrap().remove(&conn.lr.my_id);
|
||||
break;
|
||||
}
|
||||
ipc::Data::ChatMessage{text} => {
|
||||
@ -316,6 +332,7 @@ impl Connection {
|
||||
},
|
||||
Ok(bytes) => {
|
||||
last_recv_time = Instant::now();
|
||||
*conn.last_recv_time.lock().unwrap() = Instant::now();
|
||||
if let Ok(msg_in) = Message::parse_from_bytes(&bytes) {
|
||||
if !conn.on_message(msg_in).await {
|
||||
break;
|
||||
@ -398,6 +415,7 @@ impl Connection {
|
||||
video_service::notify_video_frame_feched(id, None);
|
||||
scrap::codec::Encoder::update_video_encoder(id, scrap::codec::EncoderUpdate::Remove);
|
||||
video_service::VIDEO_QOS.lock().unwrap().reset();
|
||||
password::after_session(conn.authorized);
|
||||
if let Err(err) = conn.try_port_forward_loop(&mut rx_from_cm).await {
|
||||
conn.on_close(&err.to_string(), false);
|
||||
}
|
||||
@ -571,7 +589,7 @@ impl Connection {
|
||||
let url = self.api_server.clone();
|
||||
let mut v = v;
|
||||
v["id"] = json!(Config::get_id());
|
||||
v["uuid"] = json!(base64::encode(crate::get_uuid()));
|
||||
v["uuid"] = json!(base64::encode(hbb_common::get_uuid()));
|
||||
v["Id"] = json!(self.inner.id);
|
||||
tokio::spawn(async move {
|
||||
allow_err!(Self::post_audit_async(url, v).await);
|
||||
@ -778,8 +796,77 @@ impl Connection {
|
||||
self.tx_input.send(MessageInput::Key((msg, press))).ok();
|
||||
}
|
||||
|
||||
fn validate_one_password(&self, password: String) -> bool {
|
||||
if password.len() == 0 {
|
||||
return false;
|
||||
}
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(password);
|
||||
hasher.update(&self.hash.salt);
|
||||
let mut hasher2 = Sha256::new();
|
||||
hasher2.update(&hasher.finalize()[..]);
|
||||
hasher2.update(&self.hash.challenge);
|
||||
hasher2.finalize()[..] == self.lr.password[..]
|
||||
}
|
||||
|
||||
fn validate_password(&mut self) -> bool {
|
||||
if password::security_enabled() {
|
||||
if self.validate_one_password(Config::get_security_password()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if password::random_password_valid() {
|
||||
let password = password::random_password();
|
||||
if self.validate_one_password(password.clone()) {
|
||||
if password::onetime_password_activated() {
|
||||
password::set_onetime_password_activated(false);
|
||||
}
|
||||
SESSIONS.lock().unwrap().insert(
|
||||
self.lr.my_id.clone(),
|
||||
Session {
|
||||
name: self.lr.my_name.clone(),
|
||||
session_id: self.lr.session_id,
|
||||
last_recv_time: self.last_recv_time.clone(),
|
||||
random_password: password,
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn is_of_recent_session(&mut self) -> bool {
|
||||
let session = SESSIONS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get(&self.lr.my_id)
|
||||
.map(|s| s.to_owned());
|
||||
if let Some(session) = session {
|
||||
if session.name == self.lr.my_name
|
||||
&& session.session_id == self.lr.session_id
|
||||
&& !self.lr.password.is_empty()
|
||||
&& self.validate_one_password(session.random_password.clone())
|
||||
&& session.last_recv_time.lock().unwrap().elapsed() < SESSION_TIMEOUT
|
||||
{
|
||||
SESSIONS.lock().unwrap().insert(
|
||||
self.lr.my_id.clone(),
|
||||
Session {
|
||||
name: self.lr.my_name.clone(),
|
||||
session_id: self.lr.session_id,
|
||||
last_recv_time: self.last_recv_time.clone(),
|
||||
random_password: session.random_password,
|
||||
},
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
async fn on_message(&mut self, msg: Message) -> bool {
|
||||
if let Some(message::Union::LoginRequest(lr)) = msg.union {
|
||||
self.lr = lr.clone();
|
||||
if let Some(o) = lr.option.as_ref() {
|
||||
self.update_option(o).await;
|
||||
if let Some(q) = o.video_codec_state.clone().take() {
|
||||
@ -850,15 +937,19 @@ impl Connection {
|
||||
}
|
||||
if !crate::is_ip(&lr.username) && lr.username != Config::get_id() {
|
||||
self.send_login_error("Offline").await;
|
||||
} else if self.is_of_recent_session() {
|
||||
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;
|
||||
}
|
||||
} else if lr.password.is_empty() {
|
||||
self.try_start_cm(lr.my_id, lr.my_name, false);
|
||||
} else {
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(&Config::get_password());
|
||||
hasher.update(&self.hash.salt);
|
||||
let mut hasher2 = Sha256::new();
|
||||
hasher2.update(&hasher.finalize()[..]);
|
||||
hasher2.update(&self.hash.challenge);
|
||||
if password::passwords().len() == 0 {
|
||||
self.send_login_error("Connection not allowed").await;
|
||||
return false;
|
||||
}
|
||||
let mut failure = LOGIN_FAILURES
|
||||
.lock()
|
||||
.unwrap()
|
||||
@ -871,7 +962,7 @@ impl Connection {
|
||||
.await;
|
||||
} else if time == failure.0 && failure.1 > 6 {
|
||||
self.send_login_error("Please try 1 minute later").await;
|
||||
} else if hasher2.finalize()[..] != lr.password[..] {
|
||||
} else if !self.validate_password() {
|
||||
if failure.0 == time {
|
||||
failure.1 += 1;
|
||||
failure.2 += 1;
|
||||
@ -1103,6 +1194,11 @@ impl Connection {
|
||||
Some(Instant::now().into()),
|
||||
);
|
||||
}
|
||||
Some(misc::Union::CloseReason(_)) => {
|
||||
self.on_close("Peer close", true);
|
||||
SESSIONS.lock().unwrap().remove(&self.lr.my_id);
|
||||
return false;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
|
99
src/ui.rs
99
src/ui.rs
@ -43,6 +43,7 @@ struct UI(
|
||||
Arc<Mutex<HashMap<String, String>>>,
|
||||
Arc<Mutex<String>>,
|
||||
mpsc::UnboundedSender<ipc::Data>,
|
||||
Arc<Mutex<String>>,
|
||||
);
|
||||
|
||||
struct UIHostHandler;
|
||||
@ -169,7 +170,7 @@ pub fn start(args: &mut [String]) {
|
||||
impl UI {
|
||||
fn new(childs: Childs) -> Self {
|
||||
let res = check_connect_status(true);
|
||||
Self(childs, res.0, res.1, Default::default(), res.2)
|
||||
Self(childs, res.0, res.1, Default::default(), res.2, res.3)
|
||||
}
|
||||
|
||||
fn recent_sessions_updated(&mut self) -> bool {
|
||||
@ -186,16 +187,16 @@ impl UI {
|
||||
ipc::get_id()
|
||||
}
|
||||
|
||||
fn get_password(&mut self) -> String {
|
||||
ipc::get_password()
|
||||
fn get_random_password(&self) -> String {
|
||||
ipc::get_random_password()
|
||||
}
|
||||
|
||||
fn update_password(&mut self, password: String) {
|
||||
if password.is_empty() {
|
||||
allow_err!(ipc::set_password(Config::get_auto_password()));
|
||||
} else {
|
||||
allow_err!(ipc::set_password(password));
|
||||
}
|
||||
fn update_random_password(&self) {
|
||||
allow_err!(ipc::set_random_password(Config::get_auto_password()));
|
||||
}
|
||||
|
||||
fn set_security_password(&self, password: String) {
|
||||
allow_err!(ipc::set_security_password(password));
|
||||
}
|
||||
|
||||
fn get_remote_id(&mut self) -> String {
|
||||
@ -704,7 +705,7 @@ impl UI {
|
||||
}
|
||||
|
||||
fn get_uuid(&self) -> String {
|
||||
base64::encode(crate::get_uuid())
|
||||
base64::encode(hbb_common::get_uuid())
|
||||
}
|
||||
|
||||
fn open_url(&self, url: String) {
|
||||
@ -774,6 +775,54 @@ impl UI {
|
||||
fn get_langs(&self) -> String {
|
||||
crate::lang::LANGS.to_string()
|
||||
}
|
||||
|
||||
fn random_password_update_method(&self) -> String {
|
||||
ipc::random_password_update_method()
|
||||
}
|
||||
|
||||
fn set_random_password_update_method(&self, method: String) {
|
||||
allow_err!(ipc::set_random_password_update_method(method));
|
||||
}
|
||||
|
||||
fn is_random_password_enabled(&self) -> bool {
|
||||
ipc::is_random_password_enabled()
|
||||
}
|
||||
|
||||
fn set_random_password_enabled(&self, enabled: bool) {
|
||||
allow_err!(ipc::set_random_password_enabled(enabled));
|
||||
}
|
||||
|
||||
fn is_security_password_enabled(&self) -> bool {
|
||||
ipc::is_security_password_enabled()
|
||||
}
|
||||
|
||||
fn set_security_password_enabled(&self, enabled: bool) {
|
||||
allow_err!(ipc::set_security_password_enabled(enabled));
|
||||
}
|
||||
|
||||
fn is_onetime_password_enabled(&self) -> bool {
|
||||
ipc::is_onetime_password_enabled()
|
||||
}
|
||||
|
||||
fn set_onetime_password_enabled(&self, enabled: bool) {
|
||||
allow_err!(ipc::set_onetime_password_enabled(enabled));
|
||||
}
|
||||
|
||||
fn is_onetime_password_activated(&self) -> bool {
|
||||
ipc::is_onetime_password_activated()
|
||||
}
|
||||
|
||||
fn set_onetime_password_activated(&self, activated: bool) {
|
||||
allow_err!(ipc::set_onetime_password_activated(activated));
|
||||
}
|
||||
|
||||
fn is_random_password_valid(&self) -> bool {
|
||||
ipc::is_random_password_valid()
|
||||
}
|
||||
|
||||
fn password_description(&mut self) -> String {
|
||||
self.5.lock().unwrap().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl sciter::EventHandler for UI {
|
||||
@ -783,8 +832,9 @@ impl sciter::EventHandler for UI {
|
||||
fn is_xfce();
|
||||
fn using_public_server();
|
||||
fn get_id();
|
||||
fn get_password();
|
||||
fn update_password(String);
|
||||
fn get_random_password();
|
||||
fn update_random_password();
|
||||
fn set_security_password(String);
|
||||
fn get_remote_id();
|
||||
fn set_remote_id(String);
|
||||
fn closing(i32, i32, i32, i32);
|
||||
@ -854,6 +904,18 @@ impl sciter::EventHandler for UI {
|
||||
fn get_uuid();
|
||||
fn has_hwcodec();
|
||||
fn get_langs();
|
||||
fn random_password_update_method();
|
||||
fn set_random_password_update_method(String);
|
||||
fn is_random_password_enabled();
|
||||
fn set_random_password_enabled(bool);
|
||||
fn is_security_password_enabled();
|
||||
fn set_security_password_enabled(bool);
|
||||
fn is_onetime_password_enabled();
|
||||
fn set_onetime_password_enabled(bool);
|
||||
fn is_onetime_password_activated();
|
||||
fn set_onetime_password_activated(bool);
|
||||
fn is_random_password_valid();
|
||||
fn password_description();
|
||||
}
|
||||
}
|
||||
|
||||
@ -893,6 +955,7 @@ async fn check_connect_status_(
|
||||
status: Arc<Mutex<Status>>,
|
||||
options: Arc<Mutex<HashMap<String, String>>>,
|
||||
rx: mpsc::UnboundedReceiver<ipc::Data>,
|
||||
password: Arc<Mutex<String>>,
|
||||
) {
|
||||
let mut key_confirmed = false;
|
||||
let mut rx = rx;
|
||||
@ -919,6 +982,8 @@ async fn check_connect_status_(
|
||||
Ok(Some(ipc::Data::Config((name, Some(value))))) => {
|
||||
if name == "id" {
|
||||
id = value;
|
||||
} else if name == ipc::STR_PASSWORD_DESCRIPTION {
|
||||
*password.lock().unwrap() = value;
|
||||
}
|
||||
}
|
||||
Ok(Some(ipc::Data::OnlineStatus(Some((mut x, c))))) => {
|
||||
@ -938,6 +1003,7 @@ async fn check_connect_status_(
|
||||
c.send(&ipc::Data::OnlineStatus(None)).await.ok();
|
||||
c.send(&ipc::Data::Options(None)).await.ok();
|
||||
c.send(&ipc::Data::Config(("id".to_owned(), None))).await.ok();
|
||||
c.send(&ipc::Data::Config((ipc::STR_PASSWORD_DESCRIPTION.to_owned(), None))).await.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -986,14 +1052,19 @@ fn check_connect_status(
|
||||
Arc<Mutex<Status>>,
|
||||
Arc<Mutex<HashMap<String, String>>>,
|
||||
mpsc::UnboundedSender<ipc::Data>,
|
||||
Arc<Mutex<String>>,
|
||||
) {
|
||||
let status = Arc::new(Mutex::new((0, false, 0, "".to_owned())));
|
||||
let options = Arc::new(Mutex::new(Config::get_options()));
|
||||
let cloned = status.clone();
|
||||
let cloned_options = options.clone();
|
||||
let (tx, rx) = mpsc::unbounded_channel::<ipc::Data>();
|
||||
std::thread::spawn(move || check_connect_status_(reconnect, cloned, cloned_options, rx));
|
||||
(status, options, tx)
|
||||
let password = Arc::new(Mutex::new(String::default()));
|
||||
let cloned_password = password.clone();
|
||||
std::thread::spawn(move || {
|
||||
check_connect_status_(reconnect, cloned, cloned_options, rx, cloned_password)
|
||||
});
|
||||
(status, options, tx, password)
|
||||
}
|
||||
|
||||
const INVALID_FORMAT: &'static str = "Invalid format";
|
||||
|
@ -120,7 +120,7 @@ textarea:empty {
|
||||
@ELLIPSIS;
|
||||
}
|
||||
|
||||
div.password svg {
|
||||
div.password svg:not(.checkmark) {
|
||||
padding-left: 1em;
|
||||
size: 16px;
|
||||
color: #ddd;
|
||||
|
@ -141,7 +141,7 @@ function adjustBorder() {
|
||||
if (el) el.attributes.toggleClass("active", view.windowState == View.WINDOW_FULL_SCREEN);
|
||||
}
|
||||
|
||||
var svg_checkmark = <svg viewBox="0 0 492 492"><path d="M484 105l-16-17a27 27 0 00-38 0L204 315 62 173c-5-5-12-7-19-7s-14 2-19 7L8 189a27 27 0 000 38l160 160v1l16 16c5 5 12 8 19 8 8 0 14-3 20-8l16-16v-1l245-244a27 27 0 000-38z"/></svg>;
|
||||
var svg_checkmark = <svg class="checkmark" viewBox="0 0 492 492"><path d="M484 105l-16-17a27 27 0 00-38 0L204 315 62 173c-5-5-12-7-19-7s-14 2-19 7L8 189a27 27 0 000 38l160 160v1l16 16c5 5 12 8 19 8 8 0 14-3 20-8l16-16v-1l245-244a27 27 0 000-38z"/></svg>;
|
||||
var svg_edit = <svg #edit viewBox="0 0 384 384">
|
||||
<path d="M0 304v80h80l236-236-80-80zM378 56L328 6c-8-8-22-8-30 0l-39 39 80 80 39-39c8-8 8-22 0-30z"/>
|
||||
</svg>;
|
||||
|
@ -403,3 +403,18 @@ div.remote-session svg#menu {
|
||||
background: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
svg#refresh-password {
|
||||
display: inline-block;
|
||||
stroke:#ddd;
|
||||
}
|
||||
|
||||
svg#refresh-password:hover {
|
||||
stroke:color(text);
|
||||
}
|
||||
|
||||
li:disabled, li:disabled:hover {
|
||||
color: color(lighter-text);
|
||||
background: color(menu);
|
||||
}
|
||||
|
||||
|
189
src/ui/index.tis
189
src/ui/index.tis
@ -20,6 +20,7 @@ var svg_menu = <svg #menu viewBox="0 0 512 512">
|
||||
<circle cx="256" cy="448" r="64"/>
|
||||
<circle cx="256" cy="64" r="64"/>
|
||||
</svg>;
|
||||
var svg_refresh_password = <svg #refresh-password xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="M2.5 2v6h6M2.66 15.57a10 10 0 1 0 .57-8.38"/></svg>;
|
||||
|
||||
var my_id = "";
|
||||
function get_id() {
|
||||
@ -520,10 +521,6 @@ class App: Reactor.Component
|
||||
var is_can_screen_recording = handler.is_can_screen_recording(false);
|
||||
return
|
||||
<div .app>
|
||||
<popup><menu.context #edit-password-context>
|
||||
<li #refresh-password>{translate('Refresh random password')}</li>
|
||||
<li #set-password>{translate('Set your own password')}</li>
|
||||
</menu></popup>
|
||||
<div .left-pane>
|
||||
<div>
|
||||
<div .title>{translate('Your Desktop')}</div>
|
||||
@ -533,8 +530,7 @@ class App: Reactor.Component
|
||||
{key_confirmed ? <input type="text" readonly value={formatId(get_id())}/> : translate("Generating ...")}
|
||||
</div>
|
||||
<div .your-desktop>
|
||||
<div>{translate('Password')}</div>
|
||||
<Password />
|
||||
<PasswordArea />
|
||||
</div>
|
||||
</div>
|
||||
{!is_win || handler.is_installed() ? "": <InstallMe />}
|
||||
@ -806,44 +802,151 @@ function watch_screen_recording() {
|
||||
|
||||
class PasswordEyeArea : Reactor.Component {
|
||||
render() {
|
||||
var show = handler.is_random_password_valid();
|
||||
var value = show ? handler.get_random_password() : "-";
|
||||
return
|
||||
<div .eye-area style="width: *">
|
||||
<input|text @{this.input} readonly value="******" />
|
||||
{svg_eye}
|
||||
<input|text @{this.input} readonly value={value} />
|
||||
{svg_refresh_password}
|
||||
</div>;
|
||||
}
|
||||
|
||||
event mouseenter {
|
||||
var me = this;
|
||||
me.leaved = false;
|
||||
me.timer(300ms, function() {
|
||||
if (me.leaved) return;
|
||||
me.input.value = handler.get_password();
|
||||
});
|
||||
}
|
||||
|
||||
event mouseleave {
|
||||
this.leaved = true;
|
||||
this.input.value = "******";
|
||||
event click $(svg#refresh-password) (_, me) {
|
||||
if (handler.is_random_password_valid()) handler.update_random_password();
|
||||
this.update();
|
||||
}
|
||||
}
|
||||
|
||||
class Password: Reactor.Component {
|
||||
var verificationMethodMenu;
|
||||
class VerificationMethodMenu: Reactor.Component {
|
||||
function this() {
|
||||
verificationMethodMenu = this;
|
||||
}
|
||||
|
||||
function render() {
|
||||
return <div .password style="flow:horizontal">
|
||||
<PasswordEyeArea />
|
||||
{svg_edit}
|
||||
if (!this.show) return <li />;
|
||||
var me = this;
|
||||
self.timer(1ms, function() { me.toggleMenuState() });
|
||||
return <li>{translate('Verification Method')}
|
||||
<menu #verification-method>
|
||||
<li #verification-method-security><span>{svg_checkmark}</span>{translate('Enable security password')}</li>
|
||||
<li #verification-method-random><span>{svg_checkmark}</span>{translate('Enable random password')}</li>
|
||||
</menu>
|
||||
</li>;
|
||||
}
|
||||
|
||||
function toggleMenuState() {
|
||||
var security_enabled = handler.is_security_password_enabled();
|
||||
var random_enabled = handler.is_random_password_enabled();
|
||||
var onetime_enabled = handler.is_onetime_password_enabled();
|
||||
for (var (index, el) in this.$$(menu#verification-method>li)) {
|
||||
if (index == 0) el.attributes.toggleClass("selected", security_enabled);
|
||||
if (index == 1) el.attributes.toggleClass("selected", random_enabled);
|
||||
}
|
||||
}
|
||||
|
||||
event click $(menu#verification-method>li) (_, me) {
|
||||
switch (me.id.substring('verification-method-'.length)) {
|
||||
case 'security':
|
||||
{
|
||||
var security_enabled = handler.is_security_password_enabled();
|
||||
handler.set_security_password_enabled(!security_enabled);
|
||||
}
|
||||
break;
|
||||
case 'random':
|
||||
{
|
||||
var random_enabled = handler.is_random_password_enabled();
|
||||
handler.set_random_password_enabled(!random_enabled);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this.toggleMenuState();
|
||||
passwordArea.update();
|
||||
}
|
||||
}
|
||||
|
||||
var randomPasswordUpdateMethodMenu;
|
||||
class RandomPasswordUpdateMethodMenu: Reactor.Component {
|
||||
function this() {
|
||||
randomPasswordUpdateMethodMenu = this;
|
||||
}
|
||||
|
||||
function render() {
|
||||
if (!this.show) return <li />;
|
||||
var me = this;
|
||||
var random_enabled = handler.is_random_password_enabled();
|
||||
self.timer(1ms, function() { me.toggleMenuState() });
|
||||
return <li disabled={ random_enabled ? "false" : "true" }>{translate('Random Password After Session')}
|
||||
<menu #random-password-update-method>
|
||||
<li #random-password-update-method-keep><span>{svg_checkmark}</span>{translate('Keep')}</li>
|
||||
<li #random-password-update-method-update><span>{svg_checkmark}</span>{translate('Update')}</li>
|
||||
<li #random-password-update-method-disable><span>{svg_checkmark}</span>{translate('Disable')}</li>
|
||||
</menu>
|
||||
</li>;
|
||||
}
|
||||
|
||||
function toggleMenuState() {
|
||||
var method = handler.random_password_update_method();
|
||||
for (var (index, el) in this.$$(menu#random-password-update-method>li)) {
|
||||
if (index == 0) el.attributes.toggleClass("selected", method == "KEEP");
|
||||
if (index == 1) el.attributes.toggleClass("selected", method == "UPDATE");
|
||||
if (index == 2) el.attributes.toggleClass("selected", method == "DISABLE");
|
||||
}
|
||||
}
|
||||
|
||||
event click $(menu#random-password-update-method>li) (_, me) {
|
||||
if (me.id === 'random-password-update-method-keep') handler.set_random_password_update_method("KEEP");
|
||||
if (me.id === 'random-password-update-method-update') handler.set_random_password_update_method("UPDATE");
|
||||
if (me.id === 'random-password-update-method-disable') handler.set_random_password_update_method("DISABLE");
|
||||
this.toggleMenuState();
|
||||
passwordArea.update();
|
||||
}
|
||||
}
|
||||
|
||||
var passwordArea;
|
||||
class PasswordArea: Reactor.Component {
|
||||
function this() {
|
||||
passwordArea = this;
|
||||
}
|
||||
|
||||
function render() {
|
||||
var onetime_enabled = handler.is_onetime_password_enabled();
|
||||
|
||||
return
|
||||
<div>
|
||||
<div>{translate(onetime_enabled ? 'Onetime Password' : 'Password')}</div>
|
||||
<div .password style="flow:horizontal">
|
||||
{this.renderPop()}
|
||||
<PasswordEyeArea />
|
||||
{svg_edit}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
event click $(svg#edit) (_, me) {
|
||||
var menu = $(menu#edit-password-context);
|
||||
me.popup(menu);
|
||||
function renderPop() {
|
||||
var security_enabled = handler.is_security_password_enabled();
|
||||
var random_enabled = handler.is_random_password_enabled();
|
||||
var onetime_enabled = handler.is_onetime_password_enabled();
|
||||
var onetime_activated = handler.is_onetime_password_activated();
|
||||
|
||||
return <popup><menu.context #edit-password-context>
|
||||
<li #enable-onetime-password disabled={ random_enabled ? "false" : "true" }>{translate(onetime_enabled ? "Disable onetime password" : "Enable onetime password")}</li>
|
||||
<li #activate-onetime-password disabled={ !random_enabled || !onetime_enabled || onetime_activated ? "true" : "false" }>{translate('Activate onetime password')}</li>
|
||||
<div .separator />
|
||||
<VerificationMethodMenu />
|
||||
<div .separator />
|
||||
<li #set-password disabled={ security_enabled ? "false" : "true" }>{translate('Set security password')}</li>
|
||||
<div .separator />
|
||||
<RandomPasswordUpdateMethodMenu />
|
||||
</menu></popup>;
|
||||
}
|
||||
|
||||
event click $(li#refresh-password) {
|
||||
handler.update_password("");
|
||||
this.update();
|
||||
event click $(svg#edit) (_, me) {
|
||||
randomPasswordUpdateMethodMenu.update({show: true });
|
||||
verificationMethodMenu.update({show: true });
|
||||
var menu = $(menu#edit-password-context);
|
||||
me.popup(menu);
|
||||
}
|
||||
|
||||
event click $(li#set-password) {
|
||||
@ -862,12 +965,36 @@ class Password: Reactor.Component {
|
||||
if (p0 != p1) {
|
||||
return translate("The confirmation is not identical.");
|
||||
}
|
||||
handler.update_password(p0);
|
||||
handler.set_security_password(p0);
|
||||
me.update();
|
||||
});
|
||||
}
|
||||
|
||||
event click $(li#enable-onetime-password) {
|
||||
var onetime_enabled = handler.is_onetime_password_enabled();
|
||||
handler.set_onetime_password_enabled(!onetime_enabled);
|
||||
passwordArea.update();
|
||||
}
|
||||
|
||||
event click $(li#activate-onetime-password) {
|
||||
handler.set_onetime_password_activated(true);
|
||||
passwordArea.update();
|
||||
}
|
||||
}
|
||||
|
||||
var last_password_description = "";
|
||||
function updatePasswordArea() {
|
||||
self.timer(1s, function() {
|
||||
var description = handler.password_description();
|
||||
if (last_password_description != description) {
|
||||
last_password_description = description
|
||||
passwordArea.update();
|
||||
}
|
||||
updatePasswordArea();
|
||||
});
|
||||
}
|
||||
updatePasswordArea();
|
||||
|
||||
class ID: Reactor.Component {
|
||||
function render() {
|
||||
return <input type="text" #remote_id .outline-focus novalue={translate("Enter Remote ID")} maxlength="21"
|
||||
|
@ -1633,6 +1633,11 @@ impl Remote {
|
||||
// log::info!("new msg from ui, {}",data);
|
||||
match data {
|
||||
Data::Close => {
|
||||
let mut misc = Misc::new();
|
||||
misc.set_close_reason("".to_owned());
|
||||
let mut msg = Message::new();
|
||||
msg.set_misc(misc);
|
||||
allow_err!(peer.send(&msg).await);
|
||||
return false;
|
||||
}
|
||||
Data::Login((password, remember)) => {
|
||||
|
Loading…
Reference in New Issue
Block a user