VirtualBox

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

Last change on this file since 93414 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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