VirtualBox

source: vbox/trunk/src/VBox/GuestHost/SharedClipboard/clipboard-common.cpp@ 100220

Last change on this file since 100220 was 100204, checked in by vboxsync, 20 months ago

Shared Clipboard: Unified root list entry code to also use the generic list entry code, a lot of updates for the cross OS transfer handling code, more updates for HTTP server transfer handling.

This also changed the handling of how that transfers are being initiated, as we needed to have this for X11: Before, transfers were initiated as soon as on side announced the URI list format -- now we postpone initiating the transfer until the receiving side requests the data as URI list.

bugref:9437

  • Property eol-style set to native
  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 36.0 KB
Line 
1/* $Id: clipboard-common.cpp 100204 2023-06-19 09:11:37Z vboxsync $ */
2/** @file
3 * Shared Clipboard: Some helper function for converting between the various eol.
4 */
5
6/*
7 * Includes contributions from François Revol
8 *
9 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
10 *
11 * This file is part of VirtualBox base platform packages, as
12 * available from https://www.virtualbox.org.
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation, in version 3 of the
17 * License.
18 *
19 * This program is distributed in the hope that it will be useful, but
20 * WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see <https://www.gnu.org/licenses>.
26 *
27 * SPDX-License-Identifier: GPL-3.0-only
28 */
29
30#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
31
32#include <iprt/alloc.h>
33#include <iprt/assert.h>
34#include <iprt/semaphore.h>
35#include <iprt/path.h>
36#include <iprt/rand.h>
37#include <iprt/utf16.h>
38
39#include <iprt/formats/bmp.h>
40
41#include <iprt/errcore.h>
42#include <VBox/log.h>
43#include <VBox/GuestHost/clipboard-helper.h>
44#include <VBox/HostServices/VBoxClipboardSvc.h>
45
46
47/*********************************************************************************************************************************
48* Prototypes *
49*********************************************************************************************************************************/
50static void shClEventSourceResetInternal(PSHCLEVENTSOURCE pSource);
51
52static void shClEventDestroy(PSHCLEVENT pEvent);
53DECLINLINE(PSHCLEVENT) shclEventGet(PSHCLEVENTSOURCE pSource, SHCLEVENTID idEvent);
54
55
56/*********************************************************************************************************************************
57* Implementation *
58*********************************************************************************************************************************/
59
60/**
61 * Allocates a new event payload.
62 *
63 * @returns VBox status code.
64 * @param uID Payload ID to set for this payload. Useful for consequtive payloads.
65 * @param pvData Data to associate to this payload.
66 * The payload owns the data then.
67 * @param cbData Size (in bytes) of data to associate.
68 * @param ppPayload Where to store the allocated event payload on success.
69 */
70int ShClPayloadInit(uint32_t uID, void *pvData, uint32_t cbData,
71 PSHCLEVENTPAYLOAD *ppPayload)
72{
73 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
74 AssertReturn(cbData > 0, VERR_INVALID_PARAMETER);
75
76 PSHCLEVENTPAYLOAD pPayload = (PSHCLEVENTPAYLOAD)RTMemAlloc(sizeof(SHCLEVENTPAYLOAD));
77 if (pPayload)
78 {
79 pPayload->pvData = pvData;
80 pPayload->cbData = cbData;
81 pPayload->uID = uID;
82
83 *ppPayload = pPayload;
84 return VINF_SUCCESS;
85 }
86
87 return VERR_NO_MEMORY;
88}
89
90/**
91 * Allocates a new event payload.
92 *
93 * @returns VBox status code.
94 * @param uID Payload ID to set for this payload. Useful for consequtive payloads.
95 * @param pvData Data block to allocate (duplicate) to this payload.
96 * @param cbData Size (in bytes) of data block to allocate.
97 * @param ppPayload Where to store the allocated event payload on success.
98 */
99int ShClPayloadAlloc(uint32_t uID, const void *pvData, uint32_t cbData,
100 PSHCLEVENTPAYLOAD *ppPayload)
101{
102 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
103 AssertReturn(cbData > 0, VERR_INVALID_PARAMETER);
104
105 void *pvDataDup = RTMemDup(pvData, cbData);
106 if (pvDataDup)
107 return ShClPayloadInit(uID, pvDataDup, cbData, ppPayload);
108
109 return VERR_NO_MEMORY;
110}
111
112/**
113 * Frees an event payload.
114 *
115 * @returns VBox status code.
116 * @param pPayload Event payload to free.
117 */
118void ShClPayloadFree(PSHCLEVENTPAYLOAD pPayload)
119{
120 if (!pPayload)
121 return;
122
123 if (pPayload->pvData)
124 {
125 Assert(pPayload->cbData);
126 RTMemFree(pPayload->pvData);
127 pPayload->pvData = NULL;
128 }
129
130 pPayload->cbData = 0;
131 pPayload->uID = UINT32_MAX;
132
133 RTMemFree(pPayload);
134}
135
136/**
137 * Creates a new event source.
138 *
139 * @returns VBox status code.
140 * @param pSource Event source to create.
141 * @param uID ID to use for event source.
142 */
143int ShClEventSourceCreate(PSHCLEVENTSOURCE pSource, SHCLEVENTSOURCEID uID)
144{
145 LogFlowFunc(("pSource=%p, uID=%RU16\n", pSource, uID));
146 AssertPtrReturn(pSource, VERR_INVALID_POINTER);
147
148 int rc = RTCritSectInit(&pSource->CritSect);
149 AssertRCReturn(rc, rc);
150
151 RTListInit(&pSource->lstEvents);
152
153 pSource->uID = uID;
154 /* Choose a random event ID starting point. */
155 pSource->idNextEvent = RTRandU32Ex(1, VBOX_SHCL_MAX_EVENTS - 1);
156
157 return VINF_SUCCESS;
158}
159
160/**
161 * Destroys an event source.
162 *
163 * @returns VBox status code.
164 * @param pSource Event source to destroy.
165 */
166int ShClEventSourceDestroy(PSHCLEVENTSOURCE pSource)
167{
168 if (!pSource)
169 return VINF_SUCCESS;
170
171 if (!RTCritSectIsInitialized(&pSource->CritSect)) /* Already destroyed? Bail out. */
172 return VINF_SUCCESS;
173
174 LogFlowFunc(("ID=%RU32\n", pSource->uID));
175
176 int rc = RTCritSectEnter(&pSource->CritSect);
177 if (RT_SUCCESS(rc))
178 {
179 shClEventSourceResetInternal(pSource);
180
181 rc = RTCritSectLeave(&pSource->CritSect);
182 AssertRC(rc);
183
184 RTCritSectDelete(&pSource->CritSect);
185
186 pSource->uID = UINT16_MAX;
187 pSource->idNextEvent = UINT32_MAX;
188 }
189
190 return rc;
191}
192
193/**
194 * Resets an event source, internal version.
195 *
196 * @param pSource Event source to reset.
197 */
198static void shClEventSourceResetInternal(PSHCLEVENTSOURCE pSource)
199{
200 LogFlowFunc(("ID=%RU32\n", pSource->uID));
201
202 PSHCLEVENT pEvIt;
203 PSHCLEVENT pEvItNext;
204 RTListForEachSafe(&pSource->lstEvents, pEvIt, pEvItNext, SHCLEVENT, Node)
205 {
206 RTListNodeRemove(&pEvIt->Node);
207
208 shClEventDestroy(pEvIt);
209
210 RTMemFree(pEvIt);
211 pEvIt = NULL;
212 }
213}
214
215/**
216 * Resets an event source.
217 *
218 * @param pSource Event source to reset.
219 */
220void ShClEventSourceReset(PSHCLEVENTSOURCE pSource)
221{
222 int rc2 = RTCritSectEnter(&pSource->CritSect);
223 if (RT_SUCCESS(rc2))
224 {
225 shClEventSourceResetInternal(pSource);
226
227 rc2 = RTCritSectLeave(&pSource->CritSect);
228 AssertRC(rc2);
229 }
230}
231
232/**
233 * Generates a new event ID for a specific event source and registers it.
234 *
235 * @returns VBox status code.
236 * @param pSource Event source to generate event for.
237 * @param ppEvent Where to return the new event generated on success.
238 */
239int ShClEventSourceGenerateAndRegisterEvent(PSHCLEVENTSOURCE pSource, PSHCLEVENT *ppEvent)
240{
241 AssertPtrReturn(pSource, VERR_INVALID_POINTER);
242 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
243
244 PSHCLEVENT pEvent = (PSHCLEVENT)RTMemAllocZ(sizeof(SHCLEVENT));
245 AssertReturn(pEvent, VERR_NO_MEMORY);
246 int rc = RTSemEventMultiCreate(&pEvent->hEvtMulSem);
247 if (RT_SUCCESS(rc))
248 {
249 rc = RTCritSectEnter(&pSource->CritSect);
250 if (RT_SUCCESS(rc))
251 {
252 /*
253 * Allocate an unique event ID.
254 */
255 for (uint32_t cTries = 0;; cTries++)
256 {
257 SHCLEVENTID idEvent = ++pSource->idNextEvent;
258 if (idEvent < VBOX_SHCL_MAX_EVENTS)
259 { /* likely */ }
260 else
261 pSource->idNextEvent = idEvent = 1; /* zero == error, remember! */
262
263 if (shclEventGet(pSource, idEvent) == NULL)
264 {
265 pEvent->pParent = pSource;
266 pEvent->idEvent = idEvent;
267 RTListAppend(&pSource->lstEvents, &pEvent->Node);
268
269 rc = RTCritSectLeave(&pSource->CritSect);
270 AssertRC(rc);
271
272 LogFlowFunc(("uSource=%RU16: New event: %#x\n", pSource->uID, idEvent));
273
274 ShClEventRetain(pEvent);
275 *ppEvent = pEvent;
276
277 return VINF_SUCCESS;
278 }
279
280 AssertBreak(cTries < 4096);
281 }
282
283 rc = RTCritSectLeave(&pSource->CritSect);
284 AssertRC(rc);
285 }
286 }
287
288 AssertMsgFailed(("Unable to register a new event ID for event source %RU16\n", pSource->uID));
289
290 RTSemEventMultiDestroy(pEvent->hEvtMulSem);
291 pEvent->hEvtMulSem = NIL_RTSEMEVENTMULTI;
292 RTMemFree(pEvent);
293 return rc;
294}
295
296/**
297 * Destroys an event.
298 *
299 * @param pEvent Event to destroy.
300 */
301static void shClEventDestroy(PSHCLEVENT pEvent)
302{
303 if (!pEvent)
304 return;
305
306 AssertMsgReturnVoid(pEvent->cRefs == 0, ("Event %RU32 still has %RU32 references\n",
307 pEvent->idEvent, pEvent->cRefs));
308
309 LogFlowFunc(("Event %RU32\n", pEvent->idEvent));
310
311 if (pEvent->hEvtMulSem != NIL_RTSEMEVENT)
312 {
313 RTSemEventMultiDestroy(pEvent->hEvtMulSem);
314 pEvent->hEvtMulSem = NIL_RTSEMEVENT;
315 }
316
317 ShClPayloadFree(pEvent->pPayload);
318 pEvent->pPayload = NULL;
319
320 pEvent->idEvent = NIL_SHCLEVENTID;
321}
322
323/**
324 * Unregisters an event.
325 *
326 * @returns VBox status code.
327 * @param pSource Event source to unregister event for.
328 * @param pEvent Event to unregister. On success the pointer will be invalid.
329 */
330static int shClEventSourceUnregisterEventInternal(PSHCLEVENTSOURCE pSource, PSHCLEVENT pEvent)
331{
332 LogFlowFunc(("idEvent=%RU32, cRefs=%RU32\n", pEvent->idEvent, pEvent->cRefs));
333
334 AssertReturn(pEvent->cRefs == 0, VERR_WRONG_ORDER);
335
336 int rc = RTCritSectEnter(&pSource->CritSect);
337 if (RT_SUCCESS(rc))
338 {
339 RTListNodeRemove(&pEvent->Node);
340
341 shClEventDestroy(pEvent);
342
343 rc = RTCritSectLeave(&pSource->CritSect);
344 if (RT_SUCCESS(rc))
345 {
346 RTMemFree(pEvent);
347 pEvent = NULL;
348 }
349 }
350
351 return rc;
352}
353
354/**
355 * Returns a specific event of a event source. Inlined version.
356 *
357 * @returns Pointer to event if found, or NULL if not found.
358 * @param pSource Event source to get event from.
359 * @param uID Event ID to get.
360 */
361DECLINLINE(PSHCLEVENT) shclEventGet(PSHCLEVENTSOURCE pSource, SHCLEVENTID idEvent)
362{
363 PSHCLEVENT pEvent;
364 RTListForEach(&pSource->lstEvents, pEvent, SHCLEVENT, Node)
365 {
366 if (pEvent->idEvent == idEvent)
367 return pEvent;
368 }
369
370 return NULL;
371}
372
373/**
374 * Returns a specific event of a event source.
375 *
376 * @returns Pointer to event if found, or NULL if not found.
377 * @param pSource Event source to get event from.
378 * @param idEvent ID of event to return.
379 */
380PSHCLEVENT ShClEventSourceGetFromId(PSHCLEVENTSOURCE pSource, SHCLEVENTID idEvent)
381{
382 AssertPtrReturn(pSource, NULL);
383
384 int rc = RTCritSectEnter(&pSource->CritSect);
385 if (RT_SUCCESS(rc))
386 {
387 PSHCLEVENT pEvent = shclEventGet(pSource, idEvent);
388
389 rc = RTCritSectLeave(&pSource->CritSect);
390 AssertRC(rc);
391
392 return pEvent;
393 }
394
395 return NULL;
396}
397
398/**
399 * Returns the last (newest) event ID which has been registered for an event source.
400 *
401 * @returns Pointer to last registered event, or NULL if not found.
402 * @param pSource Event source to get last registered event from.
403 */
404PSHCLEVENT ShClEventSourceGetLast(PSHCLEVENTSOURCE pSource)
405{
406 AssertPtrReturn(pSource, NULL);
407
408 int rc = RTCritSectEnter(&pSource->CritSect);
409 if (RT_SUCCESS(rc))
410 {
411 PSHCLEVENT pEvent = RTListGetLast(&pSource->lstEvents, SHCLEVENT, Node);
412
413 rc = RTCritSectLeave(&pSource->CritSect);
414 AssertRC(rc);
415
416 return pEvent;
417 }
418
419 return NULL;
420}
421
422/**
423 * Returns the current reference count for a specific event.
424 *
425 * @returns Reference count.
426 * @param pSource Event source the specific event is part of.
427 * @param idEvent Event ID to return reference count for.
428 */
429uint32_t ShClEventGetRefs(PSHCLEVENT pEvent)
430{
431 AssertPtrReturn(pEvent, 0);
432
433 return ASMAtomicReadU32(&pEvent->cRefs);
434}
435
436/**
437 * Detaches a payload from an event, internal version.
438 *
439 * @returns Pointer to the detached payload. Can be NULL if the event has no payload.
440 * @param pEvent Event to detach payload for.
441 */
442static PSHCLEVENTPAYLOAD shclEventPayloadDetachInternal(PSHCLEVENT pEvent)
443{
444#ifdef VBOX_STRICT
445 AssertPtrReturn(pEvent, NULL);
446#endif
447
448 PSHCLEVENTPAYLOAD pPayload = pEvent->pPayload;
449
450 pEvent->pPayload = NULL;
451
452 return pPayload;
453}
454
455/**
456 * Waits for an event to get signalled.
457 *
458 * @returns VBox status code.
459 * @param pEvent Event to wait for.
460 * @param uTimeoutMs Timeout (in ms) to wait.
461 * @param ppPayload Where to store the (allocated) event payload on success. Needs to be free'd with
462 * SharedClipboardPayloadFree(). Optional.
463 */
464int ShClEventWait(PSHCLEVENT pEvent, RTMSINTERVAL uTimeoutMs, PSHCLEVENTPAYLOAD *ppPayload)
465{
466 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
467 AssertPtrNullReturn(ppPayload, VERR_INVALID_POINTER);
468 LogFlowFuncEnter();
469
470 int rc = RTSemEventMultiWait(pEvent->hEvtMulSem, uTimeoutMs);
471 if (RT_SUCCESS(rc))
472 {
473 if (ppPayload)
474 {
475 /* Make sure to detach payload here, as the caller now owns the data. */
476 *ppPayload = shclEventPayloadDetachInternal(pEvent);
477 }
478 }
479
480 if (RT_FAILURE(rc))
481 LogRel2(("Shared Clipboard: Waiting for event %RU32 failed, rc=%Rrc\n", pEvent->idEvent, rc));
482
483 LogFlowFuncLeaveRC(rc);
484 return rc;
485}
486
487/**
488 * Retains an event by increasing its reference count.
489 *
490 * @returns New reference count, or UINT32_MAX if failed.
491 * @param pEvent Event to retain.
492 */
493uint32_t ShClEventRetain(PSHCLEVENT pEvent)
494{
495 AssertPtrReturn(pEvent, UINT32_MAX);
496 AssertReturn(ASMAtomicReadU32(&pEvent->cRefs) < 64, UINT32_MAX);
497 return ASMAtomicIncU32(&pEvent->cRefs);
498}
499
500/**
501 * Releases event by decreasing its reference count. Will be destroys once the reference count reaches 0.
502 *
503 * @returns New reference count, or UINT32_MAX if failed.
504 * @param pEvent Event to release.
505 * If the reference count reaches 0, the event will
506 * be destroyed and \a pEvent will be invalid.
507 */
508uint32_t ShClEventRelease(PSHCLEVENT pEvent)
509{
510 if (!pEvent)
511 return 0;
512
513 AssertReturn(ASMAtomicReadU32(&pEvent->cRefs) > 0, UINT32_MAX);
514
515 uint32_t const cRefs = ASMAtomicDecU32(&pEvent->cRefs);
516 if (cRefs == 0)
517 {
518 AssertPtr(pEvent->pParent);
519 int rc2 = shClEventSourceUnregisterEventInternal(pEvent->pParent, pEvent);
520 AssertRC(rc2);
521
522 return RT_SUCCESS(rc2) ? 0 : UINT32_MAX;
523 }
524
525 return cRefs;
526}
527
528/**
529 * Signals an event.
530 *
531 * @returns VBox status code.
532 * @param pEvent Event to signal.
533 * @param pPayload Event payload to associate. Takes ownership on
534 * success. Optional.
535 */
536int ShClEventSignal(PSHCLEVENT pEvent, PSHCLEVENTPAYLOAD pPayload)
537{
538 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
539
540 Assert(pEvent->pPayload == NULL);
541
542 pEvent->pPayload = pPayload;
543
544 int rc = RTSemEventMultiSignal(pEvent->hEvtMulSem);
545 if (RT_FAILURE(rc))
546 pEvent->pPayload = NULL; /* (no race condition if consumer also enters the critical section) */
547
548 LogFlowFuncLeaveRC(rc);
549 return rc;
550}
551
552int ShClUtf16LenUtf8(PCRTUTF16 pcwszSrc, size_t cwcSrc, size_t *pchLen)
553{
554 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
555 AssertPtrReturn(pchLen, VERR_INVALID_POINTER);
556
557 size_t chLen = 0;
558 int rc = RTUtf16CalcUtf8LenEx(pcwszSrc, cwcSrc, &chLen);
559 if (RT_SUCCESS(rc))
560 *pchLen = chLen;
561 return rc;
562}
563
564int ShClConvUtf16CRLFToUtf8LF(PCRTUTF16 pcwszSrc, size_t cwcSrc,
565 char *pszBuf, size_t cbBuf, size_t *pcbLen)
566{
567 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
568 AssertReturn (cwcSrc, VERR_INVALID_PARAMETER);
569 AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
570 AssertPtrReturn(pcbLen, VERR_INVALID_POINTER);
571
572 int rc;
573
574 PRTUTF16 pwszTmp = NULL;
575 size_t cchTmp = 0;
576
577 size_t cbLen = 0;
578
579 /* How long will the converted text be? */
580 rc = ShClUtf16CRLFLenUtf8(pcwszSrc, cwcSrc, &cchTmp);
581 if (RT_SUCCESS(rc))
582 {
583 cchTmp++; /* Add space for terminator. */
584
585 pwszTmp = (PRTUTF16)RTMemAlloc(cchTmp * sizeof(RTUTF16));
586 if (pwszTmp)
587 {
588 rc = ShClConvUtf16CRLFToLF(pcwszSrc, cwcSrc, pwszTmp, cchTmp);
589 if (RT_SUCCESS(rc))
590 rc = RTUtf16ToUtf8Ex(pwszTmp + 1, cchTmp - 1, &pszBuf, cbBuf, &cbLen);
591
592 RTMemFree(reinterpret_cast<void *>(pwszTmp));
593 }
594 else
595 rc = VERR_NO_MEMORY;
596 }
597
598 if (RT_SUCCESS(rc))
599 {
600 *pcbLen = cbLen;
601 }
602
603 return rc;
604}
605
606int ShClConvUtf16LFToCRLFA(PCRTUTF16 pcwszSrc, size_t cwcSrc,
607 PRTUTF16 *ppwszDst, size_t *pcwDst)
608{
609 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
610 AssertPtrReturn(ppwszDst, VERR_INVALID_POINTER);
611 AssertPtrReturn(pcwDst, VERR_INVALID_POINTER);
612
613 PRTUTF16 pwszDst = NULL;
614 size_t cchDst;
615
616 int rc = ShClUtf16LFLenUtf8(pcwszSrc, cwcSrc, &cchDst);
617 if (RT_SUCCESS(rc))
618 {
619 pwszDst = (PRTUTF16)RTMemAlloc((cchDst + 1 /* Leave space for terminator */) * sizeof(RTUTF16));
620 if (pwszDst)
621 {
622 rc = ShClConvUtf16LFToCRLF(pcwszSrc, cwcSrc, pwszDst, cchDst + 1 /* Include terminator */);
623 }
624 else
625 rc = VERR_NO_MEMORY;
626 }
627
628 if (RT_SUCCESS(rc))
629 {
630 *ppwszDst = pwszDst;
631 *pcwDst = cchDst;
632 }
633 else
634 RTMemFree(pwszDst);
635
636 LogFlowFuncLeaveRC(rc);
637 return rc;
638}
639
640int ShClConvUtf8LFToUtf16CRLF(const char *pcszSrc, size_t cbSrc,
641 PRTUTF16 *ppwszDst, size_t *pcwDst)
642{
643 AssertPtrReturn(pcszSrc, VERR_INVALID_POINTER);
644 AssertReturn(cbSrc, VERR_INVALID_PARAMETER);
645 AssertPtrReturn(ppwszDst, VERR_INVALID_POINTER);
646 AssertPtrReturn(pcwDst, VERR_INVALID_POINTER);
647
648 /* Intermediate conversion to UTF-16. */
649 size_t cwcTmp;
650 PRTUTF16 pwcTmp = NULL;
651 int rc = RTStrToUtf16Ex(pcszSrc, cbSrc, &pwcTmp, 0, &cwcTmp);
652 if (RT_SUCCESS(rc))
653 {
654 rc = ShClConvUtf16LFToCRLFA(pwcTmp, cwcTmp, ppwszDst, pcwDst);
655 RTUtf16Free(pwcTmp);
656 }
657
658 return rc;
659}
660
661/**
662 * Converts a Latin-1 string with LF line endings into an UTF-16 string with CRLF endings.
663 *
664 * @returns VBox status code.
665 * @param pcszSrc Latin-1 string to convert.
666 * @param cbSrc Size (in bytes) of Latin-1 string to convert.
667 * @param ppwszDst Where to return the converted UTF-16 string on success.
668 * @param pcwDst Where to return the length (in UTF-16 characters) on success.
669 *
670 * @note Only converts the source until the string terminator is found (or length limit is hit).
671 */
672int ShClConvLatin1LFToUtf16CRLF(const char *pcszSrc, size_t cbSrc,
673 PRTUTF16 *ppwszDst, size_t *pcwDst)
674{
675 AssertPtrReturn(pcszSrc, VERR_INVALID_POINTER);
676 AssertReturn(cbSrc, VERR_INVALID_PARAMETER);
677 AssertPtrReturn(ppwszDst, VERR_INVALID_POINTER);
678 AssertPtrReturn(pcwDst, VERR_INVALID_POINTER);
679
680 size_t chSrc = 0;
681
682 PRTUTF16 pwszDst = NULL;
683
684 /* Calculate the space needed. */
685 size_t cwDst = 0;
686 for (size_t i = 0; i < cbSrc && pcszSrc[i] != '\0'; ++i)
687 {
688 if (pcszSrc[i] == VBOX_SHCL_LINEFEED)
689 cwDst += 2; /* Space for VBOX_SHCL_CARRIAGERETURN + VBOX_SHCL_LINEFEED. */
690 else
691 ++cwDst;
692 chSrc++;
693 }
694
695 pwszDst = (PRTUTF16)RTMemAlloc((cwDst + 1 /* Leave space for the terminator */) * sizeof(RTUTF16));
696 AssertPtrReturn(pwszDst, VERR_NO_MEMORY);
697
698 /* Do the conversion, bearing in mind that Latin-1 expands "naturally" to UTF-16. */
699 for (size_t i = 0, j = 0; i < chSrc; ++i, ++j)
700 {
701 AssertMsg(j <= cwDst, ("cbSrc=%zu, j=%u vs. cwDst=%u\n", cbSrc, j, cwDst));
702 if (pcszSrc[i] != VBOX_SHCL_LINEFEED)
703 pwszDst[j] = pcszSrc[i];
704 else
705 {
706 pwszDst[j] = VBOX_SHCL_CARRIAGERETURN;
707 pwszDst[j + 1] = VBOX_SHCL_LINEFEED;
708 ++j;
709 }
710 }
711
712 pwszDst[cwDst] = '\0'; /* Make sure we are zero-terminated. */
713
714 *ppwszDst = pwszDst;
715 *pcwDst = cwDst;
716
717 return VINF_SUCCESS;
718}
719
720int ShClConvUtf16ToUtf8HTML(PCRTUTF16 pcwszSrc, size_t cwcSrc, char **ppszDst, size_t *pcbDst)
721{
722 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
723 AssertReturn (cwcSrc, VERR_INVALID_PARAMETER);
724 AssertPtrReturn(ppszDst, VERR_INVALID_POINTER);
725 AssertPtrReturn(pcbDst, VERR_INVALID_POINTER);
726
727 int rc = VINF_SUCCESS;
728
729 size_t cwTmp = cwcSrc;
730 PCRTUTF16 pwTmp = pcwszSrc;
731
732 char *pchDst = NULL;
733 size_t cbDst = 0;
734
735 size_t i = 0;
736 while (i < cwTmp)
737 {
738 /* Find zero symbol (end of string). */
739 for (; i < cwTmp && pcwszSrc[i] != 0; i++)
740 ;
741
742 /* Convert found string. */
743 char *psz = NULL;
744 size_t cch = 0;
745 rc = RTUtf16ToUtf8Ex(pwTmp, cwTmp, &psz, pwTmp - pcwszSrc, &cch);
746 if (RT_FAILURE(rc))
747 break;
748
749 /* Append new substring. */
750 char *pchNew = (char *)RTMemRealloc(pchDst, cbDst + cch + 1);
751 if (!pchNew)
752 {
753 RTStrFree(psz);
754 rc = VERR_NO_MEMORY;
755 break;
756 }
757
758 pchDst = pchNew;
759 memcpy(pchDst + cbDst, psz, cch + 1);
760
761 RTStrFree(psz);
762
763 cbDst += cch + 1;
764
765 /* Skip zero symbols. */
766 for (; i < cwTmp && pcwszSrc[i] == 0; i++)
767 ;
768
769 /* Remember start of string. */
770 pwTmp += i;
771 }
772
773 if (RT_SUCCESS(rc))
774 {
775 *ppszDst = pchDst;
776 *pcbDst = cbDst;
777
778 return VINF_SUCCESS;
779 }
780
781 RTMemFree(pchDst);
782
783 return rc;
784}
785
786int ShClUtf16LFLenUtf8(PCRTUTF16 pcwszSrc, size_t cwSrc, size_t *pchLen)
787{
788 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
789 AssertPtrReturn(pchLen, VERR_INVALID_POINTER);
790
791 AssertMsgReturn(pcwszSrc[0] != VBOX_SHCL_UTF16BEMARKER,
792 ("Big endian UTF-16 not supported yet\n"), VERR_NOT_SUPPORTED);
793
794 size_t cLen = 0;
795
796 /* Don't copy the endian marker. */
797 size_t i = pcwszSrc[0] == VBOX_SHCL_UTF16LEMARKER ? 1 : 0;
798
799 /* Calculate the size of the destination text string. */
800 /* Is this Utf16 or Utf16-LE? */
801 for (; i < cwSrc; ++i, ++cLen)
802 {
803 /* Check for a single line feed */
804 if (pcwszSrc[i] == VBOX_SHCL_LINEFEED)
805 ++cLen;
806#ifdef RT_OS_DARWIN
807 /* Check for a single carriage return (MacOS) */
808 if (pcwszSrc[i] == VBOX_SHCL_CARRIAGERETURN)
809 ++cLen;
810#endif
811 if (pcwszSrc[i] == 0)
812 {
813 /* Don't count this, as we do so below. */
814 break;
815 }
816 }
817
818 *pchLen = cLen;
819
820 return VINF_SUCCESS;
821}
822
823int ShClUtf16CRLFLenUtf8(PCRTUTF16 pcwszSrc, size_t cwSrc, size_t *pchLen)
824{
825 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
826 AssertReturn(cwSrc, VERR_INVALID_PARAMETER);
827 AssertPtrReturn(pchLen, VERR_INVALID_POINTER);
828
829 AssertMsgReturn(pcwszSrc[0] != VBOX_SHCL_UTF16BEMARKER,
830 ("Big endian UTF-16 not supported yet\n"), VERR_NOT_SUPPORTED);
831
832 size_t cLen = 0;
833
834 /* Calculate the size of the destination text string. */
835 /* Is this Utf16 or Utf16-LE? */
836 if (pcwszSrc[0] == VBOX_SHCL_UTF16LEMARKER)
837 cLen = 0;
838 else
839 cLen = 1;
840
841 for (size_t i = 0; i < cwSrc; ++i, ++cLen)
842 {
843 if ( (i + 1 < cwSrc)
844 && (pcwszSrc[i] == VBOX_SHCL_CARRIAGERETURN)
845 && (pcwszSrc[i + 1] == VBOX_SHCL_LINEFEED))
846 {
847 ++i;
848 }
849 if (pcwszSrc[i] == 0)
850 break;
851 }
852
853 *pchLen = cLen;
854
855 return VINF_SUCCESS;
856}
857
858int ShClConvUtf16LFToCRLF(PCRTUTF16 pcwszSrc, size_t cwcSrc, PRTUTF16 pu16Dst, size_t cwDst)
859{
860 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
861 AssertPtrReturn(pu16Dst, VERR_INVALID_POINTER);
862 AssertReturn(cwDst, VERR_INVALID_PARAMETER);
863
864 AssertMsgReturn(pcwszSrc[0] != VBOX_SHCL_UTF16BEMARKER,
865 ("Big endian UTF-16 not supported yet\n"), VERR_NOT_SUPPORTED);
866
867 int rc = VINF_SUCCESS;
868
869 /* Don't copy the endian marker. */
870 size_t i = pcwszSrc[0] == VBOX_SHCL_UTF16LEMARKER ? 1 : 0;
871 size_t j = 0;
872
873 for (; i < cwcSrc; ++i, ++j)
874 {
875 /* Don't copy the null byte, as we add it below. */
876 if (pcwszSrc[i] == 0)
877 break;
878
879 /* Not enough space in destination? */
880 if (j == cwDst)
881 {
882 rc = VERR_BUFFER_OVERFLOW;
883 break;
884 }
885
886 if (pcwszSrc[i] == VBOX_SHCL_LINEFEED)
887 {
888 pu16Dst[j] = VBOX_SHCL_CARRIAGERETURN;
889 ++j;
890
891 /* Not enough space in destination? */
892 if (j == cwDst)
893 {
894 rc = VERR_BUFFER_OVERFLOW;
895 break;
896 }
897 }
898#ifdef RT_OS_DARWIN
899 /* Check for a single carriage return (MacOS) */
900 else if (pcwszSrc[i] == VBOX_SHCL_CARRIAGERETURN)
901 {
902 /* Set CR.r */
903 pu16Dst[j] = VBOX_SHCL_CARRIAGERETURN;
904 ++j;
905
906 /* Not enough space in destination? */
907 if (j == cwDst)
908 {
909 rc = VERR_BUFFER_OVERFLOW;
910 break;
911 }
912
913 /* Add line feed. */
914 pu16Dst[j] = VBOX_SHCL_LINEFEED;
915 continue;
916 }
917#endif
918 pu16Dst[j] = pcwszSrc[i];
919 }
920
921 if (j == cwDst)
922 rc = VERR_BUFFER_OVERFLOW;
923
924 if (RT_SUCCESS(rc))
925 {
926 /* Add terminator. */
927 pu16Dst[j] = 0;
928 }
929
930 return rc;
931}
932
933int ShClConvUtf16CRLFToLF(PCRTUTF16 pcwszSrc, size_t cwcSrc, PRTUTF16 pu16Dst, size_t cwDst)
934{
935 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
936 AssertReturn(cwcSrc, VERR_INVALID_PARAMETER);
937 AssertPtrReturn(pu16Dst, VERR_INVALID_POINTER);
938 AssertReturn(cwDst, VERR_INVALID_PARAMETER);
939
940 AssertMsgReturn(pcwszSrc[0] != VBOX_SHCL_UTF16BEMARKER,
941 ("Big endian UTF-16 not supported yet\n"), VERR_NOT_SUPPORTED);
942
943 /* Prepend the Utf16 byte order marker if it is missing. */
944 size_t cwDstPos;
945 if (pcwszSrc[0] == VBOX_SHCL_UTF16LEMARKER)
946 {
947 cwDstPos = 0;
948 }
949 else
950 {
951 pu16Dst[0] = VBOX_SHCL_UTF16LEMARKER;
952 cwDstPos = 1;
953 }
954
955 for (size_t i = 0; i < cwcSrc; ++i, ++cwDstPos)
956 {
957 if (pcwszSrc[i] == 0)
958 break;
959
960 if (cwDstPos == cwDst)
961 return VERR_BUFFER_OVERFLOW;
962
963 if ( (i + 1 < cwcSrc)
964 && (pcwszSrc[i] == VBOX_SHCL_CARRIAGERETURN)
965 && (pcwszSrc[i + 1] == VBOX_SHCL_LINEFEED))
966 {
967 ++i;
968 }
969
970 pu16Dst[cwDstPos] = pcwszSrc[i];
971 }
972
973 if (cwDstPos == cwDst)
974 return VERR_BUFFER_OVERFLOW;
975
976 /* Add terminating zero. */
977 pu16Dst[cwDstPos] = 0;
978
979 return VINF_SUCCESS;
980}
981
982int ShClDibToBmp(const void *pvSrc, size_t cbSrc, void **ppvDest, size_t *pcbDest)
983{
984 AssertPtrReturn(pvSrc, VERR_INVALID_POINTER);
985 AssertReturn(cbSrc, VERR_INVALID_PARAMETER);
986 AssertPtrReturn(ppvDest, VERR_INVALID_POINTER);
987 AssertPtrReturn(pcbDest, VERR_INVALID_POINTER);
988
989 PBMPWIN3XINFOHDR coreHdr = (PBMPWIN3XINFOHDR)pvSrc;
990 /** @todo Support all the many versions of the DIB headers. */
991 if ( cbSrc < sizeof(BMPWIN3XINFOHDR)
992 || RT_LE2H_U32(coreHdr->cbSize) < sizeof(BMPWIN3XINFOHDR)
993 || RT_LE2H_U32(coreHdr->cbSize) != sizeof(BMPWIN3XINFOHDR))
994 {
995 return VERR_INVALID_PARAMETER;
996 }
997
998 size_t offPixel = sizeof(BMPFILEHDR)
999 + RT_LE2H_U32(coreHdr->cbSize)
1000 + RT_LE2H_U32(coreHdr->cClrUsed) * sizeof(uint32_t);
1001 if (cbSrc < offPixel)
1002 return VERR_INVALID_PARAMETER;
1003
1004 size_t cbDst = sizeof(BMPFILEHDR) + cbSrc;
1005
1006 void *pvDest = RTMemAlloc(cbDst);
1007 if (!pvDest)
1008 return VERR_NO_MEMORY;
1009
1010 PBMPFILEHDR fileHdr = (PBMPFILEHDR)pvDest;
1011
1012 fileHdr->uType = BMP_HDR_MAGIC;
1013 fileHdr->cbFileSize = (uint32_t)RT_H2LE_U32(cbDst);
1014 fileHdr->Reserved1 = 0;
1015 fileHdr->Reserved2 = 0;
1016 fileHdr->offBits = (uint32_t)RT_H2LE_U32(offPixel);
1017
1018 memcpy((uint8_t *)pvDest + sizeof(BMPFILEHDR), pvSrc, cbSrc);
1019
1020 *ppvDest = pvDest;
1021 *pcbDest = cbDst;
1022
1023 return VINF_SUCCESS;
1024}
1025
1026int ShClBmpGetDib(const void *pvSrc, size_t cbSrc, const void **ppvDest, size_t *pcbDest)
1027{
1028 AssertPtrReturn(pvSrc, VERR_INVALID_POINTER);
1029 AssertReturn(cbSrc, VERR_INVALID_PARAMETER);
1030 AssertPtrReturn(ppvDest, VERR_INVALID_POINTER);
1031 AssertPtrReturn(pcbDest, VERR_INVALID_POINTER);
1032
1033 PBMPFILEHDR pBmpHdr = (PBMPFILEHDR)pvSrc;
1034 if ( cbSrc < sizeof(BMPFILEHDR)
1035 || pBmpHdr->uType != BMP_HDR_MAGIC
1036 || RT_LE2H_U32(pBmpHdr->cbFileSize) != cbSrc)
1037 {
1038 return VERR_INVALID_PARAMETER;
1039 }
1040
1041 *ppvDest = ((uint8_t *)pvSrc) + sizeof(BMPFILEHDR);
1042 *pcbDest = cbSrc - sizeof(BMPFILEHDR);
1043
1044 return VINF_SUCCESS;
1045}
1046
1047#ifdef LOG_ENABLED
1048
1049int ShClDbgDumpHtml(const char *pcszSrc, size_t cbSrc)
1050{
1051 int rc = VINF_SUCCESS;
1052 char *pszBuf = (char *)RTMemTmpAllocZ(cbSrc + 1);
1053 if (pszBuf)
1054 {
1055 memcpy(pszBuf, pcszSrc, cbSrc);
1056 pszBuf[cbSrc] = '\0';
1057 for (size_t off = 0; off < cbSrc; ++off)
1058 if (pszBuf[off] == '\n' || pszBuf[off] == '\r')
1059 pszBuf[off] = ' ';
1060 LogFunc(("Removed \\r\\n: %s\n", pszBuf));
1061 RTMemTmpFree(pszBuf);
1062 }
1063 else
1064 rc = VERR_NO_MEMORY;
1065 return rc;
1066}
1067
1068void ShClDbgDumpData(const void *pv, size_t cb, SHCLFORMAT uFormat)
1069{
1070 if (LogIsEnabled())
1071 {
1072 if (uFormat & VBOX_SHCL_FMT_UNICODETEXT)
1073 {
1074 LogFunc(("VBOX_SHCL_FMT_UNICODETEXT:\n"));
1075 if (pv && cb)
1076 LogFunc(("%ls\n", pv));
1077 else
1078 LogFunc(("%p %zu\n", pv, cb));
1079 }
1080 else if (uFormat & VBOX_SHCL_FMT_BITMAP)
1081 LogFunc(("VBOX_SHCL_FMT_BITMAP\n"));
1082 else if (uFormat & VBOX_SHCL_FMT_HTML)
1083 {
1084 LogFunc(("VBOX_SHCL_FMT_HTML:\n"));
1085 if (pv && cb)
1086 {
1087 LogFunc(("%s\n", pv));
1088 ShClDbgDumpHtml((const char *)pv, cb);
1089 }
1090 else
1091 LogFunc(("%p %zu\n", pv, cb));
1092 }
1093 else
1094 LogFunc(("Invalid format %02X\n", uFormat));
1095 }
1096}
1097
1098#endif /* LOG_ENABLED */
1099
1100/**
1101 * Translates a Shared Clipboard host function number to a string.
1102 *
1103 * @returns Function ID string name.
1104 * @param uFn The function to translate.
1105 */
1106const char *ShClHostFunctionToStr(uint32_t uFn)
1107{
1108 switch (uFn)
1109 {
1110 RT_CASE_RET_STR(VBOX_SHCL_HOST_FN_SET_MODE);
1111 RT_CASE_RET_STR(VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE);
1112 RT_CASE_RET_STR(VBOX_SHCL_HOST_FN_SET_HEADLESS);
1113 RT_CASE_RET_STR(VBOX_SHCL_HOST_FN_CANCEL);
1114 RT_CASE_RET_STR(VBOX_SHCL_HOST_FN_ERROR);
1115 }
1116 return "Unknown";
1117}
1118
1119/**
1120 * Translates a Shared Clipboard host message enum to a string.
1121 *
1122 * @returns Message ID string name.
1123 * @param uMsg The message to translate.
1124 */
1125const char *ShClHostMsgToStr(uint32_t uMsg)
1126{
1127 switch (uMsg)
1128 {
1129 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_QUIT);
1130 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_READ_DATA);
1131 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_FORMATS_REPORT);
1132 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_CANCELED);
1133 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_READ_DATA_CID);
1134 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_STATUS);
1135 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_READ);
1136 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_WRITE);
1137 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_READ);
1138 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_WRITE);
1139 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_OPEN);
1140 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_CLOSE);
1141 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_READ);
1142 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_WRITE);
1143 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ);
1144 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_WRITE);
1145 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_OPEN);
1146 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_CLOSE);
1147 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_READ);
1148 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_WRITE);
1149 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_CANCEL);
1150 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_ERROR);
1151 }
1152 return "Unknown";
1153}
1154
1155/**
1156 * Translates a Shared Clipboard guest message enum to a string.
1157 *
1158 * @returns Message ID string name.
1159 * @param uMsg The message to translate.
1160 */
1161const char *ShClGuestMsgToStr(uint32_t uMsg)
1162{
1163 switch (uMsg)
1164 {
1165 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT);
1166 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_REPORT_FORMATS);
1167 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_DATA_READ);
1168 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_DATA_WRITE);
1169 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_CONNECT);
1170 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_REPORT_FEATURES);
1171 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_QUERY_FEATURES);
1172 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT);
1173 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT);
1174 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_MSG_GET);
1175 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_MSG_CANCEL);
1176 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_REPLY);
1177 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_READ);
1178 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_WRITE);
1179 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_READ);
1180 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_WRITE);
1181 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_OPEN);
1182 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_CLOSE);
1183 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_HDR_READ);
1184 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_HDR_WRITE);
1185 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_ENTRY_READ);
1186 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_ENTRY_WRITE);
1187 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_OBJ_OPEN);
1188 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_OBJ_CLOSE);
1189 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_OBJ_READ);
1190 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_OBJ_WRITE);
1191 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_ERROR);
1192 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_NEGOTIATE_CHUNK_SIZE);
1193 }
1194 return "Unknown";
1195}
1196
1197/**
1198 * Converts Shared Clipboard formats to a string.
1199 *
1200 * @returns Stringified Shared Clipboard formats, or NULL on failure. Must be free'd with RTStrFree().
1201 * @param fFormats Shared Clipboard formats to convert.
1202 *
1203 */
1204char *ShClFormatsToStrA(SHCLFORMATS fFormats)
1205{
1206#define APPEND_FMT_TO_STR(_aFmt) \
1207 if (fFormats & VBOX_SHCL_FMT_##_aFmt) \
1208 { \
1209 if (pszFmts) \
1210 { \
1211 rc2 = RTStrAAppend(&pszFmts, ", "); \
1212 if (RT_FAILURE(rc2)) \
1213 break; \
1214 } \
1215 \
1216 rc2 = RTStrAAppend(&pszFmts, #_aFmt); \
1217 if (RT_FAILURE(rc2)) \
1218 break; \
1219 }
1220
1221 char *pszFmts = NULL;
1222 int rc2 = VINF_SUCCESS;
1223
1224 do
1225 {
1226 APPEND_FMT_TO_STR(UNICODETEXT);
1227 APPEND_FMT_TO_STR(BITMAP);
1228 APPEND_FMT_TO_STR(HTML);
1229# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1230 APPEND_FMT_TO_STR(URI_LIST);
1231# endif
1232
1233 } while (0);
1234
1235 if (!pszFmts)
1236 rc2 = RTStrAAppend(&pszFmts, "NONE");
1237
1238 if ( RT_FAILURE(rc2)
1239 && pszFmts)
1240 {
1241 RTStrFree(pszFmts);
1242 pszFmts = NULL;
1243 }
1244
1245#undef APPEND_FMT_TO_STR
1246
1247 return pszFmts;
1248}
1249
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette