ironrdp_cliprdr/pdu/
file_contents.rs1use std::borrow::Cow;
2
3use bitflags::bitflags;
4use ironrdp_core::{
5 cast_int, ensure_size, invalid_field_err, Decode, DecodeResult, Encode, EncodeResult, IntoOwned, ReadCursor,
6 WriteCursor,
7};
8use ironrdp_pdu::impl_pdu_borrowing;
9use ironrdp_pdu::utils::{combine_u64, split_u64};
10
11use crate::pdu::{ClipboardPduFlags, PartialHeader};
12
13bitflags! {
14 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
16 pub struct FileContentsFlags: u32 {
17 const SIZE = 0x0000_0001;
22 const DATA = 0x0000_0002;
27 }
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
32pub struct FileContentsResponse<'a> {
33 is_error: bool,
34 stream_id: u32,
35 data: Cow<'a, [u8]>,
36}
37
38impl_pdu_borrowing!(FileContentsResponse<'_>, OwnedFileContentsResponse);
39
40impl IntoOwned for FileContentsResponse<'_> {
41 type Owned = OwnedFileContentsResponse;
42
43 fn into_owned(self) -> Self::Owned {
44 OwnedFileContentsResponse {
45 is_error: self.is_error,
46 stream_id: self.stream_id,
47 data: Cow::Owned(self.data.into_owned()),
48 }
49 }
50}
51
52impl<'a> FileContentsResponse<'a> {
53 const NAME: &'static str = "CLIPRDR_FILECONTENTS_RESPONSE";
54 const FIXED_PART_SIZE: usize = 4 ;
55
56 fn inner_size(&self) -> usize {
57 Self::FIXED_PART_SIZE + self.data.len()
58 }
59
60 pub fn new_size_response(stream_id: u32, size: u64) -> Self {
62 Self {
63 is_error: false,
64 stream_id,
65 data: Cow::Owned(size.to_le_bytes().to_vec()),
66 }
67 }
68
69 pub fn new_data_response(stream_id: u32, data: impl Into<Cow<'a, [u8]>>) -> Self {
71 Self {
72 is_error: false,
73 stream_id,
74 data: data.into(),
75 }
76 }
77
78 pub fn new_error(stream_id: u32) -> Self {
80 Self {
81 is_error: true,
82 stream_id,
83 data: Cow::Borrowed(&[]),
84 }
85 }
86
87 pub fn stream_id(&self) -> u32 {
88 self.stream_id
89 }
90
91 pub fn data(&self) -> &[u8] {
92 &self.data
93 }
94
95 pub fn data_as_size(&self) -> DecodeResult<u64> {
97 if self.data.len() != 8 {
98 return Err(invalid_field_err!(
99 "requestedFileContentsData",
100 "Invalid data size for u64 size"
101 ));
102 }
103
104 Ok(u64::from_le_bytes(self.data.as_ref().try_into().unwrap()))
105 }
106}
107
108impl Encode for FileContentsResponse<'_> {
109 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
110 let flags = if self.is_error {
111 ClipboardPduFlags::RESPONSE_FAIL
112 } else {
113 ClipboardPduFlags::RESPONSE_OK
114 };
115
116 let header = PartialHeader::new_with_flags(cast_int!("dataLen", self.inner_size())?, flags);
117 header.encode(dst)?;
118
119 ensure_size!(in: dst, size: self.inner_size());
120
121 dst.write_u32(self.stream_id);
122 dst.write_slice(&self.data);
123
124 Ok(())
125 }
126
127 fn name(&self) -> &'static str {
128 Self::NAME
129 }
130
131 fn size(&self) -> usize {
132 PartialHeader::SIZE + self.inner_size()
133 }
134}
135
136impl<'de> Decode<'de> for FileContentsResponse<'de> {
137 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
138 let header = PartialHeader::decode(src)?;
139
140 let is_error = header.message_flags.contains(ClipboardPduFlags::RESPONSE_FAIL);
141
142 ensure_size!(in: src, size: header.data_length());
143
144 if header.data_length() < Self::FIXED_PART_SIZE {
145 return Err(invalid_field_err!("requestedFileContentsData", "Invalid data size"));
146 };
147
148 let data_size = header.data_length() - Self::FIXED_PART_SIZE;
149
150 let stream_id = src.read_u32();
151 let data = src.read_slice(data_size);
152
153 Ok(Self {
154 is_error,
155 stream_id,
156 data: Cow::Borrowed(data),
157 })
158 }
159}
160
161#[derive(Debug, Clone, PartialEq, Eq)]
163pub struct FileContentsRequest {
164 pub stream_id: u32,
165 pub index: u32,
166 pub flags: FileContentsFlags,
167 pub position: u64,
168 pub requested_size: u32,
169 pub data_id: Option<u32>,
170}
171
172impl FileContentsRequest {
173 const NAME: &'static str = "CLIPRDR_FILECONTENTS_REQUEST";
174 const FIXED_PART_SIZE: usize = 4 + 4 + 4 + 8 + 4 ;
175
176 fn inner_size(&self) -> usize {
177 let data_id_size = match self.data_id {
178 Some(_) => 4,
179 None => 0,
180 };
181
182 Self::FIXED_PART_SIZE + data_id_size
183 }
184}
185
186impl Encode for FileContentsRequest {
187 fn encode(&self, dst: &mut WriteCursor<'_>) -> EncodeResult<()> {
188 let header = PartialHeader::new(cast_int!("dataLen", self.inner_size())?);
189 header.encode(dst)?;
190
191 ensure_size!(in: dst, size: self.inner_size());
192
193 dst.write_u32(self.stream_id);
194 dst.write_u32(self.index);
195 dst.write_u32(self.flags.bits());
196
197 let (position_lo, position_hi) = split_u64(self.position);
198 dst.write_u32(position_lo);
199 dst.write_u32(position_hi);
200 dst.write_u32(self.requested_size);
201
202 if let Some(data_id) = self.data_id {
203 dst.write_u32(data_id);
204 };
205
206 Ok(())
207 }
208
209 fn name(&self) -> &'static str {
210 Self::NAME
211 }
212
213 fn size(&self) -> usize {
214 PartialHeader::SIZE + self.inner_size()
215 }
216}
217
218impl<'de> Decode<'de> for FileContentsRequest {
219 fn decode(src: &mut ReadCursor<'de>) -> DecodeResult<Self> {
220 let header = PartialHeader::decode(src)?;
221
222 let read_data_id = header.data_length() > Self::FIXED_PART_SIZE;
223
224 let mut expected_size = Self::FIXED_PART_SIZE;
225 if read_data_id {
226 expected_size += 4;
227 }
228
229 ensure_size!(in: src, size: expected_size);
230
231 let stream_id = src.read_u32();
232 let index = src.read_u32();
233 let flags = FileContentsFlags::from_bits_truncate(src.read_u32());
234 let position_lo = src.read_u32();
235 let position_hi = src.read_u32();
236 let position = combine_u64(position_lo, position_hi);
237 let requested_size = src.read_u32();
238 let data_id = if read_data_id { Some(src.read_u32()) } else { None };
239
240 Ok(Self {
241 stream_id,
242 index,
243 flags,
244 position,
245 requested_size,
246 data_id,
247 })
248 }
249}