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 NULL;
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 = NULL;
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 NULL;
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 NULL;
155 }
156
157 *pSize = (UINT32)size;
158 return pDstData;
159 }
160
161 return NULL;
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 = NULL;
188 pDstData = (UINT32*)malloc(sizeof(UINT32));
189
190 if (!pDstData)
191 return NULL;
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 = NULL;
208 WCHAR* pDstData = NULL;
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 NULL;
216
217 size = *pSize;
218 crlfStr = ConvertLineEndingToCRLF((const char*)data, &size);
219
220 if (!crlfStr)
221 return NULL;
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 NULL;
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 NULL;
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 NULL;
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 NULL;
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 = NULL;
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 = { 0 };
318 wStream sbuffer = { 0 };
319 wStream* s = Stream_StaticConstInit(&sbuffer, data, SrcSize);
320 if (!readBitmapFileHeader(s, &pFileHeader))
321 return NULL;
322
323 DstSize = SrcSize - sizeof(BITMAPFILEHEADER);
324 pDstData = (BYTE*)malloc(DstSize);
325
326 if (!pDstData)
327 return NULL;
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 NULL;
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 NULL;
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 NULL;
402
403 const size_t DstSize = sizeof(WINPR_BITMAP_FILE_HEADER) + size;
404 if (DstSize > UINT32_MAX)
405 return NULL;
406
407 wStream* s = Stream_New(NULL, DstSize);
408 if (!s)
409 return NULL;
410
411 WINPR_BITMAP_FILE_HEADER fileHeader = { 0 };
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 const size_t len = Stream_GetPosition(s);
423 if (len != DstSize)
424 goto fail;
425 *pSize = (UINT32)DstSize;
426
427 BYTE* dst = Stream_Buffer(s);
428 Stream_Free(s, FALSE);
429 return dst;
430
431fail:
432 Stream_Free(s, TRUE);
433 return NULL;
434}
435
442static void* clipboard_synthesize_image_bmp(WINPR_ATTR_UNUSED wClipboard* clipboard,
443 UINT32 formatId, const void* data, UINT32* pSize)
444{
445 UINT32 SrcSize = *pSize;
446
447 if (formatId == CF_DIB)
448 {
449 if (SrcSize < sizeof(BITMAPINFOHEADER))
450 return NULL;
451
452 wStream sbuffer = { 0 };
453 size_t offset = 0;
454 WINPR_BITMAP_INFO_HEADER header = { 0 };
455 wStream* s = Stream_StaticConstInit(&sbuffer, data, SrcSize);
456 if (!readBitmapInfoHeader(s, &header, &offset))
457 return NULL;
458
459 return clipboard_prepend_bmp_header(&header, data, SrcSize, pSize);
460 }
461#if defined(WINPR_UTILS_IMAGE_DIBv5)
462 else if (formatId == CF_DIBV5)
463 {
464 WLog_WARN(TAG, "[BMP] Unsupported destination format %s",
465 ClipboardGetFormatName(clipboard, formatId));
466 }
467#endif
468 else
469 {
470 WLog_WARN(TAG, "[BMP] Unsupported destination format %s",
471 ClipboardGetFormatName(clipboard, formatId));
472 }
473
474 return NULL;
475}
476
477#if defined(WINPR_UTILS_IMAGE_PNG) || defined(WINPR_UTILS_IMAGE_WEBP) || \
478 defined(WINPR_UTILS_IMAGE_JPEG)
479static void* clipboard_synthesize_image_bmp_to_format(wClipboard* clipboard, UINT32 formatId,
480 UINT32 bmpFormat, const void* data,
481 UINT32* pSize)
482{
483 WINPR_ASSERT(clipboard);
484 WINPR_ASSERT(data);
485 WINPR_ASSERT(pSize);
486
487 size_t dsize = 0;
488 void* result = NULL;
489
490 wImage* img = winpr_image_new();
491 void* bmp = clipboard_synthesize_image_bmp(clipboard, formatId, data, pSize);
492 const UINT32 SrcSize = *pSize;
493 *pSize = 0;
494
495 if (!bmp || !img)
496 goto fail;
497
498 if (winpr_image_read_buffer(img, bmp, SrcSize) <= 0)
499 goto fail;
500
501 result = winpr_image_write_buffer(img, bmpFormat, &dsize);
502 if (result)
503 {
504 if (dsize <= UINT32_MAX)
505 *pSize = (UINT32)dsize;
506 else
507 {
508 free(result);
509 result = NULL;
510 }
511 }
512
513fail:
514 free(bmp);
515 winpr_image_free(img, TRUE);
516 return result;
517}
518#endif
519
520#if defined(WINPR_UTILS_IMAGE_PNG)
521static void* clipboard_synthesize_image_bmp_to_png(wClipboard* clipboard, UINT32 formatId,
522 const void* data, UINT32* pSize)
523{
524 return clipboard_synthesize_image_bmp_to_format(clipboard, formatId, WINPR_IMAGE_PNG, data,
525 pSize);
526}
527#endif
528
529#if defined(WINPR_UTILS_IMAGE_PNG) || defined(WINPR_UTILS_IMAGE_WEBP) || \
530 defined(WINPR_UTILS_IMAGE_JPEG)
531static void* clipboard_synthesize_image_format_to_bmp(wClipboard* clipboard,
532 WINPR_ATTR_UNUSED UINT32 srcFormatId,
533 const void* data, UINT32* pSize)
534{
535 WINPR_ASSERT(clipboard);
536 WINPR_ASSERT(data);
537 WINPR_ASSERT(pSize);
538
539 BYTE* dst = NULL;
540 const UINT32 SrcSize = *pSize;
541 size_t size = 0;
542 wImage* image = winpr_image_new();
543 if (!image)
544 goto fail;
545
546 const int res = winpr_image_read_buffer(image, data, SrcSize);
547 if (res <= 0)
548 goto fail;
549
550 dst = winpr_image_write_buffer(image, WINPR_IMAGE_BITMAP, &size);
551 if ((size < sizeof(WINPR_BITMAP_FILE_HEADER)) || (size > UINT32_MAX))
552 {
553 free(dst);
554 dst = NULL;
555 goto fail;
556 }
557 *pSize = (UINT32)size;
558
559fail:
560 winpr_image_free(image, TRUE);
561
562 if (dst)
563 memmove(dst, &dst[sizeof(WINPR_BITMAP_FILE_HEADER)],
564 size - sizeof(WINPR_BITMAP_FILE_HEADER));
565 return dst;
566}
567#endif
568
569#if defined(WINPR_UTILS_IMAGE_PNG)
570static void* clipboard_synthesize_image_png_to_bmp(wClipboard* clipboard, UINT32 formatId,
571 const void* data, UINT32* pSize)
572{
573 return clipboard_synthesize_image_format_to_bmp(clipboard, formatId, data, pSize);
574}
575#endif
576
577#if defined(WINPR_UTILS_IMAGE_WEBP)
578static void* clipboard_synthesize_image_bmp_to_webp(wClipboard* clipboard, UINT32 formatId,
579 const void* data, UINT32* pSize)
580{
581 return clipboard_synthesize_image_bmp_to_format(clipboard, formatId, WINPR_IMAGE_WEBP, data,
582 pSize);
583}
584
585static void* clipboard_synthesize_image_webp_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_JPEG)
593static void* clipboard_synthesize_image_bmp_to_jpeg(wClipboard* clipboard, UINT32 formatId,
594 const void* data, UINT32* pSize)
595{
596 return clipboard_synthesize_image_bmp_to_format(clipboard, formatId, WINPR_IMAGE_JPEG, data,
597 pSize);
598}
599
600static void* clipboard_synthesize_image_jpeg_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
613static void* clipboard_synthesize_html_format(wClipboard* clipboard, UINT32 formatId,
614 const void* pData, UINT32* pSize)
615{
616 union
617 {
618 const void* cpv;
619 const char* cpc;
620 const BYTE* cpb;
621 WCHAR* pv;
622 } pSrcData;
623 char* pDstData = NULL;
624
625 pSrcData.cpv = NULL;
626
627 WINPR_ASSERT(clipboard);
628 WINPR_ASSERT(pSize);
629
630 if (formatId == ClipboardGetFormatId(clipboard, mime_html))
631 {
632 const size_t SrcSize = (size_t)*pSize;
633 const size_t DstSize = SrcSize + 200;
634 char* body = NULL;
635 char num[20] = { 0 };
636
637 /* Create a copy, we modify the input data */
638 pSrcData.pv = calloc(1, SrcSize + 1);
639 if (!pSrcData.pv)
640 goto fail;
641 memcpy(pSrcData.pv, pData, SrcSize);
642
643 if (SrcSize > 2)
644 {
645 if (SrcSize > INT_MAX)
646 goto fail;
647
648 /* Check the BOM (Byte Order Mark) */
649 if ((pSrcData.cpb[0] == 0xFE) && (pSrcData.cpb[1] == 0xFF))
650 ByteSwapUnicode(pSrcData.pv, (SrcSize / 2));
651
652 /* Check if we have WCHAR, convert to UTF-8 */
653 if ((pSrcData.cpb[0] == 0xFF) && (pSrcData.cpb[1] == 0xFE))
654 {
655 char* utfString =
656 ConvertWCharNToUtf8Alloc(&pSrcData.pv[1], SrcSize / sizeof(WCHAR), NULL);
657 free(pSrcData.pv);
658 pSrcData.cpc = utfString;
659 if (!utfString)
660 goto fail;
661 }
662 }
663
664 pDstData = (char*)calloc(1, DstSize);
665
666 if (!pDstData)
667 goto fail;
668
669 (void)sprintf_s(pDstData, DstSize,
670 "Version:0.9\r\n"
671 "StartHTML:0000000000\r\n"
672 "EndHTML:0000000000\r\n"
673 "StartFragment:0000000000\r\n"
674 "EndFragment:0000000000\r\n");
675 body = strstr(pSrcData.cpc, "<body");
676
677 if (!body)
678 body = strstr(pSrcData.cpc, "<BODY");
679
680 /* StartHTML */
681 (void)sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, DstSize));
682 CopyMemory(&pDstData[23], num, 10);
683
684 if (!body)
685 {
686 if (!winpr_str_append("<HTML><BODY>", pDstData, DstSize, NULL))
687 goto fail;
688 }
689
690 if (!winpr_str_append("<!--StartFragment-->", pDstData, DstSize, NULL))
691 goto fail;
692
693 /* StartFragment */
694 (void)sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, SrcSize + 200));
695 CopyMemory(&pDstData[69], num, 10);
696
697 if (!winpr_str_append(pSrcData.cpc, pDstData, DstSize, NULL))
698 goto fail;
699
700 /* EndFragment */
701 (void)sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, SrcSize + 200));
702 CopyMemory(&pDstData[93], num, 10);
703
704 if (!winpr_str_append("<!--EndFragment-->", pDstData, DstSize, NULL))
705 goto fail;
706
707 if (!body)
708 {
709 if (!winpr_str_append("</BODY></HTML>", pDstData, DstSize, NULL))
710 goto fail;
711 }
712
713 /* EndHTML */
714 (void)sprintf_s(num, sizeof(num), "%010" PRIuz "", strnlen(pDstData, DstSize));
715 CopyMemory(&pDstData[43], num, 10);
716 *pSize = (UINT32)strnlen(pDstData, DstSize) + 1;
717 }
718fail:
719 free(pSrcData.pv);
720 return pDstData;
721}
722
723static char* html_pre_write(wStream* s, const char* what)
724{
725 const size_t len = strlen(what);
726 Stream_Write(s, what, len);
727 char* startHTML = Stream_PointerAs(s, char);
728 for (size_t x = 0; x < 10; x++)
729 Stream_Write_INT8(s, '0');
730 Stream_Write(s, "\r\n", 2);
731 return startHTML;
732}
733
734static void html_fill_number(char* pos, size_t val)
735{
736 char str[11] = { 0 };
737 (void)_snprintf(str, sizeof(str), "%010" PRIuz, val);
738 memcpy(pos, str, 10);
739}
740
741static void* clipboard_wrap_html(const char* mime, const char* idata, size_t ilength,
742 uint32_t* plen)
743{
744 WINPR_ASSERT(mime);
745 WINPR_ASSERT(plen);
746
747 *plen = 0;
748
749 size_t b64len = 0;
750 char* b64 = b64_encode((const BYTE*)idata, ilength, &b64len);
751 if (!b64)
752 return NULL;
753
754 const size_t mimelen = strlen(mime);
755 wStream* s = Stream_New(NULL, b64len + 225 + mimelen);
756 if (!s)
757 {
758 free(b64);
759 return NULL;
760 }
761
762 char* startHTML = html_pre_write(s, "Version:0.9\r\nStartHTML:");
763 char* endHTML = html_pre_write(s, "EndHTML:");
764 char* startFragment = html_pre_write(s, "StartFragment:");
765 char* endFragment = html_pre_write(s, "EndFragment:");
766
767 html_fill_number(startHTML, Stream_GetPosition(s));
768 const char html[] = "<html><!--StartFragment-->";
769 Stream_Write(s, html, strnlen(html, sizeof(html)));
770
771 html_fill_number(startFragment, Stream_GetPosition(s));
772
773 const char body[] = "<body><img alt=\"FreeRDP clipboard image\" src=\"data:";
774 Stream_Write(s, body, strnlen(body, sizeof(body)));
775
776 Stream_Write(s, mime, mimelen);
777
778 const char base64[] = ";base64,";
779 Stream_Write(s, base64, strnlen(base64, sizeof(base64)));
780 Stream_Write(s, b64, b64len);
781
782 const char end[] = "\"/></body>";
783 Stream_Write(s, end, strnlen(end, sizeof(end)));
784
785 html_fill_number(endFragment, Stream_GetPosition(s));
786
787 const char fragend[] = "<!--EndFragment--></html>";
788 Stream_Write(s, fragend, strnlen(fragend, sizeof(fragend)));
789 html_fill_number(endHTML, Stream_GetPosition(s));
790
791 void* res = Stream_Buffer(s);
792 const size_t pos = Stream_GetPosition(s);
793 *plen = WINPR_ASSERTING_INT_CAST(uint32_t, pos);
794 Stream_Free(s, FALSE);
795 free(b64);
796 return res;
797}
798
799static void* clipboard_wrap_format_to_html(uint32_t bmpFormat, const char* idata, size_t ilength,
800 uint32_t* plen)
801{
802 void* res = NULL;
803 wImage* img = winpr_image_new();
804 if (!img)
805 goto fail;
806
807 if (winpr_image_read_buffer(img, (const BYTE*)idata, ilength) <= 0)
808 goto fail;
809
810 size_t bmpsize = 0;
811 void* bmp = winpr_image_write_buffer(img, bmpFormat, &bmpsize);
812 if (!bmp)
813 goto fail;
814
815 res = clipboard_wrap_html(winpr_image_format_mime(bmpFormat), bmp, bmpsize, plen);
816 free(bmp);
817
818fail:
819 winpr_image_free(img, TRUE);
820 return res;
821}
822
823static void* clipboard_wrap_bmp_to_html(const char* idata, size_t ilength, uint32_t* plen)
824{
825 const uint32_t formats[] = { WINPR_IMAGE_WEBP, WINPR_IMAGE_PNG, WINPR_IMAGE_JPEG };
826
827 for (size_t x = 0; x < ARRAYSIZE(formats); x++)
828 {
829 const uint32_t format = formats[x];
830 if (winpr_image_format_is_supported(format))
831 {
832 return clipboard_wrap_format_to_html(format, idata, ilength, plen);
833 }
834 }
835 const uint32_t bmpFormat = WINPR_IMAGE_BITMAP;
836 return clipboard_wrap_html(winpr_image_format_mime(bmpFormat), idata, ilength, plen);
837}
838
839static void* clipboard_synthesize_image_html(WINPR_ATTR_UNUSED wClipboard* clipboard,
840 UINT32 formatId, const void* data, UINT32* pSize)
841{
842 WINPR_ASSERT(pSize);
843
844 const size_t datalen = *pSize;
845
846 switch (formatId)
847 {
848 case CF_TIFF:
849 return clipboard_wrap_html(mime_tiff, data, datalen, pSize);
850 case CF_DIB:
851 case CF_DIBV5:
852 {
853 uint32_t bmplen = *pSize;
854 void* bmp = clipboard_synthesize_image_bmp(clipboard, formatId, data, &bmplen);
855 if (!bmp)
856 {
857 WLog_WARN(TAG, "failed to convert formatId 0x%08" PRIx32 " [%s]", formatId,
858 ClipboardGetFormatName(clipboard, formatId));
859 *pSize = 0;
860 return NULL;
861 }
862
863 void* res = clipboard_wrap_bmp_to_html(bmp, bmplen, pSize);
864 free(bmp);
865 return res;
866 }
867 default:
868 {
869 const uint32_t idWebp = ClipboardRegisterFormat(clipboard, mime_webp);
870 const uint32_t idPng = ClipboardRegisterFormat(clipboard, mime_png);
871 const uint32_t idJpeg = ClipboardRegisterFormat(clipboard, mime_jpeg);
872 const uint32_t idTiff = ClipboardRegisterFormat(clipboard, mime_tiff);
873 if (formatId == idWebp)
874 {
875 return clipboard_wrap_html(mime_webp, data, datalen, pSize);
876 }
877 else if (formatId == idPng)
878 {
879 return clipboard_wrap_html(mime_png, data, datalen, pSize);
880 }
881 else if (formatId == idJpeg)
882 {
883 return clipboard_wrap_html(mime_jpeg, data, datalen, pSize);
884 }
885 else if (formatId == idTiff)
886 {
887 return clipboard_wrap_html(mime_tiff, data, datalen, pSize);
888 }
889 else
890 {
891 for (size_t x = 0; x < ARRAYSIZE(mime_bitmap); x++)
892 {
893 const char* mime = mime_bitmap[x];
894 const uint32_t id = ClipboardRegisterFormat(clipboard, mime);
895
896 if (formatId == id)
897 return clipboard_wrap_bmp_to_html(data, datalen, pSize);
898 }
899 }
900
901 WLog_WARN(TAG, "Unsupported image format id 0x%08" PRIx32 " [%s]", formatId,
902 ClipboardGetFormatName(clipboard, formatId));
903 *pSize = 0;
904 return NULL;
905 }
906 }
907}
908
915static void* clipboard_synthesize_text_html(wClipboard* clipboard, UINT32 formatId,
916 const void* data, UINT32* pSize)
917{
918 char* pDstData = NULL;
919
920 if (formatId == ClipboardGetFormatId(clipboard, mime_ms_html))
921 {
922 const char* str = (const char*)data;
923 const size_t SrcSize = *pSize;
924 const char* begStr = strstr(str, "StartHTML:");
925 const char* endStr = strstr(str, "EndHTML:");
926
927 if (!begStr || !endStr)
928 return NULL;
929
930 errno = 0;
931 const long beg = strtol(&begStr[10], NULL, 10);
932
933 if (errno != 0)
934 return NULL;
935
936 const long end = strtol(&endStr[8], NULL, 10);
937
938 if ((beg < 0) || (end < 0) || ((size_t)beg > SrcSize) || ((size_t)end > SrcSize) ||
939 (beg >= end) || (errno != 0))
940 return NULL;
941
942 const size_t DstSize = (size_t)(end - beg);
943 pDstData = calloc(DstSize + 1, sizeof(char));
944
945 if (!pDstData)
946 return NULL;
947
948 CopyMemory(pDstData, &str[beg], DstSize);
949 const size_t rc = ConvertLineEndingToLF(pDstData, DstSize);
950 WINPR_ASSERT(rc <= UINT32_MAX);
951 *pSize = (UINT32)rc;
952 }
953
954 return pDstData;
955}
956
957BOOL ClipboardInitSynthesizers(wClipboard* clipboard)
958{
962 {
963 ClipboardRegisterSynthesizer(clipboard, CF_TEXT, CF_OEMTEXT,
964 clipboard_synthesize_cf_oemtext);
965 ClipboardRegisterSynthesizer(clipboard, CF_TEXT, CF_UNICODETEXT,
966 clipboard_synthesize_cf_unicodetext);
967 ClipboardRegisterSynthesizer(clipboard, CF_TEXT, CF_LOCALE, clipboard_synthesize_cf_locale);
968
969 UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_text_plain);
970 ClipboardRegisterSynthesizer(clipboard, CF_TEXT, altFormatId,
971 clipboard_synthesize_utf8_string);
972 }
976 {
977 ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, CF_TEXT, clipboard_synthesize_cf_text);
978 ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, CF_UNICODETEXT,
979 clipboard_synthesize_cf_unicodetext);
980 ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, CF_LOCALE,
981 clipboard_synthesize_cf_locale);
982 UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_text_plain);
983 ClipboardRegisterSynthesizer(clipboard, CF_OEMTEXT, altFormatId,
984 clipboard_synthesize_utf8_string);
985 }
989 {
990 ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, CF_TEXT,
991 clipboard_synthesize_cf_text);
992 ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, CF_OEMTEXT,
993 clipboard_synthesize_cf_oemtext);
994 ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, CF_LOCALE,
995 clipboard_synthesize_cf_locale);
996 UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_text_plain);
997 ClipboardRegisterSynthesizer(clipboard, CF_UNICODETEXT, altFormatId,
998 clipboard_synthesize_utf8_string);
999 }
1003 {
1004 UINT32 formatId = ClipboardRegisterFormat(clipboard, mime_text_plain);
1005
1006 if (formatId)
1007 {
1008 ClipboardRegisterSynthesizer(clipboard, formatId, CF_TEXT,
1009 clipboard_synthesize_cf_text);
1010 ClipboardRegisterSynthesizer(clipboard, formatId, CF_OEMTEXT,
1011 clipboard_synthesize_cf_oemtext);
1012 ClipboardRegisterSynthesizer(clipboard, formatId, CF_UNICODETEXT,
1013 clipboard_synthesize_cf_unicodetext);
1014 ClipboardRegisterSynthesizer(clipboard, formatId, CF_LOCALE,
1015 clipboard_synthesize_cf_locale);
1016 }
1017 }
1021 {
1022 UINT32 formatId = ClipboardRegisterFormat(clipboard, mime_text_plain);
1023
1024 if (formatId)
1025 {
1026 ClipboardRegisterSynthesizer(clipboard, formatId, CF_TEXT,
1027 clipboard_synthesize_cf_text);
1028 ClipboardRegisterSynthesizer(clipboard, formatId, CF_OEMTEXT,
1029 clipboard_synthesize_cf_oemtext);
1030 ClipboardRegisterSynthesizer(clipboard, formatId, CF_UNICODETEXT,
1031 clipboard_synthesize_cf_unicodetext);
1032 ClipboardRegisterSynthesizer(clipboard, formatId, CF_LOCALE,
1033 clipboard_synthesize_cf_locale);
1034 }
1035 }
1036
1037 const uint32_t htmlFormat = ClipboardRegisterFormat(clipboard, mime_ms_html);
1038 const uint32_t tiffFormat = ClipboardRegisterFormat(clipboard, mime_tiff);
1039
1043 ClipboardRegisterSynthesizer(clipboard, CF_TIFF, htmlFormat, clipboard_synthesize_image_html);
1044 ClipboardRegisterSynthesizer(clipboard, tiffFormat, htmlFormat,
1045 clipboard_synthesize_image_html);
1046
1050 {
1051#if defined(WINPR_UTILS_IMAGE_DIBv5)
1052 ClipboardRegisterSynthesizer(clipboard, CF_DIB, CF_DIBV5, clipboard_synthesize_cf_dibv5);
1053#endif
1054 for (size_t x = 0; x < ARRAYSIZE(mime_bitmap); x++)
1055 {
1056 const char* mime = mime_bitmap[x];
1057 const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime);
1058 if (altFormatId == 0)
1059 continue;
1060 ClipboardRegisterSynthesizer(clipboard, CF_DIB, altFormatId,
1061 clipboard_synthesize_image_bmp);
1062 }
1063 ClipboardRegisterSynthesizer(clipboard, CF_DIB, htmlFormat,
1064 clipboard_synthesize_image_html);
1065 }
1066
1070#if defined(WINPR_UTILS_IMAGE_DIBv5)
1071 {
1072 ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, CF_DIB, clipboard_synthesize_cf_dib);
1073
1074 for (size_t x = 0; x < ARRAYSIZE(mime_bitmap); x++)
1075 {
1076 const char* mime = mime_bitmap[x];
1077 const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime);
1078 if (altFormatId == 0)
1079 continue;
1080 ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, altFormatId,
1081 clipboard_synthesize_image_bmp);
1082 }
1083 ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, htmlFormat,
1084 clipboard_synthesize_image_html);
1085 }
1086#endif
1087
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 ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIB, clipboard_synthesize_cf_dib);
1098#if defined(WINPR_UTILS_IMAGE_DIBv5)
1099 ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIBV5,
1100 clipboard_synthesize_cf_dibv5);
1101#endif
1102 ClipboardRegisterSynthesizer(clipboard, altFormatId, htmlFormat,
1103 clipboard_synthesize_image_html);
1104 }
1105
1109#if defined(WINPR_UTILS_IMAGE_PNG)
1110 {
1111 const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_png);
1112 ClipboardRegisterSynthesizer(clipboard, CF_DIB, altFormatId,
1113 clipboard_synthesize_image_bmp_to_png);
1114 ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIB,
1115 clipboard_synthesize_image_png_to_bmp);
1116 ClipboardRegisterSynthesizer(clipboard, altFormatId, htmlFormat,
1117 clipboard_synthesize_image_html);
1118#if defined(WINPR_UTILS_IMAGE_DIBv5)
1119 ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, altFormatId,
1120 clipboard_synthesize_image_bmp_to_png);
1121 ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIBV5,
1122 clipboard_synthesize_image_png_to_bmp);
1123#endif
1124 }
1125#endif
1126
1130#if defined(WINPR_UTILS_IMAGE_WEBP)
1131 {
1132 const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_webp);
1133 ClipboardRegisterSynthesizer(clipboard, CF_DIB, altFormatId,
1134 clipboard_synthesize_image_bmp_to_webp);
1135 ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIB,
1136 clipboard_synthesize_image_webp_to_bmp);
1137 ClipboardRegisterSynthesizer(clipboard, altFormatId, htmlFormat,
1138 clipboard_synthesize_image_html);
1139#if defined(WINPR_UTILS_IMAGE_DIBv5)
1140 ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, altFormatId,
1141 clipboard_synthesize_image_bmp_to_webp);
1142 ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIBV5,
1143 clipboard_synthesize_image_webp_to_bmp);
1144#endif
1145 }
1146#endif
1147
1151#if defined(WINPR_UTILS_IMAGE_JPEG)
1152 {
1153 const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_jpeg);
1154 ClipboardRegisterSynthesizer(clipboard, CF_DIB, altFormatId,
1155 clipboard_synthesize_image_bmp_to_jpeg);
1156 ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIB,
1157 clipboard_synthesize_image_jpeg_to_bmp);
1158 ClipboardRegisterSynthesizer(clipboard, altFormatId, htmlFormat,
1159 clipboard_synthesize_image_html);
1160#if defined(WINPR_UTILS_IMAGE_DIBv5)
1161 ClipboardRegisterSynthesizer(clipboard, altFormatId, CF_DIBV5,
1162 clipboard_synthesize_image_jpeg_to_bmp);
1163 ClipboardRegisterSynthesizer(clipboard, CF_DIBV5, altFormatId,
1164 clipboard_synthesize_image_bmp_to_jpeg);
1165#endif
1166 }
1167#endif
1168
1172 {
1173 UINT32 formatId = ClipboardRegisterFormat(clipboard, mime_ms_html);
1174
1175 if (formatId)
1176 {
1177 const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_html);
1178 ClipboardRegisterSynthesizer(clipboard, formatId, altFormatId,
1179 clipboard_synthesize_text_html);
1180 }
1181 }
1182
1186 {
1187 UINT32 formatId = ClipboardRegisterFormat(clipboard, mime_html);
1188
1189 if (formatId)
1190 {
1191 const UINT32 altFormatId = ClipboardRegisterFormat(clipboard, mime_ms_html);
1192 ClipboardRegisterSynthesizer(clipboard, formatId, altFormatId,
1193 clipboard_synthesize_html_format);
1194 }
1195 }
1196
1197 return TRUE;
1198}