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