FreeRDP
Loading...
Searching...
No Matches
TestClipboardFormats.c
1
2#include <winpr/crt.h>
3#include <winpr/print.h>
4#include <winpr/image.h>
5#include <winpr/clipboard.h>
6#include <winpr/stream.h>
7#include <winpr/user.h>
8#include <winpr/file.h>
9#include <winpr/shell.h>
10#include <winpr/path.h>
11
12#define test_log(fmt, ...) test_log_fn(__FILE__, __func__, __LINE__, (fmt), ##__VA_ARGS__)
13static void test_log_fn(const char* file, const char* func, size_t line, const char* fmt, ...)
14{
15 va_list ap;
16 va_start(ap, fmt);
17
18 char* fstr = nullptr;
19 size_t fslen = 0;
20 winpr_vasprintf(&fstr, &fslen, fmt, ap);
21 (void)fprintf(stderr, "[%" PRIu32 ":%s]: %s\n", line, func, fstr);
22 free(fstr);
23 va_end(ap);
24}
25
26#define test_ClipboardSetData(clipboard, formatId, data, size) \
27 test_ClipboardSetDataFn(__FILE__, __func__, __LINE__, (clipboard), (formatId), (data), (size))
28
29WINPR_ATTR_NODISCARD
30static BOOL test_ClipboardSetDataFn(const char* file, const char* func, size_t line,
31 wClipboard* clipboard, UINT32 formatId, const void* data,
32 UINT32 size)
33{
34 const BOOL rc = ClipboardSetData(clipboard, formatId, data, size);
35 if (!rc)
36 {
37 test_log_fn(file, func, line, "ClipboardSetData %s[0x%04" PRIx32 "][%" PRIu32 "] failed",
38 ClipboardGetFormatName(clipboard, formatId), formatId, size);
39 }
40 return rc;
41}
42
43#define test_ClipboardGetData(clipboard, formatId, size) \
44 test_ClipboardGetDataFn(__FILE__, __func__, __LINE__, (clipboard), (formatId), (size))
45
46WINPR_ATTR_NODISCARD
47static void* test_ClipboardGetDataFn(const char* file, const char* func, size_t line,
48 wClipboard* clipboard, UINT32 formatId, UINT32* pSize)
49{
50 void* rc = ClipboardGetData(clipboard, formatId, pSize);
51 if (!rc)
52 {
53 test_log_fn(file, func, line, "ClipboardGetData %s[0x%04" PRIx32 "] failed",
54 ClipboardGetFormatName(clipboard, formatId), formatId);
55 }
56 return rc;
57}
58
59WINPR_ATTR_NODISCARD
60static BOOL test_dib_to_bmp(const BYTE* dib, size_t dibSize, size_t expectedOffset)
61{
62 BOOL rc = FALSE;
63
64 wClipboard* clipboard = ClipboardCreate();
65 if (!clipboard)
66 {
67 test_log("ClipboardCreate failed");
68 return FALSE;
69 }
70
71 const UINT32 bmpId = ClipboardRegisterFormat(clipboard, "image/bmp");
72 if (bmpId == 0)
73 {
74 test_log("bmpId == 0");
75 goto fail;
76 }
77 if (dibSize > UINT32_MAX)
78 {
79 test_log("dibSize %" PRIuz " > UINT32_MAX failed");
80 goto fail;
81 }
82 if (!test_ClipboardSetData(clipboard, CF_DIB, dib, (UINT32)dibSize))
83 goto fail;
84
85 UINT32 bmpSize = 0;
86 BYTE* bmp = test_ClipboardGetData(clipboard, bmpId, &bmpSize);
87 if (!bmp)
88 goto fail;
89
90 /* The synthesized BMP must also be readable by the image conversion code. */
91 wImage* image = winpr_image_new();
92 if (!image)
93 {
94 test_log("winpr_image_new failed");
95 goto fail_bmp;
96 }
97 rc = (winpr_image_read_buffer(image, bmp, bmpSize) > 0);
98 if (!rc)
99 test_log("winpr_image_read_buffer failed");
100 winpr_image_free(image, TRUE);
101
102fail_bmp:
103 free(bmp);
104fail:
105 ClipboardDestroy(clipboard);
106 return rc;
107}
108
109static void write_dib_info_header(wStream* s, UINT32 size, UINT16 bpp, UINT32 compression)
110{
111 WINPR_ASSERT(s);
112
113 Stream_Write_UINT32(s, size);
114 Stream_Write_INT32(s, 1); /* width */
115 Stream_Write_INT32(s, 1); /* height */
116 Stream_Write_UINT16(s, 1); /* planes */
117 Stream_Write_UINT16(s, bpp);
118 Stream_Write_UINT32(s, compression);
119 Stream_Write_UINT32(s, 4); /* image size */
120 Stream_Zero(s, 16); /* resolution and palette metadata */
121}
122
123WINPR_ATTR_NODISCARD
124static BOOL test_dib_offsets(void)
125{
126 BYTE v4[sizeof(BITMAPV4HEADER) + 4] = WINPR_C_ARRAY_INIT;
127 wStream sbuffer = WINPR_C_ARRAY_INIT;
128 wStream* s = Stream_StaticInit(&sbuffer, v4, sizeof(v4));
129 if (!s)
130 return FALSE;
131 write_dib_info_header(s, sizeof(BITMAPV4HEADER), 32, BI_BITFIELDS);
132
133 Stream_Write_UINT32(s, 0x00FF0000); /* red mask */
134 Stream_Write_UINT32(s, 0x0000FF00); /* green mask */
135 Stream_Write_UINT32(s, 0x000000FF); /* blue mask */
136 Stream_Write_UINT32(s, 0xFF000000); /* alpha mask */
137 Stream_Zero(s, sizeof(BITMAPV4HEADER) - Stream_GetPosition(s));
138 Stream_Write_UINT32(s, 0xFF123456); /* pixel */
139 if (!test_dib_to_bmp(v4, sizeof(v4), sizeof(WINPR_BITMAP_FILE_HEADER) + sizeof(BITMAPV4HEADER)))
140 return FALSE;
141
142 BYTE bitfields[sizeof(BITMAPINFOHEADER) + 3 * sizeof(DWORD) + 4] = WINPR_C_ARRAY_INIT;
143 s = Stream_StaticInit(&sbuffer, bitfields, sizeof(bitfields));
144 if (!s)
145 return FALSE;
146 write_dib_info_header(s, sizeof(BITMAPINFOHEADER), 32, BI_BITFIELDS);
147 Stream_Write_UINT32(s, 0x00FF0000); /* red mask */
148 Stream_Write_UINT32(s, 0x0000FF00); /* green mask */
149 Stream_Write_UINT32(s, 0x000000FF); /* blue mask */
150 Stream_Write_UINT32(s, 0xFF123456); /* pixel */
151 if (!test_dib_to_bmp(bitfields, sizeof(bitfields),
153 3 * sizeof(DWORD)))
154 return FALSE;
155
156 BYTE paletted[sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD) + 4] = WINPR_C_ARRAY_INIT;
157 s = Stream_StaticInit(&sbuffer, paletted, sizeof(paletted));
158 if (!s)
159 return FALSE;
160 write_dib_info_header(s, sizeof(BITMAPINFOHEADER), 8, BI_RGB);
161 Stream_Zero(s, 256 * sizeof(RGBQUAD));
162 Stream_Write_UINT32(s, 0); /* pixel row */
163 return test_dib_to_bmp(paletted, sizeof(paletted),
165 256 * sizeof(RGBQUAD));
166}
167
168WINPR_ATTR_NODISCARD
169static BOOL test_text_leading_newline(void)
170{
171 const char* tests[][2] = { { "\nfoo", "\r\nfoo" }, { "\n", "\r\n" } };
172 BOOL rc = FALSE;
173
174 wClipboard* clipboard = ClipboardCreate();
175 if (!clipboard)
176 {
177 test_log("ClipboardCreate failed");
178 return FALSE;
179 }
180
181 const UINT32 textId = ClipboardRegisterFormat(clipboard, "text/plain");
182 for (size_t x = 0; x < ARRAYSIZE(tests); x++)
183 {
184 const char** cur = tests[x];
185 /* local text/plain is set without a terminator */
186 if (!test_ClipboardSetData(clipboard, textId, cur[0], (UINT32)strlen(cur[0])))
187 goto fail;
188
189 UINT32 size = 0;
190 WCHAR* wstr = test_ClipboardGetData(clipboard, CF_UNICODETEXT, &size);
191 char* str = ConvertWCharNToUtf8Alloc(wstr, size / sizeof(WCHAR), nullptr);
192 const BOOL match = str && (strcmp(str, cur[1]) == 0);
193 if (!match)
194 {
195 test_log("text/plain to CF_UNICODETEXT failed for case %" PRIuz "[%s (%" PRIu32
196 ") %p : expect %s (%" PRIuz ") : from %s (" PRIuz ")]",
197 x, str, size, wstr, cur[1], strlen(cur[1]), cur[0], strlen(cur[0]));
198 }
199 free(wstr);
200 free(str);
201 if (!match)
202 goto fail;
203 }
204 rc = TRUE;
205
206fail:
207 ClipboardDestroy(clipboard);
208 return rc;
209}
210
211WINPR_ATTR_NODISCARD
212static BOOL test_text_conversion(void)
213{
214 wClipboard* clipboard = ClipboardCreate();
215 if (!clipboard)
216 return FALSE;
217
218 typedef struct
219 {
220 UINT32 srcFormat;
221 UINT32 dstFormat;
222 const char* src;
223 size_t srcLen;
224 const char* expect;
225 size_t expectLen;
226 } test_case_t;
227
228 /* text/plain is UTF-8 on Windows, and ASCII with \u escapes for other characters elsewhere */
229 const test_case_t tests[] = {
230#if defined(_WIN32)
231 { ClipboardRegisterFormat(clipboard, "text/plain"),
232 ClipboardRegisterFormat(clipboard, "text/plain;charset=utf-8"), "a\nb\ncሴ՞", 10,
233 "a\nb\ncሴ՞", 11 },
234 { ClipboardRegisterFormat(clipboard, "text/plain;charset=utf-8"),
235 ClipboardRegisterFormat(clipboard, "text/plain"), "a\r\nb\nc՞՞", 10, "a\r\nb\nc՞՞", 11 },
236 { ClipboardRegisterFormat(clipboard, "text/plain"), CF_UNICODETEXT, "a\nb\ncሴ՞", 10,
237 "\x61\x00\x0d\x00\x0a\x00\x62\x00\x0d\x0\x0a\x00\x63\x00\x34\x12\x5e\x05\x00\x00\x00",
238 20 },
239 { CF_UNICODETEXT, ClipboardRegisterFormat(clipboard, "text/plain"),
240 "\x61\x00\x0d\x00\x0a\x00\x62\x00\x0d\x0\x0a\x00\x63\x00\x34\x12\x5e\x05\x00\x00\x00", 18,
241 "a\nb\ncሴ՞", 11 },
242 { ClipboardRegisterFormat(clipboard, "text/plain"), CF_TEXT, "a\nb\ncሴ՞", 10,
243 "a\nb\nc\\u1234\\u055e", 18 },
244 { CF_TEXT, ClipboardRegisterFormat(clipboard, "text/plain"), "a\nb\nc\\u1234\\u055e", 17,
245 "a\nb\ncሴ՞", 11 },
246 { ClipboardRegisterFormat(clipboard, "text/plain"), CF_OEMTEXT, "a\nb\ncሴ՞", 10,
247 "a\nb\nc\\u1234\\u055e", 18 },
248 { CF_OEMTEXT, ClipboardRegisterFormat(clipboard, "text/plain"), "a\nb\rc\\u1234\\u055e", 17,
249 "a\nb\ncሴ՞", 11 },
250#else
251 { ClipboardRegisterFormat(clipboard, "text/plain"),
252 ClipboardRegisterFormat(clipboard, "text/plain;charset=utf-8"), "a\nb\rc\\u1234\\u055e",
253 17, "a\nb\ncሴ՞", 11 },
254 { ClipboardRegisterFormat(clipboard, "text/plain;charset=utf-8"),
255 ClipboardRegisterFormat(clipboard, "text/plain"), "a\r\nb\nc՞՞", 10,
256 "a\r\nb\nc\\u055e\\u055e", 19 },
257 { ClipboardRegisterFormat(clipboard, "text/plain"), CF_UNICODETEXT, "a\nb\rc\\u1234\\u055e",
258 17, "\x61\x00\x0d\x00\x0a\x00\x62\x00\x0d\x0\x0a\x00\x63\x00\x34\x12\x5e\x05\x00\x00\x00",
259 20 },
260 { CF_UNICODETEXT, ClipboardRegisterFormat(clipboard, "text/plain"),
261 "\x61\x00\x0d\x00\x0a\x00\x62\x00\x0d\x0\x0a\x00\x63\x00\x34\x12\x5e\x05\x00\x00\x00", 18,
262 "a\nb\nc\\u1234\\u055e", 18 },
263 { ClipboardRegisterFormat(clipboard, "text/plain"), CF_TEXT, "a\nb\rc\\u1234\\u055e", 17,
264 "a\nb\nc\\u1234\\u055e", 18 },
265 { CF_TEXT, ClipboardRegisterFormat(clipboard, "text/plain"), "a\nb\nc\\u1234\\u055e", 17,
266 "a\nb\nc\\u1234\\u055e", 18 },
267 { ClipboardRegisterFormat(clipboard, "text/plain"), CF_OEMTEXT, "a\nb\rc\\u1234\\u055e", 17,
268 "a\nb\nc\\u1234\\u055e", 18 },
269 { CF_OEMTEXT, ClipboardRegisterFormat(clipboard, "text/plain"), "a\nb\rc\\u1234\\u055e", 17,
270 "a\nb\nc\\u1234\\u055e", 18 },
271#endif
272 /* UTF-8 set with its terminator */
273 { ClipboardRegisterFormat(clipboard, "text/plain;charset=utf-8"),
274 ClipboardRegisterFormat(clipboard, "UTF8_STRING"), "ab", 3, "ab", 3 },
275 { ClipboardRegisterFormat(clipboard, "text/plain;charset=utf-8"), CF_UNICODETEXT, "ab", 3,
276 "\x61\x00\x62\x00\x00\x00", 6 }
277 };
278
279 BOOL rc = FALSE;
280
281 for (size_t x = 0; x < ARRAYSIZE(tests); x++)
282 {
283 const test_case_t* cur = &tests[x];
284
285 /* local text/plain is set without a terminator */
286 if (!test_ClipboardSetData(clipboard, cur->srcFormat, cur->src, cur->srcLen))
287 goto fail;
288
289 UINT32 dstSize = 0;
290 BYTE* data = test_ClipboardGetData(clipboard, cur->dstFormat, &dstSize);
291 if (dstSize != cur->expectLen)
292 {
293 winpr_znfree(data, dstSize);
294 goto fail;
295 }
296 if (dstSize != 0)
297 {
298 if (!data)
299 {
300 winpr_znfree(data, dstSize);
301 goto fail;
302 }
303 const int rc = memcmp(cur->expect, data, cur->expectLen);
304 winpr_znfree(data, dstSize);
305 if (rc != 0)
306 goto fail;
307 }
308 else
309 winpr_znfree(data, dstSize);
310 }
311 rc = TRUE;
312
313fail:
314 ClipboardDestroy(clipboard);
315 return rc;
316}
317
318WINPR_ATTR_NODISCARD
319static BOOL set_descriptor_name(FILEDESCRIPTORW* dsc, const char* name, const char* trailing)
320{
321 BOOL rc = TRUE;
322 /* what follows the '\0' of cFileName is garbage the converter must ignore */
323 memset(dsc->cFileName, 0xff, sizeof(dsc->cFileName));
324 const size_t len = strlen(name);
325 if (ConvertUtf8NToWChar(name, len + 1, dsc->cFileName, ARRAYSIZE(dsc->cFileName)) < 0)
326 rc = FALSE;
327 else if (trailing && (ConvertUtf8NToWChar(trailing, strlen(trailing), &dsc->cFileName[len],
328 ARRAYSIZE(dsc->cFileName) - len - 1) < 0))
329 rc = FALSE;
330 if (!rc)
331 test_log("set_descriptor_name(%s, %s) failed", name, trailing);
332 return TRUE;
333}
334
335WINPR_ATTR_NODISCARD
336static BOOL test_file_descriptors_to_uri_list(void)
337{
338 BOOL rc = FALSE;
339 char* basePath = GetKnownSubPath(KNOWN_PATH_TEMP, "frdp-test");
340 if (!basePath)
341 return FALSE;
342 char* uris = nullptr;
343
344 struct
345 {
346 UINT32 count;
347 FILEDESCRIPTORW dsc[4];
348 } data = WINPR_C_ARRAY_INIT;
349
350 char* cmpdir = nullptr;
351 char* cmpa = nullptr;
352 wClipboard* clipboard = ClipboardCreate();
353 if (!clipboard)
354 goto fail;
355
356 HRESULT hr = PathCchConvertStyleA(basePath, strlen(basePath), PATH_STYLE_UNIX);
357 if (FAILED(hr))
358 goto fail;
359
360 wClipboardDelegate* delegate = ClipboardGetDelegate(clipboard);
361 if (!delegate)
362 goto fail;
363 delegate->basePath = basePath;
364
365 size_t len = 0;
366 winpr_asprintf(&cmpdir, &len, "file://%s/dir\r\n", basePath);
367 winpr_asprintf(&cmpa, &len, "file://%s/a.txt", basePath);
368 if (!cmpdir || !cmpa)
369 goto fail;
370
371 /* data must be little endian */
372 winpr_Data_Write_UINT32(&data.count, ARRAYSIZE(data.dsc));
373 winpr_Data_Write_UINT32(&data.dsc[0].dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY);
374 if (!set_descriptor_name(&data.dsc[0], "dir", nullptr))
375 goto fail;
376 if (!set_descriptor_name(&data.dsc[1], "dir\\b.txt", nullptr))
377 goto fail;
378 /* a stale '\' after the name must not make a top level file look nested */
379 if (!set_descriptor_name(&data.dsc[2], "a.txt", "old\\name"))
380 goto fail;
381 if (!set_descriptor_name(&data.dsc[3], "a.txt", "old\\"))
382 goto fail;
383
384 {
385 const UINT32 srcId = ClipboardRegisterFormat(clipboard, "FileGroupDescriptorW");
386 const UINT32 dstId = ClipboardRegisterFormat(clipboard, "text/uri-list");
387 if (!test_ClipboardSetData(clipboard, srcId, &data, sizeof(data)))
388 goto fail;
389
390 UINT32 size = 0;
391 uris = test_ClipboardGetData(clipboard, dstId, &size);
392 if (!uris)
393 goto fail;
394 test_log("text/uri-list: '%s'", uris);
395
396 if (!strstr(uris, cmpdir))
397 {
398 test_log("strstr(%s, %s) failed", uris, cmpdir);
399 goto fail;
400 }
401 if (strstr(uris, "b.txt"))
402 {
403 test_log("strstr(%s, %s) failed", uris, "b.txt");
404 goto fail;
405 }
406 }
407
408 rc = TRUE;
409fail:
410 if (!rc)
411 test_log("FileGroupDescriptorW -> text/uri-list conversion failed");
412 free(uris);
413 free(cmpdir);
414 free(cmpa);
415 free(basePath);
416 ClipboardDestroy(clipboard);
417 return rc;
418}
419
420/* image/bmp from the clipboard has a plain BITMAPINFOHEADER and is stored bottom-up */
421WINPR_ATTR_NODISCARD
422static BOOL test_is_plain_bmp(const BYTE* bmp, size_t size)
423{
424 if ((size < 54) || (bmp[0] != 'B') || (bmp[1] != 'M'))
425 return FALSE;
426
427 const UINT32 biSize = winpr_Data_Get_UINT32(&bmp[14]);
428 const INT32 biHeight = winpr_Data_Get_INT32(&bmp[22]);
429 const UINT32 biCompression = winpr_Data_Get_UINT32(&bmp[30]);
430 if ((biSize != 40) || (biHeight <= 0) || (biCompression != BI_RGB))
431 {
432 test_log("not a plain bottom-up BMP: biSize=%" PRIu32 " biHeight=%" PRId32
433 " biCompression=%" PRIu32,
434 biSize, biHeight, biCompression);
435 return FALSE;
436 }
437 return TRUE;
438}
439
440/* convert the clipboard content to dstMime and compare the image with the expected one */
441WINPR_ATTR_NODISCARD
442static BOOL test_convert_image(wClipboard* clipboard, const char* dstMime, const wImage* expected)
443{
444 BOOL rc = FALSE;
445 UINT32 size = 0;
446 wImage* img = winpr_image_new();
447 BYTE* data =
448 test_ClipboardGetData(clipboard, ClipboardRegisterFormat(clipboard, dstMime), &size);
449 if (!img || !data)
450 goto fail;
451
452 if (winpr_image_read_buffer(img, data, size) <= 0)
453 {
454 test_log("can not read %s", dstMime);
455 goto fail;
456 }
457
458 if (!winpr_image_equal(expected, img,
459 WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA |
460 WINPR_IMAGE_CMP_FUZZY))
461 {
462 test_log("%s differs from the source image", dstMime);
463 goto fail;
464 }
465
466 if ((strcmp(dstMime, "image/bmp") == 0) && !test_is_plain_bmp(data, size))
467 goto fail;
468
469 rc = TRUE;
470fail:
471 free(data);
472 winpr_image_free(img, TRUE);
473 return rc;
474}
475
476/* image formats convert into each other directly, without going through CF_DIB first */
477WINPR_ATTR_NODISCARD
478static BOOL test_image_to_image(void)
479{
480 BOOL rc = FALSE;
481 void* data = nullptr;
482 size_t size = 0;
483 wClipboard* clipboard = ClipboardCreate();
484 wImage* img = winpr_image_new();
485 if (!clipboard || !img)
486 goto fail;
487
488 if (winpr_image_read(img, TEST_CLIP_BMP) <= 0)
489 goto fail;
490
491 data = winpr_image_write_buffer(img, WINPR_IMAGE_BITMAP, &size);
492 if (!data || !test_ClipboardSetData(clipboard, ClipboardRegisterFormat(clipboard, "image/bmp"),
493 data, (UINT32)size))
494 goto fail;
495 free(data);
496 data = nullptr;
497
498 if (!test_convert_image(clipboard, "image/x-bmp", img))
499 goto fail;
500#if defined(WINPR_UTILS_IMAGE_PNG)
501 if (!test_convert_image(clipboard, "image/png", img))
502 goto fail;
503
504 data = winpr_image_write_buffer(img, WINPR_IMAGE_PNG, &size);
505 if (!data || !test_ClipboardSetData(clipboard, ClipboardRegisterFormat(clipboard, "image/png"),
506 data, (UINT32)size))
507 goto fail;
508
509 if (!test_convert_image(clipboard, "image/bmp", img))
510 goto fail;
511#endif
512
513 rc = TRUE;
514fail:
515 free(data);
516 winpr_image_free(img, TRUE);
517 ClipboardDestroy(clipboard);
518 return rc;
519}
520
521/* text/plain is UTF-8 on Windows and ASCII with \u escapes elsewhere; both convert back */
522WINPR_ATTR_NODISCARD
523static BOOL test_text_plain(void)
524{
525 BOOL rc = FALSE;
526 const char utf8[] = "A\xe4\xb8\xad\xe6\x96\x87\xc3\xa9"; /* A, two CJK characters, e acute */
527 const size_t utf8len = strlen(utf8);
528 char* plain = nullptr;
529 WCHAR* wstr = nullptr;
530 char* back = nullptr;
531 UINT32 size = 0;
532
533 wClipboard* clipboard = ClipboardCreate();
534 if (!clipboard)
535 goto fail;
536
537 const UINT32 idUtf8 = ClipboardRegisterFormat(clipboard, "text/plain;charset=utf-8");
538 const UINT32 idPlain = ClipboardRegisterFormat(clipboard, "text/plain");
539 if (!test_ClipboardSetData(clipboard, idUtf8, utf8, (UINT32)utf8len))
540 goto fail;
541
542 plain = test_ClipboardGetData(clipboard, idPlain, &size);
543 if (!plain)
544 goto fail;
545
546#if defined(_WIN32)
547 if ((size < utf8len) || (memcmp(plain, utf8, utf8len) != 0))
548 {
549 test_log("text/plain is not UTF-8");
550 goto fail;
551 }
552#else
553 for (UINT32 x = 0; x < size; x++)
554 {
555 if ((BYTE)plain[x] > 0x7f)
556 {
557 test_log("text/plain is not ASCII");
558 goto fail;
559 }
560 }
561#endif
562
563 if (!test_ClipboardSetData(clipboard, idPlain, plain, size))
564 goto fail;
565
566 wstr = test_ClipboardGetData(clipboard, CF_UNICODETEXT, &size);
567 if (!wstr)
568 goto fail;
569
570 {
571 size_t backlen = 0;
572 back = ConvertWCharNToUtf8Alloc(wstr, size / sizeof(WCHAR), &backlen);
573 if (!back || (backlen < utf8len) || (memcmp(back, utf8, utf8len) != 0))
574 {
575 test_log("text/plain does not convert back to the original text");
576 goto fail;
577 }
578 }
579
580 rc = TRUE;
581fail:
582 free(back);
583 free(wstr);
584 free(plain);
585 ClipboardDestroy(clipboard);
586 return rc;
587}
588
589static BOOL test_cf_locale(void)
590{
591 BOOL rc = FALSE;
592 wClipboard* cb = ClipboardCreate();
593 if (!cb)
594 return FALSE;
595
596 const UINT32 formats[] = { CF_OEMTEXT, CF_TEXT, CF_UNICODETEXT,
597 ClipboardRegisterFormat(cb, "text/plain"),
598 ClipboardRegisterFormat(cb, "text/plain;charset=utf-8") };
599
600 for (size_t x = 0; x < ARRAYSIZE(formats); x++)
601 {
602 const UINT32 format = formats[x];
603
604 const char text[] = "some test string";
605 if (!test_ClipboardSetData(cb, format, text, sizeof(text)))
606 goto fail;
607 UINT32 size = 0;
608 void* locale = test_ClipboardGetData(cb, CF_LOCALE, &size);
609 if (!locale || (size != 4))
610 {
611 free(locale);
612 goto fail;
613 }
614 const UINT32 loc = *(UINT32*)locale;
615 free(locale);
616 if (loc != 0x409)
617 goto fail;
618 }
619 rc = TRUE;
620fail:
621 ClipboardDestroy(cb);
622 return rc;
623}
624
625int TestClipboardFormats(int argc, char* argv[])
626{
627 int rc = -1;
628 UINT32 count = 0;
629 UINT32* pFormatIds = nullptr;
630 const char* formatName = nullptr;
631 UINT32 utf8StringFormatId = 0;
632
633 WINPR_UNUSED(argc);
634 WINPR_UNUSED(argv);
635
636 if (!test_cf_locale())
637 return -1;
638 if (!test_text_leading_newline())
639 return -1;
640
641 if (!test_text_conversion())
642 return -1;
643
644 if (!test_file_descriptors_to_uri_list())
645 return -1;
646
647 wClipboard* clipboard = ClipboardCreate();
648 if (!clipboard)
649 return -1;
650
651 const char* mime_types[] = { "text/plain", "text/plain;charset=utf-8",
652 "text/html", "image/bmp",
653 "image/png", "image/webp",
654 "image/jpeg" };
655 for (size_t x = 0; x < ARRAYSIZE(mime_types); x++)
656 {
657 const char* mime = mime_types[x];
658 UINT32 id = ClipboardRegisterFormat(clipboard, mime);
659 test_log("ClipboardRegisterFormat(%s) -> 0x%08" PRIx32 "", mime, id);
660 if (id == 0)
661 goto fail;
662 }
663
664 utf8StringFormatId = ClipboardRegisterFormat(clipboard, "UTF8_STRING");
665 pFormatIds = nullptr;
666 count = ClipboardGetRegisteredFormatIds(clipboard, &pFormatIds);
667
668 for (UINT32 index = 0; index < count; index++)
669 {
670 UINT32 formatId = pFormatIds[index];
671 formatName = ClipboardGetFormatName(clipboard, formatId);
672 test_log("Format: 0x%08" PRIX32 " %s", formatId, formatName);
673 }
674
675 free(pFormatIds);
676
677 if (1)
678 {
679 const char pSrcData[] = "this is a test string";
680 char* pDstData = nullptr;
681
682 UINT32 SrcSize = (UINT32)(strnlen(pSrcData, ARRAYSIZE(pSrcData)) + 1);
683 const BOOL bSuccess =
684 test_ClipboardSetData(clipboard, utf8StringFormatId, pSrcData, SrcSize);
685 if (!bSuccess)
686 goto fail;
687
688 UINT32 DstSize = 0;
689 pDstData = (char*)test_ClipboardGetData(clipboard, utf8StringFormatId, &DstSize);
690 const BOOL equal = (SrcSize == DstSize) && (strncmp(pSrcData, pDstData, SrcSize) == 0);
691 free(pDstData);
692 if (!equal)
693 goto fail;
694 {
695 UINT32 WDstSize = 0;
696 WCHAR* pDstData = (WCHAR*)test_ClipboardGetData(clipboard, CF_UNICODETEXT, &WDstSize);
697
698 size_t cmpSrcSize = 0;
699 char* pCmpData =
700 ConvertWCharNToUtf8Alloc(pDstData, WDstSize / sizeof(WCHAR), &cmpSrcSize);
701 const BOOL equal2 =
702 (SrcSize == cmpSrcSize + 1ull) && (strncmp(pCmpData, pSrcData, SrcSize) == 0);
703 free(pDstData);
704 free(pCmpData);
705 if (!equal2)
706 goto fail;
707 }
708 }
709
710 pFormatIds = nullptr;
711 count = ClipboardGetFormatIds(clipboard, &pFormatIds);
712
713 for (UINT32 index = 0; index < count; index++)
714 {
715 UINT32 formatId = pFormatIds[index];
716 formatName = ClipboardGetFormatName(clipboard, formatId);
717 test_log("Format: 0x%08" PRIX32 " %s", formatId, formatName);
718 }
719
720 if (1)
721 {
722 const char* name = TEST_CLIP_BMP;
723 BOOL bSuccess = FALSE;
724 UINT32 idBmp = ClipboardRegisterFormat(clipboard, "image/bmp");
725
726 wImage* img = winpr_image_new();
727 if (!img)
728 goto fail;
729
730 if (winpr_image_read(img, name) <= 0)
731 {
732 winpr_image_free(img, TRUE);
733 goto fail;
734 }
735
736 size_t bmpsize = 0;
737 void* data = winpr_image_write_buffer(img, WINPR_IMAGE_BITMAP, &bmpsize);
738 bSuccess = test_ClipboardSetData(clipboard, idBmp, data, bmpsize);
739
740 free(data);
741 winpr_image_free(img, TRUE);
742 if (!bSuccess)
743 goto fail;
744
745 {
746 UINT32 id = CF_DIB;
747
748 UINT32 DstSize = 0;
749 void* pDstData = test_ClipboardGetData(clipboard, id, &DstSize);
750 if (!pDstData)
751 goto fail;
752 bSuccess = test_ClipboardSetData(clipboard, id, pDstData, DstSize);
753 free(pDstData);
754 if (!bSuccess)
755 goto fail;
756 }
757 {
758 const uint32_t id = ClipboardGetFormatId(clipboard, "HTML Format");
759 UINT32 DstSize = 0;
760 void* pDstData = test_ClipboardGetData(clipboard, id, &DstSize);
761 if (!pDstData)
762 goto fail;
763 {
764 FILE* fp = fopen("test.html", "w");
765 if (fp)
766 {
767 (void)fwrite(pDstData, 1, DstSize, fp);
768 (void)fclose(fp);
769 }
770 }
771 free(pDstData);
772 }
773 {
774 UINT32 id = ClipboardRegisterFormat(clipboard, "image/bmp");
775
776 UINT32 DstSize = 0;
777 void* pDstData = test_ClipboardGetData(clipboard, id, &DstSize);
778 if (!pDstData)
779 goto fail;
780 free(pDstData);
781 if (DstSize != bmpsize)
782 goto fail;
783 }
784
785#if defined(WINPR_UTILS_IMAGE_PNG)
786 {
787 UINT32 id = ClipboardRegisterFormat(clipboard, "image/png");
788
789 UINT32 DstSize = 0;
790 void* pDstData = test_ClipboardGetData(clipboard, id, &DstSize);
791 if (!pDstData)
792 goto fail;
793 free(pDstData);
794 }
795 {
796 const char* name = TEST_CLIP_PNG;
797 BOOL bSuccess = FALSE;
798 UINT32 idBmp = ClipboardRegisterFormat(clipboard, "image/png");
799
800 wImage* img = winpr_image_new();
801 if (!img)
802 goto fail;
803
804 if (winpr_image_read(img, name) <= 0)
805 {
806 winpr_image_free(img, TRUE);
807 goto fail;
808 }
809
810 size_t bmpsize = 0;
811 void* data = winpr_image_write_buffer(img, WINPR_IMAGE_PNG, &bmpsize);
812 bSuccess = test_ClipboardSetData(clipboard, idBmp, data, bmpsize);
813
814 free(data);
815 winpr_image_free(img, TRUE);
816 if (!bSuccess)
817 goto fail;
818 }
819 {
820 UINT32 id = CF_DIB;
821
822 UINT32 DstSize = 0;
823 void* pDstData = test_ClipboardGetData(clipboard, id, &DstSize);
824 if (!pDstData)
825 goto fail;
826 bSuccess = test_ClipboardSetData(clipboard, id, pDstData, DstSize);
827 free(pDstData);
828 if (!bSuccess)
829 goto fail;
830 }
831#endif
832
833#if defined(WINPR_UTILS_IMAGE_WEBP)
834 {
835 UINT32 id = ClipboardRegisterFormat(clipboard, "image/webp");
836
837 UINT32 DstSize = 0;
838 void* pDstData = test_ClipboardGetData(clipboard, id, &DstSize);
839 if (!pDstData)
840 goto fail;
841 free(pDstData);
842 }
843#endif
844
845#if defined(WINPR_UTILS_IMAGE_JPEG)
846 {
847 UINT32 id = ClipboardRegisterFormat(clipboard, "image/jpeg");
848
849 UINT32 DstSize = 0;
850 void* pDstData = test_ClipboardGetData(clipboard, id, &DstSize);
851 if (!pDstData)
852 goto fail;
853 free(pDstData);
854 }
855#endif
856 }
857
858 if (!test_dib_offsets())
859 goto fail;
860
861 if (!test_image_to_image())
862 goto fail;
863
864 if (!test_text_plain())
865 goto fail;
866
867 rc = 0;
868
869fail:
870 free(pFormatIds);
871 ClipboardDestroy(clipboard);
872 return rc;
873}