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