FreeRDP
Loading...
Searching...
No Matches
synthetic_file.c
1
20#include <winpr/config.h>
21#include <winpr/platform.h>
22
23WINPR_PRAGMA_DIAG_PUSH
24WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO
25WINPR_PRAGMA_DIAG_IGNORED_UNUSED_MACRO
26
27#define _FILE_OFFSET_BITS 64 // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
28
29WINPR_PRAGMA_DIAG_POP
30
31#include <errno.h>
32
33#include <winpr/wtypes.h>
34
35#include <winpr/crt.h>
36#include <winpr/clipboard.h>
37#include <winpr/collections.h>
38#include <winpr/file.h>
39#include <winpr/shell.h>
40#include <winpr/string.h>
41#include <winpr/wlog.h>
42#include <winpr/path.h>
43#include <winpr/print.h>
44
45#include "clipboard.h"
46#include "synthetic_file.h"
47
48#include "../log.h"
49#define TAG WINPR_TAG("clipboard.synthetic.file")
50
51static const char* mime_uri_list = "text/uri-list";
52static const char* mime_FileGroupDescriptorW = "FileGroupDescriptorW";
53static const char* mime_gnome_copied_files = "x-special/gnome-copied-files";
54static const char* mime_mate_copied_files = "x-special/mate-copied-files";
55
56struct synthetic_file
57{
58 WCHAR* local_name;
59 WCHAR* remote_name;
60
61 HANDLE fd;
62 INT64 offset;
63
64 DWORD dwFileAttributes;
65 FILETIME ftCreationTime;
66 FILETIME ftLastAccessTime;
67 FILETIME ftLastWriteTime;
68 DWORD nFileSizeHigh;
69 DWORD nFileSizeLow;
70};
71
72void free_synthetic_file(struct synthetic_file* file);
73
74static struct synthetic_file* make_synthetic_file(const WCHAR* local_name, const WCHAR* remote_name)
75{
76 struct synthetic_file* file = nullptr;
77 WIN32_FIND_DATAW fd = WINPR_C_ARRAY_INIT;
78 HANDLE hFind = nullptr;
79
80 WINPR_ASSERT(local_name);
81 WINPR_ASSERT(remote_name);
82
83 hFind = FindFirstFileW(local_name, &fd);
84 if (INVALID_HANDLE_VALUE == hFind)
85 {
86 WLog_ERR(TAG, "FindFirstFile failed (%" PRIu32 ")", GetLastError());
87 return nullptr;
88 }
89 FindClose(hFind);
90
91 file = calloc(1, sizeof(*file));
92 if (!file)
93 return nullptr;
94
95 file->fd = INVALID_HANDLE_VALUE;
96 file->offset = 0;
97 file->local_name = _wcsdup(local_name);
98 if (!file->local_name)
99 goto fail;
100
101 file->remote_name = _wcsdup(remote_name);
102 if (!file->remote_name)
103 goto fail;
104
105 {
106 const size_t len = _wcslen(file->remote_name);
107 if (S_OK != PathCchConvertStyleW(file->remote_name, len, PATH_STYLE_WINDOWS))
108 goto fail;
109 }
110
111 file->dwFileAttributes = fd.dwFileAttributes;
112 file->ftCreationTime = fd.ftCreationTime;
113 file->ftLastWriteTime = fd.ftLastWriteTime;
114 file->ftLastAccessTime = fd.ftLastAccessTime;
115 file->nFileSizeHigh = fd.nFileSizeHigh;
116 file->nFileSizeLow = fd.nFileSizeLow;
117
118 return file;
119fail:
120 free_synthetic_file(file);
121 return nullptr;
122}
123
124static UINT synthetic_file_read_close(struct synthetic_file* file, BOOL force);
125
126void free_synthetic_file(struct synthetic_file* file)
127{
128 if (!file)
129 return;
130
131 synthetic_file_read_close(file, TRUE);
132
133 free(file->local_name);
134 free(file->remote_name);
135 free(file);
136}
137
138/*
139 * Note that the function converts a single file name component,
140 * it does not take care of component separators.
141 */
142static WCHAR* convert_local_name_component_to_remote(wClipboard* clipboard, const WCHAR* local_name)
143{
144 wClipboardDelegate* delegate = ClipboardGetDelegate(clipboard);
145 WCHAR* remote_name = nullptr;
146
147 WINPR_ASSERT(delegate);
148
149 remote_name = _wcsdup(local_name);
150
151 /*
152 * Some file names are not valid on Windows. Check for these now
153 * so that we won't get ourselves into a trouble later as such names
154 * are known to crash some Windows shells when pasted via clipboard.
155 *
156 * The IsFileNameComponentValid callback can be overridden by the API
157 * user, if it is known, that the connected peer is not on the
158 * Windows platform.
159 */
160 if (!delegate->IsFileNameComponentValid(remote_name))
161 {
162 char name[MAX_PATH] = WINPR_C_ARRAY_INIT;
163 ConvertWCharToUtf8(local_name, name, sizeof(name) - 1);
164 WLog_ERR(TAG, "invalid file name component: %s", name);
165 goto error;
166 }
167
168 return remote_name;
169error:
170 free(remote_name);
171 return nullptr;
172}
173
174static WCHAR* concat_file_name(const WCHAR* dir, const WCHAR* file)
175{
176 size_t len_dir = 0;
177 size_t len_file = 0;
178 const WCHAR slash = '/';
179 WCHAR* buffer = nullptr;
180
181 WINPR_ASSERT(dir);
182 WINPR_ASSERT(file);
183
184 len_dir = _wcslen(dir);
185 len_file = _wcslen(file);
186 buffer = calloc(len_dir + 1 + len_file + 2, sizeof(WCHAR));
187
188 if (!buffer)
189 return nullptr;
190
191 memcpy(buffer, dir, len_dir * sizeof(WCHAR));
192 buffer[len_dir] = slash;
193 memcpy(buffer + len_dir + 1, file, len_file * sizeof(WCHAR));
194 return buffer;
195}
196
197static BOOL add_file_to_list(wClipboard* clipboard, size_t recursionDepth, const WCHAR* local_name,
198 const WCHAR* remote_name, wArrayList* files);
199
200static BOOL add_directory_entry_to_list(wClipboard* clipboard, size_t recursionDepth,
201 const WCHAR* local_dir_name, const WCHAR* remote_dir_name,
202 const LPWIN32_FIND_DATAW pFileData, wArrayList* files)
203{
204 BOOL result = FALSE;
205 WCHAR* local_name = nullptr;
206 WCHAR* remote_name = nullptr;
207 WCHAR* remote_base_name = nullptr;
208
209 WCHAR dotbuffer[6] = WINPR_C_ARRAY_INIT;
210 WCHAR dotdotbuffer[6] = WINPR_C_ARRAY_INIT;
211 const WCHAR* dot = InitializeConstWCharFromUtf8(".", dotbuffer, ARRAYSIZE(dotbuffer));
212 const WCHAR* dotdot = InitializeConstWCharFromUtf8("..", dotdotbuffer, ARRAYSIZE(dotdotbuffer));
213
214 WINPR_ASSERT(clipboard);
215 WINPR_ASSERT(local_dir_name);
216 WINPR_ASSERT(remote_dir_name);
217 WINPR_ASSERT(pFileData);
218 WINPR_ASSERT(files);
219
220 /* Skip special directory entries. */
221
222 if ((_wcscmp(pFileData->cFileName, dot) == 0) || (_wcscmp(pFileData->cFileName, dotdot) == 0))
223 return TRUE;
224
225 remote_base_name = convert_local_name_component_to_remote(clipboard, pFileData->cFileName);
226
227 if (!remote_base_name)
228 return FALSE;
229
230 local_name = concat_file_name(local_dir_name, pFileData->cFileName);
231 remote_name = concat_file_name(remote_dir_name, remote_base_name);
232
233 if (local_name && remote_name)
234 result = add_file_to_list(clipboard, recursionDepth, local_name, remote_name, files);
235
236 free(remote_base_name);
237 free(remote_name);
238 free(local_name);
239 return result;
240}
241
242static BOOL do_add_directory_contents_to_list(wClipboard* clipboard, size_t recursionDepth,
243 const WCHAR* local_name, const WCHAR* remote_name,
244 WCHAR* namebuf, wArrayList* files)
245{
246 WINPR_ASSERT(clipboard);
247 WINPR_ASSERT(local_name);
248 WINPR_ASSERT(remote_name);
249 WINPR_ASSERT(files);
250 WINPR_ASSERT(namebuf);
251
252 WIN32_FIND_DATAW FindData = WINPR_C_ARRAY_INIT;
253 HANDLE hFind = FindFirstFileW(namebuf, &FindData);
254 if (INVALID_HANDLE_VALUE == hFind)
255 {
256 WLog_ERR(TAG, "FindFirstFile failed (%" PRIu32 ")", GetLastError());
257 return FALSE;
258 }
259 while (TRUE)
260 {
261 if (!add_directory_entry_to_list(clipboard, recursionDepth, local_name, remote_name,
262 &FindData, files))
263 {
264 FindClose(hFind);
265 return FALSE;
266 }
267
268 BOOL bRet = FindNextFileW(hFind, &FindData);
269 if (!bRet)
270 {
271 FindClose(hFind);
272 if (ERROR_NO_MORE_FILES == GetLastError())
273 return TRUE;
274 WLog_WARN(TAG, "FindNextFile failed (%" PRIu32 ")", GetLastError());
275 return FALSE;
276 }
277 }
278
279 return TRUE;
280}
281
282static BOOL add_directory_contents_to_list(wClipboard* clipboard, size_t recursionDepth,
283 const WCHAR* local_name, const WCHAR* remote_name,
284 wArrayList* files)
285{
286 BOOL result = FALSE;
287 union
288 {
289 const char* c;
290 const WCHAR* w;
291 } wildcard;
292 const char buffer[6] = { '/', '\0', '*', '\0', '\0', '\0' };
293 wildcard.c = buffer;
294 const size_t wildcardLen = ARRAYSIZE(buffer) / sizeof(WCHAR);
295
296 WINPR_ASSERT(clipboard);
297 WINPR_ASSERT(local_name);
298 WINPR_ASSERT(remote_name);
299 WINPR_ASSERT(files);
300
301 const size_t max = CLIPBOARD_FILE_MAX_RECURSION;
302 if (recursionDepth > max)
303 {
304 WLog_ERR(TAG,
305 "This build only supports recursionDepth=%" PRIuz
306 " <= CLIPBOARD_FILE_MAX_RECURSION=%" PRIuz " recursion depth for folders copied.",
307 recursionDepth, max);
308 return FALSE;
309 }
310
311 size_t len = _wcslen(local_name);
312 WCHAR* namebuf = calloc(len + wildcardLen, sizeof(WCHAR));
313 if (!namebuf)
314 return FALSE;
315
316 _wcsncat(namebuf, local_name, len);
317 _wcsncat(namebuf, wildcard.w, wildcardLen);
318
319 result = do_add_directory_contents_to_list(clipboard, recursionDepth, local_name, remote_name,
320 namebuf, files);
321
322 free(namebuf);
323 return result;
324}
325
326BOOL add_file_to_list(wClipboard* clipboard, size_t recursionDepth, const WCHAR* local_name,
327 const WCHAR* remote_name, wArrayList* files)
328{
329 struct synthetic_file* file = nullptr;
330
331 WINPR_ASSERT(clipboard);
332 WINPR_ASSERT(local_name);
333 WINPR_ASSERT(remote_name);
334 WINPR_ASSERT(files);
335
336 file = make_synthetic_file(local_name, remote_name);
337
338 if (!file)
339 return FALSE;
340
341 if (!ArrayList_Append(files, file))
342 {
343 free_synthetic_file(file);
344 return FALSE;
345 }
346
347 if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
348 {
349 /*
350 * This is effectively a recursive call, but we do not track
351 * recursion depth, thus filesystem loops can cause a crash.
352 */
353 if (!add_directory_contents_to_list(clipboard, recursionDepth + 1, local_name, remote_name,
354 files))
355 return FALSE;
356 }
357
358 return TRUE;
359}
360
361static const WCHAR* get_basename(const WCHAR* name)
362{
363 const WCHAR* c = name;
364 const WCHAR* last_name = name;
365 const WCHAR slash = '/';
366
367 WINPR_ASSERT(name);
368
369 while (*c++)
370 {
371 if (*c == slash)
372 last_name = c + 1;
373 }
374
375 return last_name;
376}
377
378static BOOL process_file_name(wClipboard* clipboard, const WCHAR* local_name, wArrayList* files)
379{
380 BOOL result = FALSE;
381 const WCHAR* base_name = nullptr;
382 WCHAR* remote_name = nullptr;
383
384 WINPR_ASSERT(clipboard);
385 WINPR_ASSERT(local_name);
386 WINPR_ASSERT(files);
387
388 /*
389 * Start with the base name of the file. text/uri-list contains the
390 * exact files selected by the user, and we want the remote files
391 * to have names relative to that selection.
392 */
393 base_name = get_basename(local_name);
394 remote_name = convert_local_name_component_to_remote(clipboard, base_name);
395
396 if (!remote_name)
397 return FALSE;
398
399 result = add_file_to_list(clipboard, 0, local_name, remote_name, files);
400 free(remote_name);
401 return result;
402}
403
404static BOOL process_uri(wClipboard* clipboard, const char* uri, size_t uri_len)
405{
406 // URI is specified by RFC 8089: https://datatracker.ietf.org/doc/html/rfc8089
407 BOOL result = FALSE;
408 char* name = nullptr;
409
410 WINPR_ASSERT(clipboard);
411
412 name = parse_uri_to_local_file(uri, uri_len);
413 if (name)
414 {
415 WCHAR* wname = nullptr;
416 /*
417 * Note that local file names are not actually guaranteed to be
418 * encoded in UTF-8. Filesystems and users can use whatever they
419 * want. The OS does not care, aside from special treatment of
420 * '\0' and '/' bytes. But we need to make some decision here.
421 * Assuming UTF-8 is currently the most sane thing.
422 */
423 wname = ConvertUtf8ToWCharAlloc(name, nullptr);
424 if (wname)
425 result = process_file_name(clipboard, wname, clipboard->localFiles);
426
427 free(name);
428 free(wname);
429 }
430
431 return result;
432}
433
434static BOOL process_uri_list(wClipboard* clipboard, const char* data, size_t length)
435{
436 const char* cur = data;
437 const char* lim = data + length;
438
439 WINPR_ASSERT(clipboard);
440 WINPR_ASSERT(data);
441
442 WLog_VRB(TAG, "processing URI list:\n%.*s", WINPR_ASSERTING_INT_CAST(int, length), data);
443 ArrayList_Clear(clipboard->localFiles);
444
445 /*
446 * The "text/uri-list" Internet Media Type is specified by RFC 2483.
447 *
448 * While the RFCs 2046 and 2483 require the lines of text/... formats
449 * to be terminated by CRLF sequence, be prepared for those who don't
450 * read the spec, use plain LFs, and don't leave the trailing CRLF.
451 */
452
453 while (cur < lim)
454 {
455 BOOL comment = (*cur == '#');
456 const char* start = cur;
457 const char* stop = cur;
458
459 for (; stop < lim; stop++)
460 {
461 if (*stop == '\r')
462 {
463 if ((stop + 1 < lim) && (*(stop + 1) == '\n'))
464 cur = stop + 2;
465 else
466 cur = stop + 1;
467
468 break;
469 }
470
471 if (*stop == '\n')
472 {
473 cur = stop + 1;
474 break;
475 }
476 }
477
478 if (stop == lim)
479 {
480 if (strnlen(start, WINPR_ASSERTING_INT_CAST(size_t, stop - start)) < 1)
481 return TRUE;
482 cur = lim;
483 }
484
485 if (comment)
486 continue;
487
488 if (!process_uri(clipboard, start, WINPR_ASSERTING_INT_CAST(size_t, stop - start)))
489 return FALSE;
490 }
491
492 return TRUE;
493}
494
495static BOOL convert_local_file_to_filedescriptor(const struct synthetic_file* file,
496 FILEDESCRIPTORW* descriptor)
497{
498 size_t remote_len = 0;
499
500 WINPR_ASSERT(file);
501 WINPR_ASSERT(descriptor);
502
503 descriptor->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI;
504
505 if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
506 {
507 descriptor->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
508 descriptor->nFileSizeLow = 0;
509 descriptor->nFileSizeHigh = 0;
510 }
511 else
512 {
513 descriptor->dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
514 descriptor->nFileSizeLow = file->nFileSizeLow;
515 descriptor->nFileSizeHigh = file->nFileSizeHigh;
516 }
517
518 descriptor->ftLastWriteTime = file->ftLastWriteTime;
519
520 remote_len = _wcsnlen(file->remote_name, ARRAYSIZE(descriptor->cFileName));
521
522 if (remote_len >= ARRAYSIZE(descriptor->cFileName))
523 {
524 WLog_ERR(TAG, "file name too long (%" PRIuz " characters)", remote_len);
525 return FALSE;
526 }
527
528 memcpy(descriptor->cFileName, file->remote_name, remote_len * sizeof(WCHAR));
529 return TRUE;
530}
531
532static FILEDESCRIPTORW* convert_local_file_list_to_filedescriptors(wArrayList* files)
533{
534 size_t count = 0;
535 FILEDESCRIPTORW* descriptors = nullptr;
536
537 count = ArrayList_Count(files);
538
539 descriptors = calloc(count, sizeof(FILEDESCRIPTORW));
540
541 if (!descriptors)
542 goto error;
543
544 for (size_t i = 0; i < count; i++)
545 {
546 const struct synthetic_file* file = ArrayList_GetItem(files, i);
547
548 if (!convert_local_file_to_filedescriptor(file, &descriptors[i]))
549 goto error;
550 }
551
552 return descriptors;
553error:
554 free(descriptors);
555 return nullptr;
556}
557
558static void* convert_any_uri_list_to_filedescriptors(wClipboard* clipboard,
559 WINPR_ATTR_UNUSED UINT32 formatId,
560 UINT32* pSize)
561{
562 FILEDESCRIPTORW* descriptors = nullptr;
563
564 WINPR_ASSERT(clipboard);
565 WINPR_ASSERT(pSize);
566
567 descriptors = convert_local_file_list_to_filedescriptors(clipboard->localFiles);
568 *pSize = 0;
569 if (!descriptors)
570 return nullptr;
571
572 *pSize = (UINT32)ArrayList_Count(clipboard->localFiles) * sizeof(FILEDESCRIPTORW);
573 clipboard->fileListSequenceNumber = clipboard->sequenceNumber;
574 return descriptors;
575}
576
577static void* convert_uri_list_to_filedescriptors(wClipboard* clipboard, UINT32 formatId,
578 const void* data, UINT32* pSize)
579{
580 if (formatId != ClipboardGetFormatId(clipboard, mime_FileGroupDescriptorW))
581 {
582 WLog_ERR(TAG,
583 "Unuspported destination format %s [0x%04" PRIx32
584 "], trying to convert from %s [0x%04" PRIx32 "]",
585 ClipboardGetFormatName(clipboard, formatId), formatId,
586 ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
587 return nullptr;
588 }
589 if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_uri_list))
590 {
591 WLog_ERR(TAG,
592 "Unuspported destination format %s [0x%04" PRIx32
593 "], trying to convert from %s [0x%04" PRIx32 "]",
594 ClipboardGetFormatName(clipboard, formatId), formatId,
595 ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
596 return nullptr;
597 }
598 if (!process_uri_list(clipboard, (const char*)data, *pSize))
599 return nullptr;
600 return convert_any_uri_list_to_filedescriptors(clipboard, clipboard->formatId, pSize);
601}
602
603static BOOL process_files(wClipboard* clipboard, const char* data, UINT32 pSize, const char* prefix)
604{
605 WINPR_ASSERT(prefix);
606
607 const size_t prefix_len = strlen(prefix);
608
609 WINPR_ASSERT(clipboard);
610
611 ArrayList_Clear(clipboard->localFiles);
612
613 if (!data || (pSize < prefix_len))
614 return FALSE;
615 if (strncmp(data, prefix, prefix_len) != 0)
616 return FALSE;
617 data += prefix_len;
618 if (pSize < prefix_len)
619 return FALSE;
620 pSize -= WINPR_ASSERTING_INT_CAST(uint32_t, prefix_len);
621
622 BOOL rc = FALSE;
623 char* copy = strndup(data, pSize);
624 if (!copy)
625 goto fail;
626
627 {
628 char* endptr = nullptr;
629 char* tok = strtok_s(copy, "\n", &endptr);
630 while (tok)
631 {
632 const size_t tok_len = strnlen(tok, pSize);
633 if (!process_uri(clipboard, tok, tok_len))
634 goto fail;
635 if (pSize < tok_len)
636 goto fail;
637 pSize -= WINPR_ASSERTING_INT_CAST(uint32_t, tok_len);
638 tok = strtok_s(nullptr, "\n", &endptr);
639 }
640 }
641
642 rc = TRUE;
643
644fail:
645 free(copy);
646 return rc;
647}
648
649static BOOL process_gnome_copied_files(wClipboard* clipboard, const char* data, UINT32 pSize)
650{
651 return process_files(clipboard, data, pSize, "copy\n");
652}
653
654static BOOL process_mate_copied_files(wClipboard* clipboard, const char* data, UINT32 pSize)
655{
656 return process_files(clipboard, data, pSize, "copy\n");
657}
658
659static void* convert_gnome_copied_files_to_filedescriptors(wClipboard* clipboard, UINT32 formatId,
660 const void* data, UINT32* pSize)
661{
662 if (formatId != ClipboardGetFormatId(clipboard, mime_FileGroupDescriptorW))
663 {
664 WLog_ERR(TAG,
665 "Unuspported destination format %s [0x%04" PRIx32
666 "], trying to convert from %s [0x%04" PRIx32 "]",
667 ClipboardGetFormatName(clipboard, formatId), formatId,
668 ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
669 return nullptr;
670 }
671 if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_gnome_copied_files))
672 {
673 WLog_ERR(TAG,
674 "Unuspported destination format %s [0x%04" PRIx32
675 "], trying to convert from %s [0x%04" PRIx32 "]",
676 ClipboardGetFormatName(clipboard, formatId), formatId,
677 ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
678 return nullptr;
679 }
680 if (!process_gnome_copied_files(clipboard, (const char*)data, *pSize))
681 return nullptr;
682 return convert_any_uri_list_to_filedescriptors(clipboard, clipboard->formatId, pSize);
683}
684
685static void* convert_mate_copied_files_to_filedescriptors(wClipboard* clipboard, UINT32 formatId,
686 const void* data, UINT32* pSize)
687{
688 if (formatId != ClipboardGetFormatId(clipboard, mime_FileGroupDescriptorW))
689 {
690 WLog_ERR(TAG,
691 "Unuspported destination format %s [0x%04" PRIx32
692 "], trying to convert from %s [0x%04" PRIx32 "]",
693 ClipboardGetFormatName(clipboard, formatId), formatId,
694 ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
695 return nullptr;
696 }
697 if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_mate_copied_files))
698 {
699 WLog_ERR(TAG,
700 "Unuspported destination format %s [0x%04" PRIx32
701 "], trying to convert from %s [0x%04" PRIx32 "]",
702 ClipboardGetFormatName(clipboard, formatId), formatId,
703 ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
704 return nullptr;
705 }
706
707 if (!process_mate_copied_files(clipboard, (const char*)data, *pSize))
708 return nullptr;
709
710 return convert_any_uri_list_to_filedescriptors(clipboard, formatId, pSize);
711}
712
713WINPR_ATTR_NODISCARD
714static size_t count_special_chars(const WCHAR* str, size_t charLen)
715{
716 size_t count = 0;
717 const WCHAR* start = str;
718 const WCHAR* end = &str[charLen];
719
720 WINPR_ASSERT(str);
721 while ((start < end) && (*start))
722 {
723 const WCHAR sharp = winpr_wchar_ToLE('#');
724 const WCHAR questionmark = winpr_wchar_ToLE('?');
725 const WCHAR star = winpr_wchar_ToLE('*');
726 const WCHAR exclamationmark = winpr_wchar_ToLE('!');
727 const WCHAR percent = winpr_wchar_ToLE('%');
728
729 if ((*start == sharp) || (*start == questionmark) || (*start == star) ||
730 (*start == exclamationmark) || (*start == percent))
731 {
732 count++;
733 }
734 start++;
735 }
736 return count;
737}
738
739static const char* stop_at_special_chars(const char* str)
740{
741 const char* start = str;
742 WINPR_ASSERT(str);
743
744 while (*start)
745 {
746 if (*start == '#' || *start == '?' || *start == '*' || *start == '!' || *start == '%')
747 {
748 return start;
749 }
750 start++;
751 }
752 return nullptr;
753}
754
757WINPR_ATTR_NODISCARD
758static BOOL descriptor_is_nested(wLog* log, const FILEDESCRIPTORW* dsc)
759{
760 WINPR_ASSERT(dsc);
761
762 /* only search the name: what follows its '\0' in cFileName is not guaranteed to be zeroed */
763 const size_t len = _wcsnlen(dsc->cFileName, ARRAYSIZE(dsc->cFileName));
764 const WCHAR* found = winpr_wcsnchr(dsc->cFileName, len, winpr_wchar_ToLE('\\'));
765
766 const DWORD level = WLOG_TRACE;
767 if (WLog_IsLevelActive(log, level))
768 {
769 char cFileName[260] = WINPR_C_ARRAY_INIT;
770 const int clen = sizeof(cFileName);
771 ConvertWCharNToUtf8(dsc->cFileName, ARRAYSIZE(dsc->cFileName), cFileName,
772 ARRAYSIZE(cFileName));
773 WLog_Print(log, level, "%.*s %s nested", clen, cFileName, found ? "is" : "is NOT");
774 }
775 return found != nullptr;
776}
777
778WINPR_ATTR_NODISCARD
779static FILETIME readft(wStream* s)
780{
781 FILETIME ft = WINPR_C_ARRAY_INIT;
782 ft.dwLowDateTime = Stream_Get_UINT32(s);
783 ft.dwHighDateTime = Stream_Get_UINT32(s);
784 return ft;
785}
786
787WINPR_ATTR_NODISCARD
788static BOOL read_descriptor(wStream* s, FILEDESCRIPTORW* dsc)
789{
790 WINPR_ASSERT(dsc);
791
792 if (!Stream_CheckAndLogRequiredLength(TAG, s, sizeof(FILEDESCRIPTORW)))
793 return FALSE;
794
795 dsc->dwFlags = Stream_Get_UINT32(s);
796 Stream_Read(s, &dsc->clsid, sizeof(dsc->clsid));
797 dsc->sizel.cx = Stream_Get_INT32(s);
798 dsc->sizel.cy = Stream_Get_INT32(s);
799 dsc->pointl.x = Stream_Get_INT32(s);
800 dsc->pointl.y = Stream_Get_INT32(s);
801 dsc->dwFileAttributes = Stream_Get_UINT32(s);
802 dsc->ftCreationTime = readft(s);
803 dsc->ftLastAccessTime = readft(s);
804 dsc->ftLastWriteTime = readft(s);
805 dsc->nFileSizeHigh = Stream_Get_UINT32(s);
806 dsc->nFileSizeLow = Stream_Get_UINT32(s);
807 Stream_Read(s, dsc->cFileName, sizeof(dsc->cFileName));
808 return TRUE;
809}
810
811/* The universal converter from filedescriptors to different file lists */
812WINPR_ATTR_MALLOC(free, 1)
813static void* convert_filedescriptors_to_file_list(wClipboard* clipboard, const void* data,
814 UINT32* pSize, const char* header,
815 const char* lineprefix, const char* lineending,
816 BOOL skip_last_lineending)
817{
818 wLog* log = WLog_Get(TAG);
819 UINT32 nrDescriptors = 0;
820 size_t baseLength = 0;
821 size_t header_len = strlen(header);
822 size_t lineprefix_len = strlen(lineprefix);
823 size_t lineending_len = strlen(lineending);
824
825 if (!clipboard || !data || !pSize)
826 return nullptr;
827
828 if (*pSize < sizeof(UINT32))
829 return nullptr;
830
831 if (clipboard->delegate.basePath)
832 baseLength = strnlen(clipboard->delegate.basePath, MAX_PATH);
833
834 if (baseLength < 1)
835 return nullptr;
836
837 wStream sbuffer = WINPR_C_ARRAY_INIT;
838 wStream* s = Stream_StaticConstInit(&sbuffer, data, *pSize);
839 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
840 return nullptr;
841
842 Stream_Read_UINT32(s, nrDescriptors);
843
844 const size_t count = (*pSize - 4) / sizeof(FILEDESCRIPTORW);
845
846 if ((count < 1) || (count != nrDescriptors))
847 return nullptr;
848
849 /* formatId is the destination format (see ClipboardRegisterSynthesizerEx), the descriptors
850 * are the source data */
851 if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_FileGroupDescriptorW))
852 return nullptr;
853
854 /* Plus 1 for '/' between basepath and filename*/
855 const size_t decoration_len = lineprefix_len + lineending_len + baseLength + 1;
856 size_t alloc = header_len;
857
858 const size_t descriptorStartPos = Stream_GetPosition(s);
859 /* Get total size of file/folder names under first level folder only */
860 for (size_t x = 0; x < count; x++)
861 {
862 FILEDESCRIPTORW dsc = WINPR_C_ARRAY_INIT;
863 if (!read_descriptor(s, &dsc))
864 return nullptr;
865
866 if (!descriptor_is_nested(log, &dsc))
867 {
868 alloc += ARRAYSIZE(dsc.cFileName) *
869 8; /* Overallocate, just take the biggest value the result path can have */
870 /* # (1 char) -> %23 (3 chars) , the first char is replaced inplace */
871 alloc += count_special_chars(dsc.cFileName, ARRAYSIZE(dsc.cFileName)) * sizeof(WCHAR);
872 alloc += decoration_len;
873 }
874 }
875
876 if (!Stream_SetPosition(s, descriptorStartPos))
877 return nullptr;
878
879 /* Append a prefix file:// and postfix \n for each file */
880 /* We need to keep last \n since snprintf is null terminated!! */
881 alloc++;
882 char* dst = calloc(alloc, sizeof(char));
883
884 if (!dst)
885 return nullptr;
886
887 (void)_snprintf(&dst[0], alloc, "%s", header);
888
889 size_t pos = header_len;
890
891 for (size_t x = 0; x < count; x++)
892 {
893 int rc = -1;
894 BOOL fail = TRUE;
895 FILEDESCRIPTORW dsc = WINPR_C_ARRAY_INIT;
896 if (!read_descriptor(s, &dsc))
897 goto loop_fail;
898
899 if (descriptor_is_nested(log, &dsc))
900 continue;
901
902 char curName[520] = WINPR_C_ARRAY_INIT;
903 const char* stop_at = nullptr;
904 const char* previous_at = nullptr;
905
906 if (ConvertWCharNToUtf8(dsc.cFileName, ARRAYSIZE(dsc.cFileName), curName,
907 ARRAYSIZE(curName)) < 0)
908 goto loop_fail;
909
910 HRESULT hr = PathCchConvertStyleA(curName, ARRAYSIZE(curName), PATH_STYLE_UNIX);
911 if (FAILED(hr))
912 goto loop_fail;
913
914 rc = _snprintf(&dst[pos], alloc - pos, "%s%s/", lineprefix, clipboard->delegate.basePath);
915
916 if (rc < 0)
917 goto loop_fail;
918
919 pos += (size_t)rc;
920
921 previous_at = curName;
922 while ((stop_at = stop_at_special_chars(previous_at)) != nullptr)
923 {
924 const intptr_t diff = stop_at - previous_at;
925 if (diff < 0)
926 goto loop_fail;
927 char* tmp = strndup(previous_at, WINPR_ASSERTING_INT_CAST(size_t, diff));
928 if (!tmp)
929 goto loop_fail;
930
931 rc = _snprintf(&dst[pos], WINPR_ASSERTING_INT_CAST(size_t, diff + 1), "%s", tmp);
932 free(tmp);
933 if (rc < 0)
934 goto loop_fail;
935
936 pos += (size_t)rc;
937 rc = _snprintf(&dst[pos], 4, "%%%x", *stop_at);
938 if (rc < 0)
939 goto loop_fail;
940
941 pos += (size_t)rc;
942 previous_at = stop_at + 1;
943 }
944
945 rc = _snprintf(&dst[pos], alloc - pos, "%s%s", previous_at, lineending);
946
947 fail = FALSE;
948 loop_fail:
949 if ((rc < 0) || fail)
950 {
951 free(dst);
952 return nullptr;
953 }
954
955 pos += (size_t)rc;
956 }
957
958 if (skip_last_lineending)
959 {
960 const size_t endlen = strlen(lineending);
961 if (alloc > endlen)
962 {
963 const size_t len = strnlen(dst, alloc);
964 if (len < endlen)
965 {
966 free(dst);
967 return nullptr;
968 }
969
970 if (memcmp(&dst[len - endlen], lineending, endlen) == 0)
971 {
972 memset(&dst[len - endlen], 0, endlen);
973 alloc -= endlen;
974 }
975 }
976 }
977
978 alloc = strnlen(dst, alloc) + 1;
979 *pSize = (UINT32)alloc;
980 clipboard->fileListSequenceNumber = clipboard->sequenceNumber;
981 return dst;
982}
983
984/* Prepend header of kde dolphin format to file list
985 * See:
986 * GTK: https://docs.gtk.org/glib/struct.Uri.html
987 * uri syntax: https://www.rfc-editor.org/rfc/rfc3986#section-3
988 * uri-lists format: https://www.rfc-editor.org/rfc/rfc2483#section-5
989 */
990WINPR_ATTR_MALLOC(free, 1)
991static void* convert_filedescriptors_to_uri_list(wClipboard* clipboard, UINT32 formatId,
992 const void* data, UINT32* pSize)
993{
994 if (formatId != ClipboardGetFormatId(clipboard, mime_uri_list))
995 {
996 WLog_ERR(TAG,
997 "Unuspported destination format %s [0x%04" PRIx32
998 "], trying to convert from %s [0x%04" PRIx32 "]",
999 ClipboardGetFormatName(clipboard, formatId), formatId,
1000 ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
1001 return nullptr;
1002 }
1003 if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_FileGroupDescriptorW))
1004 {
1005 WLog_ERR(TAG,
1006 "Unuspported destination format %s [0x%04" PRIx32
1007 "], trying to convert from %s [0x%04" PRIx32 "]",
1008 ClipboardGetFormatName(clipboard, formatId), formatId,
1009 ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
1010 return nullptr;
1011 }
1012 return convert_filedescriptors_to_file_list(clipboard, data, pSize, "", "file://", "\r\n",
1013 FALSE);
1014}
1015
1016/* Prepend header of common gnome format to file list*/
1017WINPR_ATTR_MALLOC(free, 1)
1018static void* convert_filedescriptors_to_gnome_copied_files(wClipboard* clipboard, UINT32 formatId,
1019 const void* data, UINT32* pSize)
1020{
1021 if (formatId != ClipboardGetFormatId(clipboard, mime_gnome_copied_files))
1022 {
1023 WLog_ERR(TAG,
1024 "Unuspported destination format %s [0x%04" PRIx32
1025 "], trying to convert from %s [0x%04" PRIx32 "]",
1026 ClipboardGetFormatName(clipboard, formatId), formatId,
1027 ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
1028 return nullptr;
1029 }
1030 if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_FileGroupDescriptorW))
1031 {
1032 WLog_ERR(TAG,
1033 "Unuspported destination format %s [0x%04" PRIx32
1034 "], trying to convert from %s [0x%04" PRIx32 "]",
1035 ClipboardGetFormatName(clipboard, formatId), formatId,
1036 ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
1037 return nullptr;
1038 }
1039 return convert_filedescriptors_to_file_list(clipboard, data, pSize, "copy\n", "file://", "\n",
1040 TRUE);
1041}
1042
1043WINPR_ATTR_MALLOC(free, 1)
1044static void* convert_filedescriptors_to_mate_copied_files(wClipboard* clipboard, UINT32 formatId,
1045 const void* data, UINT32* pSize)
1046{
1047 if (formatId != ClipboardGetFormatId(clipboard, mime_mate_copied_files))
1048 {
1049 WLog_ERR(TAG,
1050 "Unuspported destination format %s [0x%04" PRIx32
1051 "], trying to convert from %s [0x%04" PRIx32 "]",
1052 ClipboardGetFormatName(clipboard, formatId), formatId,
1053 ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
1054 return nullptr;
1055 }
1056 if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_FileGroupDescriptorW))
1057 {
1058 WLog_ERR(TAG,
1059 "Unuspported destination format %s [0x%04" PRIx32
1060 "], trying to convert from %s [0x%04" PRIx32 "]",
1061 ClipboardGetFormatName(clipboard, formatId), formatId,
1062 ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
1063 return nullptr;
1064 }
1065 char* pDstData = convert_filedescriptors_to_file_list(clipboard, data, pSize, "copy\n",
1066 "file://", "\n", TRUE);
1067 if (!pDstData)
1068 {
1069 return pDstData;
1070 }
1071 /* Replace last \n with \0
1072 see
1073 mate-desktop/caja/libcaja-private/caja-clipboard.c:caja_clipboard_get_uri_list_from_selection_data
1074 */
1075
1076 pDstData[*pSize - 1] = '\0';
1077 *pSize = *pSize - 1;
1078 return pDstData;
1079}
1080
1081static void array_free_synthetic_file(void* the_file)
1082{
1083 struct synthetic_file* file = the_file;
1084 free_synthetic_file(file);
1085}
1086
1087static BOOL register_file_formats_and_synthesizers(wClipboard* clipboard)
1088{
1089 wObject* obj = nullptr;
1090
1091 /*
1092 1. Gnome Nautilus based file manager (Nautilus only with version >= 3.30 AND < 40):
1093 TARGET: UTF8_STRING
1094 format: x-special/nautilus-clipboard\copy\n\file://path\n\0
1095 2. Kde Dolpin and Qt:
1096 TARGET: text/uri-list
1097 format: file:path\r\n\0
1098 See:
1099 GTK: https://docs.gtk.org/glib/struct.Uri.html
1100 uri syntax: https://www.rfc-editor.org/rfc/rfc3986#section-3
1101 uri-lists format: https://www.rfc-editor.org/rfc/rfc2483#section-5
1102 3. Gnome and others (Unity/XFCE/Nautilus < 3.30/Nautilus >= 40):
1103 TARGET: x-special/gnome-copied-files
1104 format: copy\nfile://path\n\0
1105 4. Mate Caja:
1106 TARGET: x-special/mate-copied-files
1107 format: copy\nfile://path\n
1108
1109 TODO: other file managers do not use previous targets and formats.
1110 */
1111
1112 const UINT32 local_gnome_file_format_id =
1113 ClipboardRegisterFormat(clipboard, mime_gnome_copied_files);
1114 const UINT32 local_mate_file_format_id =
1115 ClipboardRegisterFormat(clipboard, mime_mate_copied_files);
1116 const UINT32 file_group_format_id =
1117 ClipboardRegisterFormat(clipboard, mime_FileGroupDescriptorW);
1118 const UINT32 local_file_format_id = ClipboardRegisterFormat(clipboard, mime_uri_list);
1119
1120 if (!file_group_format_id || !local_file_format_id || !local_gnome_file_format_id ||
1121 !local_mate_file_format_id)
1122 goto error;
1123
1124 clipboard->localFiles = ArrayList_New(FALSE);
1125
1126 if (!clipboard->localFiles)
1127 goto error;
1128
1129 obj = ArrayList_Object(clipboard->localFiles);
1130 obj->fnObjectFree = array_free_synthetic_file;
1131
1132 if (!ClipboardRegisterSynthesizerEx(clipboard, local_file_format_id, file_group_format_id,
1133 convert_uri_list_to_filedescriptors))
1134 goto error_free_local_files;
1135
1136 if (!ClipboardRegisterSynthesizerEx(clipboard, file_group_format_id, local_file_format_id,
1137 convert_filedescriptors_to_uri_list))
1138 goto error_free_local_files;
1139
1140 if (!ClipboardRegisterSynthesizerEx(clipboard, local_gnome_file_format_id, file_group_format_id,
1141 convert_gnome_copied_files_to_filedescriptors))
1142 goto error_free_local_files;
1143
1144 if (!ClipboardRegisterSynthesizerEx(clipboard, file_group_format_id, local_gnome_file_format_id,
1145 convert_filedescriptors_to_gnome_copied_files))
1146 goto error_free_local_files;
1147
1148 if (!ClipboardRegisterSynthesizerEx(clipboard, local_mate_file_format_id, file_group_format_id,
1149 convert_mate_copied_files_to_filedescriptors))
1150 goto error_free_local_files;
1151
1152 if (!ClipboardRegisterSynthesizerEx(clipboard, file_group_format_id, local_mate_file_format_id,
1153 convert_filedescriptors_to_mate_copied_files))
1154 goto error_free_local_files;
1155
1156 return TRUE;
1157error_free_local_files:
1158 ArrayList_Free(clipboard->localFiles);
1159 clipboard->localFiles = nullptr;
1160error:
1161 return FALSE;
1162}
1163
1164static int32_t file_get_size(const struct synthetic_file* file, UINT64* size)
1165{
1166 UINT64 s = 0;
1167
1168 if (!file || !size)
1169 return E_INVALIDARG;
1170
1171 s = file->nFileSizeHigh;
1172 s <<= 32;
1173 s |= file->nFileSizeLow;
1174 *size = s;
1175 return NO_ERROR;
1176}
1177
1178static UINT delegate_file_request_size(wClipboardDelegate* delegate,
1179 const wClipboardFileSizeRequest* request)
1180{
1181 UINT64 size = 0;
1182
1183 if (!delegate || !delegate->clipboard || !request)
1184 return ERROR_BAD_ARGUMENTS;
1185
1186 if (delegate->clipboard->sequenceNumber != delegate->clipboard->fileListSequenceNumber)
1187 return ERROR_INVALID_STATE;
1188
1189 struct synthetic_file* file =
1190 ArrayList_GetItem(delegate->clipboard->localFiles, request->listIndex);
1191
1192 if (!file)
1193 return ERROR_INDEX_ABSENT;
1194
1195 const int32_t s = file_get_size(file, &size);
1196 uint32_t error = 0;
1197 if (error)
1198 error = delegate->ClipboardFileSizeFailure(delegate, request, (UINT)s);
1199 else
1200 error = delegate->ClipboardFileSizeSuccess(delegate, request, size);
1201
1202 if (error)
1203 WLog_WARN(TAG, "failed to report file size result: 0x%08X", error);
1204
1205 return NO_ERROR;
1206}
1207
1208UINT synthetic_file_read_close(struct synthetic_file* file, BOOL force)
1209{
1210 if (!file || INVALID_HANDLE_VALUE == file->fd)
1211 return NO_ERROR;
1212
1213 /* Always force close the file. Clipboard might open hundreds of files
1214 * so avoid caching to prevent running out of available file descriptors */
1215 UINT64 size = 0;
1216 file_get_size(file, &size);
1217 if ((file->offset < 0) || ((UINT64)file->offset >= size) || force)
1218 {
1219 WLog_VRB(TAG, "close file %p", file->fd);
1220 if (!CloseHandle(file->fd))
1221 {
1222 WLog_WARN(TAG, "failed to close fd %p: %" PRIu32, file->fd, GetLastError());
1223 }
1224
1225 file->fd = INVALID_HANDLE_VALUE;
1226 }
1227
1228 return NO_ERROR;
1229}
1230
1231static UINT file_get_range(struct synthetic_file* file, UINT64 offset, UINT32 size,
1232 BYTE** actual_data, UINT32* actual_size)
1233{
1234 UINT error = NO_ERROR;
1235 DWORD dwLow = 0;
1236 DWORD dwHigh = 0;
1237
1238 WINPR_ASSERT(file);
1239 WINPR_ASSERT(actual_data);
1240 WINPR_ASSERT(actual_size);
1241
1242 if (INVALID_HANDLE_VALUE == file->fd)
1243 {
1244 BY_HANDLE_FILE_INFORMATION FileInfo = WINPR_C_ARRAY_INIT;
1245
1246 file->fd = CreateFileW(file->local_name, GENERIC_READ, 0, nullptr, OPEN_EXISTING,
1247 FILE_ATTRIBUTE_NORMAL, nullptr);
1248 if (INVALID_HANDLE_VALUE == file->fd)
1249 {
1250 char name[MAX_PATH] = WINPR_C_ARRAY_INIT;
1251 ConvertWCharToUtf8(file->local_name, name, sizeof(name) - 1);
1252 error = GetLastError();
1253 WLog_ERR(TAG, "failed to open file %s: 0x%08" PRIx32, name, error);
1254 return error;
1255 }
1256
1257 if (!GetFileInformationByHandle(file->fd, &FileInfo))
1258 {
1259 char name[MAX_PATH] = WINPR_C_ARRAY_INIT;
1260 ConvertWCharToUtf8(file->local_name, name, sizeof(name) - 1);
1261 (void)CloseHandle(file->fd);
1262 file->fd = INVALID_HANDLE_VALUE;
1263 error = GetLastError();
1264 WLog_ERR(TAG, "Get file [%s] information fail: 0x%08" PRIx32, name, error);
1265 return error;
1266 }
1267
1268 file->offset = 0;
1269 file->nFileSizeHigh = FileInfo.nFileSizeHigh;
1270 file->nFileSizeLow = FileInfo.nFileSizeLow;
1271
1272 /*
1273 {
1274 UINT64 s = 0;
1275 file_get_size(file, &s);
1276 WLog_DBG(TAG, "open file %d -> %s", file->fd, file->local_name);
1277 WLog_DBG(TAG, "file %d size: %" PRIu64 " bytes", file->fd, s);
1278 } //*/
1279 }
1280
1281 do
1282 {
1283 /*
1284 * We should avoid seeking when possible as some filesystems (e.g.,
1285 * an FTP server mapped via FUSE) may not support seeking. We keep
1286 * an accurate account of the current file offset and do not call
1287 * lseek() if the client requests file content sequentially.
1288 */
1289 if (offset > INT64_MAX)
1290 {
1291 WLog_ERR(TAG, "offset [%" PRIu64 "] > INT64_MAX", offset);
1292 error = ERROR_SEEK;
1293 break;
1294 }
1295
1296 if (file->offset != (INT64)offset)
1297 {
1298 WLog_DBG(TAG, "file %p force seeking to %" PRIu64 ", current %" PRId64, file->fd,
1299 offset, file->offset);
1300
1301 dwHigh = offset >> 32;
1302 dwLow = offset & 0xFFFFFFFF;
1303 if (INVALID_SET_FILE_POINTER == SetFilePointer(file->fd,
1304 WINPR_ASSERTING_INT_CAST(LONG, dwLow),
1305 (PLONG)&dwHigh, FILE_BEGIN))
1306 {
1307 error = GetLastError();
1308 break;
1309 }
1310 }
1311
1312 BYTE* buffer = malloc(size);
1313 if (!buffer)
1314 {
1315 error = ERROR_NOT_ENOUGH_MEMORY;
1316 break;
1317 }
1318 if (!ReadFile(file->fd, buffer, size, (LPDWORD)actual_size, nullptr))
1319 {
1320 free(buffer);
1321 error = GetLastError();
1322 break;
1323 }
1324
1325 *actual_data = buffer;
1326 file->offset += *actual_size;
1327 WLog_VRB(TAG, "file %p actual read %" PRIu32 " bytes (offset %" PRId64 ")", file->fd,
1328 *actual_size, file->offset);
1329 } while (0);
1330
1331 synthetic_file_read_close(file, TRUE /* (error != NO_ERROR) && (size > 0) */);
1332 return error;
1333}
1334
1335static UINT delegate_file_request_range(wClipboardDelegate* delegate,
1336 const wClipboardFileRangeRequest* request)
1337{
1338 UINT error = 0;
1339 BYTE* data = nullptr;
1340 UINT32 size = 0;
1341 UINT64 offset = 0;
1342 struct synthetic_file* file = nullptr;
1343
1344 if (!delegate || !delegate->clipboard || !request)
1345 return ERROR_BAD_ARGUMENTS;
1346
1347 if (delegate->clipboard->sequenceNumber != delegate->clipboard->fileListSequenceNumber)
1348 return ERROR_INVALID_STATE;
1349
1350 file = ArrayList_GetItem(delegate->clipboard->localFiles, request->listIndex);
1351
1352 if (!file)
1353 return ERROR_INDEX_ABSENT;
1354
1355 offset = (((UINT64)request->nPositionHigh) << 32) | ((UINT64)request->nPositionLow);
1356 error = file_get_range(file, offset, request->cbRequested, &data, &size);
1357
1358 if (error)
1359 error = delegate->ClipboardFileRangeFailure(delegate, request, error);
1360 else
1361 error = delegate->ClipboardFileRangeSuccess(delegate, request, data, size);
1362
1363 if (error)
1364 WLog_WARN(TAG, "failed to report file range result: 0x%08X", error);
1365
1366 free(data);
1367 return NO_ERROR;
1368}
1369
1370static UINT dummy_file_size_success(WINPR_ATTR_UNUSED wClipboardDelegate* delegate,
1371 WINPR_ATTR_UNUSED const wClipboardFileSizeRequest* request,
1372 WINPR_ATTR_UNUSED UINT64 fileSize)
1373{
1374 return ERROR_NOT_SUPPORTED;
1375}
1376
1377static UINT dummy_file_size_failure(WINPR_ATTR_UNUSED wClipboardDelegate* delegate,
1378 WINPR_ATTR_UNUSED const wClipboardFileSizeRequest* request,
1379 WINPR_ATTR_UNUSED UINT errorCode)
1380{
1381 return ERROR_NOT_SUPPORTED;
1382}
1383
1384static UINT dummy_file_range_success(WINPR_ATTR_UNUSED wClipboardDelegate* delegate,
1385 WINPR_ATTR_UNUSED const wClipboardFileRangeRequest* request,
1386 WINPR_ATTR_UNUSED const BYTE* data,
1387 WINPR_ATTR_UNUSED UINT32 size)
1388{
1389 return ERROR_NOT_SUPPORTED;
1390}
1391
1392static UINT dummy_file_range_failure(WINPR_ATTR_UNUSED wClipboardDelegate* delegate,
1393 WINPR_ATTR_UNUSED const wClipboardFileRangeRequest* request,
1394 WINPR_ATTR_UNUSED UINT errorCode)
1395{
1396 return ERROR_NOT_SUPPORTED;
1397}
1398
1399static void setup_delegate(wClipboardDelegate* delegate)
1400{
1401 WINPR_ASSERT(delegate);
1402
1403 delegate->ClientRequestFileSize = delegate_file_request_size;
1404 delegate->ClipboardFileSizeSuccess = dummy_file_size_success;
1405 delegate->ClipboardFileSizeFailure = dummy_file_size_failure;
1406 delegate->ClientRequestFileRange = delegate_file_request_range;
1407 delegate->ClipboardFileRangeSuccess = dummy_file_range_success;
1408 delegate->ClipboardFileRangeFailure = dummy_file_range_failure;
1409 delegate->IsFileNameComponentValid = ValidFileNameComponent;
1410}
1411
1412BOOL ClipboardInitSyntheticFileSubsystem(wClipboard* clipboard)
1413{
1414 if (!clipboard)
1415 return FALSE;
1416
1417 if (!register_file_formats_and_synthesizers(clipboard))
1418 return FALSE;
1419
1420 setup_delegate(&clipboard->delegate);
1421 return TRUE;
1422}
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59