ironrdp_cliprdr/pdu/
mod.rs1mod capabilities;
5mod client_temporary_directory;
6mod file_contents;
7mod format_data;
8mod format_list;
9mod lock;
10
11pub use self::capabilities::*;
12pub use self::client_temporary_directory::*;
13pub use self::file_contents::*;
14pub use self::format_data::*;
15pub use self::format_list::*;
16pub use self::lock::*;
17
18#[rustfmt::skip]
19use bitflags::bitflags;
20use ironrdp_core::{
21 ensure_fixed_part_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, ReadCursor, WriteCursor,
22};
23use ironrdp_svc::SvcEncode;
24
25const MSG_TYPE_MONITOR_READY: u16 = 0x0001;
26const MSG_TYPE_FORMAT_LIST: u16 = 0x0002;
27const MSG_TYPE_FORMAT_LIST_RESPONSE: u16 = 0x0003;
28const MSG_TYPE_FORMAT_DATA_REQUEST: u16 = 0x0004;
29const MSG_TYPE_FORMAT_DATA_RESPONSE: u16 = 0x0005;
30const MSG_TYPE_TEMPORARY_DIRECTORY: u16 = 0x0006;
31const MSG_TYPE_CAPABILITIES: u16 = 0x0007;
32const MSG_TYPE_FILE_CONTENTS_REQUEST: u16 = 0x0008;
33const MSG_TYPE_FILE_CONTENTS_RESPONSE: u16 = 0x0009;
34const MSG_TYPE_LOCK_CLIPDATA: u16 = 0x000A;
35const MSG_TYPE_UNLOCK_CLIPDATA: u16 = 0x000B;
36
37pub const FORMAT_ID_PALETTE: u32 = 9;
38pub const FORMAT_ID_METAFILE: u32 = 3;
39pub const FORMAT_NAME_FILE_LIST: &str = "FileGroupDescriptorW";
40
41struct PartialHeader {
43 message_flags: ClipboardPduFlags,
44 data_length: u32,
45}
46
47impl PartialHeader {
48 const NAME: &'static str = "CLIPRDR_HEADER";
49 const FIXED_PART_SIZE: usize = 2 + 4 ;
50 const SIZE: usize = Self::FIXED_PART_SIZE;
51
52 pub(crate) fn new(inner_data_length: u32) -> Self {
53 Self::new_with_flags(inner_data_length, ClipboardPduFlags::empty())
54 }
55
56 pub(crate) fn new_with_flags(data_length: u32, message_flags: ClipboardPduFlags) -> Self {
57 Self {
58 message_flags,
59 data_length,
60 }
61 }
62
63 pub(crate) fn data_length(&self) -> usize {
64 usize::try_from(self.data_length).expect("BUG: Upcasting u32 -> usize should be infallible")
65 }
66}
67
68impl<'de> Decode<'de> for PartialHeader {
69 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
70 ensure_fixed_part_size!(in: src);
71
72 let message_flags = ClipboardPduFlags::from_bits_truncate(src.read_u16());
73 let data_length = src.read_u32();
74
75 Ok(Self {
76 message_flags,
77 data_length,
78 })
79 }
80}
81
82impl Encode for PartialHeader {
83 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
84 ensure_fixed_part_size!(in: dst);
85
86 dst.write_u16(self.message_flags.bits());
87 dst.write_u32(self.data_length);
88
89 Ok(())
90 }
91
92 fn name(&self) -> &'static str {
93 Self::NAME
94 }
95
96 fn size(&self) -> usize {
97 Self::FIXED_PART_SIZE
98 }
99}
100
101#[derive(Debug, Clone, PartialEq, Eq)]
103pub enum ClipboardPdu<'a> {
104 MonitorReady,
105 FormatList(FormatList<'a>),
106 FormatListResponse(FormatListResponse),
107 FormatDataRequest(FormatDataRequest),
108 FormatDataResponse(FormatDataResponse<'a>),
109 TemporaryDirectory(ClientTemporaryDirectory<'a>),
110 Capabilities(Capabilities),
111 FileContentsRequest(FileContentsRequest),
112 FileContentsResponse(FileContentsResponse<'a>),
113 LockData(LockDataId),
114 UnlockData(LockDataId),
115}
116
117impl ClipboardPdu<'_> {
118 const NAME: &'static str = "ClipboardPdu";
119 const FIXED_PART_SIZE: usize = 2 ;
120
121 pub fn message_name(&self) -> &'static str {
122 match self {
123 ClipboardPdu::MonitorReady => "CLIPRDR_MONITOR_READY",
124 ClipboardPdu::FormatList(_) => "CLIPRDR_FORMAT_LIST",
125 ClipboardPdu::FormatListResponse(_) => "CLIPRDR_FORMAT_LIST_RESPONSE",
126 ClipboardPdu::FormatDataRequest(_) => "CLIPRDR_FORMAT_DATA_REQUEST",
127 ClipboardPdu::FormatDataResponse(_) => "CLIPRDR_FORMAT_DATA_RESPONSE",
128 ClipboardPdu::TemporaryDirectory(_) => "CLIPRDR_TEMP_DIRECTORY",
129 ClipboardPdu::Capabilities(_) => "CLIPRDR_CAPABILITIES",
130 ClipboardPdu::FileContentsRequest(_) => "CLIPRDR_FILECONTENTS_REQUEST",
131 ClipboardPdu::FileContentsResponse(_) => "CLIPRDR_FILECONTENTS_RESPONSE",
132 ClipboardPdu::LockData(_) => "CLIPRDR_LOCK_CLIPDATA",
133 ClipboardPdu::UnlockData(_) => "CLIPRDR_UNLOCK_CLIPDATA",
134 }
135 }
136}
137
138impl Encode for ClipboardPdu<'_> {
139 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
140 ensure_fixed_part_size!(in: dst);
141
142 let write_empty_pdu = |dst: &mut WriteCursor<'_>| {
143 let header = PartialHeader::new(0);
144 header.encode(dst)
145 };
146
147 match self {
148 ClipboardPdu::MonitorReady => {
149 dst.write_u16(MSG_TYPE_MONITOR_READY);
150 write_empty_pdu(dst)
151 }
152 ClipboardPdu::FormatList(pdu) => {
153 dst.write_u16(MSG_TYPE_FORMAT_LIST);
154 pdu.encode(dst)
155 }
156 ClipboardPdu::FormatListResponse(pdu) => {
157 dst.write_u16(MSG_TYPE_FORMAT_LIST_RESPONSE);
158 pdu.encode(dst)
159 }
160 ClipboardPdu::FormatDataRequest(pdu) => {
161 dst.write_u16(MSG_TYPE_FORMAT_DATA_REQUEST);
162 pdu.encode(dst)
163 }
164 ClipboardPdu::FormatDataResponse(pdu) => {
165 dst.write_u16(MSG_TYPE_FORMAT_DATA_RESPONSE);
166 pdu.encode(dst)
167 }
168 ClipboardPdu::TemporaryDirectory(pdu) => {
169 dst.write_u16(MSG_TYPE_TEMPORARY_DIRECTORY);
170 pdu.encode(dst)
171 }
172 ClipboardPdu::Capabilities(pdu) => {
173 dst.write_u16(MSG_TYPE_CAPABILITIES);
174 pdu.encode(dst)
175 }
176 ClipboardPdu::FileContentsRequest(pdu) => {
177 dst.write_u16(MSG_TYPE_FILE_CONTENTS_REQUEST);
178 pdu.encode(dst)
179 }
180 ClipboardPdu::FileContentsResponse(pdu) => {
181 dst.write_u16(MSG_TYPE_FILE_CONTENTS_RESPONSE);
182 pdu.encode(dst)
183 }
184 ClipboardPdu::LockData(pdu) => {
185 dst.write_u16(MSG_TYPE_LOCK_CLIPDATA);
186 pdu.encode(dst)
187 }
188 ClipboardPdu::UnlockData(pdu) => {
189 dst.write_u16(MSG_TYPE_UNLOCK_CLIPDATA);
190 pdu.encode(dst)
191 }
192 }
193 }
194
195 fn name(&self) -> &'static str {
196 Self::NAME
197 }
198
199 fn size(&self) -> usize {
200 let empty_size = PartialHeader::SIZE;
201
202 let variable_size = match self {
203 ClipboardPdu::MonitorReady => empty_size,
204 ClipboardPdu::FormatList(pdu) => pdu.size(),
205 ClipboardPdu::FormatListResponse(pdu) => pdu.size(),
206 ClipboardPdu::FormatDataRequest(pdu) => pdu.size(),
207 ClipboardPdu::FormatDataResponse(pdu) => pdu.size(),
208 ClipboardPdu::TemporaryDirectory(pdu) => pdu.size(),
209 ClipboardPdu::Capabilities(pdu) => pdu.size(),
210 ClipboardPdu::FileContentsRequest(pdu) => pdu.size(),
211 ClipboardPdu::FileContentsResponse(pdu) => pdu.size(),
212 ClipboardPdu::LockData(pdu) => pdu.size(),
213 ClipboardPdu::UnlockData(pdu) => pdu.size(),
214 };
215
216 Self::FIXED_PART_SIZE + variable_size
217 }
218}
219
220impl SvcEncode for ClipboardPdu<'_> {}
221
222impl<'de> Decode<'de> for ClipboardPdu<'de> {
223 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
224 ensure_fixed_part_size!(in: src);
225
226 let read_empty_pdu = |src: &mut ReadCursor<'de>| -> DecodeResult<()> {
227 let _header = PartialHeader::decode(src)?;
228 Ok(())
229 };
230
231 let pdu = match src.read_u16() {
232 MSG_TYPE_MONITOR_READY => {
233 read_empty_pdu(src)?;
234 ClipboardPdu::MonitorReady
235 }
236 MSG_TYPE_FORMAT_LIST => ClipboardPdu::FormatList(FormatList::decode(src)?),
237 MSG_TYPE_FORMAT_LIST_RESPONSE => ClipboardPdu::FormatListResponse(FormatListResponse::decode(src)?),
238 MSG_TYPE_FORMAT_DATA_REQUEST => ClipboardPdu::FormatDataRequest(FormatDataRequest::decode(src)?),
239 MSG_TYPE_FORMAT_DATA_RESPONSE => ClipboardPdu::FormatDataResponse(FormatDataResponse::decode(src)?),
240 MSG_TYPE_TEMPORARY_DIRECTORY => ClipboardPdu::TemporaryDirectory(ClientTemporaryDirectory::decode(src)?),
241 MSG_TYPE_CAPABILITIES => ClipboardPdu::Capabilities(Capabilities::decode(src)?),
242 MSG_TYPE_FILE_CONTENTS_REQUEST => ClipboardPdu::FileContentsRequest(FileContentsRequest::decode(src)?),
243 MSG_TYPE_FILE_CONTENTS_RESPONSE => ClipboardPdu::FileContentsResponse(FileContentsResponse::decode(src)?),
244 MSG_TYPE_LOCK_CLIPDATA => ClipboardPdu::LockData(LockDataId::decode(src)?),
245 MSG_TYPE_UNLOCK_CLIPDATA => ClipboardPdu::UnlockData(LockDataId::decode(src)?),
246 _ => return Err(invalid_field_err!("msgType", "Unknown clipboard PDU type")),
247 };
248
249 Ok(pdu)
250 }
251}
252
253bitflags! {
254 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
255 pub struct ClipboardPduFlags: u16 {
257 const RESPONSE_OK = 0x0001;
262 const RESPONSE_FAIL = 0x0002;
266 const ASCII_NAMES = 0x0004;
269 }
270}