673 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			673 lines
		
	
	
		
			20 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use cliprdr::*;
 | |
| use hbb_common::{
 | |
|     allow_err, log,
 | |
|     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::{Arc, Mutex, RwLock},
 | |
| };
 | |
| 
 | |
| pub mod cliprdr;
 | |
| pub mod context_send;
 | |
| pub use context_send::*;
 | |
| 
 | |
| #[derive(Debug, Serialize, Deserialize, Clone)]
 | |
| #[serde(tag = "t", content = "c")]
 | |
| pub enum ClipbaordFile {
 | |
|     MonitorReady,
 | |
|     FormatList {
 | |
|         format_list: Vec<(i32, String)>,
 | |
|     },
 | |
|     FormatListResponse {
 | |
|         msg_flags: i32,
 | |
|     },
 | |
|     FormatDataRequest {
 | |
|         requested_format_id: i32,
 | |
|     },
 | |
|     FormatDataResponse {
 | |
|         msg_flags: i32,
 | |
|         format_data: Vec<u8>,
 | |
|     },
 | |
|     FileContentsRequest {
 | |
|         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 {
 | |
|         msg_flags: i32,
 | |
|         stream_id: i32,
 | |
|         requested_data: Vec<u8>,
 | |
|     },
 | |
| }
 | |
| 
 | |
| #[derive(Default)]
 | |
| struct ConnEnabled {
 | |
|     conn_enabled: HashMap<i32, bool>,
 | |
| }
 | |
| 
 | |
| struct MsgChannel {
 | |
|     peer_id: String,
 | |
|     conn_id: i32,
 | |
|     sender: UnboundedSender<ClipbaordFile>,
 | |
|     receiver: Arc<TokioMutex<UnboundedReceiver<ClipbaordFile>>>,
 | |
| }
 | |
| 
 | |
| #[derive(PartialEq)]
 | |
| pub enum ProcessSide {
 | |
|     UnknownSide,
 | |
|     ClientSide,
 | |
|     ServerSide,
 | |
| }
 | |
| 
 | |
| lazy_static::lazy_static! {
 | |
|     static ref VEC_MSG_CHANNEL: RwLock<Vec<MsgChannel>> = Default::default();
 | |
|     static ref CLIP_CONN_ENABLED: Mutex<ConnEnabled> = Mutex::new(ConnEnabled::default());
 | |
|     static ref PROCESS_SIDE: RwLock<ProcessSide> = RwLock::new(ProcessSide::UnknownSide);
 | |
| }
 | |
| 
 | |
| pub fn get_client_conn_id(peer_id: &str) -> Option<i32> {
 | |
|     VEC_MSG_CHANNEL
 | |
|         .read()
 | |
|         .unwrap()
 | |
|         .iter()
 | |
|         .find(|x| x.peer_id == peer_id.to_owned())
 | |
|         .map(|x| x.conn_id)
 | |
| }
 | |
| 
 | |
| pub fn get_rx_cliprdr_client(
 | |
|     peer_id: &str,
 | |
| ) -> (i32, Arc<TokioMutex<UnboundedReceiver<ClipbaordFile>>>) {
 | |
|     let mut lock = VEC_MSG_CHANNEL.write().unwrap();
 | |
|     match lock.iter().find(|x| x.peer_id == peer_id.to_owned()) {
 | |
|         Some(msg_channel) => (msg_channel.conn_id, msg_channel.receiver.clone()),
 | |
|         None => {
 | |
|             let (sender, receiver) = unbounded_channel();
 | |
|             let receiver = Arc::new(TokioMutex::new(receiver));
 | |
|             let receiver2 = receiver.clone();
 | |
|             let conn_id = lock.len() as i32 + 1;
 | |
|             let msg_channel = MsgChannel {
 | |
|                 peer_id: peer_id.to_owned(),
 | |
|                 conn_id,
 | |
|                 sender,
 | |
|                 receiver,
 | |
|             };
 | |
|             lock.push(msg_channel);
 | |
|             (conn_id, receiver2)
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn get_rx_cliprdr_server(conn_id: i32) -> Arc<TokioMutex<UnboundedReceiver<ClipbaordFile>>> {
 | |
|     let mut lock = VEC_MSG_CHANNEL.write().unwrap();
 | |
|     match lock.iter().find(|x| x.conn_id == conn_id) {
 | |
|         Some(msg_channel) => msg_channel.receiver.clone(),
 | |
|         None => {
 | |
|             let (sender, receiver) = unbounded_channel();
 | |
|             let receiver = Arc::new(TokioMutex::new(receiver));
 | |
|             let receiver2 = receiver.clone();
 | |
|             let msg_channel = MsgChannel {
 | |
|                 peer_id: "".to_owned(),
 | |
|                 conn_id,
 | |
|                 sender,
 | |
|                 receiver,
 | |
|             };
 | |
|             lock.push(msg_channel);
 | |
|             receiver2
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| #[inline]
 | |
| fn send_data(conn_id: i32, data: ClipbaordFile) {
 | |
|     // no need to handle result here
 | |
|     if let Some(msg_channel) = VEC_MSG_CHANNEL
 | |
|         .read()
 | |
|         .unwrap()
 | |
|         .iter()
 | |
|         .find(|x| x.conn_id == conn_id)
 | |
|     {
 | |
|         allow_err!(msg_channel.sender.send(data));
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn set_conn_enabled(conn_id: i32, enabled: bool) {
 | |
|     let mut lock = CLIP_CONN_ENABLED.lock().unwrap();
 | |
|     if conn_id != 0 {
 | |
|         let _ = lock.conn_enabled.insert(conn_id, enabled);
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn empty_clipboard(context: &mut Box<CliprdrClientContext>, conn_id: i32) -> bool {
 | |
|     unsafe { TRUE == cliprdr::empty_cliprdr(&mut (**context), conn_id as u32) }
 | |
| }
 | |
| 
 | |
| pub fn server_clip_file(
 | |
|     context: &mut Box<CliprdrClientContext>,
 | |
|     conn_id: i32,
 | |
|     msg: ClipbaordFile,
 | |
| ) -> u32 {
 | |
|     match msg {
 | |
|         ClipbaordFile::MonitorReady => {
 | |
|             log::debug!("server_monitor_ready called");
 | |
|             let ret = server_monitor_ready(context, conn_id);
 | |
|             log::debug!("server_monitor_ready called, return {}", ret);
 | |
|             ret
 | |
|         }
 | |
|         ClipbaordFile::FormatList { format_list } => {
 | |
|             log::debug!("server_format_list called");
 | |
|             let ret = server_format_list(context, conn_id, format_list);
 | |
|             log::debug!("server_format_list called, return {}", ret);
 | |
|             ret
 | |
|         }
 | |
|         ClipbaordFile::FormatListResponse { msg_flags } => {
 | |
|             log::debug!("format_list_response called");
 | |
|             let ret = server_format_list_response(context, conn_id, msg_flags);
 | |
|             log::debug!("server_format_list_response called, return {}", ret);
 | |
|             ret
 | |
|         }
 | |
|         ClipbaordFile::FormatDataRequest {
 | |
|             requested_format_id,
 | |
|         } => {
 | |
|             log::debug!("format_data_request called");
 | |
|             let ret = server_format_data_request(context, conn_id, requested_format_id);
 | |
|             log::debug!("server_format_data_request called, return {}", ret);
 | |
|             ret
 | |
|         }
 | |
|         ClipbaordFile::FormatDataResponse {
 | |
|             msg_flags,
 | |
|             format_data,
 | |
|         } => {
 | |
|             log::debug!("format_data_response called");
 | |
|             let ret = server_format_data_response(context, conn_id, msg_flags, format_data);
 | |
|             log::debug!("server_format_data_response called, return {}", ret);
 | |
|             ret
 | |
|         }
 | |
|         ClipbaordFile::FileContentsRequest {
 | |
|             stream_id,
 | |
|             list_index,
 | |
|             dw_flags,
 | |
|             n_position_low,
 | |
|             n_position_high,
 | |
|             cb_requested,
 | |
|             have_clip_data_id,
 | |
|             clip_data_id,
 | |
|         } => {
 | |
|             log::debug!("file_contents_request called");
 | |
|             let ret = server_file_contents_request(
 | |
|                 context,
 | |
|                 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
 | |
|         }
 | |
|         ClipbaordFile::FileContentsResponse {
 | |
|             msg_flags,
 | |
|             stream_id,
 | |
|             requested_data,
 | |
|         } => {
 | |
|             log::debug!("file_contents_response called");
 | |
|             let ret = server_file_contents_response(
 | |
|                 context,
 | |
|                 conn_id,
 | |
|                 msg_flags,
 | |
|                 stream_id,
 | |
|                 requested_data,
 | |
|             );
 | |
|             log::debug!("server_file_contents_response called, return {}", ret);
 | |
|             ret
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn server_monitor_ready(context: &mut Box<CliprdrClientContext>, conn_id: i32) -> u32 {
 | |
|     unsafe {
 | |
|         let monitor_ready = CLIPRDR_MONITOR_READY {
 | |
|             connID: conn_id as UINT32,
 | |
|             msgType: 0 as UINT16,
 | |
|             msgFlags: 0 as UINT16,
 | |
|             dataLen: 0 as UINT32,
 | |
|         };
 | |
|         let ret = ((**context).MonitorReady.unwrap())(&mut (**context), &monitor_ready);
 | |
|         ret as u32
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn server_format_list(
 | |
|     context: &mut Box<CliprdrClientContext>,
 | |
|     conn_id: i32,
 | |
|     format_list: Vec<(i32, String)>,
 | |
| ) -> u32 {
 | |
|     unsafe {
 | |
|         let num_formats = format_list.len() as UINT32;
 | |
|         let mut formats = format_list
 | |
|             .into_iter()
 | |
|             .map(|format| {
 | |
|                 if format.1.is_empty() {
 | |
|                     CLIPRDR_FORMAT {
 | |
|                         formatId: format.0 as UINT32,
 | |
|                         formatName: 0 as *mut _,
 | |
|                     }
 | |
|                 } else {
 | |
|                     let n = match CString::new(format.1) {
 | |
|                         Ok(n) => n,
 | |
|                         Err(_) => CString::new("").unwrap(),
 | |
|                     };
 | |
|                     CLIPRDR_FORMAT {
 | |
|                         formatId: format.0 as UINT32,
 | |
|                         formatName: n.into_raw(),
 | |
|                     }
 | |
|                 }
 | |
|             })
 | |
|             .collect::<Vec<CLIPRDR_FORMAT>>();
 | |
| 
 | |
|         let format_list = CLIPRDR_FORMAT_LIST {
 | |
|             connID: conn_id as UINT32,
 | |
|             msgType: 0 as UINT16,
 | |
|             msgFlags: 0 as UINT16,
 | |
|             dataLen: 0 as UINT32,
 | |
|             numFormats: num_formats,
 | |
|             formats: formats.as_mut_ptr(),
 | |
|         };
 | |
| 
 | |
|         let ret = ((**context).ServerFormatList.unwrap())(&mut (**context), &format_list);
 | |
| 
 | |
|         for f in formats {
 | |
|             if !f.formatName.is_null() {
 | |
|                 // retake pointer to free memory
 | |
|                 let _ = CString::from_raw(f.formatName);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         ret as u32
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn server_format_list_response(
 | |
|     context: &mut Box<CliprdrClientContext>,
 | |
|     conn_id: i32,
 | |
|     msg_flags: i32,
 | |
| ) -> u32 {
 | |
|     unsafe {
 | |
|         let format_list_response = CLIPRDR_FORMAT_LIST_RESPONSE {
 | |
|             connID: conn_id as UINT32,
 | |
|             msgType: 0 as UINT16,
 | |
|             msgFlags: msg_flags as UINT16,
 | |
|             dataLen: 0 as UINT32,
 | |
|         };
 | |
| 
 | |
|         let ret =
 | |
|             (**context).ServerFormatListResponse.unwrap()(&mut (**context), &format_list_response);
 | |
| 
 | |
|         ret as u32
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn server_format_data_request(
 | |
|     context: &mut Box<CliprdrClientContext>,
 | |
|     conn_id: i32,
 | |
|     requested_format_id: i32,
 | |
| ) -> u32 {
 | |
|     unsafe {
 | |
|         let format_data_request = CLIPRDR_FORMAT_DATA_REQUEST {
 | |
|             connID: conn_id as UINT32,
 | |
|             msgType: 0 as UINT16,
 | |
|             msgFlags: 0 as UINT16,
 | |
|             dataLen: 0 as UINT32,
 | |
|             requestedFormatId: requested_format_id as UINT32,
 | |
|         };
 | |
|         let ret =
 | |
|             ((**context).ServerFormatDataRequest.unwrap())(&mut (**context), &format_data_request);
 | |
|         ret as u32
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn server_format_data_response(
 | |
|     context: &mut Box<CliprdrClientContext>,
 | |
|     conn_id: i32,
 | |
|     msg_flags: i32,
 | |
|     mut format_data: Vec<u8>,
 | |
| ) -> u32 {
 | |
|     unsafe {
 | |
|         let format_data_response = CLIPRDR_FORMAT_DATA_RESPONSE {
 | |
|             connID: conn_id as UINT32,
 | |
|             msgType: 0 as UINT16,
 | |
|             msgFlags: msg_flags as UINT16,
 | |
|             dataLen: format_data.len() as UINT32,
 | |
|             requestedFormatData: format_data.as_mut_ptr(),
 | |
|         };
 | |
|         let ret = ((**context).ServerFormatDataResponse.unwrap())(
 | |
|             &mut (**context),
 | |
|             &format_data_response,
 | |
|         );
 | |
|         ret as u32
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn server_file_contents_request(
 | |
|     context: &mut Box<CliprdrClientContext>,
 | |
|     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 file_contents_request = CLIPRDR_FILE_CONTENTS_REQUEST {
 | |
|             connID: conn_id as UINT32,
 | |
|             msgType: 0 as UINT16,
 | |
|             msgFlags: 0 as UINT16,
 | |
|             dataLen: 0 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),
 | |
|             &file_contents_request,
 | |
|         );
 | |
|         ret as u32
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn server_file_contents_response(
 | |
|     context: &mut Box<CliprdrClientContext>,
 | |
|     conn_id: i32,
 | |
|     msg_flags: i32,
 | |
|     stream_id: i32,
 | |
|     mut requested_data: Vec<u8>,
 | |
| ) -> u32 {
 | |
|     unsafe {
 | |
|         let file_contents_response = CLIPRDR_FILE_CONTENTS_RESPONSE {
 | |
|             connID: conn_id as UINT32,
 | |
|             msgType: 0 as UINT16,
 | |
|             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),
 | |
|             &file_contents_response,
 | |
|         );
 | |
|         ret as u32
 | |
|     }
 | |
| }
 | |
| 
 | |
| pub fn create_cliprdr_context(
 | |
|     enable_files: bool,
 | |
|     enable_others: bool,
 | |
|     process_side: ProcessSide,
 | |
| ) -> ResultType<Box<CliprdrClientContext>> {
 | |
|     *PROCESS_SIDE.write().unwrap() = process_side;
 | |
| 
 | |
|     Ok(CliprdrClientContext::create(
 | |
|         enable_files,
 | |
|         enable_others,
 | |
|         Some(check_enabled),
 | |
|         Some(client_format_list),
 | |
|         Some(client_format_list_response),
 | |
|         Some(client_format_data_request),
 | |
|         Some(client_format_data_response),
 | |
|         Some(client_file_contents_request),
 | |
|         Some(client_file_contents_response),
 | |
|     )?)
 | |
| }
 | |
| 
 | |
| extern "C" fn check_enabled(conn_id: UINT32) -> BOOL {
 | |
|     if *PROCESS_SIDE.read().unwrap() == ProcessSide::ClientSide {
 | |
|         return TRUE;
 | |
|     }
 | |
| 
 | |
|     let lock = CLIP_CONN_ENABLED.lock().unwrap();
 | |
|     let mut connd_enabled = false;
 | |
|     if conn_id != 0 {
 | |
|         if let Some(true) = lock.conn_enabled.get(&(conn_id as i32)) {
 | |
|             connd_enabled = true;
 | |
|         }
 | |
|     } else {
 | |
|         connd_enabled = true;
 | |
|     }
 | |
| 
 | |
|     return if connd_enabled { TRUE } else { FALSE };
 | |
| }
 | |
| 
 | |
| extern "C" fn client_format_list(
 | |
|     _context: *mut CliprdrClientContext,
 | |
|     clip_format_list: *const CLIPRDR_FORMAT_LIST,
 | |
| ) -> UINT {
 | |
|     log::debug!("client_format_list called");
 | |
| 
 | |
|     let conn_id;
 | |
|     let mut format_list: Vec<(i32, String)> = Vec::new();
 | |
|     unsafe {
 | |
|         let mut i = 0u32;
 | |
|         while i < (*clip_format_list).numFormats {
 | |
|             let format_data = &(*(*clip_format_list).formats.offset(i as isize));
 | |
|             if format_data.formatName.is_null() {
 | |
|                 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 {
 | |
|                     Ok(n) => n.to_owned(),
 | |
|                     Err(_) => {
 | |
|                         log::warn!("failed to get format name");
 | |
|                         "".to_owned()
 | |
|                     }
 | |
|                 };
 | |
|                 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;
 | |
|         }
 | |
|         conn_id = (*clip_format_list).connID as i32;
 | |
|     }
 | |
|     let data = ClipbaordFile::FormatList { format_list };
 | |
|     // no need to handle result here
 | |
|     if conn_id == 0 {
 | |
|         VEC_MSG_CHANNEL
 | |
|             .read()
 | |
|             .unwrap()
 | |
|             .iter()
 | |
|             .for_each(|msg_channel| allow_err!(msg_channel.sender.send(data.clone())));
 | |
|     } else {
 | |
|         send_data(conn_id, data);
 | |
|     }
 | |
| 
 | |
|     0
 | |
| }
 | |
| 
 | |
| extern "C" fn client_format_list_response(
 | |
|     _context: *mut CliprdrClientContext,
 | |
|     format_list_response: *const CLIPRDR_FORMAT_LIST_RESPONSE,
 | |
| ) -> UINT {
 | |
|     log::debug!("client_format_list_response called");
 | |
| 
 | |
|     let conn_id;
 | |
|     let msg_flags;
 | |
|     unsafe {
 | |
|         conn_id = (*format_list_response).connID as i32;
 | |
|         msg_flags = (*format_list_response).msgFlags as i32;
 | |
|     }
 | |
|     let data = ClipbaordFile::FormatListResponse { msg_flags };
 | |
|     send_data(conn_id, data);
 | |
| 
 | |
|     0
 | |
| }
 | |
| 
 | |
| extern "C" fn client_format_data_request(
 | |
|     _context: *mut CliprdrClientContext,
 | |
|     format_data_request: *const CLIPRDR_FORMAT_DATA_REQUEST,
 | |
| ) -> UINT {
 | |
|     log::debug!("client_format_data_request called");
 | |
| 
 | |
|     let conn_id;
 | |
|     let requested_format_id;
 | |
|     unsafe {
 | |
|         conn_id = (*format_data_request).connID as i32;
 | |
|         requested_format_id = (*format_data_request).requestedFormatId as i32;
 | |
|     }
 | |
|     let data = ClipbaordFile::FormatDataRequest {
 | |
|         requested_format_id,
 | |
|     };
 | |
|     // no need to handle result here
 | |
|     send_data(conn_id, data);
 | |
| 
 | |
|     0
 | |
| }
 | |
| 
 | |
| extern "C" fn client_format_data_response(
 | |
|     _context: *mut CliprdrClientContext,
 | |
|     format_data_response: *const CLIPRDR_FORMAT_DATA_RESPONSE,
 | |
| ) -> UINT {
 | |
|     log::debug!("cconn_idlient_format_data_response called");
 | |
| 
 | |
|     let conn_id;
 | |
|     let msg_flags;
 | |
|     let format_data;
 | |
|     unsafe {
 | |
|         conn_id = (*format_data_response).connID 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 data = ClipbaordFile::FormatDataResponse {
 | |
|         msg_flags,
 | |
|         format_data,
 | |
|     };
 | |
|     send_data(conn_id, data);
 | |
| 
 | |
|     0
 | |
| }
 | |
| 
 | |
| extern "C" fn client_file_contents_request(
 | |
|     _context: *mut CliprdrClientContext,
 | |
|     file_contents_request: *const CLIPRDR_FILE_CONTENTS_REQUEST,
 | |
| ) -> UINT {
 | |
|     log::debug!("client_file_contents_request called");
 | |
| 
 | |
|     // TODO: support huge file?
 | |
|     // if (!cliprdr->hasHugeFileSupport)
 | |
|     // {
 | |
|     // 	if (((UINT64)fileContentsRequest->cbRequested + fileContentsRequest->nPositionLow) >
 | |
|     // 	    UINT32_MAX)
 | |
|     // 		return ERROR_INVALID_PARAMETER;
 | |
|     // 	if (fileContentsRequest->nPositionHigh != 0)
 | |
|     // 		return ERROR_INVALID_PARAMETER;
 | |
|     // }
 | |
| 
 | |
|     let 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 {
 | |
|         conn_id = (*file_contents_request).connID 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 data = ClipbaordFile::FileContentsRequest {
 | |
|         stream_id,
 | |
|         list_index,
 | |
|         dw_flags,
 | |
|         n_position_low,
 | |
|         n_position_high,
 | |
|         cb_requested,
 | |
|         have_clip_data_id,
 | |
|         clip_data_id,
 | |
|     };
 | |
|     send_data(conn_id, data);
 | |
| 
 | |
|     0
 | |
| }
 | |
| 
 | |
| extern "C" fn client_file_contents_response(
 | |
|     _context: *mut CliprdrClientContext,
 | |
|     file_contents_response: *const CLIPRDR_FILE_CONTENTS_RESPONSE,
 | |
| ) -> UINT {
 | |
|     log::debug!("client_file_contents_response called");
 | |
| 
 | |
|     let conn_id;
 | |
|     let msg_flags;
 | |
|     let stream_id;
 | |
|     let requested_data;
 | |
|     unsafe {
 | |
|         conn_id = (*file_contents_response).connID 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 data = ClipbaordFile::FileContentsResponse {
 | |
|         msg_flags,
 | |
|         stream_id,
 | |
|         requested_data,
 | |
|     };
 | |
|     send_data(conn_id, data);
 | |
| 
 | |
|     0
 | |
| }
 | |
| 
 | |
| #[cfg(test)]
 | |
| mod tests {
 | |
|     // #[test]
 | |
|     // fn test_cliprdr_run() {
 | |
|     //     super::cliprdr_run();
 | |
|     // }
 | |
| }
 |