Coverage Report

Created: 2026-10-15 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/clipboard/clipboard.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Clipboard Functions
4
 *
5
 * Copyright 2014 Marc-Andre Moreau <marcandre.moreau@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
22
#include <winpr/crt.h>
23
#include <winpr/collections.h>
24
#include <winpr/wlog.h>
25
26
#include <winpr/clipboard.h>
27
28
#include "clipboard.h"
29
30
#include "synthetic_file.h"
31
32
#include "../log.h"
33
#define TAG WINPR_TAG("clipboard")
34
35
const char* const mime_text_plain = "text/plain";
36
const char* const mime_text_utf8 = "text/plain;charset=utf-8";
37
const char* const mime_text_UTF8_STRING = "UTF8_STRING";
38
39
/**
40
 * Clipboard (Windows):
41
 * msdn.microsoft.com/en-us/library/windows/desktop/ms648709/
42
 *
43
 * W3C Clipboard API and events:
44
 * http://www.w3.org/TR/clipboard-apis/
45
 */
46
47
static const char* CF_STANDARD_STRINGS[] = {
48
  "CF_RAW",          /* 0 */
49
  "CF_TEXT",         /* 1 */
50
  "CF_BITMAP",       /* 2 */
51
  "CF_METAFILEPICT", /* 3 */
52
  "CF_SYLK",         /* 4 */
53
  "CF_DIF",          /* 5 */
54
  "CF_TIFF",         /* 6 */
55
  "CF_OEMTEXT",      /* 7 */
56
  "CF_DIB",          /* 8 */
57
  "CF_PALETTE",      /* 9 */
58
  "CF_PENDATA",      /* 10 */
59
  "CF_RIFF",         /* 11 */
60
  "CF_WAVE",         /* 12 */
61
  "CF_UNICODETEXT",  /* 13 */
62
  "CF_ENHMETAFILE",  /* 14 */
63
  "CF_HDROP",        /* 15 */
64
  "CF_LOCALE",       /* 16 */
65
  "CF_DIBV5"         /* 17 */
66
};
67
68
const char* ClipboardGetFormatIdString(UINT32 formatId)
69
0
{
70
0
  if (formatId < ARRAYSIZE(CF_STANDARD_STRINGS))
71
0
    return CF_STANDARD_STRINGS[formatId];
72
0
  return "CF_REGISTERED_FORMAT";
73
0
}
74
75
static wClipboardFormat* ClipboardFindFormat(wClipboard* clipboard, UINT32 formatId,
76
                                             const char* name)
77
936k
{
78
936k
  wClipboardFormat* format = nullptr;
79
80
936k
  if (!clipboard)
81
0
    return nullptr;
82
83
936k
  if (formatId)
84
792k
  {
85
14.7M
    for (UINT32 index = 0; index < clipboard->numFormats; index++)
86
14.7M
    {
87
14.7M
      wClipboardFormat* cformat = &clipboard->formats[index];
88
14.7M
      if (formatId == cformat->formatId)
89
792k
      {
90
792k
        format = cformat;
91
792k
        break;
92
792k
      }
93
14.7M
    }
94
792k
  }
95
144k
  else if (name)
96
144k
  {
97
3.59M
    for (UINT32 index = 0; index < clipboard->numFormats; index++)
98
3.55M
    {
99
3.55M
      wClipboardFormat* cformat = &clipboard->formats[index];
100
3.55M
      if (!cformat->formatName)
101
0
        continue;
102
103
3.55M
      if (strcmp(name, cformat->formatName) == 0)
104
97.2k
      {
105
97.2k
        format = cformat;
106
97.2k
        break;
107
97.2k
      }
108
3.55M
    }
109
144k
  }
110
0
  else
111
0
  {
112
    /* special "CF_RAW" case */
113
0
    if (clipboard->numFormats > 0)
114
0
    {
115
0
      format = &clipboard->formats[0];
116
117
0
      if (format->formatId)
118
0
        return nullptr;
119
120
0
      if (!format->formatName || (strcmp(format->formatName, CF_STANDARD_STRINGS[0]) == 0))
121
0
        return format;
122
0
    }
123
0
  }
124
125
936k
  return format;
126
936k
}
127
128
static wClipboardSynthesizer* ClipboardFindSynthesizer(wClipboardFormat* format, UINT32 formatId)
129
262k
{
130
262k
  if (!format)
131
0
    return nullptr;
132
133
793k
  for (UINT32 index = 0; index < format->numSynthesizers; index++)
134
543k
  {
135
543k
    wClipboardSynthesizer* synthesizer = &(format->synthesizers[index]);
136
137
543k
    if (formatId == synthesizer->syntheticId)
138
12.9k
      return synthesizer;
139
543k
  }
140
141
249k
  return nullptr;
142
262k
}
143
144
void ClipboardLock(wClipboard* clipboard)
145
0
{
146
0
  if (!clipboard)
147
0
    return;
148
149
0
  EnterCriticalSection(&(clipboard->lock));
150
0
}
151
152
void ClipboardUnlock(wClipboard* clipboard)
153
0
{
154
0
  if (!clipboard)
155
0
    return;
156
157
0
  LeaveCriticalSection(&(clipboard->lock));
158
0
}
159
160
BOOL ClipboardEmpty(wClipboard* clipboard)
161
0
{
162
0
  if (!clipboard)
163
0
    return FALSE;
164
165
0
  if (clipboard->data)
166
0
  {
167
0
    free(clipboard->data);
168
0
    clipboard->data = nullptr;
169
0
  }
170
171
0
  clipboard->size = 0;
172
0
  clipboard->formatId = 0;
173
0
  clipboard->sequenceNumber++;
174
0
  return TRUE;
175
0
}
176
177
UINT32 ClipboardCountRegisteredFormats(wClipboard* clipboard)
178
0
{
179
0
  if (!clipboard)
180
0
    return 0;
181
182
0
  return clipboard->numFormats;
183
0
}
184
185
UINT32 ClipboardGetRegisteredFormatIds(wClipboard* clipboard, UINT32** ppFormatIds)
186
0
{
187
0
  UINT32* pFormatIds = nullptr;
188
0
  wClipboardFormat* format = nullptr;
189
190
0
  if (!clipboard)
191
0
    return 0;
192
193
0
  if (!ppFormatIds)
194
0
    return 0;
195
196
0
  pFormatIds = *ppFormatIds;
197
198
0
  if (!pFormatIds)
199
0
  {
200
0
    pFormatIds = calloc(clipboard->numFormats, sizeof(UINT32));
201
202
0
    if (!pFormatIds)
203
0
      return 0;
204
205
0
    *ppFormatIds = pFormatIds;
206
0
  }
207
208
0
  for (UINT32 index = 0; index < clipboard->numFormats; index++)
209
0
  {
210
0
    format = &(clipboard->formats[index]);
211
0
    pFormatIds[index] = format->formatId;
212
0
  }
213
214
0
  return clipboard->numFormats;
215
0
}
216
217
UINT32 ClipboardRegisterFormat(wClipboard* clipboard, const char* name)
218
131k
{
219
131k
  wClipboardFormat* format = nullptr;
220
221
131k
  if (!clipboard)
222
0
    return 0;
223
224
131k
  format = ClipboardFindFormat(clipboard, 0, name);
225
226
131k
  if (format)
227
84.2k
    return format->formatId;
228
229
47.2k
  if ((clipboard->numFormats + 1) >= clipboard->maxFormats)
230
0
  {
231
0
    UINT32 numFormats = clipboard->maxFormats * 2;
232
0
    wClipboardFormat* tmpFormat = nullptr;
233
0
    tmpFormat =
234
0
        (wClipboardFormat*)realloc(clipboard->formats, numFormats * sizeof(wClipboardFormat));
235
236
0
    if (!tmpFormat)
237
0
      return 0;
238
239
0
    clipboard->formats = tmpFormat;
240
0
    clipboard->maxFormats = numFormats;
241
0
  }
242
243
47.2k
  format = &(clipboard->formats[clipboard->numFormats]);
244
47.2k
  ZeroMemory(format, sizeof(wClipboardFormat));
245
246
47.2k
  if (name)
247
47.2k
  {
248
47.2k
    format->formatName = _strdup(name);
249
250
47.2k
    if (!format->formatName)
251
0
      return 0;
252
47.2k
  }
253
254
47.2k
  format->formatId = clipboard->nextFormatId++;
255
47.2k
  clipboard->numFormats++;
256
47.2k
  return format->formatId;
257
47.2k
}
258
259
WINPR_ATTR_NODISCARD
260
static wClipboardSynthesizer* registerSynthesizer(wClipboard* clipboard, UINT32 formatId,
261
                                                  UINT32 syntheticId)
262
249k
{
263
249k
  if (!clipboard)
264
0
  {
265
0
    WLog_WARN(TAG,
266
0
              "Failed to register synthesizer for %s[0x%04" PRIx32 "] to %s [0x%04" PRIx32
267
0
              "]: clipboard=NULL",
268
0
              ClipboardGetFormatName(clipboard, formatId), formatId,
269
0
              ClipboardGetFormatName(clipboard, syntheticId), syntheticId);
270
0
    return nullptr;
271
0
  }
272
273
249k
  WLog_DBG(TAG, "Registering synthesizer for %s[0x%04" PRIx32 "] to %s [0x%04" PRIx32 "]",
274
249k
           ClipboardGetFormatName(clipboard, formatId), formatId,
275
249k
           ClipboardGetFormatName(clipboard, syntheticId), syntheticId);
276
277
249k
  wClipboardFormat* format = ClipboardFindFormat(clipboard, formatId, nullptr);
278
279
249k
  if (!format)
280
0
  {
281
0
    WLog_WARN(TAG,
282
0
              "Failed to register synthesizer for %s[0x%04" PRIx32 "] to %s [0x%04" PRIx32
283
0
              "]: format=NULL",
284
0
              ClipboardGetFormatName(clipboard, formatId), formatId,
285
0
              ClipboardGetFormatName(clipboard, syntheticId), syntheticId);
286
0
    return nullptr;
287
0
  }
288
289
249k
  if (format->formatId == syntheticId)
290
0
  {
291
0
    WLog_WARN(TAG,
292
0
              "Failed to register synthesizer for %s[0x%04" PRIx32 "] to %s [0x%04" PRIx32
293
0
              "]: formats equal",
294
0
              ClipboardGetFormatName(clipboard, formatId), formatId,
295
0
              ClipboardGetFormatName(clipboard, syntheticId), syntheticId);
296
0
    return nullptr;
297
0
  }
298
299
249k
  wClipboardSynthesizer* synthesizer = ClipboardFindSynthesizer(format, formatId);
300
301
249k
  if (!synthesizer)
302
249k
  {
303
249k
    UINT32 numSynthesizers = format->numSynthesizers + 1;
304
249k
    wClipboardSynthesizer* tmpSynthesizer = (wClipboardSynthesizer*)realloc(
305
249k
        format->synthesizers, numSynthesizers * sizeof(wClipboardSynthesizer));
306
307
249k
    if (!tmpSynthesizer)
308
0
    {
309
0
      WLog_WARN(TAG,
310
0
                "Failed to register synthesizer for %s[0x%04" PRIx32 "] to %s [0x%04" PRIx32
311
0
                "]: failed to allocate new synthesizer",
312
0
                ClipboardGetFormatName(clipboard, formatId), formatId,
313
0
                ClipboardGetFormatName(clipboard, syntheticId), syntheticId);
314
0
      return nullptr;
315
0
    }
316
317
249k
    format->synthesizers = tmpSynthesizer;
318
249k
    format->numSynthesizers = numSynthesizers;
319
249k
    const UINT32 index = numSynthesizers - 1;
320
249k
    synthesizer = &(format->synthesizers[index]);
321
249k
  }
322
249k
  return synthesizer;
323
249k
}
324
325
BOOL ClipboardRegisterSynthesizer(wClipboard* clipboard, UINT32 formatId, UINT32 syntheticId,
326
                                  CLIPBOARD_SYNTHESIZE_FN pfnSynthesize)
327
0
{
328
0
  wClipboardSynthesizer* synthesizer = registerSynthesizer(clipboard, formatId, syntheticId);
329
0
  if (!synthesizer)
330
0
    return FALSE;
331
332
0
  synthesizer->syntheticId = syntheticId;
333
0
  synthesizer->pfnSynthesize = pfnSynthesize;
334
0
  return TRUE;
335
0
}
336
337
BOOL ClipboardRegisterSynthesizerEx(wClipboard* clipboard, UINT32 formatId, UINT32 syntheticId,
338
                                    CLIPBOARD_SYNTHESIZE_FN pfnSynthesize)
339
249k
{
340
249k
  wClipboardSynthesizer* synthesizer = registerSynthesizer(clipboard, formatId, syntheticId);
341
249k
  if (!synthesizer)
342
0
    return FALSE;
343
344
249k
  synthesizer->syntheticId = syntheticId;
345
249k
  synthesizer->pfnSynthesizeEx = pfnSynthesize;
346
249k
  return TRUE;
347
249k
}
348
349
UINT32 ClipboardCountFormats(wClipboard* clipboard)
350
0
{
351
0
  UINT32 count = 0;
352
0
  wClipboardFormat* format = nullptr;
353
354
0
  if (!clipboard)
355
0
    return 0;
356
357
0
  format = ClipboardFindFormat(clipboard, clipboard->formatId, nullptr);
358
359
0
  if (!format)
360
0
    return 0;
361
362
0
  count = 1 + format->numSynthesizers;
363
0
  return count;
364
0
}
365
366
UINT32 ClipboardGetFormatIds(wClipboard* clipboard, UINT32** ppFormatIds)
367
3.37k
{
368
3.37k
  UINT32 count = 0;
369
3.37k
  UINT32* pFormatIds = nullptr;
370
3.37k
  wClipboardFormat* format = nullptr;
371
3.37k
  wClipboardSynthesizer* synthesizer = nullptr;
372
373
3.37k
  if (!clipboard)
374
0
    return 0;
375
376
3.37k
  format = ClipboardFindFormat(clipboard, clipboard->formatId, nullptr);
377
378
3.37k
  if (!format)
379
0
    return 0;
380
381
3.37k
  count = 1 + format->numSynthesizers;
382
383
3.37k
  if (!ppFormatIds)
384
0
    return 0;
385
386
3.37k
  pFormatIds = *ppFormatIds;
387
388
3.37k
  if (!pFormatIds)
389
3.37k
  {
390
3.37k
    pFormatIds = calloc(count, sizeof(UINT32));
391
392
3.37k
    if (!pFormatIds)
393
0
      return 0;
394
395
3.37k
    *ppFormatIds = pFormatIds;
396
3.37k
  }
397
398
3.37k
  pFormatIds[0] = format->formatId;
399
400
16.3k
  for (UINT32 index = 1; index < count; index++)
401
12.9k
  {
402
12.9k
    synthesizer = &(format->synthesizers[index - 1]);
403
12.9k
    pFormatIds[index] = synthesizer->syntheticId;
404
12.9k
  }
405
406
3.37k
  return count;
407
3.37k
}
408
409
static void ClipboardUninitFormats(wClipboard* clipboard)
410
3.37k
{
411
3.37k
  WINPR_ASSERT(clipboard);
412
111k
  for (UINT32 formatId = 0; formatId < clipboard->numFormats; formatId++)
413
107k
  {
414
107k
    wClipboardFormat* format = &clipboard->formats[formatId];
415
107k
    free(format->formatName);
416
107k
    free(format->synthesizers);
417
107k
    format->formatName = nullptr;
418
107k
    format->synthesizers = nullptr;
419
107k
  }
420
3.37k
}
421
422
static BOOL ClipboardInitFormats(wClipboard* clipboard)
423
3.37k
{
424
3.37k
  UINT32 formatId = 0;
425
3.37k
  wClipboardFormat* format = nullptr;
426
427
3.37k
  if (!clipboard)
428
0
    return FALSE;
429
430
64.0k
  for (formatId = 0; formatId < CF_MAX; formatId++, clipboard->numFormats++)
431
60.7k
  {
432
60.7k
    format = &(clipboard->formats[clipboard->numFormats]);
433
60.7k
    ZeroMemory(format, sizeof(wClipboardFormat));
434
60.7k
    format->formatId = formatId;
435
60.7k
    format->formatName = _strdup(CF_STANDARD_STRINGS[formatId]);
436
437
60.7k
    if (!format->formatName)
438
0
      goto error;
439
60.7k
  }
440
441
3.37k
  if (!ClipboardInitSynthesizers(clipboard))
442
0
    goto error;
443
444
3.37k
  return TRUE;
445
0
error:
446
447
0
  ClipboardUninitFormats(clipboard);
448
0
  return FALSE;
449
3.37k
}
450
451
UINT32 ClipboardGetFormatId(wClipboard* clipboard, const char* name)
452
13.0k
{
453
13.0k
  wClipboardFormat* format = nullptr;
454
455
13.0k
  if (!clipboard)
456
0
    return 0;
457
458
13.0k
  format = ClipboardFindFormat(clipboard, 0, name);
459
460
13.0k
  if (!format)
461
0
    return 0;
462
463
13.0k
  return format->formatId;
464
13.0k
}
465
466
const char* ClipboardGetFormatName(wClipboard* clipboard, UINT32 formatId)
467
519k
{
468
519k
  wClipboardFormat* format = nullptr;
469
470
519k
  if (!clipboard)
471
0
    return nullptr;
472
473
519k
  format = ClipboardFindFormat(clipboard, formatId, nullptr);
474
475
519k
  if (!format)
476
0
    return nullptr;
477
478
519k
  return format->formatName;
479
519k
}
480
481
void* ClipboardGetData(wClipboard* clipboard, UINT32 formatId, UINT32* pSize)
482
16.3k
{
483
16.3k
  void* pDstData = nullptr;
484
485
16.3k
  if (!clipboard || !pSize)
486
0
  {
487
0
    WLog_ERR(TAG, "Invalid parameters clipboard=%p, pSize=%p",
488
0
             WINPR_CXX_COMPAT_CAST(const void*, clipboard),
489
0
             WINPR_CXX_COMPAT_CAST(const void*, pSize));
490
0
    return nullptr;
491
0
  }
492
493
16.3k
  *pSize = 0;
494
16.3k
  wClipboardFormat* format = ClipboardFindFormat(clipboard, clipboard->formatId, nullptr);
495
496
16.3k
  if (!format)
497
0
  {
498
0
    WLog_ERR(TAG, "Format [0x%08" PRIx32 "] not found", clipboard->formatId);
499
0
    return nullptr;
500
0
  }
501
502
16.3k
  const UINT32 SrcSize = clipboard->size;
503
16.3k
  void* pSrcData = clipboard->data;
504
505
16.3k
  if (formatId == format->formatId)
506
3.37k
  {
507
3.37k
    UINT32 DstSize = SrcSize;
508
3.37k
    pDstData = malloc(DstSize);
509
510
3.37k
    if (!pDstData)
511
0
      return nullptr;
512
513
3.37k
    CopyMemory(pDstData, pSrcData, SrcSize);
514
3.37k
    *pSize = DstSize;
515
3.37k
  }
516
12.9k
  else
517
12.9k
  {
518
12.9k
    wClipboardSynthesizer* synthesizer = ClipboardFindSynthesizer(format, formatId);
519
520
12.9k
    if (!synthesizer || !(synthesizer->pfnSynthesize || synthesizer->pfnSynthesizeEx))
521
0
    {
522
0
      WLog_ERR(TAG, "No synthesizer for format %s [0x%08" PRIx32 "] --> %s [0x%08" PRIx32 "]",
523
0
               ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId,
524
0
               ClipboardGetFormatName(clipboard, formatId), formatId);
525
0
      return nullptr;
526
0
    }
527
528
12.9k
    UINT32 DstSize = SrcSize;
529
12.9k
    if (synthesizer->pfnSynthesizeEx)
530
12.9k
      pDstData = synthesizer->pfnSynthesizeEx(clipboard, synthesizer->syntheticId, pSrcData,
531
12.9k
                                              &DstSize);
532
0
    else
533
0
      pDstData = synthesizer->pfnSynthesize(clipboard, format->formatId, pSrcData, &DstSize);
534
12.9k
    if (pDstData)
535
4.83k
      *pSize = DstSize;
536
12.9k
  }
537
538
16.3k
  WLog_DBG(TAG, "getting formatId=%s [0x%08" PRIx32 "] data=%p, size=%" PRIu32,
539
16.3k
           ClipboardGetFormatName(clipboard, formatId), formatId, pDstData, *pSize);
540
16.3k
  return pDstData;
541
16.3k
}
542
543
BOOL ClipboardSetData(wClipboard* clipboard, UINT32 formatId, const void* data, UINT32 size)
544
3.37k
{
545
3.37k
  wClipboardFormat* format = nullptr;
546
547
3.37k
  WLog_DBG(TAG, "setting formatId=%s [0x%08" PRIx32 "], size=%" PRIu32,
548
3.37k
           ClipboardGetFormatName(clipboard, formatId), formatId, size);
549
3.37k
  if (!clipboard)
550
0
    return FALSE;
551
552
3.37k
  format = ClipboardFindFormat(clipboard, formatId, nullptr);
553
554
3.37k
  if (!format)
555
0
    return FALSE;
556
557
3.37k
  free(clipboard->data);
558
559
  /* append 2 WCHAR zero bytes. Prevent problems with odd input data sizes. */
560
3.37k
  clipboard->data = calloc(size + 2ull * sizeof(WCHAR), sizeof(char));
561
562
3.37k
  if (!clipboard->data)
563
0
    return FALSE;
564
565
3.37k
  memcpy(clipboard->data, data, size);
566
567
  /* For string values we don“t know if they are '\0' terminated.
568
   * so set the size to the full length in bytes (e.g. string length + 1)
569
   */
570
3.37k
  switch (formatId)
571
3.37k
  {
572
263
    case CF_TEXT:
573
507
    case CF_OEMTEXT:
574
507
      clipboard->size = (UINT32)(strnlen(clipboard->data, size) + 1UL);
575
507
      break;
576
303
    case CF_UNICODETEXT:
577
303
      clipboard->size =
578
303
          (UINT32)((_wcsnlen(clipboard->data, size / sizeof(WCHAR)) + 1UL) * sizeof(WCHAR));
579
303
      break;
580
2.56k
    default:
581
2.56k
      clipboard->size = size;
582
2.56k
      break;
583
3.37k
  }
584
585
3.37k
  clipboard->formatId = formatId;
586
3.37k
  clipboard->sequenceNumber++;
587
3.37k
  return TRUE;
588
3.37k
}
589
590
UINT64 ClipboardGetOwner(wClipboard* clipboard)
591
0
{
592
0
  if (!clipboard)
593
0
    return 0;
594
595
0
  return clipboard->ownerId;
596
0
}
597
598
void ClipboardSetOwner(wClipboard* clipboard, UINT64 ownerId)
599
0
{
600
0
  if (!clipboard)
601
0
    return;
602
603
0
  clipboard->ownerId = ownerId;
604
0
}
605
606
wClipboardDelegate* ClipboardGetDelegate(wClipboard* clipboard)
607
384k
{
608
384k
  if (!clipboard)
609
0
    return nullptr;
610
611
384k
  return &clipboard->delegate;
612
384k
}
613
614
static void ClipboardInitLocalFileSubsystem(wClipboard* clipboard)
615
3.37k
{
616
  /*
617
   * There can be only one local file subsystem active.
618
   * Return as soon as initialization succeeds.
619
   */
620
3.37k
  if (ClipboardInitSyntheticFileSubsystem(clipboard))
621
3.37k
  {
622
3.37k
    WLog_DBG(TAG, "initialized synthetic local file subsystem");
623
3.37k
    return;
624
3.37k
  }
625
0
  else
626
0
  {
627
0
    WLog_WARN(TAG, "failed to initialize synthetic local file subsystem");
628
0
  }
629
630
0
  WLog_INFO(TAG, "failed to initialize local file subsystem, file transfer not available");
631
0
}
632
633
wClipboard* ClipboardCreate(void)
634
3.37k
{
635
3.37k
  wClipboard* clipboard = (wClipboard*)calloc(1, sizeof(wClipboard));
636
637
3.37k
  if (!clipboard)
638
0
    return nullptr;
639
640
3.37k
  clipboard->nextFormatId = 0xC000;
641
3.37k
  clipboard->sequenceNumber = 0;
642
643
3.37k
  if (!InitializeCriticalSectionAndSpinCount(&(clipboard->lock), 4000))
644
0
    goto fail;
645
646
3.37k
  clipboard->numFormats = 0;
647
3.37k
  clipboard->maxFormats = 64;
648
3.37k
  clipboard->formats = (wClipboardFormat*)calloc(clipboard->maxFormats, sizeof(wClipboardFormat));
649
650
3.37k
  if (!clipboard->formats)
651
0
    goto fail;
652
653
3.37k
  if (!ClipboardInitFormats(clipboard))
654
0
    goto fail;
655
656
3.37k
  clipboard->delegate.clipboard = clipboard;
657
3.37k
  ClipboardInitLocalFileSubsystem(clipboard);
658
3.37k
  return clipboard;
659
0
fail:
660
0
  ClipboardDestroy(clipboard);
661
0
  return nullptr;
662
3.37k
}
663
664
void ClipboardDestroy(wClipboard* clipboard)
665
3.37k
{
666
3.37k
  if (!clipboard)
667
0
    return;
668
669
3.37k
  ArrayList_Free(clipboard->localFiles);
670
3.37k
  clipboard->localFiles = nullptr;
671
672
3.37k
  ClipboardUninitFormats(clipboard);
673
674
3.37k
  free(clipboard->data);
675
3.37k
  clipboard->data = nullptr;
676
3.37k
  clipboard->size = 0;
677
3.37k
  clipboard->numFormats = 0;
678
3.37k
  free(clipboard->formats);
679
3.37k
  DeleteCriticalSection(&(clipboard->lock));
680
3.37k
  free(clipboard);
681
3.37k
}
682
683
static BOOL is_dos_drive(const char* path, size_t len)
684
9.56k
{
685
9.56k
  if (len < 2)
686
34
    return FALSE;
687
688
9.53k
  WINPR_ASSERT(path);
689
9.53k
  if (path[1] == ':' || path[1] == '|')
690
127
  {
691
127
    if (((path[0] >= 'A') && (path[0] <= 'Z')) || ((path[0] >= 'a') && (path[0] <= 'z')))
692
82
      return TRUE;
693
127
  }
694
9.45k
  return FALSE;
695
9.53k
}
696
697
char* parse_uri_to_local_file(const char* uri, size_t uri_len)
698
9.70k
{
699
  // URI is specified by RFC 8089: https://datatracker.ietf.org/doc/html/rfc8089
700
9.70k
  const char prefix[] = "file:";
701
9.70k
  const char prefixTraditional[] = "file://";
702
9.70k
  const char* localName = nullptr;
703
9.70k
  size_t localLen = 0;
704
9.70k
  char* buffer = nullptr;
705
9.70k
  const size_t prefixLen = strnlen(prefix, sizeof(prefix));
706
9.70k
  const size_t prefixTraditionalLen = strnlen(prefixTraditional, sizeof(prefixTraditional));
707
708
9.70k
  WINPR_ASSERT(uri || (uri_len == 0));
709
710
9.70k
  WLog_VRB(TAG, "processing URI: %.*s", WINPR_ASSERTING_INT_CAST(int, uri_len), uri);
711
712
9.70k
  if ((uri_len <= prefixLen) || strncmp(uri, prefix, prefixLen) != 0)
713
117
  {
714
117
    WLog_ERR(TAG, "non-'file:' URI schemes are not supported");
715
117
    return nullptr;
716
117
  }
717
718
9.58k
  do
719
9.58k
  {
720
    /* https://datatracker.ietf.org/doc/html/rfc8089#appendix-F
721
     * - The minimal representation of a local file in a DOS- or Windows-
722
     *   based environment with no authority field and an absolute path
723
     *   that begins with a drive letter.
724
     *
725
     *   "file:c:/path/to/file"
726
     *
727
     * - Regular DOS or Windows file URIs with vertical line characters in
728
     *   the drive letter construct.
729
     *
730
     *   "file:c|/path/to/file"
731
     *
732
     */
733
9.58k
    if (uri[prefixLen] != '/')
734
106
    {
735
736
106
      if (is_dos_drive(&uri[prefixLen], uri_len - prefixLen))
737
56
      {
738
        // Dos and Windows file URI
739
56
        localName = &uri[prefixLen];
740
56
        localLen = uri_len - prefixLen;
741
56
        break;
742
56
      }
743
50
      else
744
50
      {
745
50
        WLog_ERR(TAG, "URI format are not supported: %s", uri);
746
50
        return nullptr;
747
50
      }
748
106
    }
749
750
    /*
751
     * - The minimal representation of a local file with no authority field
752
     *   and an absolute path that begins with a slash "/".  For example:
753
     *
754
     *   "file:/path/to/file"
755
     *
756
     */
757
9.48k
    else if ((uri_len > prefixLen + 1) && (uri[prefixLen + 1] != '/'))
758
1.75k
    {
759
1.75k
      if (is_dos_drive(&uri[prefixLen + 1], uri_len - prefixLen - 1))
760
18
      {
761
        // Dos and Windows file URI
762
18
        localName = (uri + prefixLen + 1);
763
18
        localLen = uri_len - prefixLen - 1;
764
18
      }
765
1.73k
      else
766
1.73k
      {
767
1.73k
        localName = &uri[prefixLen];
768
1.73k
        localLen = uri_len - prefixLen;
769
1.73k
      }
770
1.75k
      break;
771
1.75k
    }
772
773
    /*
774
     * - A traditional file URI for a local file with an empty authority.
775
     *
776
     *   "file:///path/to/file"
777
     */
778
7.73k
    if ((uri_len < prefixTraditionalLen) ||
779
7.72k
        strncmp(uri, prefixTraditional, prefixTraditionalLen) != 0)
780
2
    {
781
2
      WLog_ERR(TAG, "non-'file:' URI schemes are not supported");
782
2
      return nullptr;
783
2
    }
784
785
7.72k
    localName = &uri[prefixTraditionalLen];
786
7.72k
    localLen = uri_len - prefixTraditionalLen;
787
788
7.72k
    if (localLen < 1)
789
3
    {
790
3
      WLog_ERR(TAG, "empty 'file:' URI schemes are not supported");
791
3
      return nullptr;
792
3
    }
793
794
    /*
795
     * "file:///c:/path/to/file"
796
     * "file:///c|/path/to/file"
797
     */
798
7.72k
    if (localName[0] != '/')
799
15
    {
800
15
      WLog_ERR(TAG, "URI format are not supported: %s", uri);
801
15
      return nullptr;
802
15
    }
803
804
7.71k
    if (is_dos_drive(&localName[1], localLen - 1))
805
8
    {
806
8
      localName++;
807
8
      localLen--;
808
8
    }
809
810
7.71k
  } while (0);
811
812
9.51k
  buffer = winpr_str_url_decode(localName, localLen);
813
9.51k
  if (buffer)
814
9.51k
  {
815
9.51k
    if (buffer[1] == '|' &&
816
30
        ((buffer[0] >= 'A' && buffer[0] <= 'Z') || (buffer[0] >= 'a' && buffer[0] <= 'z')))
817
24
      buffer[1] = ':';
818
9.51k
    return buffer;
819
9.51k
  }
820
821
0
  return nullptr;
822
9.51k
}