mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Merge pull request #510 from Kingtous/opt/file_transfer_resume
opt: file transfer can resume when encountering reconnecting or close&open window
This commit is contained in:
commit
7c2db5a835
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -3899,7 +3899,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustdesk"
|
name = "rustdesk"
|
||||||
version = "1.1.9"
|
version = "1.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"android_logger 0.11.0",
|
"android_logger 0.11.0",
|
||||||
"arboard",
|
"arboard",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rustdesk"
|
name = "rustdesk"
|
||||||
version = "1.1.9"
|
version = "1.2.0"
|
||||||
authors = ["rustdesk <info@rustdesk.com>"]
|
authors = ["rustdesk <info@rustdesk.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
build= "build.rs"
|
build= "build.rs"
|
||||||
|
|||||||
@ -251,6 +251,7 @@ message FileAction {
|
|||||||
FileRemoveFile remove_file = 6;
|
FileRemoveFile remove_file = 6;
|
||||||
ReadAllFiles all_files = 7;
|
ReadAllFiles all_files = 7;
|
||||||
FileTransferCancel cancel = 8;
|
FileTransferCancel cancel = 8;
|
||||||
|
FileTransferSendConfirmRequest send_confirm = 9;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,14 +263,24 @@ message FileResponse {
|
|||||||
FileTransferBlock block = 2;
|
FileTransferBlock block = 2;
|
||||||
FileTransferError error = 3;
|
FileTransferError error = 3;
|
||||||
FileTransferDone done = 4;
|
FileTransferDone done = 4;
|
||||||
|
FileTransferDigest digest = 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message FileTransferDigest {
|
||||||
|
int32 id = 1;
|
||||||
|
sint32 file_num = 2;
|
||||||
|
uint64 last_modified = 3;
|
||||||
|
uint64 file_size = 4;
|
||||||
|
bool is_upload = 5;
|
||||||
|
}
|
||||||
|
|
||||||
message FileTransferBlock {
|
message FileTransferBlock {
|
||||||
int32 id = 1;
|
int32 id = 1;
|
||||||
sint32 file_num = 2;
|
sint32 file_num = 2;
|
||||||
bytes data = 3;
|
bytes data = 3;
|
||||||
bool compressed = 4;
|
bool compressed = 4;
|
||||||
|
uint32 blk_id = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message FileTransferError {
|
message FileTransferError {
|
||||||
@ -282,6 +293,16 @@ message FileTransferSendRequest {
|
|||||||
int32 id = 1;
|
int32 id = 1;
|
||||||
string path = 2;
|
string path = 2;
|
||||||
bool include_hidden = 3;
|
bool include_hidden = 3;
|
||||||
|
int32 file_num = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message FileTransferSendConfirmRequest {
|
||||||
|
int32 id = 1;
|
||||||
|
sint32 file_num = 2;
|
||||||
|
oneof union {
|
||||||
|
bool skip = 3;
|
||||||
|
uint32 offset_blk = 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
message FileTransferDone {
|
message FileTransferDone {
|
||||||
@ -293,6 +314,7 @@ message FileTransferReceiveRequest {
|
|||||||
int32 id = 1;
|
int32 id = 1;
|
||||||
string path = 2; // path written to
|
string path = 2; // path written to
|
||||||
repeated FileEntry files = 3;
|
repeated FileEntry files = 3;
|
||||||
|
int32 file_num = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message FileRemoveDir {
|
message FileRemoveDir {
|
||||||
|
|||||||
@ -145,6 +145,8 @@ pub struct PeerConfig {
|
|||||||
pub options: HashMap<String, String>,
|
pub options: HashMap<String, String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub info: PeerInfoSerde,
|
pub info: PeerInfoSerde,
|
||||||
|
#[serde(default)]
|
||||||
|
pub transfer: TransferSerde,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Default, Serialize, Deserialize, Clone)]
|
#[derive(Debug, PartialEq, Default, Serialize, Deserialize, Clone)]
|
||||||
@ -157,6 +159,14 @@ pub struct PeerInfoSerde {
|
|||||||
pub platform: String,
|
pub platform: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct TransferSerde {
|
||||||
|
#[serde(default)]
|
||||||
|
pub write_jobs: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub read_jobs: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
fn patch(path: PathBuf) -> PathBuf {
|
fn patch(path: PathBuf) -> PathBuf {
|
||||||
if let Some(_tmp) = path.to_str() {
|
if let Some(_tmp) = path.to_str() {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@ -864,6 +874,7 @@ impl LanPeers {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialize() {
|
fn test_serialize() {
|
||||||
let cfg: Config = Default::default();
|
let cfg: Config = Default::default();
|
||||||
|
|||||||
@ -1,13 +1,17 @@
|
|||||||
use crate::{bail, message_proto::*, ResultType};
|
#[cfg(windows)]
|
||||||
|
use std::os::windows::prelude::*;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
use tokio::{fs::File, io::*};
|
||||||
|
|
||||||
|
use crate::{bail, get_version_number, message_proto::*, ResultType, Stream};
|
||||||
// https://doc.rust-lang.org/std/os/windows/fs/trait.MetadataExt.html
|
// https://doc.rust-lang.org/std/os/windows/fs/trait.MetadataExt.html
|
||||||
use crate::{
|
use crate::{
|
||||||
compress::{compress, decompress},
|
compress::{compress, decompress},
|
||||||
config::{Config, COMPRESS_LEVEL},
|
config::{Config, COMPRESS_LEVEL},
|
||||||
};
|
};
|
||||||
#[cfg(windows)]
|
|
||||||
use std::os::windows::prelude::*;
|
|
||||||
use tokio::{fs::File, io::*};
|
|
||||||
|
|
||||||
pub fn read_dir(path: &PathBuf, include_hidden: bool) -> ResultType<FileDirectory> {
|
pub fn read_dir(path: &PathBuf, include_hidden: bool) -> ResultType<FileDirectory> {
|
||||||
let mut dir = FileDirectory {
|
let mut dir = FileDirectory {
|
||||||
@ -184,16 +188,61 @@ pub fn get_recursive_files(path: &str, include_hidden: bool) -> ResultType<Vec<F
|
|||||||
read_dir_recursive(&get_path(path), &get_path(""), include_hidden)
|
read_dir_recursive(&get_path(path), &get_path(""), include_hidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_file_exists(file_path: &str) -> bool {
|
||||||
|
return Path::new(file_path).exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn can_enable_overwrite_detection(version: i64) -> bool {
|
||||||
|
version >= get_version_number("1.2.0")
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct TransferJob {
|
pub struct TransferJob {
|
||||||
id: i32,
|
pub id: i32,
|
||||||
path: PathBuf,
|
pub remote: String,
|
||||||
files: Vec<FileEntry>,
|
pub path: PathBuf,
|
||||||
file_num: i32,
|
pub show_hidden: bool,
|
||||||
|
pub is_remote: bool,
|
||||||
|
pub is_last_job: bool,
|
||||||
|
pub file_num: i32,
|
||||||
|
pub files: Vec<FileEntry>,
|
||||||
|
|
||||||
file: Option<File>,
|
file: Option<File>,
|
||||||
total_size: u64,
|
total_size: u64,
|
||||||
finished_size: u64,
|
finished_size: u64,
|
||||||
transferred: u64,
|
transferred: u64,
|
||||||
|
enable_overwrite_detection: bool,
|
||||||
|
file_confirmed: bool,
|
||||||
|
file_is_waiting: bool,
|
||||||
|
default_overwrite_strategy: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct TransferJobMeta {
|
||||||
|
#[serde(default)]
|
||||||
|
pub id: i32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub remote: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub to: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub show_hidden: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub file_num: i32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub is_remote: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||||
|
pub struct RemoveJobMeta {
|
||||||
|
#[serde(default)]
|
||||||
|
pub path: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub is_remote: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub no_confirm: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -219,25 +268,54 @@ fn is_compressed_file(name: &str) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TransferJob {
|
impl TransferJob {
|
||||||
pub fn new_write(id: i32, path: String, files: Vec<FileEntry>) -> Self {
|
pub fn new_write(
|
||||||
|
id: i32,
|
||||||
|
remote: String,
|
||||||
|
path: String,
|
||||||
|
file_num: i32,
|
||||||
|
show_hidden: bool,
|
||||||
|
is_remote: bool,
|
||||||
|
files: Vec<FileEntry>,
|
||||||
|
enable_override_detection: bool,
|
||||||
|
) -> Self {
|
||||||
|
log::info!("new write {}", path);
|
||||||
let total_size = files.iter().map(|x| x.size as u64).sum();
|
let total_size = files.iter().map(|x| x.size as u64).sum();
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
|
remote,
|
||||||
path: get_path(&path),
|
path: get_path(&path),
|
||||||
|
file_num,
|
||||||
|
show_hidden,
|
||||||
|
is_remote,
|
||||||
files,
|
files,
|
||||||
total_size,
|
total_size,
|
||||||
|
enable_overwrite_detection: enable_override_detection,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_read(id: i32, path: String, include_hidden: bool) -> ResultType<Self> {
|
pub fn new_read(
|
||||||
let files = get_recursive_files(&path, include_hidden)?;
|
id: i32,
|
||||||
|
remote: String,
|
||||||
|
path: String,
|
||||||
|
file_num: i32,
|
||||||
|
show_hidden: bool,
|
||||||
|
is_remote: bool,
|
||||||
|
enable_override_detection: bool,
|
||||||
|
) -> ResultType<Self> {
|
||||||
|
log::info!("new read {}", path);
|
||||||
|
let files = get_recursive_files(&path, show_hidden)?;
|
||||||
let total_size = files.iter().map(|x| x.size as u64).sum();
|
let total_size = files.iter().map(|x| x.size as u64).sum();
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
id,
|
id,
|
||||||
|
remote,
|
||||||
path: get_path(&path),
|
path: get_path(&path),
|
||||||
|
file_num,
|
||||||
|
show_hidden,
|
||||||
|
is_remote,
|
||||||
files,
|
files,
|
||||||
total_size,
|
total_size,
|
||||||
|
enable_overwrite_detection: enable_override_detection,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -342,7 +420,7 @@ impl TransferJob {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn join(&self, name: &str) -> PathBuf {
|
pub fn join(&self, name: &str) -> PathBuf {
|
||||||
if name.is_empty() {
|
if name.is_empty() {
|
||||||
self.path.clone()
|
self.path.clone()
|
||||||
} else {
|
} else {
|
||||||
@ -350,7 +428,7 @@ impl TransferJob {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read(&mut self) -> ResultType<Option<FileTransferBlock>> {
|
pub async fn read(&mut self, stream: &mut Stream) -> ResultType<Option<FileTransferBlock>> {
|
||||||
let file_num = self.file_num as usize;
|
let file_num = self.file_num as usize;
|
||||||
if file_num >= self.files.len() {
|
if file_num >= self.files.len() {
|
||||||
self.file.take();
|
self.file.take();
|
||||||
@ -361,13 +439,26 @@ impl TransferJob {
|
|||||||
match File::open(self.join(&name)).await {
|
match File::open(self.join(&name)).await {
|
||||||
Ok(file) => {
|
Ok(file) => {
|
||||||
self.file = Some(file);
|
self.file = Some(file);
|
||||||
|
self.file_confirmed = false;
|
||||||
|
self.file_is_waiting = false;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.file_num += 1;
|
self.file_num += 1;
|
||||||
|
self.file_confirmed = false;
|
||||||
|
self.file_is_waiting = false;
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if self.enable_overwrite_detection {
|
||||||
|
if !self.file_confirmed() {
|
||||||
|
if !self.file_is_waiting() {
|
||||||
|
self.send_current_digest(stream).await?;
|
||||||
|
self.set_file_is_waiting(true);
|
||||||
|
}
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
const BUF_SIZE: usize = 128 * 1024;
|
const BUF_SIZE: usize = 128 * 1024;
|
||||||
let mut buf: Vec<u8> = Vec::with_capacity(BUF_SIZE);
|
let mut buf: Vec<u8> = Vec::with_capacity(BUF_SIZE);
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -380,6 +471,8 @@ impl TransferJob {
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.file_num += 1;
|
self.file_num += 1;
|
||||||
self.file = None;
|
self.file = None;
|
||||||
|
self.file_confirmed = false;
|
||||||
|
self.file_is_waiting = false;
|
||||||
return Err(err.into());
|
return Err(err.into());
|
||||||
}
|
}
|
||||||
Ok(n) => {
|
Ok(n) => {
|
||||||
@ -394,6 +487,8 @@ impl TransferJob {
|
|||||||
if offset == 0 {
|
if offset == 0 {
|
||||||
self.file_num += 1;
|
self.file_num += 1;
|
||||||
self.file = None;
|
self.file = None;
|
||||||
|
self.file_confirmed = false;
|
||||||
|
self.file_is_waiting = false;
|
||||||
} else {
|
} else {
|
||||||
self.finished_size += offset as u64;
|
self.finished_size += offset as u64;
|
||||||
if !is_compressed_file(name) {
|
if !is_compressed_file(name) {
|
||||||
@ -413,6 +508,99 @@ impl TransferJob {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn send_current_digest(&mut self, stream: &mut Stream) -> ResultType<()> {
|
||||||
|
let mut msg = Message::new();
|
||||||
|
let mut resp = FileResponse::new();
|
||||||
|
let meta = self.file.as_ref().unwrap().metadata().await?;
|
||||||
|
let last_modified = meta
|
||||||
|
.modified()?
|
||||||
|
.duration_since(SystemTime::UNIX_EPOCH)?
|
||||||
|
.as_secs();
|
||||||
|
resp.set_digest(FileTransferDigest {
|
||||||
|
id: self.id,
|
||||||
|
file_num: self.file_num,
|
||||||
|
last_modified,
|
||||||
|
file_size: meta.len(),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
msg.set_file_response(resp);
|
||||||
|
stream.send(&msg).await?;
|
||||||
|
log::info!(
|
||||||
|
"id: {}, file_num:{}, digest message is sent. waiting for confirm. msg: {:?}",
|
||||||
|
self.id,
|
||||||
|
self.file_num,
|
||||||
|
msg
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_overwrite_strategy(&mut self, overwrite_strategy: Option<bool>) {
|
||||||
|
self.default_overwrite_strategy = overwrite_strategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default_overwrite_strategy(&self) -> Option<bool> {
|
||||||
|
self.default_overwrite_strategy
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_file_confirmed(&mut self, file_confirmed: bool) {
|
||||||
|
self.file_confirmed = file_confirmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_file_is_waiting(&mut self, file_is_waiting: bool) {
|
||||||
|
self.file_is_waiting = file_is_waiting;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file_is_waiting(&self) -> bool {
|
||||||
|
self.file_is_waiting
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file_confirmed(&self) -> bool {
|
||||||
|
self.file_confirmed
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn skip_current_file(&mut self) -> bool {
|
||||||
|
self.file.take();
|
||||||
|
self.set_file_confirmed(false);
|
||||||
|
self.set_file_is_waiting(false);
|
||||||
|
self.file_num += 1;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn confirm(&mut self, r: &FileTransferSendConfirmRequest) -> bool {
|
||||||
|
if self.file_num() != r.file_num {
|
||||||
|
log::info!("file num truncated, ignoring");
|
||||||
|
} else {
|
||||||
|
match r.union {
|
||||||
|
Some(file_transfer_send_confirm_request::Union::skip(s)) => {
|
||||||
|
if s {
|
||||||
|
log::debug!("skip file id:{}, file_num:{}", r.id, r.file_num);
|
||||||
|
self.skip_current_file();
|
||||||
|
} else {
|
||||||
|
self.set_file_confirmed(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(file_transfer_send_confirm_request::Union::offset_blk(offset)) => {
|
||||||
|
log::debug!("file confirmed");
|
||||||
|
self.set_file_confirmed(true);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn gen_meta(&self) -> TransferJobMeta {
|
||||||
|
TransferJobMeta {
|
||||||
|
id: self.id,
|
||||||
|
remote: self.remote.to_string(),
|
||||||
|
to: self.path.to_string_lossy().to_string(),
|
||||||
|
file_num: self.file_num,
|
||||||
|
show_hidden: self.show_hidden,
|
||||||
|
is_remote: self.is_remote,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -453,12 +641,22 @@ pub fn new_block(block: FileTransferBlock) -> Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new_receive(id: i32, path: String, files: Vec<FileEntry>) -> Message {
|
pub fn new_send_confirm(r: FileTransferSendConfirmRequest) -> Message {
|
||||||
|
let mut msg_out = Message::new();
|
||||||
|
let mut action = FileAction::new();
|
||||||
|
action.set_send_confirm(r);
|
||||||
|
msg_out.set_file_action(action);
|
||||||
|
msg_out
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn new_receive(id: i32, path: String, file_num: i32, files: Vec<FileEntry>) -> Message {
|
||||||
let mut action = FileAction::new();
|
let mut action = FileAction::new();
|
||||||
action.set_receive(FileTransferReceiveRequest {
|
action.set_receive(FileTransferReceiveRequest {
|
||||||
id,
|
id,
|
||||||
path,
|
path,
|
||||||
files: files.into(),
|
files: files.into(),
|
||||||
|
file_num,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
let mut msg_out = Message::new();
|
let mut msg_out = Message::new();
|
||||||
@ -467,12 +665,14 @@ pub fn new_receive(id: i32, path: String, files: Vec<FileEntry>) -> Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new_send(id: i32, path: String, include_hidden: bool) -> Message {
|
pub fn new_send(id: i32, path: String, file_num: i32, include_hidden: bool) -> Message {
|
||||||
|
log::info!("new send: {},id : {}", path, id);
|
||||||
let mut action = FileAction::new();
|
let mut action = FileAction::new();
|
||||||
action.set_send(FileTransferSendRequest {
|
action.set_send(FileTransferSendRequest {
|
||||||
id,
|
id,
|
||||||
path,
|
path,
|
||||||
include_hidden,
|
include_hidden,
|
||||||
|
file_num,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
});
|
});
|
||||||
let mut msg_out = Message::new();
|
let mut msg_out = Message::new();
|
||||||
@ -509,7 +709,10 @@ pub async fn handle_read_jobs(
|
|||||||
) -> ResultType<()> {
|
) -> ResultType<()> {
|
||||||
let mut finished = Vec::new();
|
let mut finished = Vec::new();
|
||||||
for job in jobs.iter_mut() {
|
for job in jobs.iter_mut() {
|
||||||
match job.read().await {
|
if job.is_last_job {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match job.read(stream).await {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
stream
|
stream
|
||||||
.send(&new_error(job.id(), err, job.file_num()))
|
.send(&new_error(job.id(), err, job.file_num()))
|
||||||
@ -519,8 +722,13 @@ pub async fn handle_read_jobs(
|
|||||||
stream.send(&new_block(block)).await?;
|
stream.send(&new_block(block)).await?;
|
||||||
}
|
}
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
|
if !job.enable_overwrite_detection || (!job.file_confirmed && !job.file_is_waiting)
|
||||||
|
{
|
||||||
finished.push(job.id());
|
finished.push(job.id());
|
||||||
stream.send(&new_done(job.id(), job.file_num())).await?;
|
stream.send(&new_done(job.id(), job.file_num())).await?;
|
||||||
|
} else {
|
||||||
|
// waiting confirmation.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -558,3 +766,35 @@ pub fn create_dir(dir: &str) -> ResultType<()> {
|
|||||||
std::fs::create_dir_all(get_path(dir))?;
|
std::fs::create_dir_all(get_path(dir))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum DigestCheckResult {
|
||||||
|
IsSame,
|
||||||
|
NeedConfirm(FileTransferDigest),
|
||||||
|
NoSuchFile,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_write_need_confirmation(
|
||||||
|
file_path: &str,
|
||||||
|
digest: &FileTransferDigest,
|
||||||
|
) -> ResultType<DigestCheckResult> {
|
||||||
|
let path = Path::new(file_path);
|
||||||
|
if path.exists() && path.is_file() {
|
||||||
|
let metadata = std::fs::metadata(path)?;
|
||||||
|
let modified_time = metadata.modified()?;
|
||||||
|
let remote_mt = Duration::from_secs(digest.last_modified);
|
||||||
|
let local_mt = modified_time.duration_since(UNIX_EPOCH)?;
|
||||||
|
if remote_mt == local_mt && digest.file_size == metadata.len() {
|
||||||
|
return Ok(DigestCheckResult::IsSame);
|
||||||
|
}
|
||||||
|
Ok(DigestCheckResult::NeedConfirm(FileTransferDigest {
|
||||||
|
id: digest.id,
|
||||||
|
file_num: digest.file_num,
|
||||||
|
last_modified: local_mt.as_secs(),
|
||||||
|
file_size: metadata.len(),
|
||||||
|
..Default::default()
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
Ok(DigestCheckResult::NoSuchFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,9 +1,20 @@
|
|||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
net::SocketAddr,
|
||||||
|
ops::Deref,
|
||||||
|
sync::{mpsc, Arc, RwLock},
|
||||||
|
};
|
||||||
|
|
||||||
pub use async_trait::async_trait;
|
pub use async_trait::async_trait;
|
||||||
#[cfg(not(any(target_os = "android", target_os = "linux")))]
|
#[cfg(not(any(target_os = "android", target_os = "linux")))]
|
||||||
use cpal::{
|
use cpal::{
|
||||||
traits::{DeviceTrait, HostTrait, StreamTrait},
|
traits::{DeviceTrait, HostTrait, StreamTrait},
|
||||||
Device, Host, StreamConfig,
|
Device, Host, StreamConfig,
|
||||||
};
|
};
|
||||||
|
use magnum_opus::{Channels::*, Decoder as AudioDecoder};
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
allow_err,
|
allow_err,
|
||||||
anyhow::{anyhow, Context},
|
anyhow::{anyhow, Context},
|
||||||
@ -19,24 +30,15 @@ use hbb_common::{
|
|||||||
tokio::time::Duration,
|
tokio::time::Duration,
|
||||||
AddrMangle, ResultType, Stream,
|
AddrMangle, ResultType, Stream,
|
||||||
};
|
};
|
||||||
use magnum_opus::{Channels::*, Decoder as AudioDecoder};
|
|
||||||
use scrap::{Decoder, Image, VideoCodecId};
|
use scrap::{Decoder, Image, VideoCodecId};
|
||||||
use sha2::{Digest, Sha256};
|
|
||||||
use std::{
|
pub use super::lang::*;
|
||||||
collections::HashMap,
|
|
||||||
net::SocketAddr,
|
|
||||||
ops::Deref,
|
|
||||||
sync::{mpsc, Arc, RwLock},
|
|
||||||
};
|
|
||||||
use uuid::Uuid;
|
|
||||||
pub mod file_trait;
|
pub mod file_trait;
|
||||||
pub use file_trait::FileManager;
|
pub use file_trait::FileManager;
|
||||||
pub const SEC30: Duration = Duration::from_secs(30);
|
pub const SEC30: Duration = Duration::from_secs(30);
|
||||||
|
|
||||||
pub struct Client;
|
pub struct Client;
|
||||||
|
|
||||||
pub use super::lang::*;
|
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "android", target_os = "linux")))]
|
#[cfg(not(any(target_os = "android", target_os = "linux")))]
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
static ref AUDIO_HOST: Host = cpal::default_host();
|
static ref AUDIO_HOST: Host = cpal::default_host();
|
||||||
@ -1311,7 +1313,7 @@ pub enum Data {
|
|||||||
Close,
|
Close,
|
||||||
Login((String, bool)),
|
Login((String, bool)),
|
||||||
Message(Message),
|
Message(Message),
|
||||||
SendFiles((i32, String, String, bool, bool)),
|
SendFiles((i32, String, String, i32, bool, bool)),
|
||||||
RemoveDirAll((i32, String, bool)),
|
RemoveDirAll((i32, String, bool)),
|
||||||
ConfirmDeleteFiles((i32, i32)),
|
ConfirmDeleteFiles((i32, i32)),
|
||||||
SetNoConfirm(i32),
|
SetNoConfirm(i32),
|
||||||
@ -1323,6 +1325,9 @@ pub enum Data {
|
|||||||
AddPortForward((i32, String, i32)),
|
AddPortForward((i32, String, i32)),
|
||||||
ToggleClipboardFile,
|
ToggleClipboardFile,
|
||||||
NewRDP,
|
NewRDP,
|
||||||
|
SetConfirmOverrideFile((i32, i32, bool, bool, bool)),
|
||||||
|
AddJob((i32, String, String, i32, bool, bool)),
|
||||||
|
ResumeJob((i32, bool)),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|||||||
@ -80,9 +80,26 @@ pub trait FileManager: Interface {
|
|||||||
id: i32,
|
id: i32,
|
||||||
path: String,
|
path: String,
|
||||||
to: String,
|
to: String,
|
||||||
|
file_num: i32,
|
||||||
include_hidden: bool,
|
include_hidden: bool,
|
||||||
is_remote: bool,
|
is_remote: bool,
|
||||||
) {
|
) {
|
||||||
self.send(Data::SendFiles((id, path, to, include_hidden, is_remote)));
|
self.send(Data::SendFiles((id, path, to, file_num, include_hidden, is_remote)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_job(
|
||||||
|
&mut self,
|
||||||
|
id: i32,
|
||||||
|
path: String,
|
||||||
|
to: String,
|
||||||
|
file_num: i32,
|
||||||
|
include_hidden: bool,
|
||||||
|
is_remote: bool,
|
||||||
|
) {
|
||||||
|
self.send(Data::AddJob((id, path, to, file_num, include_hidden, is_remote)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resume_job(&mut self, id: i32, is_remote: bool){
|
||||||
|
self.send(Data::ResumeJob((id,is_remote)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/ipc.rs
13
src/ipc.rs
@ -44,6 +44,7 @@ pub enum FS {
|
|||||||
NewWrite {
|
NewWrite {
|
||||||
path: String,
|
path: String,
|
||||||
id: i32,
|
id: i32,
|
||||||
|
file_num: i32,
|
||||||
files: Vec<(String, u64)>,
|
files: Vec<(String, u64)>,
|
||||||
},
|
},
|
||||||
CancelWrite {
|
CancelWrite {
|
||||||
@ -59,6 +60,18 @@ pub enum FS {
|
|||||||
id: i32,
|
id: i32,
|
||||||
file_num: i32,
|
file_num: i32,
|
||||||
},
|
},
|
||||||
|
WriteOffset {
|
||||||
|
id: i32,
|
||||||
|
file_num: i32,
|
||||||
|
offset_blk: u32
|
||||||
|
},
|
||||||
|
CheckDigest {
|
||||||
|
id: i32,
|
||||||
|
file_num: i32,
|
||||||
|
file_size: u64,
|
||||||
|
last_modified: u64,
|
||||||
|
is_upload: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
|||||||
@ -266,6 +266,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("android_start_service_tip", "点击 [启动服务] 或打开 [屏幕录制] 权限开启手机屏幕共享服务。"),
|
("android_start_service_tip", "点击 [启动服务] 或打开 [屏幕录制] 权限开启手机屏幕共享服务。"),
|
||||||
("Account", "账号"),
|
("Account", "账号"),
|
||||||
("Quit", "退出"),
|
("Quit", "退出"),
|
||||||
|
("Overwrite", "覆盖"),
|
||||||
|
("This file exists, skip or overwrite this file?", "这个文件/文件夹已存在,跳过/覆盖?"),
|
||||||
("doc_mac_permission", "https://rustdesk.com/docs/zh-cn/manual/mac/#启用权限"),
|
("doc_mac_permission", "https://rustdesk.com/docs/zh-cn/manual/mac/#启用权限"),
|
||||||
("Help", "帮助"),
|
("Help", "帮助"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
|
|||||||
@ -266,6 +266,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("android_start_service_tip", "Нажмите [Запуск промежуточного сервера] или ОТКРЫТЬ разрешение [Скриншот], чтобы запустить службу демонстрации экрана."),
|
("android_start_service_tip", "Нажмите [Запуск промежуточного сервера] или ОТКРЫТЬ разрешение [Скриншот], чтобы запустить службу демонстрации экрана."),
|
||||||
("Account", "Аккаунт"),
|
("Account", "Аккаунт"),
|
||||||
("Quit", "Выйти"),
|
("Quit", "Выйти"),
|
||||||
|
("Overwrite", "крышка"),
|
||||||
|
("This file exists, skip or overwrite this file?", "Этот файл существует, пропустить или перезаписать этот файл?"),
|
||||||
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
|
("doc_mac_permission", "https://rustdesk.com/docs/en/manual/mac/#enable-permissions"),
|
||||||
("Help", "Помощь"),
|
("Help", "Помощь"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
|
|||||||
@ -265,6 +265,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("android_version_audio_tip", ""),
|
("android_version_audio_tip", ""),
|
||||||
("android_start_service_tip", ""),
|
("android_start_service_tip", ""),
|
||||||
("Account", ""),
|
("Account", ""),
|
||||||
|
("Overwrite", ""),
|
||||||
|
("This file exists, skip or overwrite this file?", "")
|
||||||
("Quit", ""),
|
("Quit", ""),
|
||||||
("doc_mac_permission", ""),
|
("doc_mac_permission", ""),
|
||||||
("Help", ""),
|
("Help", ""),
|
||||||
|
|||||||
@ -266,6 +266,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("android_start_service_tip", "點擊 [啟動服務] 或打開 [屏幕錄製] 權限開啟手機屏幕共享服務。"),
|
("android_start_service_tip", "點擊 [啟動服務] 或打開 [屏幕錄製] 權限開啟手機屏幕共享服務。"),
|
||||||
("Account", "帳戶"),
|
("Account", "帳戶"),
|
||||||
("Quit", "退出"),
|
("Quit", "退出"),
|
||||||
|
("Overwrite", "覆蓋"),
|
||||||
|
("This file exists, skip or overwrite this file?", "這個文件/文件夾已存在,跳過/覆蓋?"),
|
||||||
("doc_mac_permission", "https://rustdesk.com/docs/zh-tw/manual/mac/#啟用權限"),
|
("doc_mac_permission", "https://rustdesk.com/docs/zh-tw/manual/mac/#啟用權限"),
|
||||||
("Help", "幫助"),
|
("Help", "幫助"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
|
|||||||
@ -3,13 +3,17 @@ use super::{input_service::*, *};
|
|||||||
use crate::clipboard_file::*;
|
use crate::clipboard_file::*;
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
use crate::common::update_clipboard;
|
use crate::common::update_clipboard;
|
||||||
use crate::ipc;
|
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
use crate::{common::MOBILE_INFO2, mobile::connection_manager::start_channel};
|
use crate::{common::MOBILE_INFO2, mobile::connection_manager::start_channel};
|
||||||
|
use crate::{ipc, VERSION};
|
||||||
|
use hbb_common::fs::can_enable_overwrite_detection;
|
||||||
|
use hbb_common::log::debug;
|
||||||
|
use hbb_common::message_proto::file_transfer_send_confirm_request::Union;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
config::Config,
|
config::Config,
|
||||||
fs,
|
fs,
|
||||||
futures::{SinkExt, StreamExt},
|
futures::{SinkExt, StreamExt},
|
||||||
|
get_version_number,
|
||||||
message_proto::{option_message::BoolOption, permission_info::Permission},
|
message_proto::{option_message::BoolOption, permission_info::Permission},
|
||||||
sleep, timeout,
|
sleep, timeout,
|
||||||
tokio::{
|
tokio::{
|
||||||
@ -19,6 +23,7 @@ use hbb_common::{
|
|||||||
},
|
},
|
||||||
tokio_util::codec::{BytesCodec, Framed},
|
tokio_util::codec::{BytesCodec, Framed},
|
||||||
};
|
};
|
||||||
|
use libc::{printf, send};
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
use scrap::android::call_input_service_mouse_input;
|
use scrap::android::call_input_service_mouse_input;
|
||||||
use serde_json::{json, value::Value};
|
use serde_json::{json, value::Value};
|
||||||
@ -968,9 +973,18 @@ impl Connection {
|
|||||||
}
|
}
|
||||||
Some(file_action::Union::send(s)) => {
|
Some(file_action::Union::send(s)) => {
|
||||||
let id = s.id;
|
let id = s.id;
|
||||||
let path = s.path;
|
let od =
|
||||||
match fs::TransferJob::new_read(id, path.clone(), s.include_hidden)
|
can_enable_overwrite_detection(get_version_number(VERSION));
|
||||||
{
|
let path = s.path.clone();
|
||||||
|
match fs::TransferJob::new_read(
|
||||||
|
id,
|
||||||
|
"".to_string(),
|
||||||
|
path.clone(),
|
||||||
|
s.file_num,
|
||||||
|
s.include_hidden,
|
||||||
|
false,
|
||||||
|
od,
|
||||||
|
) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.send(fs::new_error(id, err, 0)).await;
|
self.send(fs::new_error(id, err, 0)).await;
|
||||||
}
|
}
|
||||||
@ -986,6 +1000,7 @@ impl Connection {
|
|||||||
self.send_fs(ipc::FS::NewWrite {
|
self.send_fs(ipc::FS::NewWrite {
|
||||||
path: r.path,
|
path: r.path,
|
||||||
id: r.id,
|
id: r.id,
|
||||||
|
file_num: r.file_num,
|
||||||
files: r
|
files: r
|
||||||
.files
|
.files
|
||||||
.to_vec()
|
.to_vec()
|
||||||
@ -1018,6 +1033,11 @@ impl Connection {
|
|||||||
self.send_fs(ipc::FS::CancelWrite { id: c.id });
|
self.send_fs(ipc::FS::CancelWrite { id: c.id });
|
||||||
fs::remove_job(c.id, &mut self.read_jobs);
|
fs::remove_job(c.id, &mut self.read_jobs);
|
||||||
}
|
}
|
||||||
|
Some(file_action::Union::send_confirm(r)) => {
|
||||||
|
if let Some(job) = fs::get_job(r.id, &mut self.read_jobs) {
|
||||||
|
job.confirm(&r);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1037,6 +1057,13 @@ impl Connection {
|
|||||||
file_num: d.file_num,
|
file_num: d.file_num,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
Some(file_response::Union::digest(d)) => self.send_fs(ipc::FS::CheckDigest {
|
||||||
|
id: d.id,
|
||||||
|
file_num: d.file_num,
|
||||||
|
file_size: d.file_size,
|
||||||
|
last_modified: d.last_modified,
|
||||||
|
is_upload: true,
|
||||||
|
}),
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
Some(message::Union::misc(misc)) => match misc.union {
|
Some(message::Union::misc(misc)) => match misc.union {
|
||||||
|
|||||||
76
src/ui/cm.rs
76
src/ui/cm.rs
@ -1,15 +1,22 @@
|
|||||||
use crate::ipc::{self, new_listener, Connection, Data};
|
use crate::ipc::{self, new_listener, Connection, Data};
|
||||||
|
use crate::VERSION;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use clipboard::{
|
use clipboard::{
|
||||||
create_cliprdr_context, empty_clipboard, get_rx_clip_client, server_clip_file, set_conn_enabled,
|
create_cliprdr_context, empty_clipboard, get_rx_clip_client, server_clip_file, set_conn_enabled,
|
||||||
};
|
};
|
||||||
|
use hbb_common::fs::{
|
||||||
|
can_enable_overwrite_detection, get_string, is_write_need_confirmation, new_send_confirm,
|
||||||
|
DigestCheckResult,
|
||||||
|
};
|
||||||
|
use hbb_common::log::log;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
allow_err,
|
allow_err,
|
||||||
config::Config,
|
config::Config,
|
||||||
fs, log,
|
fs, get_version_number, log,
|
||||||
message_proto::*,
|
message_proto::*,
|
||||||
protobuf::Message as _,
|
protobuf::Message as _,
|
||||||
tokio::{self, sync::mpsc, task::spawn_blocking},
|
tokio::{self, sync::mpsc, task::spawn_blocking},
|
||||||
|
ResultType,
|
||||||
};
|
};
|
||||||
use sciter::{make_args, Element, Value, HELEMENT};
|
use sciter::{make_args, Element, Value, HELEMENT};
|
||||||
use std::{
|
use std::{
|
||||||
@ -151,11 +158,19 @@ impl ConnectionManager {
|
|||||||
ipc::FS::NewWrite {
|
ipc::FS::NewWrite {
|
||||||
path,
|
path,
|
||||||
id,
|
id,
|
||||||
|
file_num,
|
||||||
mut files,
|
mut files,
|
||||||
} => {
|
} => {
|
||||||
|
let od = can_enable_overwrite_detection(get_version_number(VERSION));
|
||||||
|
// cm has no show_hidden context
|
||||||
|
// dummy remote, show_hidden, is_remote
|
||||||
write_jobs.push(fs::TransferJob::new_write(
|
write_jobs.push(fs::TransferJob::new_write(
|
||||||
id,
|
id,
|
||||||
|
"".to_string(),
|
||||||
path,
|
path,
|
||||||
|
file_num,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
files
|
files
|
||||||
.drain(..)
|
.drain(..)
|
||||||
.map(|f| FileEntry {
|
.map(|f| FileEntry {
|
||||||
@ -164,6 +179,7 @@ impl ConnectionManager {
|
|||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
od,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
ipc::FS::CancelWrite { id } => {
|
ipc::FS::CancelWrite { id } => {
|
||||||
@ -179,6 +195,59 @@ impl ConnectionManager {
|
|||||||
fs::remove_job(id, write_jobs);
|
fs::remove_job(id, write_jobs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ipc::FS::CheckDigest {
|
||||||
|
id,
|
||||||
|
file_num,
|
||||||
|
file_size,
|
||||||
|
last_modified,
|
||||||
|
is_upload,
|
||||||
|
} => {
|
||||||
|
if let Some(job) = fs::get_job(id, write_jobs) {
|
||||||
|
let mut req = FileTransferSendConfirmRequest {
|
||||||
|
id,
|
||||||
|
file_num,
|
||||||
|
union: Some(file_transfer_send_confirm_request::Union::offset_blk(0)),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let digest = FileTransferDigest {
|
||||||
|
id,
|
||||||
|
file_num,
|
||||||
|
last_modified,
|
||||||
|
file_size,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
if let Some(file) = job.files().get(file_num as usize) {
|
||||||
|
let path = get_string(&job.join(&file.name));
|
||||||
|
match is_write_need_confirmation(&path, &digest) {
|
||||||
|
Ok(digest_result) => {
|
||||||
|
match digest_result {
|
||||||
|
DigestCheckResult::IsSame => {
|
||||||
|
req.set_skip(true);
|
||||||
|
let msg_out = new_send_confirm(req);
|
||||||
|
Self::send(msg_out, conn).await;
|
||||||
|
}
|
||||||
|
DigestCheckResult::NeedConfirm(mut digest) => {
|
||||||
|
// upload to server, but server has the same file, request
|
||||||
|
digest.is_upload = is_upload;
|
||||||
|
let mut msg_out = Message::new();
|
||||||
|
let mut fr = FileResponse::new();
|
||||||
|
fr.set_digest(digest);
|
||||||
|
msg_out.set_file_response(fr);
|
||||||
|
Self::send(msg_out, conn).await;
|
||||||
|
}
|
||||||
|
DigestCheckResult::NoSuchFile => {
|
||||||
|
let msg_out = new_send_confirm(req);
|
||||||
|
Self::send(msg_out, conn).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
Self::send(fs::new_error(id, err, file_num), conn).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ipc::FS::WriteBlock {
|
ipc::FS::WriteBlock {
|
||||||
id,
|
id,
|
||||||
file_num,
|
file_num,
|
||||||
@ -208,6 +277,11 @@ impl ConnectionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ipc::FS::WriteOffset {
|
||||||
|
id,
|
||||||
|
file_num,
|
||||||
|
offset_blk,
|
||||||
|
} => {}
|
||||||
},
|
},
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
Data::ClipbaordFile(_clip) => {
|
Data::ClipbaordFile(_clip) => {
|
||||||
|
|||||||
@ -220,6 +220,10 @@ table.job-table tr.is_remote svg {
|
|||||||
transform: scale(-1, 1);
|
transform: scale(-1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
table.job-table tr.is_remote div.svg_continue svg {
|
||||||
|
transform: scale(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
table.job-table tr td div.text {
|
table.job-table tr td div.text {
|
||||||
width: *;
|
width: *;
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|||||||
@ -19,6 +19,7 @@ var svg_refresh = <svg viewBox="0 0 551.13 551.13">
|
|||||||
<path d="m482.24 310.01c0 113.97-92.707 206.67-206.67 206.67s-206.67-92.708-206.67-206.67c0-102.21 74.639-187.09 172.23-203.56v65.78l86.114-86.114-86.114-86.115v71.641c-116.65 16.802-206.67 117.14-206.67 238.37 0 132.96 108.16 241.12 241.12 241.12s241.12-108.16 241.12-241.12z"/>
|
<path d="m482.24 310.01c0 113.97-92.707 206.67-206.67 206.67s-206.67-92.708-206.67-206.67c0-102.21 74.639-187.09 172.23-203.56v65.78l86.114-86.114-86.114-86.115v71.641c-116.65 16.802-206.67 117.14-206.67 238.37 0 132.96 108.16 241.12 241.12 241.12s241.12-108.16 241.12-241.12z"/>
|
||||||
</svg>;
|
</svg>;
|
||||||
var svg_cancel = <svg .cancel viewBox="0 0 612 612"><polygon points="612 36.004 576.52 0.603 306 270.61 35.478 0.603 0 36.004 270.52 306.01 0 576 35.478 611.4 306 341.41 576.52 611.4 612 576 341.46 306.01"/></svg>;
|
var svg_cancel = <svg .cancel viewBox="0 0 612 612"><polygon points="612 36.004 576.52 0.603 306 270.61 35.478 0.603 0 36.004 270.52 306.01 0 576 35.478 611.4 306 341.41 576.52 611.4 612 576 341.46 306.01"/></svg>;
|
||||||
|
var svg_continue = <svg .continue t="1652493728825" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="793" ne="0.8429451094012732" width="200" height="200"><path d="M458.69056 948.9408v74.39872c-102.4512-10.22464-196.25472-51.00544-272.1024-113.024l52.2752-52.2752c62.09024 48.87552 137.58464 81.39776 219.8272 90.89536z m562.47296-434.70848c0 263.99744-202.6496 482.9696-460.06784 509.0304v-74.5472c216.6272-25.99424 385.95584-211.73248 385.95584-435.072 0-223.27296-169.32352-409.00096-385.95584-434.9952V5.19168c257.42336 26.07104 460.06784 245.0432 460.06784 509.04064zM166.24128 785.83296l-52.13696 52.14208C50.82112 760.9088 9.51808 665.20576 0 560.67584h73.36448c9.1136 84.41856 42.44992 161.85344 92.8768 225.15712zM113.77664 190.88384l51.65056 51.6608C117.12 303.5392 84.64384 377.56928 74.3424 458.2656H0.95744C12.032 357.632 52.60288 265.53344 113.77664 190.88384zM458.69056 5.12v73.30304c-82.69312 9.55392-158.5664 42.37824-220.8512 91.6992L186.17856 118.4768C262.10304 56.2688 356.06528 15.36 458.69568 5.12z" p-id="794"></path><path d="M652.8 512l-276.48 166.4v-332.8z" p-id="795"></path></svg>;
|
||||||
var svg_computer = <svg .computer viewBox="0 0 480 480">
|
var svg_computer = <svg .computer viewBox="0 0 480 480">
|
||||||
<g>
|
<g>
|
||||||
<path fill="#2C8CFF" d="m276 395v11.148c0 2.327-1.978 4.15-4.299 3.985-21.145-1.506-42.392-1.509-63.401-0.011-2.322 0.166-4.3-1.657-4.3-3.985v-11.137c0-2.209 1.791-4 4-4h64c2.209 0 4 1.791 4 4zm204-340v288c0 17.65-14.35 32-32 32h-416c-17.65 0-32-14.35-32-32v-288c0-17.65 14.35-32 32-32h416c17.65 0 32 14.35 32 32zm-125.62 386.36c-70.231-21.843-158.71-21.784-228.76 0-4.22 1.31-6.57 5.8-5.26 10.02 1.278 4.085 5.639 6.591 10.02 5.26 66.093-20.58 151.37-21.125 219.24 0 4.22 1.31 8.71-1.04 10.02-5.26s-1.04-8.71-5.26-10.02z"/>
|
<path fill="#2C8CFF" d="m276 395v11.148c0 2.327-1.978 4.15-4.299 3.985-21.145-1.506-42.392-1.509-63.401-0.011-2.322 0.166-4.3-1.657-4.3-3.985v-11.137c0-2.209 1.791-4 4-4h64c2.209 0 4 1.791 4 4zm204-340v288c0 17.65-14.35 32-32 32h-416c-17.65 0-32-14.35-32-32v-288c0-17.65 14.35-32 32-32h416c17.65 0 32 14.35 32 32zm-125.62 386.36c-70.231-21.843-158.71-21.784-228.76 0-4.22 1.31-6.57 5.8-5.26 10.02 1.278 4.085 5.639 6.591 10.02 5.26 66.093-20.58 151.37-21.125 219.24 0 4.22 1.31 8.71-1.04 10.02-5.26s-1.04-8.71-5.26-10.02z"/>
|
||||||
@ -100,6 +101,19 @@ class JobTable: Reactor.Component {
|
|||||||
refreshDir(is_remote);
|
refreshDir(is_remote);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event click $(svg.continue) (_, me) {
|
||||||
|
var job = this.jobs[me.parent.parent.parent.index];
|
||||||
|
var id = job.id;
|
||||||
|
this.continueJob(id);
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAllJobs() {
|
||||||
|
this.jobs = [];
|
||||||
|
this.job_map = {};
|
||||||
|
this.update();
|
||||||
|
}
|
||||||
|
|
||||||
function send(path, is_remote) {
|
function send(path, is_remote) {
|
||||||
var to;
|
var to;
|
||||||
var show_hidden;
|
var show_hidden;
|
||||||
@ -117,13 +131,36 @@ class JobTable: Reactor.Component {
|
|||||||
this.jobs.push({ type: "transfer",
|
this.jobs.push({ type: "transfer",
|
||||||
id: id, path: path, to: to,
|
id: id, path: path, to: to,
|
||||||
include_hidden: show_hidden,
|
include_hidden: show_hidden,
|
||||||
is_remote: is_remote });
|
is_remote: is_remote,
|
||||||
|
is_last: false
|
||||||
|
});
|
||||||
this.job_map[id] = this.jobs[this.jobs.length - 1];
|
this.job_map[id] = this.jobs[this.jobs.length - 1];
|
||||||
handler.send_files(id, path, to, show_hidden, is_remote);
|
handler.send_files(id, path, to, 0, show_hidden, is_remote);
|
||||||
var self = this;
|
var self = this;
|
||||||
self.timer(30ms, function() { self.update(); });
|
self.timer(30ms, function() { self.update(); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addJob(id, path, to, file_num, show_hidden, is_remote) {
|
||||||
|
var job = { type: "transfer",
|
||||||
|
id: id, path: path, to: to,
|
||||||
|
include_hidden: show_hidden,
|
||||||
|
is_remote: is_remote, is_last: true, file_num: file_num };
|
||||||
|
this.jobs.push(job);
|
||||||
|
this.job_map[id] = this.jobs[this.jobs.length - 1];
|
||||||
|
jobIdCounter = id + 1;
|
||||||
|
handler.add_job(id, path, to, file_num, show_hidden, is_remote);
|
||||||
|
stdout.println(JSON.stringify(job));
|
||||||
|
}
|
||||||
|
|
||||||
|
function continueJob(id) {
|
||||||
|
var job = this.job_map[id];
|
||||||
|
if (job == null || !job.is_last){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
job.is_last = false;
|
||||||
|
handler.resume_job(job.id, job.is_remote);
|
||||||
|
}
|
||||||
|
|
||||||
function addDelDir(path, is_remote) {
|
function addDelDir(path, is_remote) {
|
||||||
var id = jobIdCounter;
|
var id = jobIdCounter;
|
||||||
jobIdCounter += 1;
|
jobIdCounter += 1;
|
||||||
@ -258,6 +295,9 @@ class JobTable: Reactor.Component {
|
|||||||
<div .path>{job.path}</div>
|
<div .path>{job.path}</div>
|
||||||
<div id={"s" + job.id}>{this.getStatus(job)}</div>
|
<div id={"s" + job.id}>{this.getStatus(job)}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="svg_continue" style={job.is_last ? "" : "visibility: hidden"}>
|
||||||
|
{svg_continue}
|
||||||
|
</div>
|
||||||
{svg_cancel}
|
{svg_cancel}
|
||||||
</td></tr>;
|
</td></tr>;
|
||||||
}
|
}
|
||||||
@ -603,6 +643,7 @@ function initializeFileTransfer()
|
|||||||
}
|
}
|
||||||
|
|
||||||
handler.updateFolderFiles = function(fd) {
|
handler.updateFolderFiles = function(fd) {
|
||||||
|
// stdout.println("update folder files: " + JSON.stringify(fd));
|
||||||
fd.entries = fd.entries || [];
|
fd.entries = fd.entries || [];
|
||||||
if (fd.id > 0) {
|
if (fd.id > 0) {
|
||||||
var jt = file_transfer.job_table;
|
var jt = file_transfer.job_table;
|
||||||
@ -649,6 +690,19 @@ handler.jobError = function(id, err, file_num = -1) {
|
|||||||
file_transfer.job_table.updateJobStatus(id, file_num, err);
|
file_transfer.job_table.updateJobStatus(id, file_num, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handler.clearAllJobs = function() {
|
||||||
|
file_transfer.job_table.clearAllJobs();
|
||||||
|
}
|
||||||
|
|
||||||
|
handler.addJob = function (id, path, to, file_num, show_hidden, is_remote) {
|
||||||
|
// stdout.println("restore job: " + is_remote);
|
||||||
|
file_transfer.job_table.addJob(id,path,to,file_num,show_hidden,is_remote);
|
||||||
|
}
|
||||||
|
|
||||||
|
handler.updateTransferList = function () {
|
||||||
|
file_transfer.job_table.update();
|
||||||
|
}
|
||||||
|
|
||||||
function refreshDir(is_remote) {
|
function refreshDir(is_remote) {
|
||||||
if (is_remote) file_transfer.remote_folder_view.refreshDir();
|
if (is_remote) file_transfer.remote_folder_view.refreshDir();
|
||||||
else file_transfer.local_folder_view.refreshDir();
|
else file_transfer.local_folder_view.refreshDir();
|
||||||
@ -716,6 +770,33 @@ handler.confirmDeleteFiles = function(id, i, name) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handler.overrideFileConfirm = function(id, file_num, to, is_upload) {
|
||||||
|
var jt = file_transfer.job_table;
|
||||||
|
msgbox("custom-skip", "Confirm Write Strategy", "<div .form> \
|
||||||
|
<div>" + translate('Overwrite') + translate('files') + ".</div> \
|
||||||
|
<div>" + translate('This file exists, skip or overwrite this file?') + "</div> \
|
||||||
|
<div.ellipsis style=\"font-weight: bold;\" .text>" + to + "</div> \
|
||||||
|
<div><button|checkbox(remember) {ts}>" + translate('Do this for all conflicts') + "</button></div> \
|
||||||
|
</div>", function(res=null) {
|
||||||
|
if (!res) {
|
||||||
|
jt.updateJobStatus(id, -1, "cancel");
|
||||||
|
handler.cancel_job(id);
|
||||||
|
} else if (res.skip) {
|
||||||
|
if (res.remember){
|
||||||
|
handler.set_write_override(id,file_num,false,true, is_upload); //
|
||||||
|
} else {
|
||||||
|
handler.set_write_override(id,file_num,false,false,is_upload); //
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (res.remember){
|
||||||
|
handler.set_write_override(id,file_num,true,true,is_upload); //
|
||||||
|
} else {
|
||||||
|
handler.set_write_override(id,file_num,true,false,is_upload); //
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function save_file_transfer_close_state() {
|
function save_file_transfer_close_state() {
|
||||||
var local_dir = file_transfer.local_folder_view.fd.path || "";
|
var local_dir = file_transfer.local_folder_view.fd.path || "";
|
||||||
var local_show_hidden = file_transfer.local_folder_view.show_hidden ? "Y" : "";
|
var local_show_hidden = file_transfer.local_folder_view.show_hidden ? "Y" : "";
|
||||||
|
|||||||
408
src/ui/remote.rs
408
src/ui/remote.rs
@ -1,19 +1,34 @@
|
|||||||
#[cfg(windows)]
|
use std::{
|
||||||
use crate::clipboard_file::*;
|
collections::HashMap,
|
||||||
use crate::{
|
ops::Deref,
|
||||||
client::*,
|
sync::{Arc, Mutex, RwLock},
|
||||||
common::{self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use sciter::{
|
||||||
|
dom::{
|
||||||
|
event::{EventReason, BEHAVIOR_EVENTS, EVENT_GROUPS, PHASE_MASK},
|
||||||
|
Element, HELEMENT,
|
||||||
|
},
|
||||||
|
make_args,
|
||||||
|
video::{video_destination, AssetPtr, COLOR_SPACE},
|
||||||
|
Value,
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use clipboard::{
|
use clipboard::{
|
||||||
cliprdr::CliprdrClientContext, create_cliprdr_context as create_clipboard_file_context,
|
cliprdr::CliprdrClientContext, create_cliprdr_context as create_clipboard_file_context,
|
||||||
get_rx_clip_client, server_clip_file,
|
get_rx_clip_client, server_clip_file,
|
||||||
};
|
};
|
||||||
use enigo::{self, Enigo, KeyboardControllable};
|
use enigo::{self, Enigo, KeyboardControllable};
|
||||||
|
use hbb_common::fs::{
|
||||||
|
can_enable_overwrite_detection, get_string, is_file_exists, new_send_confirm,
|
||||||
|
DigestCheckResult, RemoveJobMeta, get_job,
|
||||||
|
};
|
||||||
|
use hbb_common::log::log;
|
||||||
use hbb_common::{
|
use hbb_common::{
|
||||||
allow_err,
|
allow_err,
|
||||||
config::{Config, LocalConfig, PeerConfig},
|
config::{self, Config, LocalConfig, PeerConfig},
|
||||||
fs, log,
|
fs, get_version_number, log,
|
||||||
message_proto::{permission_info::Permission, *},
|
message_proto::{permission_info::Permission, *},
|
||||||
protobuf::Message as _,
|
protobuf::Message as _,
|
||||||
rendezvous_proto::ConnType,
|
rendezvous_proto::ConnType,
|
||||||
@ -25,19 +40,14 @@ use hbb_common::{
|
|||||||
},
|
},
|
||||||
Stream,
|
Stream,
|
||||||
};
|
};
|
||||||
use sciter::{
|
use hbb_common::{config::TransferSerde, fs::TransferJobMeta};
|
||||||
dom::{
|
|
||||||
event::{EventReason, BEHAVIOR_EVENTS, EVENT_GROUPS, PHASE_MASK},
|
#[cfg(windows)]
|
||||||
Element, HELEMENT,
|
use crate::clipboard_file::*;
|
||||||
},
|
use crate::{
|
||||||
make_args,
|
client::*,
|
||||||
video::{video_destination, AssetPtr, COLOR_SPACE},
|
common::{self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL},
|
||||||
Value,
|
VERSION,
|
||||||
};
|
|
||||||
use std::{
|
|
||||||
collections::HashMap,
|
|
||||||
ops::Deref,
|
|
||||||
sync::{Arc, Mutex, RwLock},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type Video = AssetPtr<video_destination>;
|
type Video = AssetPtr<video_destination>;
|
||||||
@ -194,7 +204,9 @@ impl sciter::EventHandler for Handler {
|
|||||||
fn confirm_delete_files(i32, i32);
|
fn confirm_delete_files(i32, i32);
|
||||||
fn set_no_confirm(i32);
|
fn set_no_confirm(i32);
|
||||||
fn cancel_job(i32);
|
fn cancel_job(i32);
|
||||||
fn send_files(i32, String, String, bool, bool);
|
fn send_files(i32, String, String, i32, bool, bool);
|
||||||
|
fn add_job(i32, String, String, i32, bool, bool);
|
||||||
|
fn resume_job(i32, bool);
|
||||||
fn get_platform(bool);
|
fn get_platform(bool);
|
||||||
fn get_path_sep(bool);
|
fn get_path_sep(bool);
|
||||||
fn get_icon_path(i32, String);
|
fn get_icon_path(i32, String);
|
||||||
@ -216,6 +228,7 @@ impl sciter::EventHandler for Handler {
|
|||||||
fn toggle_option(String);
|
fn toggle_option(String);
|
||||||
fn get_remember();
|
fn get_remember();
|
||||||
fn peer_platform();
|
fn peer_platform();
|
||||||
|
fn set_write_override(i32, i32, bool, bool, bool);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,6 +549,24 @@ impl Handler {
|
|||||||
self.lc.read().unwrap().remember
|
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 t(&self, name: String) -> String {
|
fn t(&self, name: String) -> String {
|
||||||
crate::client::translate(name)
|
crate::client::translate(name)
|
||||||
}
|
}
|
||||||
@ -744,6 +775,7 @@ impl Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn reconnect(&mut self) {
|
fn reconnect(&mut self) {
|
||||||
|
println!("reconnecting");
|
||||||
let cloned = self.clone();
|
let cloned = self.clone();
|
||||||
let mut lock = self.write().unwrap();
|
let mut lock = self.write().unwrap();
|
||||||
lock.thread.take().map(|t| t.join());
|
lock.thread.take().map(|t| t.join());
|
||||||
@ -1289,6 +1321,7 @@ async fn io_loop(handler: Handler) {
|
|||||||
clipboard_file_context: None,
|
clipboard_file_context: None,
|
||||||
};
|
};
|
||||||
remote.io_loop(&key, &token).await;
|
remote.io_loop(&key, &token).await;
|
||||||
|
remote.sync_jobs_status_to_local().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RemoveJob {
|
struct RemoveJob {
|
||||||
@ -1311,6 +1344,14 @@ impl RemoveJob {
|
|||||||
last_update_job_status: Instant::now(),
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Remote {
|
struct Remote {
|
||||||
@ -1497,7 +1538,57 @@ impl Remote {
|
|||||||
Some(tx)
|
Some(tx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn load_last_jobs(&mut self) {
|
||||||
|
log::info!("start load last jobs");
|
||||||
|
self.handler.call("clearAllJobs", &make_args!());
|
||||||
|
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<TransferJobMeta, serde_json::Error> = serde_json::from_str(&job_str);
|
||||||
|
if let Ok(job) = job {
|
||||||
|
self.handler.call(
|
||||||
|
"addJob",
|
||||||
|
&make_args!(
|
||||||
|
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<TransferJobMeta, serde_json::Error> = serde_json::from_str(&job_str);
|
||||||
|
if let Ok(job) = job {
|
||||||
|
self.handler.call(
|
||||||
|
"addJob",
|
||||||
|
&make_args!(
|
||||||
|
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!());
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool {
|
async fn handle_msg_from_ui(&mut self, data: Data, peer: &mut Stream) -> bool {
|
||||||
|
// log::info!("new msg from ui, {}",data);
|
||||||
match data {
|
match data {
|
||||||
Data::Close => {
|
Data::Close => {
|
||||||
return false;
|
return false;
|
||||||
@ -1513,14 +1604,35 @@ impl Remote {
|
|||||||
Data::Message(msg) => {
|
Data::Message(msg) => {
|
||||||
allow_err!(peer.send(&msg).await);
|
allow_err!(peer.send(&msg).await);
|
||||||
}
|
}
|
||||||
Data::SendFiles((id, path, to, include_hidden, is_remote)) => {
|
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 {
|
if is_remote {
|
||||||
log::debug!("New job {}, write to {} from remote {}", id, to, path);
|
log::debug!("New job {}, write to {} from remote {}", id, to, path);
|
||||||
self.write_jobs
|
self.write_jobs.push(fs::TransferJob::new_write(
|
||||||
.push(fs::TransferJob::new_write(id, to, Vec::new()));
|
id,
|
||||||
allow_err!(peer.send(&fs::new_send(id, path, include_hidden)).await);
|
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 {
|
} else {
|
||||||
match fs::TransferJob::new_read(id, path.clone(), include_hidden) {
|
match fs::TransferJob::new_read(
|
||||||
|
id,
|
||||||
|
to.clone(),
|
||||||
|
path.clone(),
|
||||||
|
file_num,
|
||||||
|
include_hidden,
|
||||||
|
is_remote,
|
||||||
|
od,
|
||||||
|
) {
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.handle_job_status(id, -1, Some(err.to_string()));
|
self.handle_job_status(id, -1, Some(err.to_string()));
|
||||||
}
|
}
|
||||||
@ -1537,11 +1649,74 @@ impl Remote {
|
|||||||
let files = job.files().clone();
|
let files = job.files().clone();
|
||||||
self.read_jobs.push(job);
|
self.read_jobs.push(job);
|
||||||
self.timer = time::interval(MILLI1);
|
self.timer = time::interval(MILLI1);
|
||||||
allow_err!(peer.send(&fs::new_receive(id, to, files)).await);
|
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) => {
|
Data::SetNoConfirm(id) => {
|
||||||
if let Some(job) = self.remove_jobs.get_mut(&id) {
|
if let Some(job) = self.remove_jobs.get_mut(&id) {
|
||||||
job.no_confirm = true;
|
job.no_confirm = true;
|
||||||
@ -1558,6 +1733,45 @@ impl Remote {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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::offset_blk(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::offset_blk(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)) => {
|
Data::RemoveDirAll((id, path, is_remote)) => {
|
||||||
let sep = self.handler.get_path_sep(is_remote);
|
let sep = self.handler.get_path_sep(is_remote);
|
||||||
if is_remote {
|
if is_remote {
|
||||||
@ -1715,6 +1929,24 @@ impl Remote {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
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.handler.save_config(config);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool {
|
async fn handle_msg_from_peer(&mut self, data: &[u8], peer: &mut Stream) -> bool {
|
||||||
if let Ok(msg_in) = Message::parse_from_bytes(&data) {
|
if let Ok(msg_in) = Message::parse_from_bytes(&data) {
|
||||||
match msg_in.union {
|
match msg_in.union {
|
||||||
@ -1755,6 +1987,10 @@ impl Remote {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.handler.is_file_transfer() {
|
||||||
|
self.load_last_jobs().await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
@ -1782,7 +2018,8 @@ impl Remote {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(message::Union::file_response(fr)) => match fr.union {
|
Some(message::Union::file_response(fr)) => {
|
||||||
|
match fr.union {
|
||||||
Some(file_response::Union::dir(fd)) => {
|
Some(file_response::Union::dir(fd)) => {
|
||||||
let entries = fd.entries.to_vec();
|
let entries = fd.entries.to_vec();
|
||||||
let mut m = make_fd(fd.id, &entries, fd.id > 0);
|
let mut m = make_fd(fd.id, &entries, fd.id > 0);
|
||||||
@ -1791,12 +2028,116 @@ impl Remote {
|
|||||||
}
|
}
|
||||||
self.handler.call("updateFolderFiles", &make_args!(m));
|
self.handler.call("updateFolderFiles", &make_args!(m));
|
||||||
if let Some(job) = fs::get_job(fd.id, &mut self.write_jobs) {
|
if let Some(job) = fs::get_job(fd.id, &mut self.write_jobs) {
|
||||||
|
log::info!("job set_files: {:?}", entries);
|
||||||
job.set_files(entries);
|
job.set_files(entries);
|
||||||
} else if let Some(job) = self.remove_jobs.get_mut(&fd.id) {
|
} else if let Some(job) = self.remove_jobs.get_mut(&fd.id) {
|
||||||
job.files = entries;
|
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::offset_blk(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
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} 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::offset_blk(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
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DigestCheckResult::NoSuchFile => {
|
||||||
|
let msg = new_send_confirm(
|
||||||
|
FileTransferSendConfirmRequest {
|
||||||
|
id: digest.id,
|
||||||
|
file_num: digest.file_num,
|
||||||
|
union: Some(file_transfer_send_confirm_request::Union::offset_blk(0)),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
);
|
||||||
|
allow_err!(peer.send(&msg).await);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
println!("error recving digest: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some(file_response::Union::block(block)) => {
|
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 Some(job) = fs::get_job(block.id, &mut self.write_jobs) {
|
||||||
if let Err(_err) = job.write(block, None).await {
|
if let Err(_err) = job.write(block, None).await {
|
||||||
// to-do: add "skip" for writing job
|
// to-do: add "skip" for writing job
|
||||||
@ -1815,7 +2156,8 @@ impl Remote {
|
|||||||
self.handle_job_status(e.id, e.file_num, Some(e.error));
|
self.handle_job_status(e.id, e.file_num, Some(e.error));
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
}
|
||||||
|
}
|
||||||
Some(message::Union::misc(misc)) => match misc.union {
|
Some(message::Union::misc(misc)) => match misc.union {
|
||||||
Some(misc::Union::audio_format(f)) => {
|
Some(misc::Union::audio_format(f)) => {
|
||||||
self.audio_sender.send(MediaData::AudioFormat(f)).ok();
|
self.audio_sender.send(MediaData::AudioFormat(f)).ok();
|
||||||
@ -1891,6 +2233,14 @@ impl Remote {
|
|||||||
self.audio_sender.send(MediaData::AudioFrame(frame)).ok();
|
self.audio_sender.send(MediaData::AudioFrame(frame)).ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some(message::Union::file_action(action)) => match action.union {
|
||||||
|
Some(file_action::Union::send_confirm(c)) => {
|
||||||
|
if let Some(job) = fs::get_job(c.id, &mut self.read_jobs) {
|
||||||
|
job.confirm(&c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user