2023-09-08 19:39:00 +08:00
|
|
|
#[allow(dead_code)]
|
2023-08-24 22:34:12 +08:00
|
|
|
use std::{
|
2023-09-08 19:39:00 +08:00
|
|
|
path::PathBuf,
|
2023-08-24 22:34:12 +08:00
|
|
|
sync::{Arc, Mutex, RwLock},
|
|
|
|
};
|
|
|
|
|
2022-02-14 17:34:09 +08:00
|
|
|
use hbb_common::{
|
2023-06-29 13:47:55 +08:00
|
|
|
allow_err, lazy_static, log,
|
2022-02-14 17:34:09 +08:00
|
|
|
tokio::sync::{
|
|
|
|
mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
|
|
|
|
Mutex as TokioMutex,
|
|
|
|
},
|
2023-10-08 21:44:54 +08:00
|
|
|
ResultType,
|
2022-02-14 17:34:09 +08:00
|
|
|
};
|
2022-02-22 14:17:50 +08:00
|
|
|
use serde_derive::{Deserialize, Serialize};
|
2023-08-24 22:34:12 +08:00
|
|
|
use thiserror::Error;
|
2022-02-14 17:34:09 +08:00
|
|
|
|
2022-10-26 22:37:45 +08:00
|
|
|
pub mod context_send;
|
2023-08-24 22:34:12 +08:00
|
|
|
pub mod platform;
|
2022-10-26 22:37:45 +08:00
|
|
|
pub use context_send::*;
|
2022-02-14 17:34:09 +08:00
|
|
|
|
2023-09-08 19:39:00 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
2023-06-18 22:55:38 +08:00
|
|
|
const ERR_CODE_SERVER_FUNCTION_NONE: u32 = 0x00000001;
|
2023-09-08 19:39:00 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
2023-06-29 13:47:55 +08:00
|
|
|
const ERR_CODE_INVALID_PARAMETER: u32 = 0x00000002;
|
|
|
|
|
2023-08-24 22:34:12 +08:00
|
|
|
pub(crate) use platform::create_cliprdr_context;
|
|
|
|
|
|
|
|
/// Ability to handle Clipboard File from remote rustdesk client
|
|
|
|
///
|
|
|
|
/// # Note
|
|
|
|
/// There actually should be 2 parts to implement a useable clipboard file service,
|
|
|
|
/// but this only contains the RPC server part.
|
|
|
|
/// The local listener and transport part is too platform specific to wrap up in typeclasses.
|
|
|
|
pub trait CliprdrServiceContext: Send + Sync {
|
|
|
|
/// set to be stopped
|
|
|
|
fn set_is_stopped(&mut self) -> Result<(), CliprdrError>;
|
|
|
|
/// clear the content on clipboard
|
2023-09-08 19:39:00 +08:00
|
|
|
fn empty_clipboard(&mut self, conn_id: i32) -> Result<bool, CliprdrError>;
|
2023-08-24 22:34:12 +08:00
|
|
|
|
|
|
|
/// run as a server for clipboard RPC
|
|
|
|
fn server_clip_file(&mut self, conn_id: i32, msg: ClipboardFile) -> Result<(), CliprdrError>;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Error, Debug)]
|
|
|
|
pub enum CliprdrError {
|
|
|
|
#[error("invalid cliprdr name")]
|
|
|
|
CliprdrName,
|
|
|
|
#[error("failed to init cliprdr")]
|
|
|
|
CliprdrInit,
|
|
|
|
#[error("cliprdr out of memory")]
|
|
|
|
CliprdrOutOfMemory,
|
|
|
|
#[error("cliprdr internal error")]
|
|
|
|
ClipboardInternalError,
|
2023-09-04 15:38:53 +08:00
|
|
|
#[error("cliprdr occupied")]
|
|
|
|
ClipboardOccupied,
|
|
|
|
#[error("conversion failure")]
|
|
|
|
ConversionFailure,
|
2023-09-08 19:39:00 +08:00
|
|
|
#[error("failure to read clipboard")]
|
|
|
|
OpenClipboard,
|
|
|
|
#[error("failure to read file metadata or content")]
|
|
|
|
FileError { path: PathBuf, err: std::io::Error },
|
|
|
|
#[error("invalid request")]
|
|
|
|
InvalidRequest { description: String },
|
2023-08-24 22:34:12 +08:00
|
|
|
#[error("unknown cliprdr error")]
|
2023-09-08 19:39:00 +08:00
|
|
|
Unknown(u32),
|
2023-08-24 22:34:12 +08:00
|
|
|
}
|
|
|
|
|
2022-02-22 14:17:50 +08:00
|
|
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
|
|
|
#[serde(tag = "t", content = "c")]
|
2023-01-09 15:30:21 +08:00
|
|
|
pub enum ClipboardFile {
|
2023-06-29 18:12:34 +08:00
|
|
|
NotifyCallback {
|
|
|
|
r#type: String,
|
|
|
|
title: String,
|
|
|
|
text: String,
|
|
|
|
},
|
2022-10-26 19:42:14 +08:00
|
|
|
MonitorReady,
|
2022-10-25 18:46:38 +08:00
|
|
|
FormatList {
|
2022-02-22 14:17:50 +08:00
|
|
|
format_list: Vec<(i32, String)>,
|
|
|
|
},
|
2022-10-25 18:46:38 +08:00
|
|
|
FormatListResponse {
|
2022-02-22 14:17:50 +08:00
|
|
|
msg_flags: i32,
|
|
|
|
},
|
2022-10-25 18:46:38 +08:00
|
|
|
FormatDataRequest {
|
2022-02-22 14:17:50 +08:00
|
|
|
requested_format_id: i32,
|
|
|
|
},
|
2022-10-25 18:46:38 +08:00
|
|
|
FormatDataResponse {
|
2022-02-22 14:17:50 +08:00
|
|
|
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>,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2022-10-25 18:46:38 +08:00
|
|
|
struct MsgChannel {
|
2023-10-08 21:44:54 +08:00
|
|
|
peer_id: String,
|
2022-10-25 18:46:38 +08:00
|
|
|
conn_id: i32,
|
2023-01-09 15:30:21 +08:00
|
|
|
sender: UnboundedSender<ClipboardFile>,
|
|
|
|
receiver: Arc<TokioMutex<UnboundedReceiver<ClipboardFile>>>,
|
2022-10-25 18:46:38 +08:00
|
|
|
}
|
2022-02-22 22:26:22 +08:00
|
|
|
|
2022-10-25 18:46:38 +08:00
|
|
|
lazy_static::lazy_static! {
|
|
|
|
static ref VEC_MSG_CHANNEL: RwLock<Vec<MsgChannel>> = Default::default();
|
2023-06-18 20:23:54 +08:00
|
|
|
static ref CLIENT_CONN_ID_COUNTER: Mutex<i32> = Mutex::new(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ClipboardFile {
|
|
|
|
pub fn is_stopping_allowed(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
ClipboardFile::MonitorReady
|
|
|
|
| ClipboardFile::FormatList { .. }
|
|
|
|
| ClipboardFile::FormatDataRequest { .. } => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
2023-06-20 00:05:23 +08:00
|
|
|
|
|
|
|
pub fn is_stopping_allowed_from_peer(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
ClipboardFile::MonitorReady | ClipboardFile::FormatList { .. } => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
2022-02-14 17:34:09 +08:00
|
|
|
}
|
|
|
|
|
2023-10-08 21:44:54 +08:00
|
|
|
pub fn get_client_conn_id(peer_id: &str) -> Option<i32> {
|
2022-10-27 10:56:14 +08:00
|
|
|
VEC_MSG_CHANNEL
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.iter()
|
2023-10-08 21:44:54 +08:00
|
|
|
.find(|x| x.peer_id == peer_id)
|
2022-10-27 10:56:14 +08:00
|
|
|
.map(|x| x.conn_id)
|
|
|
|
}
|
|
|
|
|
2023-06-18 20:23:54 +08:00
|
|
|
fn get_conn_id() -> i32 {
|
|
|
|
let mut lock = CLIENT_CONN_ID_COUNTER.lock().unwrap();
|
|
|
|
*lock += 1;
|
|
|
|
*lock
|
|
|
|
}
|
|
|
|
|
2022-10-27 10:56:14 +08:00
|
|
|
pub fn get_rx_cliprdr_client(
|
2023-10-08 21:44:54 +08:00
|
|
|
peer_id: &str,
|
2023-01-09 15:30:21 +08:00
|
|
|
) -> (i32, Arc<TokioMutex<UnboundedReceiver<ClipboardFile>>>) {
|
2022-10-25 18:46:38 +08:00
|
|
|
let mut lock = VEC_MSG_CHANNEL.write().unwrap();
|
2023-10-08 21:44:54 +08:00
|
|
|
match lock.iter().find(|x| x.peer_id == peer_id) {
|
2022-10-25 18:46:38 +08:00
|
|
|
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();
|
2023-06-18 20:23:54 +08:00
|
|
|
let conn_id = get_conn_id();
|
2022-10-25 18:46:38 +08:00
|
|
|
let msg_channel = MsgChannel {
|
2023-10-08 21:44:54 +08:00
|
|
|
peer_id: peer_id.to_owned(),
|
2022-10-25 18:46:38 +08:00
|
|
|
conn_id,
|
|
|
|
sender,
|
|
|
|
receiver,
|
|
|
|
};
|
|
|
|
lock.push(msg_channel);
|
|
|
|
(conn_id, receiver2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-09 15:30:21 +08:00
|
|
|
pub fn get_rx_cliprdr_server(conn_id: i32) -> Arc<TokioMutex<UnboundedReceiver<ClipboardFile>>> {
|
2022-10-25 18:46:38 +08:00
|
|
|
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 {
|
2023-10-08 21:44:54 +08:00
|
|
|
peer_id: "".to_string(),
|
2022-10-25 18:46:38 +08:00
|
|
|
conn_id,
|
|
|
|
sender,
|
|
|
|
receiver,
|
|
|
|
};
|
|
|
|
lock.push(msg_channel);
|
|
|
|
receiver2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2023-01-09 15:30:21 +08:00
|
|
|
fn send_data(conn_id: i32, data: ClipboardFile) {
|
2023-10-07 17:26:20 +08:00
|
|
|
#[cfg(target_os = "windows")]
|
|
|
|
return send_data_to_channel(conn_id, data);
|
|
|
|
#[cfg(not(target_os = "windows"))]
|
|
|
|
if conn_id == 0 {
|
|
|
|
send_data_to_all(data);
|
|
|
|
} else {
|
|
|
|
send_data_to_channel(conn_id, data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn send_data_to_channel(conn_id: i32, data: ClipboardFile) {
|
2022-10-25 18:46:38 +08:00
|
|
|
// no need to handle result here
|
|
|
|
if let Some(msg_channel) = VEC_MSG_CHANNEL
|
|
|
|
.read()
|
|
|
|
.unwrap()
|
|
|
|
.iter()
|
|
|
|
.find(|x| x.conn_id == conn_id)
|
|
|
|
{
|
2023-10-07 17:26:20 +08:00
|
|
|
log::debug!("send data to connection: {}", conn_id);
|
2022-10-25 18:46:38 +08:00
|
|
|
allow_err!(msg_channel.sender.send(data));
|
|
|
|
}
|
2022-02-14 17:34:09 +08:00
|
|
|
}
|
|
|
|
|
2023-10-07 17:26:20 +08:00
|
|
|
#[inline]
|
|
|
|
fn send_data_to_all(data: ClipboardFile) {
|
|
|
|
// no need to handle result here
|
|
|
|
for msg_channel in VEC_MSG_CHANNEL.read().unwrap().iter() {
|
|
|
|
log::debug!("send data to connection: {}", msg_channel.conn_id);
|
|
|
|
allow_err!(msg_channel.sender.send(data.clone()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-14 17:34:09 +08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
// #[test]
|
|
|
|
// fn test_cliprdr_run() {
|
|
|
|
// super::cliprdr_run();
|
|
|
|
// }
|
|
|
|
}
|