diff --git a/Cargo.lock b/Cargo.lock index 32e50572e..72bc94475 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -395,6 +395,8 @@ dependencies = [ "cc", "hbb_common", "lazy_static", + "serde 1.0.135", + "serde_derive", "thiserror", ] diff --git a/libs/clipboard/Cargo.toml b/libs/clipboard/Cargo.toml index 3d2b5ef77..3a27833cc 100644 --- a/libs/clipboard/Cargo.toml +++ b/libs/clipboard/Cargo.toml @@ -12,4 +12,6 @@ cc = "1.0" [dependencies] thiserror = "1.0.30" lazy_static = "1.4" +serde = "1.0" +serde_derive = "1.0" hbb_common = { path = "../hbb_common" } diff --git a/libs/clipboard/src/cliprdr.h b/libs/clipboard/src/cliprdr.h index 0e581cc2f..260db685a 100644 --- a/libs/clipboard/src/cliprdr.h +++ b/libs/clipboard/src/cliprdr.h @@ -192,6 +192,8 @@ extern "C" typedef UINT (*pcCliprdrServerFileContentsResponse)( CliprdrClientContext *context, const CLIPRDR_FILE_CONTENTS_RESPONSE *fileContentsResponse); + typedef BOOL (*pcCheckEnabled)(UINT32 server_conn_id, UINT32 remote_conn_id); + // TODO: hide more members of clipboard context struct _cliprdr_client_context { @@ -199,6 +201,7 @@ extern "C" BOOL enableFiles; BOOL enableOthers; + pcCheckEnabled CheckEnabled; pcCliprdrServerCapabilities ServerCapabilities; pcCliprdrClientCapabilities ClientCapabilities; pcCliprdrMonitorReady MonitorReady; diff --git a/libs/clipboard/src/cliprdr.rs b/libs/clipboard/src/cliprdr.rs index b88d9c40d..184711595 100644 --- a/libs/clipboard/src/cliprdr.rs +++ b/libs/clipboard/src/cliprdr.rs @@ -456,6 +456,9 @@ pub type pcCliprdrServerFileContentsResponse = ::std::option::Option< fileContentsResponse: *const CLIPRDR_FILE_CONTENTS_RESPONSE, ) -> UINT, >; +pub type pcCheckEnabled = ::std::option::Option< + unsafe extern "C" fn(server_conn_id: UINT32, remote_conn_id: UINT32) -> BOOL, +>; // TODO: hide more members of clipboard context #[repr(C)] @@ -464,6 +467,7 @@ pub struct _cliprdr_client_context { pub custom: *mut ::std::os::raw::c_void, pub enableFiles: BOOL, pub enableOthers: BOOL, + pub CheckEnabled: pcCheckEnabled, pub ServerCapabilities: pcCliprdrServerCapabilities, pub ClientCapabilities: pcCliprdrClientCapabilities, pub MonitorReady: pcCliprdrMonitorReady, @@ -492,6 +496,11 @@ pub struct _cliprdr_client_context { extern "C" { pub(crate) fn init_cliprdr(context: *mut CliprdrClientContext) -> BOOL; pub(crate) fn uninit_cliprdr(context: *mut CliprdrClientContext) -> BOOL; + pub(crate) fn empty_cliprdr( + context: *mut CliprdrClientContext, + server_conn_id: UINT32, + remote_conn_id: UINT32, + ) -> BOOL; } #[derive(Error, Debug)] @@ -508,6 +517,7 @@ impl CliprdrClientContext { pub fn create( enable_files: bool, enable_others: bool, + check_enabled: pcCheckEnabled, client_format_list: pcCliprdrClientFormatList, client_format_list_response: pcCliprdrClientFormatListResponse, client_format_data_request: pcCliprdrClientFormatDataRequest, @@ -519,6 +529,7 @@ impl CliprdrClientContext { custom: 0 as *mut _, enableFiles: if enable_files { TRUE } else { FALSE }, enableOthers: if enable_others { TRUE } else { FALSE }, + CheckEnabled: check_enabled, ServerCapabilities: None, ClientCapabilities: None, MonitorReady: None, diff --git a/libs/clipboard/src/lib.rs b/libs/clipboard/src/lib.rs index bf5a0f140..b66b43bba 100644 --- a/libs/clipboard/src/lib.rs +++ b/libs/clipboard/src/lib.rs @@ -1,17 +1,18 @@ use cliprdr::*; use hbb_common::{ log, - message_proto::cliprdr as msg_cliprdr, - message_proto::*, tokio::sync::{ mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, Mutex as TokioMutex, }, ResultType, }; +use serde_derive::{Deserialize, Serialize}; use std::{ boxed::Box, + collections::HashMap, ffi::{CStr, CString}, + sync::Mutex, }; pub mod cliprdr; @@ -22,111 +23,273 @@ pub struct ConnID { pub remote_conn_id: u32, } +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(tag = "t", content = "c")] +pub enum ClipbaordFile { + ServerFormatList { + server_conn_id: i32, + remote_conn_id: i32, + format_list: Vec<(i32, String)>, + }, + ServerFormatListResponse { + server_conn_id: i32, + remote_conn_id: i32, + msg_flags: i32, + }, + ServerFormatDataRequest { + server_conn_id: i32, + remote_conn_id: i32, + requested_format_id: i32, + }, + ServerFormatDataResponse { + server_conn_id: i32, + remote_conn_id: i32, + msg_flags: i32, + format_data: Vec, + }, + FileContentsRequest { + server_conn_id: i32, + remote_conn_id: i32, + stream_id: i32, + list_index: i32, + dw_flags: i32, + n_position_low: i32, + n_position_high: i32, + cb_requested: i32, + have_clip_data_id: bool, + clip_data_id: i32, + }, + FileContentsResponse { + server_conn_id: i32, + remote_conn_id: i32, + msg_flags: i32, + stream_id: i32, + requested_data: Vec, + }, +} + +#[derive(Default)] +struct ConnEnabled { + server_conn_enabled: HashMap, + remote_conn_enabled: HashMap, +} + lazy_static::lazy_static! { - static ref MSG_CHANNEL_CLIENT: (UnboundedSender<(ConnID, Message)>, TokioMutex>) = { + static ref MSG_CHANNEL_CLIENT: (UnboundedSender<(ConnID, ClipbaordFile)>, TokioMutex>) = { let (tx, rx) = unbounded_channel(); (tx, TokioMutex::new(rx)) }; + + static ref CLIP_CONN_ENABLED: Mutex = Mutex::new(ConnEnabled::default()); } #[inline(always)] -pub fn get_rx_client_msg<'a>() -> &'a TokioMutex> { +pub fn get_rx_clip_client<'a>() -> &'a TokioMutex> { &MSG_CHANNEL_CLIENT.1 } -pub fn server_msg(context: &mut Box, conn_id: ConnID, msg: Cliprdr) -> u32 { - match msg.union { - Some(msg_cliprdr::Union::ready(_)) => { - // proc ready - 0 - } - Some(msg_cliprdr::Union::format_list(req)) => { +pub fn set_conn_enabled(server_conn_id: i32, remote_conn_id: i32, enabled: bool) { + let mut lock = CLIP_CONN_ENABLED.lock().unwrap(); + if server_conn_id != 0 { + let _ = lock.server_conn_enabled.insert(server_conn_id, enabled); + } + if remote_conn_id != 0 { + let _ = lock.remote_conn_enabled.insert(remote_conn_id, enabled); + } +} + +pub fn empty_clipboard( + context: &mut Box, + server_conn_id: i32, + remote_conn_id: i32, +) -> bool { + unsafe { + TRUE == cliprdr::empty_cliprdr( + &mut (**context), + server_conn_id as u32, + remote_conn_id as u32, + ) + } +} + +pub fn server_clip_file( + context: &mut Box, + conn_id: ConnID, + msg: ClipbaordFile, +) -> u32 { + match msg { + ClipbaordFile::ServerFormatList { + mut server_conn_id, + mut remote_conn_id, + format_list, + } => { + if conn_id.server_conn_id != 0 { + server_conn_id = conn_id.server_conn_id as i32; + } + if conn_id.remote_conn_id != 0 { + remote_conn_id = conn_id.remote_conn_id as i32; + } log::debug!("server_format_list called"); - let ret = server_format_list(context, conn_id, req); + let ret = server_format_list(context, server_conn_id, remote_conn_id, format_list); log::debug!("server_format_list called, return {}", ret); ret } - Some(msg_cliprdr::Union::format_list_response(req)) => { + ClipbaordFile::ServerFormatListResponse { + mut server_conn_id, + mut remote_conn_id, + msg_flags, + } => { + if conn_id.server_conn_id != 0 { + server_conn_id = conn_id.server_conn_id as i32; + } + if conn_id.remote_conn_id != 0 { + remote_conn_id = conn_id.remote_conn_id as i32; + } log::debug!("format_list_response called"); - let ret = server_format_list_response(context, conn_id, req); + let ret = + server_format_list_response(context, server_conn_id, remote_conn_id, msg_flags); log::debug!("server_format_list_response called, return {}", ret); ret } - Some(msg_cliprdr::Union::format_data_request(req)) => { + ClipbaordFile::ServerFormatDataRequest { + mut server_conn_id, + mut remote_conn_id, + requested_format_id, + } => { + if conn_id.server_conn_id != 0 { + server_conn_id = conn_id.server_conn_id as i32; + } + if conn_id.remote_conn_id != 0 { + remote_conn_id = conn_id.remote_conn_id as i32; + } log::debug!("format_data_request called"); - let ret = server_format_data_request(context, conn_id, req); + let ret = server_format_data_request( + context, + server_conn_id, + remote_conn_id, + requested_format_id, + ); log::debug!("server_format_data_request called, return {}", ret); ret } - Some(msg_cliprdr::Union::format_data_response(req)) => { + ClipbaordFile::ServerFormatDataResponse { + mut server_conn_id, + mut remote_conn_id, + msg_flags, + format_data, + } => { + if conn_id.server_conn_id != 0 { + server_conn_id = conn_id.server_conn_id as i32; + } + if conn_id.remote_conn_id != 0 { + remote_conn_id = conn_id.remote_conn_id as i32; + } log::debug!("format_data_response called"); - let ret = server_format_data_response(context, conn_id, req); + let ret = server_format_data_response( + context, + server_conn_id, + remote_conn_id, + msg_flags, + format_data, + ); log::debug!("server_format_data_response called, return {}", ret); ret } - Some(msg_cliprdr::Union::file_contents_request(req)) => { + ClipbaordFile::FileContentsRequest { + mut server_conn_id, + mut remote_conn_id, + stream_id, + list_index, + dw_flags, + n_position_low, + n_position_high, + cb_requested, + have_clip_data_id, + clip_data_id, + } => { + if conn_id.server_conn_id != 0 { + server_conn_id = conn_id.server_conn_id as i32; + } + if conn_id.remote_conn_id != 0 { + remote_conn_id = conn_id.remote_conn_id as i32; + } log::debug!("file_contents_request called"); - let ret = server_file_contents_request(context, conn_id, req); + let ret = server_file_contents_request( + context, + server_conn_id, + remote_conn_id, + stream_id, + list_index, + dw_flags, + n_position_low, + n_position_high, + cb_requested, + have_clip_data_id, + clip_data_id, + ); log::debug!("server_file_contents_request called, return {}", ret); ret } - Some(msg_cliprdr::Union::file_contents_response(req)) => { + ClipbaordFile::FileContentsResponse { + mut server_conn_id, + mut remote_conn_id, + msg_flags, + stream_id, + requested_data, + } => { + if conn_id.server_conn_id != 0 { + server_conn_id = conn_id.server_conn_id as i32; + } + if conn_id.remote_conn_id != 0 { + remote_conn_id = conn_id.remote_conn_id as i32; + } log::debug!("file_contents_response called"); - let ret = server_file_contents_response(context, conn_id, req); + let ret = server_file_contents_response( + context, + server_conn_id, + remote_conn_id, + msg_flags, + stream_id, + requested_data, + ); log::debug!("server_file_contents_response called, return {}", ret); ret } - None => { - // unreachable!() - 0 - } } } -fn server_format_list( +pub fn server_format_list( context: &mut Box, - conn_id: ConnID, - data: CliprdrServerFormatList, + server_conn_id: i32, + remote_conn_id: i32, + format_list: Vec<(i32, String)>, ) -> u32 { - // do not check msgFlags for now unsafe { - let num_formats = data.formats.len() as UINT32; - let mut formats = data - .formats + let num_formats = format_list.len() as UINT32; + let mut formats = format_list .into_iter() .map(|format| { - if format.format.is_empty() { + if format.1.is_empty() { CLIPRDR_FORMAT { - formatId: format.id as UINT32, + formatId: format.0 as UINT32, formatName: 0 as *mut _, } } else { - let n = match CString::new(format.format) { + let n = match CString::new(format.1) { Ok(n) => n, Err(_) => CString::new("").unwrap(), }; CLIPRDR_FORMAT { - formatId: format.id as UINT32, + formatId: format.0 as UINT32, formatName: n.into_raw(), } } }) .collect::>(); - let server_conn_id = if conn_id.server_conn_id != 0 { - conn_id.server_conn_id as UINT32 - } else { - data.server_conn_id as UINT32 - }; - let remote_conn_id = if conn_id.remote_conn_id != 0 { - conn_id.remote_conn_id as UINT32 - } else { - data.remote_conn_id as UINT32 - }; - let format_list = CLIPRDR_FORMAT_LIST { - serverConnID: server_conn_id, - remoteConnID: remote_conn_id, + serverConnID: server_conn_id as UINT32, + remoteConnID: remote_conn_id as UINT32, msgType: 0 as UINT16, msgFlags: 0 as UINT16, dataLen: 0 as UINT32, @@ -146,28 +309,18 @@ fn server_format_list( ret as u32 } } -fn server_format_list_response( +pub fn server_format_list_response( context: &mut Box, - conn_id: ConnID, - data: CliprdrServerFormatListResponse, + server_conn_id: i32, + remote_conn_id: i32, + msg_flags: i32, ) -> u32 { unsafe { - let server_conn_id = if conn_id.server_conn_id != 0 { - conn_id.server_conn_id as UINT32 - } else { - data.server_conn_id as UINT32 - }; - let remote_conn_id = if conn_id.remote_conn_id != 0 { - conn_id.remote_conn_id as UINT32 - } else { - data.remote_conn_id as UINT32 - }; - let format_list_response = CLIPRDR_FORMAT_LIST_RESPONSE { - serverConnID: server_conn_id, - remoteConnID: remote_conn_id, + serverConnID: server_conn_id as UINT32, + remoteConnID: remote_conn_id as UINT32, msgType: 0 as UINT16, - msgFlags: data.msg_flags as UINT16, + msgFlags: msg_flags as UINT16, dataLen: 0 as UINT32, }; @@ -177,59 +330,41 @@ fn server_format_list_response( ret as u32 } } -fn server_format_data_request( +pub fn server_format_data_request( context: &mut Box, - conn_id: ConnID, - data: CliprdrServerFormatDataRequest, + server_conn_id: i32, + remote_conn_id: i32, + requested_format_id: i32, ) -> u32 { unsafe { - let server_conn_id = if conn_id.server_conn_id != 0 { - conn_id.server_conn_id as UINT32 - } else { - data.server_conn_id as UINT32 - }; - let remote_conn_id = if conn_id.remote_conn_id != 0 { - conn_id.remote_conn_id as UINT32 - } else { - data.remote_conn_id as UINT32 - }; - let format_data_request = CLIPRDR_FORMAT_DATA_REQUEST { - serverConnID: server_conn_id, - remoteConnID: remote_conn_id, + serverConnID: server_conn_id as UINT32, + remoteConnID: remote_conn_id as UINT32, msgType: 0 as UINT16, msgFlags: 0 as UINT16, dataLen: 0 as UINT32, - requestedFormatId: data.requested_format_id as UINT32, + requestedFormatId: requested_format_id as UINT32, }; let ret = ((**context).ServerFormatDataRequest.unwrap())(&mut (**context), &format_data_request); ret as u32 } } -fn server_format_data_response( +pub fn server_format_data_response( context: &mut Box, - conn_id: ConnID, - mut data: CliprdrServerFormatDataResponse, + server_conn_id: i32, + remote_conn_id: i32, + msg_flags: i32, + mut format_data: Vec, ) -> u32 { unsafe { - let server_conn_id = if conn_id.server_conn_id != 0 { - conn_id.server_conn_id as UINT32 - } else { - data.server_conn_id as UINT32 - }; - let remote_conn_id = if conn_id.remote_conn_id != 0 { - conn_id.remote_conn_id as UINT32 - } else { - data.remote_conn_id as UINT32 - }; let format_data_response = CLIPRDR_FORMAT_DATA_RESPONSE { - serverConnID: server_conn_id, - remoteConnID: remote_conn_id, + serverConnID: server_conn_id as UINT32, + remoteConnID: remote_conn_id as UINT32, msgType: 0 as UINT16, - msgFlags: data.msg_flags as UINT16, - dataLen: data.format_data.len() as UINT32, - requestedFormatData: data.format_data.as_mut_ptr(), + msgFlags: msg_flags as UINT16, + dataLen: format_data.len() as UINT32, + requestedFormatData: format_data.as_mut_ptr(), }; let ret = ((**context).ServerFormatDataResponse.unwrap())( &mut (**context), @@ -238,36 +373,34 @@ fn server_format_data_response( ret as u32 } } -fn server_file_contents_request( +pub fn server_file_contents_request( context: &mut Box, - conn_id: ConnID, - data: CliprdrFileContentsRequest, + server_conn_id: i32, + remote_conn_id: i32, + stream_id: i32, + list_index: i32, + dw_flags: i32, + n_position_low: i32, + n_position_high: i32, + cb_requested: i32, + have_clip_data_id: bool, + clip_data_id: i32, ) -> u32 { unsafe { - let server_conn_id = if conn_id.server_conn_id != 0 { - conn_id.server_conn_id as UINT32 - } else { - data.server_conn_id as UINT32 - }; - let remote_conn_id = if conn_id.remote_conn_id != 0 { - conn_id.remote_conn_id as UINT32 - } else { - data.remote_conn_id as UINT32 - }; let file_contents_request = CLIPRDR_FILE_CONTENTS_REQUEST { - serverConnID: server_conn_id, - remoteConnID: remote_conn_id, + serverConnID: server_conn_id as UINT32, + remoteConnID: remote_conn_id as UINT32, msgType: 0 as UINT16, msgFlags: 0 as UINT16, dataLen: 0 as UINT32, - streamId: data.stream_id as UINT32, - listIndex: data.list_index as UINT32, - dwFlags: data.dw_flags as UINT32, - nPositionLow: data.n_position_low as UINT32, - nPositionHigh: data.n_position_high as UINT32, - cbRequested: data.cb_requested as UINT32, - haveClipDataId: if data.have_clip_data_id { TRUE } else { FALSE }, - clipDataId: data.clip_data_id as UINT32, + streamId: stream_id as UINT32, + listIndex: list_index as UINT32, + dwFlags: dw_flags as UINT32, + nPositionLow: n_position_low as UINT32, + nPositionHigh: n_position_high as UINT32, + cbRequested: cb_requested as UINT32, + haveClipDataId: if have_clip_data_id { TRUE } else { FALSE }, + clipDataId: clip_data_id as UINT32, }; let ret = ((**context).ServerFileContentsRequest.unwrap())( &mut (**context), @@ -276,31 +409,24 @@ fn server_file_contents_request( ret as u32 } } -fn server_file_contents_response( +pub fn server_file_contents_response( context: &mut Box, - conn_id: ConnID, - mut data: CliprdrFileContentsResponse, + server_conn_id: i32, + remote_conn_id: i32, + msg_flags: i32, + stream_id: i32, + mut requested_data: Vec, ) -> u32 { unsafe { - let server_conn_id = if conn_id.server_conn_id != 0 { - conn_id.server_conn_id as UINT32 - } else { - data.server_conn_id as UINT32 - }; - let remote_conn_id = if conn_id.remote_conn_id != 0 { - conn_id.remote_conn_id as UINT32 - } else { - data.remote_conn_id as UINT32 - }; let file_contents_response = CLIPRDR_FILE_CONTENTS_RESPONSE { - serverConnID: server_conn_id, - remoteConnID: remote_conn_id, + serverConnID: server_conn_id as UINT32, + remoteConnID: remote_conn_id as UINT32, msgType: 0 as UINT16, - msgFlags: data.msg_flags as UINT16, - dataLen: 4 + data.requested_data.len() as UINT32, - streamId: data.stream_id as UINT32, - cbRequested: data.requested_data.len() as UINT32, - requestedData: data.requested_data.as_mut_ptr(), + msgFlags: msg_flags as UINT16, + dataLen: 4 + requested_data.len() as UINT32, + streamId: stream_id as UINT32, + cbRequested: requested_data.len() as UINT32, + requestedData: requested_data.as_mut_ptr(), }; let ret = ((**context).ServerFileContentsResponse.unwrap())( &mut (**context), @@ -310,10 +436,14 @@ fn server_file_contents_response( } } -pub fn create_cliprdr_context(enable_files: bool, enable_others: bool) -> ResultType> { +pub fn create_cliprdr_context( + enable_files: bool, + enable_others: bool, +) -> ResultType> { Ok(CliprdrClientContext::create( enable_files, enable_others, + Some(check_enabled), Some(client_format_list), Some(client_format_list_response), Some(client_format_data_request), @@ -323,23 +453,54 @@ pub fn create_cliprdr_context(enable_files: bool, enable_others: bool) -> Result )?) } +extern "C" fn check_enabled(server_conn_id: UINT32, remote_conn_id: UINT32) -> BOOL { + let lock = CLIP_CONN_ENABLED.lock().unwrap(); + if server_conn_id == 0 && remote_conn_id == 0 { + return FALSE; + } + + let mut server_conn_enabled = false; + if server_conn_id != 0 { + if let Some(true) = lock.server_conn_enabled.get(&(server_conn_id as i32)) { + server_conn_enabled = true; + } + } else { + server_conn_enabled = true; + } + + // let mut remote_conn_enabled = false; + // remote connection is always enabled + // if remote_conn_id != 0 { + // if let Some(true) = lock.remote_conn_enabled.get(&(remote_conn_id as i32)) { + // remote_conn_enabled = true; + // } + // } else { + // remote_conn_enabled = true; + // } + let remote_conn_enabled = true; + + if server_conn_enabled && remote_conn_enabled { + return TRUE; + } else { + return FALSE; + } +} + extern "C" fn client_format_list( _context: *mut CliprdrClientContext, - format_list: *const CLIPRDR_FORMAT_LIST, + clip_format_list: *const CLIPRDR_FORMAT_LIST, ) -> UINT { log::debug!("client_format_list called"); - let mut data = CliprdrServerFormatList::default(); + let server_conn_id; + let remote_conn_id; + let mut format_list: Vec<(i32, String)> = Vec::new(); unsafe { let mut i = 0u32; - while i < (*format_list).numFormats { - let format_data = &(*(*format_list).formats.offset(i as isize)); + while i < (*clip_format_list).numFormats { + let format_data = &(*(*clip_format_list).formats.offset(i as isize)); if format_data.formatName.is_null() { - data.formats.push(CliprdrFormat { - id: format_data.formatId as i32, - format: "".to_owned(), - ..Default::default() - }); + format_list.push((format_data.formatId as i32, "".to_owned())); } else { let format_name = CStr::from_ptr(format_data.formatName).to_str(); let format_name = match format_name { @@ -349,30 +510,25 @@ extern "C" fn client_format_list( "".to_owned() } }; - data.formats.push(CliprdrFormat { - id: format_data.formatId as i32, - format: format_name, - ..Default::default() - }); + format_list.push((format_data.formatId as i32, format_name)); } + // log::debug!("format list item {}: format id: {}, format name: {}", i, format_data.formatId, &format_name); i += 1; } - - data.server_conn_id = (*format_list).serverConnID as i32; - data.remote_conn_id = (*format_list).remoteConnID as i32; + server_conn_id = (*clip_format_list).serverConnID as i32; + remote_conn_id = (*clip_format_list).remoteConnID as i32; } let conn_id = ConnID { - server_conn_id: data.server_conn_id as u32, - remote_conn_id: data.remote_conn_id as u32, + server_conn_id: server_conn_id as u32, + remote_conn_id: remote_conn_id as u32, + }; + let data = ClipbaordFile::ServerFormatList { + server_conn_id, + remote_conn_id, + format_list, }; - - let mut msg = Message::new(); - let mut cliprdr = Cliprdr::new(); - cliprdr.set_format_list(data); - msg.set_cliprdr(cliprdr); - // no need to handle result here - MSG_CHANNEL_CLIENT.0.send((conn_id, msg)).unwrap(); + MSG_CHANNEL_CLIENT.0.send((conn_id, data)).unwrap(); 0 } @@ -383,23 +539,25 @@ extern "C" fn client_format_list_response( ) -> UINT { log::debug!("client_format_list_response called"); - let mut data = CliprdrServerFormatListResponse::default(); + let server_conn_id; + let remote_conn_id; + let msg_flags; unsafe { - data.server_conn_id = (*format_list_response).serverConnID as i32; - data.remote_conn_id = (*format_list_response).remoteConnID as i32; - data.msg_flags = (*format_list_response).msgFlags as i32; + server_conn_id = (*format_list_response).serverConnID as i32; + remote_conn_id = (*format_list_response).remoteConnID as i32; + msg_flags = (*format_list_response).msgFlags as i32; } let conn_id = ConnID { - server_conn_id: data.server_conn_id as u32, - remote_conn_id: data.remote_conn_id as u32, + server_conn_id: server_conn_id as u32, + remote_conn_id: remote_conn_id as u32, + }; + let data = ClipbaordFile::ServerFormatListResponse { + server_conn_id, + remote_conn_id, + msg_flags, }; - - let mut msg = Message::new(); - let mut cliprdr = Cliprdr::new(); - cliprdr.set_format_list_response(data); - msg.set_cliprdr(cliprdr); // no need to handle result here - MSG_CHANNEL_CLIENT.0.send((conn_id, msg)).unwrap(); + MSG_CHANNEL_CLIENT.0.send((conn_id, data)).unwrap(); 0 } @@ -410,23 +568,25 @@ extern "C" fn client_format_data_request( ) -> UINT { log::debug!("client_format_data_request called"); - let mut data = CliprdrServerFormatDataRequest::default(); + let server_conn_id; + let remote_conn_id; + let requested_format_id; unsafe { - data.server_conn_id = (*format_data_request).serverConnID as i32; - data.remote_conn_id = (*format_data_request).remoteConnID as i32; - data.requested_format_id = (*format_data_request).requestedFormatId as i32; + server_conn_id = (*format_data_request).serverConnID as i32; + remote_conn_id = (*format_data_request).remoteConnID as i32; + requested_format_id = (*format_data_request).requestedFormatId as i32; } let conn_id = ConnID { - server_conn_id: data.server_conn_id as u32, - remote_conn_id: data.remote_conn_id as u32, + server_conn_id: server_conn_id as u32, + remote_conn_id: remote_conn_id as u32, + }; + let data = ClipbaordFile::ServerFormatDataRequest { + server_conn_id, + remote_conn_id, + requested_format_id, }; - - let mut msg = Message::new(); - let mut cliprdr = Cliprdr::new(); - cliprdr.set_format_data_request(data); - msg.set_cliprdr(cliprdr); // no need to handle result here - MSG_CHANNEL_CLIENT.0.send((conn_id, msg)).unwrap(); + MSG_CHANNEL_CLIENT.0.send((conn_id, data)).unwrap(); 0 } @@ -437,28 +597,36 @@ extern "C" fn client_format_data_response( ) -> UINT { log::debug!("client_format_data_response called"); - let mut data = CliprdrServerFormatDataResponse::default(); + let server_conn_id; + let remote_conn_id; + let msg_flags; + let format_data; unsafe { - data.server_conn_id = (*format_data_response).serverConnID as i32; - data.remote_conn_id = (*format_data_response).remoteConnID as i32; - data.msg_flags = (*format_data_response).msgFlags as i32; - data.format_data = std::slice::from_raw_parts( - (*format_data_response).requestedFormatData, - (*format_data_response).dataLen as usize, - ) - .to_vec(); + server_conn_id = (*format_data_response).serverConnID as i32; + remote_conn_id = (*format_data_response).remoteConnID as i32; + msg_flags = (*format_data_response).msgFlags as i32; + if (*format_data_response).requestedFormatData.is_null() { + format_data = Vec::new(); + } else { + format_data = std::slice::from_raw_parts( + (*format_data_response).requestedFormatData, + (*format_data_response).dataLen as usize, + ) + .to_vec(); + } } let conn_id = ConnID { - server_conn_id: data.server_conn_id as u32, - remote_conn_id: data.remote_conn_id as u32, + server_conn_id: server_conn_id as u32, + remote_conn_id: remote_conn_id as u32, + }; + let data = ClipbaordFile::ServerFormatDataResponse { + server_conn_id, + remote_conn_id, + msg_flags, + format_data, }; - - let mut msg = Message::new(); - let mut cliprdr = Cliprdr::new(); - cliprdr.set_format_data_response(data); - msg.set_cliprdr(cliprdr); // no need to handle result here - MSG_CHANNEL_CLIENT.0.send((conn_id, msg)).unwrap(); + MSG_CHANNEL_CLIENT.0.send((conn_id, data)).unwrap(); 0 } @@ -479,30 +647,47 @@ extern "C" fn client_file_contents_request( // return ERROR_INVALID_PARAMETER; // } - let mut data = CliprdrFileContentsRequest::default(); + let server_conn_id; + let remote_conn_id; + let stream_id; + let list_index; + let dw_flags; + let n_position_low; + let n_position_high; + let cb_requested; + let have_clip_data_id; + let clip_data_id; unsafe { - data.server_conn_id = (*file_contents_request).serverConnID as i32; - data.remote_conn_id = (*file_contents_request).remoteConnID as i32; - data.stream_id = (*file_contents_request).streamId as i32; - data.list_index = (*file_contents_request).listIndex as i32; - data.dw_flags = (*file_contents_request).dwFlags as i32; - data.n_position_low = (*file_contents_request).nPositionLow as i32; - data.n_position_high = (*file_contents_request).nPositionHigh as i32; - data.cb_requested = (*file_contents_request).cbRequested as i32; - data.have_clip_data_id = (*file_contents_request).haveClipDataId == TRUE; - data.clip_data_id = (*file_contents_request).clipDataId as i32; + server_conn_id = (*file_contents_request).serverConnID as i32; + remote_conn_id = (*file_contents_request).remoteConnID as i32; + stream_id = (*file_contents_request).streamId as i32; + list_index = (*file_contents_request).listIndex as i32; + dw_flags = (*file_contents_request).dwFlags as i32; + n_position_low = (*file_contents_request).nPositionLow as i32; + n_position_high = (*file_contents_request).nPositionHigh as i32; + cb_requested = (*file_contents_request).cbRequested as i32; + have_clip_data_id = (*file_contents_request).haveClipDataId == TRUE; + clip_data_id = (*file_contents_request).clipDataId as i32; } let conn_id = ConnID { - server_conn_id: data.server_conn_id as u32, - remote_conn_id: data.remote_conn_id as u32, + server_conn_id: server_conn_id as u32, + remote_conn_id: remote_conn_id as u32, }; - let mut msg = Message::new(); - let mut cliprdr = Cliprdr::new(); - cliprdr.set_file_contents_request(data); - msg.set_cliprdr(cliprdr); + let data = ClipbaordFile::FileContentsRequest { + server_conn_id, + remote_conn_id, + stream_id, + list_index, + dw_flags, + n_position_low, + n_position_high, + cb_requested, + have_clip_data_id, + clip_data_id, + }; // no need to handle result here - MSG_CHANNEL_CLIENT.0.send((conn_id, msg)).unwrap(); + MSG_CHANNEL_CLIENT.0.send((conn_id, data)).unwrap(); 0 } @@ -513,29 +698,40 @@ extern "C" fn client_file_contents_response( ) -> UINT { log::debug!("client_file_contents_response called"); - let mut data = CliprdrFileContentsResponse::default(); + let server_conn_id; + let remote_conn_id; + let msg_flags; + let stream_id; + let requested_data; unsafe { - data.server_conn_id = (*file_contents_response).serverConnID as i32; - data.remote_conn_id = (*file_contents_response).remoteConnID as i32; - data.msg_flags = (*file_contents_response).msgFlags as i32; - data.stream_id = (*file_contents_response).streamId as i32; - data.requested_data = std::slice::from_raw_parts( - (*file_contents_response).requestedData, - (*file_contents_response).cbRequested as usize, - ) - .to_vec(); + server_conn_id = (*file_contents_response).serverConnID as i32; + remote_conn_id = (*file_contents_response).remoteConnID as i32; + msg_flags = (*file_contents_response).msgFlags as i32; + stream_id = (*file_contents_response).streamId as i32; + if (*file_contents_response).requestedData.is_null() { + requested_data = Vec::new(); + } else { + requested_data = std::slice::from_raw_parts( + (*file_contents_response).requestedData, + (*file_contents_response).cbRequested as usize, + ) + .to_vec(); + } } let conn_id = ConnID { - server_conn_id: data.server_conn_id as u32, - remote_conn_id: data.remote_conn_id as u32, + server_conn_id: server_conn_id as u32, + remote_conn_id: remote_conn_id as u32, }; - let mut msg = Message::new(); - let mut cliprdr = Cliprdr::new(); - cliprdr.set_file_contents_response(data); - msg.set_cliprdr(cliprdr); + let data = ClipbaordFile::FileContentsResponse { + server_conn_id, + remote_conn_id, + msg_flags, + stream_id, + requested_data, + }; // no need to handle result here - MSG_CHANNEL_CLIENT.0.send((conn_id, msg)).unwrap(); + MSG_CHANNEL_CLIENT.0.send((conn_id, data)).unwrap(); 0 } diff --git a/libs/clipboard/src/windows/wf_cliprdr.c b/libs/clipboard/src/windows/wf_cliprdr.c index 99fccfb18..192af18d1 100644 --- a/libs/clipboard/src/windows/wf_cliprdr.c +++ b/libs/clipboard/src/windows/wf_cliprdr.c @@ -228,6 +228,8 @@ struct wf_clipboard HANDLE response_data_event; LPDATAOBJECT data_obj; + HANDLE data_obj_mutex; + ULONG req_fsize; char *req_fdata; HANDLE req_fevent; @@ -252,6 +254,7 @@ typedef struct wf_clipboard wfClipboard; BOOL wf_cliprdr_init(wfClipboard *clipboard, CliprdrClientContext *cliprdr); BOOL wf_cliprdr_uninit(wfClipboard *clipboard, CliprdrClientContext *cliprdr); +BOOL wf_do_empty_cliprdr(wfClipboard *clipboard); static BOOL wf_create_file_obj(UINT32 serverConnID, UINT32 remoteConnID, wfClipboard *cliprdrrdr, IDataObject **ppDataObject); static void wf_destroy_file_obj(IDataObject *instance); @@ -682,6 +685,10 @@ static HRESULT STDMETHODCALLTYPE CliprdrDataObject_GetData(IDataObject *This, FO return E_INVALIDARG; clipboard = (wfClipboard *)instance->m_pData; + if (!clipboard->context->CheckEnabled(instance->m_serverConnID, instance->m_remoteConnID)) + { + return E_INVALIDARG; + } if (!clipboard) return E_INVALIDARG; @@ -699,7 +706,13 @@ static HRESULT STDMETHODCALLTYPE CliprdrDataObject_GetData(IDataObject *This, FO // DWORD remote_format_id = get_remote_format_id(clipboard, instance->m_pFormatEtc[idx].cfFormat); // FIXME: origin code may be failed here??? if (cliprdr_send_data_request(instance->m_serverConnID, instance->m_remoteConnID, clipboard, instance->m_pFormatEtc[idx].cfFormat) != 0) + { return E_UNEXPECTED; + } + if (!clipboard->hmem) + { + return E_UNEXPECTED; + } pMedium->hGlobal = clipboard->hmem; /* points to a FILEGROUPDESCRIPTOR structure */ /* GlobalLock returns a pointer to the first byte of the memory block, @@ -1355,7 +1368,16 @@ static UINT cliprdr_send_format_list(wfClipboard *clipboard) /* Ignore if other app is holding clipboard */ if (try_open_clipboard(clipboard->hwnd)) { + // If current process is running as service with SYSTEM user. + // Clipboard api works fine for text, but copying files works no good. + // GetLastError() returns various error codes count = CountClipboardFormats(); + if (count == 0) + { + CloseClipboard(); + return CHANNEL_RC_NULL_DATA; + } + numFormats = (UINT32)count; formats = (CLIPRDR_FORMAT *)calloc(numFormats, sizeof(CLIPRDR_FORMAT)); @@ -1377,7 +1399,7 @@ static UINT cliprdr_send_format_list(wfClipboard *clipboard) } else { - while (formatId = EnumClipboardFormats(formatId)) + while (formatId = EnumClipboardFormats(formatId) && index < numFormats) formats[index++].formatId = formatId; } @@ -1395,6 +1417,10 @@ static UINT cliprdr_send_format_list(wfClipboard *clipboard) { formats[index].formatName = _strdup(formatName); } + else + { + formats[index].formatName = NULL; + } } } @@ -1408,11 +1434,16 @@ static UINT cliprdr_send_format_list(wfClipboard *clipboard) rc = clipboard->context->ClientFormatList(clipboard->context, &formatList); for (index = 0; index < numFormats; index++) - free(formats[index].formatName); - + { + if (formats[index].formatName != NULL) + { + free(formats[index].formatName); + formats[index].formatName = NULL; + } + } free(formats); + return rc; - // return 0; } static UINT cliprdr_send_data_request(UINT32 serverConnID, UINT32 remoteConnID, wfClipboard *clipboard, UINT32 formatId) @@ -1431,13 +1462,56 @@ static UINT cliprdr_send_data_request(UINT32 serverConnID, UINT32 remoteConnID, formatDataRequest.requestedFormatId = remoteFormatId; clipboard->requestedFormatId = formatId; rc = clipboard->context->ClientFormatDataRequest(clipboard->context, &formatDataRequest); + if (rc != ERROR_SUCCESS) + { + return rc; + } - if (WaitForSingleObject(clipboard->response_data_event, INFINITE) != WAIT_OBJECT_0) - rc = ERROR_INTERNAL_ERROR; - else if (!ResetEvent(clipboard->response_data_event)) - rc = ERROR_INTERNAL_ERROR; + // with default 3min timeout + for (int i = 0; i < 20 * 60 * 3; i++) + { + DWORD waitRes = WaitForSingleObject(clipboard->response_data_event, 50); + if (waitRes == WAIT_TIMEOUT) + { + if (clipboard->context->CheckEnabled(serverConnID, remoteConnID)) + { + continue; + } + else + { + break; + } + } - return rc; + if (waitRes != WAIT_OBJECT_0) + { + return ERROR_INTERNAL_ERROR; + } + + if (!ResetEvent(clipboard->response_data_event)) + { + // NOTE: critical error here, crash may be better + rc = ERROR_INTERNAL_ERROR; + } + + if (!clipboard->hmem) + { + rc = ERROR_INTERNAL_ERROR; + } + + return rc; + } + + if (clipboard->hmem) + { + if (!ResetEvent(clipboard->response_data_event)) + { + // NOTE: critical error here, crash may be better + } + return ERROR_SUCCESS; + } + + return ERROR_INTERNAL_ERROR; } UINT cliprdr_send_request_filecontents(wfClipboard *clipboard, UINT32 serverConnID, UINT32 remoteConnID, const void *streamid, ULONG index, @@ -1461,29 +1535,81 @@ UINT cliprdr_send_request_filecontents(wfClipboard *clipboard, UINT32 serverConn fileContentsRequest.clipDataId = 0; fileContentsRequest.msgFlags = 0; rc = clipboard->context->ClientFileContentsRequest(clipboard->context, &fileContentsRequest); + if (rc != ERROR_SUCCESS) + { + return rc; + } - if (WaitForSingleObject(clipboard->req_fevent, INFINITE) != WAIT_OBJECT_0) - rc = ERROR_INTERNAL_ERROR; - else if (!ResetEvent(clipboard->req_fevent)) - rc = ERROR_INTERNAL_ERROR; + // with default 3min timeout + for (int i = 0; i < 20 * 60 * 3; i++) + { + DWORD waitRes = WaitForSingleObject(clipboard->req_fevent, 50); + if (waitRes == WAIT_TIMEOUT) + { + if (clipboard->context->CheckEnabled(serverConnID, remoteConnID)) + { + continue; + } + else + { + break; + } + } - return rc; + if (waitRes != WAIT_OBJECT_0) + { + return ERROR_INTERNAL_ERROR; + } + + if (!ResetEvent(clipboard->req_fevent)) + { + // NOTE: critical error here, crash may be better + rc = ERROR_INTERNAL_ERROR; + } + if (!clipboard->req_fdata) + { + rc = ERROR_INTERNAL_ERROR; + } + + return rc; + } + + if (clipboard->req_fdata) + { + if (!ResetEvent(clipboard->req_fevent)) + { + // NOTE: critical error here, crash may be better + } + return ERROR_SUCCESS; + } + + return ERROR_INTERNAL_ERROR; } -static UINT cliprdr_send_response_filecontents(wfClipboard *clipboard, UINT32 serverConnID, UINT32 remoteConnID, UINT32 streamId, UINT32 size, - BYTE *data) +static UINT cliprdr_send_response_filecontents( + wfClipboard *clipboard, + UINT32 serverConnID, + UINT32 remoteConnID, + UINT16 msgFlags, + UINT32 streamId, + UINT32 size, + BYTE *data) { CLIPRDR_FILE_CONTENTS_RESPONSE fileContentsResponse; if (!clipboard || !clipboard->context || !clipboard->context->ClientFileContentsResponse) - return ERROR_INTERNAL_ERROR; + { + data = NULL; + size = 0; + msgFlags = CB_RESPONSE_FAIL; + } fileContentsResponse.serverConnID = serverConnID; fileContentsResponse.remoteConnID = remoteConnID; fileContentsResponse.streamId = streamId; fileContentsResponse.cbRequested = size; fileContentsResponse.requestedData = data; - fileContentsResponse.msgFlags = CB_RESPONSE_OK; + fileContentsResponse.msgFlags = msgFlags; return clipboard->context->ClientFileContentsResponse(clipboard->context, &fileContentsResponse); } @@ -1522,7 +1648,6 @@ static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM case WM_CLIPBOARDUPDATE: DEBUG_CLIPRDR("info: WM_CLIPBOARDUPDATE"); - // if (clipboard->sync) { if ((GetClipboardOwner() != clipboard->hwnd) && @@ -1614,6 +1739,11 @@ static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM case OLE_SETCLIPBOARD: DEBUG_CLIPRDR("info: OLE_SETCLIPBOARD"); + if (WaitForSingleObject(clipboard->data_obj_mutex, INFINITE) != WAIT_OBJECT_0) + { + break; + } + if (clipboard->data_obj != NULL) { wf_destroy_file_obj(clipboard->data_obj); @@ -1630,6 +1760,11 @@ static LRESULT CALLBACK cliprdr_proc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM } free((void *)lParam); + if (!ReleaseMutex(clipboard->data_obj_mutex)) + { + // critical error!!! + } + break; case DELAYED_RENDERING: @@ -2270,7 +2405,7 @@ static UINT wf_cliprdr_server_format_data_request(CliprdrClientContext *context, const CLIPRDR_FORMAT_DATA_REQUEST *formatDataRequest) { - UINT rc; + UINT rc = ERROR_SUCCESS; size_t size = 0; void *buff = NULL; char *globlemem = NULL; @@ -2280,12 +2415,18 @@ wf_cliprdr_server_format_data_request(CliprdrClientContext *context, wfClipboard *clipboard; if (!context || !formatDataRequest) - return ERROR_INTERNAL_ERROR; + { + rc = ERROR_INTERNAL_ERROR; + goto exit; + } clipboard = (wfClipboard *)context->custom; if (!clipboard) - return ERROR_INTERNAL_ERROR; + { + rc = ERROR_INTERNAL_ERROR; + goto exit; + } requestedFormatId = formatDataRequest->requestedFormatId; @@ -2303,7 +2444,10 @@ wf_cliprdr_server_format_data_request(CliprdrClientContext *context, result = OleGetClipboard(&dataObj); if (FAILED(result)) - return ERROR_INTERNAL_ERROR; + { + rc = ERROR_INTERNAL_ERROR; + goto exit; + } ZeroMemory(&format_etc, sizeof(FORMATETC)); ZeroMemory(&stg_medium, sizeof(STGMEDIUM)); @@ -2316,7 +2460,7 @@ wf_cliprdr_server_format_data_request(CliprdrClientContext *context, if (FAILED(result)) { - DEBUG_CLIPRDR("dataObj->GetData failed."); + rc = ERROR_INTERNAL_ERROR; goto exit; } @@ -2327,7 +2471,7 @@ wf_cliprdr_server_format_data_request(CliprdrClientContext *context, GlobalUnlock(stg_medium.hGlobal); ReleaseStgMedium(&stg_medium); clipboard->nFiles = 0; - goto exit; + goto resp; } clear_file_array(clipboard); @@ -2344,7 +2488,6 @@ wf_cliprdr_server_format_data_request(CliprdrClientContext *context, else { char *p; - for (p = (char *)((char *)dropFiles + dropFiles->pFiles); (len = strlen(p)) > 0; p += len + 1, clipboard->nFiles++) { @@ -2359,7 +2502,7 @@ wf_cliprdr_server_format_data_request(CliprdrClientContext *context, GlobalUnlock(stg_medium.hGlobal); ReleaseStgMedium(&stg_medium); - exit: + resp: size = 4 + clipboard->nFiles * sizeof(FILEDESCRIPTORW); groupDsc = (FILEGROUPDESCRIPTORW *)malloc(size); @@ -2377,6 +2520,7 @@ wf_cliprdr_server_format_data_request(CliprdrClientContext *context, } IDataObject_Release(dataObj); + rc = ERROR_SUCCESS; } else { @@ -2388,26 +2532,50 @@ wf_cliprdr_server_format_data_request(CliprdrClientContext *context, if (!hClipdata) { CloseClipboard(); - return ERROR_INTERNAL_ERROR; + { + rc = ERROR_INTERNAL_ERROR; + goto exit; + } } - - globlemem = (char *)GlobalLock(hClipdata); - size = (int)GlobalSize(hClipdata); - buff = malloc(size); - CopyMemory(buff, globlemem, size); - GlobalUnlock(hClipdata); - CloseClipboard(); + else + { + globlemem = (char *)GlobalLock(hClipdata); + size = (int)GlobalSize(hClipdata); + buff = malloc(size); + CopyMemory(buff, globlemem, size); + GlobalUnlock(hClipdata); + CloseClipboard(); + rc = ERROR_SUCCESS; + } + } + else + { + rc = ERROR_INTERNAL_ERROR; } } +exit: + if (rc == ERROR_SUCCESS) + { + response.msgFlags = CB_RESPONSE_OK; + } + else + { + response.msgFlags = CB_RESPONSE_FAIL; + } response.serverConnID = formatDataRequest->serverConnID; response.remoteConnID = formatDataRequest->remoteConnID; - response.msgFlags = CB_RESPONSE_OK; response.dataLen = size; response.requestedFormatData = (BYTE *)buff; - rc = clipboard->context->ClientFormatDataResponse(clipboard->context, &response); + if (ERROR_SUCCESS != clipboard->context->ClientFormatDataResponse(clipboard->context, &response)) + { + // CAUTION: if failed to send, server will wait a long time + } - free(buff); + if (buff) + { + free(buff); + } return rc; } @@ -2420,48 +2588,69 @@ static UINT wf_cliprdr_server_format_data_response(CliprdrClientContext *context, const CLIPRDR_FORMAT_DATA_RESPONSE *formatDataResponse) { + UINT rc = ERROR_INTERNAL_ERROR; BYTE *data; HANDLE hMem; wfClipboard *clipboard; - if (!context || !formatDataResponse) - return ERROR_INTERNAL_ERROR; - - if (formatDataResponse->msgFlags != CB_RESPONSE_OK) - return E_FAIL; - - clipboard = (wfClipboard *)context->custom; - - if (!clipboard) - return ERROR_INTERNAL_ERROR; - - hMem = GlobalAlloc(GMEM_MOVEABLE, formatDataResponse->dataLen); - - if (!hMem) - return ERROR_INTERNAL_ERROR; - - data = (BYTE *)GlobalLock(hMem); - - if (!data) + do { - GlobalFree(hMem); - return ERROR_INTERNAL_ERROR; - } + if (!context || !formatDataResponse) + { + rc = ERROR_INTERNAL_ERROR; + break; + } - CopyMemory(data, formatDataResponse->requestedFormatData, formatDataResponse->dataLen); + clipboard = (wfClipboard *)context->custom; + if (!clipboard) + { + rc = ERROR_INTERNAL_ERROR; + break; + } + clipboard->hmem = NULL; - if (!GlobalUnlock(hMem) && GetLastError()) - { - GlobalFree(hMem); - return ERROR_INTERNAL_ERROR; - } + if (formatDataResponse->msgFlags != CB_RESPONSE_OK) + { + // BOOL emptyRes = wf_do_empty_cliprdr((wfClipboard *)context->custom); + // (void)emptyRes; + rc = E_FAIL; + break; + } - clipboard->hmem = hMem; + hMem = GlobalAlloc(GMEM_MOVEABLE, formatDataResponse->dataLen); + if (!hMem) + { + rc = ERROR_INTERNAL_ERROR; + break; + } + + data = (BYTE *)GlobalLock(hMem); + if (!data) + { + GlobalFree(hMem); + rc = ERROR_INTERNAL_ERROR; + break; + } + + CopyMemory(data, formatDataResponse->requestedFormatData, formatDataResponse->dataLen); + + if (!GlobalUnlock(hMem) && GetLastError()) + { + GlobalFree(hMem); + rc = ERROR_INTERNAL_ERROR; + break; + } + + clipboard->hmem = hMem; + rc = CHANNEL_RC_OK; + } while (0); if (!SetEvent(clipboard->response_data_event)) - return ERROR_INTERNAL_ERROR; - - return CHANNEL_RC_OK; + { + // CAUTION: critical error here, process will hang up until wait timeout default 3min. + rc = ERROR_INTERNAL_ERROR; + } + return rc; } /** @@ -2488,12 +2677,18 @@ wf_cliprdr_server_file_contents_request(CliprdrClientContext *context, UINT32 cbRequested; if (!context || !fileContentsRequest) - return ERROR_INTERNAL_ERROR; + { + rc = ERROR_INTERNAL_ERROR; + goto exit; + } clipboard = (wfClipboard *)context->custom; if (!clipboard) - return ERROR_INTERNAL_ERROR; + { + rc = ERROR_INTERNAL_ERROR; + goto exit; + } cbRequested = fileContentsRequest->cbRequested; if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE) @@ -2502,14 +2697,18 @@ wf_cliprdr_server_file_contents_request(CliprdrClientContext *context, pData = (BYTE *)calloc(1, cbRequested); if (!pData) - goto error; + { + rc = ERROR_INTERNAL_ERROR; + goto exit; + } hRet = OleGetClipboard(&pDataObj); if (FAILED(hRet)) { printf("filecontents: get ole clipboard failed.\n"); - goto error; + rc = ERROR_INTERNAL_ERROR; + goto exit; } ZeroMemory(&vFormatEtc, sizeof(FORMATETC)); @@ -2593,7 +2792,10 @@ wf_cliprdr_server_file_contents_request(CliprdrClientContext *context, if (fileContentsRequest->dwFlags == FILECONTENTS_SIZE) { if (clipboard->nFiles <= fileContentsRequest->listIndex) - goto error; + { + rc = ERROR_INTERNAL_ERROR; + goto exit; + } *((UINT32 *)&pData[0]) = clipboard->fileDescriptor[fileContentsRequest->listIndex]->nFileSizeLow; *((UINT32 *)&pData[4]) = @@ -2604,7 +2806,10 @@ wf_cliprdr_server_file_contents_request(CliprdrClientContext *context, { BOOL bRet; if (clipboard->nFiles <= fileContentsRequest->listIndex) - goto error; + { + rc = ERROR_INTERNAL_ERROR; + goto exit; + } bRet = wf_cliprdr_get_file_contents( clipboard->file_names[fileContentsRequest->listIndex], pData, fileContentsRequest->nPositionLow, fileContentsRequest->nPositionHigh, cbRequested, @@ -2614,21 +2819,30 @@ wf_cliprdr_server_file_contents_request(CliprdrClientContext *context, { printf("get file contents failed.\n"); uSize = 0; - goto error; + rc = ERROR_INTERNAL_ERROR; + goto exit; } } } rc = CHANNEL_RC_OK; -error: +exit: if (pDataObj) IDataObject_Release(pDataObj); + if (rc != CHANNEL_RC_OK) + { + uSize = 0; + } + if (uSize == 0) { - free(pData); - pData = NULL; + if (pData) + { + free(pData); + pData = NULL; + } } sRc = @@ -2636,13 +2850,18 @@ error: clipboard, fileContentsRequest->serverConnID, fileContentsRequest->remoteConnID, + rc == CHANNEL_RC_OK ? CB_RESPONSE_OK : CB_RESPONSE_FAIL, fileContentsRequest->streamId, uSize, pData); - free(pData); - if (sRc != CHANNEL_RC_OK) - return sRc; + if (pData) + { + free(pData); + } + + // if (sRc != CHANNEL_RC_OK) + // return sRc; return rc; } @@ -2657,34 +2876,50 @@ wf_cliprdr_server_file_contents_response(CliprdrClientContext *context, const CLIPRDR_FILE_CONTENTS_RESPONSE *fileContentsResponse) { wfClipboard *clipboard; + UINT rc = ERROR_INTERNAL_ERROR; - if (!context || !fileContentsResponse) - return ERROR_INTERNAL_ERROR; + do + { + if (!context || !fileContentsResponse) + { + rc = ERROR_INTERNAL_ERROR; + break; + } - if (fileContentsResponse->msgFlags != CB_RESPONSE_OK) - return E_FAIL; + clipboard = (wfClipboard *)context->custom; + if (!clipboard) + { + rc = ERROR_INTERNAL_ERROR; + break; + } + clipboard->req_fsize = 0; + clipboard->req_fdata = NULL; - clipboard = (wfClipboard *)context->custom; + if (fileContentsResponse->msgFlags != CB_RESPONSE_OK) + { + rc = E_FAIL; + break; + } - if (!clipboard) - return ERROR_INTERNAL_ERROR; + clipboard->req_fsize = fileContentsResponse->cbRequested; + clipboard->req_fdata = (char *)malloc(fileContentsResponse->cbRequested); + if (!clipboard->req_fdata) + { + rc = ERROR_INTERNAL_ERROR; + break; + } - clipboard->req_fsize = fileContentsResponse->cbRequested; - clipboard->req_fdata = (char *)malloc(fileContentsResponse->cbRequested); + CopyMemory(clipboard->req_fdata, fileContentsResponse->requestedData, + fileContentsResponse->cbRequested); - if (!clipboard->req_fdata) - return ERROR_INTERNAL_ERROR; - - CopyMemory(clipboard->req_fdata, fileContentsResponse->requestedData, - fileContentsResponse->cbRequested); + rc = CHANNEL_RC_OK; + } while (0); if (!SetEvent(clipboard->req_fevent)) { - free(clipboard->req_fdata); - return ERROR_INTERNAL_ERROR; + // CAUTION: critical error here, process will hang up until wait timeout default 3min. } - - return CHANNEL_RC_OK; + return rc; } BOOL wf_cliprdr_init(wfClipboard *clipboard, CliprdrClientContext *cliprdr) @@ -2720,6 +2955,9 @@ BOOL wf_cliprdr_init(wfClipboard *clipboard, CliprdrClientContext *cliprdr) if (!(clipboard->response_data_event = CreateEvent(NULL, TRUE, FALSE, NULL))) goto error; + if (!(clipboard->data_obj_mutex = CreateMutex(NULL, FALSE, "data_obj_mutex"))) + goto error; + if (!(clipboard->req_fevent = CreateEvent(NULL, TRUE, FALSE, NULL))) goto error; @@ -2750,6 +2988,23 @@ BOOL wf_cliprdr_uninit(wfClipboard *clipboard, CliprdrClientContext *cliprdr) cliprdr->custom = NULL; + /* discard all contexts in clipboard */ + if (try_open_clipboard(clipboard->hwnd)) + { + if (!EmptyClipboard()) + { + DEBUG_CLIPRDR("EmptyClipboard failed with 0x%x", GetLastError()); + } + if (!CloseClipboard()) + { + // critical error!!! + } + } + else + { + DEBUG_CLIPRDR("OpenClipboard failed with 0x%x", GetLastError()); + } + if (clipboard->hwnd) PostMessage(clipboard->hwnd, WM_QUIT, 0, 0); @@ -2768,6 +3023,9 @@ BOOL wf_cliprdr_uninit(wfClipboard *clipboard, CliprdrClientContext *cliprdr) if (clipboard->response_data_event) CloseHandle(clipboard->response_data_event); + if (clipboard->data_obj_mutex) + CloseHandle(clipboard->data_obj_mutex); + if (clipboard->req_fevent) CloseHandle(clipboard->req_fevent); @@ -2788,3 +3046,86 @@ BOOL uninit_cliprdr(CliprdrClientContext *context) { return wf_cliprdr_uninit(&clipboard, context); } + +BOOL empty_cliprdr(CliprdrClientContext *context, UINT32 server_conn_id, UINT32 remote_conn_id) +{ + wfClipboard *clipboard = NULL; + CliprdrDataObject *instance = NULL; + BOOL rc = FALSE; + if (!context) + { + return FALSE; + } + if (server_conn_id == 0 && remote_conn_id == 0) + { + return TRUE; + } + + clipboard = (wfClipboard *)context->custom; + if (!clipboard) + { + return FALSE; + } + + instance = clipboard->data_obj; + if (instance) + { + if (server_conn_id != 0 && instance->m_serverConnID != server_conn_id) + { + return TRUE; + } + if (remote_conn_id != 0 && instance->m_remoteConnID != remote_conn_id) + { + return TRUE; + } + } + + return wf_do_empty_cliprdr(clipboard); +} + +BOOL wf_do_empty_cliprdr(wfClipboard *clipboard) +{ + BOOL rc = FALSE; + if (!clipboard) + { + return FALSE; + } + + if (WaitForSingleObject(clipboard->data_obj_mutex, INFINITE) != WAIT_OBJECT_0) + { + return FALSE; + } + + do + { + if (clipboard->data_obj != NULL) + { + wf_destroy_file_obj(clipboard->data_obj); + clipboard->data_obj = NULL; + } + + /* discard all contexts in clipboard */ + if (!try_open_clipboard(clipboard->hwnd)) + { + DEBUG_CLIPRDR("OpenClipboard failed with 0x%x", GetLastError()); + rc = FALSE; + break; + } + + if (!EmptyClipboard()) + { + rc = FALSE; + } + if (!CloseClipboard()) + { + // critical error!!! + } + rc = TRUE; + } while (0); + + if (!ReleaseMutex(clipboard->data_obj_mutex)) + { + // critical error!!! + } + return rc; +} diff --git a/src/clipboard_file.rs b/src/clipboard_file.rs new file mode 100644 index 000000000..093dcd90a --- /dev/null +++ b/src/clipboard_file.rs @@ -0,0 +1,207 @@ +use clipboard::ClipbaordFile; +use hbb_common::message_proto::*; + +pub fn clip_2_msg(clip: ClipbaordFile) -> Message { + match clip { + ClipbaordFile::ServerFormatList { + server_conn_id, + remote_conn_id, + format_list, + } => { + let mut formats: Vec = Vec::new(); + for v in format_list.iter() { + formats.push(CliprdrFormat { + server_conn_id: 0, + remote_conn_id: 0, + id: v.0, + format: v.1.clone(), + ..Default::default() + }); + } + Message { + union: Some(message::Union::cliprdr(Cliprdr { + union: Some(cliprdr::Union::format_list(CliprdrServerFormatList { + server_conn_id, + remote_conn_id, + formats, + ..Default::default() + })), + ..Default::default() + })), + ..Default::default() + } + } + ClipbaordFile::ServerFormatListResponse { + server_conn_id, + remote_conn_id, + msg_flags, + } => Message { + union: Some(message::Union::cliprdr(Cliprdr { + union: Some(cliprdr::Union::format_list_response( + CliprdrServerFormatListResponse { + server_conn_id, + remote_conn_id, + msg_flags, + ..Default::default() + }, + )), + ..Default::default() + })), + ..Default::default() + }, + ClipbaordFile::ServerFormatDataRequest { + server_conn_id, + remote_conn_id, + requested_format_id, + } => Message { + union: Some(message::Union::cliprdr(Cliprdr { + union: Some(cliprdr::Union::format_data_request( + CliprdrServerFormatDataRequest { + server_conn_id, + remote_conn_id, + requested_format_id, + ..Default::default() + }, + )), + ..Default::default() + })), + ..Default::default() + }, + ClipbaordFile::ServerFormatDataResponse { + server_conn_id, + remote_conn_id, + msg_flags, + format_data, + } => Message { + union: Some(message::Union::cliprdr(Cliprdr { + union: Some(cliprdr::Union::format_data_response( + CliprdrServerFormatDataResponse { + server_conn_id, + remote_conn_id, + msg_flags, + format_data, + ..Default::default() + }, + )), + ..Default::default() + })), + ..Default::default() + }, + ClipbaordFile::FileContentsRequest { + server_conn_id, + remote_conn_id, + stream_id, + list_index, + dw_flags, + n_position_low, + n_position_high, + cb_requested, + have_clip_data_id, + clip_data_id, + } => Message { + union: Some(message::Union::cliprdr(Cliprdr { + union: Some(cliprdr::Union::file_contents_request( + CliprdrFileContentsRequest { + server_conn_id, + remote_conn_id, + stream_id, + list_index, + dw_flags, + n_position_low, + n_position_high, + cb_requested, + have_clip_data_id, + clip_data_id, + ..Default::default() + }, + )), + ..Default::default() + })), + ..Default::default() + }, + ClipbaordFile::FileContentsResponse { + server_conn_id, + remote_conn_id, + msg_flags, + stream_id, + requested_data, + } => Message { + union: Some(message::Union::cliprdr(Cliprdr { + union: Some(cliprdr::Union::file_contents_response( + CliprdrFileContentsResponse { + server_conn_id, + remote_conn_id, + msg_flags, + stream_id, + requested_data, + ..Default::default() + }, + )), + ..Default::default() + })), + ..Default::default() + }, + } +} + +pub fn msg_2_clip(msg: Cliprdr) -> Option { + match msg.union { + Some(cliprdr::Union::format_list(data)) => { + let mut format_list: Vec<(i32, String)> = Vec::new(); + for v in data.formats.iter() { + format_list.push((v.id, v.format.clone())); + } + Some(ClipbaordFile::ServerFormatList { + server_conn_id: data.server_conn_id, + remote_conn_id: data.remote_conn_id, + format_list, + }) + } + Some(cliprdr::Union::format_list_response(data)) => { + Some(ClipbaordFile::ServerFormatListResponse { + server_conn_id: data.server_conn_id, + remote_conn_id: data.remote_conn_id, + msg_flags: data.msg_flags, + }) + } + Some(cliprdr::Union::format_data_request(data)) => { + Some(ClipbaordFile::ServerFormatDataRequest { + server_conn_id: data.server_conn_id, + remote_conn_id: data.remote_conn_id, + requested_format_id: data.requested_format_id, + }) + } + Some(cliprdr::Union::format_data_response(data)) => { + Some(ClipbaordFile::ServerFormatDataResponse { + server_conn_id: data.server_conn_id, + remote_conn_id: data.remote_conn_id, + msg_flags: data.msg_flags, + format_data: data.format_data, + }) + } + Some(cliprdr::Union::file_contents_request(data)) => { + Some(ClipbaordFile::FileContentsRequest { + server_conn_id: data.server_conn_id, + remote_conn_id: data.remote_conn_id, + stream_id: data.stream_id, + list_index: data.list_index, + dw_flags: data.dw_flags, + n_position_low: data.n_position_low, + n_position_high: data.n_position_high, + cb_requested: data.cb_requested, + have_clip_data_id: data.have_clip_data_id, + clip_data_id: data.clip_data_id, + }) + } + Some(cliprdr::Union::file_contents_response(data)) => { + Some(ClipbaordFile::FileContentsResponse { + server_conn_id: data.server_conn_id, + remote_conn_id: data.remote_conn_id, + msg_flags: data.msg_flags, + stream_id: data.stream_id, + requested_data: data.requested_data, + }) + } + _ => None, + } +} diff --git a/src/ipc.rs b/src/ipc.rs index 30c643ec5..41f59bddb 100644 --- a/src/ipc.rs +++ b/src/ipc.rs @@ -1,4 +1,5 @@ use crate::rendezvous_mediator::RendezvousMediator; +pub use clipboard::ClipbaordFile; use hbb_common::{ allow_err, bail, bytes, bytes_codec::BytesCodec, @@ -73,6 +74,7 @@ pub enum Data { clipboard: bool, audio: bool, file: bool, + file_transfer_enabled: bool, }, ChatMessage { text: String, @@ -103,6 +105,8 @@ pub enum Data { to: String, }, SyncConfigToUserResp(bool), + ClipbaordFile(ClipbaordFile), + ClipboardFileEnabled(bool), } #[tokio::main(flavor = "current_thread")] diff --git a/src/lib.rs b/src/lib.rs index 2df657ab0..0f788cb67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,3 +28,6 @@ pub mod cli; #[cfg(not(any(target_os = "android", target_os = "ios")))] mod port_forward; mod lang; + +#[cfg(windows)] +pub mod clipboard_file; diff --git a/src/server.rs b/src/server.rs index a1d0454ae..ef0070d77 100644 --- a/src/server.rs +++ b/src/server.rs @@ -29,8 +29,6 @@ use std::{ pub mod audio_service; mod clipboard_service; -#[cfg(windows)] -pub mod clipboard_file_service; mod connection; pub mod input_service; mod service; @@ -63,8 +61,6 @@ pub fn new() -> ServerPtr { server.add_service(Box::new(audio_service::new())); server.add_service(Box::new(video_service::new())); server.add_service(Box::new(clipboard_service::new())); - #[cfg(windows)] - server.add_service(Box::new(clipboard_file_service::new())); server.add_service(Box::new(input_service::new_cursor())); server.add_service(Box::new(input_service::new_pos())); Arc::new(RwLock::new(server)) diff --git a/src/server/clipboard_file_service.rs b/src/server/clipboard_file_service.rs deleted file mode 100644 index 718f44434..000000000 --- a/src/server/clipboard_file_service.rs +++ /dev/null @@ -1,104 +0,0 @@ -use super::*; -use clipboard::{create_cliprdr_context, get_rx_client_msg, server_msg, ConnID}; -use hbb_common::{ - log, - tokio::sync::{ - mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender}, - Mutex as TokioMutex, - }, - tokio::time::{self, Duration, Instant}, - ResultType, -}; -use std::sync::atomic::{AtomicBool, Ordering}; - -pub const NAME: &'static str = "cliprdr"; - -lazy_static::lazy_static! { - static ref MSG_CHANNEL_SERVER: (UnboundedSender<(ConnID, Cliprdr)>, TokioMutex>) = { - let (tx, rx) = unbounded_channel(); - (tx, TokioMutex::new(rx)) - }; -} - -static RUNNING: AtomicBool = AtomicBool::new(false); - -pub fn new() -> GenericService { - let sp = GenericService::new(NAME, true); - sp.run::<_>(listen::run); - sp -} - -pub fn handle_serve_cliprdr_msg(id: i32, msg: Cliprdr) { - if RUNNING.load(Ordering::SeqCst) { - log::debug!("handle handle_serve_cliprdr_msg"); - MSG_CHANNEL_SERVER - .0 - .send(( - ConnID { - server_conn_id: id as u32, - remote_conn_id: 0, - }, - msg, - )) - .unwrap(); - } else { - // should not reach this branch - } -} - -mod listen { - use super::*; - - static WAIT: Duration = Duration::from_millis(1500); - - #[tokio::main] - pub async fn run(sp: GenericService) -> ResultType<()> { - let mut cliprdr_context = create_cliprdr_context(true, false)?; - - RUNNING.store(false, Ordering::SeqCst); - - let mut timer = time::interval_at(Instant::now() + WAIT, WAIT); - let mut client_rx = get_rx_client_msg().lock().await; - let mut server_rx = MSG_CHANNEL_SERVER.1.lock().await; - while sp.ok() { - RUNNING.store(true, Ordering::SeqCst); - - tokio::select! { - msg = client_rx.recv() => { - match msg { - Some((conn_id, msg)) => { - if conn_id.server_conn_id == 0 { - sp.send(msg) - } else { - sp.send_to(msg, conn_id.server_conn_id as i32) - } - } - None => { - // unreachable!() - } - } - } - msg = server_rx.recv() => { - match msg { - Some((conn_id, msg)) => { - let res = server_msg(&mut cliprdr_context, conn_id, msg); - if res != 0 { - // log::warn!("failed to process message for {}", id); - } - } - None => { - // unreachable!() - } - } - } - _ = timer.tick() => {}, - } - sp.snapshot(|_| Ok(()))?; - } - - RUNNING.store(false, Ordering::SeqCst); - log::info!("Clipboard listener stopped!"); - - Ok(()) - } -} diff --git a/src/server/connection.rs b/src/server/connection.rs index 2e4f40aa3..97413e0ce 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -1,4 +1,7 @@ use super::{input_service::*, *}; +#[cfg(windows)] +use crate::{clipboard_file::*, common::update_clipboard, ipc}; +#[cfg(not(windows))] use crate::{common::update_clipboard, ipc}; use hbb_common::{ config::Config, @@ -243,17 +246,18 @@ impl Connection { } else if &name == "file" { conn.file = enabled; conn.send_permission(Permission::File, enabled).await; - #[cfg(windows)] - if let Some(s) = conn.server.upgrade() { - s.write().unwrap().subscribe( - super::clipboard_file_service::NAME, - conn.inner.clone(), conn.file_transfer_enabled()); - } + conn.send_to_cm(ipc::Data::ClipboardFileEnabled(conn.file_transfer_enabled())); } } ipc::Data::RawMessage(bytes) => { allow_err!(conn.stream.send_raw(bytes).await); } + ipc::Data::ClipbaordFile(_clip) => { + if conn.file_transfer_enabled() { + #[cfg(windows)] + allow_err!(conn.stream.send(&clip_2_msg(_clip)).await); + } + } _ => {} } }, @@ -620,10 +624,6 @@ impl Connection { if !self.audio_enabled() { noperms.push(super::audio_service::NAME); } - if !self.file_transfer_enabled() { - #[cfg(windows)] - noperms.push(super::clipboard_file_service::NAME); - } s.write() .unwrap() .add_connection(self.inner.clone(), &noperms); @@ -655,6 +655,7 @@ impl Connection { clipboard: self.clipboard, audio: self.audio, file: self.file, + file_transfer_enabled: self.file_transfer_enabled(), }); } @@ -849,9 +850,13 @@ impl Connection { update_clipboard(cb, None); } } - #[cfg(windows)] - Some(message::Union::cliprdr(clip)) => { - clipboard_file_service::handle_serve_cliprdr_msg(self.inner.id, clip) + Some(message::Union::cliprdr(_clip)) => { + if self.file_transfer_enabled() { + #[cfg(windows)] + if let Some(clip) = msg_2_clip(_clip) { + self.send_to_cm(ipc::Data::ClipbaordFile(clip)) + } + } } Some(message::Union::file_action(fa)) => { if self.file_transfer.is_some() { @@ -1017,13 +1022,7 @@ impl Connection { if let Ok(q) = o.enable_file_transfer.enum_value() { if q != BoolOption::NotSet { self.enable_file_transfer = q == BoolOption::Yes; - if let Some(s) = self.server.upgrade() { - s.write().unwrap().subscribe( - super::clipboard_file_service::NAME, - self.inner.clone(), - self.file_transfer_enabled(), - ); - } + self.send_to_cm(ipc::Data::ClipboardFileEnabled(self.file_transfer_enabled())); } } if let Ok(q) = o.disable_clipboard.enum_value() { diff --git a/src/ui/cm.rs b/src/ui/cm.rs index 138324471..f7fe3b1e8 100644 --- a/src/ui/cm.rs +++ b/src/ui/cm.rs @@ -1,4 +1,9 @@ use crate::ipc::{self, new_listener, Connection, Data}; +#[cfg(windows)] +use clipboard::{ + create_cliprdr_context, empty_clipboard, get_rx_clip_client, server_clip_file, + set_conn_enabled, ConnID, +}; use hbb_common::{ allow_err, config::{Config, ICON}, @@ -106,6 +111,8 @@ impl ConnectionManager { &self, id: i32, data: Data, + _tx_clip_file: &mpsc::UnboundedSender<(i32, ipc::ClipbaordFile)>, + _tx_clip_empty: &mpsc::UnboundedSender, write_jobs: &mut Vec, conn: &mut Connection, ) { @@ -186,6 +193,17 @@ impl ConnectionManager { } } }, + Data::ClipbaordFile(_clip) => { + #[cfg(windows)] + allow_err!(_tx_clip_file.send((id, _clip))); + } + Data::ClipboardFileEnabled(enabled) => { + #[cfg(windows)] + set_conn_enabled(id, 0, enabled); + if !enabled { + allow_err!(_tx_clip_empty.send(id)); + } + } _ => {} } } @@ -326,6 +344,13 @@ impl sciter::EventHandler for ConnectionManager { #[tokio::main(flavor = "current_thread")] async fn start_ipc(cm: ConnectionManager) { + let (tx_file, _rx_file) = mpsc::unbounded_channel::<(i32, ipc::ClipbaordFile)>(); + let (tx_clip_empty, _rx_clip_empty) = mpsc::unbounded_channel::(); + #[cfg(windows)] + let cm_clip = cm.clone(); + #[cfg(windows)] + std::thread::spawn(move || start_clipboard_file(cm_clip, _rx_file, _rx_clip_empty)); + match new_listener("_cm").await { Ok(mut incoming) => { while let Some(result) = incoming.next().await { @@ -333,6 +358,8 @@ async fn start_ipc(cm: ConnectionManager) { Ok(stream) => { let mut stream = Connection::new(stream); let cm = cm.clone(); + let tx_file = tx_file.clone(); + let tx_clip_empty = tx_clip_empty.clone(); tokio::spawn(async move { let mut conn_id: i32 = 0; let (tx, mut rx) = mpsc::unbounded_channel::(); @@ -347,16 +374,22 @@ async fn start_ipc(cm: ConnectionManager) { } Ok(Some(data)) => { match data { - Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file} => { + Data::Login{id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, file_transfer_enabled} => { conn_id = id; + #[cfg(windows)] + set_conn_enabled(id, 0, file_transfer_enabled); + let _ = file_transfer_enabled; cm.add_connection(id, is_file_transfer, port_forward, peer_id, name, authorized, keyboard, clipboard, audio, file, tx.clone()); } Data::Close => { + allow_err!(tx_clip_empty.send(conn_id)); + #[cfg(windows)] + set_conn_enabled(conn_id, 0, false); log::info!("cm ipc connection closed from connection request"); break; } _ => { - cm.handle_data(conn_id, data, &mut write_jobs, &mut stream).await; + cm.handle_data(conn_id, data, &tx_file, &tx_clip_empty, &mut write_jobs, &mut stream).await; } } } @@ -465,3 +498,79 @@ async fn start_pa() { } } } + +#[cfg(windows)] +#[tokio::main(flavor = "current_thread")] +async fn start_clipboard_file( + cm: ConnectionManager, + mut rx: mpsc::UnboundedReceiver<(i32, ipc::ClipbaordFile)>, + mut rx_clip_empty: mpsc::UnboundedReceiver, +) { + let mut cliprdr_context = match create_cliprdr_context(true, false) { + Ok(context) => { + log::info!("clipboard context for file transfer created."); + context + } + Err(err) => { + log::error!( + "Create clipboard context for file transfer: {}", + err.to_string() + ); + return; + } + }; + let mut rx_clip_client = get_rx_clip_client().lock().await; + + loop { + tokio::select! { + clip_file = rx_clip_client.recv() => match clip_file { + Some((conn_id, clip)) => { + cmd_inner_send( + &cm, + conn_id.server_conn_id as i32, + Data::ClipbaordFile(clip) + ); + } + None => { + // + } + }, + server_msg = rx.recv() => match server_msg { + Some((server_conn_id, clip)) => { + let conn_id = ConnID { + server_conn_id: server_conn_id as u32, + remote_conn_id: 0, + }; + server_clip_file(&mut cliprdr_context, conn_id, clip); + } + None => { + break + } + }, + server_conn_id = rx_clip_empty.recv() => match server_conn_id { + Some(server_conn_id) => { + if !empty_clipboard(&mut cliprdr_context, server_conn_id, 0) { + // log::error!("failed to empty clipboard"); + } + } + None => { + break + } + } + } + } +} + +#[cfg(windows)] +fn cmd_inner_send(cm: &ConnectionManager, id: i32, data: Data) { + let lock = cm.read().unwrap(); + if id != 0 { + if let Some(s) = lock.senders.get(&id) { + allow_err!(s.send(data)); + } + } else { + for s in lock.senders.values() { + allow_err!(s.send(data.clone())); + } + } +} diff --git a/src/ui/remote.rs b/src/ui/remote.rs index eb99fa302..d13715178 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -1,12 +1,18 @@ -use crate::client::*; -use crate::common::{ - self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL, +#[cfg(windows)] +use crate::{ + client::*, + clipboard_file::*, + common::{self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL}, +}; +#[cfg(not(windows))] +use crate::{ + client::*, + common::{self, check_clipboard, update_clipboard, ClipboardContext, CLIPBOARD_INTERVAL}, }; #[cfg(windows)] use clipboard::{ cliprdr::CliprdrClientContext, create_cliprdr_context as create_clipboard_file_context, - get_rx_client_msg as get_clipboard_file_rx_client_msg, server_msg as clipboard_file_msg, - ConnID as ClipboardFileConnID, + get_rx_clip_client, server_clip_file, ConnID as ClipboardFileConnID, }; use enigo::{self, Enigo, KeyboardControllable}; use hbb_common::{ @@ -1313,9 +1319,9 @@ impl Remote { // just build for now #[cfg(not(windows))] - let (_, mut clipboard_file_client_rx) = mpsc::unbounded_channel::(); + let (_, mut rx_clip_client) = mpsc::unbounded_channel::(); #[cfg(windows)] - let mut clipboard_file_client_rx = get_clipboard_file_rx_client_msg().lock().await; + let mut rx_clip_client = get_rx_clip_client().lock().await; loop { tokio::select! { @@ -1347,12 +1353,12 @@ impl Remote { } } } - _msg = clipboard_file_client_rx.recv() => { + _msg = rx_clip_client.recv() => { #[cfg(windows)] match _msg { - Some((conn_id, msg)) => { + Some((conn_id, clip)) => { if conn_id.remote_conn_id == 0 || conn_id.remote_conn_id == self.pid { - allow_err!(peer.send(&msg).await); + allow_err!(peer.send(&clip_2_msg(clip)).await); } } None => { @@ -1740,14 +1746,16 @@ impl Remote { Some(message::Union::cliprdr(clip)) => { if !self.handler.lc.read().unwrap().disable_clipboard { if let Some(context) = &mut self.clipboard_file_context { - clipboard_file_msg( - context, - ClipboardFileConnID { - server_conn_id: 0, - remote_conn_id: self.pid, - }, - clip, - ); + if let Some(clip) = msg_2_clip(clip) { + server_clip_file( + context, + ClipboardFileConnID { + server_conn_id: 0, + remote_conn_id: self.pid, + }, + clip, + ); + } } } }