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