VirtualBox

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

Last change on this file since 100566 was 100566, checked in by vboxsync, 11 months ago

Shared Clipboard: Added ShClEventWaitEx() + ShClEventSignalEx() for easier error propagation when waiting for events, plus new error codes VERR_SHCLPB_EVENT_FAILED + VERR_SHCLPB_GUEST_ERROR. 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: 42.3 KB
Line 
1/* $Id: clipboard-common.cpp 100566 2023-07-13 18:50:43Z 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 * @retval VERR_SHCLPB_EVENT_FAILED if the event has a set error code.
460 * @param pEvent Event to wait for.
461 * @param uTimeoutMs Timeout (in ms) to wait.
462 * @param pRc Where to return the event rc. Optional and can be NULL.
463 * @param ppPayload Where to store the (allocated) event payload on success. Needs to be free'd with
464 * SharedClipboardPayloadFree(). Optional.
465 */
466int ShClEventWaitEx(PSHCLEVENT pEvent, RTMSINTERVAL uTimeoutMs, int *pRc, PSHCLEVENTPAYLOAD *ppPayload)
467{
468 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
469 AssertPtrNullReturn(ppPayload, VERR_INVALID_POINTER);
470 LogFlowFuncEnter();
471
472 int rc = RTSemEventMultiWait(pEvent->hEvtMulSem, uTimeoutMs);
473 if (RT_SUCCESS(rc))
474 {
475 if (RT_FAILURE(pEvent->rc))
476 rc = VERR_SHCLPB_EVENT_FAILED;
477
478 if (pRc)
479 *pRc = pEvent->rc;
480
481 if (ppPayload)
482 {
483 /* Make sure to detach payload here, as the caller now owns the data. */
484 *ppPayload = shclEventPayloadDetachInternal(pEvent);
485 }
486 }
487
488 if (RT_FAILURE(rc))
489 LogRel2(("Shared Clipboard: Waiting for event %RU32 failed, rc=%Rrc\n", pEvent->idEvent, rc));
490
491 LogFlowFuncLeaveRC(rc);
492 return rc;
493}
494
495/**
496 * Waits for an event to get signalled.
497 *
498 * @returns VBox status code.
499 * @retval VERR_SHCLPB_EVENT_FAILED if the event has a set error code.
500 * @param pEvent Event to wait for.
501 * @param uTimeoutMs Timeout (in ms) to wait.
502 * @param ppPayload Where to store the (allocated) event payload on success. Needs to be free'd with
503 * SharedClipboardPayloadFree(). Optional.
504 */
505int ShClEventWait(PSHCLEVENT pEvent, RTMSINTERVAL uTimeoutMs, PSHCLEVENTPAYLOAD *ppPayload)
506{
507 return ShClEventWaitEx(pEvent, uTimeoutMs, NULL /* pRc */, ppPayload);
508}
509
510/**
511 * Retains an event by increasing its reference count.
512 *
513 * @returns New reference count, or UINT32_MAX if failed.
514 * @param pEvent Event to retain.
515 */
516uint32_t ShClEventRetain(PSHCLEVENT pEvent)
517{
518 AssertPtrReturn(pEvent, UINT32_MAX);
519 AssertReturn(ASMAtomicReadU32(&pEvent->cRefs) < 64, UINT32_MAX);
520 return ASMAtomicIncU32(&pEvent->cRefs);
521}
522
523/**
524 * Releases event by decreasing its reference count. Will be destroys once the reference count reaches 0.
525 *
526 * @returns New reference count, or UINT32_MAX if failed.
527 * @param pEvent Event to release.
528 * If the reference count reaches 0, the event will
529 * be destroyed and \a pEvent will be invalid.
530 */
531uint32_t ShClEventRelease(PSHCLEVENT pEvent)
532{
533 if (!pEvent)
534 return 0;
535
536 AssertReturn(ASMAtomicReadU32(&pEvent->cRefs) > 0, UINT32_MAX);
537
538 uint32_t const cRefs = ASMAtomicDecU32(&pEvent->cRefs);
539 if (cRefs == 0)
540 {
541 AssertPtr(pEvent->pParent);
542 int rc2 = shClEventSourceUnregisterEventInternal(pEvent->pParent, pEvent);
543 AssertRC(rc2);
544
545 return RT_SUCCESS(rc2) ? 0 : UINT32_MAX;
546 }
547
548 return cRefs;
549}
550
551/**
552 * Signals an event, extended version.
553 *
554 * @returns VBox status code.
555 * @param pEvent Event to signal.
556 * @param rc Result code to set.
557 * @param pPayload Event payload to associate. Takes ownership on
558 * success. Optional.
559 */
560int ShClEventSignalEx(PSHCLEVENT pEvent, int rc, PSHCLEVENTPAYLOAD pPayload)
561{
562 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
563
564 Assert(pEvent->pPayload == NULL);
565
566 pEvent->rc = rc;
567 pEvent->pPayload = pPayload;
568
569 int rc2 = RTSemEventMultiSignal(pEvent->hEvtMulSem);
570 if (RT_FAILURE(rc2))
571 pEvent->pPayload = NULL; /* (no race condition if consumer also enters the critical section) */
572
573 LogFlowFuncLeaveRC(rc2);
574 return rc2;
575}
576
577/**
578 * Signals an event.
579 *
580 * @returns VBox status code.
581 * @param pEvent Event to signal.
582 * @param pPayload Event payload to associate. Takes ownership on
583 * success. Optional.
584 */
585int ShClEventSignal(PSHCLEVENT pEvent, PSHCLEVENTPAYLOAD pPayload)
586{
587 return ShClEventSignalEx(pEvent, VINF_SUCCESS, pPayload);
588}
589
590int ShClUtf16LenUtf8(PCRTUTF16 pcwszSrc, size_t cwcSrc, size_t *pchLen)
591{
592 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
593 AssertPtrReturn(pchLen, VERR_INVALID_POINTER);
594
595 size_t chLen = 0;
596 int rc = RTUtf16CalcUtf8LenEx(pcwszSrc, cwcSrc, &chLen);
597 if (RT_SUCCESS(rc))
598 *pchLen = chLen;
599 return rc;
600}
601
602int ShClConvUtf16CRLFToUtf8LF(PCRTUTF16 pcwszSrc, size_t cwcSrc,
603 char *pszBuf, size_t cbBuf, size_t *pcbLen)
604{
605 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
606 AssertReturn (cwcSrc, VERR_INVALID_PARAMETER);
607 AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
608 AssertPtrReturn(pcbLen, VERR_INVALID_POINTER);
609
610 int rc;
611
612 PRTUTF16 pwszTmp = NULL;
613 size_t cchTmp = 0;
614
615 size_t cbLen = 0;
616
617 /* How long will the converted text be? */
618 rc = ShClUtf16CRLFLenUtf8(pcwszSrc, cwcSrc, &cchTmp);
619 if (RT_SUCCESS(rc))
620 {
621 cchTmp++; /* Add space for terminator. */
622
623 pwszTmp = (PRTUTF16)RTMemAlloc(cchTmp * sizeof(RTUTF16));
624 if (pwszTmp)
625 {
626 rc = ShClConvUtf16CRLFToLF(pcwszSrc, cwcSrc, pwszTmp, cchTmp);
627 if (RT_SUCCESS(rc))
628 rc = RTUtf16ToUtf8Ex(pwszTmp + 1, cchTmp - 1, &pszBuf, cbBuf, &cbLen);
629
630 RTMemFree(reinterpret_cast<void *>(pwszTmp));
631 }
632 else
633 rc = VERR_NO_MEMORY;
634 }
635
636 if (RT_SUCCESS(rc))
637 {
638 *pcbLen = cbLen;
639 }
640
641 return rc;
642}
643
644int ShClConvUtf16LFToCRLFA(PCRTUTF16 pcwszSrc, size_t cwcSrc,
645 PRTUTF16 *ppwszDst, size_t *pcwDst)
646{
647 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
648 AssertPtrReturn(ppwszDst, VERR_INVALID_POINTER);
649 AssertPtrReturn(pcwDst, VERR_INVALID_POINTER);
650
651 PRTUTF16 pwszDst = NULL;
652 size_t cchDst;
653
654 int rc = ShClUtf16LFLenUtf8(pcwszSrc, cwcSrc, &cchDst);
655 if (RT_SUCCESS(rc))
656 {
657 pwszDst = (PRTUTF16)RTMemAlloc((cchDst + 1 /* Leave space for terminator */) * sizeof(RTUTF16));
658 if (pwszDst)
659 {
660 rc = ShClConvUtf16LFToCRLF(pcwszSrc, cwcSrc, pwszDst, cchDst + 1 /* Include terminator */);
661 }
662 else
663 rc = VERR_NO_MEMORY;
664 }
665
666 if (RT_SUCCESS(rc))
667 {
668 *ppwszDst = pwszDst;
669 *pcwDst = cchDst;
670 }
671 else
672 RTMemFree(pwszDst);
673
674 LogFlowFuncLeaveRC(rc);
675 return rc;
676}
677
678int ShClConvUtf8LFToUtf16CRLF(const char *pcszSrc, size_t cbSrc,
679 PRTUTF16 *ppwszDst, size_t *pcwDst)
680{
681 AssertPtrReturn(pcszSrc, VERR_INVALID_POINTER);
682 AssertReturn(cbSrc, VERR_INVALID_PARAMETER);
683 AssertPtrReturn(ppwszDst, VERR_INVALID_POINTER);
684 AssertPtrReturn(pcwDst, VERR_INVALID_POINTER);
685
686 /* Intermediate conversion to UTF-16. */
687 size_t cwcTmp;
688 PRTUTF16 pwcTmp = NULL;
689 int rc = RTStrToUtf16Ex(pcszSrc, cbSrc, &pwcTmp, 0, &cwcTmp);
690 if (RT_SUCCESS(rc))
691 {
692 rc = ShClConvUtf16LFToCRLFA(pwcTmp, cwcTmp, ppwszDst, pcwDst);
693 RTUtf16Free(pwcTmp);
694 }
695
696 return rc;
697}
698
699/**
700 * Converts a Latin-1 string with LF line endings into an UTF-16 string with CRLF endings.
701 *
702 * @returns VBox status code.
703 * @param pcszSrc Latin-1 string to convert.
704 * @param cbSrc Size (in bytes) of Latin-1 string to convert.
705 * @param ppwszDst Where to return the converted UTF-16 string on success.
706 * @param pcwDst Where to return the length (in UTF-16 characters) on success.
707 *
708 * @note Only converts the source until the string terminator is found (or length limit is hit).
709 */
710int ShClConvLatin1LFToUtf16CRLF(const char *pcszSrc, size_t cbSrc,
711 PRTUTF16 *ppwszDst, size_t *pcwDst)
712{
713 AssertPtrReturn(pcszSrc, VERR_INVALID_POINTER);
714 AssertReturn(cbSrc, VERR_INVALID_PARAMETER);
715 AssertPtrReturn(ppwszDst, VERR_INVALID_POINTER);
716 AssertPtrReturn(pcwDst, VERR_INVALID_POINTER);
717
718 size_t chSrc = 0;
719
720 PRTUTF16 pwszDst = NULL;
721
722 /* Calculate the space needed. */
723 size_t cwDst = 0;
724 for (size_t i = 0; i < cbSrc && pcszSrc[i] != '\0'; ++i)
725 {
726 if (pcszSrc[i] == VBOX_SHCL_LINEFEED)
727 cwDst += 2; /* Space for VBOX_SHCL_CARRIAGERETURN + VBOX_SHCL_LINEFEED. */
728 else
729 ++cwDst;
730 chSrc++;
731 }
732
733 pwszDst = (PRTUTF16)RTMemAlloc((cwDst + 1 /* Leave space for the terminator */) * sizeof(RTUTF16));
734 AssertPtrReturn(pwszDst, VERR_NO_MEMORY);
735
736 /* Do the conversion, bearing in mind that Latin-1 expands "naturally" to UTF-16. */
737 for (size_t i = 0, j = 0; i < chSrc; ++i, ++j)
738 {
739 AssertMsg(j <= cwDst, ("cbSrc=%zu, j=%u vs. cwDst=%u\n", cbSrc, j, cwDst));
740 if (pcszSrc[i] != VBOX_SHCL_LINEFEED)
741 pwszDst[j] = pcszSrc[i];
742 else
743 {
744 pwszDst[j] = VBOX_SHCL_CARRIAGERETURN;
745 pwszDst[j + 1] = VBOX_SHCL_LINEFEED;
746 ++j;
747 }
748 }
749
750 pwszDst[cwDst] = '\0'; /* Make sure we are zero-terminated. */
751
752 *ppwszDst = pwszDst;
753 *pcwDst = cwDst;
754
755 return VINF_SUCCESS;
756}
757
758int ShClConvUtf16ToUtf8HTML(PCRTUTF16 pcwszSrc, size_t cwcSrc, char **ppszDst, size_t *pcbDst)
759{
760 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
761 AssertReturn (cwcSrc, VERR_INVALID_PARAMETER);
762 AssertPtrReturn(ppszDst, VERR_INVALID_POINTER);
763 AssertPtrReturn(pcbDst, VERR_INVALID_POINTER);
764
765 int rc = VINF_SUCCESS;
766
767 size_t cwTmp = cwcSrc;
768 PCRTUTF16 pwTmp = pcwszSrc;
769
770 char *pchDst = NULL;
771 size_t cbDst = 0;
772
773 size_t i = 0;
774 while (i < cwTmp)
775 {
776 /* Find zero symbol (end of string). */
777 for (; i < cwTmp && pcwszSrc[i] != 0; i++)
778 ;
779
780 /* Convert found string. */
781 char *psz = NULL;
782 size_t cch = 0;
783 rc = RTUtf16ToUtf8Ex(pwTmp, cwTmp, &psz, pwTmp - pcwszSrc, &cch);
784 if (RT_FAILURE(rc))
785 break;
786
787 /* Append new substring. */
788 char *pchNew = (char *)RTMemRealloc(pchDst, cbDst + cch + 1);
789 if (!pchNew)
790 {
791 RTStrFree(psz);
792 rc = VERR_NO_MEMORY;
793 break;
794 }
795
796 pchDst = pchNew;
797 memcpy(pchDst + cbDst, psz, cch + 1);
798
799 RTStrFree(psz);
800
801 cbDst += cch + 1;
802
803 /* Skip zero symbols. */
804 for (; i < cwTmp && pcwszSrc[i] == 0; i++)
805 ;
806
807 /* Remember start of string. */
808 pwTmp += i;
809 }
810
811 if (RT_SUCCESS(rc))
812 {
813 *ppszDst = pchDst;
814 *pcbDst = cbDst;
815
816 return VINF_SUCCESS;
817 }
818
819 RTMemFree(pchDst);
820
821 return rc;
822}
823
824int ShClUtf16LFLenUtf8(PCRTUTF16 pcwszSrc, size_t cwSrc, size_t *pchLen)
825{
826 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
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 /* Don't copy the endian marker. */
835 size_t i = pcwszSrc[0] == VBOX_SHCL_UTF16LEMARKER ? 1 : 0;
836
837 /* Calculate the size of the destination text string. */
838 /* Is this Utf16 or Utf16-LE? */
839 for (; i < cwSrc; ++i, ++cLen)
840 {
841 /* Check for a single line feed */
842 if (pcwszSrc[i] == VBOX_SHCL_LINEFEED)
843 ++cLen;
844#ifdef RT_OS_DARWIN
845 /* Check for a single carriage return (MacOS) */
846 if (pcwszSrc[i] == VBOX_SHCL_CARRIAGERETURN)
847 ++cLen;
848#endif
849 if (pcwszSrc[i] == 0)
850 {
851 /* Don't count this, as we do so below. */
852 break;
853 }
854 }
855
856 *pchLen = cLen;
857
858 return VINF_SUCCESS;
859}
860
861int ShClUtf16CRLFLenUtf8(PCRTUTF16 pcwszSrc, size_t cwSrc, size_t *pchLen)
862{
863 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
864 AssertReturn(cwSrc, VERR_INVALID_PARAMETER);
865 AssertPtrReturn(pchLen, VERR_INVALID_POINTER);
866
867 AssertMsgReturn(pcwszSrc[0] != VBOX_SHCL_UTF16BEMARKER,
868 ("Big endian UTF-16 not supported yet\n"), VERR_NOT_SUPPORTED);
869
870 size_t cLen = 0;
871
872 /* Calculate the size of the destination text string. */
873 /* Is this Utf16 or Utf16-LE? */
874 if (pcwszSrc[0] == VBOX_SHCL_UTF16LEMARKER)
875 cLen = 0;
876 else
877 cLen = 1;
878
879 for (size_t i = 0; i < cwSrc; ++i, ++cLen)
880 {
881 if ( (i + 1 < cwSrc)
882 && (pcwszSrc[i] == VBOX_SHCL_CARRIAGERETURN)
883 && (pcwszSrc[i + 1] == VBOX_SHCL_LINEFEED))
884 {
885 ++i;
886 }
887 if (pcwszSrc[i] == 0)
888 break;
889 }
890
891 *pchLen = cLen;
892
893 return VINF_SUCCESS;
894}
895
896int ShClConvUtf16LFToCRLF(PCRTUTF16 pcwszSrc, size_t cwcSrc, PRTUTF16 pu16Dst, size_t cwDst)
897{
898 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
899 AssertPtrReturn(pu16Dst, VERR_INVALID_POINTER);
900 AssertReturn(cwDst, VERR_INVALID_PARAMETER);
901
902 AssertMsgReturn(pcwszSrc[0] != VBOX_SHCL_UTF16BEMARKER,
903 ("Big endian UTF-16 not supported yet\n"), VERR_NOT_SUPPORTED);
904
905 int rc = VINF_SUCCESS;
906
907 /* Don't copy the endian marker. */
908 size_t i = pcwszSrc[0] == VBOX_SHCL_UTF16LEMARKER ? 1 : 0;
909 size_t j = 0;
910
911 for (; i < cwcSrc; ++i, ++j)
912 {
913 /* Don't copy the null byte, as we add it below. */
914 if (pcwszSrc[i] == 0)
915 break;
916
917 /* Not enough space in destination? */
918 if (j == cwDst)
919 {
920 rc = VERR_BUFFER_OVERFLOW;
921 break;
922 }
923
924 if (pcwszSrc[i] == VBOX_SHCL_LINEFEED)
925 {
926 pu16Dst[j] = VBOX_SHCL_CARRIAGERETURN;
927 ++j;
928
929 /* Not enough space in destination? */
930 if (j == cwDst)
931 {
932 rc = VERR_BUFFER_OVERFLOW;
933 break;
934 }
935 }
936#ifdef RT_OS_DARWIN
937 /* Check for a single carriage return (MacOS) */
938 else if (pcwszSrc[i] == VBOX_SHCL_CARRIAGERETURN)
939 {
940 /* Set CR.r */
941 pu16Dst[j] = VBOX_SHCL_CARRIAGERETURN;
942 ++j;
943
944 /* Not enough space in destination? */
945 if (j == cwDst)
946 {
947 rc = VERR_BUFFER_OVERFLOW;
948 break;
949 }
950
951 /* Add line feed. */
952 pu16Dst[j] = VBOX_SHCL_LINEFEED;
953 continue;
954 }
955#endif
956 pu16Dst[j] = pcwszSrc[i];
957 }
958
959 if (j == cwDst)
960 rc = VERR_BUFFER_OVERFLOW;
961
962 if (RT_SUCCESS(rc))
963 {
964 /* Add terminator. */
965 pu16Dst[j] = 0;
966 }
967
968 return rc;
969}
970
971int ShClConvUtf16CRLFToLF(PCRTUTF16 pcwszSrc, size_t cwcSrc, PRTUTF16 pu16Dst, size_t cwDst)
972{
973 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
974 AssertReturn(cwcSrc, VERR_INVALID_PARAMETER);
975 AssertPtrReturn(pu16Dst, VERR_INVALID_POINTER);
976 AssertReturn(cwDst, VERR_INVALID_PARAMETER);
977
978 AssertMsgReturn(pcwszSrc[0] != VBOX_SHCL_UTF16BEMARKER,
979 ("Big endian UTF-16 not supported yet\n"), VERR_NOT_SUPPORTED);
980
981 /* Prepend the Utf16 byte order marker if it is missing. */
982 size_t cwDstPos;
983 if (pcwszSrc[0] == VBOX_SHCL_UTF16LEMARKER)
984 {
985 cwDstPos = 0;
986 }
987 else
988 {
989 pu16Dst[0] = VBOX_SHCL_UTF16LEMARKER;
990 cwDstPos = 1;
991 }
992
993 for (size_t i = 0; i < cwcSrc; ++i, ++cwDstPos)
994 {
995 if (pcwszSrc[i] == 0)
996 break;
997
998 if (cwDstPos == cwDst)
999 return VERR_BUFFER_OVERFLOW;
1000
1001 if ( (i + 1 < cwcSrc)
1002 && (pcwszSrc[i] == VBOX_SHCL_CARRIAGERETURN)
1003 && (pcwszSrc[i + 1] == VBOX_SHCL_LINEFEED))
1004 {
1005 ++i;
1006 }
1007
1008 pu16Dst[cwDstPos] = pcwszSrc[i];
1009 }
1010
1011 if (cwDstPos == cwDst)
1012 return VERR_BUFFER_OVERFLOW;
1013
1014 /* Add terminating zero. */
1015 pu16Dst[cwDstPos] = 0;
1016
1017 return VINF_SUCCESS;
1018}
1019
1020int ShClDibToBmp(const void *pvSrc, size_t cbSrc, void **ppvDest, size_t *pcbDest)
1021{
1022 AssertPtrReturn(pvSrc, VERR_INVALID_POINTER);
1023 AssertReturn(cbSrc, VERR_INVALID_PARAMETER);
1024 AssertPtrReturn(ppvDest, VERR_INVALID_POINTER);
1025 AssertPtrReturn(pcbDest, VERR_INVALID_POINTER);
1026
1027 PBMPWIN3XINFOHDR coreHdr = (PBMPWIN3XINFOHDR)pvSrc;
1028 /** @todo Support all the many versions of the DIB headers. */
1029 if ( cbSrc < sizeof(BMPWIN3XINFOHDR)
1030 || RT_LE2H_U32(coreHdr->cbSize) < sizeof(BMPWIN3XINFOHDR)
1031 || RT_LE2H_U32(coreHdr->cbSize) != sizeof(BMPWIN3XINFOHDR))
1032 {
1033 return VERR_INVALID_PARAMETER;
1034 }
1035
1036 size_t offPixel = sizeof(BMPFILEHDR)
1037 + RT_LE2H_U32(coreHdr->cbSize)
1038 + RT_LE2H_U32(coreHdr->cClrUsed) * sizeof(uint32_t);
1039 if (cbSrc < offPixel)
1040 return VERR_INVALID_PARAMETER;
1041
1042 size_t cbDst = sizeof(BMPFILEHDR) + cbSrc;
1043
1044 void *pvDest = RTMemAlloc(cbDst);
1045 if (!pvDest)
1046 return VERR_NO_MEMORY;
1047
1048 PBMPFILEHDR fileHdr = (PBMPFILEHDR)pvDest;
1049
1050 fileHdr->uType = BMP_HDR_MAGIC;
1051 fileHdr->cbFileSize = (uint32_t)RT_H2LE_U32(cbDst);
1052 fileHdr->Reserved1 = 0;
1053 fileHdr->Reserved2 = 0;
1054 fileHdr->offBits = (uint32_t)RT_H2LE_U32(offPixel);
1055
1056 memcpy((uint8_t *)pvDest + sizeof(BMPFILEHDR), pvSrc, cbSrc);
1057
1058 *ppvDest = pvDest;
1059 *pcbDest = cbDst;
1060
1061 return VINF_SUCCESS;
1062}
1063
1064int ShClBmpGetDib(const void *pvSrc, size_t cbSrc, const void **ppvDest, size_t *pcbDest)
1065{
1066 AssertPtrReturn(pvSrc, VERR_INVALID_POINTER);
1067 AssertReturn(cbSrc, VERR_INVALID_PARAMETER);
1068 AssertPtrReturn(ppvDest, VERR_INVALID_POINTER);
1069 AssertPtrReturn(pcbDest, VERR_INVALID_POINTER);
1070
1071 PBMPFILEHDR pBmpHdr = (PBMPFILEHDR)pvSrc;
1072 if ( cbSrc < sizeof(BMPFILEHDR)
1073 || pBmpHdr->uType != BMP_HDR_MAGIC
1074 || RT_LE2H_U32(pBmpHdr->cbFileSize) != cbSrc)
1075 {
1076 return VERR_INVALID_PARAMETER;
1077 }
1078
1079 *ppvDest = ((uint8_t *)pvSrc) + sizeof(BMPFILEHDR);
1080 *pcbDest = cbSrc - sizeof(BMPFILEHDR);
1081
1082 return VINF_SUCCESS;
1083}
1084
1085#ifdef LOG_ENABLED
1086
1087int ShClDbgDumpHtml(const char *pcszSrc, size_t cbSrc)
1088{
1089 int rc = VINF_SUCCESS;
1090 char *pszBuf = (char *)RTMemTmpAllocZ(cbSrc + 1);
1091 if (pszBuf)
1092 {
1093 memcpy(pszBuf, pcszSrc, cbSrc);
1094 pszBuf[cbSrc] = '\0';
1095 for (size_t off = 0; off < cbSrc; ++off)
1096 if (pszBuf[off] == '\n' || pszBuf[off] == '\r')
1097 pszBuf[off] = ' ';
1098 LogFunc(("Removed \\r\\n: %s\n", pszBuf));
1099 RTMemTmpFree(pszBuf);
1100 }
1101 else
1102 rc = VERR_NO_MEMORY;
1103 return rc;
1104}
1105
1106void ShClDbgDumpData(const void *pv, size_t cb, SHCLFORMAT uFormat)
1107{
1108 if (LogIsEnabled())
1109 {
1110 if (uFormat & VBOX_SHCL_FMT_UNICODETEXT)
1111 {
1112 LogFunc(("VBOX_SHCL_FMT_UNICODETEXT:\n"));
1113 if (pv && cb)
1114 LogFunc(("%ls\n", pv));
1115 else
1116 LogFunc(("%p %zu\n", pv, cb));
1117 }
1118 else if (uFormat & VBOX_SHCL_FMT_BITMAP)
1119 LogFunc(("VBOX_SHCL_FMT_BITMAP\n"));
1120 else if (uFormat & VBOX_SHCL_FMT_HTML)
1121 {
1122 LogFunc(("VBOX_SHCL_FMT_HTML:\n"));
1123 if (pv && cb)
1124 {
1125 LogFunc(("%s\n", pv));
1126 ShClDbgDumpHtml((const char *)pv, cb);
1127 }
1128 else
1129 LogFunc(("%p %zu\n", pv, cb));
1130 }
1131 else
1132 LogFunc(("Invalid format %02X\n", uFormat));
1133 }
1134}
1135
1136#endif /* LOG_ENABLED */
1137
1138/**
1139 * Translates a Shared Clipboard host function number to a string.
1140 *
1141 * @returns Function ID string name.
1142 * @param uFn The function to translate.
1143 */
1144const char *ShClHostFunctionToStr(uint32_t uFn)
1145{
1146 switch (uFn)
1147 {
1148 RT_CASE_RET_STR(VBOX_SHCL_HOST_FN_SET_MODE);
1149 RT_CASE_RET_STR(VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE);
1150 RT_CASE_RET_STR(VBOX_SHCL_HOST_FN_SET_HEADLESS);
1151 RT_CASE_RET_STR(VBOX_SHCL_HOST_FN_CANCEL);
1152 RT_CASE_RET_STR(VBOX_SHCL_HOST_FN_ERROR);
1153 }
1154 return "Unknown";
1155}
1156
1157/**
1158 * Translates a Shared Clipboard host message enum to a string.
1159 *
1160 * @returns Message ID string name.
1161 * @param uMsg The message to translate.
1162 */
1163const char *ShClHostMsgToStr(uint32_t uMsg)
1164{
1165 switch (uMsg)
1166 {
1167 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_QUIT);
1168 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_READ_DATA);
1169 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_FORMATS_REPORT);
1170 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_CANCELED);
1171 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_READ_DATA_CID);
1172 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_STATUS);
1173 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_READ);
1174 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_WRITE);
1175 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_READ);
1176 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_WRITE);
1177 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_OPEN);
1178 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_CLOSE);
1179 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_READ);
1180 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_WRITE);
1181 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ);
1182 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_WRITE);
1183 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_OPEN);
1184 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_CLOSE);
1185 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_READ);
1186 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_WRITE);
1187 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_CANCEL);
1188 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_ERROR);
1189 }
1190 return "Unknown";
1191}
1192
1193/**
1194 * Translates a Shared Clipboard guest message enum to a string.
1195 *
1196 * @returns Message ID string name.
1197 * @param uMsg The message to translate.
1198 */
1199const char *ShClGuestMsgToStr(uint32_t uMsg)
1200{
1201 switch (uMsg)
1202 {
1203 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT);
1204 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_REPORT_FORMATS);
1205 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_DATA_READ);
1206 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_DATA_WRITE);
1207 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_CONNECT);
1208 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_REPORT_FEATURES);
1209 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_QUERY_FEATURES);
1210 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT);
1211 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT);
1212 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_MSG_GET);
1213 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_MSG_CANCEL);
1214 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_REPLY);
1215 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_READ);
1216 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_WRITE);
1217 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_READ);
1218 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_WRITE);
1219 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_OPEN);
1220 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_CLOSE);
1221 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_HDR_READ);
1222 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_HDR_WRITE);
1223 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_ENTRY_READ);
1224 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_ENTRY_WRITE);
1225 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_OBJ_OPEN);
1226 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_OBJ_CLOSE);
1227 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_OBJ_READ);
1228 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_OBJ_WRITE);
1229 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_ERROR);
1230 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_NEGOTIATE_CHUNK_SIZE);
1231 }
1232 return "Unknown";
1233}
1234
1235/**
1236 * Converts Shared Clipboard formats to a string.
1237 *
1238 * @returns Stringified Shared Clipboard formats, or NULL on failure. Must be free'd with RTStrFree().
1239 * @param fFormats Shared Clipboard formats to convert.
1240 *
1241 */
1242char *ShClFormatsToStrA(SHCLFORMATS fFormats)
1243{
1244#define APPEND_FMT_TO_STR(_aFmt) \
1245 if (fFormats & VBOX_SHCL_FMT_##_aFmt) \
1246 { \
1247 if (pszFmts) \
1248 { \
1249 rc2 = RTStrAAppend(&pszFmts, ", "); \
1250 if (RT_FAILURE(rc2)) \
1251 break; \
1252 } \
1253 \
1254 rc2 = RTStrAAppend(&pszFmts, #_aFmt); \
1255 if (RT_FAILURE(rc2)) \
1256 break; \
1257 }
1258
1259 char *pszFmts = NULL;
1260 int rc2 = VINF_SUCCESS;
1261
1262 do
1263 {
1264 APPEND_FMT_TO_STR(UNICODETEXT);
1265 APPEND_FMT_TO_STR(BITMAP);
1266 APPEND_FMT_TO_STR(HTML);
1267# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1268 APPEND_FMT_TO_STR(URI_LIST);
1269# endif
1270
1271 } while (0);
1272
1273 if (!pszFmts)
1274 rc2 = RTStrAAppend(&pszFmts, "NONE");
1275
1276 if ( RT_FAILURE(rc2)
1277 && pszFmts)
1278 {
1279 RTStrFree(pszFmts);
1280 pszFmts = NULL;
1281 }
1282
1283#undef APPEND_FMT_TO_STR
1284
1285 return pszFmts;
1286}
1287
1288
1289/*********************************************************************************************************************************
1290* Shared Clipboard Cache *
1291*********************************************************************************************************************************/
1292
1293/**
1294 * Returns (mutable) data of a cache entry.
1295 *
1296 * @param pCacheEntry Cache entry to return data for.
1297 * @param pvData Where to return the (mutable) data pointer.
1298 * @param pcbData Where to return the data size.
1299 */
1300void ShClCacheEntryGet(PSHCLCACHEENTRY pCacheEntry, void **pvData, size_t *pcbData)
1301{
1302 AssertPtrReturnVoid(pCacheEntry);
1303 AssertPtrReturnVoid(pvData);
1304 AssertReturnVoid(pcbData);
1305
1306 *pvData = pCacheEntry->pvData;
1307 *pcbData = pCacheEntry->cbData;
1308}
1309
1310/**
1311 * Initializes a cache entry.
1312 *
1313 * @returns VBox status code.
1314 * @param pCacheEntry Cache entry to init.
1315 * @param pvData Data to copy to entry. Can be NULL to initialize an emptry entry.
1316 * @param cbData Size (in bytes) of \a pvData to copy to entry. Must be 0 if \a pvData is NULL.
1317 */
1318static int shClCacheEntryInit(PSHCLCACHEENTRY pCacheEntry, const void *pvData, size_t cbData)
1319{
1320 AssertReturn(RT_VALID_PTR(pvData) || cbData == 0, VERR_INVALID_PARAMETER);
1321
1322 RT_BZERO(pCacheEntry, sizeof(SHCLCACHEENTRY));
1323
1324 if (pvData)
1325 {
1326 pCacheEntry->pvData = RTMemDup(pvData, cbData);
1327 AssertPtrReturn(pCacheEntry->pvData, VERR_NO_MEMORY);
1328 pCacheEntry->cbData = cbData;
1329 }
1330
1331 return VINF_SUCCESS;
1332}
1333
1334/**
1335 * Returns whether a cache entry is valid (cache hit) or not.
1336 *
1337 * @returns \c true if valid, or \c false if not.
1338 * @param pCacheEntry Cache entry to check for.
1339 */
1340DECLINLINE(bool) shClCacheEntryIsValid(PSHCLCACHEENTRY pCacheEntry)
1341{
1342 return pCacheEntry->pvData != NULL;
1343}
1344
1345/**
1346 * Destroys a cache entry.
1347 *
1348 * @param pCacheEntry Cache entry to destroy.
1349 */
1350DECLINLINE(void) shClCacheEntryDestroy(PSHCLCACHEENTRY pCacheEntry)
1351{
1352 if (pCacheEntry->pvData)
1353 {
1354 Assert(pCacheEntry->cbData);
1355 RTMemFree(pCacheEntry->pvData);
1356 pCacheEntry->pvData = NULL;
1357 pCacheEntry->cbData = 0;
1358 }
1359}
1360
1361/**
1362 * Initializes a cache.
1363 *
1364 * @param pCache Cache to init.
1365 */
1366void ShClCacheInit(PSHCLCACHE pCache)
1367{
1368 AssertPtrReturnVoid(pCache);
1369
1370 RT_BZERO(pCache, sizeof(SHCLCACHE));
1371
1372 for (size_t i = 0; i < RT_ELEMENTS(pCache->aEntries); i++)
1373 shClCacheEntryInit(&pCache->aEntries[i], NULL, 0);
1374}
1375
1376/**
1377 * Destroys all entries of a cache.
1378 *
1379 * @param pCache Cache to destroy entries for.
1380 */
1381DECLINLINE(void) shClCacheDestroyEntries(PSHCLCACHE pCache)
1382{
1383 for (size_t i = 0; i < RT_ELEMENTS(pCache->aEntries); i++)
1384 shClCacheEntryDestroy(&pCache->aEntries[i]);
1385}
1386
1387/**
1388 * Destroys a cache.
1389 *
1390 * @param pCache Cache to destroy.
1391 */
1392void ShClCacheDestroy(PSHCLCACHE pCache)
1393{
1394 AssertPtrReturnVoid(pCache);
1395
1396 shClCacheDestroyEntries(pCache);
1397
1398 RT_BZERO(pCache, sizeof(SHCLCACHE));
1399}
1400
1401/**
1402 * Invalidates a cache.
1403 *
1404 * @param pCache Cache to invalidate.
1405 */
1406void ShClCacheInvalidate(PSHCLCACHE pCache)
1407{
1408 AssertPtrReturnVoid(pCache);
1409
1410 shClCacheDestroyEntries(pCache);
1411}
1412
1413/**
1414 * Invalidates a specific cache entry.
1415 *
1416 * @param pCache Cache to invalidate.
1417 * @param uFmt Format to invalidate entry for.
1418 */
1419void ShClCacheInvalidateEntry(PSHCLCACHE pCache, SHCLFORMAT uFmt)
1420{
1421 AssertPtrReturnVoid(pCache);
1422 AssertReturnVoid(uFmt < VBOX_SHCL_FMT_MAX);
1423 shClCacheEntryDestroy(&pCache->aEntries[uFmt]);
1424}
1425
1426/**
1427 * Gets an entry for a Shared Clipboard format.
1428 *
1429 * @returns Pointer to entry if cached, or NULL if not in cache (cache miss).
1430 * @param pCache Cache to get entry for.
1431 * @param uFmt Format to get entry for.
1432 */
1433PSHCLCACHEENTRY ShClCacheGet(PSHCLCACHE pCache, SHCLFORMAT uFmt)
1434{
1435 AssertReturn(uFmt < VBOX_SHCL_FMT_MAX, NULL);
1436 return shClCacheEntryIsValid(&pCache->aEntries[uFmt]) ? &pCache->aEntries[uFmt] : NULL;
1437}
1438
1439/**
1440 * Sets data to cache for a specific clipboard format.
1441 *
1442 * @returns VBox status code.
1443 * @param pCache Cache to set data for.
1444 * @param uFmt Clipboard format to set data for.
1445 * @param pvData Data to set.
1446 * @param cbData Size (in bytes) of data to set.
1447 */
1448int ShClCacheSet(PSHCLCACHE pCache, SHCLFORMAT uFmt, const void *pvData, size_t cbData)
1449{
1450 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
1451
1452 if (!pvData) /* Nothing to cache? */
1453 return VINF_SUCCESS;
1454
1455 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1456 AssertReturn(uFmt < VBOX_SHCL_FMT_MAX, VERR_INVALID_PARAMETER);
1457 AssertReturn(shClCacheEntryIsValid(&pCache->aEntries[uFmt]) == false, VERR_ALREADY_EXISTS);
1458
1459 return shClCacheEntryInit(&pCache->aEntries[uFmt], pvData, cbData);
1460}
1461
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use