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