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