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