ironrdp_cliprdr/pdu/
capabilities.rs1use bitflags::bitflags;
2use ironrdp_core::{
3 cast_int, cast_length, ensure_fixed_part_size, ensure_size, invalid_field_err, Decode, DecodeError, DecodeResult,
4 Encode, EncodeResult, ReadCursor, WriteCursor,
5};
6use ironrdp_pdu::{impl_pdu_pod, read_padding, write_padding};
7
8use crate::pdu::PartialHeader;
9
10#[derive(Debug, Default, Clone, PartialEq, Eq)]
12pub struct Capabilities {
13 pub capabilities: Vec<CapabilitySet>,
14}
15
16impl_pdu_pod!(Capabilities);
17
18impl Capabilities {
19 const NAME: &'static str = "CLIPRDR_CAPS";
20 const FIXED_PART_SIZE: usize = 2 + 2 ;
21
22 fn inner_size(&self) -> usize {
23 Self::FIXED_PART_SIZE + self.capabilities.iter().map(|c| c.size()).sum::<usize>()
24 }
25
26 pub fn new(version: ClipboardProtocolVersion, general_flags: ClipboardGeneralCapabilityFlags) -> Self {
27 let capabilities = vec![CapabilitySet::General(GeneralCapabilitySet { version, general_flags })];
28
29 Self { capabilities }
30 }
31
32 pub fn flags(&self) -> ClipboardGeneralCapabilityFlags {
33 self.capabilities
35 .first()
36 .map(|set| set.general().general_flags)
37 .unwrap_or_else(ClipboardGeneralCapabilityFlags::empty)
38 }
39
40 pub fn version(&self) -> ClipboardProtocolVersion {
41 self.capabilities
42 .first()
43 .map(|set| set.general().version)
44 .unwrap_or(ClipboardProtocolVersion::V1)
45 }
46
47 pub fn downgrade(&mut self, server_caps: &Self) {
48 let client_flags = self.flags();
49 let server_flags = self.flags();
50
51 let flags = client_flags & server_flags;
52 let version = self.version().downgrade(server_caps.version());
53
54 self.capabilities = vec![CapabilitySet::General(GeneralCapabilitySet {
55 version,
56 general_flags: flags,
57 })];
58 }
59}
60
61impl Encode for Capabilities {
62 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
63 let header = PartialHeader::new(cast_int!("dataLen", self.inner_size())?);
64 header.encode(dst)?;
65
66 ensure_size!(in: dst, size: self.inner_size());
67
68 dst.write_u16(cast_length!(Self::NAME, "cCapabilitiesSets", self.capabilities.len())?);
69 write_padding!(dst, 2);
70
71 for capability in &self.capabilities {
72 capability.encode(dst)?;
73 }
74
75 Ok(())
76 }
77
78 fn name(&self) -> &'static str {
79 Self::NAME
80 }
81
82 fn size(&self) -> usize {
83 self.inner_size() + PartialHeader::SIZE
84 }
85}
86
87impl<'de> Decode<'de> for Capabilities {
88 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
89 let _header = PartialHeader::decode(src)?;
90
91 ensure_fixed_part_size!(in: src);
92 let capabilities_count = src.read_u16();
93 read_padding!(src, 2);
94
95 let mut capabilities = Vec::with_capacity(usize::from(capabilities_count));
96
97 for _ in 0..capabilities_count {
98 let caps = CapabilitySet::decode(src)?;
99 capabilities.push(caps);
100 }
101
102 Ok(Self { capabilities })
103 }
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
108pub enum CapabilitySet {
109 General(GeneralCapabilitySet),
110}
111
112impl_pdu_pod!(CapabilitySet);
113
114impl CapabilitySet {
115 const NAME: &'static str = "CLIPRDR_CAPS_SET";
116 const FIXED_PART_SIZE: usize = 2 + 2 ;
117
118 const CAPSTYPE_GENERAL: u16 = 0x0001;
119
120 pub fn general(&self) -> &GeneralCapabilitySet {
121 match self {
122 Self::General(value) => value,
123 }
124 }
125}
126
127impl From<GeneralCapabilitySet> for CapabilitySet {
128 fn from(value: GeneralCapabilitySet) -> Self {
129 Self::General(value)
130 }
131}
132
133impl Encode for CapabilitySet {
134 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
135 let (caps, length) = match self {
136 Self::General(value) => {
137 let length = value.size() + Self::FIXED_PART_SIZE;
138 (value, length)
139 }
140 };
141
142 ensure_size!(in: dst, size: length);
143 dst.write_u16(Self::CAPSTYPE_GENERAL);
144 dst.write_u16(cast_int!("lengthCapability", length)?);
145 caps.encode(dst)
146 }
147
148 fn name(&self) -> &'static str {
149 Self::NAME
150 }
151
152 fn size(&self) -> usize {
153 let variable_size = match self {
154 Self::General(value) => value.size(),
155 };
156
157 Self::FIXED_PART_SIZE + variable_size
158 }
159}
160
161impl<'de> Decode<'de> for CapabilitySet {
162 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
163 ensure_fixed_part_size!(in: src);
164
165 let caps_type = src.read_u16();
166 let _length = src.read_u16();
167
168 match caps_type {
169 Self::CAPSTYPE_GENERAL => {
170 let general = GeneralCapabilitySet::decode(src)?;
171 Ok(Self::General(general))
172 }
173 _ => Err(invalid_field_err!(
174 "capabilitySetType",
175 "invalid clipboard capability set type"
176 )),
177 }
178 }
179}
180
181#[derive(Debug, Clone, PartialEq, Eq)]
183pub struct GeneralCapabilitySet {
184 pub version: ClipboardProtocolVersion,
185 pub general_flags: ClipboardGeneralCapabilityFlags,
186}
187
188impl GeneralCapabilitySet {
189 const NAME: &'static str = "CLIPRDR_GENERAL_CAPABILITY";
190 const FIXED_PART_SIZE: usize = 4 + 4 ;
191}
192
193impl Encode for GeneralCapabilitySet {
194 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
195 ensure_fixed_part_size!(in: dst);
196
197 dst.write_u32(self.version.into());
198 dst.write_u32(self.general_flags.bits());
199
200 Ok(())
201 }
202
203 fn name(&self) -> &'static str {
204 Self::NAME
205 }
206
207 fn size(&self) -> usize {
208 Self::FIXED_PART_SIZE
209 }
210}
211
212impl<'de> Decode<'de> for GeneralCapabilitySet {
213 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
214 ensure_fixed_part_size!(in: src);
215
216 let version: ClipboardProtocolVersion = src.read_u32().try_into()?;
217 let general_flags = ClipboardGeneralCapabilityFlags::from_bits_truncate(src.read_u32());
218
219 Ok(Self { version, general_flags })
220 }
221}
222
223#[derive(Debug, Clone, Copy, PartialEq, Eq)]
227pub enum ClipboardProtocolVersion {
228 V1,
229 V2,
230}
231
232impl ClipboardProtocolVersion {
233 const VERSION_VALUE_V1: u32 = 0x0000_0001;
234 const VERSION_VALUE_V2: u32 = 0x0000_0002;
235
236 #[must_use]
237 pub fn downgrade(self, other: Self) -> Self {
238 if self != other {
239 return Self::V1;
240 }
241 self
242 }
243}
244
245impl From<ClipboardProtocolVersion> for u32 {
246 fn from(version: ClipboardProtocolVersion) -> Self {
247 match version {
248 ClipboardProtocolVersion::V1 => ClipboardProtocolVersion::VERSION_VALUE_V1,
249 ClipboardProtocolVersion::V2 => ClipboardProtocolVersion::VERSION_VALUE_V2,
250 }
251 }
252}
253
254impl TryFrom<u32> for ClipboardProtocolVersion {
255 type Error = DecodeError;
256
257 fn try_from(value: u32) -> Result<Self, Self::Error> {
258 match value {
259 Self::VERSION_VALUE_V1 => Ok(Self::V1),
260 Self::VERSION_VALUE_V2 => Ok(Self::V2),
261 _ => Err(invalid_field_err!("version", "Invalid clipboard capabilities version")),
262 }
263 }
264}
265
266bitflags! {
267 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
268 pub struct ClipboardGeneralCapabilityFlags: u32 {
269 const USE_LONG_FORMAT_NAMES = 0x0000_0002;
275 const STREAM_FILECLIP_ENABLED = 0x0000_0004;
279 const FILECLIP_NO_FILE_PATHS = 0x0000_0008;
282 const CAN_LOCK_CLIPDATA = 0x0000_0010;
286 const HUGE_FILE_SUPPORT_ENABLED = 0x0000_0020;
292 }
293}