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