From b3114d4147b41b78dfa0b754e763647e2eee1c11 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 18 Dec 2022 16:17:10 +0800 Subject: [PATCH 1/7] file audit Signed-off-by: 21pages --- .../lib/desktop/widgets/remote_menubar.dart | 3 +- src/common.rs | 4 +- src/flutter_ffi.rs | 4 +- src/server/connection.rs | 87 ++++++++++++++++--- src/ui/header.tis | 2 +- src/ui/remote.rs | 10 +-- src/ui_session_interface.rs | 5 +- 7 files changed, 90 insertions(+), 25 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_menubar.dart b/flutter/lib/desktop/widgets/remote_menubar.dart index c776f0e89..1a7e9aeb7 100644 --- a/flutter/lib/desktop/widgets/remote_menubar.dart +++ b/flutter/lib/desktop/widgets/remote_menubar.dart @@ -571,7 +571,8 @@ class _RemoteMenubarState extends State { ), ]); // {handler.get_audit_server() &&
  • {translate('Note')}
  • } - final auditServer = bind.sessionGetAuditServerSync(id: widget.id); + final auditServer = + bind.sessionGetAuditServerSync(id: widget.id, typ: "conn"); if (auditServer.isNotEmpty) { displayMenu.add( MenuEntryButton( diff --git a/src/common.rs b/src/common.rs index 9023780f4..fe76b3168 100644 --- a/src/common.rs +++ b/src/common.rs @@ -620,12 +620,12 @@ pub fn get_api_server(api: String, custom: String) -> String { "https://admin.rustdesk.com".to_owned() } -pub fn get_audit_server(api: String, custom: String) -> String { +pub fn get_audit_server(api: String, custom: String, typ: String) -> String { let url = get_api_server(api, custom); if url.is_empty() || url.contains("rustdesk.com") { return "".to_owned(); } - format!("{}/api/audit", url) + format!("{}/api/audit/{}", url, typ) } pub async fn post_request(url: String, body: String, header: &str) -> ResultType { diff --git a/src/flutter_ffi.rs b/src/flutter_ffi.rs index ddfaad06d..dc9d7a04a 100644 --- a/src/flutter_ffi.rs +++ b/src/flutter_ffi.rs @@ -894,9 +894,9 @@ pub fn session_restart_remote_device(id: String) { } } -pub fn session_get_audit_server_sync(id: String) -> SyncReturn { +pub fn session_get_audit_server_sync(id: String, typ: String) -> SyncReturn { let res = if let Some(session) = SESSIONS.read().unwrap().get(&id) { - session.get_audit_server() + session.get_audit_server(typ) } else { "".to_owned() }; diff --git a/src/server/connection.rs b/src/server/connection.rs index fdd0ea77a..32ce97ea8 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -93,7 +93,8 @@ pub struct Connection { tx_input: std_mpsc::Sender, // handle input messages video_ack_required: bool, peer_info: (String, String), - api_server: String, + server_audit_conn: String, + server_audit_file: String, lr: LoginRequest, last_recv_time: Arc>, chat_unanswered: bool, @@ -184,7 +185,8 @@ impl Connection { tx_input, video_ack_required: false, peer_info: Default::default(), - api_server: "".to_owned(), + server_audit_conn: "".to_owned(), + server_audit_file: "".to_owned(), lr: Default::default(), last_recv_time: Arc::new(Mutex::new(Instant::now())), chat_unanswered: false, @@ -384,7 +386,7 @@ impl Connection { } else { conn.timer = time::interval_at(Instant::now() + SEC30, SEC30); } - conn.post_audit(json!({})); // heartbeat + conn.post_conn_audit(json!({})); // heartbeat }, Some((instant, value)) = rx_video.recv() => { if !conn.video_ack_required { @@ -497,7 +499,7 @@ impl Connection { conn.on_close(&err.to_string(), false).await; } - conn.post_audit(json!({ + conn.post_conn_audit(json!({ "action": "close", })); log::info!("#{} connection loop exited", id); @@ -601,7 +603,7 @@ impl Connection { if last_recv_time.elapsed() >= H1 { bail!("Timeout"); } - self.post_audit(json!({})); // heartbeat + self.post_conn_audit(json!({})); // heartbeat } } } @@ -650,7 +652,7 @@ impl Connection { msg_out.set_hash(self.hash.clone()); self.send(msg_out).await; self.get_api_server(); - self.post_audit(json!({ + self.post_conn_audit(json!({ "ip": addr.ip(), "action": "new", })); @@ -658,17 +660,23 @@ impl Connection { } fn get_api_server(&mut self) { - self.api_server = crate::get_audit_server( + self.server_audit_conn = crate::get_audit_server( Config::get_option("api-server"), Config::get_option("custom-rendezvous-server"), + "conn".to_owned(), + ); + self.server_audit_file = crate::get_audit_server( + Config::get_option("api-server"), + Config::get_option("custom-rendezvous-server"), + "file".to_owned(), ); } - fn post_audit(&self, v: Value) { - if self.api_server.is_empty() { + fn post_conn_audit(&self, v: Value) { + if self.server_audit_conn.is_empty() { return; } - let url = self.api_server.clone(); + let url = self.server_audit_conn.clone(); let mut v = v; v["id"] = json!(Config::get_id()); v["uuid"] = json!(base64::encode(hbb_common::get_uuid())); @@ -678,6 +686,41 @@ impl Connection { }); } + fn post_file_audit(&self, action: &str, path: &str, files: Vec<(String, i64)>, info: Value) { + if self.server_audit_file.is_empty() { + return; + } + let url = self.server_audit_file.clone(); + let file_num = files.len(); + let mut files = files; + files.sort_by(|a, b| b.1.cmp(&a.1)); + files.truncate(10); + let is_file = match action { + "send" | "receive" => files.len() == 1 && files[0].0.is_empty(), + "remove_dir" | "create_dir" => false, + "remove_file" => true, + _ => true, + }; + let mut info = info; + info["ip"] = json!(self.ip.clone()); + info["name"] = json!(self.lr.my_name.clone()); + info["num"] = json!(file_num); + info["files"] = json!(files); + let v = json!({ + "id":json!(Config::get_id()), + "uuid":json!(base64::encode(hbb_common::get_uuid())), + "Id":json!(self.inner.id), + "peer_id":json!(self.lr.my_id), + "action": action, + "path":path, + "is_file":is_file, + "info":json!(info).to_string(), + }); + tokio::spawn(async move { + allow_err!(Self::post_audit_async(url, v).await); + }); + } + #[inline] async fn post_audit_async(url: String, v: Value) -> ResultType<()> { crate::post_request(url, v.to_string(), "").await?; @@ -695,7 +738,7 @@ impl Connection { } else { 0 }; - self.post_audit(json!({"peer": self.peer_info, "Type": conn_type})); + self.post_conn_audit(json!({"peer": self.peer_info, "Type": conn_type})); #[allow(unused_mut)] let mut username = crate::platform::get_active_username(); let mut res = LoginResponse::new(); @@ -1225,8 +1268,18 @@ impl Connection { Ok(job) => { self.send(fs::new_dir(id, path, job.files().to_vec())) .await; + let mut files = job.files().to_owned(); self.read_jobs.push(job); self.timer = time::interval(MILLI1); + self.post_file_audit( + "send", + &s.path, + files + .drain(..) + .map(|f| (f.name, f.size as _)) + .collect(), + json!({}), + ); } } } @@ -1237,7 +1290,7 @@ impl Connection { &self.lr.version, )); self.send_fs(ipc::FS::NewWrite { - path: r.path, + path: r.path.clone(), id: r.id, file_num: r.file_num, files: r @@ -1248,6 +1301,16 @@ impl Connection { .collect(), overwrite_detection: od, }); + self.post_file_audit( + "receive", + &r.path, + r.files + .to_vec() + .drain(..) + .map(|f| (f.name, f.size as _)) + .collect(), + json!({}), + ); } Some(file_action::Union::RemoveDir(d)) => { self.send_fs(ipc::FS::RemoveDir { diff --git a/src/ui/header.tis b/src/ui/header.tis index 086696726..d1bb91cb9 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -208,7 +208,7 @@ class Header: Reactor.Component { {keyboard_enabled ?
  • {translate('OS Password')}
  • : ""}
  • {translate('Transfer File')}
  • {translate('TCP Tunneling')}
  • - {handler.get_audit_server() &&
  • {translate('Note')}
  • } + {handler.get_audit_server("conn") &&
  • {translate('Note')}
  • }
    {keyboard_enabled && (pi.platform == "Linux" || pi.sas_enabled) ?
  • {translate('Insert')} Ctrl + Alt + Del
  • : ""} {restart_enabled && (pi.platform == "Linux" || pi.platform == "Windows" || pi.platform == "Mac OS") ?
  • {translate('Restart Remote Device')}
  • : ""} diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 29b1a9eee..df5d98038 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -233,12 +233,12 @@ impl InvokeUiSession for SciterHandler { fn on_connected(&self, conn_type: ConnType) { match conn_type { - ConnType::RDP => {}, - ConnType::PORT_FORWARD => {}, - ConnType::FILE_TRANSFER => {}, + ConnType::RDP => {} + ConnType::PORT_FORWARD => {} + ConnType::FILE_TRANSFER => {} ConnType::DEFAULT_CONN => { crate::keyboard::client::start_grab_loop(); - }, + } } } @@ -348,7 +348,7 @@ impl sciter::EventHandler for SciterSession { } sciter::dispatch_script_call! { - fn get_audit_server(); + fn get_audit_server(String); fn send_note(String); fn is_xfce(); fn get_id(); diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 9868b5bb1..55984e343 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -173,7 +173,7 @@ impl Session { self.send(Data::Message(msg)); } - pub fn get_audit_server(&self) -> String { + pub fn get_audit_server(&self, typ: String) -> String { if self.lc.read().unwrap().conn_id <= 0 || LocalConfig::get_option("access_token").is_empty() { @@ -182,11 +182,12 @@ impl Session { crate::get_audit_server( Config::get_option("api-server"), Config::get_option("custom-rendezvous-server"), + typ, ) } pub fn send_note(&self, note: String) { - let url = self.get_audit_server(); + let url = self.get_audit_server("conn".to_string()); let id = self.id.clone(); let conn_id = self.lc.read().unwrap().conn_id; std::thread::spawn(move || { From 38b6ba66915c1ce327b3a36faba189b1f25de7d8 Mon Sep 17 00:00:00 2001 From: 21pages Date: Sun, 18 Dec 2022 17:00:10 +0800 Subject: [PATCH 2/7] split connection timer Signed-off-by: 21pages --- src/server/connection.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/server/connection.rs b/src/server/connection.rs index 32ce97ea8..659a97136 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -72,6 +72,8 @@ pub struct Connection { hash: Hash, read_jobs: Vec, timer: Interval, + file_timer: Interval, + http_timer: Interval, file_transfer: Option<(String, bool)>, port_forward_socket: Option>, port_forward_address: String, @@ -164,6 +166,8 @@ impl Connection { hash, read_jobs: Vec::new(), timer: time::interval(SEC30), + file_timer: time::interval(SEC30), + http_timer: time::interval(Duration::from_secs(3)), file_transfer: None, port_forward_socket: None, port_forward_address: "".to_owned(), @@ -377,15 +381,17 @@ impl Connection { break; } }, - _ = conn.timer.tick() => { + _ = conn.file_timer.tick() => { if !conn.read_jobs.is_empty() { if let Err(err) = fs::handle_read_jobs(&mut conn.read_jobs, &mut conn.stream).await { conn.on_close(&err.to_string(), false).await; break; } } else { - conn.timer = time::interval_at(Instant::now() + SEC30, SEC30); + conn.file_timer = time::interval_at(Instant::now() + SEC30, SEC30); } + } + _ = conn.http_timer.tick() => { conn.post_conn_audit(json!({})); // heartbeat }, Some((instant, value)) = rx_video.recv() => { @@ -1270,7 +1276,7 @@ impl Connection { .await; let mut files = job.files().to_owned(); self.read_jobs.push(job); - self.timer = time::interval(MILLI1); + self.file_timer = time::interval(MILLI1); self.post_file_audit( "send", &s.path, From 866ab240875905c01ae72ff06a7267cbd3a018f7 Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 20 Dec 2022 10:22:28 +0800 Subject: [PATCH 3/7] disconnect conn from web console Signed-off-by: 21pages --- src/lang/ca.rs | 1 + src/lang/cn.rs | 1 + src/lang/cs.rs | 1 + src/lang/da.rs | 1 + src/lang/de.rs | 1 + src/lang/eo.rs | 1 + src/lang/es.rs | 1 + src/lang/fa.rs | 1 + src/lang/fr.rs | 1 + src/lang/gr.rs | 1 + src/lang/hu.rs | 1 + src/lang/id.rs | 1 + src/lang/it.rs | 1 + src/lang/ja.rs | 1 + src/lang/ko.rs | 1 + src/lang/kz.rs | 1 + src/lang/pl.rs | 1 + src/lang/pt_PT.rs | 1 + src/lang/ptbr.rs | 1 + src/lang/ru.rs | 1 + src/lang/sk.rs | 1 + src/lang/sq.rs | 1 + src/lang/sr.rs | 1 + src/lang/sv.rs | 1 + src/lang/template.rs | 1 + src/lang/tr.rs | 1 + src/lang/tw.rs | 1 + src/lang/ua.rs | 1 + src/lang/vn.rs | 1 + src/server/connection.rs | 47 ++++++++++++++++++++++++++++++++-------- 30 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/lang/ca.rs b/src/lang/ca.rs index a8f39a23f..2f9bf7b25 100644 --- a/src/lang/ca.rs +++ b/src/lang/ca.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/cn.rs b/src/lang/cn.rs index 5dce8cd38..be0d7803e 100644 --- a/src/lang/cn.rs +++ b/src/lang/cn.rs @@ -406,5 +406,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", "添加到地址簿"), ("Group", "小组"), ("Search", "搜索"), + ("Closed manually by the web console", "被web控制台手动关闭"), ].iter().cloned().collect(); } diff --git a/src/lang/cs.rs b/src/lang/cs.rs index 52571ef07..eb57edc0a 100644 --- a/src/lang/cs.rs +++ b/src/lang/cs.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/da.rs b/src/lang/da.rs index 138777e32..c34a31aef 100644 --- a/src/lang/da.rs +++ b/src/lang/da.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/de.rs b/src/lang/de.rs index d5d75d90b..b551774bf 100644 --- a/src/lang/de.rs +++ b/src/lang/de.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", "Zum Adressbuch hinzufügen"), ("Group", "Gruppe"), ("Search", "Suchen"), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/eo.rs b/src/lang/eo.rs index d22cb2311..3f22f489e 100644 --- a/src/lang/eo.rs +++ b/src/lang/eo.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/es.rs b/src/lang/es.rs index ce0254a98..6923029ff 100644 --- a/src/lang/es.rs +++ b/src/lang/es.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", "Añadir a la libreta de direcciones"), ("Group", "Grupo"), ("Search", "Búsqueda"), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fa.rs b/src/lang/fa.rs index b3850e1f2..20a1663f2 100644 --- a/src/lang/fa.rs +++ b/src/lang/fa.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", "افزودن به دفترچه آدرس"), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/fr.rs b/src/lang/fr.rs index 124bfc00c..3b81fa1e4 100644 --- a/src/lang/fr.rs +++ b/src/lang/fr.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", "Ajouter au carnet d'adresses"), ("Group", "Groupe"), ("Search", "Rechercher"), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/gr.rs b/src/lang/gr.rs index 933a84143..c708ce478 100644 --- a/src/lang/gr.rs +++ b/src/lang/gr.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", "Προσθήκη στο Βιβλίο Διευθύνσεων"), ("Group", "Ομάδα"), ("Search", "Αναζήτηση"), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/hu.rs b/src/lang/hu.rs index f3f1e8fd9..849bfa0af 100644 --- a/src/lang/hu.rs +++ b/src/lang/hu.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/id.rs b/src/lang/id.rs index 89728b3e6..da69f2c76 100644 --- a/src/lang/id.rs +++ b/src/lang/id.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/it.rs b/src/lang/it.rs index 2237c81db..3b112618d 100644 --- a/src/lang/it.rs +++ b/src/lang/it.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", "Aggiungi alla rubrica"), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ja.rs b/src/lang/ja.rs index e40c81ae8..3d76f446b 100644 --- a/src/lang/ja.rs +++ b/src/lang/ja.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ko.rs b/src/lang/ko.rs index 426a027db..92bb85bce 100644 --- a/src/lang/ko.rs +++ b/src/lang/ko.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/kz.rs b/src/lang/kz.rs index 6acd892f8..90a2730f6 100644 --- a/src/lang/kz.rs +++ b/src/lang/kz.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pl.rs b/src/lang/pl.rs index eb3a45d53..e5604c442 100644 --- a/src/lang/pl.rs +++ b/src/lang/pl.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", "Dodaj do Książki Adresowej"), ("Group", "Grypy"), ("Search", "Szukaj"), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/pt_PT.rs b/src/lang/pt_PT.rs index 4d3d057ee..2adb4eb9e 100644 --- a/src/lang/pt_PT.rs +++ b/src/lang/pt_PT.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ptbr.rs b/src/lang/ptbr.rs index bc878b680..6256d2e7a 100644 --- a/src/lang/ptbr.rs +++ b/src/lang/ptbr.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/ru.rs b/src/lang/ru.rs index f42d3146e..6e08322f3 100644 --- a/src/lang/ru.rs +++ b/src/lang/ru.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", "Добавить в адресную книгу"), ("Group", "Группа"), ("Search", "Поиск"), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sk.rs b/src/lang/sk.rs index e1b82d4f4..a65e0519b 100644 --- a/src/lang/sk.rs +++ b/src/lang/sk.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sq.rs b/src/lang/sq.rs index cbc71d4aa..27f37260d 100644 --- a/src/lang/sq.rs +++ b/src/lang/sq.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sr.rs b/src/lang/sr.rs index 43490c0b2..5d04f0150 100644 --- a/src/lang/sr.rs +++ b/src/lang/sr.rs @@ -410,5 +410,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", "Dodaj u adresar"), ("Group", "Grupa"), ("Search", "Pretraga"), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/sv.rs b/src/lang/sv.rs index 4dcababc0..72cc83fce 100644 --- a/src/lang/sv.rs +++ b/src/lang/sv.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/template.rs b/src/lang/template.rs index 34fe5077f..a92df2b52 100644 --- a/src/lang/template.rs +++ b/src/lang/template.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tr.rs b/src/lang/tr.rs index b0c0686c1..cb7203c2d 100644 --- a/src/lang/tr.rs +++ b/src/lang/tr.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/tw.rs b/src/lang/tw.rs index d8d6c5ba0..6eef3656c 100644 --- a/src/lang/tw.rs +++ b/src/lang/tw.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", "添加到地址簿"), ("Group", "小組"), ("Search", "搜索"), + ("Closed manually by the web console", "被web控制台手動關閉"), ].iter().cloned().collect(); } diff --git a/src/lang/ua.rs b/src/lang/ua.rs index 83b5e6984..373c3267e 100644 --- a/src/lang/ua.rs +++ b/src/lang/ua.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", "Додати IP до Адресної книги"), ("Group", "Група"), ("Search", "Пошук"), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/lang/vn.rs b/src/lang/vn.rs index 412f04999..7b7808bea 100644 --- a/src/lang/vn.rs +++ b/src/lang/vn.rs @@ -405,5 +405,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> = ("Add to Address Book", ""), ("Group", ""), ("Search", ""), + ("Closed manually by the web console", ""), ].iter().cloned().collect(); } diff --git a/src/server/connection.rs b/src/server/connection.rs index 659a97136..bfc8cd78b 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -26,6 +26,7 @@ use hbb_common::{ }; #[cfg(any(target_os = "android", target_os = "ios"))] use scrap::android::call_main_service_mouse_input; +use serde::Deserialize; use serde_json::{json, value::Value}; use sha2::{Digest, Sha256}; #[cfg(not(any(target_os = "android", target_os = "ios")))] @@ -261,7 +262,7 @@ impl Connection { } } ipc::Data::Close => { - conn.on_close_manually("connection manager").await; + conn.on_close_manually("connection manager", "peer").await; break; } ipc::Data::ChatMessage{text} => { @@ -392,7 +393,10 @@ impl Connection { } } _ = conn.http_timer.tick() => { - conn.post_conn_audit(json!({})); // heartbeat + if let Err(_) = Connection::post_heartbeat(conn.server_audit_conn.clone(), conn.inner.id).await { + conn.on_close_manually("web console", "web console").await; + break; + } }, Some((instant, value)) = rx_video.recv() => { if !conn.video_ack_required { @@ -420,7 +424,7 @@ impl Connection { Some(message::Union::Misc(m)) => { match &m.union { Some(misc::Union::StopService(_)) => { - conn.on_close_manually("stop service").await; + conn.on_close_manually("stop service", "peer").await; break; } _ => {}, @@ -609,7 +613,7 @@ impl Connection { if last_recv_time.elapsed() >= H1 { bail!("Timeout"); } - self.post_conn_audit(json!({})); // heartbeat + Connection::post_heartbeat(self.server_audit_conn.clone(), self.inner.id).await?; } } } @@ -692,6 +696,25 @@ impl Connection { }); } + async fn post_heartbeat(server_audit_conn: String, conn_id: i32) -> ResultType<()> { + if server_audit_conn.is_empty() { + return Ok(()); + } + let url = server_audit_conn.clone(); + let mut v = Value::default(); + v["id"] = json!(Config::get_id()); + v["uuid"] = json!(base64::encode(hbb_common::get_uuid())); + v["Id"] = json!(conn_id); + if let Ok(rsp) = Self::post_audit_async(url, v).await { + if let Ok(rsp) = serde_json::from_str::(&rsp) { + if rsp.action == "disconnect" { + bail!("disconnect by server"); + } + } + } + return Ok(()); + } + fn post_file_audit(&self, action: &str, path: &str, files: Vec<(String, i64)>, info: Value) { if self.server_audit_file.is_empty() { return; @@ -728,9 +751,8 @@ impl Connection { } #[inline] - async fn post_audit_async(url: String, v: Value) -> ResultType<()> { - crate::post_request(url, v.to_string(), "").await?; - Ok(()) + async fn post_audit_async(url: String, v: Value) -> ResultType { + crate::post_request(url, v.to_string(), "").await } async fn send_logon_response(&mut self) { @@ -1610,10 +1632,10 @@ impl Connection { self.port_forward_socket.take(); } - async fn on_close_manually(&mut self, close_from: &str) { + async fn on_close_manually(&mut self, close_from: &str, close_by: &str) { self.close_manually = true; let mut misc = Misc::new(); - misc.set_close_reason("Closed manually by the peer".into()); + misc.set_close_reason(format!("Closed manually by the {}", close_by)); let mut msg_out = Message::new(); msg_out.set_misc(misc); self.send(msg_out).await; @@ -1790,3 +1812,10 @@ mod privacy_mode { } } } + +#[derive(Debug, Deserialize)] +struct ConnAuditResponse { + #[allow(dead_code)] + ret: bool, + action: String, +} From 56f154f69a39389f99b02a54132a850975ba0a7f Mon Sep 17 00:00:00 2001 From: 21pages Date: Tue, 20 Dec 2022 16:20:42 +0800 Subject: [PATCH 4/7] alarm audit Signed-off-by: 21pages --- src/server/connection.rs | 47 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/server/connection.rs b/src/server/connection.rs index bfc8cd78b..136a4b692 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -654,6 +654,13 @@ impl Connection { { self.send_login_error("Your ip is blocked by the peer") .await; + Self::post_alarm_audit( + AlarmAuditType::IpWhiltelist, //"ip whiltelist", + true, + json!({ + "ip":addr.ip(), + }), + ); sleep(1.).await; return false; } @@ -750,6 +757,26 @@ impl Connection { }); } + pub fn post_alarm_audit(typ: AlarmAuditType, from_remote: bool, info: Value) { + let url = crate::get_audit_server( + Config::get_option("api-server"), + Config::get_option("custom-rendezvous-server"), + "alarm".to_owned(), + ); + if url.is_empty() { + return; + } + let mut v = Value::default(); + v["id"] = json!(Config::get_id()); + v["uuid"] = json!(base64::encode(hbb_common::get_uuid())); + v["typ"] = json!(typ as i8); + v["from_remote"] = json!(from_remote); + v["info"] = serde_json::Value::String(info.to_string()); + tokio::spawn(async move { + allow_err!(Self::post_audit_async(url, v).await); + }); + } + #[inline] async fn post_audit_async(url: String, v: Value) -> ResultType { crate::post_request(url, v.to_string(), "").await @@ -1157,8 +1184,22 @@ impl Connection { if failure.2 > 30 { self.send_login_error("Too many wrong password attempts") .await; + Self::post_alarm_audit( + AlarmAuditType::ManyWrongPassword, + true, + json!({ + "ip":self.ip, + }), + ); } else if time == failure.0 && failure.1 > 6 { self.send_login_error("Please try 1 minute later").await; + Self::post_alarm_audit( + AlarmAuditType::FrequentAttempt, + true, + json!({ + "ip":self.ip, + }), + ); } else if !self.validate_password() { if failure.0 == time { failure.1 += 1; @@ -1819,3 +1860,9 @@ struct ConnAuditResponse { ret: bool, action: String, } + +pub enum AlarmAuditType { + IpWhiltelist = 0, + ManyWrongPassword = 1, + FrequentAttempt = 2, +} From 54ce0a977587161fa5259aafcdf727430c7190bb Mon Sep 17 00:00:00 2001 From: 21pages Date: Fri, 23 Dec 2022 10:36:18 +0800 Subject: [PATCH 5/7] refactor audit field Signed-off-by: 21pages --- flutter/lib/models/group_model.dart | 12 +++++------ src/server/connection.rs | 33 +++++++++++++++++------------ 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/flutter/lib/models/group_model.dart b/flutter/lib/models/group_model.dart index adc92f182..f220d62f1 100644 --- a/flutter/lib/models/group_model.dart +++ b/flutter/lib/models/group_model.dart @@ -45,8 +45,9 @@ class GroupModel { var uri0 = Uri.parse(api); final pageSize = 20; var total = 0; - int current = 1; + int current = 0; do { + current += 1; var uri = Uri( scheme: uri0.scheme, host: uri0.host, @@ -58,7 +59,6 @@ class GroupModel { if (gFFI.userModel.isAdmin.isFalse) 'grp': gFFI.userModel.groupName.value, }); - current += pageSize; final resp = await http.get(uri, headers: await getHttpHeaders()); if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") { Map json = jsonDecode(resp.body); @@ -76,7 +76,7 @@ class GroupModel { } } } - } while (current < total + 1); + } while (current * pageSize < total); } catch (err) { debugPrint('$err'); userLoadError.value = err.toString(); @@ -96,8 +96,9 @@ class GroupModel { var uri0 = Uri.parse(api); final pageSize = 20; var total = 0; - int current = 1; + int current = 0; do { + current += 1; var uri = Uri( scheme: uri0.scheme, host: uri0.host, @@ -109,7 +110,6 @@ class GroupModel { 'grp': gFFI.userModel.groupName.value, 'target_user': username }); - current += pageSize; final resp = await http.get(uri, headers: await getHttpHeaders()); if (resp.body.isNotEmpty && resp.body.toLowerCase() != "null") { Map json = jsonDecode(resp.body); @@ -129,7 +129,7 @@ class GroupModel { } } } - } while (current < total + 1); + } while (current * pageSize < total); } catch (err) { debugPrint('$err'); peerLoadError.value = err.toString(); diff --git a/src/server/connection.rs b/src/server/connection.rs index 136a4b692..394d330a9 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -697,7 +697,7 @@ impl Connection { let mut v = v; v["id"] = json!(Config::get_id()); v["uuid"] = json!(base64::encode(hbb_common::get_uuid())); - v["Id"] = json!(self.inner.id); + v["conn_id"] = json!(self.inner.id); tokio::spawn(async move { allow_err!(Self::post_audit_async(url, v).await); }); @@ -711,7 +711,7 @@ impl Connection { let mut v = Value::default(); v["id"] = json!(Config::get_id()); v["uuid"] = json!(base64::encode(hbb_common::get_uuid())); - v["Id"] = json!(conn_id); + v["conn_id"] = json!(conn_id); if let Ok(rsp) = Self::post_audit_async(url, v).await { if let Ok(rsp) = serde_json::from_str::(&rsp) { if rsp.action == "disconnect" { @@ -722,7 +722,13 @@ impl Connection { return Ok(()); } - fn post_file_audit(&self, action: &str, path: &str, files: Vec<(String, i64)>, info: Value) { + fn post_file_audit( + &self, + r#type: FileAuditType, + path: &str, + files: Vec<(String, i64)>, + info: Value, + ) { if self.server_audit_file.is_empty() { return; } @@ -731,12 +737,7 @@ impl Connection { let mut files = files; files.sort_by(|a, b| b.1.cmp(&a.1)); files.truncate(10); - let is_file = match action { - "send" | "receive" => files.len() == 1 && files[0].0.is_empty(), - "remove_dir" | "create_dir" => false, - "remove_file" => true, - _ => true, - }; + let is_file = files.len() == 1 && files[0].0.is_empty(); let mut info = info; info["ip"] = json!(self.ip.clone()); info["name"] = json!(self.lr.my_name.clone()); @@ -745,9 +746,8 @@ impl Connection { let v = json!({ "id":json!(Config::get_id()), "uuid":json!(base64::encode(hbb_common::get_uuid())), - "Id":json!(self.inner.id), "peer_id":json!(self.lr.my_id), - "action": action, + "type": r#type as i8, "path":path, "is_file":is_file, "info":json!(info).to_string(), @@ -793,7 +793,7 @@ impl Connection { } else { 0 }; - self.post_conn_audit(json!({"peer": self.peer_info, "Type": conn_type})); + self.post_conn_audit(json!({"peer": self.peer_info, "type": conn_type})); #[allow(unused_mut)] let mut username = crate::platform::get_active_username(); let mut res = LoginResponse::new(); @@ -1341,7 +1341,7 @@ impl Connection { self.read_jobs.push(job); self.file_timer = time::interval(MILLI1); self.post_file_audit( - "send", + FileAuditType::RemoteSend, &s.path, files .drain(..) @@ -1371,7 +1371,7 @@ impl Connection { overwrite_detection: od, }); self.post_file_audit( - "receive", + FileAuditType::RemoteReceive, &r.path, r.files .to_vec() @@ -1866,3 +1866,8 @@ pub enum AlarmAuditType { ManyWrongPassword = 1, FrequentAttempt = 2, } + +pub enum FileAuditType { + RemoteSend = 0, + RemoteReceive = 1, +} From a5643a6b59b59b26a2c2960ab870fb7045d7a01c Mon Sep 17 00:00:00 2001 From: 21pages Date: Sat, 24 Dec 2022 14:29:32 +0800 Subject: [PATCH 6/7] fix two finger scroll Signed-off-by: 21pages --- flutter/lib/desktop/pages/desktop_setting_page.dart | 1 + flutter/lib/desktop/widgets/tabbar_widget.dart | 1 + 2 files changed, 2 insertions(+) diff --git a/flutter/lib/desktop/pages/desktop_setting_page.dart b/flutter/lib/desktop/pages/desktop_setting_page.dart index a45de24b0..40cd794ab 100644 --- a/flutter/lib/desktop/pages/desktop_setting_page.dart +++ b/flutter/lib/desktop/pages/desktop_setting_page.dart @@ -125,6 +125,7 @@ class _DesktopSettingPageState extends State scrollController: controller, child: PageView( controller: controller, + physics: NeverScrollableScrollPhysics(), children: const [ _General(), _Safety(), diff --git a/flutter/lib/desktop/widgets/tabbar_widget.dart b/flutter/lib/desktop/widgets/tabbar_widget.dart index 436011cb5..f6a5da819 100644 --- a/flutter/lib/desktop/widgets/tabbar_widget.dart +++ b/flutter/lib/desktop/widgets/tabbar_widget.dart @@ -331,6 +331,7 @@ class DesktopTab extends StatelessWidget { return _buildBlock( child: Obx(() => PageView( controller: state.value.pageController, + physics: NeverScrollableScrollPhysics(), children: state.value.tabs .map((tab) => tab.page) .toList(growable: false)))); From 8b7e6935f4e19c92ba10cc2fc815500bee39404b Mon Sep 17 00:00:00 2001 From: 21pages Date: Sat, 24 Dec 2022 19:47:12 +0800 Subject: [PATCH 7/7] remove ReorderedListView Signed-off-by: 21pages --- flutter/lib/common/widgets/peer_tab_page.dart | 233 ++++++------------ 1 file changed, 74 insertions(+), 159 deletions(-) diff --git a/flutter/lib/common/widgets/peer_tab_page.dart b/flutter/lib/common/widgets/peer_tab_page.dart index b501bb44b..0c24fe7ea 100644 --- a/flutter/lib/common/widgets/peer_tab_page.dart +++ b/flutter/lib/common/widgets/peer_tab_page.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'dart:ui' as ui; import 'package:bot_toast/bot_toast.dart'; @@ -22,10 +21,7 @@ const String defaultGroupTabname = 'Group'; class StatePeerTab { final RxInt currentTab = 0.obs; - static const List tabIndexs = [0, 1, 2, 3, 4]; - List tabOrder = List.empty(growable: true); - final RxList visibleTabOrder = RxList.empty(growable: true); - int tabHiddenFlag = 0; + final RxInt tabHiddenFlag = 0.obs; final RxList tabNames = [ 'Recent Sessions', 'Favorites', @@ -35,44 +31,25 @@ class StatePeerTab { ].obs; StatePeerTab._() { - tabHiddenFlag = (int.tryParse( + tabHiddenFlag.value = (int.tryParse( bind.getLocalFlutterConfig(k: 'hidden-peer-card'), radix: 2) ?? 0); + var tabs = _notHiddenTabs(); currentTab.value = int.tryParse(bind.getLocalFlutterConfig(k: 'peer-tab-index')) ?? 0; - if (!tabIndexs.contains(currentTab.value)) { - currentTab.value = tabIndexs[0]; + if (!tabs.contains(currentTab.value)) { + currentTab.value = 0; } - tabOrder = tabIndexs.toList(); - try { - final conf = bind.getLocalFlutterConfig(k: 'peer-tab-order'); - if (conf.isNotEmpty) { - final json = jsonDecode(conf); - if (json is List) { - final List list = - json.map((e) => int.tryParse(e.toString()) ?? -1).toList(); - if (list.length == tabOrder.length && - tabOrder.every((e) => list.contains(e))) { - tabOrder = list; - } - } - } - } catch (e) { - debugPrintStack(label: '$e'); - } - visibleTabOrder.value = tabOrder.where((e) => !isTabHidden(e)).toList(); - visibleTabOrder.remove(groupTabIndex); } static final StatePeerTab instance = StatePeerTab._(); check() { - List oldOrder = visibleTabOrder; + var tabs = _notHiddenTabs(); if (filterGroupCard()) { - visibleTabOrder.remove(groupTabIndex); if (currentTab.value == groupTabIndex) { currentTab.value = - visibleTabOrder.firstWhereOrNull((e) => e != groupTabIndex) ?? 0; + tabs.firstWhereOrNull((e) => e != groupTabIndex) ?? 0; bind.setLocalFlutterConfig( k: 'peer-tab-index', v: currentTab.value.toString()); } @@ -83,26 +60,22 @@ class StatePeerTab { } else { tabNames[groupTabIndex] = defaultGroupTabname; } - if (isTabHidden(groupTabIndex)) { - visibleTabOrder.remove(groupTabIndex); - } else { - if (!visibleTabOrder.contains(groupTabIndex)) { - addTabInOrder(visibleTabOrder, groupTabIndex); - } - } - if (visibleTabOrder.contains(groupTabIndex) && + if (tabs.contains(groupTabIndex) && int.tryParse(bind.getLocalFlutterConfig(k: 'peer-tab-index')) == groupTabIndex) { currentTab.value = groupTabIndex; } } - if (oldOrder != visibleTabOrder) { - saveTabOrder(); - } } - bool isTabHidden(int tabindex) { - return tabHiddenFlag & (1 << tabindex) != 0; + List currentTabs() { + var v = List.empty(growable: true); + for (int i = 0; i < tabNames.length; i++) { + if (!_isTabHidden(i) && !_isTabFilter(i)) { + v.add(i); + } + } + return v; } bool filterGroupCard() { @@ -114,50 +87,25 @@ class StatePeerTab { } } - addTabInOrder(List list, int tabIndex) { - if (!tabOrder.contains(tabIndex) || list.contains(tabIndex)) { - return; - } - bool sameOrder = true; - int lastIndex = -1; - for (int i = 0; i < list.length; i++) { - var index = tabOrder.lastIndexOf(list[i]); - if (index > lastIndex) { - lastIndex = index; - continue; - } else { - sameOrder = false; - break; - } - } - if (sameOrder) { - var indexInTabOrder = tabOrder.indexOf(tabIndex); - var left = List.empty(growable: true); - for (int i = 0; i < indexInTabOrder; i++) { - left.add(tabOrder[i]); - } - int insertIndex = list.lastIndexWhere((e) => left.contains(e)); - if (insertIndex < 0) { - insertIndex = 0; - } else { - insertIndex += 1; - } - list.insert(insertIndex, tabIndex); - } else { - list.add(tabIndex); - } + bool _isTabHidden(int tabindex) { + return tabHiddenFlag & (1 << tabindex) != 0; } - saveTabOrder() { - var list = statePeerTab.visibleTabOrder.toList(); - var left = tabOrder - .where((e) => !statePeerTab.visibleTabOrder.contains(e)) - .toList(); - for (var t in left) { - addTabInOrder(list, t); + bool _isTabFilter(int tabIndex) { + if (tabIndex == groupTabIndex) { + return filterGroupCard(); } - statePeerTab.tabOrder = list; - bind.setLocalFlutterConfig(k: 'peer-tab-order', v: jsonEncode(list)); + return false; + } + + List _notHiddenTabs() { + var v = List.empty(growable: true); + for (int i = 0; i < tabNames.length; i++) { + if (!_isTabHidden(i)) { + v.add(i); + } + } + return v; } } @@ -266,59 +214,41 @@ class _PeerTabPageState extends State Widget _createSwitchBar(BuildContext context) { final textColor = Theme.of(context).textTheme.titleLarge?.color; - statePeerTab.visibleTabOrder - .removeWhere((e) => !StatePeerTab.tabIndexs.contains(e)); return Obx(() { - int indexCounter = -1; - return ReorderableListView( - buildDefaultDragHandles: false, - onReorder: (oldIndex, newIndex) { - var list = statePeerTab.visibleTabOrder.toList(); - if (oldIndex < newIndex) { - newIndex -= 1; - } - final int item = list.removeAt(oldIndex); - list.insert(newIndex, item); - statePeerTab.visibleTabOrder.value = list; - statePeerTab.saveTabOrder(); - }, + var tabs = statePeerTab.currentTabs(); + return ListView( scrollDirection: Axis.horizontal, - shrinkWrap: true, - scrollController: ScrollController(), - children: statePeerTab.visibleTabOrder.map((t) { - indexCounter++; - return ReorderableDragStartListener( - key: ValueKey(t), - index: indexCounter, - child: InkWell( - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 8), - decoration: BoxDecoration( - color: statePeerTab.currentTab.value == t - ? Theme.of(context).backgroundColor - : null, - borderRadius: BorderRadius.circular(isDesktop ? 2 : 6), + physics: NeverScrollableScrollPhysics(), + controller: ScrollController(), + children: tabs.map((t) { + return InkWell( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 8), + decoration: BoxDecoration( + color: statePeerTab.currentTab.value == t + ? Theme.of(context).backgroundColor + : null, + borderRadius: BorderRadius.circular(isDesktop ? 2 : 6), + ), + child: Align( + alignment: Alignment.center, + child: Text( + translatedTabname(t), + textAlign: TextAlign.center, + style: TextStyle( + height: 1, + fontSize: 14, + color: statePeerTab.currentTab.value == t + ? textColor + : textColor + ?..withOpacity(0.5)), ), - child: Align( - alignment: Alignment.center, - child: Text( - translatedTabname(t), - textAlign: TextAlign.center, - style: TextStyle( - height: 1, - fontSize: 14, - color: statePeerTab.currentTab.value == t - ? textColor - : textColor - ?..withOpacity(0.5)), - ), - )), - onTap: () async { - await handleTabSelection(t); - await bind.setLocalFlutterConfig( - k: 'peer-tab-index', v: t.toString()); - }, - ), + )), + onTap: () async { + await handleTabSelection(t); + await bind.setLocalFlutterConfig( + k: 'peer-tab-index', v: t.toString()); + }, ); }).toList()); }); @@ -343,20 +273,18 @@ class _PeerTabPageState extends State Widget _createPeersView() { final verticalMargin = isDesktop ? 12.0 : 6.0; - statePeerTab.visibleTabOrder - .removeWhere((e) => !StatePeerTab.tabIndexs.contains(e)); return Expanded( child: Obx(() { - if (statePeerTab.visibleTabOrder.isEmpty) { + var tabs = statePeerTab.currentTabs(); + if (tabs.isEmpty) { return visibleContextMenuListener(Center( child: Text(translate('Right click to select tabs')), )); } else { - if (statePeerTab.visibleTabOrder - .contains(statePeerTab.currentTab.value)) { + if (tabs.contains(statePeerTab.currentTab.value)) { return entries[statePeerTab.currentTab.value].widget; } else { - statePeerTab.currentTab.value = statePeerTab.visibleTabOrder[0]; + statePeerTab.currentTab.value = tabs[0]; return entries[statePeerTab.currentTab.value].widget; } } @@ -394,13 +322,9 @@ class _PeerTabPageState extends State } adjustTab() { - if (statePeerTab.visibleTabOrder.isNotEmpty) { - if (!statePeerTab.visibleTabOrder - .contains(statePeerTab.currentTab.value)) { - handleTabSelection(statePeerTab.visibleTabOrder[0]); - } - } else { - statePeerTab.currentTab.value = 0; + var tabs = statePeerTab.currentTabs(); + if (tabs.isNotEmpty && !tabs.contains(statePeerTab.currentTab.value)) { + statePeerTab.currentTab.value = tabs[0]; } } @@ -438,22 +362,13 @@ class _PeerTabPageState extends State }, setter: (show) async { if (show) { - statePeerTab.tabHiddenFlag &= ~bitMask; + statePeerTab.tabHiddenFlag.value &= ~bitMask; } else { - statePeerTab.tabHiddenFlag |= bitMask; + statePeerTab.tabHiddenFlag.value |= bitMask; } await bind.setLocalFlutterConfig( k: 'hidden-peer-card', - v: statePeerTab.tabHiddenFlag.toRadixString(2)); - statePeerTab.visibleTabOrder - .removeWhere((e) => statePeerTab.isTabHidden(e)); - for (int j = 0; j < statePeerTab.tabNames.length; j++) { - if (!statePeerTab.isTabHidden(j) && - !(j == groupTabIndex && statePeerTab.filterGroupCard())) { - statePeerTab.addTabInOrder(statePeerTab.visibleTabOrder, j); - } - } - statePeerTab.saveTabOrder(); + v: statePeerTab.tabHiddenFlag.value.toRadixString(2)); cancelFunc(); adjustTab(); }));