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