20#include <winpr/config.h>
21#include <winpr/platform.h>
24WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO
25WINPR_PRAGMA_DIAG_IGNORED_UNUSED_MACRO
27#define _FILE_OFFSET_BITS 64
33#include <winpr/wtypes.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>
46#include "synthetic_file.h"
49#define TAG WINPR_TAG("clipboard.synthetic.file")
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";
64 DWORD dwFileAttributes;
72void free_synthetic_file(
struct synthetic_file* file);
74static struct synthetic_file* make_synthetic_file(
const WCHAR* local_name,
const WCHAR* remote_name)
76 struct synthetic_file* file =
nullptr;
78 HANDLE hFind =
nullptr;
80 WINPR_ASSERT(local_name);
81 WINPR_ASSERT(remote_name);
83 hFind = FindFirstFileW(local_name, &fd);
84 if (INVALID_HANDLE_VALUE == hFind)
86 WLog_ERR(TAG,
"FindFirstFile failed (%" PRIu32
")", GetLastError());
91 file = calloc(1,
sizeof(*file));
95 file->fd = INVALID_HANDLE_VALUE;
97 file->local_name = _wcsdup(local_name);
98 if (!file->local_name)
101 file->remote_name = _wcsdup(remote_name);
102 if (!file->remote_name)
106 const size_t len = _wcslen(file->remote_name);
107 if (S_OK != PathCchConvertStyleW(file->remote_name, len, PATH_STYLE_WINDOWS))
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;
120 free_synthetic_file(file);
124static UINT synthetic_file_read_close(
struct synthetic_file* file, BOOL force);
126void free_synthetic_file(
struct synthetic_file* file)
131 synthetic_file_read_close(file, TRUE);
133 free(file->local_name);
134 free(file->remote_name);
142static WCHAR* convert_local_name_component_to_remote(wClipboard* clipboard,
const WCHAR* local_name)
144 wClipboardDelegate* delegate = ClipboardGetDelegate(clipboard);
145 WCHAR* remote_name =
nullptr;
147 WINPR_ASSERT(delegate);
149 remote_name = _wcsdup(local_name);
160 if (!delegate->IsFileNameComponentValid(remote_name))
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);
174static WCHAR* concat_file_name(
const WCHAR* dir,
const WCHAR* file)
178 const WCHAR slash =
'/';
179 WCHAR* buffer =
nullptr;
184 len_dir = _wcslen(dir);
185 len_file = _wcslen(file);
186 buffer = calloc(len_dir + 1 + len_file + 2,
sizeof(WCHAR));
191 memcpy(buffer, dir, len_dir *
sizeof(WCHAR));
192 buffer[len_dir] = slash;
193 memcpy(buffer + len_dir + 1, file, len_file *
sizeof(WCHAR));
197static BOOL add_file_to_list(wClipboard* clipboard,
size_t recursionDepth,
const WCHAR* local_name,
198 const WCHAR* remote_name, wArrayList* files);
200static BOOL add_directory_entry_to_list(wClipboard* clipboard,
size_t recursionDepth,
201 const WCHAR* local_dir_name,
const WCHAR* remote_dir_name,
205 WCHAR* local_name =
nullptr;
206 WCHAR* remote_name =
nullptr;
207 WCHAR* remote_base_name =
nullptr;
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));
214 WINPR_ASSERT(clipboard);
215 WINPR_ASSERT(local_dir_name);
216 WINPR_ASSERT(remote_dir_name);
217 WINPR_ASSERT(pFileData);
222 if ((_wcscmp(pFileData->cFileName, dot) == 0) || (_wcscmp(pFileData->cFileName, dotdot) == 0))
225 remote_base_name = convert_local_name_component_to_remote(clipboard, pFileData->cFileName);
227 if (!remote_base_name)
230 local_name = concat_file_name(local_dir_name, pFileData->cFileName);
231 remote_name = concat_file_name(remote_dir_name, remote_base_name);
233 if (local_name && remote_name)
234 result = add_file_to_list(clipboard, recursionDepth, local_name, remote_name, files);
236 free(remote_base_name);
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)
246 WINPR_ASSERT(clipboard);
247 WINPR_ASSERT(local_name);
248 WINPR_ASSERT(remote_name);
250 WINPR_ASSERT(namebuf);
253 HANDLE hFind = FindFirstFileW(namebuf, &FindData);
254 if (INVALID_HANDLE_VALUE == hFind)
256 WLog_ERR(TAG,
"FindFirstFile failed (%" PRIu32
")", GetLastError());
261 if (!add_directory_entry_to_list(clipboard, recursionDepth, local_name, remote_name,
268 BOOL bRet = FindNextFileW(hFind, &FindData);
272 if (ERROR_NO_MORE_FILES == GetLastError())
274 WLog_WARN(TAG,
"FindNextFile failed (%" PRIu32
")", GetLastError());
282static BOOL add_directory_contents_to_list(wClipboard* clipboard,
size_t recursionDepth,
283 const WCHAR* local_name,
const WCHAR* remote_name,
292 const char buffer[6] = {
'/',
'\0',
'*',
'\0',
'\0',
'\0' };
294 const size_t wildcardLen = ARRAYSIZE(buffer) /
sizeof(WCHAR);
296 WINPR_ASSERT(clipboard);
297 WINPR_ASSERT(local_name);
298 WINPR_ASSERT(remote_name);
301 const size_t max = CLIPBOARD_FILE_MAX_RECURSION;
302 if (recursionDepth > max)
305 "This build only supports recursionDepth=%" PRIuz
306 " <= CLIPBOARD_FILE_MAX_RECURSION=%" PRIuz
" recursion depth for folders copied.",
307 recursionDepth, max);
311 size_t len = _wcslen(local_name);
312 WCHAR* namebuf = calloc(len + wildcardLen,
sizeof(WCHAR));
316 _wcsncat(namebuf, local_name, len);
317 _wcsncat(namebuf, wildcard.w, wildcardLen);
319 result = do_add_directory_contents_to_list(clipboard, recursionDepth, local_name, remote_name,
326BOOL add_file_to_list(wClipboard* clipboard,
size_t recursionDepth,
const WCHAR* local_name,
327 const WCHAR* remote_name, wArrayList* files)
329 struct synthetic_file* file =
nullptr;
331 WINPR_ASSERT(clipboard);
332 WINPR_ASSERT(local_name);
333 WINPR_ASSERT(remote_name);
336 file = make_synthetic_file(local_name, remote_name);
341 if (!ArrayList_Append(files, file))
343 free_synthetic_file(file);
347 if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
353 if (!add_directory_contents_to_list(clipboard, recursionDepth + 1, local_name, remote_name,
361static const WCHAR* get_basename(
const WCHAR* name)
363 const WCHAR* c = name;
364 const WCHAR* last_name = name;
365 const WCHAR slash =
'/';
378static BOOL process_file_name(wClipboard* clipboard,
const WCHAR* local_name, wArrayList* files)
381 const WCHAR* base_name =
nullptr;
382 WCHAR* remote_name =
nullptr;
384 WINPR_ASSERT(clipboard);
385 WINPR_ASSERT(local_name);
393 base_name = get_basename(local_name);
394 remote_name = convert_local_name_component_to_remote(clipboard, base_name);
399 result = add_file_to_list(clipboard, 0, local_name, remote_name, files);
404static BOOL process_uri(wClipboard* clipboard,
const char* uri,
size_t uri_len)
408 char* name =
nullptr;
410 WINPR_ASSERT(clipboard);
412 name = parse_uri_to_local_file(uri, uri_len);
415 WCHAR* wname =
nullptr;
423 wname = ConvertUtf8ToWCharAlloc(name,
nullptr);
425 result = process_file_name(clipboard, wname, clipboard->localFiles);
434static BOOL process_uri_list(wClipboard* clipboard,
const char* data,
size_t length)
436 const char* cur = data;
437 const char* lim = data + length;
439 WINPR_ASSERT(clipboard);
442 WLog_VRB(TAG,
"processing URI list:\n%.*s", WINPR_ASSERTING_INT_CAST(
int, length), data);
443 ArrayList_Clear(clipboard->localFiles);
455 BOOL comment = (*cur ==
'#');
456 const char* start = cur;
457 const char* stop = cur;
459 for (; stop < lim; stop++)
463 if ((stop + 1 < lim) && (*(stop + 1) ==
'\n'))
480 if (strnlen(start, WINPR_ASSERTING_INT_CAST(
size_t, stop - start)) < 1)
488 if (!process_uri(clipboard, start, WINPR_ASSERTING_INT_CAST(
size_t, stop - start)))
495static BOOL convert_local_file_to_filedescriptor(
const struct synthetic_file* file,
498 size_t remote_len = 0;
501 WINPR_ASSERT(descriptor);
503 descriptor->dwFlags = FD_ATTRIBUTES | FD_FILESIZE | FD_WRITESTIME | FD_PROGRESSUI;
505 if (file->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
507 descriptor->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
508 descriptor->nFileSizeLow = 0;
509 descriptor->nFileSizeHigh = 0;
513 descriptor->dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
514 descriptor->nFileSizeLow = file->nFileSizeLow;
515 descriptor->nFileSizeHigh = file->nFileSizeHigh;
518 descriptor->ftLastWriteTime = file->ftLastWriteTime;
520 remote_len = _wcsnlen(file->remote_name, ARRAYSIZE(descriptor->cFileName));
522 if (remote_len >= ARRAYSIZE(descriptor->cFileName))
524 WLog_ERR(TAG,
"file name too long (%" PRIuz
" characters)", remote_len);
528 memcpy(descriptor->cFileName, file->remote_name, remote_len *
sizeof(WCHAR));
532static FILEDESCRIPTORW* convert_local_file_list_to_filedescriptors(wArrayList* files)
537 count = ArrayList_Count(files);
544 for (
size_t i = 0; i < count; i++)
546 const struct synthetic_file* file = ArrayList_GetItem(files, i);
548 if (!convert_local_file_to_filedescriptor(file, &descriptors[i]))
558static void* convert_any_uri_list_to_filedescriptors(wClipboard* clipboard,
559 WINPR_ATTR_UNUSED UINT32 formatId,
564 WINPR_ASSERT(clipboard);
567 descriptors = convert_local_file_list_to_filedescriptors(clipboard->localFiles);
572 *pSize = (UINT32)ArrayList_Count(clipboard->localFiles) *
sizeof(
FILEDESCRIPTORW);
573 clipboard->fileListSequenceNumber = clipboard->sequenceNumber;
577static void* convert_uri_list_to_filedescriptors(wClipboard* clipboard, UINT32 formatId,
578 const void* data, UINT32* pSize)
580 if (formatId != ClipboardGetFormatId(clipboard, mime_FileGroupDescriptorW))
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);
589 if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_uri_list))
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);
598 if (!process_uri_list(clipboard, (
const char*)data, *pSize))
600 return convert_any_uri_list_to_filedescriptors(clipboard, clipboard->formatId, pSize);
603static BOOL process_files(wClipboard* clipboard,
const char* data, UINT32 pSize,
const char* prefix)
605 WINPR_ASSERT(prefix);
607 const size_t prefix_len = strlen(prefix);
609 WINPR_ASSERT(clipboard);
611 ArrayList_Clear(clipboard->localFiles);
613 if (!data || (pSize < prefix_len))
615 if (strncmp(data, prefix, prefix_len) != 0)
618 if (pSize < prefix_len)
620 pSize -= WINPR_ASSERTING_INT_CAST(uint32_t, prefix_len);
623 char* copy = strndup(data, pSize);
628 char* endptr =
nullptr;
629 char* tok = strtok_s(copy,
"\n", &endptr);
632 const size_t tok_len = strnlen(tok, pSize);
633 if (!process_uri(clipboard, tok, tok_len))
637 pSize -= WINPR_ASSERTING_INT_CAST(uint32_t, tok_len);
638 tok = strtok_s(
nullptr,
"\n", &endptr);
649static BOOL process_gnome_copied_files(wClipboard* clipboard,
const char* data, UINT32 pSize)
651 return process_files(clipboard, data, pSize,
"copy\n");
654static BOOL process_mate_copied_files(wClipboard* clipboard,
const char* data, UINT32 pSize)
656 return process_files(clipboard, data, pSize,
"copy\n");
659static void* convert_gnome_copied_files_to_filedescriptors(wClipboard* clipboard, UINT32 formatId,
660 const void* data, UINT32* pSize)
662 if (formatId != ClipboardGetFormatId(clipboard, mime_FileGroupDescriptorW))
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);
671 if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_gnome_copied_files))
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);
680 if (!process_gnome_copied_files(clipboard, (
const char*)data, *pSize))
682 return convert_any_uri_list_to_filedescriptors(clipboard, clipboard->formatId, pSize);
685static void* convert_mate_copied_files_to_filedescriptors(wClipboard* clipboard, UINT32 formatId,
686 const void* data, UINT32* pSize)
688 if (formatId != ClipboardGetFormatId(clipboard, mime_FileGroupDescriptorW))
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);
697 if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_mate_copied_files))
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);
707 if (!process_mate_copied_files(clipboard, (
const char*)data, *pSize))
710 return convert_any_uri_list_to_filedescriptors(clipboard, formatId, pSize);
714static size_t count_special_chars(
const WCHAR* str,
size_t charLen)
717 const WCHAR* start = str;
718 const WCHAR* end = &str[charLen];
721 while ((start < end) && (*start))
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(
'%');
729 if ((*start == sharp) || (*start == questionmark) || (*start == star) ||
730 (*start == exclamationmark) || (*start == percent))
739static const char* stop_at_special_chars(
const char* str)
741 const char* start = str;
746 if (*start ==
'#' || *start ==
'?' || *start ==
'*' || *start ==
'!' || *start ==
'%')
763 const size_t len = _wcsnlen(dsc->cFileName, ARRAYSIZE(dsc->cFileName));
764 const WCHAR* found = winpr_wcsnchr(dsc->cFileName, len, winpr_wchar_ToLE(
'\\'));
766 const DWORD level = WLOG_TRACE;
767 if (WLog_IsLevelActive(log, level))
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");
775 return found !=
nullptr;
782 ft.dwLowDateTime = Stream_Get_UINT32(s);
783 ft.dwHighDateTime = Stream_Get_UINT32(s);
792 if (!Stream_CheckAndLogRequiredLength(TAG, s,
sizeof(
FILEDESCRIPTORW)))
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));
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)
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);
825 if (!clipboard || !data || !pSize)
828 if (*pSize <
sizeof(UINT32))
831 if (clipboard->delegate.basePath)
832 baseLength = strnlen(clipboard->delegate.basePath, MAX_PATH);
837 wStream sbuffer = WINPR_C_ARRAY_INIT;
838 wStream* s = Stream_StaticConstInit(&sbuffer, data, *pSize);
839 if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
842 Stream_Read_UINT32(s, nrDescriptors);
846 if ((count < 1) || (count != nrDescriptors))
851 if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_FileGroupDescriptorW))
855 const size_t decoration_len = lineprefix_len + lineending_len + baseLength + 1;
856 size_t alloc = header_len;
858 const size_t descriptorStartPos = Stream_GetPosition(s);
860 for (
size_t x = 0; x < count; x++)
863 if (!read_descriptor(s, &dsc))
866 if (!descriptor_is_nested(log, &dsc))
868 alloc += ARRAYSIZE(dsc.cFileName) *
871 alloc += count_special_chars(dsc.cFileName, ARRAYSIZE(dsc.cFileName)) *
sizeof(WCHAR);
872 alloc += decoration_len;
876 if (!Stream_SetPosition(s, descriptorStartPos))
882 char* dst = calloc(alloc,
sizeof(
char));
887 (void)_snprintf(&dst[0], alloc,
"%s", header);
889 size_t pos = header_len;
891 for (
size_t x = 0; x < count; x++)
896 if (!read_descriptor(s, &dsc))
899 if (descriptor_is_nested(log, &dsc))
902 char curName[520] = WINPR_C_ARRAY_INIT;
903 const char* stop_at =
nullptr;
904 const char* previous_at =
nullptr;
906 if (ConvertWCharNToUtf8(dsc.cFileName, ARRAYSIZE(dsc.cFileName), curName,
907 ARRAYSIZE(curName)) < 0)
910 HRESULT hr = PathCchConvertStyleA(curName, ARRAYSIZE(curName), PATH_STYLE_UNIX);
914 rc = _snprintf(&dst[pos], alloc - pos,
"%s%s/", lineprefix, clipboard->delegate.basePath);
921 previous_at = curName;
922 while ((stop_at = stop_at_special_chars(previous_at)) !=
nullptr)
924 const intptr_t diff = stop_at - previous_at;
927 char* tmp = strndup(previous_at, WINPR_ASSERTING_INT_CAST(
size_t, diff));
931 rc = _snprintf(&dst[pos], WINPR_ASSERTING_INT_CAST(
size_t, diff + 1),
"%s", tmp);
937 rc = _snprintf(&dst[pos], 4,
"%%%x", *stop_at);
942 previous_at = stop_at + 1;
945 rc = _snprintf(&dst[pos], alloc - pos,
"%s%s", previous_at, lineending);
949 if ((rc < 0) || fail)
958 if (skip_last_lineending)
960 const size_t endlen = strlen(lineending);
963 const size_t len = strnlen(dst, alloc);
970 if (memcmp(&dst[len - endlen], lineending, endlen) == 0)
972 memset(&dst[len - endlen], 0, endlen);
978 alloc = strnlen(dst, alloc) + 1;
979 *pSize = (UINT32)alloc;
980 clipboard->fileListSequenceNumber = clipboard->sequenceNumber;
990WINPR_ATTR_MALLOC(free, 1)
991static
void* convert_filedescriptors_to_uri_list(wClipboard* clipboard, UINT32 formatId,
992 const
void* data, UINT32* pSize)
994 if (formatId != ClipboardGetFormatId(clipboard, mime_uri_list))
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);
1003 if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_FileGroupDescriptorW))
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);
1012 return convert_filedescriptors_to_file_list(clipboard, data, pSize,
"",
"file://",
"\r\n",
1017WINPR_ATTR_MALLOC(free, 1)
1018static
void* convert_filedescriptors_to_gnome_copied_files(wClipboard* clipboard, UINT32 formatId,
1019 const
void* data, UINT32* pSize)
1021 if (formatId != ClipboardGetFormatId(clipboard, mime_gnome_copied_files))
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);
1030 if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_FileGroupDescriptorW))
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);
1039 return convert_filedescriptors_to_file_list(clipboard, data, pSize,
"copy\n",
"file://",
"\n",
1043WINPR_ATTR_MALLOC(free, 1)
1044static
void* convert_filedescriptors_to_mate_copied_files(wClipboard* clipboard, UINT32 formatId,
1045 const
void* data, UINT32* pSize)
1047 if (formatId != ClipboardGetFormatId(clipboard, mime_mate_copied_files))
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);
1056 if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_FileGroupDescriptorW))
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);
1065 char* pDstData = convert_filedescriptors_to_file_list(clipboard, data, pSize,
"copy\n",
1066 "file://",
"\n", TRUE);
1076 pDstData[*pSize - 1] =
'\0';
1077 *pSize = *pSize - 1;
1081static void array_free_synthetic_file(
void* the_file)
1083 struct synthetic_file* file = the_file;
1084 free_synthetic_file(file);
1087static BOOL register_file_formats_and_synthesizers(wClipboard* clipboard)
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);
1120 if (!file_group_format_id || !local_file_format_id || !local_gnome_file_format_id ||
1121 !local_mate_file_format_id)
1124 clipboard->localFiles = ArrayList_New(FALSE);
1126 if (!clipboard->localFiles)
1129 obj = ArrayList_Object(clipboard->localFiles);
1132 if (!ClipboardRegisterSynthesizerEx(clipboard, local_file_format_id, file_group_format_id,
1133 convert_uri_list_to_filedescriptors))
1134 goto error_free_local_files;
1136 if (!ClipboardRegisterSynthesizerEx(clipboard, file_group_format_id, local_file_format_id,
1137 convert_filedescriptors_to_uri_list))
1138 goto error_free_local_files;
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;
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;
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;
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;
1157error_free_local_files:
1158 ArrayList_Free(clipboard->localFiles);
1159 clipboard->localFiles =
nullptr;
1164static int32_t file_get_size(
const struct synthetic_file* file, UINT64* size)
1169 return E_INVALIDARG;
1171 s = file->nFileSizeHigh;
1173 s |= file->nFileSizeLow;
1178static UINT delegate_file_request_size(wClipboardDelegate* delegate,
1183 if (!delegate || !delegate->clipboard || !request)
1184 return ERROR_BAD_ARGUMENTS;
1186 if (delegate->clipboard->sequenceNumber != delegate->clipboard->fileListSequenceNumber)
1187 return ERROR_INVALID_STATE;
1189 struct synthetic_file* file =
1190 ArrayList_GetItem(delegate->clipboard->localFiles, request->listIndex);
1193 return ERROR_INDEX_ABSENT;
1195 const int32_t s = file_get_size(file, &size);
1198 error = delegate->ClipboardFileSizeFailure(delegate, request, (UINT)s);
1200 error = delegate->ClipboardFileSizeSuccess(delegate, request, size);
1203 WLog_WARN(TAG,
"failed to report file size result: 0x%08X", error);
1208UINT synthetic_file_read_close(
struct synthetic_file* file, BOOL force)
1210 if (!file || INVALID_HANDLE_VALUE == file->fd)
1216 file_get_size(file, &size);
1217 if ((file->offset < 0) || ((UINT64)file->offset >= size) || force)
1219 WLog_VRB(TAG,
"close file %p", file->fd);
1220 if (!CloseHandle(file->fd))
1222 WLog_WARN(TAG,
"failed to close fd %p: %" PRIu32, file->fd, GetLastError());
1225 file->fd = INVALID_HANDLE_VALUE;
1231static UINT file_get_range(
struct synthetic_file* file, UINT64 offset, UINT32 size,
1232 BYTE** actual_data, UINT32* actual_size)
1234 UINT error = NO_ERROR;
1239 WINPR_ASSERT(actual_data);
1240 WINPR_ASSERT(actual_size);
1242 if (INVALID_HANDLE_VALUE == file->fd)
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)
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);
1257 if (!GetFileInformationByHandle(file->fd, &FileInfo))
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);
1269 file->nFileSizeHigh = FileInfo.nFileSizeHigh;
1270 file->nFileSizeLow = FileInfo.nFileSizeLow;
1289 if (offset > INT64_MAX)
1291 WLog_ERR(TAG,
"offset [%" PRIu64
"] > INT64_MAX", offset);
1296 if (file->offset != (INT64)offset)
1298 WLog_DBG(TAG,
"file %p force seeking to %" PRIu64
", current %" PRId64, file->fd,
1299 offset, file->offset);
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))
1307 error = GetLastError();
1312 BYTE* buffer = malloc(size);
1315 error = ERROR_NOT_ENOUGH_MEMORY;
1318 if (!ReadFile(file->fd, buffer, size, (LPDWORD)actual_size,
nullptr))
1321 error = GetLastError();
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);
1331 synthetic_file_read_close(file, TRUE );
1335static UINT delegate_file_request_range(wClipboardDelegate* delegate,
1339 BYTE* data =
nullptr;
1342 struct synthetic_file* file =
nullptr;
1344 if (!delegate || !delegate->clipboard || !request)
1345 return ERROR_BAD_ARGUMENTS;
1347 if (delegate->clipboard->sequenceNumber != delegate->clipboard->fileListSequenceNumber)
1348 return ERROR_INVALID_STATE;
1350 file = ArrayList_GetItem(delegate->clipboard->localFiles, request->listIndex);
1353 return ERROR_INDEX_ABSENT;
1355 offset = (((UINT64)request->nPositionHigh) << 32) | ((UINT64)request->nPositionLow);
1356 error = file_get_range(file, offset, request->cbRequested, &data, &size);
1359 error = delegate->ClipboardFileRangeFailure(delegate, request, error);
1361 error = delegate->ClipboardFileRangeSuccess(delegate, request, data, size);
1364 WLog_WARN(TAG,
"failed to report file range result: 0x%08X", error);
1370static UINT dummy_file_size_success(WINPR_ATTR_UNUSED wClipboardDelegate* delegate,
1372 WINPR_ATTR_UNUSED UINT64 fileSize)
1374 return ERROR_NOT_SUPPORTED;
1377static UINT dummy_file_size_failure(WINPR_ATTR_UNUSED wClipboardDelegate* delegate,
1379 WINPR_ATTR_UNUSED UINT errorCode)
1381 return ERROR_NOT_SUPPORTED;
1384static UINT dummy_file_range_success(WINPR_ATTR_UNUSED wClipboardDelegate* delegate,
1386 WINPR_ATTR_UNUSED
const BYTE* data,
1387 WINPR_ATTR_UNUSED UINT32 size)
1389 return ERROR_NOT_SUPPORTED;
1392static UINT dummy_file_range_failure(WINPR_ATTR_UNUSED wClipboardDelegate* delegate,
1394 WINPR_ATTR_UNUSED UINT errorCode)
1396 return ERROR_NOT_SUPPORTED;
1399static void setup_delegate(wClipboardDelegate* delegate)
1401 WINPR_ASSERT(delegate);
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;
1412BOOL ClipboardInitSyntheticFileSubsystem(wClipboard* clipboard)
1417 if (!register_file_formats_and_synthesizers(clipboard))
1420 setup_delegate(&clipboard->delegate);
This struct contains function pointer to initialize/free objects.
OBJECT_FREE_FN fnObjectFree