VirtualBox

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

Last change on this file since 104760 was 104760, checked in by vboxsync, 7 months ago

Shared Clipboard: Attempt to prevent extra new lines when copying text from Linux to Windows (take care about buf[0] == '\n' as well), bugref:10694.

  • Property eol-style set to native
  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 46.0 KB
Line 
1/* $Id: clipboard-common.cpp 104760 2024-05-22 14:32:06Z 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);
51static int shClEventSourceUnregisterEvent(PSHCLEVENTSOURCE pSource, PSHCLEVENT pEvent);
52
53static void shClEventDestroy(PSHCLEVENT pEvent);
54DECLINLINE(PSHCLEVENT) shclEventGet(PSHCLEVENTSOURCE pSource, SHCLEVENTID idEvent);
55
56
57/*********************************************************************************************************************************
58* Implementation *
59*********************************************************************************************************************************/
60
61/**
62 * Allocates a new event payload.
63 *
64 * @returns VBox status code.
65 * @param uID Payload ID to set for this payload. Useful for consequtive payloads.
66 * @param pvData Data to associate to this payload.
67 * The payload owns the data then.
68 * @param cbData Size (in bytes) of data to associate.
69 * @param ppPayload Where to store the allocated event payload on success.
70 */
71int ShClPayloadInit(uint32_t uID, void *pvData, uint32_t cbData,
72 PSHCLEVENTPAYLOAD *ppPayload)
73{
74 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
75 AssertReturn(cbData > 0, VERR_INVALID_PARAMETER);
76
77 PSHCLEVENTPAYLOAD pPayload = (PSHCLEVENTPAYLOAD)RTMemAlloc(sizeof(SHCLEVENTPAYLOAD));
78 if (pPayload)
79 {
80 pPayload->pvData = pvData;
81 pPayload->cbData = cbData;
82 pPayload->uID = uID;
83
84 *ppPayload = pPayload;
85 return VINF_SUCCESS;
86 }
87
88 return VERR_NO_MEMORY;
89}
90
91/**
92 * Allocates a new event payload.
93 *
94 * @returns VBox status code.
95 * @param uID Payload ID to set for this payload. Useful for consequtive payloads.
96 * @param pvData Data block to allocate (duplicate) to this payload.
97 * @param cbData Size (in bytes) of data block to allocate.
98 * @param ppPayload Where to store the allocated event payload on success.
99 */
100int ShClPayloadAlloc(uint32_t uID, const void *pvData, uint32_t cbData,
101 PSHCLEVENTPAYLOAD *ppPayload)
102{
103 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
104 AssertReturn(cbData > 0, VERR_INVALID_PARAMETER);
105
106 void *pvDataDup = RTMemDup(pvData, cbData);
107 if (pvDataDup)
108 return ShClPayloadInit(uID, pvDataDup, cbData, ppPayload);
109
110 return VERR_NO_MEMORY;
111}
112
113/**
114 * Frees an event payload.
115 *
116 * @returns VBox status code.
117 * @param pPayload Event payload to free.
118 */
119void ShClPayloadFree(PSHCLEVENTPAYLOAD pPayload)
120{
121 if (!pPayload)
122 return;
123
124 if (pPayload->pvData)
125 {
126 Assert(pPayload->cbData);
127 RTMemFree(pPayload->pvData);
128 pPayload->pvData = NULL;
129 }
130
131 pPayload->cbData = 0;
132 pPayload->uID = UINT32_MAX;
133
134 RTMemFree(pPayload);
135}
136
137/**
138 * Creates a new event source.
139 *
140 * @returns VBox status code.
141 * @param pSource Event source to create.
142 * @param uID ID to use for event source.
143 */
144int ShClEventSourceCreate(PSHCLEVENTSOURCE pSource, SHCLEVENTSOURCEID uID)
145{
146 LogFlowFunc(("pSource=%p, uID=%RU16\n", pSource, uID));
147 AssertPtrReturn(pSource, VERR_INVALID_POINTER);
148
149 int rc = RTCritSectInit(&pSource->CritSect);
150 AssertRCReturn(rc, rc);
151
152 RTListInit(&pSource->lstEvents);
153
154 pSource->uID = uID;
155 /* Choose a random event ID starting point. */
156 pSource->idNextEvent = RTRandU32Ex(1, VBOX_SHCL_MAX_EVENTS - 1);
157
158 return VINF_SUCCESS;
159}
160
161/**
162 * Destroys an event source.
163 *
164 * @returns VBox status code.
165 * @param pSource Event source to destroy.
166 */
167int ShClEventSourceDestroy(PSHCLEVENTSOURCE pSource)
168{
169 if (!pSource)
170 return VINF_SUCCESS;
171
172 if (!RTCritSectIsInitialized(&pSource->CritSect)) /* Already destroyed? Bail out. */
173 return VINF_SUCCESS;
174
175 LogFlowFunc(("ID=%RU32\n", pSource->uID));
176
177 int rc = RTCritSectEnter(&pSource->CritSect);
178 if (RT_SUCCESS(rc))
179 {
180 shClEventSourceResetInternal(pSource);
181
182 rc = RTCritSectLeave(&pSource->CritSect);
183 AssertRC(rc);
184
185 RTCritSectDelete(&pSource->CritSect);
186
187 pSource->uID = UINT16_MAX;
188 pSource->idNextEvent = UINT32_MAX;
189 }
190
191 return rc;
192}
193
194/**
195 * Resets an event source, internal version.
196 *
197 * @param pSource Event source to reset.
198 */
199static void shClEventSourceResetInternal(PSHCLEVENTSOURCE pSource)
200{
201 LogFlowFunc(("ID=%RU32\n", pSource->uID));
202
203 PSHCLEVENT pEvIt;
204 PSHCLEVENT pEvItNext;
205 RTListForEachSafe(&pSource->lstEvents, pEvIt, pEvItNext, SHCLEVENT, Node)
206 {
207 bool const fDealloc = ASMAtomicReadU32(&pEvIt->cRefs) == 0; /* Still any references left? Skip de-allocation. */
208 if (!fDealloc)
209 Log3Func(("Event %RU32 has %RU32 references left, skipping de-allocation\n", pEvIt->idEvent, pEvIt->cRefs));
210
211 shClEventDestroy(pEvIt);
212
213 int rc2 = shClEventSourceUnregisterEvent(pSource, pEvIt);
214 AssertRC(rc2);
215
216 if (fDealloc)
217 {
218 RTMemFree(pEvIt);
219 pEvIt = NULL;
220 }
221 }
222}
223
224/**
225 * Resets an event source.
226 *
227 * @param pSource Event source to reset.
228 */
229void ShClEventSourceReset(PSHCLEVENTSOURCE pSource)
230{
231 int rc2 = RTCritSectEnter(&pSource->CritSect);
232 if (RT_SUCCESS(rc2))
233 {
234 shClEventSourceResetInternal(pSource);
235
236 rc2 = RTCritSectLeave(&pSource->CritSect);
237 AssertRC(rc2);
238 }
239}
240
241/**
242 * Generates a new event ID for a specific event source and registers it.
243 *
244 * @returns VBox status code.
245 * @param pSource Event source to generate event for.
246 * @param ppEvent Where to return the new event generated on success.
247 */
248int ShClEventSourceGenerateAndRegisterEvent(PSHCLEVENTSOURCE pSource, PSHCLEVENT *ppEvent)
249{
250 AssertPtrReturn(pSource, VERR_INVALID_POINTER);
251 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
252
253 PSHCLEVENT pEvent = (PSHCLEVENT)RTMemAllocZ(sizeof(SHCLEVENT));
254 AssertReturn(pEvent, VERR_NO_MEMORY);
255 int rc = RTSemEventMultiCreate(&pEvent->hEvtMulSem);
256 if (RT_SUCCESS(rc))
257 {
258 rc = RTCritSectEnter(&pSource->CritSect);
259 if (RT_SUCCESS(rc))
260 {
261 /*
262 * Allocate an unique event ID.
263 */
264 for (uint32_t cTries = 0;; cTries++)
265 {
266 SHCLEVENTID idEvent = ++pSource->idNextEvent;
267 if (idEvent < VBOX_SHCL_MAX_EVENTS)
268 { /* likely */ }
269 else
270 pSource->idNextEvent = idEvent = 1; /* zero == error, remember! */
271
272 if (shclEventGet(pSource, idEvent) == NULL)
273 {
274 pEvent->pParent = pSource;
275 pEvent->idEvent = idEvent;
276 RTListAppend(&pSource->lstEvents, &pEvent->Node);
277
278 rc = RTCritSectLeave(&pSource->CritSect);
279 AssertRC(rc);
280
281 LogFlowFunc(("uSource=%RU16: New event: %#x\n", pSource->uID, idEvent));
282
283 ShClEventRetain(pEvent);
284 *ppEvent = pEvent;
285
286 return VINF_SUCCESS;
287 }
288
289 AssertBreak(cTries < 4096);
290 }
291
292 rc = RTCritSectLeave(&pSource->CritSect);
293 AssertRC(rc);
294 }
295 }
296
297 AssertMsgFailed(("Unable to register a new event ID for event source %RU16\n", pSource->uID));
298
299 RTSemEventMultiDestroy(pEvent->hEvtMulSem);
300 pEvent->hEvtMulSem = NIL_RTSEMEVENTMULTI;
301 RTMemFree(pEvent);
302 return rc;
303}
304
305/**
306 * Destroys an event.
307 *
308 * @param pEvent Event to destroy.
309 */
310static void shClEventDestroy(PSHCLEVENT pEvent)
311{
312 if (!pEvent)
313 return;
314
315 LogFlowFunc(("Event %RU32\n", pEvent->idEvent));
316
317 if (pEvent->hEvtMulSem != NIL_RTSEMEVENT)
318 {
319 RTSemEventMultiDestroy(pEvent->hEvtMulSem);
320 pEvent->hEvtMulSem = NIL_RTSEMEVENT;
321 }
322
323 ShClPayloadFree(pEvent->pPayload);
324 pEvent->pPayload = NULL;
325
326 pEvent->idEvent = NIL_SHCLEVENTID;
327}
328
329/**
330 * Unregisters an event.
331 *
332 * @returns VBox status code.
333 * @param pSource Event source to unregister event for.
334 * @param pEvent Event to unregister.
335 */
336static int shClEventSourceUnregisterEvent(PSHCLEVENTSOURCE pSource, PSHCLEVENT pEvent)
337{
338 RT_NOREF(pSource);
339
340 LogFlowFunc(("idEvent=%RU32, cRefs=%RU32\n", pEvent->idEvent, pEvent->cRefs));
341
342 RTListNodeRemove(&pEvent->Node);
343 pEvent->pParent = NULL;
344
345 return VINF_SUCCESS;
346}
347
348/**
349 * Returns a specific event of a event source. Inlined version.
350 *
351 * @returns Pointer to event if found, or NULL if not found.
352 * @param pSource Event source to get event from.
353 * @param uID Event ID to get.
354 */
355DECLINLINE(PSHCLEVENT) shclEventGet(PSHCLEVENTSOURCE pSource, SHCLEVENTID idEvent)
356{
357 PSHCLEVENT pEvent;
358 RTListForEach(&pSource->lstEvents, pEvent, SHCLEVENT, Node)
359 {
360 if (pEvent->idEvent == idEvent)
361 return pEvent;
362 }
363
364 return NULL;
365}
366
367/**
368 * Returns a specific event of a event source.
369 *
370 * @returns Pointer to event if found, or NULL if not found.
371 * @param pSource Event source to get event from.
372 * @param idEvent ID of event to return.
373 */
374PSHCLEVENT ShClEventSourceGetFromId(PSHCLEVENTSOURCE pSource, SHCLEVENTID idEvent)
375{
376 AssertPtrReturn(pSource, NULL);
377
378 int rc = RTCritSectEnter(&pSource->CritSect);
379 if (RT_SUCCESS(rc))
380 {
381 PSHCLEVENT pEvent = shclEventGet(pSource, idEvent);
382
383 rc = RTCritSectLeave(&pSource->CritSect);
384 AssertRC(rc);
385
386 return pEvent;
387 }
388
389 return NULL;
390}
391
392/**
393 * Returns the last (newest) event ID which has been registered for an event source.
394 *
395 * @returns Pointer to last registered event, or NULL if not found.
396 * @param pSource Event source to get last registered event from.
397 */
398PSHCLEVENT ShClEventSourceGetLast(PSHCLEVENTSOURCE pSource)
399{
400 AssertPtrReturn(pSource, NULL);
401
402 int rc = RTCritSectEnter(&pSource->CritSect);
403 if (RT_SUCCESS(rc))
404 {
405 PSHCLEVENT pEvent = RTListGetLast(&pSource->lstEvents, SHCLEVENT, Node);
406
407 rc = RTCritSectLeave(&pSource->CritSect);
408 AssertRC(rc);
409
410 return pEvent;
411 }
412
413 return NULL;
414}
415
416/**
417 * Returns the current reference count for a specific event.
418 *
419 * @returns Reference count.
420 * @param pSource Event source the specific event is part of.
421 * @param idEvent Event ID to return reference count for.
422 */
423uint32_t ShClEventGetRefs(PSHCLEVENT pEvent)
424{
425 AssertPtrReturn(pEvent, 0);
426
427 return ASMAtomicReadU32(&pEvent->cRefs);
428}
429
430/**
431 * Detaches a payload from an event, internal version.
432 *
433 * @returns Pointer to the detached payload. Can be NULL if the event has no payload.
434 * @param pEvent Event to detach payload for.
435 */
436static PSHCLEVENTPAYLOAD shclEventPayloadDetachInternal(PSHCLEVENT pEvent)
437{
438#ifdef VBOX_STRICT
439 AssertPtrReturn(pEvent, NULL);
440#endif
441
442 PSHCLEVENTPAYLOAD pPayload = pEvent->pPayload;
443
444 pEvent->pPayload = NULL;
445
446 return pPayload;
447}
448
449/**
450 * Waits for an event to get signalled.
451 *
452 * @returns VBox status code.
453 * @retval VERR_SHCLPB_EVENT_FAILED if the event has a set error code.
454 * @param pEvent Event to wait for.
455 * @param uTimeoutMs Timeout (in ms) to wait.
456 * @param pRc Where to return the event rc. Optional and can be NULL.
457 * @param ppPayload Where to store the (allocated) event payload on success. Needs to be free'd with
458 * SharedClipboardPayloadFree(). Optional.
459 */
460int ShClEventWaitEx(PSHCLEVENT pEvent, RTMSINTERVAL uTimeoutMs, int *pRc, PSHCLEVENTPAYLOAD *ppPayload)
461{
462 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
463 AssertPtrNullReturn(ppPayload, VERR_INVALID_POINTER);
464 LogFlowFuncEnter();
465
466 int rc = RTSemEventMultiWait(pEvent->hEvtMulSem, uTimeoutMs);
467 if (RT_SUCCESS(rc))
468 {
469 if (RT_FAILURE(pEvent->rc))
470 rc = VERR_SHCLPB_EVENT_FAILED;
471
472 if (pRc)
473 *pRc = pEvent->rc;
474
475 if (ppPayload)
476 {
477 /* Make sure to detach payload here, as the caller now owns the data. */
478 *ppPayload = shclEventPayloadDetachInternal(pEvent);
479 }
480 }
481
482 if (RT_FAILURE(rc))
483 LogRel2(("Shared Clipboard: Waiting for event %RU32 failed, rc=%Rrc\n", pEvent->idEvent, rc));
484
485 LogFlowFuncLeaveRC(rc);
486 return rc;
487}
488
489/**
490 * Waits for an event to get signalled.
491 *
492 * @returns VBox status code.
493 * @retval VERR_SHCLPB_EVENT_FAILED if the event has a set error code.
494 * @param pEvent Event to wait for.
495 * @param uTimeoutMs Timeout (in ms) to wait.
496 * @param ppPayload Where to store the (allocated) event payload on success. Needs to be free'd with
497 * SharedClipboardPayloadFree(). Optional.
498 */
499int ShClEventWait(PSHCLEVENT pEvent, RTMSINTERVAL uTimeoutMs, PSHCLEVENTPAYLOAD *ppPayload)
500{
501 return ShClEventWaitEx(pEvent, uTimeoutMs, NULL /* pRc */, ppPayload);
502}
503
504/**
505 * Retains an event by increasing its reference count.
506 *
507 * @returns New reference count, or UINT32_MAX if failed.
508 * @param pEvent Event to retain.
509 */
510uint32_t ShClEventRetain(PSHCLEVENT pEvent)
511{
512 AssertPtrReturn(pEvent, UINT32_MAX);
513 AssertReturn(ASMAtomicReadU32(&pEvent->cRefs) < 64, UINT32_MAX);
514 return ASMAtomicIncU32(&pEvent->cRefs);
515}
516
517/**
518 * Releases event by decreasing its reference count. Will be destroyed once the reference count reaches 0.
519 *
520 * @returns New reference count, or UINT32_MAX if failed.
521 * @param pEvent Event to release.
522 * If the reference count reaches 0, the event will
523 * be destroyed and \a pEvent will be invalid.
524 */
525uint32_t ShClEventRelease(PSHCLEVENT pEvent)
526{
527 if (!pEvent)
528 return 0;
529
530 AssertReturn(ASMAtomicReadU32(&pEvent->cRefs) > 0, UINT32_MAX);
531
532 uint32_t const cRefs = ASMAtomicDecU32(&pEvent->cRefs);
533 if (cRefs == 0)
534 {
535 int rc;
536 PSHCLEVENTSOURCE pParent = pEvent->pParent;
537 if ( pParent
538 && RTCritSectIsInitialized(&pParent->CritSect))
539 {
540 rc = RTCritSectEnter(&pParent->CritSect);
541 if (RT_SUCCESS(rc))
542 {
543 rc = shClEventSourceUnregisterEvent(pParent, pEvent);
544
545 int rc2 = RTCritSectLeave(&pParent->CritSect);
546 if (RT_SUCCESS(rc))
547 rc = rc2;
548 }
549 }
550 else
551 rc = VINF_SUCCESS;
552
553 if (RT_SUCCESS(rc))
554 {
555 shClEventDestroy(pEvent);
556
557 RTMemFree(pEvent);
558 pEvent = NULL;
559 }
560
561 return RT_SUCCESS(rc) ? 0 : UINT32_MAX;
562 }
563
564 return cRefs;
565}
566
567/**
568 * Signals an event, extended version.
569 *
570 * @returns VBox status code.
571 * @param pEvent Event to signal.
572 * @param rc Result code to set.
573 * @param pPayload Event payload to associate. Takes ownership on
574 * success. Optional.
575 */
576int ShClEventSignalEx(PSHCLEVENT pEvent, int rc, PSHCLEVENTPAYLOAD pPayload)
577{
578 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
579
580 Assert(pEvent->pPayload == NULL);
581
582 pEvent->rc = rc;
583 pEvent->pPayload = pPayload;
584
585 int rc2 = RTSemEventMultiSignal(pEvent->hEvtMulSem);
586 if (RT_FAILURE(rc2))
587 pEvent->pPayload = NULL; /* (no race condition if consumer also enters the critical section) */
588
589 LogFlowFuncLeaveRC(rc2);
590 return rc2;
591}
592
593/**
594 * Signals an event.
595 *
596 * @returns VBox status code.
597 * @param pEvent Event to signal.
598 * @param pPayload Event payload to associate. Takes ownership on
599 * success. Optional.
600 */
601int ShClEventSignal(PSHCLEVENT pEvent, PSHCLEVENTPAYLOAD pPayload)
602{
603 return ShClEventSignalEx(pEvent, VINF_SUCCESS, pPayload);
604}
605
606int ShClUtf16LenUtf8(PCRTUTF16 pcwszSrc, size_t cwcSrc, size_t *pchLen)
607{
608 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
609 AssertPtrReturn(pchLen, VERR_INVALID_POINTER);
610
611 size_t chLen = 0;
612 int rc = RTUtf16CalcUtf8LenEx(pcwszSrc, cwcSrc, &chLen);
613 if (RT_SUCCESS(rc))
614 *pchLen = chLen;
615 return rc;
616}
617
618int ShClConvUtf16CRLFToUtf8LF(PCRTUTF16 pcwszSrc, size_t cwcSrc,
619 char *pszBuf, size_t cbBuf, size_t *pcbLen)
620{
621 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
622 AssertReturn (cwcSrc, VERR_INVALID_PARAMETER);
623 AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
624 AssertPtrReturn(pcbLen, VERR_INVALID_POINTER);
625
626 int rc;
627
628 PRTUTF16 pwszTmp = NULL;
629 size_t cchTmp = 0;
630
631 size_t cbLen = 0;
632
633 /* How long will the converted text be? */
634 rc = ShClUtf16CRLFLenUtf8(pcwszSrc, cwcSrc, &cchTmp);
635 if (RT_SUCCESS(rc))
636 {
637 cchTmp++; /* Add space for terminator. */
638
639 pwszTmp = (PRTUTF16)RTMemAlloc(cchTmp * sizeof(RTUTF16));
640 if (pwszTmp)
641 {
642 rc = ShClConvUtf16CRLFToLF(pcwszSrc, cwcSrc, pwszTmp, cchTmp);
643 if (RT_SUCCESS(rc))
644 rc = RTUtf16ToUtf8Ex(pwszTmp + 1, cchTmp - 1, &pszBuf, cbBuf, &cbLen);
645
646 RTMemFree(reinterpret_cast<void *>(pwszTmp));
647 }
648 else
649 rc = VERR_NO_MEMORY;
650 }
651
652 if (RT_SUCCESS(rc))
653 {
654 *pcbLen = cbLen;
655 }
656
657 return rc;
658}
659
660int ShClConvUtf16LFToCRLFA(PCRTUTF16 pcwszSrc, size_t cwcSrc,
661 PRTUTF16 *ppwszDst, size_t *pcwDst)
662{
663 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
664 AssertPtrReturn(ppwszDst, VERR_INVALID_POINTER);
665 AssertPtrReturn(pcwDst, VERR_INVALID_POINTER);
666
667 PRTUTF16 pwszDst = NULL;
668 size_t cchDst;
669
670 int rc = ShClUtf16LFLenUtf8(pcwszSrc, cwcSrc, &cchDst);
671 if (RT_SUCCESS(rc))
672 {
673 pwszDst = (PRTUTF16)RTMemAlloc((cchDst + 1 /* Leave space for terminator */) * sizeof(RTUTF16));
674 if (pwszDst)
675 {
676 rc = ShClConvUtf16LFToCRLF(pcwszSrc, cwcSrc, pwszDst, cchDst + 1 /* Include terminator */);
677 }
678 else
679 rc = VERR_NO_MEMORY;
680 }
681
682 if (RT_SUCCESS(rc))
683 {
684 *ppwszDst = pwszDst;
685 *pcwDst = cchDst;
686 }
687 else
688 RTMemFree(pwszDst);
689
690 LogFlowFuncLeaveRC(rc);
691 return rc;
692}
693
694int ShClConvUtf8LFToUtf16CRLF(const char *pcszSrc, size_t cbSrc,
695 PRTUTF16 *ppwszDst, size_t *pcwDst)
696{
697 AssertPtrReturn(pcszSrc, VERR_INVALID_POINTER);
698 AssertReturn(cbSrc, VERR_INVALID_PARAMETER);
699 AssertPtrReturn(ppwszDst, VERR_INVALID_POINTER);
700 AssertPtrReturn(pcwDst, VERR_INVALID_POINTER);
701
702 /* Intermediate conversion to UTF-16. */
703 size_t cwcTmp;
704 PRTUTF16 pwcTmp = NULL;
705 int rc = RTStrToUtf16Ex(pcszSrc, cbSrc, &pwcTmp, 0, &cwcTmp);
706 if (RT_SUCCESS(rc))
707 {
708 rc = ShClConvUtf16LFToCRLFA(pwcTmp, cwcTmp, ppwszDst, pcwDst);
709 RTUtf16Free(pwcTmp);
710 }
711
712 return rc;
713}
714
715/**
716 * Converts a Latin-1 string with LF line endings into an UTF-16 string with CRLF endings.
717 *
718 * @returns VBox status code.
719 * @param pcszSrc Latin-1 string to convert.
720 * @param cbSrc Size (in bytes) of Latin-1 string to convert.
721 * @param ppwszDst Where to return the converted UTF-16 string on success.
722 * @param pcwDst Where to return the length (in UTF-16 characters) on success.
723 *
724 * @note Only converts the source until the string terminator is found (or length limit is hit).
725 */
726int ShClConvLatin1LFToUtf16CRLF(const char *pcszSrc, size_t cbSrc,
727 PRTUTF16 *ppwszDst, size_t *pcwDst)
728{
729 AssertPtrReturn(pcszSrc, VERR_INVALID_POINTER);
730 AssertReturn(cbSrc, VERR_INVALID_PARAMETER);
731 AssertPtrReturn(ppwszDst, VERR_INVALID_POINTER);
732 AssertPtrReturn(pcwDst, VERR_INVALID_POINTER);
733
734 size_t chSrc = 0;
735
736 PRTUTF16 pwszDst = NULL;
737
738 /* Calculate the space needed. */
739 size_t cwDst = 0;
740 for (size_t i = 0; i < cbSrc && pcszSrc[i] != '\0'; ++i)
741 {
742 if (pcszSrc[i] == VBOX_SHCL_LINEFEED)
743 cwDst += 2; /* Space for VBOX_SHCL_CARRIAGERETURN + VBOX_SHCL_LINEFEED. */
744 else
745 ++cwDst;
746 chSrc++;
747 }
748
749 pwszDst = (PRTUTF16)RTMemAlloc((cwDst + 1 /* Leave space for the terminator */) * sizeof(RTUTF16));
750 AssertPtrReturn(pwszDst, VERR_NO_MEMORY);
751
752 /* Do the conversion, bearing in mind that Latin-1 expands "naturally" to UTF-16. */
753 for (size_t i = 0, j = 0; i < chSrc; ++i, ++j)
754 {
755 AssertMsg(j <= cwDst, ("cbSrc=%zu, j=%u vs. cwDst=%u\n", cbSrc, j, cwDst));
756 if (pcszSrc[i] != VBOX_SHCL_LINEFEED)
757 pwszDst[j] = pcszSrc[i];
758 else
759 {
760 pwszDst[j] = VBOX_SHCL_CARRIAGERETURN;
761 pwszDst[j + 1] = VBOX_SHCL_LINEFEED;
762 ++j;
763 }
764 }
765
766 pwszDst[cwDst] = '\0'; /* Make sure we are zero-terminated. */
767
768 *ppwszDst = pwszDst;
769 *pcwDst = cwDst;
770
771 return VINF_SUCCESS;
772}
773
774int ShClConvUtf16ToUtf8HTML(PCRTUTF16 pcwszSrc, size_t cwcSrc, char **ppszDst, size_t *pcbDst)
775{
776 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
777 AssertReturn (cwcSrc, VERR_INVALID_PARAMETER);
778 AssertPtrReturn(ppszDst, VERR_INVALID_POINTER);
779 AssertPtrReturn(pcbDst, VERR_INVALID_POINTER);
780
781 int rc = VINF_SUCCESS;
782
783 size_t cwTmp = cwcSrc;
784 PCRTUTF16 pwTmp = pcwszSrc;
785
786 char *pchDst = NULL;
787 size_t cbDst = 0;
788
789 size_t i = 0;
790 while (i < cwTmp)
791 {
792 /* Find zero symbol (end of string). */
793 for (; i < cwTmp && pcwszSrc[i] != 0; i++)
794 ;
795
796 /* Convert found string. */
797 char *psz = NULL;
798 size_t cch = 0;
799 rc = RTUtf16ToUtf8Ex(pwTmp, cwTmp, &psz, pwTmp - pcwszSrc, &cch);
800 if (RT_FAILURE(rc))
801 break;
802
803 /* Append new substring. */
804 char *pchNew = (char *)RTMemRealloc(pchDst, cbDst + cch + 1);
805 if (!pchNew)
806 {
807 RTStrFree(psz);
808 rc = VERR_NO_MEMORY;
809 break;
810 }
811
812 pchDst = pchNew;
813 memcpy(pchDst + cbDst, psz, cch + 1);
814
815 RTStrFree(psz);
816
817 cbDst += cch + 1;
818
819 /* Skip zero symbols. */
820 for (; i < cwTmp && pcwszSrc[i] == 0; i++)
821 ;
822
823 /* Remember start of string. */
824 pwTmp += i;
825 }
826
827 if (RT_SUCCESS(rc))
828 {
829 *ppszDst = pchDst;
830 *pcbDst = cbDst;
831
832 return VINF_SUCCESS;
833 }
834
835 RTMemFree(pchDst);
836
837 return rc;
838}
839
840int ShClUtf16LFLenUtf8(PCRTUTF16 pcwszSrc, size_t cwSrc, size_t *pchLen)
841{
842 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
843 AssertPtrReturn(pchLen, VERR_INVALID_POINTER);
844
845 AssertMsgReturn(pcwszSrc[0] != VBOX_SHCL_UTF16BEMARKER,
846 ("Big endian UTF-16 not supported yet\n"), VERR_NOT_SUPPORTED);
847
848 size_t cLen = 0;
849
850 /* Don't copy the endian marker. */
851 size_t i = pcwszSrc[0] == VBOX_SHCL_UTF16LEMARKER ? 1 : 0;
852
853 /* Calculate the size of the destination text string. */
854 /* Is this Utf16 or Utf16-LE? */
855 for (; i < cwSrc; ++i, ++cLen)
856 {
857 /* Check for a single line feed */
858 if (pcwszSrc[i] == VBOX_SHCL_LINEFEED)
859 ++cLen;
860#ifdef RT_OS_DARWIN
861 /* Check for a single carriage return (MacOS) */
862 if (pcwszSrc[i] == VBOX_SHCL_CARRIAGERETURN)
863 ++cLen;
864#endif
865 if (pcwszSrc[i] == 0)
866 {
867 /* Don't count this, as we do so below. */
868 break;
869 }
870 }
871
872 *pchLen = cLen;
873
874 return VINF_SUCCESS;
875}
876
877int ShClUtf16CRLFLenUtf8(PCRTUTF16 pcwszSrc, size_t cwSrc, size_t *pchLen)
878{
879 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
880 AssertReturn(cwSrc, VERR_INVALID_PARAMETER);
881 AssertPtrReturn(pchLen, VERR_INVALID_POINTER);
882
883 AssertMsgReturn(pcwszSrc[0] != VBOX_SHCL_UTF16BEMARKER,
884 ("Big endian UTF-16 not supported yet\n"), VERR_NOT_SUPPORTED);
885
886 size_t cLen = 0;
887
888 /* Calculate the size of the destination text string. */
889 /* Is this Utf16 or Utf16-LE? */
890 if (pcwszSrc[0] == VBOX_SHCL_UTF16LEMARKER)
891 cLen = 0;
892 else
893 cLen = 1;
894
895 for (size_t i = 0; i < cwSrc; ++i, ++cLen)
896 {
897 if ( (i + 1 < cwSrc)
898 && (pcwszSrc[i] == VBOX_SHCL_CARRIAGERETURN)
899 && (pcwszSrc[i + 1] == VBOX_SHCL_LINEFEED))
900 {
901 ++i;
902 }
903 if (pcwszSrc[i] == 0)
904 break;
905 }
906
907 *pchLen = cLen;
908
909 return VINF_SUCCESS;
910}
911
912int ShClConvUtf16LFToCRLF(PCRTUTF16 pcwszSrc, size_t cwcSrc, PRTUTF16 pu16Dst, size_t cwDst)
913{
914 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
915 AssertPtrReturn(pu16Dst, VERR_INVALID_POINTER);
916 AssertReturn(cwDst, VERR_INVALID_PARAMETER);
917
918 AssertMsgReturn(pcwszSrc[0] != VBOX_SHCL_UTF16BEMARKER,
919 ("Big endian UTF-16 not supported yet\n"), VERR_NOT_SUPPORTED);
920
921 int rc = VINF_SUCCESS;
922
923 /* Don't copy the endian marker. */
924 size_t i = pcwszSrc[0] == VBOX_SHCL_UTF16LEMARKER ? 1 : 0;
925 size_t j = 0;
926
927 for (; i < cwcSrc; ++i, ++j)
928 {
929 /* Don't copy the null byte, as we add it below. */
930 if (pcwszSrc[i] == 0)
931 break;
932
933 /* Not enough space in destination? */
934 if (j == cwDst)
935 {
936 rc = VERR_BUFFER_OVERFLOW;
937 break;
938 }
939
940 if (pcwszSrc[i] == VBOX_SHCL_LINEFEED)
941 {
942 /* Insert '\r' in front of '\n', but avoid '\r\r\n' situations
943 because it will result in extra empty lines on the other side. */
944 if ( i == 0
945 || ( i > 1
946 && pcwszSrc[i - 1] != VBOX_SHCL_CARRIAGERETURN
947 )
948 )
949 {
950 pu16Dst[j] = VBOX_SHCL_CARRIAGERETURN;
951 ++j;
952 }
953
954 /* Not enough space in destination? */
955 if (j == cwDst)
956 {
957 rc = VERR_BUFFER_OVERFLOW;
958 break;
959 }
960 }
961#ifdef RT_OS_DARWIN
962 /* Check for a single carriage return (MacOS) */
963 else if (pcwszSrc[i] == VBOX_SHCL_CARRIAGERETURN)
964 {
965 /* Set CR.r, but avoid '\r\r'. */
966 if ( i == 0
967 || ( i > 1
968 && pcwszSrc[i - 1] != VBOX_SHCL_CARRIAGERETURN
969 )
970 )
971 {
972 pu16Dst[j] = VBOX_SHCL_CARRIAGERETURN;
973 ++j;
974 }
975
976 /* Not enough space in destination? */
977 if (j == cwDst)
978 {
979 rc = VERR_BUFFER_OVERFLOW;
980 break;
981 }
982
983 /* Add line feed. */
984 pu16Dst[j] = VBOX_SHCL_LINEFEED;
985 continue;
986 }
987#endif
988 pu16Dst[j] = pcwszSrc[i];
989 }
990
991 if (j == cwDst)
992 rc = VERR_BUFFER_OVERFLOW;
993
994 if (RT_SUCCESS(rc))
995 {
996 /* Add terminator. */
997 pu16Dst[j] = 0;
998 }
999
1000 return rc;
1001}
1002
1003int ShClConvUtf16CRLFToLF(PCRTUTF16 pcwszSrc, size_t cwcSrc, PRTUTF16 pu16Dst, size_t cwDst)
1004{
1005 AssertPtrReturn(pcwszSrc, VERR_INVALID_POINTER);
1006 AssertReturn(cwcSrc, VERR_INVALID_PARAMETER);
1007 AssertPtrReturn(pu16Dst, VERR_INVALID_POINTER);
1008 AssertReturn(cwDst, VERR_INVALID_PARAMETER);
1009
1010 AssertMsgReturn(pcwszSrc[0] != VBOX_SHCL_UTF16BEMARKER,
1011 ("Big endian UTF-16 not supported yet\n"), VERR_NOT_SUPPORTED);
1012
1013 /* Prepend the Utf16 byte order marker if it is missing. */
1014 size_t cwDstPos;
1015 if (pcwszSrc[0] == VBOX_SHCL_UTF16LEMARKER)
1016 {
1017 cwDstPos = 0;
1018 }
1019 else
1020 {
1021 pu16Dst[0] = VBOX_SHCL_UTF16LEMARKER;
1022 cwDstPos = 1;
1023 }
1024
1025 for (size_t i = 0; i < cwcSrc; ++i, ++cwDstPos)
1026 {
1027 if (pcwszSrc[i] == 0)
1028 break;
1029
1030 if (cwDstPos == cwDst)
1031 return VERR_BUFFER_OVERFLOW;
1032
1033 if ( (i + 1 < cwcSrc)
1034 && (pcwszSrc[i] == VBOX_SHCL_CARRIAGERETURN)
1035 && (pcwszSrc[i + 1] == VBOX_SHCL_LINEFEED))
1036 {
1037 ++i;
1038 }
1039
1040 pu16Dst[cwDstPos] = pcwszSrc[i];
1041 }
1042
1043 if (cwDstPos == cwDst)
1044 return VERR_BUFFER_OVERFLOW;
1045
1046 /* Add terminating zero. */
1047 pu16Dst[cwDstPos] = 0;
1048
1049 return VINF_SUCCESS;
1050}
1051
1052int ShClDibToBmp(const void *pvSrc, size_t cbSrc, void **ppvDest, size_t *pcbDest)
1053{
1054 AssertPtrReturn(pvSrc, VERR_INVALID_POINTER);
1055 AssertReturn(cbSrc, VERR_INVALID_PARAMETER);
1056 AssertPtrReturn(ppvDest, VERR_INVALID_POINTER);
1057 AssertPtrReturn(pcbDest, VERR_INVALID_POINTER);
1058
1059 PBMPWIN3XINFOHDR coreHdr = (PBMPWIN3XINFOHDR)pvSrc;
1060 /** @todo Support all the many versions of the DIB headers. */
1061 if ( cbSrc < sizeof(BMPWIN3XINFOHDR)
1062 || RT_LE2H_U32(coreHdr->cbSize) < sizeof(BMPWIN3XINFOHDR)
1063 || RT_LE2H_U32(coreHdr->cbSize) != sizeof(BMPWIN3XINFOHDR))
1064 {
1065 return VERR_INVALID_PARAMETER;
1066 }
1067
1068 size_t offPixel = sizeof(BMPFILEHDR)
1069 + RT_LE2H_U32(coreHdr->cbSize)
1070 + RT_LE2H_U32(coreHdr->cClrUsed) * sizeof(uint32_t);
1071 if (cbSrc < offPixel)
1072 return VERR_INVALID_PARAMETER;
1073
1074 size_t cbDst = sizeof(BMPFILEHDR) + cbSrc;
1075
1076 void *pvDest = RTMemAlloc(cbDst);
1077 if (!pvDest)
1078 return VERR_NO_MEMORY;
1079
1080 PBMPFILEHDR fileHdr = (PBMPFILEHDR)pvDest;
1081
1082 fileHdr->uType = BMP_HDR_MAGIC;
1083 fileHdr->cbFileSize = (uint32_t)RT_H2LE_U32(cbDst);
1084 fileHdr->Reserved1 = 0;
1085 fileHdr->Reserved2 = 0;
1086 fileHdr->offBits = (uint32_t)RT_H2LE_U32(offPixel);
1087
1088 memcpy((uint8_t *)pvDest + sizeof(BMPFILEHDR), pvSrc, cbSrc);
1089
1090 *ppvDest = pvDest;
1091 *pcbDest = cbDst;
1092
1093 return VINF_SUCCESS;
1094}
1095
1096int ShClBmpGetDib(const void *pvSrc, size_t cbSrc, const void **ppvDest, size_t *pcbDest)
1097{
1098 AssertPtrReturn(pvSrc, VERR_INVALID_POINTER);
1099 AssertReturn(cbSrc, VERR_INVALID_PARAMETER);
1100 AssertPtrReturn(ppvDest, VERR_INVALID_POINTER);
1101 AssertPtrReturn(pcbDest, VERR_INVALID_POINTER);
1102
1103 PBMPFILEHDR pBmpHdr = (PBMPFILEHDR)pvSrc;
1104 if ( cbSrc < sizeof(BMPFILEHDR)
1105 || pBmpHdr->uType != BMP_HDR_MAGIC
1106 || RT_LE2H_U32(pBmpHdr->cbFileSize) != cbSrc)
1107 {
1108 return VERR_INVALID_PARAMETER;
1109 }
1110
1111 *ppvDest = ((uint8_t *)pvSrc) + sizeof(BMPFILEHDR);
1112 *pcbDest = cbSrc - sizeof(BMPFILEHDR);
1113
1114 return VINF_SUCCESS;
1115}
1116
1117#ifdef LOG_ENABLED
1118
1119int ShClDbgDumpHtml(const char *pcszSrc, size_t cbSrc)
1120{
1121 int rc = VINF_SUCCESS;
1122 char *pszBuf = (char *)RTMemTmpAllocZ(cbSrc + 1);
1123 if (pszBuf)
1124 {
1125 memcpy(pszBuf, pcszSrc, cbSrc);
1126 pszBuf[cbSrc] = '\0';
1127 for (size_t off = 0; off < cbSrc; ++off)
1128 if (pszBuf[off] == '\n' || pszBuf[off] == '\r')
1129 pszBuf[off] = ' ';
1130 LogFunc(("Removed \\r\\n: %s\n", pszBuf));
1131 RTMemTmpFree(pszBuf);
1132 }
1133 else
1134 rc = VERR_NO_MEMORY;
1135 return rc;
1136}
1137
1138void ShClDbgDumpData(const void *pv, size_t cb, SHCLFORMAT uFormat)
1139{
1140 if (LogIsEnabled())
1141 {
1142 if (uFormat & VBOX_SHCL_FMT_UNICODETEXT)
1143 {
1144 LogFunc(("VBOX_SHCL_FMT_UNICODETEXT:\n"));
1145 if (pv && cb)
1146 LogFunc(("%ls\n", pv));
1147 else
1148 LogFunc(("%p %zu\n", pv, cb));
1149 }
1150 else if (uFormat & VBOX_SHCL_FMT_BITMAP)
1151 LogFunc(("VBOX_SHCL_FMT_BITMAP\n"));
1152 else if (uFormat & VBOX_SHCL_FMT_HTML)
1153 {
1154 LogFunc(("VBOX_SHCL_FMT_HTML:\n"));
1155 if (pv && cb)
1156 {
1157 LogFunc(("%s\n", pv));
1158 ShClDbgDumpHtml((const char *)pv, cb);
1159 }
1160 else
1161 LogFunc(("%p %zu\n", pv, cb));
1162 }
1163 else
1164 LogFunc(("Invalid format %02X\n", uFormat));
1165 }
1166}
1167
1168#endif /* LOG_ENABLED */
1169
1170/**
1171 * Translates a Shared Clipboard host function number to a string.
1172 *
1173 * @returns Function ID string name.
1174 * @param uFn The function to translate.
1175 */
1176const char *ShClHostFunctionToStr(uint32_t uFn)
1177{
1178 switch (uFn)
1179 {
1180 RT_CASE_RET_STR(VBOX_SHCL_HOST_FN_SET_MODE);
1181 RT_CASE_RET_STR(VBOX_SHCL_HOST_FN_SET_TRANSFER_MODE);
1182 RT_CASE_RET_STR(VBOX_SHCL_HOST_FN_SET_HEADLESS);
1183 RT_CASE_RET_STR(VBOX_SHCL_HOST_FN_CANCEL);
1184 RT_CASE_RET_STR(VBOX_SHCL_HOST_FN_ERROR);
1185 }
1186 return "Unknown";
1187}
1188
1189/**
1190 * Translates a Shared Clipboard host message enum to a string.
1191 *
1192 * @returns Message ID string name.
1193 * @param uMsg The message to translate.
1194 */
1195const char *ShClHostMsgToStr(uint32_t uMsg)
1196{
1197 switch (uMsg)
1198 {
1199 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_QUIT);
1200 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_READ_DATA);
1201 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_FORMATS_REPORT);
1202 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_CANCELED);
1203 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_READ_DATA_CID);
1204 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_STATUS);
1205 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_READ);
1206 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_WRITE);
1207 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_READ);
1208 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_WRITE);
1209 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_OPEN);
1210 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_CLOSE);
1211 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_READ);
1212 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_WRITE);
1213 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ);
1214 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_WRITE);
1215 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_OPEN);
1216 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_CLOSE);
1217 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_READ);
1218 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_WRITE);
1219 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_CANCEL);
1220 RT_CASE_RET_STR(VBOX_SHCL_HOST_MSG_TRANSFER_ERROR);
1221 }
1222 return "Unknown";
1223}
1224
1225/**
1226 * Translates a Shared Clipboard guest message enum to a string.
1227 *
1228 * @returns Message ID string name.
1229 * @param uMsg The message to translate.
1230 */
1231const char *ShClGuestMsgToStr(uint32_t uMsg)
1232{
1233 switch (uMsg)
1234 {
1235 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT);
1236 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_REPORT_FORMATS);
1237 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_DATA_READ);
1238 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_DATA_WRITE);
1239 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_CONNECT);
1240 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_REPORT_FEATURES);
1241 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_QUERY_FEATURES);
1242 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT);
1243 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT);
1244 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_MSG_GET);
1245 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_MSG_CANCEL);
1246 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_REPLY);
1247 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_READ);
1248 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_WRITE);
1249 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_READ);
1250 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_WRITE);
1251 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_OPEN);
1252 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_CLOSE);
1253 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_HDR_READ);
1254 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_HDR_WRITE);
1255 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_ENTRY_READ);
1256 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_LIST_ENTRY_WRITE);
1257 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_OBJ_OPEN);
1258 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_OBJ_CLOSE);
1259 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_OBJ_READ);
1260 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_OBJ_WRITE);
1261 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_ERROR);
1262 RT_CASE_RET_STR(VBOX_SHCL_GUEST_FN_NEGOTIATE_CHUNK_SIZE);
1263 }
1264 return "Unknown";
1265}
1266
1267/**
1268 * Converts Shared Clipboard formats to a string.
1269 *
1270 * @returns Stringified Shared Clipboard formats, or NULL on failure. Must be free'd with RTStrFree().
1271 * @param fFormats Shared Clipboard formats to convert.
1272 *
1273 */
1274char *ShClFormatsToStrA(SHCLFORMATS fFormats)
1275{
1276#define APPEND_FMT_TO_STR(_aFmt) \
1277 if (fFormats & VBOX_SHCL_FMT_##_aFmt) \
1278 { \
1279 if (pszFmts) \
1280 { \
1281 rc2 = RTStrAAppend(&pszFmts, ", "); \
1282 if (RT_FAILURE(rc2)) \
1283 break; \
1284 } \
1285 \
1286 rc2 = RTStrAAppend(&pszFmts, #_aFmt); \
1287 if (RT_FAILURE(rc2)) \
1288 break; \
1289 }
1290
1291 char *pszFmts = NULL;
1292 int rc2 = VINF_SUCCESS;
1293
1294 do
1295 {
1296 APPEND_FMT_TO_STR(UNICODETEXT);
1297 APPEND_FMT_TO_STR(BITMAP);
1298 APPEND_FMT_TO_STR(HTML);
1299# ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
1300 APPEND_FMT_TO_STR(URI_LIST);
1301# endif
1302
1303 } while (0);
1304
1305 if (!pszFmts)
1306 rc2 = RTStrAAppend(&pszFmts, "NONE");
1307
1308 if ( RT_FAILURE(rc2)
1309 && pszFmts)
1310 {
1311 RTStrFree(pszFmts);
1312 pszFmts = NULL;
1313 }
1314
1315#undef APPEND_FMT_TO_STR
1316
1317 return pszFmts;
1318}
1319
1320
1321/*********************************************************************************************************************************
1322* Shared Clipboard Cache *
1323*********************************************************************************************************************************/
1324
1325/**
1326 * Returns (mutable) data of a cache entry.
1327 *
1328 * @param pCacheEntry Cache entry to return data for.
1329 * @param pvData Where to return the (mutable) data pointer.
1330 * @param pcbData Where to return the data size.
1331 */
1332void ShClCacheEntryGet(PSHCLCACHEENTRY pCacheEntry, void **pvData, size_t *pcbData)
1333{
1334 AssertPtrReturnVoid(pCacheEntry);
1335 AssertPtrReturnVoid(pvData);
1336 AssertReturnVoid(pcbData);
1337
1338 *pvData = pCacheEntry->pvData;
1339 *pcbData = pCacheEntry->cbData;
1340}
1341
1342/**
1343 * Initializes a cache entry.
1344 *
1345 * @returns VBox status code.
1346 * @param pCacheEntry Cache entry to init.
1347 * @param pvData Data to copy to entry. Can be NULL to initialize an emptry entry.
1348 * @param cbData Size (in bytes) of \a pvData to copy to entry. Must be 0 if \a pvData is NULL.
1349 */
1350static int shClCacheEntryInit(PSHCLCACHEENTRY pCacheEntry, const void *pvData, size_t cbData)
1351{
1352 AssertReturn(RT_VALID_PTR(pvData) || cbData == 0, VERR_INVALID_PARAMETER);
1353
1354 RT_BZERO(pCacheEntry, sizeof(SHCLCACHEENTRY));
1355
1356 if (pvData)
1357 {
1358 pCacheEntry->pvData = RTMemDup(pvData, cbData);
1359 AssertPtrReturn(pCacheEntry->pvData, VERR_NO_MEMORY);
1360 pCacheEntry->cbData = cbData;
1361 }
1362
1363 return VINF_SUCCESS;
1364}
1365
1366/**
1367 * Returns whether a cache entry is valid (cache hit) or not.
1368 *
1369 * @returns \c true if valid, or \c false if not.
1370 * @param pCacheEntry Cache entry to check for.
1371 */
1372DECLINLINE(bool) shClCacheEntryIsValid(PSHCLCACHEENTRY pCacheEntry)
1373{
1374 return pCacheEntry->pvData != NULL;
1375}
1376
1377/**
1378 * Destroys a cache entry.
1379 *
1380 * @param pCacheEntry Cache entry to destroy.
1381 */
1382DECLINLINE(void) shClCacheEntryDestroy(PSHCLCACHEENTRY pCacheEntry)
1383{
1384 if (pCacheEntry->pvData)
1385 {
1386 Assert(pCacheEntry->cbData);
1387 RTMemFree(pCacheEntry->pvData);
1388 pCacheEntry->pvData = NULL;
1389 pCacheEntry->cbData = 0;
1390 }
1391}
1392
1393/**
1394 * Initializes a cache.
1395 *
1396 * @param pCache Cache to init.
1397 */
1398void ShClCacheInit(PSHCLCACHE pCache)
1399{
1400 AssertPtrReturnVoid(pCache);
1401
1402 RT_BZERO(pCache, sizeof(SHCLCACHE));
1403
1404 for (size_t i = 0; i < RT_ELEMENTS(pCache->aEntries); i++)
1405 shClCacheEntryInit(&pCache->aEntries[i], NULL, 0);
1406}
1407
1408/**
1409 * Destroys all entries of a cache.
1410 *
1411 * @param pCache Cache to destroy entries for.
1412 */
1413DECLINLINE(void) shClCacheDestroyEntries(PSHCLCACHE pCache)
1414{
1415 for (size_t i = 0; i < RT_ELEMENTS(pCache->aEntries); i++)
1416 shClCacheEntryDestroy(&pCache->aEntries[i]);
1417}
1418
1419/**
1420 * Destroys a cache.
1421 *
1422 * @param pCache Cache to destroy.
1423 */
1424void ShClCacheDestroy(PSHCLCACHE pCache)
1425{
1426 AssertPtrReturnVoid(pCache);
1427
1428 shClCacheDestroyEntries(pCache);
1429
1430 RT_BZERO(pCache, sizeof(SHCLCACHE));
1431}
1432
1433/**
1434 * Invalidates a cache.
1435 *
1436 * @param pCache Cache to invalidate.
1437 */
1438void ShClCacheInvalidate(PSHCLCACHE pCache)
1439{
1440 AssertPtrReturnVoid(pCache);
1441
1442 shClCacheDestroyEntries(pCache);
1443}
1444
1445/**
1446 * Invalidates a specific cache entry.
1447 *
1448 * @param pCache Cache to invalidate.
1449 * @param uFmt Format to invalidate entry for.
1450 */
1451void ShClCacheInvalidateEntry(PSHCLCACHE pCache, SHCLFORMAT uFmt)
1452{
1453 AssertPtrReturnVoid(pCache);
1454 AssertReturnVoid(uFmt < VBOX_SHCL_FMT_MAX);
1455 shClCacheEntryDestroy(&pCache->aEntries[uFmt]);
1456}
1457
1458/**
1459 * Gets an entry for a Shared Clipboard format.
1460 *
1461 * @returns Pointer to entry if cached, or NULL if not in cache (cache miss).
1462 * @param pCache Cache to get entry for.
1463 * @param uFmt Format to get entry for.
1464 */
1465PSHCLCACHEENTRY ShClCacheGet(PSHCLCACHE pCache, SHCLFORMAT uFmt)
1466{
1467 AssertReturn(uFmt < VBOX_SHCL_FMT_MAX, NULL);
1468 return shClCacheEntryIsValid(&pCache->aEntries[uFmt]) ? &pCache->aEntries[uFmt] : NULL;
1469}
1470
1471/**
1472 * Sets data to cache for a specific clipboard format, internal version.
1473 *
1474 * @returns VBox status code.
1475 * @param pCache Cache to set data for.
1476 * @param uFmt Clipboard format to set data for.
1477 * @param pvData Data to set.
1478 * @param cbData Size (in bytes) of data to set.
1479 */
1480DECLINLINE(int) shClCacheSet(PSHCLCACHE pCache, SHCLFORMAT uFmt, const void *pvData, size_t cbData)
1481{
1482 AssertReturn(uFmt < VBOX_SHCL_FMT_MAX, VERR_INVALID_PARAMETER);
1483 AssertReturn(shClCacheEntryIsValid(&pCache->aEntries[uFmt]) == false, VERR_ALREADY_EXISTS);
1484
1485 return shClCacheEntryInit(&pCache->aEntries[uFmt], pvData, cbData);
1486}
1487
1488/**
1489 * Sets data to cache for a specific clipboard format.
1490 *
1491 * @returns VBox status code.
1492 * @param pCache Cache to set data for.
1493 * @param uFmt Clipboard format to set data for.
1494 * @param pvData Data to set.
1495 * @param cbData Size (in bytes) of data to set.
1496 */
1497int ShClCacheSet(PSHCLCACHE pCache, SHCLFORMAT uFmt, const void *pvData, size_t cbData)
1498{
1499 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
1500
1501 if (!pvData) /* Nothing to cache? */
1502 return VINF_SUCCESS;
1503
1504 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1505
1506 return shClCacheSet(pCache, uFmt, pvData, cbData);
1507}
1508
1509/**
1510 * Sets data to cache for multiple clipboard formats.
1511 *
1512 * Will bail out if a given format cannot be handled with the data given.
1513 *
1514 * @returns VBox status code.
1515 * @param pCache Cache to set data for.
1516 * @param uFmt Clipboard format to set data for.
1517 * @param pvData Data to set.
1518 * @param cbData Size (in bytes) of data to set.
1519 */
1520int ShClCacheSetMultiple(PSHCLCACHE pCache, SHCLFORMATS uFmts, const void *pvData, size_t cbData)
1521{
1522 AssertPtrReturn(pCache, VERR_INVALID_POINTER);
1523
1524 if (!pvData) /* Nothing to cache? */
1525 return VINF_SUCCESS;
1526
1527 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1528
1529 int rc = VINF_SUCCESS;
1530
1531 SHCLFORMATS uFmtsLeft = uFmts;
1532 while (uFmtsLeft)
1533 {
1534 SHCLFORMAT uFmt = VBOX_SHCL_FMT_NONE;
1535 void *pvConv = NULL;
1536 size_t cbConv = 0;
1537 if (uFmtsLeft & VBOX_SHCL_FMT_UNICODETEXT)
1538 {
1539 uFmt = VBOX_SHCL_FMT_UNICODETEXT;
1540
1541 rc = RTStrValidateEncoding((const char *)pvData);
1542 if (RT_SUCCESS(rc))
1543 {
1544 rc = RTStrToUtf16((const char *)pvData, (PRTUTF16 *)&pvConv);
1545 if (RT_SUCCESS(rc))
1546 cbConv = (RTUtf16Len((const PRTUTF16)pvConv) + 1) * sizeof(RTUTF16);
1547 }
1548 else if (!RTUtf16ValidateEncoding((const PRTUTF16)pvData))
1549 {
1550 AssertFailedBreakStmt(rc = VERR_INVALID_PARAMETER);
1551 }
1552 }
1553 else if (uFmtsLeft & VBOX_SHCL_FMT_BITMAP)
1554 uFmt = VBOX_SHCL_FMT_BITMAP;
1555 else if (uFmtsLeft & VBOX_SHCL_FMT_HTML)
1556 uFmt = VBOX_SHCL_FMT_HTML;
1557 else if (uFmtsLeft & VBOX_SHCL_FMT_URI_LIST)
1558 uFmt = VBOX_SHCL_FMT_URI_LIST;
1559 else
1560 AssertFailedBreakStmt(rc = VERR_NOT_SUPPORTED);
1561
1562 uFmtsLeft &= ~uFmt; /* Remove from list. */
1563 Assert(RT_VALID_PTR(pvConv) || cbConv == 0); /* Sanity. */
1564
1565 if (RT_SUCCESS(rc))
1566 rc = shClCacheSet(pCache, uFmt,
1567 pvConv ? pvConv : pvData,
1568 cbConv ? cbConv : cbData);
1569 RTMemFree(pvConv);
1570 AssertRCBreak(rc);
1571 }
1572
1573 return rc;
1574}
1575
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