Coverage Report

Created: 2026-07-16 07:09

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