VirtualBox

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

Last change on this file since 100484 was 100328, checked in by vboxsync, 19 months ago

Shared Clipboard: Implemented a (very) basic and generic clipboard cache; to be used on the guest and host side. 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: 40.9 KB
Line 
1/* $Id: clipboard-common.cpp 100328 2023-06-29 09:47:33Z 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
1250
1251/*********************************************************************************************************************************
1252* Shared Clipboard Cache *
1253*********************************************************************************************************************************/
1254
1255/**
1256 * Returns (mutable) data of a cache entry.
1257 *
1258 * @param pCacheEntry Cache entry to return data for.
1259 * @param pvData Where to return the (mutable) data pointer.
1260 * @param pcbData Where to return the data size.
1261 */
1262void ShClCacheEntryGet(PSHCLCACHEENTRY pCacheEntry, void **pvData, size_t *pcbData)
1263{
1264 AssertPtrReturnVoid(pCacheEntry);
1265 AssertPtrReturnVoid(pvData);
1266 AssertReturnVoid(pcbData);
1267
1268 *pvData = pCacheEntry->pvData;
1269 *pcbData = pCacheEntry->cbData;
1270}
1271
1272/**
1273 * Initializes a cache entry.
1274 *
1275 * @returns VBox status code.
1276 * @param pCacheEntry Cache entry to init.
1277 * @param pvData Data to copy to entry. Can be NULL to initialize an emptry entry.
1278 * @param cbData Size (in bytes) of \a pvData to copy to entry. Must be 0 if \a pvData is NULL.
1279 */
1280static int shClCacheEntryInit(PSHCLCACHEENTRY pCacheEntry, const void *pvData, size_t cbData)
1281{
1282 AssertReturn(RT_VALID_PTR(pvData) || cbData == 0, VERR_INVALID_PARAMETER);
1283
1284 RT_BZERO(pCacheEntry, sizeof(SHCLCACHEENTRY));
1285
1286 if (pvData)
1287 {
1288 pCacheEntry->pvData = RTMemDup(pvData, cbData);
1289 AssertPtrReturn(pCacheEntry->pvData, VERR_NO_MEMORY);
1290 pCacheEntry->cbData = cbData;
1291 }
1292
1293 return VINF_SUCCESS;
1294}
1295
1296/**
1297 * Returns whether a cache entry is valid (cache hit) or not.
1298 *
1299 * @returns \c true if valid, or \c false if not.
1300 * @param pCacheEntry Cache entry to check for.
1301 */
1302DECLINLINE(bool) shClCacheEntryIsValid(PSHCLCACHEENTRY pCacheEntry)
1303{
1304 return pCacheEntry->pvData != NULL;
1305}
1306
1307/**
1308 * Destroys a cache entry.
1309 *
1310 * @param pCacheEntry Cache entry to destroy.
1311 */
1312DECLINLINE(void) shClCacheEntryDestroy(PSHCLCACHEENTRY pCacheEntry)
1313{
1314 if (pCacheEntry->pvData)
1315 {
1316 Assert(pCacheEntry->cbData);
1317 RTMemFree(pCacheEntry->pvData);
1318 pCacheEntry->pvData = NULL;
1319 pCacheEntry->cbData = 0;
1320 }
1321}
1322
1323/**
1324 * Initializes a cache.
1325 *
1326 * @param pCache Cache to init.
1327 */
1328void ShClCacheInit(PSHCLCACHE pCache)
1329{
1330 AssertPtrReturnVoid(pCache);
1331
1332 RT_BZERO(pCache, sizeof(SHCLCACHE));
1333
1334 for (size_t i = 0; i < RT_ELEMENTS(pCache->aEntries); i++)
1335 shClCacheEntryInit(&pCache->aEntries[i], NULL, 0);
1336}
1337
1338/**
1339 * Destroys all entries of a cache.
1340 *
1341 * @param pCache Cache to destroy entries for.
1342 */
1343DECLINLINE(void) shClCacheDestroyEntries(PSHCLCACHE pCache)
1344{
1345 for (size_t i = 0; i < RT_ELEMENTS(pCache->aEntries); i++)
1346 shClCacheEntryDestroy(&pCache->aEntries[i]);
1347}
1348
1349/**
1350 * Destroys a cache.
1351 *
1352 * @param pCache Cache to destroy.
1353 */
1354void ShClCacheDestroy(PSHCLCACHE pCache)
1355{
1356 AssertPtrReturnVoid(pCache);
1357
1358 shClCacheDestroyEntries(pCache);
1359
1360 RT_BZERO(pCache, sizeof(SHCLCACHE));
1361}
1362
1363/**
1364 * Invalidates a cache.
1365 *
1366 * @param pCache Cache to invalidate.
1367 */
1368void ShClCacheInvalidate(PSHCLCACHE pCache)
1369{
1370 AssertPtrReturnVoid(pCache);
1371
1372 shClCacheDestroyEntries(pCache);
1373}
1374
1375/**
1376 * Invalidates a specific cache entry.
1377 *
1378 * @param pCache Cache to invalidate.
1379 * @param uFmt Format to invalidate entry for.
1380 */
1381void ShClCacheInvalidateEntry(PSHCLCACHE pCache, SHCLFORMAT uFmt)
1382{
1383 AssertPtrReturnVoid(pCache);
1384 AssertReturnVoid(uFmt < VBOX_SHCL_FMT_MAX);
1385 shClCacheEntryDestroy(&pCache->aEntries[uFmt]);
1386}
1387
1388/**
1389 * Gets an entry for a Shared Clipboard format.
1390 *
1391 * @returns Pointer to entry if cached, or NULL if not in cache (cache miss).
1392 * @param pCache Cache to get entry for.
1393 * @param uFmt Format to get entry for.
1394 */
1395PSHCLCACHEENTRY ShClCacheGet(PSHCLCACHE pCache, SHCLFORMAT uFmt)
1396{
1397 AssertReturn(uFmt < VBOX_SHCL_FMT_MAX, NULL);
1398 return shClCacheEntryIsValid(&pCache->aEntries[uFmt]) ? &pCache->aEntries[uFmt] : NULL;
1399}
1400
1401/**
1402 * Sets data to cache for a specific clipboard format.
1403 *
1404 * @returns VBox status code.
1405 * @param pCache Cache to set data for.
1406 * @param uFmt Clipboard format to set data for.
1407 * @param pvData Data to set.
1408 * @param cbData Size (in bytes) of data to set.
1409 */
1410int ShClCacheSet(PSHCLCACHE pCache, SHCLFORMAT uFmt, const void *pvData, size_t cbData)
1411{
1412 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
1413
1414 if (!pvData) /* Nothing to cache? */
1415 return VINF_SUCCESS;
1416
1417 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1418 AssertReturn(uFmt < VBOX_SHCL_FMT_MAX, VERR_INVALID_PARAMETER);
1419 AssertReturn(shClCacheEntryIsValid(&pCache->aEntries[uFmt]) == false, VERR_ALREADY_EXISTS);
1420
1421 return shClCacheEntryInit(&pCache->aEntries[uFmt], pvData, cbData);
1422}
1423
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