Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions crates/ironrdp-client/src/rdp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,22 @@ async fn active_session(
Some(cliprdr.initiate_paste(format)
.map_err(|e| session::custom_err!("CLIPRDR", e))?)
}
ClipboardMessage::SendLockClipboard { clip_data_id } => {
Some(cliprdr.lock_clipboard(clip_data_id)
.map_err(|e| session::custom_err!("CLIPRDR", e))?)
}
ClipboardMessage::SendUnlockClipboard { clip_data_id } => {
Some(cliprdr.unlock_clipboard(clip_data_id)
.map_err(|e| session::custom_err!("CLIPRDR", e))?)
}
ClipboardMessage::SendFileContentsRequest(request) => {
Some(cliprdr.request_file_contents(request)
.map_err(|e| session::custom_err!("CLIPRDR", e))?)
}
ClipboardMessage::SendFileContentsResponse(response) => {
Some(cliprdr.submit_file_contents(response)
.map_err(|e| session::custom_err!("CLIPRDR", e))?)
}
ClipboardMessage::Error(e) => {
error!("Clipboard backend error: {}", e);
None
Expand Down
20 changes: 20 additions & 0 deletions crates/ironrdp-cliprdr/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,26 @@ pub enum ClipboardMessage {
/// received.
SendInitiatePaste(ClipboardFormatId),

/// Sent by clipboard backend when clipboard data should be locked on the remote.
///
/// Implementation should send lock clipboard data PDU on `CLIPRDR` SVC when received.
SendLockClipboard { clip_data_id: u32 },

/// Sent by clipboard backend when clipboard data should be unlocked on the remote.
///
/// Implementation should send unlock clipboard data PDU on `CLIPRDR` SVC when received.
SendUnlockClipboard { clip_data_id: u32 },

/// Sent by clipboard backend when file contents are needed from the remote.
///
/// Implementation should send file contents request on `CLIPRDR` SVC when received.
SendFileContentsRequest(FileContentsRequest),

/// Sent by clipboard backend when file contents data is ready to be sent to the remote.
///
/// Implementation should send file contents response on `CLIPRDR` SVC when received.
SendFileContentsResponse(FileContentsResponse<'static>),

/// Failure received from the OS clipboard event loop.
///
/// Client implementation should log/display this error.
Expand Down
44 changes: 42 additions & 2 deletions crates/ironrdp-cliprdr/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ use ironrdp_svc::{
};
use pdu::{
Capabilities, ClientTemporaryDirectory, ClipboardFormat, ClipboardFormatId, ClipboardGeneralCapabilityFlags,
ClipboardPdu, ClipboardProtocolVersion, FileContentsResponse, FormatDataRequest, FormatListResponse,
OwnedFormatDataResponse,
ClipboardPdu, ClipboardProtocolVersion, FileContentsRequest, FileContentsResponse, FormatDataRequest,
FormatListResponse, LockDataId, OwnedFormatDataResponse,
};
use tracing::{error, info};

Expand Down Expand Up @@ -276,6 +276,46 @@ impl<R: Role> Cliprdr<R> {

Ok(vec![into_cliprdr_message(pdu)].into())
}

/// [2.2.4.6] Lock Clipboard Data PDU (CLIPRDR_LOCK_CLIPDATA)
///
/// Locks clipboard data on the remote before file transfer. Should be called before
/// requesting file contents to ensure data stability during transfer.
///
/// [2.2.4.6]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeclip/150bac72-bc7f-42e5-9e8e-cb5a0ddc7dbc
pub fn lock_clipboard(&self, clip_data_id: u32) -> PduResult<CliprdrSvcMessages<R>> {
ready_guard!(self, lock_clipboard);

let pdu = ClipboardPdu::LockData(LockDataId(clip_data_id));
Ok(vec![into_cliprdr_message(pdu)].into())
}

/// [2.2.4.7] Unlock Clipboard Data PDU (CLIPRDR_UNLOCK_CLIPDATA)
///
/// Unlocks previously locked clipboard data. Should be called after file transfer
/// operations complete.
///
/// [2.2.4.7]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeclip/e587a20c-fb7c-47d1-8698-4bcb92c48a38
pub fn unlock_clipboard(&self, clip_data_id: u32) -> PduResult<CliprdrSvcMessages<R>> {
ready_guard!(self, unlock_clipboard);

let pdu = ClipboardPdu::UnlockData(LockDataId(clip_data_id));
Ok(vec![into_cliprdr_message(pdu)].into())
}

/// [2.2.5.3] File Contents Request PDU (CLIPRDR_FILECONTENTS_REQUEST)
///
/// Requests file contents from the Shared Clipboard Owner. Should be called when
/// the Local Clipboard Owner needs file data after receiving a file list format.
/// The remote will respond via [`CliprdrBackend::on_file_contents_response`].
///
/// [2.2.5.3]: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdpeclip/cbc851d3-4e68-45f4-9292-26872a9209f2
pub fn request_file_contents(&self, request: FileContentsRequest) -> PduResult<CliprdrSvcMessages<R>> {
ready_guard!(self, request_file_contents);

let pdu = ClipboardPdu::FileContentsRequest(request);
Ok(vec![into_cliprdr_message(pdu)].into())
}
}

impl<R: Role> SvcProcessor for Cliprdr<R> {
Expand Down
2 changes: 1 addition & 1 deletion crates/ironrdp-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ ironrdp-svc = { path = "../ironrdp-svc", version = "0.5" } # public
ironrdp-cliprdr = { path = "../ironrdp-cliprdr", version = "0.5" } # public
ironrdp-displaycontrol = { path = "../ironrdp-displaycontrol", version = "0.4" } # public
ironrdp-dvc = { path = "../ironrdp-dvc", version = "0.4" } # public
ironrdp-tokio = { path = "../ironrdp-tokio", version = "0.8" }
ironrdp-tokio = { path = "../ironrdp-tokio", version = "0.8", features = ["reqwest"] }
ironrdp-acceptor = { path = "../ironrdp-acceptor", version = "0.8" } # public
ironrdp-graphics = { path = "../ironrdp-graphics", version = "0.7" } # public
ironrdp-rdpsnd = { path = "../ironrdp-rdpsnd", version = "0.6" } # public
Expand Down
6 changes: 6 additions & 0 deletions crates/ironrdp-server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,12 @@ impl RdpServer {
ClipboardMessage::SendInitiateCopy(formats) => cliprdr.initiate_copy(&formats),
ClipboardMessage::SendFormatData(data) => cliprdr.submit_format_data(data),
ClipboardMessage::SendInitiatePaste(format) => cliprdr.initiate_paste(format),
ClipboardMessage::SendLockClipboard { clip_data_id } => cliprdr.lock_clipboard(clip_data_id),
ClipboardMessage::SendUnlockClipboard { clip_data_id } => {
cliprdr.unlock_clipboard(clip_data_id)
}
ClipboardMessage::SendFileContentsRequest(request) => cliprdr.request_file_contents(request),
ClipboardMessage::SendFileContentsResponse(response) => cliprdr.submit_file_contents(response),
ClipboardMessage::Error(error) => {
error!(?error, "Handling clipboard event");
continue;
Expand Down
16 changes: 16 additions & 0 deletions crates/ironrdp-web/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,22 @@ impl iron_remote_desktop::Session for Session {
cliprdr.initiate_paste(format)
.context("CLIPRDR initiate paste")?
),
ClipboardMessage::SendLockClipboard { clip_data_id } => Some(
cliprdr.lock_clipboard(clip_data_id)
.context("CLIPRDR lock clipboard")?
),
ClipboardMessage::SendUnlockClipboard { clip_data_id } => Some(
cliprdr.unlock_clipboard(clip_data_id)
.context("CLIPRDR unlock clipboard")?
),
ClipboardMessage::SendFileContentsRequest(request) => Some(
cliprdr.request_file_contents(request)
.context("CLIPRDR request file contents")?
),
ClipboardMessage::SendFileContentsResponse(response) => Some(
cliprdr.submit_file_contents(response)
.context("CLIPRDR submit file contents")?
),
ClipboardMessage::Error(e) => {
error!("Clipboard backend error: {}", e);
None
Expand Down
16 changes: 16 additions & 0 deletions ffi/src/clipboard/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ pub mod ffi {
ironrdp::cliprdr::backend::ClipboardMessage::SendInitiatePaste(_) => {
ClipboardMessageType::SendInitiatePaste
}
ironrdp::cliprdr::backend::ClipboardMessage::SendLockClipboard { .. } => {
ClipboardMessageType::SendLockClipboard
}
ironrdp::cliprdr::backend::ClipboardMessage::SendUnlockClipboard { .. } => {
ClipboardMessageType::SendUnlockClipboard
}
ironrdp::cliprdr::backend::ClipboardMessage::SendFileContentsRequest(_) => {
ClipboardMessageType::SendFileContentsRequest
}
ironrdp::cliprdr::backend::ClipboardMessage::SendFileContentsResponse(_) => {
ClipboardMessageType::SendFileContentsResponse
}
ironrdp::cliprdr::backend::ClipboardMessage::Error(_) => ClipboardMessageType::Error,
}
}
Expand Down Expand Up @@ -51,6 +63,10 @@ pub mod ffi {
SendInitiateCopy,
SendFormatData,
SendInitiatePaste,
SendLockClipboard,
SendUnlockClipboard,
SendFileContentsRequest,
SendFileContentsResponse,
Error,
}

Expand Down
Loading