Coverage Report

Created: 2026-10-28 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/clipboard/synthetic.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 <errno.h>
23
#include <winpr/crt.h>
24
#include <winpr/user.h>
25
#include <winpr/image.h>
26
27
#include "../utils/image.h"
28
#include "clipboard.h"
29
#include "../crt/unicode.h"
30
31
#include "../log.h"
32
#define TAG WINPR_TAG("clipboard.synthetic")
33
34
static const char mime_html[] = "text/html";
35
static const char mime_ms_html[] = "HTML Format";
36
37
#define BITMAP_MIME_TYPES "image/bmp", "image/x-bmp", "image/x-MS-bmp", "image/x-win-bitmap"
38
static const char* mime_bitmap[] = { BITMAP_MIME_TYPES };
39
40
#if defined(WINPR_UTILS_IMAGE_WEBP)
41
static const char mime_webp[] = "image/webp";
42
#endif
43
#if defined(WINPR_UTILS_IMAGE_PNG)
44
static const char mime_png[] = "image/png";
45
#endif
46
#if defined(WINPR_UTILS_IMAGE_JPEG)
47
static const char mime_jpeg[] = "image/jpeg";
48
#endif
49
static const char mime_tiff[] = "image/tiff";
50
51
static const char* mime_images[] = {
52
#if defined(WINPR_UTILS_IMAGE_WEBP)
53
  mime_webp,
54
#endif
55
#if defined(WINPR_UTILS_IMAGE_PNG)
56
  mime_png,
57
#endif
58
#if defined(WINPR_UTILS_IMAGE_JPEG)
59
  mime_jpeg,
60
#endif
61
  mime_tiff, BITMAP_MIME_TYPES
62
};
63
64
static const BYTE enc_base64url[] =
65
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
66
67
WINPR_ATTR_NODISCARD
68
static inline char* b64_encode(const BYTE* WINPR_RESTRICT data, size_t length, size_t* plen)
69
724
{
70
724
  WINPR_ASSERT(plen);
71
724
  const BYTE* WINPR_RESTRICT alphabet = enc_base64url;
72
724
  int c = 0;
73
724
  size_t blocks = 0;
74
724
  size_t outLen = (length + 3) * 4 / 3;
75
724
  size_t extra = 0;
76
77
724
  const BYTE* q = data;
78
724
  const size_t alen = outLen + extra + 1ull;
79
724
  BYTE* p = malloc(alen);
80
724
  if (!p)
81
0
    return nullptr;
82
83
724
  BYTE* ret = p;
84
85
  /* b1, b2, b3 are input bytes
86
   *
87
   * 0         1         2
88
   * 012345678901234567890123
89
   * |  b1  |  b2   |  b3   |
90
   *
91
   * [ c1 ]     [  c3 ]
92
   *      [  c2 ]     [  c4 ]
93
   *
94
   * c1, c2, c3, c4 are output chars in base64
95
   */
96
97
  /* first treat complete blocks */
98
724
  blocks = length - (length % 3);
99
26.6M
  for (size_t i = 0; i < blocks; i += 3, q += 3)
100
26.6M
  {
101
26.6M
    c = (q[0] << 16) + (q[1] << 8) + q[2];
102
103
26.6M
    *p++ = alphabet[(c & 0x00FC0000) >> 18];
104
26.6M
    *p++ = alphabet[(c & 0x0003F000) >> 12];
105
26.6M
    *p++ = alphabet[(c & 0x00000FC0) >> 6];
106
26.6M
    *p++ = alphabet[c & 0x0000003F];
107
26.6M
  }
108
109
  /* then remainder */
110
724
  switch (length % 3)
111
724
  {
112
224
    case 0:
113
224
      break;
114
236
    case 1:
115
236
      c = (q[0] << 16);
116
236
      *p++ = alphabet[(c & 0x00FC0000) >> 18];
117
236
      *p++ = alphabet[(c & 0x0003F000) >> 12];
118
236
      break;
119
264
    case 2:
120
264
      c = (q[0] << 16) + (q[1] << 8);
121
264
      *p++ = alphabet[(c & 0x00FC0000) >> 18];
122
264
      *p++ = alphabet[(c & 0x0003F000) >> 12];
123
264
      *p++ = alphabet[(c & 0x00000FC0) >> 6];
124
264
      break;
125
0
    default:
126
0
      break;
127
724
  }
128
129
724
  *p = 0;
130
724
  *plen = WINPR_ASSERTING_INT_CAST(size_t, p - ret);
131
132
724
  return (char*)ret;
133
724
}
134
135
/**
136
 * Standard Clipboard Formats:
137
 * http://msdn.microsoft.com/en-us/library/windows/desktop/ff729168/
138
 */
139
140
/**
141
 * "CF_LOCALE":
142
 *
143
 * System locale identifier associated with CF_TEXT
144
 */
145
WINPR_ATTR_MALLOC(free, 1)
146
static void* clipboard_synthesize_cf_locale(WINPR_ATTR_UNUSED wClipboard* clipboard,
147
                                            UINT32 dstFormatId, WINPR_ATTR_UNUSED const void* data,
148
                                            WINPR_ATTR_UNUSED UINT32* pSize)
149
797
{
150
797
  if (dstFormatId != CF_LOCALE)
151
0
  {
152
0
    WLog_ERR(TAG,
153
0
             "Unuspported destination format %s [0x%04" PRIx32
154
0
             "], trying to convert from %s [0x%04" PRIx32 "]",
155
0
             ClipboardGetFormatName(clipboard, dstFormatId), dstFormatId,
156
0
             ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
157
0
    return nullptr;
158
0
  }
159
160
797
  switch (clipboard->formatId)
161
797
  {
162
261
    case CF_TEXT:
163
566
    case CF_UNICODETEXT:
164
797
    case CF_OEMTEXT:
165
797
      break;
166
0
    default:
167
0
    {
168
0
      const UINT32 formatIdText = ClipboardRegisterFormat(clipboard, mime_text_plain);
169
0
      const UINT32 formatIdUtf = ClipboardRegisterFormat(clipboard, mime_text_plain);
170
0
      if ((clipboard->formatId != formatIdText) && (clipboard->formatId != formatIdUtf))
171
0
      {
172
0
        WLog_ERR(TAG,
173
0
                 "Unuspported source format %s [0x%04" PRIx32
174
0
                 "], trying to convert to %s [0x%04" PRIx32 "]",
175
0
                 ClipboardGetFormatName(clipboard, clipboard->formatId),
176
0
                 clipboard->formatId, ClipboardGetFormatName(clipboard, dstFormatId),
177
0
                 dstFormatId);
178
0
        return nullptr;
179
0
      }
180
0
    }
181
0
    break;
182
797
  }
183
184
797
  UINT32* pDstData = (UINT32*)calloc(1, sizeof(UINT32));
185
186
797
  if (!pDstData)
187
0
    return nullptr;
188
189
797
  *pDstData = 0x0409; /* English - United States */
190
797
  return (void*)pDstData;
191
797
}
192
193
/* text/plain is platform dependent: UTF-8 on Windows, where e.g. SDL treats it as such, and
194
 * ASCII with \u escapes for all other characters elsewhere. */
195
WINPR_ATTR_NODISCARD
196
static BOOL format_is_utf8(wClipboard* clipboard, UINT32 formatId)
197
4.41k
{
198
#if defined(_WIN32)
199
  if (formatId == ClipboardGetFormatId(clipboard, mime_text_plain))
200
    return TRUE;
201
#endif
202
4.41k
  return (formatId == ClipboardGetFormatId(clipboard, mime_text_utf8)) ||
203
3.76k
         (formatId == ClipboardGetFormatId(clipboard, mime_text_UTF8_STRING));
204
4.41k
}
205
206
/**
207
 * mime_utf8_string:
208
 *
209
 * Null-terminated UTF-8 string with LF line endings.
210
 */
211
WINPR_ATTR_MALLOC(free, 1)
212
static void* clipboard_synthesize_string(wClipboard* clipboard, UINT32 dstFormatId,
213
                                         const void* data, UINT32* pSize)
214
3.98k
{
215
3.98k
  WINPR_ASSERT(clipboard);
216
217
  // Step 1: convert to utf-8
218
3.98k
  char* utf8 = nullptr;
219
3.98k
  size_t utf8len = 0;
220
3.98k
  if (clipboard->formatId == CF_UNICODETEXT)
221
1.52k
  {
222
1.52k
    size_t size = 0;
223
1.52k
    utf8 = ConvertWCharNToUtf8Alloc(data, *pSize / sizeof(WCHAR), &size);
224
225
1.52k
    if (!utf8)
226
270
      return nullptr;
227
228
1.25k
    const size_t rc = ConvertLineEndingToLF(utf8, size);
229
1.25k
    utf8len = rc;
230
1.25k
  }
231
2.46k
  else if (format_is_utf8(clipboard, clipboard->formatId))
232
0
  {
233
0
    const size_t size = *pSize;
234
0
    utf8 = strndup(data, size);
235
0
    if (!utf8)
236
0
      return nullptr;
237
0
    utf8len = strlen(utf8);
238
0
  }
239
2.46k
  else if ((clipboard->formatId == CF_TEXT) || (clipboard->formatId == CF_OEMTEXT) ||
240
0
           (clipboard->formatId == ClipboardGetFormatId(clipboard, mime_text_plain)))
241
2.46k
  {
242
2.46k
    const size_t size = *pSize;
243
2.46k
    utf8 = calloc(size + 1, sizeof(char));
244
245
2.46k
    if (!utf8)
246
0
      return nullptr;
247
248
2.46k
    CopyMemory(utf8, data, size);
249
2.46k
    const size_t rc = ConvertLineEndingToLF(utf8, size);
250
2.46k
    const SSIZE_T res = winpr_utfEscapedStringToUtf8(utf8, rc);
251
2.46k
    if (res < 0)
252
450
    {
253
450
      winpr_znfree(utf8, size);
254
450
      return nullptr;
255
450
    }
256
2.01k
    utf8len = (size_t)res;
257
2.01k
  }
258
0
  else
259
0
  {
260
0
    WLog_ERR(TAG,
261
0
             "Unuspported source format %s [0x%04" PRIx32
262
0
             "], trying to convert to %s [0x%04" PRIx32 "]",
263
264
0
             ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId,
265
0
             ClipboardGetFormatName(clipboard, dstFormatId), dstFormatId);
266
0
    return nullptr;
267
0
  }
268
269
3.26k
  switch (dstFormatId)
270
3.26k
  {
271
402
    case CF_UNICODETEXT:
272
402
    {
273
402
      size_t crlfLen = utf8len;
274
402
      char* crlf = ConvertLineEndingToCRLF(utf8, &crlfLen);
275
402
      winpr_znfree(utf8, utf8len);
276
402
      if (!crlf)
277
2
        return nullptr;
278
279
400
      size_t wlen = 0;
280
400
      WCHAR* wstr = ConvertUtf8NToWCharAlloc(crlf, crlfLen, &wlen);
281
400
      winpr_znfree(crlf, crlfLen);
282
400
      if (!wstr || (wlen == 0) || (wlen >= UINT32_MAX / sizeof(WCHAR)))
283
189
      {
284
189
        winpr_znfree(wstr, wlen * sizeof(WCHAR));
285
189
        return nullptr;
286
189
      }
287
288
211
      *pSize = WINPR_ASSERTING_INT_CAST(UINT32, (wlen + 1) * sizeof(WCHAR));
289
211
      return wstr;
290
400
    }
291
482
    case CF_OEMTEXT:
292
904
    case CF_TEXT:
293
904
    {
294
904
      size_t esclen = 0;
295
904
      char* escaped = winpr_utf8ToUtfEscapedString(utf8, utf8len, &esclen);
296
904
      winpr_znfree(utf8, utf8len);
297
904
      if (!escaped || (esclen == 0) || (esclen >= UINT32_MAX))
298
191
      {
299
191
        winpr_znfree(escaped, esclen);
300
191
        return nullptr;
301
191
      }
302
713
      *pSize = WINPR_ASSERTING_INT_CAST(UINT32, esclen + 1);
303
713
      return escaped;
304
904
    }
305
1.95k
    default:
306
1.95k
      if (format_is_utf8(clipboard, dstFormatId))
307
1.30k
      {
308
1.30k
        if (utf8len >= UINT32_MAX)
309
0
        {
310
0
          winpr_znfree(utf8, utf8len);
311
0
          return nullptr;
312
0
        }
313
1.30k
        *pSize = WINPR_ASSERTING_INT_CAST(UINT32, utf8len + 1);
314
1.30k
        return utf8;
315
1.30k
      }
316
317
653
      if (dstFormatId == ClipboardGetFormatId(clipboard, mime_text_plain))
318
653
      {
319
653
        size_t esclen = 0;
320
653
        char* escaped = winpr_utf8ToUtfEscapedString(utf8, utf8len, &esclen);
321
653
        winpr_znfree(utf8, utf8len);
322
653
        if (!escaped || (esclen == 0) || (esclen >= UINT32_MAX))
323
190
        {
324
190
          winpr_znfree(escaped, esclen);
325
190
          return nullptr;
326
190
        }
327
463
        *pSize = WINPR_ASSERTING_INT_CAST(UINT32, esclen + 1);
328
463
        return escaped;
329
653
      }
330
331
0
      WLog_ERR(TAG,
332
0
               "Unuspported destination format %s [0x%04" PRIx32
333
0
               "], trying to convert from %s [0x%04" PRIx32 "]",
334
0
               ClipboardGetFormatName(clipboard, dstFormatId), dstFormatId,
335
0
               ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
336
0
      winpr_znfree(utf8, utf8len);
337
0
      return nullptr;
338
3.26k
  }
339
3.26k
}
340
341
WINPR_ATTR_MALLOC(free, 1)
342
static void* clipboard_prepend_bmp_header(const WINPR_BITMAP_INFO_HEADER* pInfoHeader,
343
                                          size_t offset, const void* data, size_t size,
344
                                          UINT32* pSize)
345
4.71k
{
346
4.71k
  WINPR_ASSERT(pInfoHeader);
347
4.71k
  WINPR_ASSERT(pSize);
348
349
4.71k
  *pSize = 0;
350
4.71k
  if ((pInfoHeader->biBitCount < 1) || (pInfoHeader->biBitCount > 32))
351
0
    return nullptr;
352
353
4.71k
  if (size > (UINT32_MAX - sizeof(WINPR_BITMAP_FILE_HEADER)))
354
0
    return nullptr;
355
4.71k
  const size_t DstSize = sizeof(WINPR_BITMAP_FILE_HEADER) + size;
356
4.71k
  if ((pInfoHeader->biSize > size) || (offset > (size - pInfoHeader->biSize)))
357
366
    return nullptr;
358
359
4.34k
  const size_t bitmapOffset = sizeof(WINPR_BITMAP_FILE_HEADER) + pInfoHeader->biSize + offset;
360
4.34k
  if (bitmapOffset > DstSize)
361
0
    return nullptr;
362
363
4.34k
  wStream* s = Stream_New(nullptr, DstSize);
364
4.34k
  if (!s)
365
0
    return nullptr;
366
367
4.34k
  WINPR_BITMAP_FILE_HEADER fileHeader = WINPR_C_ARRAY_INIT;
368
4.34k
  fileHeader.bfType[0] = 'B';
369
4.34k
  fileHeader.bfType[1] = 'M';
370
4.34k
  fileHeader.bfSize = (UINT32)DstSize;
371
4.34k
  fileHeader.bfOffBits = (UINT32)bitmapOffset;
372
4.34k
  if (!writeBitmapFileHeader(s, &fileHeader))
373
0
    goto fail;
374
375
4.34k
  if (!Stream_EnsureRemainingCapacity(s, size))
376
0
    goto fail;
377
4.34k
  Stream_Write(s, data, size);
378
379
4.34k
  {
380
4.34k
    const size_t len = Stream_GetPosition(s);
381
4.34k
    if (len != DstSize)
382
0
      goto fail;
383
4.34k
  }
384
385
4.34k
  *pSize = (UINT32)DstSize;
386
387
4.34k
  {
388
4.34k
    BYTE* dst = Stream_Buffer(s);
389
4.34k
    Stream_Free(s, FALSE);
390
4.34k
    return dst;
391
4.34k
  }
392
393
0
fail:
394
0
  Stream_Free(s, TRUE);
395
0
  return nullptr;
396
4.34k
}
397
398
/**
399
 * "image/bmp":
400
 *
401
 * Bitmap file format.
402
 */
403
WINPR_ATTR_MALLOC(free, 1)
404
static void* clipboard_synthesize_image_bmp(WINPR_ATTR_UNUSED wClipboard* clipboard,
405
                                            UINT32 dstFormatId, const void* data, UINT32* pSize)
406
6.11k
{
407
6.11k
  UINT32 SrcSize = *pSize;
408
409
6.11k
  if (dstFormatId == CF_DIB)
410
6.10k
  {
411
6.10k
    if (SrcSize < sizeof(BITMAPINFOHEADER))
412
36
      return nullptr;
413
414
6.07k
    wStream sbuffer = WINPR_C_ARRAY_INIT;
415
6.07k
    size_t offset = 0;
416
6.07k
    WINPR_BITMAP_INFO_HEADER header = WINPR_C_ARRAY_INIT;
417
6.07k
    wStream* s = Stream_StaticConstInit(&sbuffer, data, SrcSize);
418
6.07k
    if (!readBitmapInfoHeader(s, &header, &offset))
419
1.36k
      return nullptr;
420
421
4.71k
    return clipboard_prepend_bmp_header(&header, offset, data, SrcSize, pSize);
422
6.07k
  }
423
#if defined(WINPR_UTILS_IMAGE_DIBv5)
424
  else if (dstFormatId == CF_DIBV5)
425
  {
426
    WLog_ERR(TAG,
427
             "Unuspported destination format %s [0x%04" PRIx32
428
             "], trying to convert from %s [0x%04" PRIx32 "]",
429
             ClipboardGetFormatName(clipboard, dstFormatId), dstFormatId,
430
             ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
431
  }
432
#endif
433
2
  else
434
2
  {
435
2
    WLog_ERR(TAG,
436
2
             "Unuspported destination format %s [0x%04" PRIx32
437
2
             "], trying to convert from %s [0x%04" PRIx32 "]",
438
2
             ClipboardGetFormatName(clipboard, dstFormatId), dstFormatId,
439
2
             ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
440
2
  }
441
442
2
  return nullptr;
443
6.11k
}
444
445
WINPR_ATTR_NODISCARD
446
static BOOL format_is_image(wClipboard* clipboard, UINT32 formatId)
447
7.61k
{
448
7.61k
  switch (formatId)
449
7.61k
  {
450
362
    case CF_DIB:
451
362
    case CF_DIBV5:
452
362
    case CF_TIFF:
453
362
      return TRUE;
454
7.25k
    default:
455
456
21.7k
      for (size_t x = 0; x < ARRAYSIZE(mime_images); x++)
457
21.7k
      {
458
21.7k
        const char* mime = mime_images[x];
459
21.7k
        const UINT32 id = ClipboardRegisterFormat(clipboard, mime);
460
21.7k
        if (formatId == id)
461
7.25k
          return TRUE;
462
21.7k
      }
463
0
      return FALSE;
464
7.61k
  }
465
7.61k
}
466
467
WINPR_ATTR_NODISCARD
468
static UINT32 image_format(WINPR_ATTR_UNUSED wClipboard* clipboard,
469
                           WINPR_ATTR_UNUSED UINT32 formatId)
470
5.16k
{
471
#if defined(WINPR_UTILS_IMAGE_PNG)
472
  if (formatId == ClipboardRegisterFormat(clipboard, mime_png))
473
    return WINPR_IMAGE_PNG;
474
#endif
475
#if defined(WINPR_UTILS_IMAGE_JPEG)
476
  if (formatId == ClipboardRegisterFormat(clipboard, mime_jpeg))
477
    return WINPR_IMAGE_JPEG;
478
#endif
479
#if defined(WINPR_UTILS_IMAGE_WEBP)
480
  if (formatId == ClipboardRegisterFormat(clipboard, mime_webp))
481
    return WINPR_IMAGE_WEBP;
482
#endif
483
5.16k
  return WINPR_IMAGE_BITMAP;
484
5.16k
}
485
486
WINPR_ATTR_MALLOC(free, 1)
487
static void* clipboard_synthesize_image_dib_to_format(wClipboard* clipboard, UINT32 dstFormatId,
488
                                                      const void* data, UINT32* pSize)
489
5.09k
{
490
5.09k
  WINPR_ASSERT(clipboard);
491
5.09k
  WINPR_ASSERT(data);
492
5.09k
  WINPR_ASSERT(pSize);
493
494
5.09k
  if ((clipboard->formatId != CF_DIB) && (clipboard->formatId != CF_DIBV5))
495
0
  {
496
0
    WLog_ERR(TAG,
497
0
             "Unuspported source format %s [0x%04" PRIx32
498
0
             "], trying to convert to %s [0x%04" PRIx32 "]",
499
500
0
             ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId,
501
0
             ClipboardGetFormatName(clipboard, dstFormatId), dstFormatId);
502
0
    return nullptr;
503
0
  }
504
5.09k
  if (!format_is_image(clipboard, dstFormatId))
505
0
  {
506
0
    WLog_ERR(TAG,
507
0
             "Unuspported destination format %s [0x%04" PRIx32
508
0
             "], trying to convert from %s [0x%04" PRIx32 "]",
509
510
0
             ClipboardGetFormatName(clipboard, dstFormatId), dstFormatId,
511
0
             ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
512
0
    return nullptr;
513
0
  }
514
515
5.09k
  const UINT32 format = image_format(clipboard, dstFormatId);
516
5.09k
  size_t dsize = 0;
517
5.09k
  void* result = nullptr;
518
519
5.09k
  wImage* img = winpr_image_new();
520
5.09k
  void* bmp = clipboard_synthesize_image_bmp(clipboard, clipboard->formatId, data, pSize);
521
5.09k
  const UINT32 SrcSize = *pSize;
522
5.09k
  *pSize = 0;
523
524
5.09k
  if (!bmp || !img)
525
1.47k
    goto fail;
526
527
3.62k
  if (winpr_image_read_buffer(img, bmp, SrcSize) <= 0)
528
2.75k
    goto fail;
529
530
865
  result = winpr_image_write_buffer(img, format, &dsize);
531
865
  if (result)
532
865
  {
533
865
    if (dsize <= UINT32_MAX)
534
865
      *pSize = (UINT32)dsize;
535
0
    else
536
0
    {
537
0
      free(result);
538
0
      result = nullptr;
539
0
    }
540
865
  }
541
542
5.09k
fail:
543
5.09k
  free(bmp);
544
5.09k
  winpr_image_free(img, TRUE);
545
5.09k
  return result;
546
865
}
547
548
/**
549
 * "CF_DIB":
550
 *
551
 * BITMAPINFO structure followed by the bitmap bits.
552
 */
553
WINPR_ATTR_MALLOC(free, 1)
554
static void* clipboard_synthesize_cf_dib(const void* pvdata, size_t dataLen, UINT32* pSize)
555
23
{
556
23
  WINPR_ASSERT(pSize);
557
23
  const BYTE* data = pvdata;
558
559
23
  WINPR_BITMAP_FILE_HEADER pFileHeader = WINPR_C_ARRAY_INIT;
560
23
  wStream sbuffer = WINPR_C_ARRAY_INIT;
561
23
  wStream* s = Stream_StaticConstInit(&sbuffer, data, dataLen);
562
23
  if (!readBitmapFileHeader(s, &pFileHeader))
563
0
    return nullptr;
564
565
23
  const size_t DstSize = dataLen - sizeof(BITMAPFILEHEADER);
566
23
  BYTE* pDstData = (BYTE*)calloc(DstSize, 1);
567
568
23
  if (!pDstData)
569
0
    return nullptr;
570
571
23
  const void* src = &data[sizeof(BITMAPFILEHEADER)];
572
23
  memcpy(pDstData, src, DstSize);
573
23
  *pSize = WINPR_ASSERTING_INT_CAST(UINT32, DstSize);
574
23
  return pDstData;
575
23
}
576
577
WINPR_ATTR_MALLOC(free, 1)
578
static void* clipboard_synthesize_image_format_to_cf_dib(wClipboard* clipboard, UINT32 dstFormatId,
579
                                                         const void* data, UINT32* pSize)
580
360
{
581
360
  WINPR_ASSERT(clipboard);
582
360
  WINPR_ASSERT(data);
583
360
  WINPR_ASSERT(pSize);
584
585
360
  if ((dstFormatId != CF_DIB) && (dstFormatId != CF_DIBV5))
586
0
  {
587
0
    WLog_ERR(TAG,
588
0
             "Unuspported destination format %s [0x%04" PRIx32
589
0
             "], trying to convert from %s [0x%04" PRIx32 "]",
590
0
             ClipboardGetFormatName(clipboard, dstFormatId), dstFormatId,
591
0
             ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
592
0
    return nullptr;
593
0
  }
594
595
360
  if (!format_is_image(clipboard, dstFormatId))
596
0
  {
597
0
    WLog_ERR(TAG,
598
0
             "Unuspported source format %s [0x%04" PRIx32
599
0
             "], trying to convert to %s [0x%04" PRIx32 "]",
600
0
             ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId,
601
0
             ClipboardGetFormatName(clipboard, dstFormatId), dstFormatId);
602
0
    return nullptr;
603
0
  }
604
605
360
  void* result = nullptr;
606
360
  BYTE* dst = nullptr;
607
360
  const UINT32 SrcSize = *pSize;
608
360
  size_t size = 0;
609
360
  wImage* image = winpr_image_new();
610
360
  if (!image)
611
0
    goto fail;
612
613
360
  const int res = winpr_image_read_buffer(image, data, SrcSize);
614
360
  if (res <= 0)
615
337
    goto fail;
616
617
23
  dst = winpr_image_write_buffer(image, WINPR_IMAGE_BITMAP, &size);
618
23
  if ((size < sizeof(WINPR_BITMAP_FILE_HEADER)) || (size > UINT32_MAX) || !dst)
619
0
    goto fail;
620
621
23
  result = clipboard_synthesize_cf_dib(dst, size, pSize);
622
623
360
fail:
624
360
  winpr_image_free(image, TRUE);
625
360
  free(dst);
626
360
  return result;
627
23
}
628
629
/**
630
 * "image/png" <-> "image/bmp" <-> "image/jpeg" ...:
631
 *
632
 * Image file formats WinPR can read and write, converted into each other directly.
633
 */
634
WINPR_ATTR_MALLOC(free, 1)
635
static void* clipboard_synthesize_image_format_to_format(wClipboard* clipboard, UINT32 dstFormatId,
636
                                                         const void* data, UINT32* pSize)
637
1.08k
{
638
1.08k
  WINPR_ASSERT(clipboard);
639
1.08k
  WINPR_ASSERT(data);
640
1.08k
  WINPR_ASSERT(pSize);
641
642
1.08k
  if (!format_is_image(clipboard, clipboard->formatId) ||
643
1.08k
      !format_is_image(clipboard, dstFormatId))
644
0
  {
645
0
    WLog_ERR(TAG, "Unsupported conversion from %s [0x%04" PRIx32 "] to %s [0x%04" PRIx32 "]",
646
0
             ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId,
647
0
             ClipboardGetFormatName(clipboard, dstFormatId), dstFormatId);
648
0
    return nullptr;
649
0
  }
650
651
1.08k
  void* result = nullptr;
652
1.08k
  size_t size = 0;
653
1.08k
  const UINT32 SrcSize = *pSize;
654
1.08k
  *pSize = 0;
655
656
1.08k
  wImage* image = winpr_image_new();
657
1.08k
  if (!image)
658
0
    goto fail;
659
660
1.08k
  if (winpr_image_read_buffer(image, data, SrcSize) <= 0)
661
1.01k
    goto fail;
662
663
69
  result = winpr_image_write_buffer(image, image_format(clipboard, dstFormatId), &size);
664
69
  if (result)
665
69
  {
666
69
    if (size <= UINT32_MAX)
667
69
      *pSize = (UINT32)size;
668
0
    else
669
0
    {
670
0
      free(result);
671
0
      result = nullptr;
672
0
    }
673
69
  }
674
675
1.08k
fail:
676
1.08k
  winpr_image_free(image, TRUE);
677
1.08k
  return result;
678
69
}
679
680
/**
681
 * "HTML Format":
682
 *
683
 * HTML clipboard format: msdn.microsoft.com/en-us/library/windows/desktop/ms649015/
684
 */
685
WINPR_ATTR_MALLOC(free, 1)
686
static void* clipboard_synthesize_ms_html_format(wClipboard* clipboard, UINT32 formatId,
687
                                                 const void* pData, UINT32* pSize)
688
119
{
689
119
  if (formatId != ClipboardGetFormatId(clipboard, mime_ms_html))
690
0
  {
691
0
    WLog_ERR(TAG,
692
0
             "Unuspported destination format %s [0x%04" PRIx32
693
0
             "], trying to convert from %s [0x%04" PRIx32 "]",
694
0
             ClipboardGetFormatName(clipboard, formatId), formatId,
695
0
             ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
696
0
    return nullptr;
697
0
  }
698
119
  if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_html))
699
0
  {
700
0
    WLog_ERR(TAG,
701
0
             "Unuspported destination format %s [0x%04" PRIx32
702
0
             "], trying to convert from %s [0x%04" PRIx32 "]",
703
0
             ClipboardGetFormatName(clipboard, formatId), formatId,
704
0
             ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
705
0
    return nullptr;
706
0
  }
707
119
  union
708
119
  {
709
119
    const void* cpv;
710
119
    const char* cpc;
711
119
    const BYTE* cpb;
712
119
    WCHAR* pv;
713
119
  } pSrcData;
714
119
  char* pDstData = nullptr;
715
716
119
  pSrcData.cpv = nullptr;
717
718
119
  WINPR_ASSERT(clipboard);
719
119
  WINPR_ASSERT(pSize);
720
721
119
  if (formatId == ClipboardGetFormatId(clipboard, mime_ms_html))
722
119
  {
723
119
    const size_t SrcSize = (size_t)*pSize;
724
119
    const size_t DstSize = SrcSize + 200;
725
119
    char* body = nullptr;
726
119
    char num[20] = WINPR_C_ARRAY_INIT;
727
728
    /* Create a copy, we modify the input data */
729
119
    pSrcData.pv = calloc(1, SrcSize + 1);
730
119
    if (!pSrcData.pv)
731
0
      goto fail;
732
119
    memcpy(pSrcData.pv, pData, SrcSize);
733
734
119
    if (SrcSize > 2)
735
116
    {
736
116
      if (SrcSize > INT_MAX)
737
0
        goto fail;
738
739
      /* Check the BOM (Byte Order Mark) */
740
116
      if ((pSrcData.cpb[0] == 0xFE) && (pSrcData.cpb[1] == 0xFF))
741
57
      {
742
57
        if (!ByteSwapUnicode(pSrcData.pv, (SrcSize / 2)))
743
0
          goto fail;
744
57
      }
745
746
      /* Check if we have WCHAR, convert to UTF-8 */
747
116
      if ((pSrcData.cpb[0] == 0xFF) && (pSrcData.cpb[1] == 0xFE))
748
60
      {
749
60
        char* utfString = ConvertWCharNToUtf8Alloc(&pSrcData.pv[1],
750
60
                                                   (SrcSize / sizeof(WCHAR)) - 1, nullptr);
751
60
        free(pSrcData.pv);
752
60
        pSrcData.cpc = utfString;
753
60
        if (!utfString)
754
6
          goto fail;
755
60
      }
756
116
    }
757
758
113
    pDstData = (char*)calloc(1, DstSize);
759
760
113
    if (!pDstData)
761
0
      goto fail;
762
763
113
    (void)sprintf_s(pDstData, DstSize,
764
113
                    "Version:0.9\r\n"
765
113
                    "StartHTML:0000000000\r\n"
766
113
                    "EndHTML:0000000000\r\n"
767
113
                    "StartFragment:0000000000\r\n"
768
113
                    "EndFragment:0000000000\r\n");
769
113
    body = strstr(pSrcData.cpc, "<body");
770
771
113
    if (!body)
772
109
      body = strstr(pSrcData.cpc, "<BODY");
773
774
    /* StartHTML */
775
113
    (void)sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, DstSize));
776
113
    CopyMemory(&pDstData[23], num, 10);
777
778
113
    if (!body)
779
107
    {
780
107
      if (!winpr_str_append("<HTML><BODY>", pDstData, DstSize, nullptr))
781
0
        goto fail;
782
107
    }
783
784
113
    if (!winpr_str_append("<!--StartFragment-->", pDstData, DstSize, nullptr))
785
0
      goto fail;
786
787
    /* StartFragment */
788
113
    (void)sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, SrcSize + 200));
789
113
    CopyMemory(&pDstData[69], num, 10);
790
791
113
    if (!winpr_str_append(pSrcData.cpc, pDstData, DstSize, nullptr))
792
11
      goto fail;
793
794
    /* EndFragment */
795
102
    (void)sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, SrcSize + 200));
796
102
    CopyMemory(&pDstData[93], num, 10);
797
798
102
    if (!winpr_str_append("<!--EndFragment-->", pDstData, DstSize, nullptr))
799
2
      goto fail;
800
801
100
    if (!body)
802
94
    {
803
94
      if (!winpr_str_append("</BODY></HTML>", pDstData, DstSize, nullptr))
804
3
        goto fail;
805
94
    }
806
807
    /* EndHTML */
808
97
    (void)sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, DstSize));
809
97
    CopyMemory(&pDstData[43], num, 10);
810
97
    *pSize = (UINT32)strnlen(pDstData, DstSize) + 1;
811
97
  }
812
119
fail:
813
119
  free(pSrcData.pv);
814
119
  return pDstData;
815
119
}
816
817
WINPR_ATTR_MALLOC(free, 1)
818
static char* html_pre_write(wStream* s, const char* what)
819
2.89k
{
820
2.89k
  const size_t len = strlen(what);
821
2.89k
  Stream_Write(s, what, len);
822
2.89k
  char* startHTML = Stream_PointerAs(s, char);
823
31.8k
  for (size_t x = 0; x < 10; x++)
824
28.9k
    Stream_Write_INT8(s, '0');
825
2.89k
  Stream_Write(s, "\r\n", 2);
826
2.89k
  return startHTML;
827
2.89k
}
828
829
static void html_fill_number(char* pos, size_t val)
830
2.89k
{
831
2.89k
  char str[11] = WINPR_C_ARRAY_INIT;
832
2.89k
  (void)_snprintf(str, sizeof(str), "%010" PRIuz, val);
833
2.89k
  memcpy(pos, str, 10);
834
2.89k
}
835
836
WINPR_ATTR_MALLOC(free, 1)
837
static void* clipboard_wrap_html(const char* mime, const char* idata, size_t ilength,
838
                                 uint32_t* plen)
839
724
{
840
724
  WINPR_ASSERT(mime);
841
724
  WINPR_ASSERT(plen);
842
843
724
  *plen = 0;
844
845
724
  size_t b64len = 0;
846
724
  char* b64 = b64_encode((const BYTE*)idata, ilength, &b64len);
847
724
  if (!b64)
848
0
    return nullptr;
849
850
724
  const size_t mimelen = strlen(mime);
851
724
  wStream* s = Stream_New(nullptr, b64len + 225 + mimelen);
852
724
  if (!s)
853
0
  {
854
0
    free(b64);
855
0
    return nullptr;
856
0
  }
857
858
724
  char* startHTML = html_pre_write(s, "Version:0.9\r\nStartHTML:");
859
724
  char* endHTML = html_pre_write(s, "EndHTML:");
860
724
  char* startFragment = html_pre_write(s, "StartFragment:");
861
724
  char* endFragment = html_pre_write(s, "EndFragment:");
862
863
724
  html_fill_number(startHTML, Stream_GetPosition(s));
864
724
  const char html[] = "<html><!--StartFragment-->";
865
724
  Stream_Write(s, html, strnlen(html, sizeof(html)));
866
867
724
  html_fill_number(startFragment, Stream_GetPosition(s));
868
869
724
  const char body[] = "<body><img alt=\"FreeRDP clipboard image\" src=\"data:";
870
724
  Stream_Write(s, body, strnlen(body, sizeof(body)));
871
872
724
  Stream_Write(s, mime, mimelen);
873
874
724
  const char base64[] = ";base64,";
875
724
  Stream_Write(s, base64, strnlen(base64, sizeof(base64)));
876
724
  Stream_Write(s, b64, b64len);
877
878
724
  const char end[] = "\"/></body>";
879
724
  Stream_Write(s, end, strnlen(end, sizeof(end)));
880
881
724
  html_fill_number(endFragment, Stream_GetPosition(s));
882
883
724
  const char fragend[] = "<!--EndFragment--></html>";
884
724
  Stream_Write(s, fragend, strnlen(fragend, sizeof(fragend)));
885
724
  html_fill_number(endHTML, Stream_GetPosition(s));
886
887
724
  void* res = Stream_Buffer(s);
888
724
  const size_t pos = Stream_GetPosition(s);
889
724
  *plen = WINPR_ASSERTING_INT_CAST(uint32_t, pos);
890
724
  Stream_Free(s, FALSE);
891
724
  free(b64);
892
724
  return res;
893
724
}
894
895
WINPR_ATTR_MALLOC(free, 1)
896
static void* clipboard_wrap_format_to_html(uint32_t bmpFormat, const char* idata, size_t ilength,
897
                                           uint32_t* plen)
898
0
{
899
0
  void* res = nullptr;
900
0
  wImage* img = winpr_image_new();
901
0
  if (!img)
902
0
    goto fail;
903
904
0
  if (winpr_image_read_buffer(img, (const BYTE*)idata, ilength) <= 0)
905
0
    goto fail;
906
907
0
  {
908
0
    size_t bmpsize = 0;
909
0
    void* bmp = winpr_image_write_buffer(img, bmpFormat, &bmpsize);
910
0
    if (!bmp)
911
0
      goto fail;
912
913
0
    res = clipboard_wrap_html(winpr_image_format_mime(bmpFormat), bmp, bmpsize, plen);
914
0
    free(bmp);
915
0
  }
916
0
fail:
917
0
  winpr_image_free(img, TRUE);
918
0
  return res;
919
0
}
920
921
WINPR_ATTR_MALLOC(free, 1)
922
static void* clipboard_wrap_bmp_to_ms_html(const char* idata, size_t ilength, uint32_t* plen)
923
724
{
924
724
  const uint32_t formats[] = { WINPR_IMAGE_WEBP, WINPR_IMAGE_PNG, WINPR_IMAGE_JPEG };
925
926
2.89k
  for (size_t x = 0; x < ARRAYSIZE(formats); x++)
927
2.17k
  {
928
2.17k
    const uint32_t format = formats[x];
929
2.17k
    if (winpr_image_format_is_supported(format))
930
0
    {
931
0
      return clipboard_wrap_format_to_html(format, idata, ilength, plen);
932
0
    }
933
2.17k
  }
934
724
  const uint32_t bmpFormat = WINPR_IMAGE_BITMAP;
935
724
  return clipboard_wrap_html(winpr_image_format_mime(bmpFormat), idata, ilength, plen);
936
724
}
937
938
WINPR_ATTR_MALLOC(free, 1)
939
static void* clipboard_synthesize_image_ms_html(WINPR_ATTR_UNUSED wClipboard* clipboard,
940
                                                UINT32 formatId, const void* data, UINT32* pSize)
941
1.37k
{
942
1.37k
  if (formatId != ClipboardGetFormatId(clipboard, mime_ms_html))
943
0
  {
944
0
    WLog_ERR(TAG,
945
0
             "Unuspported destination format %s [0x%04" PRIx32
946
0
             "], trying to convert from %s [0x%04" PRIx32 "]",
947
0
             ClipboardGetFormatName(clipboard, formatId), formatId,
948
0
             ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
949
0
    return nullptr;
950
0
  }
951
952
1.37k
  WINPR_ASSERT(pSize);
953
954
1.37k
  const size_t datalen = *pSize;
955
956
1.37k
  switch (clipboard->formatId)
957
1.37k
  {
958
0
    case CF_TIFF:
959
0
      return clipboard_wrap_html(mime_tiff, data, datalen, pSize);
960
1.01k
    case CF_DIB:
961
1.01k
    case CF_DIBV5:
962
1.01k
    {
963
1.01k
      uint32_t bmplen = *pSize;
964
1.01k
      void* bmp =
965
1.01k
          clipboard_synthesize_image_bmp(clipboard, clipboard->formatId, data, &bmplen);
966
1.01k
      if (!bmp)
967
294
      {
968
294
        WLog_WARN(TAG, "failed to convert formatId 0x%08" PRIx32 " [%s]",
969
294
                  clipboard->formatId,
970
294
                  ClipboardGetFormatName(clipboard, clipboard->formatId));
971
294
        *pSize = 0;
972
294
        return nullptr;
973
294
      }
974
975
724
      void* res = clipboard_wrap_bmp_to_ms_html(bmp, bmplen, pSize);
976
724
      free(bmp);
977
724
      return res;
978
1.01k
    }
979
360
    default:
980
360
    {
981
#if defined(WINPR_UTILS_IMAGE_WEBP)
982
      const uint32_t idWebp = ClipboardRegisterFormat(clipboard, mime_webp);
983
      if (clipboard->formatId == idWebp)
984
        return clipboard_wrap_html(mime_webp, data, datalen, pSize);
985
#endif
986
987
#if defined(WINPR_UTILS_IMAGE_PNG)
988
      const uint32_t idPng = ClipboardRegisterFormat(clipboard, mime_png);
989
      if (clipboard->formatId == idPng)
990
        return clipboard_wrap_html(mime_png, data, datalen, pSize);
991
#endif
992
#if defined(WINPR_UTILS_IMAGE_JPEG)
993
      const uint32_t idJpeg = ClipboardRegisterFormat(clipboard, mime_jpeg);
994
      if (clipboard->formatId == idJpeg)
995
        return clipboard_wrap_html(mime_jpeg, data, datalen, pSize);
996
#endif
997
998
360
      const uint32_t idTiff = ClipboardRegisterFormat(clipboard, mime_tiff);
999
360
      if (clipboard->formatId == idTiff)
1000
0
        return clipboard_wrap_html(mime_tiff, data, datalen, pSize);
1001
1002
1.80k
      for (size_t x = 0; x < ARRAYSIZE(mime_bitmap); x++)
1003
1.44k
      {
1004
1.44k
        const char* mime = mime_bitmap[x];
1005
1.44k
        const uint32_t id = ClipboardRegisterFormat(clipboard, mime);
1006
1007
1.44k
        if (formatId == id)
1008
0
          return clipboard_wrap_bmp_to_ms_html(data, datalen, pSize);
1009
1.44k
      }
1010
1011
360
      WLog_WARN(TAG, "Unsupported image format id 0x%08" PRIx32 " [%s]", clipboard->formatId,
1012
360
                ClipboardGetFormatName(clipboard, clipboard->formatId));
1013
360
      *pSize = 0;
1014
360
      return nullptr;
1015
360
    }
1016
1.37k
  }
1017
1.37k
}
1018
1019
/**
1020
 * "text/html":
1021
 *
1022
 * HTML text format.
1023
 */
1024
WINPR_ATTR_MALLOC(free, 1)
1025
static void* clipboard_synthesize_html_format(wClipboard* clipboard, UINT32 formatId,
1026
                                              const void* data, UINT32* pSize)
1027
375
{
1028
375
  if (formatId != ClipboardGetFormatId(clipboard, mime_html))
1029
0
  {
1030
0
    WLog_ERR(TAG,
1031
0
             "Unuspported destination format %s [0x%04" PRIx32
1032
0
             "], trying to convert from %s [0x%04" PRIx32 "]",
1033
0
             ClipboardGetFormatName(clipboard, formatId), formatId,
1034
0
             ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
1035
0
    return nullptr;
1036
0
  }
1037
375
  if (clipboard->formatId != ClipboardGetFormatId(clipboard, mime_ms_html))
1038
0
  {
1039
0
    WLog_ERR(TAG,
1040
0
             "Unuspported destination format %s [0x%04" PRIx32
1041
0
             "], trying to convert from %s [0x%04" PRIx32 "]",
1042
0
             ClipboardGetFormatName(clipboard, formatId), formatId,
1043
0
             ClipboardGetFormatName(clipboard, clipboard->formatId), clipboard->formatId);
1044
0
    return nullptr;
1045
0
  }
1046
1047
375
  char* pDstData = nullptr;
1048
1049
375
  if (formatId == ClipboardGetFormatId(clipboard, mime_html))
1050
375
  {
1051
375
    const char* str = (const char*)data;
1052
375
    const size_t SrcSize = *pSize;
1053
375
    const char* begStr = strstr(str, "StartHTML:");
1054
375
    const char* endStr = strstr(str, "EndHTML:");
1055
1056
375
    if (!begStr || !endStr)
1057
2
      return nullptr;
1058
1059
375
    errno = 0;
1060
373
    const long beg = strtol(&begStr[10], nullptr, 10);
1061
1062
373
    if (errno != 0)
1063
1
      return nullptr;
1064
1065
372
    const long end = strtol(&endStr[8], nullptr, 10);
1066
1067
372
    if ((beg < 0) || (end < 0) || ((size_t)beg > SrcSize) || ((size_t)end > SrcSize) ||
1068
30
        (beg >= end) || (errno != 0))
1069
360
      return nullptr;
1070
1071
12
    const size_t DstSize = (size_t)(end - beg);
1072
12
    pDstData = calloc(DstSize + 1, sizeof(char));
1073
1074
12
    if (!pDstData)
1075
0
      return nullptr;
1076
1077
12
    CopyMemory(pDstData, &str[beg], DstSize);
1078
12
    const size_t rc = ConvertLineEndingToLF(pDstData, DstSize);
1079
12
    WINPR_ASSERT(rc <= UINT32_MAX);
1080
12
    *pSize = (UINT32)rc;
1081
12
  }
1082
1083
12
  return pDstData;
1084
375
}
1085
1086
BOOL ClipboardInitSynthesizers(wClipboard* clipboard)
1087
4.17k
{
1088
4.17k
  WINPR_ASSERT(clipboard);
1089
1090
4.17k
  const UINT32 formatIdUtf = ClipboardRegisterFormat(clipboard, mime_text_utf8);
1091
4.17k
  const UINT32 formatIdUtf8String = ClipboardRegisterFormat(clipboard, mime_text_UTF8_STRING);
1092
4.17k
  const UINT32 formatIdPlain = ClipboardRegisterFormat(clipboard, mime_text_plain);
1093
4.17k
  const UINT32 textFormatIds[] = { CF_TEXT,     CF_OEMTEXT,         CF_UNICODETEXT,
1094
4.17k
                                 formatIdUtf, formatIdUtf8String, formatIdPlain };
1095
  /**
1096
   * CF_TEXT
1097
   */
1098
29.2k
  for (size_t x = 0; x < ARRAYSIZE(textFormatIds); x++)
1099
25.0k
  {
1100
25.0k
    const UINT32 formatId = textFormatIds[x];
1101
1102
175k
    for (size_t y = 0; y < ARRAYSIZE(textFormatIds); y++)
1103
150k
    {
1104
150k
      const UINT32 dstFormatId = textFormatIds[y];
1105
150k
      if (formatId == dstFormatId)
1106
25.0k
        continue;
1107
1108
125k
      if (!ClipboardRegisterSynthesizerEx(clipboard, formatId, dstFormatId,
1109
125k
                                          clipboard_synthesize_string))
1110
0
        return FALSE;
1111
125k
    }
1112
1113
25.0k
    if (!ClipboardRegisterSynthesizerEx(clipboard, formatId, CF_LOCALE,
1114
25.0k
                                        clipboard_synthesize_cf_locale))
1115
0
      return FALSE;
1116
25.0k
  }
1117
1118
4.17k
  const uint32_t msHtmlFormat = ClipboardRegisterFormat(clipboard, mime_ms_html);
1119
1120
  /**
1121
   * CF_TIFF
1122
   */
1123
4.17k
  if (!ClipboardRegisterSynthesizerEx(clipboard, CF_TIFF, msHtmlFormat,
1124
4.17k
                                      clipboard_synthesize_image_ms_html))
1125
0
    return FALSE;
1126
1127
  /**
1128
   * CF_DIB / CF_DIBv5
1129
   */
1130
4.17k
  if (!ClipboardRegisterSynthesizerEx(clipboard, CF_DIBV5, CF_DIB,
1131
4.17k
                                      clipboard_synthesize_image_dib_to_format))
1132
0
    return FALSE;
1133
4.17k
  if (!ClipboardRegisterSynthesizerEx(clipboard, CF_DIB, msHtmlFormat,
1134
4.17k
                                      clipboard_synthesize_image_ms_html))
1135
0
    return FALSE;
1136
1137
#if defined(WINPR_UTILS_IMAGE_DIBv5)
1138
  if (!ClipboardRegisterSynthesizerEx(clipboard, CF_DIBV5, CF_DIB,
1139
                                      clipboard_synthesize_image_dib_to_format))
1140
    return FALSE;
1141
  if (!ClipboardRegisterSynthesizerEx(clipboard, CF_DIBV5, msHtmlFormat,
1142
                                      clipboard_synthesize_image_ms_html))
1143
    return FALSE;
1144
#endif
1145
1146
  /**
1147
   * image/
1148
   */
1149
25.0k
  for (size_t x = 0; x < ARRAYSIZE(mime_images); x++)
1150
20.8k
  {
1151
20.8k
    const char* mime = mime_images[x];
1152
20.8k
    const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime);
1153
20.8k
    if (altFormatId == 0)
1154
0
      continue;
1155
20.8k
    if (!ClipboardRegisterSynthesizerEx(clipboard, CF_DIB, altFormatId,
1156
20.8k
                                        clipboard_synthesize_image_dib_to_format))
1157
0
      return FALSE;
1158
20.8k
    if (!ClipboardRegisterSynthesizerEx(clipboard, altFormatId, CF_DIB,
1159
20.8k
                                        clipboard_synthesize_image_format_to_cf_dib))
1160
0
      return FALSE;
1161
20.8k
    if (!ClipboardRegisterSynthesizerEx(clipboard, altFormatId, msHtmlFormat,
1162
20.8k
                                        clipboard_synthesize_image_ms_html))
1163
0
      return FALSE;
1164
#if defined(WINPR_UTILS_IMAGE_DIBv5)
1165
    if (!ClipboardRegisterSynthesizerEx(clipboard, CF_DIBV5, altFormatId,
1166
                                        clipboard_synthesize_image_dib_to_format))
1167
      return FALSE;
1168
    if (!ClipboardRegisterSynthesizerEx(clipboard, altFormatId, CF_DIBV5,
1169
                                        clipboard_synthesize_image_format_to_cf_dib))
1170
      return FALSE;
1171
#endif
1172
20.8k
  }
1173
1174
25.0k
  for (size_t x = 0; x < ARRAYSIZE(mime_images); x++)
1175
20.8k
  {
1176
20.8k
    const char* mime = mime_images[x];
1177
20.8k
    if (strcmp(mime, mime_tiff) == 0) /* WinPR can not read or write TIFF */
1178
4.17k
      continue;
1179
16.7k
    const UINT32 formatId = ClipboardRegisterFormat(clipboard, mime);
1180
100k
    for (size_t y = 0; y < ARRAYSIZE(mime_images); y++)
1181
83.5k
    {
1182
83.5k
      const char* altMime = mime_images[y];
1183
83.5k
      if ((x == y) || (strcmp(altMime, mime_tiff) == 0))
1184
33.4k
        continue;
1185
50.1k
      const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, altMime);
1186
50.1k
      if ((formatId == 0) || (altFormatId == 0))
1187
0
        continue;
1188
50.1k
      if (!ClipboardRegisterSynthesizerEx(clipboard, formatId, altFormatId,
1189
50.1k
                                          clipboard_synthesize_image_format_to_format))
1190
0
        return FALSE;
1191
50.1k
    }
1192
16.7k
  }
1193
1194
  /**
1195
   * HTML Format
1196
   */
1197
4.17k
  if (msHtmlFormat)
1198
4.17k
  {
1199
4.17k
    const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_html);
1200
4.17k
    if (!ClipboardRegisterSynthesizerEx(clipboard, msHtmlFormat, altFormatId,
1201
4.17k
                                        clipboard_synthesize_html_format))
1202
0
      return FALSE;
1203
4.17k
  }
1204
1205
  /**
1206
   * text/html
1207
   */
1208
4.17k
  {
1209
4.17k
    UINT32 formatId = ClipboardRegisterFormat(clipboard, mime_html);
1210
1211
4.17k
    if (formatId)
1212
4.17k
    {
1213
4.17k
      const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_ms_html);
1214
4.17k
      if (!ClipboardRegisterSynthesizerEx(clipboard, formatId, altFormatId,
1215
4.17k
                                          clipboard_synthesize_ms_html_format))
1216
0
        return FALSE;
1217
4.17k
    }
1218
4.17k
  }
1219
1220
4.17k
  return TRUE;
1221
4.17k
}