diff --git a/src/client.rs b/src/client.rs index ddb093b08..6346af6d0 100644 --- a/src/client.rs +++ b/src/client.rs @@ -2,7 +2,7 @@ use std::{ collections::HashMap, net::SocketAddr, ops::{Deref, Not}, - sync::{mpsc, Arc, Mutex, RwLock}, + sync::{mpsc, Arc, Mutex, RwLock, atomic::AtomicBool}, }; pub use async_trait::async_trait; @@ -48,7 +48,12 @@ pub use super::lang::*; pub mod file_trait; pub mod helper; +pub mod io_loop; +pub static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true); +pub static SERVER_FILE_TRANSFER_ENABLED: AtomicBool = AtomicBool::new(true); +pub static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true); +pub const MILLI1: Duration = Duration::from_millis(1); pub const SEC30: Duration = Duration::from_secs(30); /// Client of the remote desktop. diff --git a/src/client/io_loop.rs b/src/client/io_loop.rs new file mode 100644 index 000000000..f7f8f4f18 --- /dev/null +++ b/src/client/io_loop.rs @@ -0,0 +1,1205 @@ +use crate::client::{ + Client, CodecFormat, FileManager, MediaData, MediaSender, QualityStatus, MILLI1, SEC30, + SERVER_CLIPBOARD_ENABLED, SERVER_FILE_TRANSFER_ENABLED, SERVER_KEYBOARD_ENABLED, +}; +use crate::common::{check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL}; + +use crate::ui_session_interface::{InvokeUi, Session}; +use crate::{client::Data, client::Interface}; + +use hbb_common::config::{PeerConfig, TransferSerde}; +use hbb_common::fs::{ + can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult, + RemoveJobMeta, TransferJobMeta, +}; +use hbb_common::message_proto::permission_info::Permission; +use hbb_common::protobuf::Message as _; +use hbb_common::rendezvous_proto::ConnType; +use hbb_common::tokio::{ + self, + sync::mpsc, + time::{self, Duration, Instant, Interval}, +}; +use hbb_common::{allow_err, message_proto::*}; +use hbb_common::{fs, log, Stream}; +use std::collections::HashMap; + +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{Arc, Mutex}; + +pub struct Remote { + handler: Session, + video_sender: MediaSender, + audio_sender: MediaSender, + receiver: mpsc::UnboundedReceiver, + sender: mpsc::UnboundedSender, + old_clipboard: Arc>, + read_jobs: Vec, + write_jobs: Vec, + remove_jobs: HashMap, + timer: Interval, + last_update_jobs_status: (Instant, HashMap), + first_frame: bool, + #[cfg(windows)] + clipboard_file_context: Option>, + data_count: Arc, + frame_count: Arc, + video_format: CodecFormat, +} + +impl Remote { + pub fn new( + handler: Session, + video_sender: MediaSender, + audio_sender: MediaSender, + receiver: mpsc::UnboundedReceiver, + sender: mpsc::UnboundedSender, + frame_count: Arc, + ) -> Self { + Self { + handler, + video_sender, + audio_sender, + receiver, + sender, + old_clipboard: Default::default(), + read_jobs: Vec::new(), + write_jobs: Vec::new(), + remove_jobs: Default::default(), + timer: time::interval(SEC30), + last_update_jobs_status: (Instant::now(), Default::default()), + first_frame: false, + #[cfg(windows)] + clipboard_file_context: None, + data_count: Arc::new(AtomicUsize::new(0)), + frame_count, + video_format: CodecFormat::Unknown, + } + } + + pub async fn io_loop(&mut self, key: &str, token: &str) { + let stop_clipboard = self.start_clipboard(); + let mut last_recv_time = Instant::now(); + let mut received = false; + let conn_type = if self.handler.is_file_transfer() { + ConnType::FILE_TRANSFER + } else { + ConnType::default() + }; + match Client::start( + &self.handler.id, + key, + token, + conn_type, + self.handler.clone(), + ) + .await + { + Ok((mut peer, direct)) => { + SERVER_KEYBOARD_ENABLED.store(true, Ordering::SeqCst); + SERVER_CLIPBOARD_ENABLED.store(true, Ordering::SeqCst); + SERVER_FILE_TRANSFER_ENABLED.store(true, Ordering::SeqCst); + self.handler.set_connection_type(peer.is_secured(), direct); // flutter -> connection_ready + + // just build for now + #[cfg(not(windows))] + let (_tx_holder, mut rx_clip_client) = mpsc::unbounded_channel::(); + #[cfg(windows)] + let mut rx_clip_client = get_rx_clip_client().lock().await; + + let mut status_timer = time::interval(Duration::new(1, 0)); + + loop { + tokio::select! { + res = peer.next() => { + if let Some(res) = res { + match res { + Err(err) => { + log::error!("Connection closed: {}", err); + self.handler.set_force_relay(direct, received); + self.handler.msgbox("error", "Connection Error", &err.to_string()); + break; + } + Ok(ref bytes) => { + last_recv_time = Instant::now(); + received = true; + self.data_count.fetch_add(bytes.len(), Ordering::Relaxed); + if !self.handle_msg_from_peer(bytes, &mut peer).await { + break + } + } + } + } else { + if self.handler.is_restarting_remote_device() { + log::info!("Restart remote device"); + self.handler.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip"); + } else { + log::info!("Reset by the peer"); + self.handler.msgbox("error", "Connection Error", "Reset by the peer"); + } + break; + } + } + d = self.receiver.recv() => { + if let Some(d) = d { + if !self.handle_msg_from_ui(d, &mut peer).await { + break; + } + } + } + _msg = rx_clip_client.recv() => { + #[cfg(windows)] + match _msg { + Some((_, clip)) => { + allow_err!(peer.send(&clip_2_msg(clip)).await); + } + None => { + // unreachable!() + } + } + } + _ = self.timer.tick() => { + if last_recv_time.elapsed() >= SEC30 { + self.handler.msgbox("error", "Connection Error", "Timeout"); + break; + } + if !self.read_jobs.is_empty() { + if let Err(err) = fs::handle_read_jobs(&mut self.read_jobs, &mut peer).await { + self.handler.msgbox("error", "Connection Error", &err.to_string()); + break; + } + self.update_jobs_status(); + } else { + self.timer = time::interval_at(Instant::now() + SEC30, SEC30); + } + } + _ = status_timer.tick() => { + let speed = self.data_count.swap(0, Ordering::Relaxed); + let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32); + let fps = self.frame_count.swap(0, Ordering::Relaxed) as _; + self.handler.update_quality_status(QualityStatus { + speed:Some(speed), + fps:Some(fps), + ..Default::default() + }); + } + } + } + log::debug!("Exit io_loop of id={}", self.handler.id); + } + Err(err) => { + self.handler + .msgbox("error", "Connection Error", &err.to_string()); + } + } + if let Some(stop) = stop_clipboard { + stop.send(()).ok(); + } + SERVER_KEYBOARD_ENABLED.store(false, Ordering::SeqCst); + SERVER_CLIPBOARD_ENABLED.store(false, Ordering::SeqCst); + SERVER_FILE_TRANSFER_ENABLED.store(false, Ordering::SeqCst); + } + + fn handle_job_status(&mut self, id: i32, file_num: i32, err: Option) { + if let Some(job) = self.remove_jobs.get_mut(&id) { + if job.no_confirm { + let file_num = (file_num + 1) as usize; + if file_num < job.files.len() { + let path = format!("{}{}{}", job.path, job.sep, job.files[file_num].name); + self.sender + .send(Data::RemoveFile((id, path, file_num as i32, job.is_remote))) + .ok(); + let elapsed = job.last_update_job_status.elapsed().as_millis() as i32; + if elapsed >= 1000 { + job.last_update_job_status = Instant::now(); + } else { + return; + } + } else { + self.remove_jobs.remove(&id); + } + } + } + if let Some(err) = err { + self.handler.job_error(id, err, file_num); + } else { + self.handler.job_done(id, file_num); + } + } + + fn start_clipboard(&mut self) -> Option> { + if self.handler.is_file_transfer() || self.handler.is_port_forward() { + return None; + } + let (tx, rx) = std::sync::mpsc::channel(); + let old_clipboard = self.old_clipboard.clone(); + let tx_protobuf = self.sender.clone(); + let lc = self.handler.lc.clone(); + match ClipboardContext::new() { + Ok(mut ctx) => { + // ignore clipboard update before service start + check_clipboard(&mut ctx, Some(&old_clipboard)); + std::thread::spawn(move || loop { + std::thread::sleep(Duration::from_millis(CLIPBOARD_INTERVAL)); + match rx.try_recv() { + Ok(_) | Err(std::sync::mpsc::TryRecvError::Disconnected) => { + log::debug!("Exit clipboard service of client"); + break; + } + _ => {} + } + if !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) + || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) + || lc.read().unwrap().disable_clipboard + { + continue; + } + if let Some(msg) = check_clipboard(&mut ctx, Some(&old_clipboard)) { + tx_protobuf.send(Data::Message(msg)).ok(); + } + }); + } + Err(err) => { + log::error!("Failed to start clipboard service of client: {}", err); + } + } + Some(tx) + } + + fn load_last_jobs(&mut self) { + log::info!("start load last jobs"); + self.handler.clear_all_jobs(); + let pc = self.handler.load_config(); + if pc.transfer.write_jobs.is_empty() && pc.transfer.read_jobs.is_empty() { + // no last jobs + return; + } + // TODO: can add a confirm dialog + let mut cnt = 1; + for job_str in pc.transfer.read_jobs.iter() { + let job: Result = serde_json::from_str(&job_str); + if let Ok(job) = job { + self.handler.add_job( + cnt, + job.to.clone(), + job.remote.clone(), + job.file_num, + job.show_hidden, + false, + ); + cnt += 1; + println!("restore read_job: {:?}", job); + } + } + for job_str in pc.transfer.write_jobs.iter() { + let job: Result = serde_json::from_str(&job_str); + if let Ok(job) = job { + self.handler.add_job( + cnt, + job.remote.clone(), + job.to.clone(), + job.file_num, + job.show_hidden, + true, + ); + cnt += 1; + println!("restore write_job: {:?}", job); + } + } + self.handler.update_transfer_list(); + } + + async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool { + 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)) => { + self.handler + .handle_login_from_ui(password, remember, peer) + .await; + } + Data::ToggleClipboardFile => { + self.check_clipboard_file_context(); + } + Data::Message(msg) => { + allow_err!(peer.send(&msg).await); + } + Data::SendFiles((id, path, to, file_num, include_hidden, is_remote)) => { + log::info!("send files, is remote {}", is_remote); + let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version); + if is_remote { + log::debug!("New job {}, write to {} from remote {}", id, to, path); + self.write_jobs.push(fs::TransferJob::new_write( + id, + path.clone(), + to, + file_num, + include_hidden, + is_remote, + Vec::new(), + od, + )); + allow_err!( + peer.send(&fs::new_send(id, path, file_num, include_hidden)) + .await + ); + } else { + match fs::TransferJob::new_read( + id, + to.clone(), + path.clone(), + file_num, + include_hidden, + is_remote, + od, + ) { + Err(err) => { + self.handle_job_status(id, -1, Some(err.to_string())); + } + Ok(job) => { + log::debug!( + "New job {}, read {} to remote {}, {} files", + id, + path, + to, + job.files().len() + ); + // let m = make_fd(job.id(), job.files(), true); + // self.handler.call("updateFolderFiles", &make_args!(m)); // TODO + #[cfg(not(windows))] + let files = job.files().clone(); + #[cfg(windows)] + let mut files = job.files().clone(); + #[cfg(windows)] + if self.handler.peer_platform() != "Windows" { + // peer is not windows, need transform \ to / + fs::transform_windows_path(&mut files); + } + self.read_jobs.push(job); + self.timer = time::interval(MILLI1); + allow_err!(peer.send(&fs::new_receive(id, to, file_num, files)).await); + } + } + } + } + Data::AddJob((id, path, to, file_num, include_hidden, is_remote)) => { + let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version); + if is_remote { + log::debug!( + "new write waiting job {}, write to {} from remote {}", + id, + to, + path + ); + let mut job = fs::TransferJob::new_write( + id, + path.clone(), + to, + file_num, + include_hidden, + is_remote, + Vec::new(), + od, + ); + job.is_last_job = true; + self.write_jobs.push(job); + } else { + match fs::TransferJob::new_read( + id, + to.clone(), + path.clone(), + file_num, + include_hidden, + is_remote, + od, + ) { + Err(err) => { + self.handle_job_status(id, -1, Some(err.to_string())); + } + Ok(mut job) => { + log::debug!( + "new read waiting job {}, read {} to remote {}, {} files", + id, + path, + to, + job.files().len() + ); + // let m = make_fd(job.id(), job.files(), true); + // self.handler.call("updateFolderFiles", &make_args!(m)); + job.is_last_job = true; + self.read_jobs.push(job); + self.timer = time::interval(MILLI1); + } + } + } + } + Data::ResumeJob((id, is_remote)) => { + if is_remote { + if let Some(job) = get_job(id, &mut self.write_jobs) { + job.is_last_job = false; + allow_err!( + peer.send(&fs::new_send( + id, + job.remote.clone(), + job.file_num, + job.show_hidden + )) + .await + ); + } + } else { + if let Some(job) = get_job(id, &mut self.read_jobs) { + job.is_last_job = false; + allow_err!( + peer.send(&fs::new_receive( + id, + job.path.to_string_lossy().to_string(), + job.file_num, + job.files.clone() + )) + .await + ); + } + } + } + Data::SetNoConfirm(id) => { + if let Some(job) = self.remove_jobs.get_mut(&id) { + job.no_confirm = true; + } + } + Data::ConfirmDeleteFiles((id, file_num)) => { + if let Some(job) = self.remove_jobs.get_mut(&id) { + let i = file_num as usize; + if i < job.files.len() { + self.handler.ui_handler.confirm_delete_files( + id, + file_num, + job.files[i].name.clone(), + ); + self.handler.confirm_delete_files(id, file_num); + } + } + } + Data::SetConfirmOverrideFile((id, file_num, need_override, remember, is_upload)) => { + if is_upload { + if let Some(job) = fs::get_job(id, &mut self.read_jobs) { + if remember { + job.set_overwrite_strategy(Some(need_override)); + } + job.confirm(&FileTransferSendConfirmRequest { + id, + file_num, + union: if need_override { + Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) + } else { + Some(file_transfer_send_confirm_request::Union::Skip(true)) + }, + ..Default::default() + }); + } + } else { + if let Some(job) = fs::get_job(id, &mut self.write_jobs) { + if remember { + job.set_overwrite_strategy(Some(need_override)); + } + let mut msg = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_send_confirm(FileTransferSendConfirmRequest { + id, + file_num, + union: if need_override { + Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) + } else { + Some(file_transfer_send_confirm_request::Union::Skip(true)) + }, + ..Default::default() + }); + msg.set_file_action(file_action); + allow_err!(peer.send(&msg).await); + } + } + } + Data::RemoveDirAll((id, path, is_remote, include_hidden)) => { + let sep = self.handler.get_path_sep(is_remote); + if is_remote { + let mut msg_out = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_all_files(ReadAllFiles { + id, + path: path.clone(), + include_hidden, + ..Default::default() + }); + msg_out.set_file_action(file_action); + allow_err!(peer.send(&msg_out).await); + self.remove_jobs + .insert(id, RemoveJob::new(Vec::new(), path, sep, is_remote)); + } else { + match fs::get_recursive_files(&path, include_hidden) { + Ok(entries) => { + // let m = make_fd(id, &entries, true); + // self.handler.call("updateFolderFiles", &make_args!(m)); + self.remove_jobs + .insert(id, RemoveJob::new(entries, path, sep, is_remote)); + } + Err(err) => { + self.handle_job_status(id, -1, Some(err.to_string())); + } + } + } + } + Data::CancelJob(id) => { + let mut msg_out = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_cancel(FileTransferCancel { + id: id, + ..Default::default() + }); + msg_out.set_file_action(file_action); + allow_err!(peer.send(&msg_out).await); + if let Some(job) = fs::get_job(id, &mut self.write_jobs) { + job.remove_download_file(); + fs::remove_job(id, &mut self.write_jobs); + } + fs::remove_job(id, &mut self.read_jobs); + self.remove_jobs.remove(&id); + } + Data::RemoveDir((id, path)) => { + let mut msg_out = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_remove_dir(FileRemoveDir { + id, + path, + recursive: true, + ..Default::default() + }); + msg_out.set_file_action(file_action); + allow_err!(peer.send(&msg_out).await); + } + Data::RemoveFile((id, path, file_num, is_remote)) => { + if is_remote { + let mut msg_out = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_remove_file(FileRemoveFile { + id, + path, + file_num, + ..Default::default() + }); + msg_out.set_file_action(file_action); + allow_err!(peer.send(&msg_out).await); + } else { + match fs::remove_file(&path) { + Err(err) => { + self.handle_job_status(id, file_num, Some(err.to_string())); + } + Ok(()) => { + self.handle_job_status(id, file_num, None); + } + } + } + } + Data::CreateDir((id, path, is_remote)) => { + if is_remote { + let mut msg_out = Message::new(); + let mut file_action = FileAction::new(); + file_action.set_create(FileDirCreate { + id, + path, + ..Default::default() + }); + msg_out.set_file_action(file_action); + allow_err!(peer.send(&msg_out).await); + } else { + match fs::create_dir(&path) { + Err(err) => { + self.handle_job_status(id, -1, Some(err.to_string())); + } + Ok(()) => { + self.handle_job_status(id, -1, None); + } + } + } + } + _ => {} + } + true + } + + #[inline] + fn update_job_status( + job: &fs::TransferJob, + elapsed: i32, + last_update_jobs_status: &mut (Instant, HashMap), + handler: &mut Session, + ) { + if elapsed <= 0 { + return; + } + let transferred = job.transferred(); + let last_transferred = { + if let Some(v) = last_update_jobs_status.1.get(&job.id()) { + v.to_owned() + } else { + 0 + } + }; + last_update_jobs_status.1.insert(job.id(), transferred); + let speed = (transferred - last_transferred) as f64 / (elapsed as f64 / 1000.); + let file_num = job.file_num() - 1; + handler.job_progress(job.id(), file_num, speed, job.finished_size() as f64); + } + + fn update_jobs_status(&mut self) { + let elapsed = self.last_update_jobs_status.0.elapsed().as_millis() as i32; + if elapsed >= 1000 { + for job in self.read_jobs.iter() { + Self::update_job_status( + job, + elapsed, + &mut self.last_update_jobs_status, + &mut self.handler, + ); + } + for job in self.write_jobs.iter() { + Self::update_job_status( + job, + elapsed, + &mut self.last_update_jobs_status, + &mut self.handler, + ); + } + self.last_update_jobs_status.0 = Instant::now(); + } + } + + pub async fn sync_jobs_status_to_local(&mut self) -> bool { + log::info!("sync transfer job status"); + let mut config: PeerConfig = self.handler.load_config(); + let mut transfer_metas = TransferSerde::default(); + for job in self.read_jobs.iter() { + let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default(); + transfer_metas.read_jobs.push(json_str); + } + for job in self.write_jobs.iter() { + let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default(); + transfer_metas.write_jobs.push(json_str); + } + log::info!("meta: {:?}", transfer_metas); + config.transfer = transfer_metas; + self.handler.save_config(config); + true + } + + async fn send_opts_after_login(&self, peer: &mut Stream) { + if let Some(opts) = self + .handler + .lc + .read() + .unwrap() + .get_option_message_after_login() + { + let mut misc = Misc::new(); + misc.set_option(opts); + let mut msg_out = Message::new(); + msg_out.set_misc(misc); + allow_err!(peer.send(&msg_out).await); + } + } + + async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { + if let Ok(msg_in) = Message::parse_from_bytes(&data) { + match msg_in.union { + Some(message::Union::VideoFrame(vf)) => { + if !self.first_frame { + self.first_frame = true; + self.handler.close_success(); + self.handler.adapt_size(); + self.send_opts_after_login(peer).await; + } + let incomming_format = CodecFormat::from(&vf); + if self.video_format != incomming_format { + self.video_format = incomming_format.clone(); + self.handler.update_quality_status(QualityStatus { + codec_format: Some(incomming_format), + ..Default::default() + }) + }; + self.video_sender.send(MediaData::VideoFrame(vf)).ok(); + } + Some(message::Union::Hash(hash)) => { + self.handler + .handle_hash(&self.handler.password.clone(), hash, peer) + .await; + } + Some(message::Union::LoginResponse(lr)) => match lr.union { + Some(login_response::Union::Error(err)) => { + if !self.handler.handle_login_error(&err) { + return false; + } + } + Some(login_response::Union::PeerInfo(pi)) => { + self.handler.handle_peer_info(pi); + // self.check_clipboard_file_context(); + // if !(self.handler.is_file_transfer() + // || self.handler.is_port_forward() + // || !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) + // || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) + // || self.handler.lc.read().unwrap().disable_clipboard) + // { + // let txt = self.old_clipboard.lock().unwrap().clone(); + // if !txt.is_empty() { + // let msg_out = crate::create_clipboard_msg(txt); + // let sender = self.sender.clone(); + // tokio::spawn(async move { + // // due to clipboard service interval time + // sleep(common::CLIPBOARD_INTERVAL as f32 / 1_000.).await; + // sender.send(Data::Message(msg_out)).ok(); + // }); + // } + // } + + // if self.handler.is_file_transfer() { + // self.load_last_jobs().await; + // } + } + _ => {} + }, + Some(message::Union::CursorData(cd)) => { + self.handler.set_cursor_data(cd); + } + Some(message::Union::CursorId(id)) => { + self.handler.set_cursor_id(id.to_string()); + } + Some(message::Union::CursorPosition(cp)) => { + self.handler.set_cursor_position(cp); + } + Some(message::Union::Clipboard(cb)) => { + if !self.handler.lc.read().unwrap().disable_clipboard { + #[cfg(not(any(target_os = "android", target_os = "ios")))] + update_clipboard(cb, Some(&self.old_clipboard)); + #[cfg(any(target_os = "android", target_os = "ios"))] + { + let content = if cb.compress { + hbb_common::compress::decompress(&cb.content) + } else { + cb.content.into() + }; + if let Ok(content) = String::from_utf8(content) { + self.handler.clipboard(content); + } + } + } + } + #[cfg(windows)] + Some(message::Union::Cliprdr(clip)) => { + if !self.handler.lc.read().unwrap().disable_clipboard { + if let Some(context) = &mut self.clipboard_file_context { + if let Some(clip) = msg_2_clip(clip) { + server_clip_file(context, 0, clip); + } + } + } + } + Some(message::Union::FileResponse(fr)) => { + match fr.union { + Some(file_response::Union::Dir(fd)) => { + #[cfg(windows)] + let entries = fd.entries.to_vec(); + #[cfg(not(windows))] + let mut entries = fd.entries.to_vec(); + #[cfg(not(windows))] + { + if self.handler.peer_platform() == "Windows" { + fs::transform_windows_path(&mut entries); + } + } + // let mut m = make_fd(fd.id, &entries, fd.id > 0); + // if fd.id <= 0 { + // m.set_item("path", fd.path); + // } + // self.handler.call("updateFolderFiles", &make_args!(m)); + if let Some(job) = fs::get_job(fd.id, &mut self.write_jobs) { + log::info!("job set_files: {:?}", entries); + job.set_files(entries); + } else if let Some(job) = self.remove_jobs.get_mut(&fd.id) { + job.files = entries; + } + } + Some(file_response::Union::Digest(digest)) => { + if digest.is_upload { + if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) { + if let Some(file) = job.files().get(digest.file_num as usize) { + let read_path = get_string(&job.join(&file.name)); + let overwrite_strategy = job.default_overwrite_strategy(); + if let Some(overwrite) = overwrite_strategy { + let req = FileTransferSendConfirmRequest { + id: digest.id, + file_num: digest.file_num, + union: Some(if overwrite { + file_transfer_send_confirm_request::Union::OffsetBlk(0) + } else { + file_transfer_send_confirm_request::Union::Skip( + true, + ) + }), + ..Default::default() + }; + job.confirm(&req); + let msg = new_send_confirm(req); + allow_err!(peer.send(&msg).await); + } else { + self.handler.override_file_confirm( + digest.id, + digest.file_num, + read_path, + true, + ); + } + } + } + } else { + if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) { + if let Some(file) = job.files().get(digest.file_num as usize) { + let write_path = get_string(&job.join(&file.name)); + let overwrite_strategy = job.default_overwrite_strategy(); + match fs::is_write_need_confirmation(&write_path, &digest) { + Ok(res) => match res { + DigestCheckResult::IsSame => { + let msg= new_send_confirm(FileTransferSendConfirmRequest { + id: digest.id, + file_num: digest.file_num, + union: Some(file_transfer_send_confirm_request::Union::Skip(true)), + ..Default::default() + }); + allow_err!(peer.send(&msg).await); + } + DigestCheckResult::NeedConfirm(digest) => { + if let Some(overwrite) = overwrite_strategy { + let msg = new_send_confirm( + FileTransferSendConfirmRequest { + id: digest.id, + file_num: digest.file_num, + union: Some(if overwrite { + file_transfer_send_confirm_request::Union::OffsetBlk(0) + } else { + file_transfer_send_confirm_request::Union::Skip(true) + }), + ..Default::default() + }, + ); + allow_err!(peer.send(&msg).await); + } else { + self.handler.override_file_confirm( + digest.id, + digest.file_num, + write_path, + false, + ); + } + } + DigestCheckResult::NoSuchFile => { + let msg = new_send_confirm( + FileTransferSendConfirmRequest { + id: digest.id, + file_num: digest.file_num, + union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), + ..Default::default() + }, + ); + allow_err!(peer.send(&msg).await); + } + }, + Err(err) => { + println!("error recving digest: {}", err); + } + } + } + } + } + } + Some(file_response::Union::Block(block)) => { + log::info!( + "file response block, file id:{}, file num: {}", + block.id, + block.file_num + ); + if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) { + if let Err(_err) = job.write(block, None).await { + // to-do: add "skip" for writing job + } + self.update_jobs_status(); + } + } + Some(file_response::Union::Done(d)) => { + if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) { + job.modify_time(); + fs::remove_job(d.id, &mut self.write_jobs); + } + self.handle_job_status(d.id, d.file_num, None); + } + Some(file_response::Union::Error(e)) => { + self.handle_job_status(e.id, e.file_num, Some(e.error)); + } + _ => {} + } + } + Some(message::Union::Misc(misc)) => match misc.union { + Some(misc::Union::AudioFormat(f)) => { + self.audio_sender.send(MediaData::AudioFormat(f)).ok(); + } + Some(misc::Union::ChatMessage(c)) => { + self.handler.new_message(c.text); + } + Some(misc::Union::PermissionInfo(p)) => { + log::info!("Change permission {:?} -> {}", p.permission, p.enabled); + match p.permission.enum_value_or_default() { + Permission::Keyboard => { + SERVER_KEYBOARD_ENABLED.store(p.enabled, Ordering::SeqCst); + self.handler.set_permission("keyboard", p.enabled); + } + Permission::Clipboard => { + SERVER_CLIPBOARD_ENABLED.store(p.enabled, Ordering::SeqCst); + self.handler.set_permission("clipboard", p.enabled); + } + Permission::Audio => { + self.handler.set_permission("audio", p.enabled); + } + Permission::File => { + SERVER_FILE_TRANSFER_ENABLED.store(p.enabled, Ordering::SeqCst); + if !p.enabled && self.handler.is_file_transfer() { + return true; + } + self.check_clipboard_file_context(); + self.handler.set_permission("file", p.enabled); + } + Permission::Restart => { + self.handler.set_permission("restart", p.enabled); + } + } + } + Some(misc::Union::SwitchDisplay(s)) => { + self.handler.ui_handler.switch_display(&s); + self.video_sender.send(MediaData::Reset).ok(); + if s.width > 0 && s.height > 0 { + self.handler.set_display(s.x, s.y, s.width, s.height); + } + } + Some(misc::Union::CloseReason(c)) => { + self.handler.msgbox("error", "Connection Error", &c); + return false; + } + Some(misc::Union::BackNotification(notification)) => { + if !self.handle_back_notification(notification).await { + return false; + } + } + _ => {} + }, + Some(message::Union::TestDelay(t)) => { + self.handler.handle_test_delay(t, peer).await; + } + Some(message::Union::AudioFrame(frame)) => { + if !self.handler.lc.read().unwrap().disable_audio { + self.audio_sender.send(MediaData::AudioFrame(frame)).ok(); + } + } + Some(message::Union::FileAction(action)) => match action.union { + Some(file_action::Union::SendConfirm(c)) => { + if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) { + job.confirm(&c); + } + } + _ => {} + }, + _ => {} + } + } + true + } + + async fn handle_back_notification(&mut self, notification: BackNotification) -> bool { + match notification.union { + Some(back_notification::Union::BlockInputState(state)) => { + self.handle_back_msg_block_input( + state.enum_value_or(back_notification::BlockInputState::BlkStateUnknown), + ) + .await; + } + Some(back_notification::Union::PrivacyModeState(state)) => { + if !self + .handle_back_msg_privacy_mode( + state.enum_value_or(back_notification::PrivacyModeState::PrvStateUnknown), + ) + .await + { + return false; + } + } + _ => {} + } + true + } + + #[inline(always)] + fn update_block_input_state(&mut self, on: bool) { + self.handler.update_block_input_state(on); + } + + async fn handle_back_msg_block_input(&mut self, state: back_notification::BlockInputState) { + match state { + back_notification::BlockInputState::BlkOnSucceeded => { + self.update_block_input_state(true); + } + back_notification::BlockInputState::BlkOnFailed => { + self.handler + .msgbox("custom-error", "Block user input", "Failed"); + self.update_block_input_state(false); + } + back_notification::BlockInputState::BlkOffSucceeded => { + self.update_block_input_state(false); + } + back_notification::BlockInputState::BlkOffFailed => { + self.handler + .msgbox("custom-error", "Unblock user input", "Failed"); + } + _ => {} + } + } + + #[inline(always)] + fn update_privacy_mode(&mut self, on: bool) { + let mut config = self.handler.load_config(); + config.privacy_mode = on; + self.handler.save_config(config); + + self.handler.update_privacy_mode(); + } + + async fn handle_back_msg_privacy_mode( + &mut self, + state: back_notification::PrivacyModeState, + ) -> bool { + match state { + back_notification::PrivacyModeState::PrvOnByOther => { + self.handler.msgbox( + "error", + "Connecting...", + "Someone turns on privacy mode, exit", + ); + return false; + } + back_notification::PrivacyModeState::PrvNotSupported => { + self.handler + .msgbox("custom-error", "Privacy mode", "Unsupported"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOnSucceeded => { + self.handler + .msgbox("custom-nocancel", "Privacy mode", "In privacy mode"); + self.update_privacy_mode(true); + } + back_notification::PrivacyModeState::PrvOnFailedDenied => { + self.handler + .msgbox("custom-error", "Privacy mode", "Peer denied"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOnFailedPlugin => { + self.handler + .msgbox("custom-error", "Privacy mode", "Please install plugins"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOnFailed => { + self.handler + .msgbox("custom-error", "Privacy mode", "Failed"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOffSucceeded => { + self.handler + .msgbox("custom-nocancel", "Privacy mode", "Out privacy mode"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOffByPeer => { + self.handler + .msgbox("custom-error", "Privacy mode", "Peer exit"); + self.update_privacy_mode(false); + } + back_notification::PrivacyModeState::PrvOffFailed => { + self.handler + .msgbox("custom-error", "Privacy mode", "Failed to turn off"); + } + back_notification::PrivacyModeState::PrvOffUnknown => { + self.handler + .msgbox("custom-error", "Privacy mode", "Turned off"); + // log::error!("Privacy mode is turned off with unknown reason"); + self.update_privacy_mode(false); + } + _ => {} + } + true + } + + fn check_clipboard_file_context(&mut self) { + #[cfg(windows)] + { + let enabled = SERVER_FILE_TRANSFER_ENABLED.load(Ordering::SeqCst) + && self.handler.lc.read().unwrap().enable_file_transfer; + if enabled == self.clipboard_file_context.is_none() { + self.clipboard_file_context = if enabled { + match create_clipboard_file_context(true, false) { + Ok(context) => { + log::info!("clipboard context for file transfer created."); + Some(context) + } + Err(err) => { + log::error!( + "Create clipboard context for file transfer: {}", + err.to_string() + ); + None + } + } + } else { + log::info!("clipboard context for file transfer destroyed."); + None + }; + } + } + } +} + +struct RemoveJob { + files: Vec, + path: String, + sep: &'static str, + is_remote: bool, + no_confirm: bool, + last_update_job_status: Instant, +} + +impl RemoveJob { + fn new(files: Vec, path: String, sep: &'static str, is_remote: bool) -> Self { + Self { + files, + path, + sep, + is_remote, + no_confirm: false, + last_update_job_status: Instant::now(), + } + } + + pub fn _gen_meta(&self) -> RemoveJobMeta { + RemoveJobMeta { + path: self.path.clone(), + is_remote: self.is_remote, + no_confirm: self.no_confirm, + } + } +} diff --git a/src/flutter.rs b/src/flutter.rs index 514048e31..0b8c3626f 100644 --- a/src/flutter.rs +++ b/src/flutter.rs @@ -1,57 +1,36 @@ use std::{ collections::HashMap, sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, - Arc, Mutex, RwLock, + Arc, RwLock, }, }; use flutter_rust_bridge::{StreamSink, ZeroCopyBuffer}; use hbb_common::{ - allow_err, bail, - compress::decompress, - config::{Config, LocalConfig, PeerConfig, TransferSerde}, - fs::{ - self, can_enable_overwrite_detection, get_job, get_string, new_send_confirm, - transform_windows_path, DigestCheckResult, - }, - log, + bail, + config::{LocalConfig}, message_proto::*, - protobuf::Message as _, - rendezvous_proto::ConnType, - tokio::{ - self, - sync::mpsc, - time::{self, Duration, Instant, Interval}, - }, - ResultType, Stream, + ResultType, }; use crate::{ - common::{self, make_fd_to_json, CLIPBOARD_INTERVAL}, ui_session_interface::{io_loop, InvokeUi, Session}, }; -#[cfg(not(any(target_os = "android", target_os = "ios")))] -use crate::common::{check_clipboard, update_clipboard, ClipboardContext}; -use crate::{client::*, flutter_ffi::EventToUI, make_fd_flutter}; + +use crate::{client::*, flutter_ffi::EventToUI}; pub(super) const APP_TYPE_MAIN: &str = "main"; pub(super) const APP_TYPE_DESKTOP_REMOTE: &str = "remote"; pub(super) const APP_TYPE_DESKTOP_FILE_TRANSFER: &str = "file transfer"; -const MILLI1: Duration = Duration::from_millis(1); - lazy_static::lazy_static! { pub static ref SESSIONS: RwLock>> = Default::default(); pub static ref GLOBAL_EVENT_STREAM: RwLock>> = Default::default(); // rust to dart event channel } -static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true); -static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true); - #[derive(Default, Clone)] pub struct FlutterHandler { pub event_stream: Arc>>>, @@ -115,7 +94,7 @@ impl InvokeUi for FlutterHandler { } fn set_permission(&self, name: &str, value: bool) { - // todo!() + self.push_event("permission", vec![(name, &value.to_string())]); } fn update_pi(&self, pi: PeerInfo) { @@ -157,11 +136,14 @@ impl InvokeUi for FlutterHandler { } fn job_error(&self, id: i32, err: String, file_num: i32) { - // todo!() + self.push_event("job_error", vec![("id", &id.to_string()), ("err", &err)]); } fn job_done(&self, id: i32, file_num: i32) { - // todo!() + self.push_event( + "job_done", + vec![("id", &id.to_string()), ("file_num", &file_num.to_string())], + ); } fn clear_all_jobs(&self) { @@ -189,11 +171,27 @@ impl InvokeUi for FlutterHandler { } fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool) { - // todo!() + self.push_event( + "override_file_confirm", + vec![ + ("id", &id.to_string()), + ("file_num", &file_num.to_string()), + ("read_path", &to), + ("is_upload", &is_upload.to_string()), + ], + ); } fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64) { - // todo!() + self.push_event( + "job_progress", + vec![ + ("id", &id.to_string()), + ("file_num", &file_num.to_string()), + ("speed", &speed.to_string()), + ("finished_size", &finished_size.to_string()), + ], + ); } fn adapt_size(&self) { @@ -245,6 +243,35 @@ impl InvokeUi for FlutterHandler { ], ); } + + fn new_message(&self, msg: String) { + self.push_event("chat_client_mode", vec![("text", &msg)]); + } + + fn switch_display(&self, display: &SwitchDisplay) { + self.push_event( + "switch_display", + vec![ + ("display", &display.to_string()), + ("x", &display.x.to_string()), + ("y", &display.y.to_string()), + ("width", &display.width.to_string()), + ("height", &display.height.to_string()), + ], + ); + } + + fn update_block_input_state(&self, on: bool) { + self.push_event( + "update_block_input_state", + [("input_state", if on { "on" } else { "off" })].into(), + ); + } + + #[cfg(any(target_os = "android", target_os = "ios"))] + fn clipboard(&self, content: String) { + self.push_event("clipboard", vec![("content", &content)]); + } } /// Create a new remote session with the given id. @@ -290,6 +317,7 @@ pub fn session_start_(id: &str, event_stream: StreamSink) -> ResultTy *session.event_stream.write().unwrap() = Some(event_stream); let session = session.clone(); std::thread::spawn(move || { + // TODO is_file_transfer is_port_forward // let is_file_transfer = session.lc.read().unwrap().is_file_transfer; // let is_port_forward = session.lc.read().unwrap().is_port_forward; // Connection::start(session, is_file_transfer, is_port_forward); @@ -301,1091 +329,7 @@ pub fn session_start_(id: &str, event_stream: StreamSink) -> ResultTy } } -// struct Connection { -// video_handler: VideoHandler, -// audio_handler: AudioHandler, -// session: Session, -// first_frame: bool, -// read_jobs: Vec, -// write_jobs: Vec, -// timer: Interval, -// last_update_jobs_status: (Instant, HashMap), -// data_count: Arc, -// frame_count: Arc, -// video_format: CodecFormat, -// } - -// impl Connection { -// // TODO: Similar to remote::start_clipboard -// // merge the code -// fn start_clipboard( -// tx_protobuf: mpsc::UnboundedSender, -// lc: Arc>, -// ) -> Option> { -// let (tx, rx) = std::sync::mpsc::channel(); -// #[cfg(not(any(target_os = "android", target_os = "ios")))] -// match ClipboardContext::new() { -// Ok(mut ctx) => { -// let old_clipboard: Arc> = Default::default(); -// // ignore clipboard update before service start -// check_clipboard(&mut ctx, Some(&old_clipboard)); -// std::thread::spawn(move || loop { -// std::thread::sleep(Duration::from_millis(CLIPBOARD_INTERVAL)); -// match rx.try_recv() { -// Ok(_) | Err(std::sync::mpsc::TryRecvError::Disconnected) => { -// log::debug!("Exit clipboard service of client"); -// break; -// } -// _ => {} -// } -// if !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) -// || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) -// || lc.read().unwrap().disable_clipboard -// { -// continue; -// } -// if let Some(msg) = check_clipboard(&mut ctx, Some(&old_clipboard)) { -// tx_protobuf.send(Data::Message(msg)).ok(); -// } -// }); -// } -// Err(err) => { -// log::error!("Failed to start clipboard service of client: {}", err); -// } -// } -// Some(tx) -// } - -// /// Create a new connection. -// /// -// /// # Arguments -// /// -// /// * `session` - The session to create a new connection for. -// /// * `is_file_transfer` - Whether the connection is for file transfer. -// /// * `is_port_forward` - Whether the connection is for port forward. -// #[tokio::main(flavor = "current_thread")] -// async fn start(session: Session, is_file_transfer: bool, is_port_forward: bool) { -// let mut last_recv_time = Instant::now(); -// let (sender, mut receiver) = mpsc::unbounded_channel::(); -// let mut stop_clipboard = None; -// if !is_file_transfer && !is_port_forward { -// stop_clipboard = Self::start_clipboard(sender.clone(), session.lc.clone()); -// } -// *session.sender.write().unwrap() = Some(sender.clone()); -// let conn_type = if is_file_transfer { -// session.lc.write().unwrap().is_file_transfer = true; -// ConnType::FILE_TRANSFER -// } else if is_port_forward { -// ConnType::PORT_FORWARD // TODO: RDP -// } else { -// ConnType::DEFAULT_CONN -// }; -// let key = Config::get_option("key"); -// let token = Config::get_option("access_token"); - -// // TODO rdp & cli args -// let is_rdp = false; -// let args: Vec = Vec::new(); - -// if is_port_forward { -// if is_rdp { -// // let port = handler -// // .get_option("rdp_port".to_owned()) -// // .parse::() -// // .unwrap_or(3389); -// // std::env::set_var( -// // "rdp_username", -// // handler.get_option("rdp_username".to_owned()), -// // ); -// // std::env::set_var( -// // "rdp_password", -// // handler.get_option("rdp_password".to_owned()), -// // ); -// // log::info!("Remote rdp port: {}", port); -// // start_one_port_forward(handler, 0, "".to_owned(), port, receiver, &key, &token).await; -// } else if args.len() == 0 { -// let pfs = session.lc.read().unwrap().port_forwards.clone(); -// let mut queues = HashMap::>::new(); -// for d in pfs { -// sender.send(Data::AddPortForward(d)).ok(); -// } -// loop { -// match receiver.recv().await { -// Some(Data::AddPortForward((port, remote_host, remote_port))) => { -// if port <= 0 || remote_port <= 0 { -// continue; -// } -// let (sender, receiver) = mpsc::unbounded_channel::(); -// queues.insert(port, sender); -// let handler = session.clone(); -// let key = key.clone(); -// let token = token.clone(); -// tokio::spawn(async move { -// start_one_port_forward( -// handler, -// port, -// remote_host, -// remote_port, -// receiver, -// &key, -// &token, -// ) -// .await; -// }); -// } -// Some(Data::RemovePortForward(port)) => { -// if let Some(s) = queues.remove(&port) { -// s.send(Data::Close).ok(); -// } -// } -// Some(Data::Close) => { -// break; -// } -// Some(d) => { -// for (_, s) in queues.iter() { -// s.send(d.clone()).ok(); -// } -// } -// _ => {} -// } -// } -// } else { -// // let port = handler.args[0].parse::().unwrap_or(0); -// // if handler.args.len() != 3 -// // || handler.args[2].parse::().unwrap_or(0) <= 0 -// // || port <= 0 -// // { -// // handler.on_error("Invalid arguments, usage:

rustdesk --port-forward remote-id listen-port remote-host remote-port"); -// // } -// // let remote_host = handler.args[1].clone(); -// // let remote_port = handler.args[2].parse::().unwrap_or(0); -// // start_one_port_forward( -// // handler, -// // port, -// // remote_host, -// // remote_port, -// // receiver, -// // &key, -// // &token, -// // ) -// // .await; -// } -// return; -// } - -// let latency_controller = LatencyController::new(); -// let latency_controller_cl = latency_controller.clone(); - -// let mut conn = Connection { -// video_handler: VideoHandler::new(latency_controller), -// audio_handler: AudioHandler::new(latency_controller_cl), -// session: session.clone(), -// first_frame: false, -// read_jobs: Vec::new(), -// write_jobs: Vec::new(), -// timer: time::interval(SEC30), -// last_update_jobs_status: (Instant::now(), Default::default()), -// data_count: Arc::new(AtomicUsize::new(0)), -// frame_count: Arc::new(AtomicUsize::new(0)), -// video_format: CodecFormat::Unknown, -// }; - -// match Client::start(&session.id, &key, &token, conn_type, session.clone()).await { -// Ok((mut peer, direct)) => { -// SERVER_KEYBOARD_ENABLED.store(true, Ordering::SeqCst); -// SERVER_CLIPBOARD_ENABLED.store(true, Ordering::SeqCst); - -// session.push_event( -// "connection_ready", -// vec![ -// ("secure", &peer.is_secured().to_string()), -// ("direct", &direct.to_string()), -// ], -// ); - -// let mut status_timer = time::interval(Duration::new(1, 0)); - -// loop { -// tokio::select! { -// res = peer.next() => { -// if let Some(res) = res { -// match res { -// Err(err) => { -// log::error!("Connection closed: {}", err); -// session.msgbox("error", "Connection Error", &err.to_string()); -// break; -// } -// Ok(ref bytes) => { -// last_recv_time = Instant::now(); -// conn.data_count.fetch_add(bytes.len(), Ordering::Relaxed); -// if !conn.handle_msg_from_peer(bytes, &mut peer).await { -// break -// } -// } -// } -// } else { -// if session.lc.read().unwrap().restarting_remote_device { -// log::info!("Restart remote device"); -// session.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip"); -// } else { -// log::info!("Reset by the peer"); -// session.msgbox("error", "Connection Error", "Reset by the peer"); -// } -// break; -// } -// } -// d = receiver.recv() => { -// if let Some(d) = d { -// if !conn.handle_msg_from_ui(d, &mut peer).await { -// break; -// } -// } -// } -// _ = conn.timer.tick() => { -// if last_recv_time.elapsed() >= SEC30 { -// session.msgbox("error", "Connection Error", "Timeout"); -// break; -// } -// if !conn.read_jobs.is_empty() { -// if let Err(err) = fs::handle_read_jobs(&mut conn.read_jobs, &mut peer).await { -// log::debug!("Connection Error: {}", err); -// break; -// } -// conn.update_jobs_status(); -// } else { -// conn.timer = time::interval_at(Instant::now() + SEC30, SEC30); -// } -// } -// _ = status_timer.tick() => { -// let speed = conn.data_count.swap(0, Ordering::Relaxed); -// let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32); -// let fps = conn.frame_count.swap(0, Ordering::Relaxed) as _; -// conn.session.update_quality_status(QualityStatus { -// speed:Some(speed), -// fps:Some(fps), -// ..Default::default() -// }); -// } -// } -// } -// log::debug!("Exit io_loop of id={}", session.id); -// } -// Err(err) => { -// session.msgbox("error", "Connection Error", &err.to_string()); -// } -// } - -// if let Some(stop) = stop_clipboard { -// stop.send(()).ok(); -// } -// SERVER_KEYBOARD_ENABLED.store(false, Ordering::SeqCst); -// SERVER_CLIPBOARD_ENABLED.store(false, Ordering::SeqCst); -// } - -// /// Handle message from peer. -// /// Return false if the connection should be closed. -// /// -// /// The message is handled by [`Message`], see [`message::Union`] for possible types. -// async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { -// if let Ok(msg_in) = Message::parse_from_bytes(&data) { -// match msg_in.union { -// Some(message::Union::VideoFrame(vf)) => { -// if !self.first_frame { -// self.first_frame = true; -// common::send_opts_after_login(&self.session.lc.read().unwrap(), peer).await; -// } -// let incomming_format = CodecFormat::from(&vf); -// if self.video_format != incomming_format { -// self.video_format = incomming_format.clone(); -// self.session.update_quality_status(QualityStatus { -// codec_format: Some(incomming_format), -// ..Default::default() -// }) -// }; -// if let Ok(true) = self.video_handler.handle_frame(vf) { -// if let Some(stream) = &*self.session.events2ui.read().unwrap() { -// self.frame_count.fetch_add(1, Ordering::Relaxed); -// stream.add(EventToUI::Rgba(ZeroCopyBuffer( -// self.video_handler.rgb.clone(), -// ))); -// } -// } -// } -// Some(message::Union::Hash(hash)) => { -// self.session.handle_hash("", hash, peer).await; -// } -// Some(message::Union::LoginResponse(lr)) => match lr.union { -// Some(login_response::Union::Error(err)) => { -// if !self.session.handle_login_error(&err) { -// return false; -// } -// } -// Some(login_response::Union::PeerInfo(pi)) => { -// self.session.handle_peer_info(pi); -// } -// _ => {} -// }, -// Some(message::Union::Clipboard(cb)) => { -// if !self.session.lc.read().unwrap().disable_clipboard { -// let content = if cb.compress { -// decompress(&cb.content) -// } else { -// cb.content.into() -// }; -// if let Ok(content) = String::from_utf8(content) { -// self.session -// .push_event("clipboard", vec![("content", &content)]); -// } -// } -// } -// Some(message::Union::CursorData(cd)) => { -// let colors = hbb_common::compress::decompress(&cd.colors); -// self.session.push_event( -// "cursor_data", -// vec![ -// ("id", &cd.id.to_string()), -// ("hotx", &cd.hotx.to_string()), -// ("hoty", &cd.hoty.to_string()), -// ("width", &cd.width.to_string()), -// ("height", &cd.height.to_string()), -// ( -// "colors", -// &serde_json::ser::to_string(&colors).unwrap_or("".to_owned()), -// ), -// ], -// ); -// } -// Some(message::Union::CursorId(id)) => { -// self.session -// .push_event("cursor_id", vec![("id", &id.to_string())]); -// } -// Some(message::Union::CursorPosition(cp)) => { -// self.session.push_event( -// "cursor_position", -// vec![("x", &cp.x.to_string()), ("y", &cp.y.to_string())], -// ); -// } -// Some(message::Union::FileResponse(fr)) => { -// match fr.union { -// Some(file_response::Union::Dir(fd)) => { -// let mut entries = fd.entries.to_vec(); -// if self.session.peer_platform() == "Windows" { -// transform_windows_path(&mut entries); -// } -// let id = fd.id; -// self.session.push_event( -// "file_dir", -// vec![("value", &make_fd_to_json(fd)), ("is_local", "false")], -// ); -// if let Some(job) = fs::get_job(id, &mut self.write_jobs) { -// job.set_files(entries); -// } -// } -// Some(file_response::Union::Block(block)) => { -// if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) { -// if let Err(_err) = job.write(block, None).await { -// // to-do: add "skip" for writing job -// } -// self.update_jobs_status(); -// } -// } -// Some(file_response::Union::Done(d)) => { -// if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) { -// job.modify_time(); -// fs::remove_job(d.id, &mut self.write_jobs); -// } -// self.handle_job_status(d.id, d.file_num, None); -// } -// Some(file_response::Union::Error(e)) => { -// self.handle_job_status(e.id, e.file_num, Some(e.error)); -// } -// Some(file_response::Union::Digest(digest)) => { -// if digest.is_upload { -// if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) { -// if let Some(file) = job.files().get(digest.file_num as usize) { -// let read_path = get_string(&job.join(&file.name)); -// let overwrite_strategy = job.default_overwrite_strategy(); -// if let Some(overwrite) = overwrite_strategy { -// let req = FileTransferSendConfirmRequest { -// id: digest.id, -// file_num: digest.file_num, -// union: Some(if overwrite { -// file_transfer_send_confirm_request::Union::OffsetBlk(0) -// } else { -// file_transfer_send_confirm_request::Union::Skip( -// true, -// ) -// }), -// ..Default::default() -// }; -// job.confirm(&req); -// let msg = new_send_confirm(req); -// allow_err!(peer.send(&msg).await); -// } else { -// self.handle_override_file_confirm( -// digest.id, -// digest.file_num, -// read_path, -// true, -// ); -// } -// } -// } -// } else { -// if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) { -// if let Some(file) = job.files().get(digest.file_num as usize) { -// let write_path = get_string(&job.join(&file.name)); -// let overwrite_strategy = job.default_overwrite_strategy(); -// match fs::is_write_need_confirmation(&write_path, &digest) { -// Ok(res) => match res { -// DigestCheckResult::IsSame => { -// let msg= new_send_confirm(FileTransferSendConfirmRequest { -// id: digest.id, -// file_num: digest.file_num, -// union: Some(file_transfer_send_confirm_request::Union::Skip(true)), -// ..Default::default() -// }); -// self.session.send_msg(msg); -// } -// DigestCheckResult::NeedConfirm(digest) => { -// if let Some(overwrite) = overwrite_strategy { -// let msg = new_send_confirm( -// FileTransferSendConfirmRequest { -// id: digest.id, -// file_num: digest.file_num, -// union: Some(if overwrite { -// file_transfer_send_confirm_request::Union::OffsetBlk(0) -// } else { -// file_transfer_send_confirm_request::Union::Skip(true) -// }), -// ..Default::default() -// }, -// ); -// self.session.send_msg(msg); -// } else { -// self.handle_override_file_confirm( -// digest.id, -// digest.file_num, -// write_path.to_string(), -// false, -// ); -// } -// } -// DigestCheckResult::NoSuchFile => { -// let msg = new_send_confirm( -// FileTransferSendConfirmRequest { -// id: digest.id, -// file_num: digest.file_num, -// union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), -// ..Default::default() -// }, -// ); -// self.session.send_msg(msg); -// } -// }, -// Err(err) => { -// println!("error recving digest: {}", err); -// } -// } -// } -// } -// } -// } -// _ => {} -// } -// } -// Some(message::Union::Misc(misc)) => match misc.union { -// Some(misc::Union::AudioFormat(f)) => { -// self.audio_handler.handle_format(f); // -// } -// Some(misc::Union::ChatMessage(c)) => { -// self.session -// .push_event("chat_client_mode", vec![("text", &c.text)]); -// } -// Some(misc::Union::PermissionInfo(p)) => { -// log::info!("Change permission {:?} -> {}", p.permission, p.enabled); -// use permission_info::Permission; -// self.session.push_event( -// "permission", -// vec![( -// match p.permission.enum_value_or_default() { -// Permission::Keyboard => "keyboard", -// Permission::Clipboard => "clipboard", -// Permission::Audio => "audio", -// Permission::Restart => "restart", -// _ => "", -// }, -// &p.enabled.to_string(), -// )], -// ); -// } -// Some(misc::Union::SwitchDisplay(s)) => { -// self.video_handler.reset(); -// self.session.push_event( -// "switch_display", -// vec![ -// ("display", &s.display.to_string()), -// ("x", &s.x.to_string()), -// ("y", &s.y.to_string()), -// ("width", &s.width.to_string()), -// ("height", &s.height.to_string()), -// ], -// ); -// } -// Some(misc::Union::CloseReason(c)) => { -// self.session.msgbox("error", "Connection Error", &c); -// return false; -// } -// Some(misc::Union::BackNotification(notification)) => { -// if !self.handle_back_notification(notification).await { -// return false; -// } -// } -// _ => {} -// }, -// Some(message::Union::TestDelay(t)) => { -// self.session.handle_test_delay(t, peer).await; -// } -// Some(message::Union::AudioFrame(frame)) => { -// if !self.session.lc.read().unwrap().disable_audio { -// self.audio_handler.handle_frame(frame); -// } -// } -// Some(message::Union::FileAction(action)) => match action.union { -// Some(file_action::Union::SendConfirm(c)) => { -// if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) { -// job.confirm(&c); -// } -// } -// _ => {} -// }, -// _ => {} -// } -// } -// true -// } - -// async fn handle_back_notification(&mut self, notification: BackNotification) -> bool { -// match notification.union { -// Some(back_notification::Union::BlockInputState(state)) => { -// self.handle_back_msg_block_input( -// state.enum_value_or(back_notification::BlockInputState::BlkStateUnknown), -// ) -// .await; -// } -// Some(back_notification::Union::PrivacyModeState(state)) => { -// if !self -// .handle_back_msg_privacy_mode( -// state.enum_value_or(back_notification::PrivacyModeState::PrvStateUnknown), -// ) -// .await -// { -// return false; -// } -// } -// _ => {} -// } -// true -// } - -// #[inline(always)] -// fn update_block_input_state(&mut self, on: bool) { -// self.session.push_event( -// "update_block_input_state", -// [("input_state", if on { "on" } else { "off" })].into(), -// ); -// } - -// async fn handle_back_msg_block_input(&mut self, state: back_notification::BlockInputState) { -// match state { -// back_notification::BlockInputState::BlkOnSucceeded => { -// self.update_block_input_state(true); -// } -// back_notification::BlockInputState::BlkOnFailed => { -// self.session -// .msgbox("custom-error", "Block user input", "Failed"); -// self.update_block_input_state(false); -// } -// back_notification::BlockInputState::BlkOffSucceeded => { -// self.update_block_input_state(false); -// } -// back_notification::BlockInputState::BlkOffFailed => { -// self.session -// .msgbox("custom-error", "Unblock user input", "Failed"); -// } -// _ => {} -// } -// } - -// #[inline(always)] -// fn update_privacy_mode(&mut self, on: bool) { -// let mut config = self.session.load_config(); -// config.privacy_mode = on; -// self.session.save_config(&config); -// self.session.lc.write().unwrap().get_config().privacy_mode = on; -// self.session.push_event("update_privacy_mode", [].into()); -// } - -// async fn handle_back_msg_privacy_mode( -// &mut self, -// state: back_notification::PrivacyModeState, -// ) -> bool { -// match state { -// back_notification::PrivacyModeState::PrvOnByOther => { -// self.session.msgbox( -// "error", -// "Connecting...", -// "Someone turns on privacy mode, exit", -// ); -// return false; -// } -// back_notification::PrivacyModeState::PrvNotSupported => { -// self.session -// .msgbox("custom-error", "Privacy mode", "Unsupported"); -// self.update_privacy_mode(false); -// } -// back_notification::PrivacyModeState::PrvOnSucceeded => { -// self.session -// .msgbox("custom-nocancel", "Privacy mode", "In privacy mode"); -// self.update_privacy_mode(true); -// } -// back_notification::PrivacyModeState::PrvOnFailedDenied => { -// self.session -// .msgbox("custom-error", "Privacy mode", "Peer denied"); -// self.update_privacy_mode(false); -// } -// back_notification::PrivacyModeState::PrvOnFailedPlugin => { -// self.session -// .msgbox("custom-error", "Privacy mode", "Please install plugins"); -// self.update_privacy_mode(false); -// } -// back_notification::PrivacyModeState::PrvOnFailed => { -// self.session -// .msgbox("custom-error", "Privacy mode", "Failed"); -// self.update_privacy_mode(false); -// } -// back_notification::PrivacyModeState::PrvOffSucceeded => { -// self.session -// .msgbox("custom-nocancel", "Privacy mode", "Out privacy mode"); -// self.update_privacy_mode(false); -// } -// back_notification::PrivacyModeState::PrvOffByPeer => { -// self.session -// .msgbox("custom-error", "Privacy mode", "Peer exit"); -// self.update_privacy_mode(false); -// } -// back_notification::PrivacyModeState::PrvOffFailed => { -// self.session -// .msgbox("custom-error", "Privacy mode", "Failed to turn off"); -// } -// back_notification::PrivacyModeState::PrvOffUnknown => { -// self.session -// .msgbox("custom-error", "Privacy mode", "Turned off"); -// // log::error!("Privacy mode is turned off with unknown reason"); -// self.update_privacy_mode(false); -// } -// _ => {} -// } -// true -// } - -// async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool { -// match data { -// Data::Close => { -// self.sync_jobs_status_to_local().await; -// return false; -// } -// Data::Login((password, remember)) => { -// self.session -// .handle_login_from_ui(password, remember, peer) -// .await; -// } -// Data::Message(msg) => { -// allow_err!(peer.send(&msg).await); -// } -// Data::SendFiles((id, path, to, file_num, include_hidden, is_remote)) => { -// let od = can_enable_overwrite_detection(self.session.lc.read().unwrap().version); -// if is_remote { -// log::debug!("New job {}, write to {} from remote {}", id, to, path); -// self.write_jobs.push(fs::TransferJob::new_write( -// id, -// path.clone(), -// to, -// file_num, -// include_hidden, -// is_remote, -// Vec::new(), -// od, -// )); -// allow_err!( -// peer.send(&fs::new_send(id, path, file_num, include_hidden)) -// .await -// ); -// } else { -// match fs::TransferJob::new_read( -// id, -// to.clone(), -// path.clone(), -// file_num, -// include_hidden, -// is_remote, -// od, -// ) { -// Err(err) => { -// self.handle_job_status(id, -1, Some(err.to_string())); -// } -// Ok(job) => { -// log::debug!( -// "New job {}, read {} to remote {}, {} files", -// id, -// path, -// to, -// job.files().len() -// ); -// let m = make_fd_flutter(id, job.files(), true); -// self.session -// .push_event("update_folder_files", vec![("info", &m)]); -// let files = job.files().clone(); -// self.read_jobs.push(job); -// self.timer = time::interval(MILLI1); -// allow_err!(peer.send(&fs::new_receive(id, to, file_num, files)).await); -// } -// } -// } -// } -// Data::RemoveDirAll((id, path, is_remote, include_hidden)) => { -// if is_remote { -// let mut msg_out = Message::new(); -// let mut file_action = FileAction::new(); -// file_action.set_all_files(ReadAllFiles { -// id, -// path: path.clone(), -// include_hidden, -// ..Default::default() -// }); -// msg_out.set_file_action(file_action); -// allow_err!(peer.send(&msg_out).await); -// } else { -// match fs::get_recursive_files(&path, include_hidden) { -// Ok(entries) => { -// let mut fd = FileDirectory::new(); -// fd.id = id; -// fd.path = path; -// fd.entries = entries; -// self.session.push_event( -// "file_dir", -// vec![("value", &make_fd_to_json(fd)), ("is_local", "true")], -// ); -// } -// Err(err) => { -// self.handle_job_status(id, -1, Some(err.to_string())); -// } -// } -// } -// } -// Data::CancelJob(id) => { -// let mut msg_out = Message::new(); -// let mut file_action = FileAction::new(); -// file_action.set_cancel(FileTransferCancel { -// id: id, -// ..Default::default() -// }); -// msg_out.set_file_action(file_action); -// allow_err!(peer.send(&msg_out).await); -// if let Some(job) = fs::get_job(id, &mut self.write_jobs) { -// job.remove_download_file(); -// fs::remove_job(id, &mut self.write_jobs); -// } -// fs::remove_job(id, &mut self.read_jobs); -// } -// Data::RemoveDir((id, path)) => { -// let mut msg_out = Message::new(); -// let mut file_action = FileAction::new(); -// file_action.set_remove_dir(FileRemoveDir { -// id, -// path, -// recursive: true, -// ..Default::default() -// }); -// msg_out.set_file_action(file_action); -// allow_err!(peer.send(&msg_out).await); -// } -// Data::RemoveFile((id, path, file_num, is_remote)) => { -// if is_remote { -// let mut msg_out = Message::new(); -// let mut file_action = FileAction::new(); -// file_action.set_remove_file(FileRemoveFile { -// id, -// path, -// file_num, -// ..Default::default() -// }); -// msg_out.set_file_action(file_action); -// allow_err!(peer.send(&msg_out).await); -// } else { -// match fs::remove_file(&path) { -// Err(err) => { -// self.handle_job_status(id, file_num, Some(err.to_string())); -// } -// Ok(()) => { -// self.handle_job_status(id, file_num, None); -// } -// } -// } -// } -// Data::CreateDir((id, path, is_remote)) => { -// if is_remote { -// let mut msg_out = Message::new(); -// let mut file_action = FileAction::new(); -// file_action.set_create(FileDirCreate { -// id, -// path, -// ..Default::default() -// }); -// msg_out.set_file_action(file_action); -// allow_err!(peer.send(&msg_out).await); -// } else { -// match fs::create_dir(&path) { -// Err(err) => { -// self.handle_job_status(id, -1, Some(err.to_string())); -// } -// Ok(()) => { -// self.handle_job_status(id, -1, None); -// } -// } -// } -// } -// Data::SetConfirmOverrideFile((id, file_num, need_override, remember, is_upload)) => { -// if is_upload { -// if let Some(job) = fs::get_job(id, &mut self.read_jobs) { -// if remember { -// job.set_overwrite_strategy(Some(need_override)); -// } -// job.confirm(&FileTransferSendConfirmRequest { -// id, -// file_num, -// union: if need_override { -// Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) -// } else { -// Some(file_transfer_send_confirm_request::Union::Skip(true)) -// }, -// ..Default::default() -// }); -// } -// } else { -// if let Some(job) = fs::get_job(id, &mut self.write_jobs) { -// if remember { -// job.set_overwrite_strategy(Some(need_override)); -// } -// let mut msg = Message::new(); -// let mut file_action = FileAction::new(); -// file_action.set_send_confirm(FileTransferSendConfirmRequest { -// id, -// file_num, -// union: if need_override { -// Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) -// } else { -// Some(file_transfer_send_confirm_request::Union::Skip(true)) -// }, -// ..Default::default() -// }); -// msg.set_file_action(file_action); -// self.session.send_msg(msg); -// } -// } -// } -// Data::AddJob((id, path, to, file_num, include_hidden, is_remote)) => { -// let od = can_enable_overwrite_detection(self.session.lc.read().unwrap().version); -// if is_remote { -// log::debug!( -// "new write waiting job {}, write to {} from remote {}", -// id, -// to, -// path -// ); -// let mut job = fs::TransferJob::new_write( -// id, -// path.clone(), -// to, -// file_num, -// include_hidden, -// is_remote, -// Vec::new(), -// od, -// ); -// job.is_last_job = true; -// self.write_jobs.push(job); -// } else { -// match fs::TransferJob::new_read( -// id, -// to.clone(), -// path.clone(), -// file_num, -// include_hidden, -// is_remote, -// od, -// ) { -// Err(err) => { -// self.handle_job_status(id, -1, Some(err.to_string())); -// } -// Ok(mut job) => { -// log::debug!( -// "new read waiting job {}, read {} to remote {}, {} files", -// id, -// path, -// to, -// job.files().len() -// ); -// let m = make_fd_flutter(job.id(), job.files(), true); -// self.session -// .push_event("update_folder_files", vec![("info", &m)]); -// job.is_last_job = true; -// self.read_jobs.push(job); -// self.timer = time::interval(MILLI1); -// } -// } -// } -// } -// Data::ResumeJob((id, is_remote)) => { -// if is_remote { -// if let Some(job) = get_job(id, &mut self.write_jobs) { -// job.is_last_job = false; -// allow_err!( -// peer.send(&fs::new_send( -// id, -// job.remote.clone(), -// job.file_num, -// job.show_hidden -// )) -// .await -// ); -// } -// } else { -// if let Some(job) = get_job(id, &mut self.read_jobs) { -// job.is_last_job = false; -// allow_err!( -// peer.send(&fs::new_receive( -// id, -// job.path.to_string_lossy().to_string(), -// job.file_num, -// job.files.clone() -// )) -// .await -// ); -// } -// } -// } -// _ => {} -// } -// true -// } - -// #[inline] -// fn update_job_status( -// job: &fs::TransferJob, -// elapsed: i32, -// last_update_jobs_status: &mut (Instant, HashMap), -// session: &Session, -// ) { -// if elapsed <= 0 { -// return; -// } -// let transferred = job.transferred(); -// let last_transferred = { -// if let Some(v) = last_update_jobs_status.1.get(&job.id()) { -// v.to_owned() -// } else { -// 0 -// } -// }; -// last_update_jobs_status.1.insert(job.id(), transferred); -// let speed = (transferred - last_transferred) as f64 / (elapsed as f64 / 1000.); -// let file_num = job.file_num() - 1; -// session.push_event( -// "job_progress", -// vec![ -// ("id", &job.id().to_string()), -// ("file_num", &file_num.to_string()), -// ("speed", &speed.to_string()), -// ("finished_size", &job.finished_size().to_string()), -// ], -// ); -// } - -// fn update_jobs_status(&mut self) { -// let elapsed = self.last_update_jobs_status.0.elapsed().as_millis() as i32; -// if elapsed >= 1000 { -// for job in self.read_jobs.iter() { -// Self::update_job_status( -// job, -// elapsed, -// &mut self.last_update_jobs_status, -// &self.session, -// ); -// } -// for job in self.write_jobs.iter() { -// Self::update_job_status( -// job, -// elapsed, -// &mut self.last_update_jobs_status, -// &self.session, -// ); -// } -// self.last_update_jobs_status.0 = Instant::now(); -// } -// } - -// fn handle_job_status(&mut self, id: i32, file_num: i32, err: Option) { -// if let Some(err) = err { -// self.session -// .push_event("job_error", vec![("id", &id.to_string()), ("err", &err)]); -// } else { -// self.session.push_event( -// "job_done", -// vec![("id", &id.to_string()), ("file_num", &file_num.to_string())], -// ); -// } -// } - -// fn handle_override_file_confirm( -// &mut self, -// id: i32, -// file_num: i32, -// read_path: String, -// is_upload: bool, -// ) { -// self.session.push_event( -// "override_file_confirm", -// vec![ -// ("id", &id.to_string()), -// ("file_num", &file_num.to_string()), -// ("read_path", &read_path), -// ("is_upload", &is_upload.to_string()), -// ], -// ); -// } - -// async fn sync_jobs_status_to_local(&mut self) -> bool { -// log::info!("sync transfer job status"); -// let mut config: PeerConfig = self.session.load_config(); -// let mut transfer_metas = TransferSerde::default(); -// for job in self.read_jobs.iter() { -// let json_str = serde_json::to_string(&job.gen_meta()).unwrap(); -// transfer_metas.read_jobs.push(json_str); -// } -// for job in self.write_jobs.iter() { -// let json_str = serde_json::to_string(&job.gen_meta()).unwrap(); -// transfer_metas.write_jobs.push(json_str); -// } -// log::info!("meta: {:?}", transfer_metas); -// config.transfer = transfer_metas; -// self.session.save_config(&config); -// true -// } -// } - // Server Side -// TODO connection_manager need use struct and trait,impl default method #[cfg(not(any(target_os = "ios")))] pub mod connection_manager { use std::{ diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 9e8c8fc51..2d412ea9b 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -2,8 +2,8 @@ use std::{ collections::HashMap, ops::{Deref, DerefMut}, sync::{ - atomic::{AtomicBool, AtomicUsize, Ordering}, - Arc, Mutex, RwLock, + atomic::{AtomicBool, Ordering}, + Arc, Mutex, }, }; @@ -22,35 +22,15 @@ use clipboard::{ cliprdr::CliprdrClientContext, create_cliprdr_context as create_clipboard_file_context, get_rx_clip_client, server_clip_file, }; -use enigo::{self, Enigo, KeyboardControllable}; -use hbb_common::{ - allow_err, - config::{Config, LocalConfig, PeerConfig, TransferSerde}, - fs::{ - self, can_enable_overwrite_detection, get_job, get_string, new_send_confirm, - DigestCheckResult, RemoveJobMeta, TransferJobMeta, - }, - get_version_number, log, - message_proto::{permission_info::Permission, *}, - protobuf::Message as _, - rendezvous_proto::ConnType, - sleep, - tokio::{ - self, - sync::mpsc, - time::{self, Duration, Instant, Interval}, - }, - Stream, -}; +use enigo::{self}; +use hbb_common::{allow_err, log, message_proto::*}; #[cfg(windows)] use crate::clipboard_file::*; use crate::{ client::*, - common::{self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL}, - ui_session_interface::{io_loop, InvokeUi, Remote, Session, SERVER_KEYBOARD_ENABLED}, + ui_session_interface::{InvokeUi, Session}, }; -use errno; type Video = AssetPtr; @@ -131,7 +111,7 @@ impl InvokeUi for SciterHandler { self.call2("setPermission", &make_args!(name, value)); } - fn update_pi(&self, pi: PeerInfo) {} + fn update_pi(&self, pi: PeerInfo) {} // TODO dup flutter fn close_success(&self) { self.call2("closeSuccess", &make_args!()); @@ -165,15 +145,15 @@ impl InvokeUi for SciterHandler { } fn job_error(&self, id: i32, err: String, file_num: i32) { - todo!() + self.call("jobError", &make_args!(id, err, file_num)); } fn job_done(&self, id: i32, file_num: i32) { - todo!() + self.call("jobDone", &make_args!(id, file_num)); } fn clear_all_jobs(&self) { - todo!() + self.call("clearAllJobs", &make_args!()); } fn add_job( @@ -189,19 +169,25 @@ impl InvokeUi for SciterHandler { } fn update_transfer_list(&self) { - todo!() + self.call("updateTransferList", &make_args!()); } fn confirm_delete_files(&self, id: i32, i: i32, name: String) { - todo!() + self.call("confirmDeleteFiles", &make_args!(id, i, name)); } fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool) { - todo!() + self.call( + "overrideFileConfirm", + &make_args!(id, file_num, to, is_upload), + ); } fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64) { - todo!() + self.call( + "jobProgress", + &make_args!(id, file_num, speed, finished_size), + ); } fn adapt_size(&self) { @@ -227,11 +213,22 @@ impl InvokeUi for SciterHandler { current_display: usize, is_file_transfer: bool, ) { - todo!() } fn msgbox(&self, msgtype: &str, title: &str, text: &str, retry: bool) { - todo!() + self.call2("msgbox_retry", &make_args!(msgtype, title, text, retry)); + } + + fn new_message(&self, msg: String) { + self.call("newMessage", &make_args!(msg)); + } + + fn switch_display(&self, display: &SwitchDisplay) { + self.call("switchDisplay", &make_args!(display.display)); + } + + fn update_block_input_state(&self, on: bool) { + self.call("updateBlockInputState", &make_args!(on)); } } @@ -405,21 +402,7 @@ impl SciterSession { Self(session) } - // fn update_quality_status(&self, status: QualityStatus) { - // self.call2( - // "updateQualityStatus", - // &make_args!( - // status.speed.map_or(Value::null(), |it| it.into()), - // status.fps.map_or(Value::null(), |it| it.into()), - // status.delay.map_or(Value::null(), |it| it.into()), - // status.target_bitrate.map_or(Value::null(), |it| it.into()), - // status - // .codec_format - // .map_or(Value::null(), |it| it.to_string().into()) - // ), - // ); - // } - + // TODO fn start_keyboard_hook(&'static self) { if self.is_port_forward() || self.is_file_transfer() { return; @@ -652,14 +635,6 @@ impl SciterSession { }); } - // fn get_view_style(&mut self) -> String { - // return self.lc.read().unwrap().view_style.clone(); - // } - - // fn get_image_quality(&mut self) -> String { - // return self.lc.read().unwrap().image_quality.clone(); - // } - // TODO fn get_custom_image_quality(&mut self) -> Value { let mut v = Value::array(0); @@ -669,87 +644,6 @@ impl SciterSession { v } - // #[inline] - // pub(super) fn save_config(&self, config: PeerConfig) { - // self.lc.write().unwrap().save_config(config); - // } - - // fn save_view_style(&mut self, value: String) { - // self.lc.write().unwrap().save_view_style(value); - // } - - // #[inline] - // pub(super) fn load_config(&self) -> PeerConfig { - // load_config(&self.id) - // } - - // fn toggle_option(&mut self, name: String) { - // let msg = self.lc.write().unwrap().toggle_option(name.clone()); - // if name == "enable-file-transfer" { - // self.send(Data::ToggleClipboardFile); - // } - // if let Some(msg) = msg { - // self.send(Data::Message(msg)); - // } - // } - - // fn get_toggle_option(&mut self, name: String) -> bool { - // self.lc.read().unwrap().get_toggle_option(&name) - // } - - // fn is_privacy_mode_supported(&self) -> bool { - // self.lc.read().unwrap().is_privacy_mode_supported() - // } - - // fn refresh_video(&mut self) { - // self.send(Data::Message(LoginConfigHandler::refresh())); - // } - - // fn save_custom_image_quality(&mut self, custom_image_quality: i32) { - // let msg = self - // .lc - // .write() - // .unwrap() - // .save_custom_image_quality(custom_image_quality); - // self.send(Data::Message(msg)); - // } - - // fn save_image_quality(&mut self, value: String) { - // let msg = self.lc.write().unwrap().save_image_quality(value); - // if let Some(msg) = msg { - // self.send(Data::Message(msg)); - // } - // } - - // fn get_remember(&mut self) -> bool { - // self.lc.read().unwrap().remember - // } - - // fn set_write_override( - // &mut self, - // job_id: i32, - // file_num: i32, - // is_override: bool, - // remember: bool, - // is_upload: bool, - // ) -> bool { - // self.send(Data::SetConfirmOverrideFile(( - // job_id, - // file_num, - // is_override, - // remember, - // is_upload, - // ))); - // true - // } - - // fn has_hwcodec(&self) -> bool { - // #[cfg(not(feature = "hwcodec"))] - // return false; - // #[cfg(feature = "hwcodec")] - // return true; - // } - // TODO fn supported_hwcodec(&self) -> Value { #[cfg(feature = "hwcodec")] @@ -775,51 +669,6 @@ impl SciterSession { } } - // fn change_prefer_codec(&self) { - // let msg = self.lc.write().unwrap().change_prefer_codec(); - // self.send(Data::Message(msg)); - // } - - // fn restart_remote_device(&mut self) { - // let mut lc = self.lc.write().unwrap(); - // lc.restarting_remote_device = true; - // let msg = lc.restart_remote_device(); - // self.send(Data::Message(msg)); - // } - - // pub fn is_restarting_remote_device(&self) -> bool { - // self.lc.read().unwrap().restarting_remote_device - // } - - // fn t(&self, name: String) -> String { - // crate::client::translate(name) - // } - - // fn get_audit_server(&self) -> String { - // if self.lc.read().unwrap().conn_id <= 0 - // || LocalConfig::get_option("access_token").is_empty() - // { - // return "".to_owned(); - // } - // crate::get_audit_server( - // Config::get_option("api-server"), - // Config::get_option("custom-rendezvous-server"), - // ) - // } - - // fn send_note(&self, note: String) { - // let url = self.get_audit_server(); - // let id = self.id.clone(); - // let conn_id = self.lc.read().unwrap().conn_id; - // std::thread::spawn(move || { - // send_note(url, id, conn_id, note); - // }); - // } - - // fn is_xfce(&self) -> bool { - // crate::platform::is_xfce() - // } - // TODO fn save_size(&mut self, x: i32, y: i32, w: i32, h: i32) { let size = (x, y, w, h); @@ -881,34 +730,6 @@ impl SciterSession { v } - // fn remove_port_forward(&mut self, port: i32) { - // let mut config = self.load_config(); - // config.port_forwards = config - // .port_forwards - // .drain(..) - // .filter(|x| x.0 != port) - // .collect(); - // self.save_config(config); - // self.send(Data::RemovePortForward(port)); - // } - - // fn add_port_forward(&mut self, port: i32, remote_host: String, remote_port: i32) { - // let mut config = self.load_config(); - // if config - // .port_forwards - // .iter() - // .filter(|x| x.0 == port) - // .next() - // .is_some() - // { - // return; - // } - // let pf = (port, remote_host, remote_port); - // config.port_forwards.push(pf.clone()); - // self.save_config(config); - // self.send(Data::AddPortForward(pf)); - // } - fn get_size(&mut self) -> Value { let s = if self.is_file_transfer() { self.lc.read().unwrap().size_ft @@ -925,10 +746,6 @@ impl SciterSession { v } - // fn get_id(&mut self) -> String { - // self.id.clone() - // } - fn get_default_pi(&mut self) -> Value { let mut pi = Value::map(); let info = self.lc.read().unwrap().info.clone(); @@ -938,158 +755,11 @@ impl SciterSession { pi } - // fn get_option(&self, k: String) -> String { - // self.lc.read().unwrap().get_option(&k) - // } - - // fn set_option(&self, k: String, v: String) { - // self.lc.write().unwrap().set_option(k, v); - // } - - // fn input_os_password(&mut self, pass: String, activate: bool) { - // input_os_password(pass, activate, self.clone()); - // } - // close_state sciter only fn save_close_state(&mut self, k: String, v: String) { self.close_state.insert(k, v); } - // fn get_chatbox(&mut self) -> String { - // #[cfg(feature = "inline")] - // return super::inline::get_chatbox(); - // #[cfg(not(feature = "inline"))] - // return "".to_owned(); - // } - - // fn get_icon(&mut self) -> String { - // crate::get_icon() - // } - - // fn send_chat(&mut self, text: String) { - // let mut misc = Misc::new(); - // misc.set_chat_message(ChatMessage { - // text, - // ..Default::default() - // }); - // let mut msg_out = Message::new(); - // msg_out.set_misc(misc); - // self.send(Data::Message(msg_out)); - // } - - // fn switch_display(&mut self, display: i32) { - // let mut misc = Misc::new(); - // misc.set_switch_display(SwitchDisplay { - // display, - // ..Default::default() - // }); - // let mut msg_out = Message::new(); - // msg_out.set_misc(misc); - // self.send(Data::Message(msg_out)); - // } - - // fn is_file_transfer(&self) -> bool { - // self.cmd == "--file-transfer" - // } - - // fn is_port_forward(&self) -> bool { - // self.cmd == "--port-forward" || self.is_rdp() - // } - - // fn is_rdp(&self) -> bool { - // self.cmd == "--rdp" - // } - - // fn reconnect(&mut self) { - // println!("reconnecting"); - // let cloned = self.clone(); - // let mut lock = self.thread.lock().unwrap(); - // lock.take().map(|t| t.join()); - // *lock = Some(std::thread::spawn(move || { - // io_loop(cloned); - // })); - // } - - // #[inline] - // fn peer_platform(&self) -> String { - // self.lc.read().unwrap().info.platform.clone() - // } - - // fn get_platform(&mut self, is_remote: bool) -> String { - // if is_remote { - // self.peer_platform() - // } else { - // whoami::platform().to_string() - // } - // } - - // fn get_path_sep(&mut self, is_remote: bool) -> &'static str { - // let p = self.get_platform(is_remote); - // if &p == "Windows" { - // return "\\"; - // } else { - // return "/"; - // } - // } - - // fn get_icon_path(&mut self, file_type: i32, ext: String) -> String { - // let mut path = Config::icon_path(); - // if file_type == FileType::DirLink as i32 { - // let new_path = path.join("dir_link"); - // if !std::fs::metadata(&new_path).is_ok() { - // #[cfg(windows)] - // allow_err!(std::os::windows::fs::symlink_file(&path, &new_path)); - // #[cfg(not(windows))] - // allow_err!(std::os::unix::fs::symlink(&path, &new_path)); - // } - // path = new_path; - // } else if file_type == FileType::File as i32 { - // if !ext.is_empty() { - // path = path.join(format!("file.{}", ext)); - // } else { - // path = path.join("file"); - // } - // if !std::fs::metadata(&path).is_ok() { - // allow_err!(std::fs::File::create(&path)); - // } - // } else if file_type == FileType::FileLink as i32 { - // let new_path = path.join("file_link"); - // if !std::fs::metadata(&new_path).is_ok() { - // path = path.join("file"); - // if !std::fs::metadata(&path).is_ok() { - // allow_err!(std::fs::File::create(&path)); - // } - // #[cfg(windows)] - // allow_err!(std::os::windows::fs::symlink_file(&path, &new_path)); - // #[cfg(not(windows))] - // allow_err!(std::os::unix::fs::symlink(&path, &new_path)); - // } - // path = new_path; - // } else if file_type == FileType::DirDrive as i32 { - // if cfg!(windows) { - // path = fs::get_path("C:"); - // } else if cfg!(target_os = "macos") { - // if let Ok(entries) = fs::get_path("/Volumes/").read_dir() { - // for entry in entries { - // if let Ok(entry) = entry { - // path = entry.path(); - // break; - // } - // } - // } - // } - // } - // fs::get_string(&path) - // } - - // fn login(&mut self, password: String, remember: bool) { - // self.send(Data::Login((password, remember))); - // } - - // fn new_rdp(&mut self) { - // self.send(Data::NewRDP); - // } - fn enter(&mut self) { #[cfg(windows)] crate::platform::windows::stop_system_key_propagate(true); @@ -1102,30 +772,6 @@ impl SciterSession { IS_IN.store(false, Ordering::SeqCst); } - // TODO - fn set_cursor_data(&mut self, cd: CursorData) { - let mut colors = hbb_common::compress::decompress(&cd.colors); - if colors.iter().filter(|x| **x != 0).next().is_none() { - log::info!("Fix transparent"); - // somehow all 0 images shows black rect, here is a workaround - colors[3] = 1; - } - let mut png = Vec::new(); - if let Ok(()) = repng::encode(&mut png, cd.width as _, cd.height as _, &colors) { - self.call( - "setCursorData", - &make_args!( - cd.id.to_string(), - cd.hotx, - cd.hoty, - cd.width, - cd.height, - &png[..] - ), - ); - } - } - fn get_key_event(&self, down_or_up: i32, name: &str, code: i32) -> Option { let mut key_event = KeyEvent::new(); if down_or_up == 2 { @@ -1260,21 +906,6 @@ impl SciterSession { log::error!("Failed to spawn IP tunneling: {}", err); } } - - // #[inline] - // fn set_cursor_id(&mut self, id: String) { - // self.call("setCursorId", &make_args!(id)); - // } - - // #[inline] - // fn set_cursor_position(&mut self, cd: CursorPosition) { - // self.call("setCursorPosition", &make_args!(cd.x, cd.y)); - // } - - // #[inline] - // fn set_display(&self, x: i32, y: i32, w: i32, h: i32) { - // self.call("setDisplay", &make_args!(x, y, w, h)); - // } } pub fn make_fd(id: i32, entries: &Vec, only_count: bool) -> Value { diff --git a/src/ui_session_interface.rs b/src/ui_session_interface.rs index 03666ed92..a164a2d94 100644 --- a/src/ui_session_interface.rs +++ b/src/ui_session_interface.rs @@ -1,33 +1,23 @@ +use crate::client::io_loop::Remote; use crate::client::{ - self, check_if_retry, get_key_state, handle_hash, handle_login_from_ui, handle_test_delay, - input_os_password, load_config, send_mouse, start_video_audio_threads, Client, CodecFormat, - FileManager, Key, LoginConfigHandler, MediaData, MediaSender, QualityStatus, KEY_MAP, SEC30, + check_if_retry, get_key_state, handle_hash, handle_login_from_ui, handle_test_delay, + input_os_password, load_config, send_mouse, start_video_audio_threads, FileManager, Key, + LoginConfigHandler, QualityStatus, KEY_MAP, }; -use crate::common::{ - self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL, -}; -use crate::platform; +use crate::common; + use crate::{client::Data, client::Interface}; use async_trait::async_trait; -use enigo::{Enigo, KeyboardControllable}; -use hbb_common::config::{Config, LocalConfig, PeerConfig, TransferSerde}; -use hbb_common::fs::{ - can_enable_overwrite_detection, get_job, get_string, new_send_confirm, DigestCheckResult, - RemoveJobMeta, TransferJobMeta, -}; -use hbb_common::message_proto::permission_info::Permission; -use hbb_common::protobuf::Message as _; -use hbb_common::rendezvous_proto::ConnType; -use hbb_common::tokio::{ - self, - sync::mpsc, - time::{self, Duration, Instant, Interval}, -}; -use hbb_common::{allow_err, message_proto::*, sleep}; + +use hbb_common::config::{Config, LocalConfig, PeerConfig}; + +use hbb_common::tokio::{self, sync::mpsc}; + +use hbb_common::{allow_err, message_proto::*}; use hbb_common::{fs, get_version_number, log, Stream}; use std::collections::HashMap; use std::ops::{Deref, DerefMut}; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, RwLock}; #[derive(Clone, Default)] @@ -541,6 +531,7 @@ pub trait InvokeUi: Send + Sync + Clone + 'static + Sized + Default { fn set_cursor_id(&self, id: String); fn set_cursor_position(&self, cp: CursorPosition); fn set_display(&self, x: i32, y: i32, w: i32, h: i32); + fn switch_display(&self, display: &SwitchDisplay); fn set_peer_info( &self, username: &str, @@ -570,14 +561,18 @@ pub trait InvokeUi: Send + Sync + Clone + 'static + Sized + Default { show_hidden: bool, is_remote: bool, ); + fn new_message(&self, msg: String); fn update_transfer_list(&self); - // fn update_folder_files(&self); // TODO + // fn update_folder_files(&self); // TODO flutter with file_dir and update_folder_files fn confirm_delete_files(&self, id: i32, i: i32, name: String); fn override_file_confirm(&self, id: i32, file_num: i32, to: String, is_upload: bool); + fn update_block_input_state(&self, on: bool); fn job_progress(&self, id: i32, file_num: i32, speed: f64, finished_size: f64); fn adapt_size(&self); fn on_rgba(&self, data: &[u8]); fn msgbox(&self, msgtype: &str, title: &str, text: &str, retry: bool); + #[cfg(any(target_os = "android", target_os = "ios"))] + fn clipboard(&self, content: String); } impl Deref for Session { @@ -604,21 +599,23 @@ impl Interface for Session { } } + // TODO flutter fn is_file_transfer(&self) -> bool { self.cmd == "--file-transfer" } + // TODO flutter fn is_port_forward(&self) -> bool { self.cmd == "--port-forward" || self.is_rdp() } + // TODO flutter fn is_rdp(&self) -> bool { self.cmd == "--rdp" } fn msgbox(&self, msgtype: &str, title: &str, text: &str) { let retry = check_if_retry(msgtype, title, text); - // self.call2("msgbox_retry", &make_args!(msgtype, title, text, retry)); self.ui_handler.msgbox(msgtype, title, text, retry); } @@ -916,1217 +913,6 @@ async fn start_one_port_forward( log::info!("port forward (:{}) exit", port); } -pub static SERVER_KEYBOARD_ENABLED: AtomicBool = AtomicBool::new(true); -pub static SERVER_FILE_TRANSFER_ENABLED: AtomicBool = AtomicBool::new(true); -pub static SERVER_CLIPBOARD_ENABLED: AtomicBool = AtomicBool::new(true); -const MILLI1: Duration = Duration::from_millis(1); - -pub struct Remote { - handler: Session, - video_sender: MediaSender, - audio_sender: MediaSender, - receiver: mpsc::UnboundedReceiver, - sender: mpsc::UnboundedSender, - old_clipboard: Arc>, - read_jobs: Vec, - write_jobs: Vec, - remove_jobs: HashMap, - timer: Interval, - last_update_jobs_status: (Instant, HashMap), - first_frame: bool, - #[cfg(windows)] - clipboard_file_context: Option>, - data_count: Arc, - frame_count: Arc, - video_format: CodecFormat, -} - -impl Remote { - pub fn new( - handler: Session, - video_sender: MediaSender, - audio_sender: MediaSender, - receiver: mpsc::UnboundedReceiver, - sender: mpsc::UnboundedSender, - frame_count: Arc, - ) -> Self { - Self { - handler, - video_sender, - audio_sender, - receiver, - sender, - old_clipboard: Default::default(), - read_jobs: Vec::new(), - write_jobs: Vec::new(), - remove_jobs: Default::default(), - timer: time::interval(SEC30), - last_update_jobs_status: (Instant::now(), Default::default()), - first_frame: false, - #[cfg(windows)] - clipboard_file_context: None, - data_count: Arc::new(AtomicUsize::new(0)), - frame_count, - video_format: CodecFormat::Unknown, - } - } - - pub async fn io_loop(&mut self, key: &str, token: &str) { - let stop_clipboard = self.start_clipboard(); - let mut last_recv_time = Instant::now(); - let mut received = false; - let conn_type = if self.handler.is_file_transfer() { - ConnType::FILE_TRANSFER - } else { - ConnType::default() - }; - match Client::start( - &self.handler.id, - key, - token, - conn_type, - self.handler.clone(), - ) - .await - { - Ok((mut peer, direct)) => { - SERVER_KEYBOARD_ENABLED.store(true, Ordering::SeqCst); - SERVER_CLIPBOARD_ENABLED.store(true, Ordering::SeqCst); - SERVER_FILE_TRANSFER_ENABLED.store(true, Ordering::SeqCst); - // self.handler - // .call("setConnectionType", &make_args!(peer.is_secured(), direct)); - self.handler.set_connection_type(peer.is_secured(), direct); // flutter -> connection_ready - - // just build for now - #[cfg(not(windows))] - let (_tx_holder, mut rx_clip_client) = mpsc::unbounded_channel::(); - #[cfg(windows)] - let mut rx_clip_client = get_rx_clip_client().lock().await; - - let mut status_timer = time::interval(Duration::new(1, 0)); - - loop { - tokio::select! { - res = peer.next() => { - if let Some(res) = res { - match res { - Err(err) => { - log::error!("Connection closed: {}", err); - self.handler.set_force_relay(direct, received); - self.handler.msgbox("error", "Connection Error", &err.to_string()); - break; - } - Ok(ref bytes) => { - last_recv_time = Instant::now(); - received = true; - self.data_count.fetch_add(bytes.len(), Ordering::Relaxed); - if !self.handle_msg_from_peer(bytes, &mut peer).await { - break - } - } - } - } else { - if self.handler.is_restarting_remote_device() { - log::info!("Restart remote device"); - self.handler.msgbox("restarting", "Restarting Remote Device", "remote_restarting_tip"); - } else { - log::info!("Reset by the peer"); - self.handler.msgbox("error", "Connection Error", "Reset by the peer"); - } - break; - } - } - d = self.receiver.recv() => { - if let Some(d) = d { - if !self.handle_msg_from_ui(d, &mut peer).await { - break; - } - } - } - _msg = rx_clip_client.recv() => { - #[cfg(windows)] - match _msg { - Some((_, clip)) => { - allow_err!(peer.send(&clip_2_msg(clip)).await); - } - None => { - // unreachable!() - } - } - } - _ = self.timer.tick() => { - if last_recv_time.elapsed() >= SEC30 { - self.handler.msgbox("error", "Connection Error", "Timeout"); - break; - } - if !self.read_jobs.is_empty() { - if let Err(err) = fs::handle_read_jobs(&mut self.read_jobs, &mut peer).await { - self.handler.msgbox("error", "Connection Error", &err.to_string()); - break; - } - self.update_jobs_status(); - } else { - self.timer = time::interval_at(Instant::now() + SEC30, SEC30); - } - } - _ = status_timer.tick() => { - let speed = self.data_count.swap(0, Ordering::Relaxed); - let speed = format!("{:.2}kB/s", speed as f32 / 1024 as f32); - let fps = self.frame_count.swap(0, Ordering::Relaxed) as _; - self.handler.update_quality_status(QualityStatus { - speed:Some(speed), - fps:Some(fps), - ..Default::default() - }); - } - } - } - log::debug!("Exit io_loop of id={}", self.handler.id); - } - Err(err) => { - self.handler - .msgbox("error", "Connection Error", &err.to_string()); - } - } - if let Some(stop) = stop_clipboard { - stop.send(()).ok(); - } - SERVER_KEYBOARD_ENABLED.store(false, Ordering::SeqCst); - SERVER_CLIPBOARD_ENABLED.store(false, Ordering::SeqCst); - SERVER_FILE_TRANSFER_ENABLED.store(false, Ordering::SeqCst); - } - - fn handle_job_status(&mut self, id: i32, file_num: i32, err: Option) { - if let Some(job) = self.remove_jobs.get_mut(&id) { - if job.no_confirm { - let file_num = (file_num + 1) as usize; - if file_num < job.files.len() { - let path = format!("{}{}{}", job.path, job.sep, job.files[file_num].name); - self.sender - .send(Data::RemoveFile((id, path, file_num as i32, job.is_remote))) - .ok(); - let elapsed = job.last_update_job_status.elapsed().as_millis() as i32; - if elapsed >= 1000 { - job.last_update_job_status = Instant::now(); - } else { - return; - } - } else { - self.remove_jobs.remove(&id); - } - } - } - if let Some(err) = err { - // self.handler - // .call("jobError", &make_args!(id, err, file_num)); - self.handler.job_error(id, err, file_num); - } else { - // self.handler.call("jobDone", &make_args!(id, file_num)); - self.handler.job_done(id, file_num); - } - } - - fn start_clipboard(&mut self) -> Option> { - if self.handler.is_file_transfer() || self.handler.is_port_forward() { - return None; - } - let (tx, rx) = std::sync::mpsc::channel(); - let old_clipboard = self.old_clipboard.clone(); - let tx_protobuf = self.sender.clone(); - let lc = self.handler.lc.clone(); - match ClipboardContext::new() { - Ok(mut ctx) => { - // ignore clipboard update before service start - check_clipboard(&mut ctx, Some(&old_clipboard)); - std::thread::spawn(move || loop { - std::thread::sleep(Duration::from_millis(CLIPBOARD_INTERVAL)); - match rx.try_recv() { - Ok(_) | Err(std::sync::mpsc::TryRecvError::Disconnected) => { - log::debug!("Exit clipboard service of client"); - break; - } - _ => {} - } - if !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) - || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) - || lc.read().unwrap().disable_clipboard - { - continue; - } - if let Some(msg) = check_clipboard(&mut ctx, Some(&old_clipboard)) { - tx_protobuf.send(Data::Message(msg)).ok(); - } - }); - } - Err(err) => { - log::error!("Failed to start clipboard service of client: {}", err); - } - } - Some(tx) - } - - fn load_last_jobs(&mut self) { - log::info!("start load last jobs"); - // self.handler.call("clearAllJobs", &make_args!()); - self.handler.clear_all_jobs(); - let pc = self.handler.load_config(); - if pc.transfer.write_jobs.is_empty() && pc.transfer.read_jobs.is_empty() { - // no last jobs - return; - } - // TODO: can add a confirm dialog - let mut cnt = 1; - for job_str in pc.transfer.read_jobs.iter() { - let job: Result = serde_json::from_str(&job_str); - if let Ok(job) = job { - self.handler.add_job( - cnt, - job.to.clone(), - job.remote.clone(), - job.file_num, - job.show_hidden, - false, - ); - cnt += 1; - println!("restore read_job: {:?}", job); - } - } - for job_str in pc.transfer.write_jobs.iter() { - let job: Result = serde_json::from_str(&job_str); - if let Ok(job) = job { - self.handler.add_job( - cnt, - job.remote.clone(), - job.to.clone(), - job.file_num, - job.show_hidden, - true, - ); - cnt += 1; - println!("restore write_job: {:?}", job); - } - } - // self.handler.call("updateTransferList", &make_args!()); - self.handler.update_transfer_list(); - } - - async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool { - 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)) => { - self.handler - .handle_login_from_ui(password, remember, peer) - .await; - } - Data::ToggleClipboardFile => { - self.check_clipboard_file_context(); - } - Data::Message(msg) => { - allow_err!(peer.send(&msg).await); - } - Data::SendFiles((id, path, to, file_num, include_hidden, is_remote)) => { - log::info!("send files, is remote {}", is_remote); - let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version); - if is_remote { - log::debug!("New job {}, write to {} from remote {}", id, to, path); - self.write_jobs.push(fs::TransferJob::new_write( - id, - path.clone(), - to, - file_num, - include_hidden, - is_remote, - Vec::new(), - od, - )); - allow_err!( - peer.send(&fs::new_send(id, path, file_num, include_hidden)) - .await - ); - } else { - match fs::TransferJob::new_read( - id, - to.clone(), - path.clone(), - file_num, - include_hidden, - is_remote, - od, - ) { - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - Ok(job) => { - log::debug!( - "New job {}, read {} to remote {}, {} files", - id, - path, - to, - job.files().len() - ); - // let m = make_fd(job.id(), job.files(), true); - // self.handler.call("updateFolderFiles", &make_args!(m)); // TODO - #[cfg(not(windows))] - let files = job.files().clone(); - #[cfg(windows)] - let mut files = job.files().clone(); - #[cfg(windows)] - if self.handler.peer_platform() != "Windows" { - // peer is not windows, need transform \ to / - fs::transform_windows_path(&mut files); - } - self.read_jobs.push(job); - self.timer = time::interval(MILLI1); - allow_err!(peer.send(&fs::new_receive(id, to, file_num, files)).await); - } - } - } - } - Data::AddJob((id, path, to, file_num, include_hidden, is_remote)) => { - let od = can_enable_overwrite_detection(self.handler.lc.read().unwrap().version); - if is_remote { - log::debug!( - "new write waiting job {}, write to {} from remote {}", - id, - to, - path - ); - let mut job = fs::TransferJob::new_write( - id, - path.clone(), - to, - file_num, - include_hidden, - is_remote, - Vec::new(), - od, - ); - job.is_last_job = true; - self.write_jobs.push(job); - } else { - match fs::TransferJob::new_read( - id, - to.clone(), - path.clone(), - file_num, - include_hidden, - is_remote, - od, - ) { - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - Ok(mut job) => { - log::debug!( - "new read waiting job {}, read {} to remote {}, {} files", - id, - path, - to, - job.files().len() - ); - // let m = make_fd(job.id(), job.files(), true); - // self.handler.call("updateFolderFiles", &make_args!(m)); - job.is_last_job = true; - self.read_jobs.push(job); - self.timer = time::interval(MILLI1); - } - } - } - } - Data::ResumeJob((id, is_remote)) => { - if is_remote { - if let Some(job) = get_job(id, &mut self.write_jobs) { - job.is_last_job = false; - allow_err!( - peer.send(&fs::new_send( - id, - job.remote.clone(), - job.file_num, - job.show_hidden - )) - .await - ); - } - } else { - if let Some(job) = get_job(id, &mut self.read_jobs) { - job.is_last_job = false; - allow_err!( - peer.send(&fs::new_receive( - id, - job.path.to_string_lossy().to_string(), - job.file_num, - job.files.clone() - )) - .await - ); - } - } - } - Data::SetNoConfirm(id) => { - if let Some(job) = self.remove_jobs.get_mut(&id) { - job.no_confirm = true; - } - } - Data::ConfirmDeleteFiles((id, file_num)) => { - if let Some(job) = self.remove_jobs.get_mut(&id) { - let i = file_num as usize; - if i < job.files.len() { - // self.handler.call( - // "confirmDeleteFiles", - // &make_args!(id, file_num, job.files[i].name.clone()), - // ); - self.handler.confirm_delete_files(id, file_num); - } - } - } - Data::SetConfirmOverrideFile((id, file_num, need_override, remember, is_upload)) => { - if is_upload { - if let Some(job) = fs::get_job(id, &mut self.read_jobs) { - if remember { - job.set_overwrite_strategy(Some(need_override)); - } - job.confirm(&FileTransferSendConfirmRequest { - id, - file_num, - union: if need_override { - Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) - } else { - Some(file_transfer_send_confirm_request::Union::Skip(true)) - }, - ..Default::default() - }); - } - } else { - if let Some(job) = fs::get_job(id, &mut self.write_jobs) { - if remember { - job.set_overwrite_strategy(Some(need_override)); - } - let mut msg = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_send_confirm(FileTransferSendConfirmRequest { - id, - file_num, - union: if need_override { - Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)) - } else { - Some(file_transfer_send_confirm_request::Union::Skip(true)) - }, - ..Default::default() - }); - msg.set_file_action(file_action); - allow_err!(peer.send(&msg).await); - } - } - } - Data::RemoveDirAll((id, path, is_remote, include_hidden)) => { - let sep = self.handler.get_path_sep(is_remote); - if is_remote { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_all_files(ReadAllFiles { - id, - path: path.clone(), - include_hidden, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - self.remove_jobs - .insert(id, RemoveJob::new(Vec::new(), path, sep, is_remote)); - } else { - match fs::get_recursive_files(&path, include_hidden) { - Ok(entries) => { - // let m = make_fd(id, &entries, true); - // self.handler.call("updateFolderFiles", &make_args!(m)); - self.remove_jobs - .insert(id, RemoveJob::new(entries, path, sep, is_remote)); - } - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - } - } - } - Data::CancelJob(id) => { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_cancel(FileTransferCancel { - id: id, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - if let Some(job) = fs::get_job(id, &mut self.write_jobs) { - job.remove_download_file(); - fs::remove_job(id, &mut self.write_jobs); - } - fs::remove_job(id, &mut self.read_jobs); - self.remove_jobs.remove(&id); - } - Data::RemoveDir((id, path)) => { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_remove_dir(FileRemoveDir { - id, - path, - recursive: true, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - } - Data::RemoveFile((id, path, file_num, is_remote)) => { - if is_remote { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_remove_file(FileRemoveFile { - id, - path, - file_num, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - } else { - match fs::remove_file(&path) { - Err(err) => { - self.handle_job_status(id, file_num, Some(err.to_string())); - } - Ok(()) => { - self.handle_job_status(id, file_num, None); - } - } - } - } - Data::CreateDir((id, path, is_remote)) => { - if is_remote { - let mut msg_out = Message::new(); - let mut file_action = FileAction::new(); - file_action.set_create(FileDirCreate { - id, - path, - ..Default::default() - }); - msg_out.set_file_action(file_action); - allow_err!(peer.send(&msg_out).await); - } else { - match fs::create_dir(&path) { - Err(err) => { - self.handle_job_status(id, -1, Some(err.to_string())); - } - Ok(()) => { - self.handle_job_status(id, -1, None); - } - } - } - } - _ => {} - } - true - } - - #[inline] - fn update_job_status( - job: &fs::TransferJob, - elapsed: i32, - last_update_jobs_status: &mut (Instant, HashMap), - handler: &mut Session, - ) { - if elapsed <= 0 { - return; - } - let transferred = job.transferred(); - let last_transferred = { - if let Some(v) = last_update_jobs_status.1.get(&job.id()) { - v.to_owned() - } else { - 0 - } - }; - last_update_jobs_status.1.insert(job.id(), transferred); - let speed = (transferred - last_transferred) as f64 / (elapsed as f64 / 1000.); - let file_num = job.file_num() - 1; - // handler.call( - // "jobProgress", - // &make_args!(job.id(), file_num, speed, job.finished_size() as f64), - // ); - handler.job_progress(job.id(), file_num, speed, job.finished_size() as f64); - } - - fn update_jobs_status(&mut self) { - let elapsed = self.last_update_jobs_status.0.elapsed().as_millis() as i32; - if elapsed >= 1000 { - for job in self.read_jobs.iter() { - Self::update_job_status( - job, - elapsed, - &mut self.last_update_jobs_status, - &mut self.handler, - ); - } - for job in self.write_jobs.iter() { - Self::update_job_status( - job, - elapsed, - &mut self.last_update_jobs_status, - &mut self.handler, - ); - } - self.last_update_jobs_status.0 = Instant::now(); - } - } - - pub async fn sync_jobs_status_to_local(&mut self) -> bool { - log::info!("sync transfer job status"); - let mut config: PeerConfig = self.handler.load_config(); - let mut transfer_metas = TransferSerde::default(); - for job in self.read_jobs.iter() { - let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default(); - transfer_metas.read_jobs.push(json_str); - } - for job in self.write_jobs.iter() { - let json_str = serde_json::to_string(&job.gen_meta()).unwrap_or_default(); - transfer_metas.write_jobs.push(json_str); - } - log::info!("meta: {:?}", transfer_metas); - config.transfer = transfer_metas; - self.handler.save_config(config); - true - } - - async fn send_opts_after_login(&self, peer: &mut Stream) { - if let Some(opts) = self - .handler - .lc - .read() - .unwrap() - .get_option_message_after_login() - { - let mut misc = Misc::new(); - misc.set_option(opts); - let mut msg_out = Message::new(); - msg_out.set_misc(misc); - allow_err!(peer.send(&msg_out).await); - } - } - - async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool { - if let Ok(msg_in) = Message::parse_from_bytes(&data) { - match msg_in.union { - Some(message::Union::VideoFrame(vf)) => { - if !self.first_frame { - self.first_frame = true; - // self.handler.call2("closeSuccess", &make_args!()); - self.handler.close_success(); - // self.handler.call("adaptSize", &make_args!()); - self.handler.adapt_size(); - self.send_opts_after_login(peer).await; - } - let incomming_format = CodecFormat::from(&vf); - if self.video_format != incomming_format { - self.video_format = incomming_format.clone(); - self.handler.update_quality_status(QualityStatus { - codec_format: Some(incomming_format), - ..Default::default() - }) - }; - self.video_sender.send(MediaData::VideoFrame(vf)).ok(); - } - Some(message::Union::Hash(hash)) => { - self.handler - .handle_hash(&self.handler.password.clone(), hash, peer) - .await; - } - Some(message::Union::LoginResponse(lr)) => match lr.union { - Some(login_response::Union::Error(err)) => { - if !self.handler.handle_login_error(&err) { - return false; - } - } - Some(login_response::Union::PeerInfo(pi)) => { - self.handler.handle_peer_info(pi); - // self.check_clipboard_file_context(); - // if !(self.handler.is_file_transfer() - // || self.handler.is_port_forward() - // || !SERVER_CLIPBOARD_ENABLED.load(Ordering::SeqCst) - // || !SERVER_KEYBOARD_ENABLED.load(Ordering::SeqCst) - // || self.handler.lc.read().unwrap().disable_clipboard) - // { - // let txt = self.old_clipboard.lock().unwrap().clone(); - // if !txt.is_empty() { - // let msg_out = crate::create_clipboard_msg(txt); - // let sender = self.sender.clone(); - // tokio::spawn(async move { - // // due to clipboard service interval time - // sleep(common::CLIPBOARD_INTERVAL as f32 / 1_000.).await; - // sender.send(Data::Message(msg_out)).ok(); - // }); - // } - // } - - // if self.handler.is_file_transfer() { - // self.load_last_jobs().await; - // } - } - _ => {} - }, - Some(message::Union::CursorData(cd)) => { - self.handler.set_cursor_data(cd); - } - Some(message::Union::CursorId(id)) => { - self.handler.set_cursor_id(id.to_string()); - } - Some(message::Union::CursorPosition(cp)) => { - self.handler.set_cursor_position(cp); - } - Some(message::Union::Clipboard(cb)) => { - if !self.handler.lc.read().unwrap().disable_clipboard { - update_clipboard(cb, Some(&self.old_clipboard)); - } - } - #[cfg(windows)] - Some(message::Union::Cliprdr(clip)) => { - if !self.handler.lc.read().unwrap().disable_clipboard { - if let Some(context) = &mut self.clipboard_file_context { - if let Some(clip) = msg_2_clip(clip) { - server_clip_file(context, 0, clip); - } - } - } - } - Some(message::Union::FileResponse(fr)) => { - match fr.union { - Some(file_response::Union::Dir(fd)) => { - #[cfg(windows)] - let entries = fd.entries.to_vec(); - #[cfg(not(windows))] - let mut entries = fd.entries.to_vec(); - #[cfg(not(windows))] - { - if self.handler.peer_platform() == "Windows" { - fs::transform_windows_path(&mut entries); - } - } - // let mut m = make_fd(fd.id, &entries, fd.id > 0); - // if fd.id <= 0 { - // m.set_item("path", fd.path); - // } - // self.handler.call("updateFolderFiles", &make_args!(m)); - if let Some(job) = fs::get_job(fd.id, &mut self.write_jobs) { - log::info!("job set_files: {:?}", entries); - job.set_files(entries); - } else if let Some(job) = self.remove_jobs.get_mut(&fd.id) { - job.files = entries; - } - } - Some(file_response::Union::Digest(digest)) => { - if digest.is_upload { - if let Some(job) = fs::get_job(digest.id, &mut self.read_jobs) { - if let Some(file) = job.files().get(digest.file_num as usize) { - let read_path = get_string(&job.join(&file.name)); - let overwrite_strategy = job.default_overwrite_strategy(); - if let Some(overwrite) = overwrite_strategy { - let req = FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(if overwrite { - file_transfer_send_confirm_request::Union::OffsetBlk(0) - } else { - file_transfer_send_confirm_request::Union::Skip( - true, - ) - }), - ..Default::default() - }; - job.confirm(&req); - let msg = new_send_confirm(req); - allow_err!(peer.send(&msg).await); - } else { - // self.handler.call( - // "overrideFileConfirm", - // &make_args!( - // digest.id, - // digest.file_num, - // read_path, - // true - // ), - // ); - self.handler.override_file_confirm( - digest.id, - digest.file_num, - read_path, - true, - ); - } - } - } - } else { - if let Some(job) = fs::get_job(digest.id, &mut self.write_jobs) { - if let Some(file) = job.files().get(digest.file_num as usize) { - let write_path = get_string(&job.join(&file.name)); - let overwrite_strategy = job.default_overwrite_strategy(); - match fs::is_write_need_confirmation(&write_path, &digest) { - Ok(res) => match res { - DigestCheckResult::IsSame => { - let msg= new_send_confirm(FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::Skip(true)), - ..Default::default() - }); - allow_err!(peer.send(&msg).await); - } - DigestCheckResult::NeedConfirm(digest) => { - if let Some(overwrite) = overwrite_strategy { - let msg = new_send_confirm( - FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(if overwrite { - file_transfer_send_confirm_request::Union::OffsetBlk(0) - } else { - file_transfer_send_confirm_request::Union::Skip(true) - }), - ..Default::default() - }, - ); - allow_err!(peer.send(&msg).await); - } else { - // self.handler.call( - // "overrideFileConfirm", - // &make_args!( - // digest.id, - // digest.file_num, - // write_path, - // false - // ), - // ); - self.handler.override_file_confirm( - digest.id, - digest.file_num, - write_path, - false, - ); - } - } - DigestCheckResult::NoSuchFile => { - let msg = new_send_confirm( - FileTransferSendConfirmRequest { - id: digest.id, - file_num: digest.file_num, - union: Some(file_transfer_send_confirm_request::Union::OffsetBlk(0)), - ..Default::default() - }, - ); - allow_err!(peer.send(&msg).await); - } - }, - Err(err) => { - println!("error recving digest: {}", err); - } - } - } - } - } - } - Some(file_response::Union::Block(block)) => { - log::info!( - "file response block, file id:{}, file num: {}", - block.id, - block.file_num - ); - if let Some(job) = fs::get_job(block.id, &mut self.write_jobs) { - if let Err(_err) = job.write(block, None).await { - // to-do: add "skip" for writing job - } - self.update_jobs_status(); - } - } - Some(file_response::Union::Done(d)) => { - if let Some(job) = fs::get_job(d.id, &mut self.write_jobs) { - job.modify_time(); - fs::remove_job(d.id, &mut self.write_jobs); - } - self.handle_job_status(d.id, d.file_num, None); - } - Some(file_response::Union::Error(e)) => { - self.handle_job_status(e.id, e.file_num, Some(e.error)); - } - _ => {} - } - } - Some(message::Union::Misc(misc)) => match misc.union { - Some(misc::Union::AudioFormat(f)) => { - self.audio_sender.send(MediaData::AudioFormat(f)).ok(); - } - Some(misc::Union::ChatMessage(c)) => { - // self.handler.call("newMessage", &make_args!(c.text)); // TODO - } - Some(misc::Union::PermissionInfo(p)) => { - log::info!("Change permission {:?} -> {}", p.permission, p.enabled); - match p.permission.enum_value_or_default() { - Permission::Keyboard => { - SERVER_KEYBOARD_ENABLED.store(p.enabled, Ordering::SeqCst); - // self.handler - // .call2("setPermission", &make_args!("keyboard", p.enabled)); - self.handler.set_permission("keyboard", p.enabled); - } - Permission::Clipboard => { - SERVER_CLIPBOARD_ENABLED.store(p.enabled, Ordering::SeqCst); - // self.handler - // .call2("setPermission", &make_args!("clipboard", p.enabled)); - self.handler.set_permission("clipboard", p.enabled); - } - Permission::Audio => { - // self.handler - // .call2("setPermission", &make_args!("audio", p.enabled)); - self.handler.set_permission("audio", p.enabled); - } - Permission::File => { - SERVER_FILE_TRANSFER_ENABLED.store(p.enabled, Ordering::SeqCst); - if !p.enabled && self.handler.is_file_transfer() { - return true; - } - self.check_clipboard_file_context(); - // self.handler - // .call2("setPermission", &make_args!("file", p.enabled)); - self.handler.set_permission("file", p.enabled); - } - Permission::Restart => { - // self.handler - // .call2("setPermission", &make_args!("restart", p.enabled)); - self.handler.set_permission("restart", p.enabled); - } - } - } - Some(misc::Union::SwitchDisplay(s)) => { - // self.handler.call("switchDisplay", &make_args!(s.display)); // TODO - self.video_sender.send(MediaData::Reset).ok(); - if s.width > 0 && s.height > 0 { - self.handler.set_display(s.x, s.y, s.width, s.height); - } - } - Some(misc::Union::CloseReason(c)) => { - self.handler.msgbox("error", "Connection Error", &c); - return false; - } - Some(misc::Union::BackNotification(notification)) => { - if !self.handle_back_notification(notification).await { - return false; - } - } - _ => {} - }, - Some(message::Union::TestDelay(t)) => { - self.handler.handle_test_delay(t, peer).await; - } - Some(message::Union::AudioFrame(frame)) => { - if !self.handler.lc.read().unwrap().disable_audio { - self.audio_sender.send(MediaData::AudioFrame(frame)).ok(); - } - } - Some(message::Union::FileAction(action)) => match action.union { - Some(file_action::Union::SendConfirm(c)) => { - if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) { - job.confirm(&c); - } - } - _ => {} - }, - _ => {} - } - } - true - } - - async fn handle_back_notification(&mut self, notification: BackNotification) -> bool { - match notification.union { - Some(back_notification::Union::BlockInputState(state)) => { - self.handle_back_msg_block_input( - state.enum_value_or(back_notification::BlockInputState::BlkStateUnknown), - ) - .await; - } - Some(back_notification::Union::PrivacyModeState(state)) => { - if !self - .handle_back_msg_privacy_mode( - state.enum_value_or(back_notification::PrivacyModeState::PrvStateUnknown), - ) - .await - { - return false; - } - } - _ => {} - } - true - } - - #[inline(always)] - fn update_block_input_state(&mut self, on: bool) { - // self.handler.call("updateBlockInputState", &make_args!(on)); // TODO - } - - async fn handle_back_msg_block_input(&mut self, state: back_notification::BlockInputState) { - match state { - back_notification::BlockInputState::BlkOnSucceeded => { - self.update_block_input_state(true); - } - back_notification::BlockInputState::BlkOnFailed => { - self.handler - .msgbox("custom-error", "Block user input", "Failed"); - self.update_block_input_state(false); - } - back_notification::BlockInputState::BlkOffSucceeded => { - self.update_block_input_state(false); - } - back_notification::BlockInputState::BlkOffFailed => { - self.handler - .msgbox("custom-error", "Unblock user input", "Failed"); - } - _ => {} - } - } - - #[inline(always)] - fn update_privacy_mode(&mut self, on: bool) { - let mut config = self.handler.load_config(); - config.privacy_mode = on; - self.handler.save_config(config); - - // self.handler.call("updatePrivacyMode", &[]); - self.handler.update_privacy_mode(); - } - - async fn handle_back_msg_privacy_mode( - &mut self, - state: back_notification::PrivacyModeState, - ) -> bool { - match state { - back_notification::PrivacyModeState::PrvOnByOther => { - self.handler.msgbox( - "error", - "Connecting...", - "Someone turns on privacy mode, exit", - ); - return false; - } - back_notification::PrivacyModeState::PrvNotSupported => { - self.handler - .msgbox("custom-error", "Privacy mode", "Unsupported"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOnSucceeded => { - self.handler - .msgbox("custom-nocancel", "Privacy mode", "In privacy mode"); - self.update_privacy_mode(true); - } - back_notification::PrivacyModeState::PrvOnFailedDenied => { - self.handler - .msgbox("custom-error", "Privacy mode", "Peer denied"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOnFailedPlugin => { - self.handler - .msgbox("custom-error", "Privacy mode", "Please install plugins"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOnFailed => { - self.handler - .msgbox("custom-error", "Privacy mode", "Failed"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOffSucceeded => { - self.handler - .msgbox("custom-nocancel", "Privacy mode", "Out privacy mode"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOffByPeer => { - self.handler - .msgbox("custom-error", "Privacy mode", "Peer exit"); - self.update_privacy_mode(false); - } - back_notification::PrivacyModeState::PrvOffFailed => { - self.handler - .msgbox("custom-error", "Privacy mode", "Failed to turn off"); - } - back_notification::PrivacyModeState::PrvOffUnknown => { - self.handler - .msgbox("custom-error", "Privacy mode", "Turned off"); - // log::error!("Privacy mode is turned off with unknown reason"); - self.update_privacy_mode(false); - } - _ => {} - } - true - } - - fn check_clipboard_file_context(&mut self) { - #[cfg(windows)] - { - let enabled = SERVER_FILE_TRANSFER_ENABLED.load(Ordering::SeqCst) - && self.handler.lc.read().unwrap().enable_file_transfer; - if enabled == self.clipboard_file_context.is_none() { - self.clipboard_file_context = if enabled { - match create_clipboard_file_context(true, false) { - Ok(context) => { - log::info!("clipboard context for file transfer created."); - Some(context) - } - Err(err) => { - log::error!( - "Create clipboard context for file transfer: {}", - err.to_string() - ); - None - } - } - } else { - log::info!("clipboard context for file transfer destroyed."); - None - }; - } - } - } -} - -struct RemoveJob { - files: Vec, - path: String, - sep: &'static str, - is_remote: bool, - no_confirm: bool, - last_update_job_status: Instant, -} - -impl RemoveJob { - fn new(files: Vec, path: String, sep: &'static str, is_remote: bool) -> Self { - Self { - files, - path, - sep, - is_remote, - no_confirm: false, - last_update_job_status: Instant::now(), - } - } - - pub fn _gen_meta(&self) -> RemoveJobMeta { - RemoveJobMeta { - path: self.path.clone(), - is_remote: self.is_remote, - no_confirm: self.no_confirm, - } - } -} - #[tokio::main(flavor = "current_thread")] async fn send_note(url: String, id: String, conn_id: i32, note: String) { let body = serde_json::json!({ "id": id, "Id": conn_id, "note": note });