commit
bdc5cded22
@ -500,7 +500,6 @@ abstract class BasePeerCard extends StatelessWidget {
|
|||||||
return await _isForceAlwaysRelay(id);
|
return await _isForceAlwaysRelay(id);
|
||||||
},
|
},
|
||||||
setter: (bool v) async {
|
setter: (bool v) async {
|
||||||
gFFI.abModel.setPeerForceAlwaysRelay(id, v);
|
|
||||||
await bind.mainSetPeerOption(
|
await bind.mainSetPeerOption(
|
||||||
id: id, key: option, value: bool2option(option, v));
|
id: id, key: option, value: bool2option(option, v));
|
||||||
},
|
},
|
||||||
@ -671,7 +670,6 @@ abstract class BasePeerCard extends StatelessWidget {
|
|||||||
isInProgress.value = true;
|
isInProgress.value = true;
|
||||||
String name = controller.text.trim();
|
String name = controller.text.trim();
|
||||||
await bind.mainSetPeerAlias(id: id, alias: name);
|
await bind.mainSetPeerAlias(id: id, alias: name);
|
||||||
gFFI.abModel.setPeerAlias(id, name);
|
|
||||||
_update();
|
_update();
|
||||||
close();
|
close();
|
||||||
isInProgress.value = false;
|
isInProgress.value = false;
|
||||||
@ -965,16 +963,6 @@ class AddressBookPeerCard extends BasePeerCard {
|
|||||||
return menuItems;
|
return menuItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
@protected
|
|
||||||
@override
|
|
||||||
Future<bool> _isForceAlwaysRelay(String id) async =>
|
|
||||||
gFFI.abModel.find(id)?.forceAlwaysRelay ?? false;
|
|
||||||
|
|
||||||
@protected
|
|
||||||
@override
|
|
||||||
Future<String> _getAlias(String id) async =>
|
|
||||||
gFFI.abModel.find(id)?.alias ?? '';
|
|
||||||
|
|
||||||
@protected
|
@protected
|
||||||
@override
|
@override
|
||||||
void _update() => gFFI.abModel.pullAb();
|
void _update() => gFFI.abModel.pullAb();
|
||||||
@ -1122,7 +1110,6 @@ void _rdpDialog(String id) async {
|
|||||||
id: id, key: 'rdp_username', value: username);
|
id: id, key: 'rdp_username', value: username);
|
||||||
await bind.mainSetPeerOption(
|
await bind.mainSetPeerOption(
|
||||||
id: id, key: 'rdp_password', value: password);
|
id: id, key: 'rdp_password', value: password);
|
||||||
gFFI.abModel.setRdp(id, port, username);
|
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
@ -31,12 +32,21 @@ class AbModel {
|
|||||||
final selectedTags = List<String>.empty(growable: true).obs;
|
final selectedTags = List<String>.empty(growable: true).obs;
|
||||||
var initialized = false;
|
var initialized = false;
|
||||||
var licensedDevices = 0;
|
var licensedDevices = 0;
|
||||||
|
var sync_all_from_recent = true;
|
||||||
|
var _timerCounter = 0;
|
||||||
|
|
||||||
WeakReference<FFI> parent;
|
WeakReference<FFI> parent;
|
||||||
|
|
||||||
AbModel(this.parent);
|
AbModel(this.parent) {
|
||||||
|
if (desktopType == DesktopType.main) {
|
||||||
|
Timer.periodic(Duration(milliseconds: 500), (timer) async {
|
||||||
|
if (_timerCounter++ % 6 == 0) syncFromRecent();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> pullAb({force = true, quiet = false}) async {
|
Future<void> pullAb({force = true, quiet = false}) async {
|
||||||
|
debugPrint("pullAb, force:$force, quite:$quiet");
|
||||||
if (gFFI.userModel.userName.isEmpty) return;
|
if (gFFI.userModel.userName.isEmpty) return;
|
||||||
if (abLoading.value) return;
|
if (abLoading.value) return;
|
||||||
if (!force && initialized) return;
|
if (!force && initialized) return;
|
||||||
@ -75,18 +85,24 @@ class AbModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
reset();
|
||||||
abError.value = err.toString();
|
abError.value = err.toString();
|
||||||
} finally {
|
} finally {
|
||||||
abLoading.value = false;
|
abLoading.value = false;
|
||||||
initialized = true;
|
initialized = true;
|
||||||
|
sync_all_from_recent = true;
|
||||||
|
_timerCounter = 0;
|
||||||
|
save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> reset() async {
|
Future<void> reset() async {
|
||||||
|
abError.value = '';
|
||||||
await bind.mainSetLocalOption(key: "selected-tags", value: '');
|
await bind.mainSetLocalOption(key: "selected-tags", value: '');
|
||||||
tags.clear();
|
tags.clear();
|
||||||
peers.clear();
|
peers.clear();
|
||||||
initialized = false;
|
initialized = false;
|
||||||
|
await bind.mainClearAb();
|
||||||
}
|
}
|
||||||
|
|
||||||
void addId(String id, String alias, List<dynamic> tags) {
|
void addId(String id, String alias, List<dynamic> tags) {
|
||||||
@ -131,10 +147,11 @@ class AbModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> pushAb() async {
|
Future<void> pushAb() async {
|
||||||
|
debugPrint("pushAb");
|
||||||
final api = "${await bind.mainGetApiServer()}/api/ab";
|
final api = "${await bind.mainGetApiServer()}/api/ab";
|
||||||
var authHeaders = getHttpHeaders();
|
var authHeaders = getHttpHeaders();
|
||||||
authHeaders['Content-Type'] = "application/json";
|
authHeaders['Content-Type'] = "application/json";
|
||||||
final peersJsonData = peers.map((e) => e.toJson()).toList();
|
final peersJsonData = peers.map((e) => e.toAbUploadJson()).toList();
|
||||||
final body = jsonEncode({
|
final body = jsonEncode({
|
||||||
"data": jsonEncode({"tags": tags, "peers": peersJsonData})
|
"data": jsonEncode({"tags": tags, "peers": peersJsonData})
|
||||||
});
|
});
|
||||||
@ -149,10 +166,14 @@ class AbModel {
|
|||||||
request.headers.addAll(authHeaders);
|
request.headers.addAll(authHeaders);
|
||||||
try {
|
try {
|
||||||
await http.Client().send(request);
|
await http.Client().send(request);
|
||||||
await pullAb(quiet: true);
|
// await pullAb(quiet: true);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
BotToast.showText(contentColor: Colors.red, text: e.toString());
|
BotToast.showText(contentColor: Colors.red, text: e.toString());
|
||||||
} finally {}
|
} finally {
|
||||||
|
sync_all_from_recent = true;
|
||||||
|
_timerCounter = 0;
|
||||||
|
save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Peer? find(String id) {
|
Peer? find(String id) {
|
||||||
@ -197,28 +218,111 @@ class AbModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setPeerAlias(String id, String value) async {
|
void syncFromRecent() async {
|
||||||
final it = peers.where((p0) => p0.id == id);
|
Peer merge(Peer r, Peer p) {
|
||||||
if (it.isNotEmpty) {
|
return Peer(
|
||||||
it.first.alias = value;
|
id: p.id,
|
||||||
await pushAb();
|
hash: r.hash.isEmpty ? p.hash : r.hash,
|
||||||
|
username: r.username.isEmpty ? p.username : r.username,
|
||||||
|
hostname: r.hostname.isEmpty ? p.hostname : r.hostname,
|
||||||
|
platform: r.platform.isEmpty ? p.platform : r.platform,
|
||||||
|
alias: r.alias,
|
||||||
|
tags: p.tags,
|
||||||
|
forceAlwaysRelay: r.forceAlwaysRelay,
|
||||||
|
rdpPort: r.rdpPort,
|
||||||
|
rdpUsername: r.rdpUsername);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shouldSync(Peer a, Peer b) {
|
||||||
|
return a.hash != b.hash ||
|
||||||
|
a.username != b.username ||
|
||||||
|
a.platform != b.platform ||
|
||||||
|
a.hostname != b.hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<Peer>> getRecentPeers() async {
|
||||||
|
try {
|
||||||
|
if (peers.isEmpty) [];
|
||||||
|
List<String> filteredPeerIDs;
|
||||||
|
if (sync_all_from_recent) {
|
||||||
|
sync_all_from_recent = false;
|
||||||
|
filteredPeerIDs = peers.map((e) => e.id).toList();
|
||||||
|
} else {
|
||||||
|
final new_stored_str = await bind.mainGetNewStoredPeers();
|
||||||
|
if (new_stored_str.isEmpty) return [];
|
||||||
|
List<String> new_stores =
|
||||||
|
(jsonDecode(new_stored_str) as List<dynamic>)
|
||||||
|
.map((e) => e.toString())
|
||||||
|
.toList();
|
||||||
|
final abPeerIds = peers.map((e) => e.id).toList();
|
||||||
|
filteredPeerIDs =
|
||||||
|
new_stores.where((e) => abPeerIds.contains(e)).toList();
|
||||||
|
}
|
||||||
|
if (filteredPeerIDs.isEmpty) return [];
|
||||||
|
final loadStr = await bind.mainLoadRecentPeersForAb(
|
||||||
|
filter: jsonEncode(filteredPeerIDs));
|
||||||
|
if (loadStr.isEmpty) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
List<dynamic> mapPeers = jsonDecode(loadStr);
|
||||||
|
List<Peer> recents = List.empty(growable: true);
|
||||||
|
for (var m in mapPeers) {
|
||||||
|
if (m is Map<String, dynamic>) {
|
||||||
|
recents.add(Peer.fromJson(m));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return recents;
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('getRecentPeers:$e');
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!shouldSyncAb()) return;
|
||||||
|
final oldPeers = peers.toList();
|
||||||
|
final recents = await getRecentPeers();
|
||||||
|
if (recents.isEmpty) return;
|
||||||
|
for (var i = 0; i < peers.length; i++) {
|
||||||
|
var p = peers[i];
|
||||||
|
var r = recents.firstWhereOrNull((r) => p.id == r.id);
|
||||||
|
if (r != null) {
|
||||||
|
peers[i] = merge(r, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool changed = false;
|
||||||
|
for (var i = 0; i < peers.length; i++) {
|
||||||
|
final o = oldPeers[i];
|
||||||
|
final p = peers[i];
|
||||||
|
if (shouldSync(o, p)) {
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Be careful with loop calls
|
||||||
|
if (changed) {
|
||||||
|
pushAb();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('syncFromRecent:$e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setPeerForceAlwaysRelay(String id, bool value) async {
|
save() {
|
||||||
final it = peers.where((p0) => p0.id == id);
|
try {
|
||||||
if (it.isNotEmpty) {
|
final infos = peers
|
||||||
it.first.forceAlwaysRelay = value;
|
.map((e) => (<String, dynamic>{
|
||||||
await pushAb();
|
"id": e.id,
|
||||||
}
|
"hash": e.hash,
|
||||||
}
|
}))
|
||||||
|
.toList();
|
||||||
Future<void> setRdp(String id, String port, String username) async {
|
final m = <String, dynamic>{
|
||||||
final it = peers.where((p0) => p0.id == id);
|
"access_token": bind.mainGetLocalOption(key: 'access_token'),
|
||||||
if (it.isNotEmpty) {
|
"peers": infos,
|
||||||
it.first.rdpPort = port;
|
};
|
||||||
it.first.rdpUsername = username;
|
bind.mainSaveAb(json: jsonEncode(m));
|
||||||
await pushAb();
|
} catch (e) {
|
||||||
|
debugPrint('ab save:$e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import 'platform_model.dart';
|
|||||||
|
|
||||||
class Peer {
|
class Peer {
|
||||||
final String id;
|
final String id;
|
||||||
|
String hash;
|
||||||
final String username;
|
final String username;
|
||||||
final String hostname;
|
final String hostname;
|
||||||
final String platform;
|
final String platform;
|
||||||
@ -23,6 +24,7 @@ class Peer {
|
|||||||
|
|
||||||
Peer.fromJson(Map<String, dynamic> json)
|
Peer.fromJson(Map<String, dynamic> json)
|
||||||
: id = json['id'] ?? '',
|
: id = json['id'] ?? '',
|
||||||
|
hash = json['hash'] ?? '',
|
||||||
username = json['username'] ?? '',
|
username = json['username'] ?? '',
|
||||||
hostname = json['hostname'] ?? '',
|
hostname = json['hostname'] ?? '',
|
||||||
platform = json['platform'] ?? '',
|
platform = json['platform'] ?? '',
|
||||||
@ -35,6 +37,7 @@ class Peer {
|
|||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
return <String, dynamic>{
|
return <String, dynamic>{
|
||||||
"id": id,
|
"id": id,
|
||||||
|
"hash": hash,
|
||||||
"username": username,
|
"username": username,
|
||||||
"hostname": hostname,
|
"hostname": hostname,
|
||||||
"platform": platform,
|
"platform": platform,
|
||||||
@ -46,8 +49,20 @@ class Peer {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toAbUploadJson() {
|
||||||
|
return <String, dynamic>{
|
||||||
|
"id": id,
|
||||||
|
"hash": hash,
|
||||||
|
"username": username,
|
||||||
|
"hostname": hostname,
|
||||||
|
"platform": platform,
|
||||||
|
"tags": tags,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Peer({
|
Peer({
|
||||||
required this.id,
|
required this.id,
|
||||||
|
required this.hash,
|
||||||
required this.username,
|
required this.username,
|
||||||
required this.hostname,
|
required this.hostname,
|
||||||
required this.platform,
|
required this.platform,
|
||||||
@ -61,6 +76,7 @@ class Peer {
|
|||||||
Peer.loading()
|
Peer.loading()
|
||||||
: this(
|
: this(
|
||||||
id: '...',
|
id: '...',
|
||||||
|
hash: '',
|
||||||
username: '...',
|
username: '...',
|
||||||
hostname: '...',
|
hostname: '...',
|
||||||
platform: '...',
|
platform: '...',
|
||||||
|
@ -24,6 +24,7 @@ directories-next = "2.0"
|
|||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
|
serde_json = "1.0"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
confy = { git = "https://github.com/open-trade/confy" }
|
confy = { git = "https://github.com/open-trade/confy" }
|
||||||
dirs-next = "2.0"
|
dirs-next = "2.0"
|
||||||
@ -56,5 +57,3 @@ winapi = { version = "0.3", features = ["winuser"] }
|
|||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
[target.'cfg(target_os = "macos")'.dependencies]
|
||||||
osascript = "0.3"
|
osascript = "0.3"
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
serde_json = "1.0"
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::{HashMap, HashSet},
|
||||||
fs,
|
fs,
|
||||||
|
io::{Read, Write},
|
||||||
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
@ -13,10 +14,12 @@ use rand::Rng;
|
|||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde as de;
|
use serde as de;
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use serde_json;
|
||||||
use sodiumoxide::base64;
|
use sodiumoxide::base64;
|
||||||
use sodiumoxide::crypto::sign;
|
use sodiumoxide::crypto::sign;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
compress::{compress, decompress},
|
||||||
log,
|
log,
|
||||||
password_security::{
|
password_security::{
|
||||||
decrypt_str_or_original, decrypt_vec_or_original, encrypt_str_or_original,
|
decrypt_str_or_original, decrypt_vec_or_original, encrypt_str_or_original,
|
||||||
@ -31,6 +34,7 @@ pub const REG_INTERVAL: i64 = 12_000;
|
|||||||
pub const COMPRESS_LEVEL: i32 = 3;
|
pub const COMPRESS_LEVEL: i32 = 3;
|
||||||
const SERIAL: i32 = 3;
|
const SERIAL: i32 = 3;
|
||||||
const PASSWORD_ENC_VERSION: &str = "00";
|
const PASSWORD_ENC_VERSION: &str = "00";
|
||||||
|
const ENCRYPT_MAX_LEN: usize = 128;
|
||||||
|
|
||||||
// config2 options
|
// config2 options
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
@ -57,6 +61,7 @@ lazy_static::lazy_static! {
|
|||||||
pub static ref APP_NAME: Arc<RwLock<String>> = Arc::new(RwLock::new("RustDesk".to_owned()));
|
pub static ref APP_NAME: Arc<RwLock<String>> = Arc::new(RwLock::new("RustDesk".to_owned()));
|
||||||
static ref KEY_PAIR: Arc<Mutex<Option<KeyPair>>> = Default::default();
|
static ref KEY_PAIR: Arc<Mutex<Option<KeyPair>>> = Default::default();
|
||||||
static ref USER_DEFAULT_CONFIG: Arc<RwLock<(UserDefaultConfig, Instant)>> = Arc::new(RwLock::new((UserDefaultConfig::load(), Instant::now())));
|
static ref USER_DEFAULT_CONFIG: Arc<RwLock<(UserDefaultConfig, Instant)>> = Arc::new(RwLock::new((UserDefaultConfig::load(), Instant::now())));
|
||||||
|
pub static ref NEW_STORED_PEER_CONFIG: Arc<Mutex<HashSet<String>>> = Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
@ -376,7 +381,8 @@ impl Config2 {
|
|||||||
fn store(&self) {
|
fn store(&self) {
|
||||||
let mut config = self.clone();
|
let mut config = self.clone();
|
||||||
if let Some(mut socks) = config.socks {
|
if let Some(mut socks) = config.socks {
|
||||||
socks.password = encrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION);
|
socks.password =
|
||||||
|
encrypt_str_or_original(&socks.password, PASSWORD_ENC_VERSION, ENCRYPT_MAX_LEN);
|
||||||
config.socks = Some(socks);
|
config.socks = Some(socks);
|
||||||
}
|
}
|
||||||
Config::store_(&config, "2");
|
Config::store_(&config, "2");
|
||||||
@ -485,8 +491,9 @@ impl Config {
|
|||||||
|
|
||||||
fn store(&self) {
|
fn store(&self) {
|
||||||
let mut config = self.clone();
|
let mut config = self.clone();
|
||||||
config.password = encrypt_str_or_original(&config.password, PASSWORD_ENC_VERSION);
|
config.password =
|
||||||
config.enc_id = encrypt_str_or_original(&config.id, PASSWORD_ENC_VERSION);
|
encrypt_str_or_original(&config.password, PASSWORD_ENC_VERSION, ENCRYPT_MAX_LEN);
|
||||||
|
config.enc_id = encrypt_str_or_original(&config.id, PASSWORD_ENC_VERSION, ENCRYPT_MAX_LEN);
|
||||||
config.id = "".to_owned();
|
config.id = "".to_owned();
|
||||||
Config::store_(&config, "");
|
Config::store_(&config, "");
|
||||||
}
|
}
|
||||||
@ -980,15 +987,17 @@ impl PeerConfig {
|
|||||||
pub fn store(&self, id: &str) {
|
pub fn store(&self, id: &str) {
|
||||||
let _lock = CONFIG.read().unwrap();
|
let _lock = CONFIG.read().unwrap();
|
||||||
let mut config = self.clone();
|
let mut config = self.clone();
|
||||||
config.password = encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION);
|
config.password =
|
||||||
|
encrypt_vec_or_original(&config.password, PASSWORD_ENC_VERSION, ENCRYPT_MAX_LEN);
|
||||||
for opt in ["rdp_password", "os-username", "os-password"] {
|
for opt in ["rdp_password", "os-username", "os-password"] {
|
||||||
if let Some(v) = config.options.get_mut(opt) {
|
if let Some(v) = config.options.get_mut(opt) {
|
||||||
*v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION)
|
*v = encrypt_str_or_original(v, PASSWORD_ENC_VERSION, ENCRYPT_MAX_LEN)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Err(err) = store_path(Self::path(id), config) {
|
if let Err(err) = store_path(Self::path(id), config) {
|
||||||
log::error!("Failed to store config: {}", err);
|
log::error!("Failed to store config: {}", err);
|
||||||
}
|
}
|
||||||
|
NEW_STORED_PEER_CONFIG.lock().unwrap().insert(id.to_owned());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove(id: &str) {
|
pub fn remove(id: &str) {
|
||||||
@ -1014,7 +1023,7 @@ impl PeerConfig {
|
|||||||
Config::with_extension(Config::path(path))
|
Config::with_extension(Config::path(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn peers() -> Vec<(String, SystemTime, PeerConfig)> {
|
pub fn peers(id_filters: Option<Vec<String>>) -> Vec<(String, SystemTime, PeerConfig)> {
|
||||||
if let Ok(peers) = Config::path(PEERS).read_dir() {
|
if let Ok(peers) = Config::path(PEERS).read_dir() {
|
||||||
if let Ok(peers) = peers
|
if let Ok(peers) = peers
|
||||||
.map(|res| res.map(|e| e.path()))
|
.map(|res| res.map(|e| e.path()))
|
||||||
@ -1027,7 +1036,6 @@ impl PeerConfig {
|
|||||||
&& p.extension().map(|p| p.to_str().unwrap_or("")) == Some("toml")
|
&& p.extension().map(|p| p.to_str().unwrap_or("")) == Some("toml")
|
||||||
})
|
})
|
||||||
.map(|p| {
|
.map(|p| {
|
||||||
let t = crate::get_modified_time(p);
|
|
||||||
let id = p
|
let id = p
|
||||||
.file_stem()
|
.file_stem()
|
||||||
.map(|p| p.to_str().unwrap_or(""))
|
.map(|p| p.to_str().unwrap_or(""))
|
||||||
@ -1041,12 +1049,21 @@ impl PeerConfig {
|
|||||||
} else {
|
} else {
|
||||||
id
|
id
|
||||||
};
|
};
|
||||||
|
(id_decoded_string, p)
|
||||||
let c = PeerConfig::load(&id_decoded_string);
|
})
|
||||||
|
.filter(|(id, _)| {
|
||||||
|
let Some(filters) = &id_filters else {
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
filters.contains(id)
|
||||||
|
})
|
||||||
|
.map(|(id, p)| {
|
||||||
|
let t = crate::get_modified_time(p);
|
||||||
|
let c = PeerConfig::load(&id);
|
||||||
if c.info.platform.is_empty() {
|
if c.info.platform.is_empty() {
|
||||||
fs::remove_file(p).ok();
|
fs::remove_file(p).ok();
|
||||||
}
|
}
|
||||||
(id_decoded_string, t, c)
|
(id, t, c)
|
||||||
})
|
})
|
||||||
.filter(|p| !p.2.info.platform.is_empty())
|
.filter(|p| !p.2.info.platform.is_empty())
|
||||||
.collect();
|
.collect();
|
||||||
@ -1445,6 +1462,74 @@ impl UserDefaultConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct AbPeer {
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
deserialize_with = "deserialize_string",
|
||||||
|
skip_serializing_if = "String::is_empty"
|
||||||
|
)]
|
||||||
|
pub id: String,
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
deserialize_with = "deserialize_string",
|
||||||
|
skip_serializing_if = "String::is_empty"
|
||||||
|
)]
|
||||||
|
pub hash: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct Ab {
|
||||||
|
#[serde(
|
||||||
|
default,
|
||||||
|
deserialize_with = "deserialize_string",
|
||||||
|
skip_serializing_if = "String::is_empty"
|
||||||
|
)]
|
||||||
|
pub access_token: String,
|
||||||
|
#[serde(default, deserialize_with = "deserialize_vec_abpeer")]
|
||||||
|
pub peers: Vec<AbPeer>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ab {
|
||||||
|
fn path() -> PathBuf {
|
||||||
|
let filename = format!("{}_ab", APP_NAME.read().unwrap().clone());
|
||||||
|
Config::path(filename)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn store(json: String) {
|
||||||
|
if let Ok(mut file) = std::fs::File::create(Self::path()) {
|
||||||
|
let data = compress(json.as_bytes());
|
||||||
|
let max_len = 32 * 1024 * 1024;
|
||||||
|
if data.len() > max_len {
|
||||||
|
// not store original
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let data = encrypt_vec_or_original(&data, PASSWORD_ENC_VERSION, max_len);
|
||||||
|
file.write_all(&data).ok();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load() -> Ab {
|
||||||
|
if let Ok(mut file) = std::fs::File::open(Self::path()) {
|
||||||
|
let mut data = vec![];
|
||||||
|
if file.read_to_end(&mut data).is_ok() {
|
||||||
|
let (data, succ, _) = decrypt_vec_or_original(&data, PASSWORD_ENC_VERSION);
|
||||||
|
if succ {
|
||||||
|
let data = decompress(&data);
|
||||||
|
if let Ok(ab) = serde_json::from_str::<Ab>(&String::from_utf8_lossy(&data)) {
|
||||||
|
return ab;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ab::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove() {
|
||||||
|
std::fs::remove_file(Self::path()).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// use default value when field type is wrong
|
// use default value when field type is wrong
|
||||||
macro_rules! deserialize_default {
|
macro_rules! deserialize_default {
|
||||||
($func_name:ident, $return_type:ty) => {
|
($func_name:ident, $return_type:ty) => {
|
||||||
@ -1464,6 +1549,7 @@ deserialize_default!(deserialize_vec_u8, Vec<u8>);
|
|||||||
deserialize_default!(deserialize_vec_string, Vec<String>);
|
deserialize_default!(deserialize_vec_string, Vec<String>);
|
||||||
deserialize_default!(deserialize_vec_i32_string_i32, Vec<(i32, String, i32)>);
|
deserialize_default!(deserialize_vec_i32_string_i32, Vec<(i32, String, i32)>);
|
||||||
deserialize_default!(deserialize_vec_discoverypeer, Vec<DiscoveryPeer>);
|
deserialize_default!(deserialize_vec_discoverypeer, Vec<DiscoveryPeer>);
|
||||||
|
deserialize_default!(deserialize_vec_abpeer, Vec<AbPeer>);
|
||||||
deserialize_default!(deserialize_keypair, KeyPair);
|
deserialize_default!(deserialize_keypair, KeyPair);
|
||||||
deserialize_default!(deserialize_size, Size);
|
deserialize_default!(deserialize_size, Size);
|
||||||
deserialize_default!(deserialize_hashmap_string_string, HashMap<String, String>);
|
deserialize_default!(deserialize_hashmap_string_string, HashMap<String, String>);
|
||||||
|
@ -84,13 +84,13 @@ pub fn hide_cm() -> bool {
|
|||||||
|
|
||||||
const VERSION_LEN: usize = 2;
|
const VERSION_LEN: usize = 2;
|
||||||
|
|
||||||
pub fn encrypt_str_or_original(s: &str, version: &str) -> String {
|
pub fn encrypt_str_or_original(s: &str, version: &str, max_len: usize) -> String {
|
||||||
if decrypt_str_or_original(s, version).1 {
|
if decrypt_str_or_original(s, version).1 {
|
||||||
log::error!("Duplicate encryption!");
|
log::error!("Duplicate encryption!");
|
||||||
return s.to_owned();
|
return s.to_owned();
|
||||||
}
|
}
|
||||||
if version == "00" {
|
if version == "00" {
|
||||||
if let Ok(s) = encrypt(s.as_bytes()) {
|
if let Ok(s) = encrypt(s.as_bytes(), max_len) {
|
||||||
return version.to_owned() + &s;
|
return version.to_owned() + &s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,13 +117,13 @@ pub fn decrypt_str_or_original(s: &str, current_version: &str) -> (String, bool,
|
|||||||
(s.to_owned(), false, !s.is_empty())
|
(s.to_owned(), false, !s.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encrypt_vec_or_original(v: &[u8], version: &str) -> Vec<u8> {
|
pub fn encrypt_vec_or_original(v: &[u8], version: &str, max_len: usize) -> Vec<u8> {
|
||||||
if decrypt_vec_or_original(v, version).1 {
|
if decrypt_vec_or_original(v, version).1 {
|
||||||
log::error!("Duplicate encryption!");
|
log::error!("Duplicate encryption!");
|
||||||
return v.to_owned();
|
return v.to_owned();
|
||||||
}
|
}
|
||||||
if version == "00" {
|
if version == "00" {
|
||||||
if let Ok(s) = encrypt(v) {
|
if let Ok(s) = encrypt(v, max_len) {
|
||||||
let mut version = version.to_owned().into_bytes();
|
let mut version = version.to_owned().into_bytes();
|
||||||
version.append(&mut s.into_bytes());
|
version.append(&mut s.into_bytes());
|
||||||
return version;
|
return version;
|
||||||
@ -148,8 +148,8 @@ pub fn decrypt_vec_or_original(v: &[u8], current_version: &str) -> (Vec<u8>, boo
|
|||||||
(v.to_owned(), false, !v.is_empty())
|
(v.to_owned(), false, !v.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encrypt(v: &[u8]) -> Result<String, ()> {
|
fn encrypt(v: &[u8], max_len: usize) -> Result<String, ()> {
|
||||||
if !v.is_empty() && v.len() <= 128 {
|
if !v.is_empty() && v.len() <= max_len {
|
||||||
symmetric_crypt(v, true).map(|v| base64::encode(v, base64::Variant::Original))
|
symmetric_crypt(v, true).map(|v| base64::encode(v, base64::Variant::Original))
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
@ -185,12 +185,15 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test() {
|
fn test() {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
let version = "00";
|
let version = "00";
|
||||||
|
let max_len = 128;
|
||||||
|
|
||||||
println!("test str");
|
println!("test str");
|
||||||
let data = "Hello World";
|
let data = "Hello World";
|
||||||
let encrypted = encrypt_str_or_original(data, version);
|
let encrypted = encrypt_str_or_original(data, version, max_len);
|
||||||
let (decrypted, succ, store) = decrypt_str_or_original(&encrypted, version);
|
let (decrypted, succ, store) = decrypt_str_or_original(&encrypted, version);
|
||||||
println!("data: {data}");
|
println!("data: {data}");
|
||||||
println!("encrypted: {encrypted}");
|
println!("encrypted: {encrypted}");
|
||||||
@ -202,11 +205,14 @@ mod test {
|
|||||||
let (_, _, store) = decrypt_str_or_original(&encrypted, "99");
|
let (_, _, store) = decrypt_str_or_original(&encrypted, "99");
|
||||||
assert!(store);
|
assert!(store);
|
||||||
assert!(!decrypt_str_or_original(&decrypted, version).1);
|
assert!(!decrypt_str_or_original(&decrypted, version).1);
|
||||||
assert_eq!(encrypt_str_or_original(&encrypted, version), encrypted);
|
assert_eq!(
|
||||||
|
encrypt_str_or_original(&encrypted, version, max_len),
|
||||||
|
encrypted
|
||||||
|
);
|
||||||
|
|
||||||
println!("test vec");
|
println!("test vec");
|
||||||
let data: Vec<u8> = vec![1, 2, 3, 4, 5, 6];
|
let data: Vec<u8> = vec![1, 2, 3, 4, 5, 6];
|
||||||
let encrypted = encrypt_vec_or_original(&data, version);
|
let encrypted = encrypt_vec_or_original(&data, version, max_len);
|
||||||
let (decrypted, succ, store) = decrypt_vec_or_original(&encrypted, version);
|
let (decrypted, succ, store) = decrypt_vec_or_original(&encrypted, version);
|
||||||
println!("data: {data:?}");
|
println!("data: {data:?}");
|
||||||
println!("encrypted: {encrypted:?}");
|
println!("encrypted: {encrypted:?}");
|
||||||
@ -218,7 +224,10 @@ mod test {
|
|||||||
let (_, _, store) = decrypt_vec_or_original(&encrypted, "99");
|
let (_, _, store) = decrypt_vec_or_original(&encrypted, "99");
|
||||||
assert!(store);
|
assert!(store);
|
||||||
assert!(!decrypt_vec_or_original(&decrypted, version).1);
|
assert!(!decrypt_vec_or_original(&decrypted, version).1);
|
||||||
assert_eq!(encrypt_vec_or_original(&encrypted, version), encrypted);
|
assert_eq!(
|
||||||
|
encrypt_vec_or_original(&encrypted, version, max_len),
|
||||||
|
encrypted
|
||||||
|
);
|
||||||
|
|
||||||
println!("test original");
|
println!("test original");
|
||||||
let data = version.to_string() + "Hello World";
|
let data = version.to_string() + "Hello World";
|
||||||
@ -238,5 +247,38 @@ mod test {
|
|||||||
let (_, succ, store) = decrypt_vec_or_original(&[], version);
|
let (_, succ, store) = decrypt_vec_or_original(&[], version);
|
||||||
assert!(!store);
|
assert!(!store);
|
||||||
assert!(!succ);
|
assert!(!succ);
|
||||||
|
|
||||||
|
println!("test speed");
|
||||||
|
let test_speed = |len: usize, name: &str| {
|
||||||
|
let mut data: Vec<u8> = vec![];
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
for _ in 0..len {
|
||||||
|
data.push(rng.gen_range(0..255));
|
||||||
|
}
|
||||||
|
let start: Instant = Instant::now();
|
||||||
|
let encrypted = encrypt_vec_or_original(&data, version, len);
|
||||||
|
assert_ne!(data, decrypted);
|
||||||
|
let t1 = start.elapsed();
|
||||||
|
let start = Instant::now();
|
||||||
|
let (decrypted, _, _) = decrypt_vec_or_original(&encrypted, version);
|
||||||
|
let t2 = start.elapsed();
|
||||||
|
assert_eq!(data, decrypted);
|
||||||
|
println!("{name}");
|
||||||
|
println!("encrypt:{:?}, decrypt:{:?}", t1, t2);
|
||||||
|
|
||||||
|
let start: Instant = Instant::now();
|
||||||
|
let encrypted = base64::encode(&data, base64::Variant::Original);
|
||||||
|
let t1 = start.elapsed();
|
||||||
|
let start = Instant::now();
|
||||||
|
let decrypted = base64::decode(&encrypted, base64::Variant::Original).unwrap();
|
||||||
|
let t2 = start.elapsed();
|
||||||
|
assert_eq!(data, decrypted);
|
||||||
|
println!("base64, encrypt:{:?}, decrypt:{:?}", t1, t2,);
|
||||||
|
};
|
||||||
|
test_speed(128, "128");
|
||||||
|
test_speed(1024, "1k");
|
||||||
|
test_speed(1024 * 1024, "1M");
|
||||||
|
test_speed(10 * 1024 * 1024, "10M");
|
||||||
|
test_speed(100 * 1024 * 1024, "100M");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,8 @@ use hbb_common::{
|
|||||||
anyhow::{anyhow, Context},
|
anyhow::{anyhow, Context},
|
||||||
bail,
|
bail,
|
||||||
config::{
|
config::{
|
||||||
Config, PeerConfig, PeerInfoSerde, Resolution, CONNECT_TIMEOUT, READ_TIMEOUT, RELAY_PORT,
|
Config, LocalConfig, PeerConfig, PeerInfoSerde, Resolution, CONNECT_TIMEOUT, READ_TIMEOUT,
|
||||||
|
RELAY_PORT,
|
||||||
},
|
},
|
||||||
get_version_number, log,
|
get_version_number, log,
|
||||||
message_proto::{option_message::BoolOption, *},
|
message_proto::{option_message::BoolOption, *},
|
||||||
@ -42,6 +43,7 @@ use hbb_common::{
|
|||||||
rand,
|
rand,
|
||||||
rendezvous_proto::*,
|
rendezvous_proto::*,
|
||||||
socket_client,
|
socket_client,
|
||||||
|
sodiumoxide::base64,
|
||||||
sodiumoxide::crypto::{box_, secretbox, sign},
|
sodiumoxide::crypto::{box_, secretbox, sign},
|
||||||
tcp::FramedStream,
|
tcp::FramedStream,
|
||||||
timeout,
|
timeout,
|
||||||
@ -2235,6 +2237,22 @@ pub async fn handle_hash(
|
|||||||
if password.is_empty() {
|
if password.is_empty() {
|
||||||
password = lc.read().unwrap().config.password.clone();
|
password = lc.read().unwrap().config.password.clone();
|
||||||
}
|
}
|
||||||
|
if password.is_empty() {
|
||||||
|
let access_token = LocalConfig::get_option("access_token");
|
||||||
|
let ab = hbb_common::config::Ab::load();
|
||||||
|
if !access_token.is_empty() && access_token == ab.access_token {
|
||||||
|
let id = lc.read().unwrap().id.clone();
|
||||||
|
if let Some(p) = ab
|
||||||
|
.peers
|
||||||
|
.iter()
|
||||||
|
.find_map(|p| if p.id == id { Some(p) } else { None })
|
||||||
|
{
|
||||||
|
if let Ok(hash) = base64::decode(p.hash.clone(), base64::Variant::Original) {
|
||||||
|
password = hash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
let password = if password.is_empty() {
|
let password = if password.is_empty() {
|
||||||
// login without password, the remote side can click accept
|
// login without password, the remote side can click accept
|
||||||
interface.msgbox("input-password", "Password Required", "", "");
|
interface.msgbox("input-password", "Password Required", "", "");
|
||||||
|
@ -618,7 +618,7 @@ pub fn main_show_option(_key: String) -> SyncReturn<bool> {
|
|||||||
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
#[cfg(all(target_os = "linux", feature = "linux_headless"))]
|
||||||
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
#[cfg(not(any(feature = "flatpak", feature = "appimage")))]
|
||||||
if _key.eq(config::CONFIG_OPTION_ALLOW_LINUX_HEADLESS) {
|
if _key.eq(config::CONFIG_OPTION_ALLOW_LINUX_HEADLESS) {
|
||||||
return SyncReturn(true)
|
return SyncReturn(true);
|
||||||
}
|
}
|
||||||
SyncReturn(false)
|
SyncReturn(false)
|
||||||
}
|
}
|
||||||
@ -777,6 +777,15 @@ pub fn main_set_peer_alias(id: String, alias: String) {
|
|||||||
set_peer_option(id, "alias".to_owned(), alias)
|
set_peer_option(id, "alias".to_owned(), alias)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn main_get_new_stored_peers() -> String {
|
||||||
|
let peers: Vec<String> = config::NEW_STORED_PEER_CONFIG
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.drain()
|
||||||
|
.collect();
|
||||||
|
serde_json::to_string(&peers).unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main_forget_password(id: String) {
|
pub fn main_forget_password(id: String) {
|
||||||
forget_password(id)
|
forget_password(id)
|
||||||
}
|
}
|
||||||
@ -787,7 +796,7 @@ pub fn main_peer_has_password(id: String) -> bool {
|
|||||||
|
|
||||||
pub fn main_load_recent_peers() {
|
pub fn main_load_recent_peers() {
|
||||||
if !config::APP_DIR.read().unwrap().is_empty() {
|
if !config::APP_DIR.read().unwrap().is_empty() {
|
||||||
let peers: Vec<HashMap<&str, String>> = PeerConfig::peers()
|
let peers: Vec<HashMap<&str, String>> = PeerConfig::peers(None)
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|(id, _, p)| peer_to_map(id, p))
|
.map(|(id, _, p)| peer_to_map(id, p))
|
||||||
.collect();
|
.collect();
|
||||||
@ -808,7 +817,7 @@ pub fn main_load_recent_peers() {
|
|||||||
|
|
||||||
pub fn main_load_recent_peers_sync() -> SyncReturn<String> {
|
pub fn main_load_recent_peers_sync() -> SyncReturn<String> {
|
||||||
if !config::APP_DIR.read().unwrap().is_empty() {
|
if !config::APP_DIR.read().unwrap().is_empty() {
|
||||||
let peers: Vec<HashMap<&str, String>> = PeerConfig::peers()
|
let peers: Vec<HashMap<&str, String>> = PeerConfig::peers(None)
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|(id, _, p)| peer_to_map(id, p))
|
.map(|(id, _, p)| peer_to_map(id, p))
|
||||||
.collect();
|
.collect();
|
||||||
@ -825,10 +834,22 @@ pub fn main_load_recent_peers_sync() -> SyncReturn<String> {
|
|||||||
SyncReturn("".to_string())
|
SyncReturn("".to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn main_load_recent_peers_for_ab(filter: String) -> String {
|
||||||
|
let id_filters = serde_json::from_str::<Vec<String>>(&filter).unwrap_or_default();
|
||||||
|
if !config::APP_DIR.read().unwrap().is_empty() {
|
||||||
|
let peers: Vec<HashMap<&str, String>> = PeerConfig::peers(Some(id_filters))
|
||||||
|
.drain(..)
|
||||||
|
.map(|(id, _, p)| peer_to_map_ab(id, p))
|
||||||
|
.collect();
|
||||||
|
return serde_json::ser::to_string(&peers).unwrap_or("".to_owned());
|
||||||
|
}
|
||||||
|
"".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn main_load_fav_peers() {
|
pub fn main_load_fav_peers() {
|
||||||
if !config::APP_DIR.read().unwrap().is_empty() {
|
if !config::APP_DIR.read().unwrap().is_empty() {
|
||||||
let favs = get_fav();
|
let favs = get_fav();
|
||||||
let mut recent = PeerConfig::peers();
|
let mut recent = PeerConfig::peers(None);
|
||||||
let mut lan = config::LanPeers::load()
|
let mut lan = config::LanPeers::load()
|
||||||
.peers
|
.peers
|
||||||
.iter()
|
.iter()
|
||||||
@ -1086,6 +1107,20 @@ pub fn main_start_dbus_server() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn main_save_ab(json: String) {
|
||||||
|
if json.len() > 1024 {
|
||||||
|
std::thread::spawn(|| {
|
||||||
|
config::Ab::store(json);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
config::Ab::store(json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main_clear_ab() {
|
||||||
|
config::Ab::remove();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn session_send_pointer(session_id: SessionID, msg: String) {
|
pub fn session_send_pointer(session_id: SessionID, msg: String) {
|
||||||
if let Ok(m) = serde_json::from_str::<HashMap<String, serde_json::Value>>(&msg) {
|
if let Ok(m) = serde_json::from_str::<HashMap<String, serde_json::Value>>(&msg) {
|
||||||
let alt = m.get("alt").is_some();
|
let alt = m.get("alt").is_some();
|
||||||
|
@ -412,7 +412,7 @@ impl UI {
|
|||||||
|
|
||||||
fn get_recent_sessions(&mut self) -> Value {
|
fn get_recent_sessions(&mut self) -> Value {
|
||||||
// to-do: limit number of recent sessions, and remove old peer file
|
// to-do: limit number of recent sessions, and remove old peer file
|
||||||
let peers: Vec<Value> = PeerConfig::peers()
|
let peers: Vec<Value> = PeerConfig::peers(None)
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|p| Self::get_peer_value(p.0, p.2))
|
.map(|p| Self::get_peer_value(p.0, p.2))
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -3,7 +3,9 @@ use hbb_common::password_security;
|
|||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
allow_err,
|
allow_err,
|
||||||
config::{self, Config, LocalConfig, PeerConfig},
|
config::{self, Config, LocalConfig, PeerConfig},
|
||||||
directories_next, log, tokio,
|
directories_next, log,
|
||||||
|
sodiumoxide::base64,
|
||||||
|
tokio,
|
||||||
};
|
};
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
bytes::Bytes,
|
bytes::Bytes,
|
||||||
@ -610,6 +612,16 @@ pub fn peer_to_map(id: String, p: PeerConfig) -> HashMap<&'static str, String> {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "flutter")]
|
||||||
|
pub fn peer_to_map_ab(id: String, p: PeerConfig) -> HashMap<&'static str, String> {
|
||||||
|
let mut m = peer_to_map(id, p.clone());
|
||||||
|
m.insert(
|
||||||
|
"hash",
|
||||||
|
base64::encode(p.password, base64::Variant::Original),
|
||||||
|
);
|
||||||
|
m
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_lan_peers() -> Vec<HashMap<&'static str, String>> {
|
pub fn get_lan_peers() -> Vec<HashMap<&'static str, String>> {
|
||||||
config::LanPeers::load()
|
config::LanPeers::load()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user