1use std::borrow::Cow;
2
3use ironrdp_core::{
4 cast_int, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, IntoOwned, ReadCursor,
5 WriteCursor,
6};
7use ironrdp_pdu::utils::{read_string_from_cursor, to_utf16_bytes, write_string_to_cursor, CharacterSet};
8use ironrdp_pdu::{decode_err, impl_pdu_borrowing, impl_pdu_pod, PduResult};
9
10use crate::pdu::{ClipboardPduFlags, PartialHeader};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub struct ClipboardFormatId(pub u32);
18
19impl ClipboardFormatId {
20 pub const CF_TEXT: Self = Self(1);
23
24 pub const CF_BITMAP: Self = Self(2);
26
27 pub const CF_METAFILEPICT: Self = Self(3);
32
33 pub const CF_SYLK: Self = Self(4);
35
36 pub const CF_DIF: Self = Self(5);
38
39 pub const CF_TIFF: Self = Self(6);
41
42 pub const CF_OEMTEXT: Self = Self(7);
45
46 pub const CF_DIB: Self = Self(8);
48
49 pub const CF_PALETTE: Self = Self(9);
62
63 pub const CF_PENDATA: Self = Self(10);
65
66 pub const CF_RIFF: Self = Self(11);
68
69 pub const CF_WAVE: Self = Self(12);
71
72 pub const CF_UNICODETEXT: Self = Self(13);
75
76 pub const CF_ENHMETAFILE: Self = Self(14);
81
82 pub const CF_HDROP: Self = Self(15);
85
86 pub const CF_LOCALE: Self = Self(16);
99
100 pub const CF_DIBV5: Self = Self(17);
103
104 pub fn new(id: u32) -> Self {
108 Self(id)
109 }
110
111 pub fn value(&self) -> u32 {
112 self.0
113 }
114
115 pub fn is_standard(self) -> bool {
116 matches!(
117 self,
118 Self::CF_TEXT
119 | Self::CF_BITMAP
120 | Self::CF_METAFILEPICT
121 | Self::CF_SYLK
122 | Self::CF_DIF
123 | Self::CF_TIFF
124 | Self::CF_OEMTEXT
125 | Self::CF_DIB
126 | Self::CF_PALETTE
127 | Self::CF_PENDATA
128 | Self::CF_RIFF
129 | Self::CF_WAVE
130 | Self::CF_UNICODETEXT
131 | Self::CF_ENHMETAFILE
132 | Self::CF_HDROP
133 | Self::CF_LOCALE
134 | Self::CF_DIBV5
135 )
136 }
137
138 pub fn is_registered(self) -> bool {
139 (self.0 >= 0xC000) && (self.0 <= 0xFFFF)
140 }
141}
142
143#[derive(Debug, Clone, PartialEq, Eq)]
146pub struct ClipboardFormatName(Cow<'static, str>);
147
148impl ClipboardFormatName {
149 pub const FILE_LIST: Self = Self::new_static("FileGroupDescriptorW");
152
153 pub const HTML: Self = Self::new_static("HTML Format");
155
156 pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
157 Self(name.into())
158 }
159
160 pub const fn new_static(name: &'static str) -> Self {
162 Self(Cow::Borrowed(name))
163 }
164
165 pub fn value(&self) -> &str {
166 &self.0
167 }
168}
169
170#[derive(Debug, Clone, PartialEq, Eq)]
172pub struct ClipboardFormat {
173 pub id: ClipboardFormatId,
174 pub name: Option<ClipboardFormatName>,
175}
176
177impl ClipboardFormat {
178 pub const fn new(id: ClipboardFormatId) -> Self {
180 Self { id, name: None }
181 }
182
183 #[must_use]
188 pub fn with_name(self, name: ClipboardFormatName) -> Self {
189 if name.0.is_empty() {
190 return Self {
191 id: self.id,
192 name: None,
193 };
194 }
195
196 Self {
197 id: self.id,
198 name: Some(name),
199 }
200 }
201
202 #[inline]
203 pub fn id(&self) -> ClipboardFormatId {
204 self.id
205 }
206
207 #[inline]
208 pub fn name(&self) -> Option<&ClipboardFormatName> {
209 self.name.as_ref()
210 }
211}
212
213#[derive(Debug, Clone, PartialEq, Eq)]
215pub struct FormatList<'a> {
216 use_ascii: bool,
217 encoded_formats: Cow<'a, [u8]>,
218}
219
220impl_pdu_borrowing!(FormatList<'_>, OwnedFormatList);
221
222impl IntoOwned for FormatList<'_> {
223 type Owned = OwnedFormatList;
224
225 fn into_owned(self) -> Self::Owned {
226 OwnedFormatList {
227 use_ascii: self.use_ascii,
228 encoded_formats: Cow::Owned(self.encoded_formats.into_owned()),
229 }
230 }
231}
232
233impl FormatList<'_> {
234 const NAME: &'static str = "CLIPRDR_FORMAT_LIST";
235
236 const SHORT_FORMAT_SIZE: usize = 4 + 32 ;
238
239 fn new_impl(formats: &[ClipboardFormat], use_long_format: bool, use_ascii: bool) -> EncodeResult<Self> {
240 let charset = if use_ascii {
241 CharacterSet::Ansi
242 } else {
243 CharacterSet::Unicode
244 };
245
246 let mut bytes_written = 0;
247
248 if use_long_format {
249 const DEFAULT_STRING_BUFFER_SIZE: usize = 1024;
251 let mut buffer = vec![0u8; DEFAULT_STRING_BUFFER_SIZE];
252
253 for format in formats {
254 let encoded_string = match charset {
255 CharacterSet::Ansi => {
256 let mut str_buffer = format
257 .name
258 .as_ref()
259 .map(|name| name.value().as_bytes().to_vec())
260 .unwrap_or_default();
261 str_buffer.push(b'\0');
262 str_buffer
263 }
264 CharacterSet::Unicode => {
265 let mut str_buffer = format
266 .name
267 .as_ref()
268 .map(|name| to_utf16_bytes(name.value()))
269 .unwrap_or_default();
270 str_buffer.push(b'\0');
271 str_buffer.push(b'\0');
272 str_buffer
273 }
274 };
275
276 let required_size = 4 + encoded_string.len();
277 if buffer.len() - bytes_written < required_size {
278 buffer.resize(bytes_written + required_size, 0);
279 }
280
281 let mut cursor = WriteCursor::new(&mut buffer[bytes_written..]);
282
283 cursor.write_u32(format.id.value());
285 cursor.write_slice(&encoded_string);
286
287 bytes_written += required_size;
288 }
289
290 buffer.truncate(bytes_written);
291
292 Ok(Self {
293 use_ascii,
294 encoded_formats: Cow::Owned(buffer),
295 })
296 } else {
297 let mut buffer = vec![0u8; Self::SHORT_FORMAT_SIZE * formats.len()];
298 for (idx, format) in formats.iter().enumerate() {
299 let mut cursor = WriteCursor::new(&mut buffer[idx * Self::SHORT_FORMAT_SIZE..]);
300 cursor.write_u32(format.id.value());
301 write_string_to_cursor(
302 &mut cursor,
303 format.name.as_ref().map(|name| name.value()).unwrap_or_default(),
304 charset,
305 true,
306 )?;
307 }
308
309 Ok(Self {
310 use_ascii,
311 encoded_formats: Cow::Owned(buffer),
312 })
313 }
314 }
315
316 pub fn new_unicode(formats: &[ClipboardFormat], use_long_format: bool) -> EncodeResult<Self> {
317 Self::new_impl(formats, use_long_format, false)
318 }
319
320 pub fn new_ascii(formats: &[ClipboardFormat], use_long_format: bool) -> EncodeResult<Self> {
321 Self::new_impl(formats, use_long_format, true)
322 }
323
324 pub fn get_formats(&self, use_long_format: bool) -> PduResult<Vec<ClipboardFormat>> {
325 let mut src = ReadCursor::new(self.encoded_formats.as_ref());
326 let charset = if self.use_ascii {
327 CharacterSet::Ansi
328 } else {
329 CharacterSet::Unicode
330 };
331
332 if use_long_format {
333 const MINIMAL_FORMAT_SIZE: usize = 4 + 2 ;
335
336 let mut formats = Vec::with_capacity(16);
337
338 while src.len() >= MINIMAL_FORMAT_SIZE {
339 let id = src.read_u32();
340 let name = read_string_from_cursor(&mut src, charset, true).map_err(|e| decode_err!(e))?;
341
342 let format = ClipboardFormat::new(ClipboardFormatId::new(id)).with_name(ClipboardFormatName::new(name));
343
344 formats.push(format);
345 }
346
347 Ok(formats)
348 } else {
349 let items_count = src.len() / Self::SHORT_FORMAT_SIZE;
350
351 let mut formats = Vec::with_capacity(items_count);
352
353 for _ in 0..items_count {
354 let id = src.read_u32();
355 let name_buffer = src.read_slice(32);
356
357 let mut name_cursor: ReadCursor<'_> = ReadCursor::new(name_buffer);
358 let name = read_string_from_cursor(&mut name_cursor, charset, true).map_err(|e| decode_err!(e))?;
359
360 let format = ClipboardFormat::new(ClipboardFormatId(id)).with_name(ClipboardFormatName::new(name));
361
362 formats.push(format);
363 }
364
365 Ok(formats)
366 }
367 }
368}
369
370impl<'de> Decode<'de> for FormatList<'de> {
371 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
372 let header = PartialHeader::decode(src)?;
373
374 let use_ascii = header.message_flags.contains(ClipboardPduFlags::ASCII_NAMES);
375 ensure_size!(in: src, size: header.data_length());
376
377 let encoded_formats = src.read_slice(header.data_length());
378
379 Ok(Self {
380 use_ascii,
381 encoded_formats: Cow::Borrowed(encoded_formats),
382 })
383 }
384}
385
386impl Encode for FormatList<'_> {
387 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
388 let header_flags = if self.use_ascii {
389 ClipboardPduFlags::ASCII_NAMES
390 } else {
391 ClipboardPduFlags::empty()
392 };
393
394 let header = PartialHeader::new_with_flags(cast_int!("dataLen", self.encoded_formats.len())?, header_flags);
395 header.encode(dst)?;
396
397 ensure_size!(in: dst, size: self.encoded_formats.len());
398
399 dst.write_slice(&self.encoded_formats);
400
401 Ok(())
402 }
403
404 fn name(&self) -> &'static str {
405 Self::NAME
406 }
407
408 fn size(&self) -> usize {
409 PartialHeader::SIZE + self.encoded_formats.len()
410 }
411}
412
413#[derive(Debug, Clone, Copy, PartialEq, Eq)]
415pub enum FormatListResponse {
416 Ok,
417 Fail,
418}
419
420impl_pdu_pod!(FormatListResponse);
421
422impl FormatListResponse {
423 const NAME: &'static str = "FORMAT_LIST_RESPONSE";
424}
425
426impl Encode for FormatListResponse {
427 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
428 let header_flags = match self {
429 FormatListResponse::Ok => ClipboardPduFlags::RESPONSE_OK,
430 FormatListResponse::Fail => ClipboardPduFlags::RESPONSE_FAIL,
431 };
432
433 let header = PartialHeader::new_with_flags(0, header_flags);
434 header.encode(dst)
435 }
436
437 fn name(&self) -> &'static str {
438 Self::NAME
439 }
440
441 fn size(&self) -> usize {
442 PartialHeader::SIZE
443 }
444}
445
446impl<'de> Decode<'de> for FormatListResponse {
447 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
448 let header = PartialHeader::decode(src)?;
449 match header.message_flags {
450 ClipboardPduFlags::RESPONSE_OK => Ok(FormatListResponse::Ok),
451 ClipboardPduFlags::RESPONSE_FAIL => Ok(FormatListResponse::Fail),
452 _ => Err(invalid_field_err!("msgFlags", "Invalid format list message flags")),
453 }
454 }
455}