FreeRDP
Loading...
Searching...
No Matches
cliprdr_utils.c
1
22#include <winpr/stream.h>
23#include <freerdp/utils/cliprdr_utils.h>
24#include <freerdp/channels/cliprdr.h>
25
26#include <freerdp/log.h>
27#define TAG FREERDP_TAG("utils." CLIPRDR_SVC_CHANNEL_NAME)
28
29#define CLIPRDR_FILEDESCRIPTOR_SIZE (4 + 32 + 4 + 16 + 8 + 8 + 520)
30#define CLIPRDR_MAX_FILE_SIZE (2U * 1024 * 1024 * 1024)
31
32static UINT64 filetime_to_uint64(FILETIME value)
33{
34 UINT64 converted = 0;
35 converted |= (UINT32)value.dwHighDateTime;
36 converted <<= 32;
37 converted |= (UINT32)value.dwLowDateTime;
38 return converted;
39}
40
41static FILETIME uint64_to_filetime(UINT64 value)
42{
43 FILETIME converted;
44 converted.dwLowDateTime = (UINT32)(value >> 0);
45 converted.dwHighDateTime = (UINT32)(value >> 32);
46 return converted;
47}
48
61UINT cliprdr_parse_file_list(const BYTE* format_data, UINT32 format_data_length,
62 FILEDESCRIPTORW** file_descriptor_array, UINT32* file_descriptor_count)
63{
64 UINT result = NO_ERROR;
65 UINT32 count = 0;
66 wStream sbuffer;
67 wStream* s = nullptr;
68
69 if (!format_data || !file_descriptor_array || !file_descriptor_count)
70 return ERROR_BAD_ARGUMENTS;
71
72 s = Stream_StaticConstInit(&sbuffer, format_data, format_data_length);
73 if (!s)
74 return ERROR_NOT_ENOUGH_MEMORY;
75
76 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
77 {
78 result = ERROR_INCORRECT_SIZE;
79 goto out;
80 }
81
82 Stream_Read_UINT32(s, count); /* cItems (4 bytes) */
83
84 if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, count, CLIPRDR_FILEDESCRIPTOR_SIZE))
85 {
86 result = ERROR_INCORRECT_SIZE;
87 goto out;
88 }
89
90 *file_descriptor_count = count;
91 *file_descriptor_array = calloc(count, sizeof(FILEDESCRIPTORW));
92 if (!*file_descriptor_array)
93 {
94 result = ERROR_NOT_ENOUGH_MEMORY;
95 goto out;
96 }
97
98 for (UINT32 i = 0; i < count; i++)
99 {
100 FILEDESCRIPTORW* file = &((*file_descriptor_array)[i]);
101
102 if (!cliprdr_read_filedescriptor(s, file))
103 goto out;
104 }
105
106 if (Stream_GetRemainingLength(s) > 0)
107 WLog_WARN(TAG, "packed file list has %" PRIuz " excess bytes",
108 Stream_GetRemainingLength(s));
109out:
110
111 return result;
112}
113
114BOOL cliprdr_read_filedescriptor(wStream* s, FILEDESCRIPTORW* descriptor)
115{
116 UINT64 tmp = 0;
117 WINPR_ASSERT(descriptor);
118
119 if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(FILEDESCRIPTORW)))
120 return FALSE;
121
122 Stream_Read_UINT32(s, descriptor->dwFlags); /* flags (4 bytes) */
123 Stream_Read_UINT32(s, descriptor->clsid.Data1);
124 Stream_Read_UINT16(s, descriptor->clsid.Data2);
125 Stream_Read_UINT16(s, descriptor->clsid.Data3);
126 Stream_Read(s, &descriptor->clsid.Data4, sizeof(descriptor->clsid.Data4));
127 Stream_Read_INT32(s, descriptor->sizel.cx);
128 Stream_Read_INT32(s, descriptor->sizel.cy);
129 Stream_Read_INT32(s, descriptor->pointl.x);
130 Stream_Read_INT32(s, descriptor->pointl.y);
131 Stream_Read_UINT32(s, descriptor->dwFileAttributes); /* fileAttributes (4 bytes) */
132 Stream_Read_UINT64(s, tmp); /* ftCreationTime (8 bytes) */
133 descriptor->ftCreationTime = uint64_to_filetime(tmp);
134 Stream_Read_UINT64(s, tmp); /* ftLastAccessTime (8 bytes) */
135 descriptor->ftLastAccessTime = uint64_to_filetime(tmp);
136 Stream_Read_UINT64(s, tmp); /* lastWriteTime (8 bytes) */
137 descriptor->ftLastWriteTime = uint64_to_filetime(tmp);
138 Stream_Read_UINT32(s, descriptor->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
139 Stream_Read_UINT32(s, descriptor->nFileSizeLow); /* fileSizeLow (4 bytes) */
140 return Stream_Read_UTF16_String(s, descriptor->cFileName,
141 ARRAYSIZE(descriptor->cFileName)); /* cFileName (520 bytes) */
142}
143
144BOOL cliprdr_write_filedescriptor(wStream* s, const FILEDESCRIPTORW* descriptor)
145{
146 WINPR_ASSERT(descriptor);
147
148 if (!Stream_EnsureRemainingCapacity(s, sizeof(FILEDESCRIPTORW)))
149 return FALSE;
150
151 Stream_Write_UINT32(s, descriptor->dwFlags); /* flags (4 bytes) */
152
153 Stream_Write_UINT32(s, descriptor->clsid.Data1);
154 Stream_Write_UINT16(s, descriptor->clsid.Data2);
155 Stream_Write_UINT16(s, descriptor->clsid.Data3);
156 Stream_Write(s, &descriptor->clsid.Data4, sizeof(descriptor->clsid.Data4));
157 Stream_Write_INT32(s, descriptor->sizel.cx);
158 Stream_Write_INT32(s, descriptor->sizel.cy);
159 Stream_Write_INT32(s, descriptor->pointl.x);
160 Stream_Write_INT32(s, descriptor->pointl.y);
161 Stream_Write_UINT32(s, descriptor->dwFileAttributes); /* fileAttributes (4 bytes) */
162 Stream_Write_UINT64(s, filetime_to_uint64(descriptor->ftCreationTime));
163 Stream_Write_UINT64(s, filetime_to_uint64(descriptor->ftLastAccessTime));
164 Stream_Write_UINT64(
165 s, filetime_to_uint64(descriptor->ftLastWriteTime)); /* lastWriteTime (8 bytes) */
166 Stream_Write_UINT32(s, descriptor->nFileSizeHigh); /* fileSizeHigh (4 bytes) */
167 Stream_Write_UINT32(s, descriptor->nFileSizeLow); /* fileSizeLow (4 bytes) */
168 return Stream_Write_UTF16_String(s, descriptor->cFileName,
169 ARRAYSIZE(descriptor->cFileName)); /* cFileName (520 bytes) */
170}
171
184UINT cliprdr_serialize_file_list(const FILEDESCRIPTORW* file_descriptor_array,
185 UINT32 file_descriptor_count, BYTE** format_data,
186 UINT32* format_data_length)
187{
188 return cliprdr_serialize_file_list_ex(CB_STREAM_FILECLIP_ENABLED, file_descriptor_array,
189 file_descriptor_count, format_data, format_data_length);
190}
191
192UINT cliprdr_serialize_file_list_ex(UINT32 flags, const FILEDESCRIPTORW* file_descriptor_array,
193 UINT32 file_descriptor_count, BYTE** format_data,
194 UINT32* format_data_length)
195{
196 UINT result = NO_ERROR;
197 size_t len = 0;
198 wStream* s = nullptr;
199
200 if (!file_descriptor_array || !format_data || !format_data_length)
201 return ERROR_BAD_ARGUMENTS;
202
203 if ((flags & CB_STREAM_FILECLIP_ENABLED) == 0)
204 {
205 WLog_WARN(TAG, "No file clipboard support annouonced!");
206 return ERROR_BAD_ARGUMENTS;
207 }
208
209 s = Stream_New(nullptr, 4 + file_descriptor_count * CLIPRDR_FILEDESCRIPTOR_SIZE);
210 if (!s)
211 return ERROR_NOT_ENOUGH_MEMORY;
212
213 Stream_Write_UINT32(s, file_descriptor_count); /* cItems (4 bytes) */
214
215 for (UINT32 i = 0; i < file_descriptor_count; i++)
216 {
217 const FILEDESCRIPTORW* file = &file_descriptor_array[i];
218
219 /*
220 * There is a known issue with Windows server getting stuck in
221 * an infinite loop when downloading files that are larger than
222 * 2 gigabytes. Do not allow clients to send such file lists.
223 *
224 * https://support.microsoft.com/en-us/help/2258090
225 */
226 if ((flags & CB_HUGE_FILE_SUPPORT_ENABLED) == 0)
227 {
228 if ((file->nFileSizeHigh > 0) || (file->nFileSizeLow >= CLIPRDR_MAX_FILE_SIZE))
229 {
230 WLog_ERR(TAG, "cliprdr does not support files over 2 GB");
231 result = ERROR_FILE_TOO_LARGE;
232 goto error;
233 }
234 }
235
236 if (!cliprdr_write_filedescriptor(s, file))
237 goto error;
238 }
239
240 Stream_SealLength(s);
241
242 Stream_GetBuffer(s, *format_data);
243 Stream_GetLength(s, len);
244 if (len > UINT32_MAX)
245 goto error;
246
247 *format_data_length = (UINT32)len;
248
249 Stream_Free(s, FALSE);
250
251 return result;
252
253error:
254 Stream_Free(s, TRUE);
255
256 return result;
257}