VirtualBox

source: vbox/trunk/src/VBox/GuestHost/SharedClipboard/clipboard-transfers.cpp@ 81342

Last change on this file since 81342 was 81342, checked in by vboxsync, 5 years ago

Shared Clipboard/Transfers: Some fixes for shClTransferResolvePathAbs(). Untested.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 94.7 KB
Line 
1/* $Id: clipboard-transfers.cpp 81342 2019-10-18 08:11:58Z vboxsync $ */
2/** @file
3 * Shared Clipboard: Common Shared Clipboard transfer handling code.
4 */
5
6/*
7 * Copyright (C) 2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
19#include <VBox/log.h>
20
21#include <iprt/dir.h>
22#include <iprt/file.h>
23#include <iprt/list.h>
24#include <iprt/path.h>
25#include <iprt/rand.h>
26#include <iprt/semaphore.h>
27
28#include <VBox/err.h>
29#include <VBox/HostServices/VBoxClipboardSvc.h>
30#include <VBox/GuestHost/SharedClipboard-transfers.h>
31
32
33static int shClTransferThreadCreate(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser);
34static int shClTransferThreadDestroy(PSHCLTRANSFER pTransfer, RTMSINTERVAL uTimeoutMs);
35static PSHCLTRANSFER shClTransferCtxGetTransferInternal(PSHCLTRANSFERCTX pTransferCtx, uint32_t uIdx);
36static int shClConvertFileCreateFlags(bool fWritable, unsigned fShClFlags, RTFMODE fMode, SHCLOBJHANDLE handleInitial, uint64_t *pfOpen);
37static int shClTransferResolvePathAbs(PSHCLTRANSFER pTransfer, const char *pszPath, uint32_t fFlags, char **ppszResolved);
38
39/** @todo Split this file up in different modules. */
40
41/**
42 * Allocates a new transfer root list.
43 *
44 * @returns Allocated transfer root list on success, or NULL on failure.
45 */
46PSHCLROOTLIST ShClTransferRootListAlloc(void)
47{
48 PSHCLROOTLIST pRootList = (PSHCLROOTLIST)RTMemAllocZ(sizeof(SHCLROOTLIST));
49
50 return pRootList;
51}
52
53/**
54 * Frees a transfer root list.
55 *
56 * @param pRootList transfer root list to free. The pointer will be
57 * invalid after returning from this function.
58 */
59void ShClTransferRootListFree(PSHCLROOTLIST pRootList)
60{
61 if (!pRootList)
62 return;
63
64 for (uint32_t i = 0; i < pRootList->Hdr.cRoots; i++)
65 ShClTransferListEntryInit(&pRootList->paEntries[i]);
66
67 RTMemFree(pRootList);
68 pRootList = NULL;
69}
70
71/**
72 * Initializes a transfer root list header.
73 *
74 * @returns VBox status code.
75 * @param pRootLstHdr Root list header to initialize.
76 */
77int ShClTransferRootListHdrInit(PSHCLROOTLISTHDR pRootLstHdr)
78{
79 AssertPtrReturn(pRootLstHdr, VERR_INVALID_POINTER);
80
81 RT_BZERO(pRootLstHdr, sizeof(SHCLROOTLISTHDR));
82
83 return VINF_SUCCESS;
84}
85
86/**
87 * Destroys a transfer root list header.
88 *
89 * @param pRootLstHdr Root list header to destroy.
90 */
91void ShClTransferRootListHdrDestroy(PSHCLROOTLISTHDR pRootLstHdr)
92{
93 if (!pRootLstHdr)
94 return;
95
96 pRootLstHdr->fRoots = 0;
97 pRootLstHdr->cRoots = 0;
98}
99
100/**
101 * Duplicates a transfer list header.
102 *
103 * @returns Duplicated transfer list header on success, or NULL on failure.
104 * @param pRootLstHdr Root list header to duplicate.
105 */
106PSHCLROOTLISTHDR ShClTransferRootListHdrDup(PSHCLROOTLISTHDR pRootLstHdr)
107{
108 AssertPtrReturn(pRootLstHdr, NULL);
109
110 int rc = VINF_SUCCESS;
111
112 PSHCLROOTLISTHDR pRootsDup = (PSHCLROOTLISTHDR)RTMemAllocZ(sizeof(SHCLROOTLISTHDR));
113 if (pRootsDup)
114 {
115 *pRootsDup = *pRootLstHdr;
116 }
117 else
118 rc = VERR_NO_MEMORY;
119
120 if (RT_FAILURE(rc))
121 {
122 ShClTransferRootListHdrDestroy(pRootsDup);
123 pRootsDup = NULL;
124 }
125
126 return pRootsDup;
127}
128
129/**
130 * (Deep) Copies a clipboard root list entry structure.
131 *
132 * @returns VBox status code.
133 * @param pDst Where to copy the source root list entry to.
134 * @param pSrc Source root list entry to copy.
135 */
136int ShClTransferRootListEntryCopy(PSHCLROOTLISTENTRY pDst, PSHCLROOTLISTENTRY pSrc)
137{
138 return ShClTransferListEntryCopy(pDst, pSrc);
139}
140
141/**
142 * Initializes a clipboard root list entry structure.
143 *
144 * @param pRootListEntry Clipboard root list entry structure to destroy.
145 */
146int ShClTransferRootListEntryInit(PSHCLROOTLISTENTRY pRootListEntry)
147{
148 return ShClTransferListEntryInit(pRootListEntry);
149}
150
151/**
152 * Destroys a clipboard root list entry structure.
153 *
154 * @param pRootListEntry Clipboard root list entry structure to destroy.
155 */
156void ShClTransferRootListEntryDestroy(PSHCLROOTLISTENTRY pRootListEntry)
157{
158 return ShClTransferListEntryDestroy(pRootListEntry);
159}
160
161/**
162 * Duplicates (allocates) a clipboard root list entry structure.
163 *
164 * @returns Duplicated clipboard root list entry structure on success.
165 * @param pRootListEntry Clipboard root list entry to duplicate.
166 */
167PSHCLROOTLISTENTRY ShClTransferRootListEntryDup(PSHCLROOTLISTENTRY pRootListEntry)
168{
169 return ShClTransferListEntryDup(pRootListEntry);
170}
171
172/**
173 * Initializes an list handle info structure.
174 *
175 * @returns VBox status code.
176 * @param pInfo List handle info structure to initialize.
177 */
178int ShClTransferListHandleInfoInit(PSHCLLISTHANDLEINFO pInfo)
179{
180 AssertPtrReturn(pInfo, VERR_INVALID_POINTER);
181
182 pInfo->hList = SHCLLISTHANDLE_INVALID;
183 pInfo->enmType = SHCLOBJTYPE_INVALID;
184
185 pInfo->pszPathLocalAbs = NULL;
186
187 RT_ZERO(pInfo->u);
188
189 return VINF_SUCCESS;
190}
191
192/**
193 * Destroys a list handle info structure.
194 *
195 * @param pInfo List handle info structure to destroy.
196 */
197void ShClTransferListHandleInfoDestroy(PSHCLLISTHANDLEINFO pInfo)
198{
199 if (!pInfo)
200 return;
201
202 if (pInfo->pszPathLocalAbs)
203 {
204 RTStrFree(pInfo->pszPathLocalAbs);
205 pInfo->pszPathLocalAbs = NULL;
206 }
207}
208
209/**
210 * Allocates a transfer list header structure.
211 *
212 * @returns VBox status code.
213 * @param ppListHdr Where to store the allocated transfer list header structure on success.
214 */
215int ShClTransferListHdrAlloc(PSHCLLISTHDR *ppListHdr)
216{
217 int rc;
218
219 PSHCLLISTHDR pListHdr = (PSHCLLISTHDR)RTMemAllocZ(sizeof(SHCLLISTHDR));
220 if (pListHdr)
221 {
222 *ppListHdr = pListHdr;
223 rc = VINF_SUCCESS;
224 }
225 else
226 rc = VERR_NO_MEMORY;
227
228 LogFlowFuncLeaveRC(rc);
229 return rc;
230}
231
232/**
233 * Frees a transfer list header structure.
234 *
235 * @param pListEntry transfer list header structure to free.
236 */
237void ShClTransferListHdrFree(PSHCLLISTHDR pListHdr)
238{
239 if (!pListHdr)
240 return;
241
242 LogFlowFuncEnter();
243
244 ShClTransferListHdrDestroy(pListHdr);
245
246 RTMemFree(pListHdr);
247 pListHdr = NULL;
248}
249
250/**
251 * Duplicates (allocates) a transfer list header structure.
252 *
253 * @returns Duplicated transfer list header structure on success.
254 * @param pListHdr transfer list header to duplicate.
255 */
256PSHCLLISTHDR ShClTransferListHdrDup(PSHCLLISTHDR pListHdr)
257{
258 AssertPtrReturn(pListHdr, NULL);
259
260 PSHCLLISTHDR pListHdrDup = (PSHCLLISTHDR)RTMemAlloc(sizeof(SHCLLISTHDR));
261 if (pListHdrDup)
262 {
263 *pListHdrDup = *pListHdr;
264 }
265
266 return pListHdrDup;
267}
268
269/**
270 * Initializes a transfer list header structure.
271 *
272 * @returns VBox status code.
273 * @param pListHdr Transfer list header struct to initialize.
274 */
275int ShClTransferListHdrInit(PSHCLLISTHDR pListHdr)
276{
277 AssertPtrReturn(pListHdr, VERR_INVALID_POINTER);
278
279 LogFlowFuncEnter();
280
281 ShClTransferListHdrReset(pListHdr);
282
283 return VINF_SUCCESS;
284}
285
286/**
287 * Destroys a transfer list header structure.
288 *
289 * @param pListHdr Transfer list header struct to destroy.
290 */
291void ShClTransferListHdrDestroy(PSHCLLISTHDR pListHdr)
292{
293 if (!pListHdr)
294 return;
295
296 LogFlowFuncEnter();
297}
298
299/**
300 * Resets a transfer list header structure.
301 *
302 * @returns VBox status code.
303 * @param pListHdr Transfer list header struct to reset.
304 */
305void ShClTransferListHdrReset(PSHCLLISTHDR pListHdr)
306{
307 AssertPtrReturnVoid(pListHdr);
308
309 LogFlowFuncEnter();
310
311 RT_BZERO(pListHdr, sizeof(SHCLLISTHDR));
312}
313
314/**
315 * Returns whether a given transfer list header is valid or not.
316 *
317 * @returns \c true if valid, \c false if not.
318 * @param pListHdr Transfer list header to validate.
319 */
320bool ShClTransferListHdrIsValid(PSHCLLISTHDR pListHdr)
321{
322 RT_NOREF(pListHdr);
323 return true; /** @todo Implement this. */
324}
325
326int ShClTransferListOpenParmsCopy(PSHCLLISTOPENPARMS pDst, PSHCLLISTOPENPARMS pSrc)
327{
328 AssertPtrReturn(pDst, VERR_INVALID_POINTER);
329 AssertPtrReturn(pSrc, VERR_INVALID_POINTER);
330
331 int rc = VINF_SUCCESS;
332
333 if (pSrc->pszFilter)
334 {
335 pDst->pszFilter = RTStrDup(pSrc->pszFilter);
336 if (!pDst->pszFilter)
337 rc = VERR_NO_MEMORY;
338 }
339
340 if ( RT_SUCCESS(rc)
341 && pSrc->pszPath)
342 {
343 pDst->pszPath = RTStrDup(pSrc->pszPath);
344 if (!pDst->pszPath)
345 rc = VERR_NO_MEMORY;
346 }
347
348 if (RT_SUCCESS(rc))
349 {
350 pDst->fList = pDst->fList;
351 pDst->cbFilter = pSrc->cbFilter;
352 pDst->cbPath = pSrc->cbPath;
353 }
354
355 return rc;
356}
357
358/**
359 * Duplicates a transfer list open parameters structure.
360 *
361 * @returns Duplicated transfer list open parameters structure on success, or NULL on failure.
362 * @param pParms transfer list open parameters structure to duplicate.
363 */
364PSHCLLISTOPENPARMS ShClTransferListOpenParmsDup(PSHCLLISTOPENPARMS pParms)
365{
366 AssertPtrReturn(pParms, NULL);
367
368 PSHCLLISTOPENPARMS pParmsDup = (PSHCLLISTOPENPARMS)RTMemAllocZ(sizeof(SHCLLISTOPENPARMS));
369 if (!pParmsDup)
370 return NULL;
371
372 int rc = ShClTransferListOpenParmsCopy(pParmsDup, pParms);
373 if (RT_FAILURE(rc))
374 {
375 ShClTransferListOpenParmsDestroy(pParmsDup);
376
377 RTMemFree(pParmsDup);
378 pParmsDup = NULL;
379 }
380
381 return pParmsDup;
382}
383
384/**
385 * Initializes a transfer list open parameters structure.
386 *
387 * @returns VBox status code.
388 * @param pParms transfer list open parameters structure to initialize.
389 */
390int ShClTransferListOpenParmsInit(PSHCLLISTOPENPARMS pParms)
391{
392 AssertPtrReturn(pParms, VERR_INVALID_POINTER);
393
394 RT_BZERO(pParms, sizeof(SHCLLISTOPENPARMS));
395
396 pParms->cbFilter = 64; /** @todo Make this dynamic. */
397 pParms->pszFilter = RTStrAlloc(pParms->cbFilter);
398
399 pParms->cbPath = RTPATH_MAX;
400 pParms->pszPath = RTStrAlloc(pParms->cbPath);
401
402 LogFlowFuncLeave();
403 return VINF_SUCCESS;
404}
405
406/**
407 * Destroys a transfer list open parameters structure.
408 *
409 * @param pParms transfer list open parameters structure to destroy.
410 */
411void ShClTransferListOpenParmsDestroy(PSHCLLISTOPENPARMS pParms)
412{
413 if (!pParms)
414 return;
415
416 if (pParms->pszFilter)
417 {
418 RTStrFree(pParms->pszFilter);
419 pParms->pszFilter = NULL;
420 }
421
422 if (pParms->pszPath)
423 {
424 RTStrFree(pParms->pszPath);
425 pParms->pszPath = NULL;
426 }
427}
428
429/**
430 * Creates (allocates) and initializes a clipboard list entry structure.
431 *
432 * @param ppDirData Where to return the created clipboard list entry structure on success.
433 */
434int ShClTransferListEntryAlloc(PSHCLLISTENTRY *ppListEntry)
435{
436 PSHCLLISTENTRY pListEntry = (PSHCLLISTENTRY)RTMemAlloc(sizeof(SHCLLISTENTRY));
437 if (!pListEntry)
438 return VERR_NO_MEMORY;
439
440 int rc = ShClTransferListEntryInit(pListEntry);
441 if (RT_SUCCESS(rc))
442 *ppListEntry = pListEntry;
443
444 return rc;
445}
446
447/**
448 * Frees a clipboard list entry structure.
449 *
450 * @param pListEntry Clipboard list entry structure to free.
451 */
452void ShClTransferListEntryFree(PSHCLLISTENTRY pListEntry)
453{
454 if (!pListEntry)
455 return;
456
457 ShClTransferListEntryDestroy(pListEntry);
458 RTMemFree(pListEntry);
459}
460
461/**
462 * (Deep) Copies a clipboard list entry structure.
463 *
464 * @returns VBox status code.
465 * @param pListEntry Clipboard list entry to copy.
466 */
467int ShClTransferListEntryCopy(PSHCLLISTENTRY pDst, PSHCLLISTENTRY pSrc)
468{
469 AssertPtrReturn(pDst, VERR_INVALID_POINTER);
470 AssertPtrReturn(pSrc, VERR_INVALID_POINTER);
471
472 int rc = VINF_SUCCESS;
473
474 *pDst = *pSrc;
475
476 if (pSrc->pszName)
477 {
478 pDst->pszName = RTStrDup(pSrc->pszName);
479 if (!pDst->pszName)
480 rc = VERR_NO_MEMORY;
481 }
482
483 if ( RT_SUCCESS(rc)
484 && pSrc->pvInfo)
485 {
486 pDst->pvInfo = RTMemDup(pSrc->pvInfo, pSrc->cbInfo);
487 if (pDst->pvInfo)
488 {
489 pDst->cbInfo = pSrc->cbInfo;
490 }
491 else
492 rc = VERR_NO_MEMORY;
493 }
494
495 if (RT_FAILURE(rc))
496 {
497 if (pDst->pvInfo)
498 {
499 RTMemFree(pDst->pvInfo);
500 pDst->pvInfo = NULL;
501 pDst->cbInfo = 0;
502 }
503 }
504
505 return rc;
506}
507
508/**
509 * Duplicates (allocates) a clipboard list entry structure.
510 *
511 * @returns Duplicated clipboard list entry structure on success.
512 * @param pListEntry Clipboard list entry to duplicate.
513 */
514PSHCLLISTENTRY ShClTransferListEntryDup(PSHCLLISTENTRY pListEntry)
515{
516 AssertPtrReturn(pListEntry, NULL);
517
518 int rc = VINF_SUCCESS;
519
520 PSHCLLISTENTRY pListEntryDup = (PSHCLLISTENTRY)RTMemAllocZ(sizeof(SHCLLISTENTRY));
521 if (pListEntryDup)
522 rc = ShClTransferListEntryCopy(pListEntryDup, pListEntry);
523
524 if (RT_FAILURE(rc))
525 {
526 ShClTransferListEntryDestroy(pListEntryDup);
527
528 RTMemFree(pListEntryDup);
529 pListEntryDup = NULL;
530 }
531
532 return pListEntryDup;
533}
534
535/**
536 * Initializes a clipboard list entry structure.
537 *
538 * @returns VBox status code.
539 * @param pListEntry Clipboard list entry structure to initialize.
540 */
541int ShClTransferListEntryInit(PSHCLLISTENTRY pListEntry)
542{
543 RT_BZERO(pListEntry, sizeof(SHCLLISTENTRY));
544
545 pListEntry->pszName = RTStrAlloc(SHCLLISTENTRY_MAX_NAME);
546 if (!pListEntry->pszName)
547 return VERR_NO_MEMORY;
548
549 pListEntry->cbName = SHCLLISTENTRY_MAX_NAME;
550
551 pListEntry->pvInfo = (PSHCLFSOBJINFO)RTMemAlloc(sizeof(SHCLFSOBJINFO));
552 if (pListEntry->pvInfo)
553 {
554 pListEntry->cbInfo = sizeof(SHCLFSOBJINFO);
555 pListEntry->fInfo = VBOX_SHCL_INFO_FLAG_FSOBJINFO;
556
557 return VINF_SUCCESS;
558 }
559
560 return VERR_NO_MEMORY;
561}
562
563/**
564 * Destroys a clipboard list entry structure.
565 *
566 * @param pListEntry Clipboard list entry structure to destroy.
567 */
568void ShClTransferListEntryDestroy(PSHCLLISTENTRY pListEntry)
569{
570 if (!pListEntry)
571 return;
572
573 if (pListEntry->pszName)
574 {
575 RTStrFree(pListEntry->pszName);
576
577 pListEntry->pszName = NULL;
578 pListEntry->cbName = 0;
579 }
580
581 if (pListEntry->pvInfo)
582 {
583 RTMemFree(pListEntry->pvInfo);
584 pListEntry->pvInfo = NULL;
585 pListEntry->cbInfo = 0;
586 }
587}
588
589/**
590 * Returns whether a given clipboard data chunk is valid or not.
591 *
592 * @returns \c true if valid, \c false if not.
593 * @param pListEntry Clipboard data chunk to validate.
594 */
595bool ShClTransferListEntryIsValid(PSHCLLISTENTRY pListEntry)
596{
597 RT_NOREF(pListEntry);
598
599 /** @todo Verify checksum. */
600
601 return true; /** @todo Implement this. */
602}
603
604/**
605 * Initializes a transfer object context.
606 *
607 * @returns VBox status code.
608 * @param pObjCtx transfer object context to initialize.
609 */
610int ShClTransferObjCtxInit(PSHCLCLIENTTRANSFEROBJCTX pObjCtx)
611{
612 AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
613
614 LogFlowFuncEnter();
615
616 pObjCtx->uHandle = SHCLOBJHANDLE_INVALID;
617
618 return VINF_SUCCESS;
619}
620
621/**
622 * Destroys a transfer object context.
623 *
624 * @param pObjCtx transfer object context to destroy.
625 */
626void ShClTransferObjCtxDestroy(PSHCLCLIENTTRANSFEROBJCTX pObjCtx)
627{
628 AssertPtrReturnVoid(pObjCtx);
629
630 LogFlowFuncEnter();
631}
632
633/**
634 * Returns if a transfer object context is valid or not.
635 *
636 * @returns \c true if valid, \c false if not.
637 * @param pObjCtx transfer object context to check.
638 */
639bool ShClTransferObjCtxIsValid(PSHCLCLIENTTRANSFEROBJCTX pObjCtx)
640{
641 return ( pObjCtx
642 && pObjCtx->uHandle != SHCLOBJHANDLE_INVALID);
643}
644
645/**
646 * Initializes an object handle info structure.
647 *
648 * @returns VBox status code.
649 * @param pInfo Object handle info structure to initialize.
650 */
651int ShClTransferObjHandleInfoInit(PSHCLOBJHANDLEINFO pInfo)
652{
653 AssertPtrReturn(pInfo, VERR_INVALID_POINTER);
654
655 pInfo->hObj = SHCLOBJHANDLE_INVALID;
656 pInfo->enmType = SHCLOBJTYPE_INVALID;
657
658 pInfo->pszPathLocalAbs = NULL;
659
660 RT_ZERO(pInfo->u);
661
662 return VINF_SUCCESS;
663}
664
665/**
666 * Destroys an object handle info structure.
667 *
668 * @param pInfo Object handle info structure to destroy.
669 */
670void ShClTransferObjHandleInfoDestroy(PSHCLOBJHANDLEINFO pInfo)
671{
672 if (!pInfo)
673 return;
674
675 if (pInfo->pszPathLocalAbs)
676 {
677 RTStrFree(pInfo->pszPathLocalAbs);
678 pInfo->pszPathLocalAbs = NULL;
679 }
680}
681
682/**
683 * Initializes a transfer object open parameters structure.
684 *
685 * @returns VBox status code.
686 * @param pParms transfer object open parameters structure to initialize.
687 */
688int ShClTransferObjOpenParmsInit(PSHCLOBJOPENCREATEPARMS pParms)
689{
690 AssertPtrReturn(pParms, VERR_INVALID_POINTER);
691
692 int rc;
693
694 RT_BZERO(pParms, sizeof(SHCLOBJOPENCREATEPARMS));
695
696 pParms->cbPath = RTPATH_MAX; /** @todo Make this dynamic. */
697 pParms->pszPath = RTStrAlloc(pParms->cbPath);
698 if (pParms->pszPath)
699 {
700 rc = VINF_SUCCESS;
701 }
702 else
703 rc = VERR_NO_MEMORY;
704
705 LogFlowFuncLeaveRC(rc);
706 return rc;
707}
708
709/**
710 * Copies a transfer object open parameters structure from source to destination.
711 *
712 * @returns VBox status code.
713 * @param pParmsDst Where to copy the source transfer object open parameters to.
714 * @param pParmsSrc Which source transfer object open parameters to copy.
715 */
716int ShClTransferObjOpenParmsCopy(PSHCLOBJOPENCREATEPARMS pParmsDst, PSHCLOBJOPENCREATEPARMS pParmsSrc)
717{
718 int rc;
719
720 *pParmsDst = *pParmsSrc;
721
722 if (pParmsSrc->pszPath)
723 {
724 Assert(pParmsSrc->cbPath);
725 pParmsDst->pszPath = RTStrDup(pParmsSrc->pszPath);
726 if (pParmsDst->pszPath)
727 {
728 rc = VINF_SUCCESS;
729 }
730 else
731 rc = VERR_NO_MEMORY;
732 }
733 else
734 rc = VINF_SUCCESS;
735
736 LogFlowFuncLeaveRC(rc);
737 return rc;
738}
739
740/**
741 * Destroys a transfer object open parameters structure.
742 *
743 * @param pParms transfer object open parameters structure to destroy.
744 */
745void ShClTransferObjOpenParmsDestroy(PSHCLOBJOPENCREATEPARMS pParms)
746{
747 if (!pParms)
748 return;
749
750 if (pParms->pszPath)
751 {
752 RTStrFree(pParms->pszPath);
753 pParms->pszPath = NULL;
754 }
755}
756
757/**
758 * Returns a specific object handle info of a transfer.
759 *
760 * @returns Pointer to object handle info if found, or NULL if not found.
761 * @param pTransfer Clipboard transfer to get object handle info from.
762 * @param hObj Object handle of the object to get handle info for.
763 */
764DECLINLINE(PSHCLOBJHANDLEINFO) shClTransferObjGet(PSHCLTRANSFER pTransfer, SHCLOBJHANDLE hObj)
765{
766 PSHCLOBJHANDLEINFO pIt;
767 RTListForEach(&pTransfer->lstObj, pIt, SHCLOBJHANDLEINFO, Node) /** @todo Slooow ...but works for now. */
768 {
769 if (pIt->hObj == hObj)
770 return pIt;
771 }
772
773 return NULL;
774}
775
776/**
777 * Opens a transfer object.
778 *
779 * @returns VBox status code.
780 * @param pTransfer Clipboard transfer to open the object for.
781 * @param pOpenCreateParms Open / create parameters of transfer object to open / create.
782 * @param phObj Where to store the handle of transfer object opened on success.
783 */
784int ShClTransferObjOpen(PSHCLTRANSFER pTransfer, PSHCLOBJOPENCREATEPARMS pOpenCreateParms,
785 PSHCLOBJHANDLE phObj)
786{
787 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
788 AssertPtrReturn(pOpenCreateParms, VERR_INVALID_POINTER);
789 AssertPtrReturn(phObj, VERR_INVALID_POINTER);
790
791 int rc = VINF_SUCCESS;
792
793 AssertMsgReturn(pTransfer->pszPathRootAbs, ("Transfer has no root path set\n"), VERR_INVALID_PARAMETER);
794 AssertMsgReturn(pOpenCreateParms->pszPath, ("No path in open/create params set\n"), VERR_INVALID_PARAMETER);
795
796 if (pTransfer->cObjHandles == pTransfer->cMaxObjHandles)
797 return VERR_SHCLPB_MAX_OBJECTS_REACHED;
798
799 LogFlowFunc(("pszPath=%s, fCreate=0x%x\n", pOpenCreateParms->pszPath, pOpenCreateParms->fCreate));
800
801 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
802 {
803 PSHCLOBJHANDLEINFO pInfo
804 = (PSHCLOBJHANDLEINFO)RTMemAllocZ(sizeof(SHCLOBJHANDLEINFO));
805 if (pInfo)
806 {
807 rc = ShClTransferObjHandleInfoInit(pInfo);
808 if (RT_SUCCESS(rc))
809 {
810 /*
811 * Make sure the transfer direction matches the open/create parameters.
812 */
813 if (pTransfer->State.enmDir == SHCLTRANSFERDIR_READ)
814 {
815 if (pOpenCreateParms->fCreate & SHCL_OBJ_CF_ACCESS_READ) /* Read access wanted? */
816 {
817 AssertMsgFailed(("Is not a write transfer, but object open flags are set to read access (0x%x)\n",
818 pOpenCreateParms->fCreate)); /* Should never happen. */
819 rc = VERR_INVALID_PARAMETER;
820 }
821 }
822 else if (pTransfer->State.enmDir == SHCLTRANSFERDIR_WRITE)
823 {
824 if (pOpenCreateParms->fCreate & SHCL_OBJ_CF_ACCESS_WRITE) /* Write access wanted? */
825 {
826 AssertMsgFailed(("Is not a read transfer, but object open flags are set to write access (0x%x)\n",
827 pOpenCreateParms->fCreate)); /* Should never happen. */
828 rc = VERR_INVALID_PARAMETER;
829 }
830 }
831 else
832 {
833 AssertFailed();
834 rc = VERR_NOT_SUPPORTED;
835 }
836
837 if (RT_SUCCESS(rc))
838 {
839 /* Only if this is a read transfer (locally) we're able to actually write to files
840 * (we're reading from the source). */
841 const bool fWritable = pTransfer->State.enmDir == SHCLTRANSFERDIR_READ;
842
843 uint64_t fOpen;
844 rc = shClConvertFileCreateFlags(fWritable,
845 pOpenCreateParms->fCreate, pOpenCreateParms->ObjInfo.Attr.fMode,
846 SHCLOBJHANDLE_INVALID, &fOpen);
847 if (RT_SUCCESS(rc))
848 {
849 rc = shClTransferResolvePathAbs(pTransfer, pOpenCreateParms->pszPath, 0 /* fFlags */, &pInfo->pszPathLocalAbs);
850 if (RT_SUCCESS(rc))
851 {
852 rc = RTFileOpen(&pInfo->u.Local.hFile, pInfo->pszPathLocalAbs, fOpen);
853 if (RT_SUCCESS(rc))
854 {
855 LogRel2(("Shared Clipboard: Opened file '%s'\n", pInfo->pszPathLocalAbs));
856 }
857 else
858 LogRel(("Shared Clipboard: Error opening file '%s', rc=%Rrc\n", pInfo->pszPathLocalAbs, rc));
859 }
860 }
861 }
862 }
863
864 if (RT_SUCCESS(rc))
865 {
866 pInfo->hObj = pTransfer->uObjHandleNext++;
867 pInfo->enmType = SHCLOBJTYPE_FILE;
868
869 RTListAppend(&pTransfer->lstObj, &pInfo->Node);
870 pTransfer->cObjHandles++;
871
872 LogFlowFunc(("cObjHandles=%RU32\n", pTransfer->cObjHandles));
873
874 *phObj = pInfo->hObj;
875 }
876 else
877 {
878 ShClTransferObjHandleInfoDestroy(pInfo);
879 RTMemFree(pInfo);
880 }
881 }
882 else
883 rc = VERR_NO_MEMORY;
884 }
885 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
886 {
887 if (pTransfer->ProviderIface.pfnObjOpen)
888 {
889 rc = pTransfer->ProviderIface.pfnObjOpen(&pTransfer->ProviderCtx, pOpenCreateParms, phObj);
890 }
891 else
892 rc = VERR_NOT_SUPPORTED;
893 }
894
895 LogFlowFuncLeaveRC(rc);
896 return rc;
897}
898
899/**
900 * Closes a transfer object.
901 *
902 * @returns VBox status code.
903 * @param pTransfer Clipboard transfer that contains the object to close.
904 * @param hObj Handle of transfer object to close.
905 */
906int ShClTransferObjClose(PSHCLTRANSFER pTransfer, SHCLOBJHANDLE hObj)
907{
908 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
909
910 int rc = VINF_SUCCESS;
911
912 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
913 {
914 PSHCLOBJHANDLEINFO pInfo = shClTransferObjGet(pTransfer, hObj);
915 if (pInfo)
916 {
917 switch (pInfo->enmType)
918 {
919 case SHCLOBJTYPE_DIRECTORY:
920 {
921 rc = RTDirClose(pInfo->u.Local.hDir);
922 if (RT_SUCCESS(rc))
923 {
924 pInfo->u.Local.hDir = NIL_RTDIR;
925
926 LogRel2(("Shared Clipboard: Closed directory '%s'\n", pInfo->pszPathLocalAbs));
927 }
928 else
929 LogRel(("Shared Clipboard: Closing directory '%s' failed with %Rrc\n", pInfo->pszPathLocalAbs, rc));
930 break;
931 }
932
933 case SHCLOBJTYPE_FILE:
934 {
935 rc = RTFileClose(pInfo->u.Local.hFile);
936 if (RT_SUCCESS(rc))
937 {
938 pInfo->u.Local.hFile = NIL_RTFILE;
939
940 LogRel2(("Shared Clipboard: Closed file '%s'\n", pInfo->pszPathLocalAbs));
941 }
942 else
943 LogRel(("Shared Clipboard: Closing file '%s' failed with %Rrc\n", pInfo->pszPathLocalAbs, rc));
944 break;
945 }
946
947 default:
948 rc = VERR_NOT_IMPLEMENTED;
949 break;
950 }
951
952 RTListNodeRemove(&pInfo->Node);
953
954 Assert(pTransfer->cObjHandles);
955 pTransfer->cObjHandles--;
956
957 ShClTransferObjHandleInfoDestroy(pInfo);
958
959 RTMemFree(pInfo);
960 pInfo = NULL;
961 }
962 else
963 rc = VERR_NOT_FOUND;
964 }
965 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
966 {
967 if (pTransfer->ProviderIface.pfnObjClose)
968 {
969 rc = pTransfer->ProviderIface.pfnObjClose(&pTransfer->ProviderCtx, hObj);
970 }
971 else
972 rc = VERR_NOT_SUPPORTED;
973 }
974
975 LogFlowFuncLeaveRC(rc);
976 return rc;
977}
978
979/**
980 * Reads from a transfer object.
981 *
982 * @returns VBox status code.
983 * @param pTransfer Clipboard transfer that contains the object to read from.
984 * @param hObj Handle of transfer object to read from.
985 * @param pvBuf Buffer for where to store the read data.
986 * @param cbBuf Size (in bytes) of buffer.
987 * @param pcbRead How much bytes were read on success. Optional.
988 */
989int ShClTransferObjRead(PSHCLTRANSFER pTransfer,
990 SHCLOBJHANDLE hObj, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead, uint32_t fFlags)
991{
992 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
993 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
994 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
995 /* pcbRead is optional. */
996 /** @todo Validate fFlags. */
997
998 int rc = VINF_SUCCESS;
999
1000 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1001 {
1002 PSHCLOBJHANDLEINFO pInfo = shClTransferObjGet(pTransfer, hObj);
1003 if (pInfo)
1004 {
1005 switch (pInfo->enmType)
1006 {
1007 case SHCLOBJTYPE_FILE:
1008 {
1009 size_t cbRead;
1010 rc = RTFileRead(pInfo->u.Local.hFile, pvBuf, cbBuf, &cbRead);
1011 if (RT_SUCCESS(rc))
1012 {
1013 if (pcbRead)
1014 *pcbRead = (uint32_t)cbRead;
1015 }
1016 break;
1017 }
1018
1019 default:
1020 rc = VERR_NOT_SUPPORTED;
1021 break;
1022 }
1023 }
1024 else
1025 rc = VERR_NOT_FOUND;
1026 }
1027 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1028 {
1029 if (pTransfer->ProviderIface.pfnObjRead)
1030 {
1031 rc = pTransfer->ProviderIface.pfnObjRead(&pTransfer->ProviderCtx, hObj, pvBuf, cbBuf, fFlags, pcbRead);
1032 }
1033 else
1034 rc = VERR_NOT_SUPPORTED;
1035 }
1036
1037 LogFlowFuncLeaveRC(rc);
1038 return rc;
1039}
1040
1041/**
1042 * Writes to a transfer object.
1043 *
1044 * @returns VBox status code.
1045 * @param pTransfer Clipboard transfer that contains the object to write to.
1046 * @param hObj Handle of transfer object to write to.
1047 * @param pvBuf Buffer of data to write.
1048 * @param cbBuf Size (in bytes) of buffer to write.
1049 * @param pcbWritten How much bytes were writtenon success. Optional.
1050 */
1051int ShClTransferObjWrite(PSHCLTRANSFER pTransfer,
1052 SHCLOBJHANDLE hObj, void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten,
1053 uint32_t fFlags)
1054{
1055 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1056 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1057 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
1058 /* pcbWritten is optional. */
1059
1060 int rc = VINF_SUCCESS;
1061
1062 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1063 {
1064 PSHCLOBJHANDLEINFO pInfo = shClTransferObjGet(pTransfer, hObj);
1065 if (pInfo)
1066 {
1067 switch (pInfo->enmType)
1068 {
1069 case SHCLOBJTYPE_FILE:
1070 {
1071 rc = RTFileWrite(pInfo->u.Local.hFile, pvBuf, cbBuf, (size_t *)pcbWritten);
1072 break;
1073 }
1074
1075 default:
1076 rc = VERR_NOT_SUPPORTED;
1077 break;
1078 }
1079 }
1080 else
1081 rc = VERR_NOT_FOUND;
1082 }
1083 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1084 {
1085 if (pTransfer->ProviderIface.pfnObjWrite)
1086 {
1087 rc = pTransfer->ProviderIface.pfnObjWrite(&pTransfer->ProviderCtx, hObj, pvBuf, cbBuf, fFlags, pcbWritten);
1088 }
1089 else
1090 rc = VERR_NOT_SUPPORTED;
1091 }
1092
1093 LogFlowFuncLeaveRC(rc);
1094 return rc;
1095}
1096
1097/**
1098 * Duplicaates a transfer object data chunk.
1099 *
1100 * @returns Duplicated object data chunk on success, or NULL on failure.
1101 * @param pDataChunk transfer object data chunk to duplicate.
1102 */
1103PSHCLOBJDATACHUNK ShClTransferObjDataChunkDup(PSHCLOBJDATACHUNK pDataChunk)
1104{
1105 if (!pDataChunk)
1106 return NULL;
1107
1108 PSHCLOBJDATACHUNK pDataChunkDup = (PSHCLOBJDATACHUNK)RTMemAllocZ(sizeof(SHCLOBJDATACHUNK));
1109 if (!pDataChunkDup)
1110 return NULL;
1111
1112 if (pDataChunk->pvData)
1113 {
1114 Assert(pDataChunk->cbData);
1115
1116 pDataChunkDup->uHandle = pDataChunk->uHandle;
1117 pDataChunkDup->pvData = RTMemDup(pDataChunk->pvData, pDataChunk->cbData);
1118 pDataChunkDup->cbData = pDataChunk->cbData;
1119 }
1120
1121 return pDataChunkDup;
1122}
1123
1124/**
1125 * Destroys a transfer object data chunk.
1126 *
1127 * @param pDataChunk transfer object data chunk to destroy.
1128 */
1129void ShClTransferObjDataChunkDestroy(PSHCLOBJDATACHUNK pDataChunk)
1130{
1131 if (!pDataChunk)
1132 return;
1133
1134 if (pDataChunk->pvData)
1135 {
1136 Assert(pDataChunk->cbData);
1137
1138 RTMemFree(pDataChunk->pvData);
1139
1140 pDataChunk->pvData = NULL;
1141 pDataChunk->cbData = 0;
1142 }
1143
1144 pDataChunk->uHandle = 0;
1145}
1146
1147/**
1148 * Frees a transfer object data chunk.
1149 *
1150 * @param pDataChunk transfer object data chunk to free. The handed-in pointer will
1151 * be invalid after calling this function.
1152 */
1153void ShClTransferObjDataChunkFree(PSHCLOBJDATACHUNK pDataChunk)
1154{
1155 if (!pDataChunk)
1156 return;
1157
1158 ShClTransferObjDataChunkDestroy(pDataChunk);
1159
1160 RTMemFree(pDataChunk);
1161 pDataChunk = NULL;
1162}
1163
1164/**
1165 * Creates an Clipboard transfer.
1166 *
1167 * @returns VBox status code.
1168 * @param ppTransfer Where to return the created Shared Clipboard transfer struct.
1169 * Must be destroyed by ShClTransferDestroy().
1170 */
1171int ShClTransferCreate(PSHCLTRANSFER *ppTransfer)
1172{
1173 AssertPtrReturn(ppTransfer, VERR_INVALID_POINTER);
1174
1175 LogFlowFuncEnter();
1176
1177 PSHCLTRANSFER pTransfer = (PSHCLTRANSFER)RTMemAlloc(sizeof(SHCLTRANSFER));
1178 if (!pTransfer)
1179 return VERR_NO_MEMORY;
1180
1181 int rc = VINF_SUCCESS;
1182
1183 pTransfer->State.uID = 0;
1184 pTransfer->State.enmStatus = SHCLTRANSFERSTATUS_NONE;
1185 pTransfer->State.enmDir = SHCLTRANSFERDIR_UNKNOWN;
1186 pTransfer->State.enmSource = SHCLSOURCE_INVALID;
1187
1188 pTransfer->pArea = NULL; /* Will be created later if needed. */
1189
1190 pTransfer->Thread.hThread = NIL_RTTHREAD;
1191 pTransfer->Thread.fCancelled = false;
1192 pTransfer->Thread.fStarted = false;
1193 pTransfer->Thread.fStop = false;
1194
1195 pTransfer->pszPathRootAbs = NULL;
1196
1197 pTransfer->uListHandleNext = 1;
1198 pTransfer->uObjHandleNext = 1;
1199
1200 pTransfer->uTimeoutMs = 30 * 1000; /* 30s timeout by default. */
1201 pTransfer->cbMaxChunkSize = _64K; /** @todo Make this configurable. */
1202
1203 pTransfer->pvUser = NULL;
1204 pTransfer->cbUser = 0;
1205
1206 RT_ZERO(pTransfer->Callbacks);
1207
1208 RTListInit(&pTransfer->lstList);
1209 RTListInit(&pTransfer->lstObj);
1210
1211 pTransfer->cRoots = 0;
1212 RTListInit(&pTransfer->lstRoots);
1213
1214 RT_ZERO(pTransfer->Events);
1215
1216 if (RT_SUCCESS(rc))
1217 {
1218 *ppTransfer = pTransfer;
1219 }
1220 else
1221 {
1222 if (pTransfer)
1223 {
1224 ShClTransferDestroy(pTransfer);
1225 RTMemFree(pTransfer);
1226 }
1227 }
1228
1229 LogFlowFuncLeaveRC(rc);
1230 return rc;
1231}
1232
1233/**
1234 * Destroys an Clipboard transfer context struct.
1235 *
1236 * @returns VBox status code.
1237 * @param pTransferCtx Clipboard transfer to destroy.
1238 */
1239int ShClTransferDestroy(PSHCLTRANSFER pTransfer)
1240{
1241 if (!pTransfer)
1242 return VINF_SUCCESS;
1243
1244 LogFlowFuncEnter();
1245
1246 int rc = shClTransferThreadDestroy(pTransfer, 30 * 1000 /* Timeout in ms */);
1247 if (RT_FAILURE(rc))
1248 return rc;
1249
1250 RTStrFree(pTransfer->pszPathRootAbs);
1251
1252 ShClEventSourceDestroy(&pTransfer->Events);
1253
1254 PSHCLLISTHANDLEINFO pItList, pItListNext;
1255 RTListForEachSafe(&pTransfer->lstList, pItList, pItListNext, SHCLLISTHANDLEINFO, Node)
1256 {
1257 ShClTransferListHandleInfoDestroy(pItList);
1258
1259 RTListNodeRemove(&pItList->Node);
1260
1261 RTMemFree(pItList);
1262 }
1263
1264 PSHCLOBJHANDLEINFO pItObj, pItObjNext;
1265 RTListForEachSafe(&pTransfer->lstObj, pItObj, pItObjNext, SHCLOBJHANDLEINFO, Node)
1266 {
1267 ShClTransferObjHandleInfoDestroy(pItObj);
1268
1269 RTListNodeRemove(&pItObj->Node);
1270
1271 RTMemFree(pItObj);
1272 }
1273
1274 LogFlowFuncLeave();
1275 return VINF_SUCCESS;
1276}
1277
1278/**
1279 * Initializes a shared Clipboard transfer object.
1280 *
1281 * @returns VBox status code.
1282 * @param pTransfer Transfer to initialize.
1283 * @param uID ID to use for the transfer. Can be set to 0 if not important.
1284 * @param enmDir Specifies the transfer direction of this transfer.
1285 * @param enmSource Specifies the data source of the transfer.
1286 */
1287int ShClTransferInit(PSHCLTRANSFER pTransfer,
1288 uint32_t uID, SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource)
1289{
1290 pTransfer->State.uID = uID;
1291 pTransfer->State.enmDir = enmDir;
1292 pTransfer->State.enmSource = enmSource;
1293
1294 LogFlowFunc(("uID=%RU32, enmDir=%RU32, enmSource=%RU32\n",
1295 pTransfer->State.uID, pTransfer->State.enmDir, pTransfer->State.enmSource));
1296
1297 int rc = ShClEventSourceCreate(&pTransfer->Events, pTransfer->State.uID);
1298 if (RT_SUCCESS(rc))
1299 {
1300 pTransfer->State.enmStatus = SHCLTRANSFERSTATUS_INITIALIZED; /* Now we're ready to run. */
1301
1302 pTransfer->cMaxListHandles = _4K; /** @todo Make this dynamic. */
1303 pTransfer->cMaxObjHandles = _4K; /** @todo Ditto. */
1304
1305 if (pTransfer->Callbacks.pfnTransferInitialize)
1306 {
1307 SHCLTRANSFERCALLBACKDATA Data = { pTransfer, pTransfer->Callbacks.pvUser, pTransfer->Callbacks.cbUser };
1308 rc = pTransfer->Callbacks.pfnTransferInitialize(&Data);
1309 }
1310 }
1311
1312 LogFlowFuncLeaveRC(rc);
1313 return rc;
1314}
1315
1316int ShClTransferOpen(PSHCLTRANSFER pTransfer)
1317{
1318 int rc = VINF_SUCCESS;
1319
1320 if (pTransfer->ProviderIface.pfnTransferOpen)
1321 rc = pTransfer->ProviderIface.pfnTransferOpen(&pTransfer->ProviderCtx);
1322
1323 LogFlowFuncLeaveRC(rc);
1324 return rc;
1325}
1326
1327int ShClTransferClose(PSHCLTRANSFER pTransfer)
1328{
1329 int rc = VINF_SUCCESS;
1330
1331 if (pTransfer->ProviderIface.pfnTransferClose)
1332 rc = pTransfer->ProviderIface.pfnTransferClose(&pTransfer->ProviderCtx);
1333
1334 LogFlowFuncLeaveRC(rc);
1335 return rc;
1336}
1337
1338/**
1339 * Returns a specific list handle info of a transfer.
1340 *
1341 * @returns Pointer to list handle info if found, or NULL if not found.
1342 * @param pTransfer Clipboard transfer to get list handle info from.
1343 * @param hList List handle of the list to get handle info for.
1344 */
1345DECLINLINE(PSHCLLISTHANDLEINFO) shClTransferListGetByHandle(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList)
1346{
1347 PSHCLLISTHANDLEINFO pIt;
1348 RTListForEach(&pTransfer->lstList, pIt, SHCLLISTHANDLEINFO, Node) /** @todo Sloooow ... improve this. */
1349 {
1350 if (pIt->hList == hList)
1351 return pIt;
1352 }
1353
1354 return NULL;
1355}
1356
1357/**
1358 * Creates a new list handle (local only).
1359 *
1360 * @returns New List handle on success, or SHCLLISTHANDLE_INVALID on error.
1361 * @param pTransfer Clipboard transfer to create new list handle for.
1362 */
1363DECLINLINE(SHCLLISTHANDLE) shClTransferListHandleNew(PSHCLTRANSFER pTransfer)
1364{
1365 return pTransfer->uListHandleNext++; /** @todo Good enough for now. Improve this later. */
1366}
1367
1368/**
1369 * Resolves a relative path of a specific transfer to its absolute path.
1370 *
1371 * @returns VBox status code.
1372 * @param pTransfer Clipboard transfer to resolve path for.
1373 * @param pszPath Path to resolve.
1374 * @param fFlags Resolve flags. Currently not used and must be 0.
1375 * @param ppszResolved Where to store the allocated resolved path. Must be free'd by the called using RTStrFree().
1376 */
1377static int shClTransferResolvePathAbs(PSHCLTRANSFER pTransfer, const char *pszPath, uint32_t fFlags,
1378 char **ppszResolved)
1379{
1380 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1381 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1382 AssertReturn (fFlags == 0, VERR_INVALID_PARAMETER);
1383
1384 LogFlowFunc(("pszPathRootAbs=%s, pszPath=%s\n", pTransfer->pszPathRootAbs, pszPath));
1385
1386 /* Paranoia. */
1387 if ( !strlen(pszPath)
1388 || !RTStrIsValidEncoding(pszPath)
1389 || RTStrStr(pszPath, ".")
1390 || RTStrStr(pszPath, ".."))
1391 {
1392 LogRel(("Shared Clipboard: Resolving absolute path '%s' failed, invalid\n", pszPath));
1393 return VERR_INVALID_PARAMETER;
1394 }
1395
1396 int rc;
1397
1398 char *pszResolved = RTPathJoinA(pTransfer->pszPathRootAbs, pszPath);
1399 if (pszResolved)
1400 {
1401 LogFlowFunc(("Resolved to: %s\n", pszResolved));
1402
1403 *ppszResolved = pszResolved;
1404
1405 rc = VINF_SUCCESS;
1406 }
1407 else
1408 rc = VERR_NO_MEMORY;
1409
1410 LogFlowFuncLeaveRC(rc);
1411 return rc;
1412}
1413
1414/**
1415 * Opens a list.
1416 *
1417 * @returns VBox status code.
1418 * @param pTransfer Clipboard transfer to handle.
1419 * @param pOpenParms List open parameters to use for opening.
1420 * @param phList Where to store the List handle of opened list on success.
1421 */
1422int ShClTransferListOpen(PSHCLTRANSFER pTransfer, PSHCLLISTOPENPARMS pOpenParms,
1423 PSHCLLISTHANDLE phList)
1424{
1425 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1426 AssertPtrReturn(pOpenParms, VERR_INVALID_POINTER);
1427 AssertPtrReturn(phList, VERR_INVALID_POINTER);
1428
1429 int rc;
1430
1431 if (pTransfer->cListHandles == pTransfer->cMaxListHandles)
1432 return VERR_SHCLPB_MAX_LISTS_REACHED;
1433
1434 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1435 {
1436 LogFlowFunc(("pszPath=%s\n", pOpenParms->pszPath));
1437
1438 PSHCLLISTHANDLEINFO pInfo
1439 = (PSHCLLISTHANDLEINFO)RTMemAllocZ(sizeof(SHCLLISTHANDLEINFO));
1440 if (pInfo)
1441 {
1442 rc = shClTransferResolvePathAbs(pTransfer, pOpenParms->pszPath, 0 /* fFlags */, &pInfo->pszPathLocalAbs);
1443 if (RT_SUCCESS(rc))
1444 {
1445 rc = ShClTransferListHandleInfoInit(pInfo);
1446 if (RT_SUCCESS(rc))
1447 {
1448 RTFSOBJINFO objInfo;
1449 rc = RTPathQueryInfo(pInfo->pszPathLocalAbs, &objInfo, RTFSOBJATTRADD_NOTHING);
1450 if (RT_SUCCESS(rc))
1451 {
1452 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1453 {
1454 rc = RTDirOpen(&pInfo->u.Local.hDir, pInfo->pszPathLocalAbs);
1455 if (RT_SUCCESS(rc))
1456 {
1457 pInfo->enmType = SHCLOBJTYPE_DIRECTORY;
1458
1459 LogRel2(("Shared Clipboard: Opening directory '%s'\n", pInfo->pszPathLocalAbs));
1460 }
1461 else
1462 LogRel(("Shared Clipboard: Opening directory '%s' failed with %Rrc\n", pInfo->pszPathLocalAbs, rc));
1463
1464 }
1465 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1466 {
1467 rc = RTFileOpen(&pInfo->u.Local.hFile, pInfo->pszPathLocalAbs,
1468 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
1469 if (RT_SUCCESS(rc))
1470 {
1471 pInfo->enmType = SHCLOBJTYPE_FILE;
1472
1473 LogRel2(("Shared Clipboard: Opening file '%s'\n", pInfo->pszPathLocalAbs));
1474 }
1475 else
1476 LogRel(("Shared Clipboard: Opening file '%s' failed with %Rrc\n", pInfo->pszPathLocalAbs, rc));
1477 }
1478 else
1479 rc = VERR_NOT_SUPPORTED;
1480
1481 if (RT_SUCCESS(rc))
1482 {
1483 pInfo->hList = shClTransferListHandleNew(pTransfer);
1484
1485 RTListAppend(&pTransfer->lstList, &pInfo->Node);
1486 pTransfer->cListHandles++;
1487
1488 LogFlowFunc(("pszPathLocalAbs=%s, hList=%RU64, cListHandles=%RU32\n",
1489 pInfo->pszPathLocalAbs, pInfo->hList, pTransfer->cListHandles));
1490 }
1491 else
1492 {
1493 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1494 {
1495 if (RTDirIsValid(pInfo->u.Local.hDir))
1496 RTDirClose(pInfo->u.Local.hDir);
1497 }
1498 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1499 {
1500 if (RTFileIsValid(pInfo->u.Local.hFile))
1501 RTFileClose(pInfo->u.Local.hFile);
1502 }
1503 }
1504 }
1505 }
1506 }
1507
1508 if (RT_FAILURE(rc))
1509 {
1510 ShClTransferListHandleInfoDestroy(pInfo);
1511
1512 RTMemFree(pInfo);
1513 pInfo = NULL;
1514 }
1515 }
1516 else
1517 rc = VERR_NO_MEMORY;
1518 }
1519 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1520 {
1521 if (pTransfer->ProviderIface.pfnListOpen)
1522 {
1523 rc = pTransfer->ProviderIface.pfnListOpen(&pTransfer->ProviderCtx, pOpenParms, phList);
1524 }
1525 else
1526 rc = VERR_NOT_SUPPORTED;
1527 }
1528 else
1529 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1530
1531 LogFlowFuncLeaveRC(rc);
1532 return rc;
1533}
1534
1535/**
1536 * Closes a list.
1537 *
1538 * @returns VBox status code.
1539 * @param pTransfer Clipboard transfer to handle.
1540 * @param hList Handle of list to close.
1541 */
1542int ShClTransferListClose(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList)
1543{
1544 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1545
1546 if (hList == SHCLLISTHANDLE_INVALID)
1547 return VINF_SUCCESS;
1548
1549 int rc = VINF_SUCCESS;
1550
1551 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1552 {
1553 PSHCLLISTHANDLEINFO pInfo = shClTransferListGetByHandle(pTransfer, hList);
1554 if (pInfo)
1555 {
1556 switch (pInfo->enmType)
1557 {
1558 case SHCLOBJTYPE_DIRECTORY:
1559 {
1560 if (RTDirIsValid(pInfo->u.Local.hDir))
1561 {
1562 RTDirClose(pInfo->u.Local.hDir);
1563 pInfo->u.Local.hDir = NIL_RTDIR;
1564 }
1565 break;
1566 }
1567
1568 default:
1569 rc = VERR_NOT_SUPPORTED;
1570 break;
1571 }
1572
1573 RTListNodeRemove(&pInfo->Node);
1574
1575 Assert(pTransfer->cListHandles);
1576 pTransfer->cListHandles--;
1577
1578 RTMemFree(pInfo);
1579 }
1580 else
1581 rc = VERR_NOT_FOUND;
1582 }
1583 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1584 {
1585 if (pTransfer->ProviderIface.pfnListClose)
1586 {
1587 rc = pTransfer->ProviderIface.pfnListClose(&pTransfer->ProviderCtx, hList);
1588 }
1589 else
1590 rc = VERR_NOT_SUPPORTED;
1591 }
1592
1593 LogFlowFuncLeaveRC(rc);
1594 return rc;
1595}
1596
1597/**
1598 * Adds a file to a list heaer.
1599 *
1600 * @returns VBox status code.
1601 * @param pHdr List header to add file to.
1602 * @param pszPath Path of file to add.
1603 */
1604static int shclTransferListHdrAddFile(PSHCLLISTHDR pHdr, const char *pszPath)
1605{
1606 uint64_t cbSize = 0;
1607 int rc = RTFileQuerySizeByPath(pszPath, &cbSize);
1608 if (RT_SUCCESS(rc))
1609 {
1610 pHdr->cbTotalSize += cbSize;
1611 pHdr->cTotalObjects++;
1612 }
1613
1614 LogFlowFuncLeaveRC(rc);
1615 return rc;
1616}
1617
1618/**
1619 * Builds a list header, internal version.
1620 *
1621 * @returns VBox status code.
1622 * @param pHdr Where to store the build list header.
1623 * @param pcszSrcPath Source path of list.
1624 * @param pcszDstPath Destination path of list.
1625 * @param pcszDstBase Destination base path.
1626 * @param cchDstBase Number of charaters of destination base path.
1627 */
1628static int shclTransferListHdrFromDir(PSHCLLISTHDR pHdr, const char *pcszPathAbs)
1629{
1630 AssertPtrReturn(pcszPathAbs, VERR_INVALID_POINTER);
1631
1632 LogFlowFunc(("pcszPathAbs=%s\n", pcszPathAbs));
1633
1634 RTFSOBJINFO objInfo;
1635 int rc = RTPathQueryInfo(pcszPathAbs, &objInfo, RTFSOBJATTRADD_NOTHING);
1636 if (RT_SUCCESS(rc))
1637 {
1638 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1639 {
1640 RTDIR hDir;
1641 rc = RTDirOpen(&hDir, pcszPathAbs);
1642 if (RT_SUCCESS(rc))
1643 {
1644 size_t cbDirEntry = 0;
1645 PRTDIRENTRYEX pDirEntry = NULL;
1646 do
1647 {
1648 /* Retrieve the next directory entry. */
1649 rc = RTDirReadExA(hDir, &pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1650 if (RT_FAILURE(rc))
1651 {
1652 if (rc == VERR_NO_MORE_FILES)
1653 rc = VINF_SUCCESS;
1654 break;
1655 }
1656
1657 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
1658 {
1659 case RTFS_TYPE_DIRECTORY:
1660 {
1661 /* Skip "." and ".." entries. */
1662 if (RTDirEntryExIsStdDotLink(pDirEntry))
1663 break;
1664
1665 pHdr->cTotalObjects++;
1666 break;
1667 }
1668 case RTFS_TYPE_FILE:
1669 {
1670 char *pszSrc = RTPathJoinA(pcszPathAbs, pDirEntry->szName);
1671 if (pszSrc)
1672 {
1673 rc = shclTransferListHdrAddFile(pHdr, pszSrc);
1674 RTStrFree(pszSrc);
1675 }
1676 else
1677 rc = VERR_NO_MEMORY;
1678 break;
1679 }
1680 case RTFS_TYPE_SYMLINK:
1681 {
1682 /** @todo Not implemented yet. */
1683 }
1684
1685 default:
1686 break;
1687 }
1688
1689 } while (RT_SUCCESS(rc));
1690
1691 RTDirReadExAFree(&pDirEntry, &cbDirEntry);
1692 RTDirClose(hDir);
1693 }
1694 }
1695 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1696 {
1697 rc = shclTransferListHdrAddFile(pHdr, pcszPathAbs);
1698 }
1699 else if (RTFS_IS_SYMLINK(objInfo.Attr.fMode))
1700 {
1701 /** @todo Not implemented yet. */
1702 }
1703 else
1704 rc = VERR_NOT_SUPPORTED;
1705 }
1706
1707 LogFlowFuncLeaveRC(rc);
1708 return rc;
1709}
1710
1711/**
1712 * Retrieves the header of a Shared Clipboard list.
1713 *
1714 * @returns VBox status code.
1715 * @param pTransfer Clipboard transfer to handle.
1716 * @param hList Handle of list to get header for.
1717 * @param pHdr Where to store the returned list header information.
1718 */
1719int ShClTransferListGetHeader(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList,
1720 PSHCLLISTHDR pHdr)
1721{
1722 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1723 AssertPtrReturn(pHdr, VERR_INVALID_POINTER);
1724
1725 int rc;
1726
1727 LogFlowFunc(("hList=%RU64\n", hList));
1728
1729 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1730 {
1731 PSHCLLISTHANDLEINFO pInfo = shClTransferListGetByHandle(pTransfer, hList);
1732 if (pInfo)
1733 {
1734 rc = ShClTransferListHdrInit(pHdr);
1735 if (RT_SUCCESS(rc))
1736 {
1737 switch (pInfo->enmType)
1738 {
1739 case SHCLOBJTYPE_DIRECTORY:
1740 {
1741 LogFlowFunc(("DirAbs: %s\n", pInfo->pszPathLocalAbs));
1742
1743 rc = shclTransferListHdrFromDir(pHdr, pInfo->pszPathLocalAbs);
1744 break;
1745 }
1746
1747 case SHCLOBJTYPE_FILE:
1748 {
1749 LogFlowFunc(("FileAbs: %s\n", pInfo->pszPathLocalAbs));
1750
1751 pHdr->cTotalObjects = 1;
1752
1753 RTFSOBJINFO objInfo;
1754 rc = RTFileQueryInfo(pInfo->u.Local.hFile, &objInfo, RTFSOBJATTRADD_NOTHING);
1755 if (RT_SUCCESS(rc))
1756 {
1757 pHdr->cbTotalSize = objInfo.cbObject;
1758 }
1759 break;
1760 }
1761
1762 default:
1763 rc = VERR_NOT_SUPPORTED;
1764 break;
1765 }
1766 }
1767
1768 LogFlowFunc(("cTotalObj=%RU64, cbTotalSize=%RU64\n", pHdr->cTotalObjects, pHdr->cbTotalSize));
1769 }
1770 else
1771 rc = VERR_NOT_FOUND;
1772 }
1773 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1774 {
1775 if (pTransfer->ProviderIface.pfnListHdrRead)
1776 {
1777 rc = pTransfer->ProviderIface.pfnListHdrRead(&pTransfer->ProviderCtx, hList, pHdr);
1778 }
1779 else
1780 rc = VERR_NOT_SUPPORTED;
1781 }
1782 else
1783 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1784
1785 LogFlowFuncLeaveRC(rc);
1786 return rc;
1787}
1788
1789/**
1790 * Returns the current transfer object for a Shared Clipboard transfer list.
1791 *
1792 * Currently not implemented and wil return NULL.
1793 *
1794 * @returns Pointer to transfer object, or NULL if not found / invalid.
1795 * @param pTransfer Clipboard transfer to return transfer object for.
1796 * @param hList Handle of Shared Clipboard transfer list to get object for.
1797 * @param uIdx Index of object to get.
1798 */
1799PSHCLTRANSFEROBJ ShClTransferListGetObj(PSHCLTRANSFER pTransfer,
1800 SHCLLISTHANDLE hList, uint64_t uIdx)
1801{
1802 AssertPtrReturn(pTransfer, NULL);
1803
1804 RT_NOREF(hList, uIdx);
1805
1806 LogFlowFunc(("hList=%RU64\n", hList));
1807
1808 return NULL;
1809}
1810
1811/**
1812 * Reads a single Shared Clipboard list entry.
1813 *
1814 * @returns VBox status code or VERR_NO_MORE_FILES if the end of the list has been reached.
1815 * @param pTransfer Clipboard transfer to handle.
1816 * @param hList List handle of list to read from.
1817 * @param pEntry Where to store the read information.
1818 */
1819int ShClTransferListRead(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList,
1820 PSHCLLISTENTRY pEntry)
1821{
1822 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1823 AssertPtrReturn(pEntry, VERR_INVALID_POINTER);
1824
1825 int rc = VINF_SUCCESS;
1826
1827 LogFlowFunc(("hList=%RU64\n", hList));
1828
1829 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1830 {
1831 PSHCLLISTHANDLEINFO pInfo = shClTransferListGetByHandle(pTransfer, hList);
1832 if (pInfo)
1833 {
1834 switch (pInfo->enmType)
1835 {
1836 case SHCLOBJTYPE_DIRECTORY:
1837 {
1838 LogFlowFunc(("\tDirectory: %s\n", pInfo->pszPathLocalAbs));
1839
1840 for (;;)
1841 {
1842 bool fSkipEntry = false; /* Whether to skip an entry in the enumeration. */
1843
1844 size_t cbDirEntry = 0;
1845 PRTDIRENTRYEX pDirEntry = NULL;
1846 rc = RTDirReadExA(pInfo->u.Local.hDir, &pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1847 if (RT_SUCCESS(rc))
1848 {
1849 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
1850 {
1851 case RTFS_TYPE_DIRECTORY:
1852 {
1853 /* Skip "." and ".." entries. */
1854 if (RTDirEntryExIsStdDotLink(pDirEntry))
1855 {
1856 fSkipEntry = true;
1857 break;
1858 }
1859
1860 LogFlowFunc(("Directory: %s\n", pDirEntry->szName));
1861 break;
1862 }
1863
1864 case RTFS_TYPE_FILE:
1865 {
1866 LogFlowFunc(("File: %s\n", pDirEntry->szName));
1867 break;
1868 }
1869
1870 case RTFS_TYPE_SYMLINK:
1871 {
1872 rc = VERR_NOT_IMPLEMENTED; /** @todo Not implemented yet. */
1873 break;
1874 }
1875
1876 default:
1877 break;
1878 }
1879
1880 if ( RT_SUCCESS(rc)
1881 && !fSkipEntry)
1882 {
1883 rc = RTStrCopy(pEntry->pszName, pEntry->cbName, pDirEntry->szName);
1884 if (RT_SUCCESS(rc))
1885 {
1886 AssertPtr(pEntry->pvInfo);
1887 Assert (pEntry->cbInfo == sizeof(SHCLFSOBJINFO));
1888
1889 ShClFsObjFromIPRT(PSHCLFSOBJINFO(pEntry->pvInfo), &pDirEntry->Info);
1890 }
1891 }
1892
1893 RTDirReadExAFree(&pDirEntry, &cbDirEntry);
1894 }
1895
1896 if ( !fSkipEntry /* Do we have a valid entry? Bail out. */
1897 || RT_FAILURE(rc))
1898 {
1899 break;
1900 }
1901 }
1902
1903 break;
1904 }
1905
1906 case SHCLOBJTYPE_FILE:
1907 {
1908 LogFlowFunc(("\tSingle file: %s\n", pInfo->pszPathLocalAbs));
1909
1910 RTFSOBJINFO objInfo;
1911 rc = RTFileQueryInfo(pInfo->u.Local.hFile, &objInfo, RTFSOBJATTRADD_NOTHING);
1912 if (RT_SUCCESS(rc))
1913 {
1914 pEntry->pvInfo = (PSHCLFSOBJINFO)RTMemAlloc(sizeof(SHCLFSOBJINFO));
1915 if (pEntry->pvInfo)
1916 {
1917 rc = RTStrCopy(pEntry->pszName, pEntry->cbName, pInfo->pszPathLocalAbs);
1918 if (RT_SUCCESS(rc))
1919 {
1920 ShClFsObjFromIPRT(PSHCLFSOBJINFO(pEntry->pvInfo), &objInfo);
1921
1922 pEntry->cbInfo = sizeof(SHCLFSOBJINFO);
1923 pEntry->fInfo = VBOX_SHCL_INFO_FLAG_FSOBJINFO;
1924 }
1925 }
1926 else
1927 rc = VERR_NO_MEMORY;
1928 }
1929
1930 break;
1931 }
1932
1933 default:
1934 rc = VERR_NOT_SUPPORTED;
1935 break;
1936 }
1937 }
1938 else
1939 rc = VERR_NOT_FOUND;
1940 }
1941 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1942 {
1943 if (pTransfer->ProviderIface.pfnListEntryRead)
1944 rc = pTransfer->ProviderIface.pfnListEntryRead(&pTransfer->ProviderCtx, hList, pEntry);
1945 else
1946 rc = VERR_NOT_SUPPORTED;
1947 }
1948
1949 LogFlowFuncLeaveRC(rc);
1950 return rc;
1951}
1952
1953int ShClTransferListWrite(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList,
1954 PSHCLLISTENTRY pEntry)
1955{
1956 RT_NOREF(pTransfer, hList, pEntry);
1957
1958 int rc = VINF_SUCCESS;
1959
1960#if 0
1961 if (pTransfer->ProviderIface.pfnListEntryWrite)
1962 rc = pTransfer->ProviderIface.pfnListEntryWrite(&pTransfer->ProviderCtx, hList, pEntry);
1963#endif
1964
1965 LogFlowFuncLeaveRC(rc);
1966 return rc;
1967}
1968
1969/**
1970 * Returns whether a given list handle is valid or not.
1971 *
1972 * @returns \c true if list handle is valid, \c false if not.
1973 * @param pTransfer Clipboard transfer to handle.
1974 * @param hList List handle to check.
1975 */
1976bool ShClTransferListHandleIsValid(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList)
1977{
1978 bool fIsValid = false;
1979
1980 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1981 {
1982 fIsValid = shClTransferListGetByHandle(pTransfer, hList) != NULL;
1983 }
1984 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1985 {
1986 AssertFailed(); /** @todo Implement. */
1987 }
1988
1989 return fIsValid;
1990}
1991
1992/**
1993 * Sets the transfer provider interface for a given transfer.
1994 *
1995 * @returns VBox status code.
1996 * @param pTransfer Transfer to create transfer provider for.
1997 * @param pCreationCtx Provider creation context to use for provider creation.
1998 */
1999int ShClTransferSetInterface(PSHCLTRANSFER pTransfer,
2000 PSHCLPROVIDERCREATIONCTX pCreationCtx)
2001{
2002 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2003 AssertPtrReturn(pCreationCtx, VERR_INVALID_POINTER);
2004
2005 LogFlowFuncEnter();
2006
2007 int rc = VINF_SUCCESS;
2008
2009 pTransfer->ProviderIface = pCreationCtx->Interface;
2010
2011#ifdef DEBUG
2012# define LOG_IFACE_PTR(a_Name) \
2013 LogFlowFunc(( #a_Name "=%p\n", pTransfer->ProviderIface.a_Name));
2014
2015 LOG_IFACE_PTR(pfnTransferOpen);
2016 LOG_IFACE_PTR(pfnTransferClose);
2017 LOG_IFACE_PTR(pfnRootsGet);
2018 LOG_IFACE_PTR(pfnListOpen);
2019 LOG_IFACE_PTR(pfnListClose);
2020
2021# undef LOG_IFACE_PTR
2022#endif
2023
2024 pTransfer->ProviderCtx.pTransfer = pTransfer;
2025 pTransfer->ProviderCtx.pvUser = pCreationCtx->pvUser;
2026
2027 LogFlowFuncLeaveRC(rc);
2028 return rc;
2029}
2030
2031/**
2032 * Clears (resets) the root list of a shared Clipboard transfer.
2033 *
2034 * @param pTransfer Transfer to clear transfer root list for.
2035 */
2036static void shClTransferListRootsClear(PSHCLTRANSFER pTransfer)
2037{
2038 AssertPtrReturnVoid(pTransfer);
2039
2040 if (pTransfer->pszPathRootAbs)
2041 {
2042 RTStrFree(pTransfer->pszPathRootAbs);
2043 pTransfer->pszPathRootAbs = NULL;
2044 }
2045
2046 PSHCLLISTROOT pListRoot, pListRootNext;
2047 RTListForEachSafe(&pTransfer->lstRoots, pListRoot, pListRootNext, SHCLLISTROOT, Node)
2048 {
2049 RTStrFree(pListRoot->pszPathAbs);
2050
2051 RTListNodeRemove(&pListRoot->Node);
2052
2053 RTMemFree(pListRoot);
2054 pListRoot = NULL;
2055 }
2056
2057 pTransfer->cRoots = 0;
2058}
2059
2060/**
2061 * Resets a shared Clipboard transfer.
2062 *
2063 * @param pTransfer Clipboard transfer to reset.
2064 */
2065void ShClTransferReset(PSHCLTRANSFER pTransfer)
2066{
2067 AssertPtrReturnVoid(pTransfer);
2068
2069 LogFlowFuncEnter();
2070
2071 shClTransferListRootsClear(pTransfer);
2072}
2073
2074/**
2075 * Returns the clipboard area for a Shared Clipboard transfer.
2076 *
2077 * @returns Current clipboard area, or NULL if none.
2078 * @param pTransfer Clipboard transfer to return clipboard area for.
2079 */
2080SharedClipboardArea *ShClTransferGetArea(PSHCLTRANSFER pTransfer)
2081{
2082 AssertPtrReturn(pTransfer, NULL);
2083
2084 return pTransfer->pArea;
2085}
2086
2087/**
2088 * Returns the number of transfer root list entries.
2089 *
2090 * @returns Root list entry count.
2091 * @param pTransfer Clipboard transfer to return root entry count for.
2092 */
2093uint32_t ShClTransferRootsCount(PSHCLTRANSFER pTransfer)
2094{
2095 AssertPtrReturn(pTransfer, 0);
2096
2097 return (uint32_t)pTransfer->cRoots;
2098}
2099
2100/**
2101 * Returns a specific root list entry of a transfer.
2102 *
2103 * @returns Pointer to root list entry if found, or NULL if not found.
2104 * @param pTransfer Clipboard transfer to get root list entry from.
2105 * @param uIdx Index of root list entry to return.
2106 */
2107DECLINLINE(PSHCLLISTROOT) shClTransferRootsGetInternal(PSHCLTRANSFER pTransfer, uint32_t uIdx)
2108{
2109 if (uIdx >= pTransfer->cRoots)
2110 return NULL;
2111
2112 PSHCLLISTROOT pIt = RTListGetFirst(&pTransfer->lstRoots, SHCLLISTROOT, Node);
2113 while (uIdx--) /** @todo Slow, but works for now. */
2114 pIt = RTListGetNext(&pTransfer->lstRoots, pIt, SHCLLISTROOT, Node);
2115
2116 return pIt;
2117}
2118
2119/**
2120 * Get a specific root list entry.
2121 *
2122 * @returns VBox status code.
2123 * @param pTransfer Clipboard transfer to get root list entry of.
2124 * @param uIndex Index (zero-based) of entry to get.
2125 * @param pEntry Where to store the returned entry on success.
2126 */
2127int ShClTransferRootsEntry(PSHCLTRANSFER pTransfer,
2128 uint64_t uIndex, PSHCLROOTLISTENTRY pEntry)
2129{
2130 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2131 AssertPtrReturn(pEntry, VERR_INVALID_POINTER);
2132
2133 if (uIndex >= pTransfer->cRoots)
2134 return VERR_INVALID_PARAMETER;
2135
2136 int rc;
2137
2138 PSHCLLISTROOT pRoot = shClTransferRootsGetInternal(pTransfer, uIndex);
2139 AssertPtrReturn(pRoot, VERR_INVALID_PARAMETER);
2140
2141 /* Make sure that we only advertise relative source paths, not absolute ones. */
2142 const char *pcszSrcPath = pRoot->pszPathAbs;
2143
2144 char *pszFileName = RTPathFilename(pcszSrcPath);
2145 if (pszFileName)
2146 {
2147 Assert(pszFileName >= pcszSrcPath);
2148 size_t cchDstBase = pszFileName - pcszSrcPath;
2149 const char *pszDstPath = &pcszSrcPath[cchDstBase];
2150
2151 LogFlowFunc(("pcszSrcPath=%s, pszDstPath=%s\n", pcszSrcPath, pszDstPath));
2152
2153 rc = ShClTransferListEntryInit(pEntry);
2154 if (RT_SUCCESS(rc))
2155 {
2156 rc = RTStrCopy(pEntry->pszName, pEntry->cbName, pszDstPath);
2157 if (RT_SUCCESS(rc))
2158 {
2159 pEntry->cbInfo = sizeof(SHCLFSOBJINFO);
2160 pEntry->pvInfo = (PSHCLFSOBJINFO)RTMemAlloc(pEntry->cbInfo);
2161 if (pEntry->pvInfo)
2162 {
2163 RTFSOBJINFO fsObjInfo;
2164 rc = RTPathQueryInfo(pcszSrcPath, &fsObjInfo, RTFSOBJATTRADD_NOTHING);
2165 if (RT_SUCCESS(rc))
2166 {
2167 ShClFsObjFromIPRT(PSHCLFSOBJINFO(pEntry->pvInfo), &fsObjInfo);
2168
2169 pEntry->fInfo = VBOX_SHCL_INFO_FLAG_FSOBJINFO;
2170 }
2171 }
2172 else
2173 rc = VERR_NO_MEMORY;
2174 }
2175 }
2176 }
2177 else
2178 rc = VERR_INVALID_POINTER;
2179
2180 LogFlowFuncLeaveRC(rc);
2181 return rc;
2182}
2183
2184/**
2185 * Returns the root entries of a shared Clipboard transfer.
2186 *
2187 * @returns VBox status code.
2188 * @param pTransfer Clipboard transfer to return root entries for.
2189 * @param ppRootList Where to store the root list on success.
2190 */
2191int ShClTransferRootsGet(PSHCLTRANSFER pTransfer, PSHCLROOTLIST *ppRootList)
2192{
2193 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2194 AssertPtrReturn(ppRootList, VERR_INVALID_POINTER);
2195
2196 LogFlowFuncEnter();
2197
2198 int rc = VINF_SUCCESS;
2199
2200 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
2201 {
2202 PSHCLROOTLIST pRootList = ShClTransferRootListAlloc();
2203 if (!pRootList)
2204 return VERR_NO_MEMORY;
2205
2206 const uint64_t cRoots = (uint32_t)pTransfer->cRoots;
2207
2208 LogFlowFunc(("cRoots=%RU64\n", cRoots));
2209
2210 if (cRoots)
2211 {
2212 PSHCLROOTLISTENTRY paRootListEntries
2213 = (PSHCLROOTLISTENTRY)RTMemAllocZ(cRoots * sizeof(SHCLROOTLISTENTRY));
2214 if (paRootListEntries)
2215 {
2216 for (uint64_t i = 0; i < cRoots; ++i)
2217 {
2218 rc = ShClTransferRootsEntry(pTransfer, i, &paRootListEntries[i]);
2219 if (RT_FAILURE(rc))
2220 break;
2221 }
2222
2223 if (RT_SUCCESS(rc))
2224 pRootList->paEntries = paRootListEntries;
2225 }
2226 else
2227 rc = VERR_NO_MEMORY;
2228 }
2229 else
2230 rc = VERR_NOT_FOUND;
2231
2232 if (RT_SUCCESS(rc))
2233 {
2234 pRootList->Hdr.cRoots = cRoots;
2235 pRootList->Hdr.fRoots = 0; /** @todo Implement this. */
2236
2237 *ppRootList = pRootList;
2238 }
2239 }
2240 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
2241 {
2242 if (pTransfer->ProviderIface.pfnRootsGet)
2243 rc = pTransfer->ProviderIface.pfnRootsGet(&pTransfer->ProviderCtx, ppRootList);
2244 else
2245 rc = VERR_NOT_SUPPORTED;
2246 }
2247
2248 LogFlowFuncLeaveRC(rc);
2249 return rc;
2250}
2251
2252
2253/**
2254 * Sets transfer root list entries for a given transfer.
2255 *
2256 * @returns VBox status code.
2257 * @param pTransfer Transfer to set transfer list entries for.
2258 * @param pszRoots String list (separated by CRLF) of root entries to set.
2259 * All entries must have the same root path.
2260 * @param cbRoots Size (in bytes) of string list.
2261 */
2262int ShClTransferRootsSet(PSHCLTRANSFER pTransfer, const char *pszRoots, size_t cbRoots)
2263{
2264 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2265 AssertPtrReturn(pszRoots, VERR_INVALID_POINTER);
2266 AssertReturn(cbRoots, VERR_INVALID_PARAMETER);
2267
2268 if (!RTStrIsValidEncoding(pszRoots))
2269 return VERR_INVALID_UTF8_ENCODING;
2270
2271 int rc = VINF_SUCCESS;
2272
2273 shClTransferListRootsClear(pTransfer);
2274
2275 char *pszPathRootAbs = NULL;
2276
2277 RTCList<RTCString> lstRootEntries = RTCString(pszRoots, cbRoots - 1).split("\r\n");
2278 for (size_t i = 0; i < lstRootEntries.size(); ++i)
2279 {
2280 PSHCLLISTROOT pListRoot = (PSHCLLISTROOT)RTMemAlloc(sizeof(SHCLLISTROOT));
2281 AssertPtrBreakStmt(pListRoot, rc = VERR_NO_MEMORY);
2282
2283 pListRoot->pszPathAbs = RTStrDup(lstRootEntries.at(i).c_str());
2284 AssertPtrBreakStmt(pListRoot->pszPathAbs, rc = VERR_NO_MEMORY);
2285
2286 if (!pszPathRootAbs)
2287 {
2288 pszPathRootAbs = RTStrDup(pListRoot->pszPathAbs);
2289 if (pszPathRootAbs)
2290 {
2291 RTPathStripFilename(pszPathRootAbs);
2292 LogFlowFunc(("pszPathRootAbs=%s\n", pszPathRootAbs));
2293 }
2294 else
2295 rc = VERR_NO_MEMORY;
2296 }
2297
2298 if (RT_FAILURE(rc))
2299 break;
2300
2301 /* Make sure all entries have the same root path. */
2302 if (!RTStrStartsWith(pListRoot->pszPathAbs, pszPathRootAbs))
2303 {
2304 rc = VERR_INVALID_PARAMETER;
2305 break;
2306 }
2307
2308 RTListAppend(&pTransfer->lstRoots, &pListRoot->Node);
2309
2310 pTransfer->cRoots++;
2311 }
2312
2313 /** @todo Entry rollback on failure? */
2314
2315 if (RT_SUCCESS(rc))
2316 {
2317 pTransfer->pszPathRootAbs = pszPathRootAbs;
2318 LogFlowFunc(("pszPathRootAbs=%s, cRoots=%zu\n", pTransfer->pszPathRootAbs, pTransfer->cRoots));
2319
2320 LogRel2(("Shared Clipboard: Transfer uses root '%s'\n", pTransfer->pszPathRootAbs));
2321 }
2322 else
2323 LogRel(("Shared Clipboard: Unable to set roots for transfer, rc=%Rrc\n", rc));
2324
2325 LogFlowFuncLeaveRC(rc);
2326 return rc;
2327}
2328
2329/**
2330 * Returns the transfer's ID.
2331 *
2332 * @returns The transfer's ID.
2333 * @param pTransfer Clipboard transfer to return ID for.
2334 */
2335SHCLTRANSFERID ShClTransferGetID(PSHCLTRANSFER pTransfer)
2336{
2337 AssertPtrReturn(pTransfer, 0);
2338
2339 return pTransfer->State.uID;
2340}
2341
2342/**
2343 * Returns the transfer's direction.
2344 *
2345 * @returns The transfer's direction.
2346 * @param pTransfer Clipboard transfer to return direction for.
2347 */
2348SHCLTRANSFERDIR ShClTransferGetDir(PSHCLTRANSFER pTransfer)
2349{
2350 AssertPtrReturn(pTransfer, SHCLTRANSFERDIR_UNKNOWN);
2351
2352 return pTransfer->State.enmDir;
2353}
2354
2355/**
2356 * Returns the transfer's source.
2357 *
2358 * @returns The transfer's source.
2359 * @param pTransfer Clipboard transfer to return source for.
2360 */
2361SHCLSOURCE ShClTransferGetSource(PSHCLTRANSFER pTransfer)
2362{
2363 AssertPtrReturn(pTransfer, SHCLSOURCE_INVALID);
2364
2365 return pTransfer->State.enmSource;
2366}
2367
2368/**
2369 * Returns the current transfer status.
2370 *
2371 * @returns Current transfer status.
2372 * @param pTransfer Clipboard transfer to return status for.
2373 */
2374SHCLTRANSFERSTATUS ShClTransferGetStatus(PSHCLTRANSFER pTransfer)
2375{
2376 AssertPtrReturn(pTransfer, SHCLTRANSFERSTATUS_NONE);
2377
2378 return pTransfer->State.enmStatus;
2379}
2380
2381/**
2382 * Runs a started Shared Clipboard transfer in a dedicated thread.
2383 *
2384 * @returns VBox status code.
2385 * @param pTransfer Clipboard transfer to run.
2386 * @param pfnThreadFunc Pointer to thread function to use.
2387 * @param pvUser Pointer to user-provided data. Optional.
2388 */
2389int ShClTransferRun(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser)
2390{
2391 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2392 AssertPtrReturn(pfnThreadFunc, VERR_INVALID_POINTER);
2393 /* pvUser is optional. */
2394
2395 AssertMsgReturn(pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_STARTED,
2396 ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
2397 VERR_WRONG_ORDER);
2398
2399 int rc = shClTransferThreadCreate(pTransfer, pfnThreadFunc, pvUser);
2400
2401 LogFlowFuncLeaveRC(rc);
2402 return rc;
2403}
2404
2405/**
2406 * Starts an initialized transfer.
2407 *
2408 * @returns VBox status code.
2409 * @param pTransfer Clipboard transfer to start.
2410 */
2411int ShClTransferStart(PSHCLTRANSFER pTransfer)
2412{
2413 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2414
2415 LogFlowFuncEnter();
2416
2417 /* Ready to start? */
2418 AssertMsgReturn(pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_INITIALIZED,
2419 ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
2420 VERR_WRONG_ORDER);
2421
2422 int rc;
2423
2424 if (pTransfer->Callbacks.pfnTransferStart)
2425 {
2426 SHCLTRANSFERCALLBACKDATA Data = { pTransfer, pTransfer->Callbacks.pvUser, pTransfer->Callbacks.cbUser };
2427 rc = pTransfer->Callbacks.pfnTransferStart(&Data);
2428 }
2429 else
2430 rc = VINF_SUCCESS;
2431
2432 if (RT_SUCCESS(rc))
2433 {
2434 pTransfer->State.enmStatus = SHCLTRANSFERSTATUS_STARTED;
2435 }
2436
2437 LogFlowFuncLeaveRC(rc);
2438 return rc;
2439}
2440
2441/**
2442 * Sets or unsets the callback table to be used for a Shared Clipboard transfer.
2443 *
2444 * @returns VBox status code.
2445 * @param pTransfer Clipboard transfer to set callbacks for.
2446 * @param pCallbacks Pointer to callback table to set.
2447 */
2448void ShClTransferSetCallbacks(PSHCLTRANSFER pTransfer,
2449 PSHCLTRANSFERCALLBACKS pCallbacks)
2450{
2451 AssertPtrReturnVoid(pTransfer);
2452 AssertPtrReturnVoid(pCallbacks);
2453
2454 LogFlowFunc(("pCallbacks=%p\n", pCallbacks));
2455
2456#define SET_CALLBACK(a_pfnCallback) \
2457 if (pCallbacks->a_pfnCallback) \
2458 pTransfer->Callbacks.a_pfnCallback = pCallbacks->a_pfnCallback
2459
2460 SET_CALLBACK(pfnTransferInitialize);
2461 SET_CALLBACK(pfnTransferStart);
2462 SET_CALLBACK(pfnListHeaderComplete);
2463 SET_CALLBACK(pfnListEntryComplete);
2464 SET_CALLBACK(pfnTransferCanceled);
2465 SET_CALLBACK(pfnTransferError);
2466
2467#undef SET_CALLBACK
2468
2469 pTransfer->Callbacks.pvUser = pCallbacks->pvUser;
2470 pTransfer->Callbacks.cbUser = pCallbacks->cbUser;
2471}
2472
2473/**
2474 * Creates a thread for a Shared Clipboard transfer.
2475 *
2476 * @returns VBox status code.
2477 * @param pTransfer Clipboard transfer to create thread for.
2478 * @param pfnThreadFunc Thread function to use for this transfer.
2479 * @param pvUser Pointer to user-provided data.
2480 */
2481static int shClTransferThreadCreate(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser)
2482
2483{
2484 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2485
2486 /* Already marked for stopping? */
2487 AssertMsgReturn(pTransfer->Thread.fStop == false,
2488 ("Thransfer thread already marked for stopping"), VERR_WRONG_ORDER);
2489 /* Already started? */
2490 AssertMsgReturn(pTransfer->Thread.fStarted == false,
2491 ("Thransfer thread already started"), VERR_WRONG_ORDER);
2492
2493 /* Spawn a worker thread, so that we don't block the window thread for too long. */
2494 int rc = RTThreadCreate(&pTransfer->Thread.hThread, pfnThreadFunc,
2495 pvUser, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
2496 "shclp");
2497 if (RT_SUCCESS(rc))
2498 {
2499 int rc2 = RTThreadUserWait(pTransfer->Thread.hThread, 30 * 1000 /* Timeout in ms */);
2500 AssertRC(rc2);
2501
2502 if (pTransfer->Thread.fStarted) /* Did the thread indicate that it started correctly? */
2503 {
2504 /* Nothing to do in here. */
2505 }
2506 else
2507 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
2508 }
2509
2510 LogFlowFuncLeaveRC(rc);
2511 return rc;
2512}
2513
2514/**
2515 * Destroys a thread of a Shared Clipboard transfer.
2516 *
2517 * @returns VBox status code.
2518 * @param pTransfer Clipboard transfer to destroy thread for.
2519 * @param uTimeoutMs Timeout (in ms) to wait for thread creation.
2520 */
2521static int shClTransferThreadDestroy(PSHCLTRANSFER pTransfer, RTMSINTERVAL uTimeoutMs)
2522{
2523 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2524
2525 if (pTransfer->Thread.hThread == NIL_RTTHREAD)
2526 return VINF_SUCCESS;
2527
2528 LogFlowFuncEnter();
2529
2530 /* Set stop indicator. */
2531 pTransfer->Thread.fStop = true;
2532
2533 int rcThread = VERR_WRONG_ORDER;
2534 int rc = RTThreadWait(pTransfer->Thread.hThread, uTimeoutMs, &rcThread);
2535
2536 LogFlowFunc(("Waiting for thread resulted in %Rrc (thread exited with %Rrc)\n", rc, rcThread));
2537
2538 return rc;
2539}
2540
2541/**
2542 * Initializes a Shared Clipboard transfer context.
2543 *
2544 * @returns VBox status code.
2545 * @param pTransferCtx Transfer context to initialize.
2546 */
2547int ShClTransferCtxInit(PSHCLTRANSFERCTX pTransferCtx)
2548{
2549 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2550
2551 LogFlowFunc(("pTransferCtx=%p\n", pTransferCtx));
2552
2553 int rc = RTCritSectInit(&pTransferCtx->CritSect);
2554 if (RT_SUCCESS(rc))
2555 {
2556 RTListInit(&pTransferCtx->List);
2557
2558 pTransferCtx->cTransfers = 0;
2559 pTransferCtx->cRunning = 0;
2560 pTransferCtx->cMaxRunning = UINT16_MAX; /** @todo Make this configurable? */
2561
2562 RT_ZERO(pTransferCtx->bmTransferIds);
2563
2564 ShClTransferCtxReset(pTransferCtx);
2565 }
2566
2567 return VINF_SUCCESS;
2568}
2569
2570/**
2571 * Destroys a shared Clipboard transfer context struct.
2572 *
2573 * @param pTransferCtx Transfer context to destroy.
2574 */
2575void ShClTransferCtxDestroy(PSHCLTRANSFERCTX pTransferCtx)
2576{
2577 AssertPtrReturnVoid(pTransferCtx);
2578
2579 LogFlowFunc(("pTransferCtx=%p\n", pTransferCtx));
2580
2581 if (RTCritSectIsInitialized(&pTransferCtx->CritSect))
2582 RTCritSectDelete(&pTransferCtx->CritSect);
2583
2584 PSHCLTRANSFER pTransfer, pTransferNext;
2585 RTListForEachSafe(&pTransferCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
2586 {
2587 ShClTransferDestroy(pTransfer);
2588
2589 RTListNodeRemove(&pTransfer->Node);
2590
2591 RTMemFree(pTransfer);
2592 pTransfer = NULL;
2593 }
2594
2595 pTransferCtx->cRunning = 0;
2596 pTransferCtx->cTransfers = 0;
2597}
2598
2599/**
2600 * Resets a shared Clipboard transfer.
2601 *
2602 * @param pTransferCtx Transfer context to reset.
2603 */
2604void ShClTransferCtxReset(PSHCLTRANSFERCTX pTransferCtx)
2605{
2606 AssertPtrReturnVoid(pTransferCtx);
2607
2608 LogFlowFuncEnter();
2609
2610 PSHCLTRANSFER pTransfer;
2611 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node)
2612 ShClTransferReset(pTransfer);
2613}
2614
2615/**
2616 * Returns a specific Shared Clipboard transfer, internal version.
2617 *
2618 * @returns Shared Clipboard transfer, or NULL if not found.
2619 * @param pTransferCtx Transfer context to return transfer for.
2620 * @param uID ID of the transfer to return.
2621 */
2622static PSHCLTRANSFER shClTransferCtxGetTransferInternal(PSHCLTRANSFERCTX pTransferCtx, uint32_t uID)
2623{
2624 PSHCLTRANSFER pTransfer;
2625 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node) /** @todo Slow, but works for now. */
2626 {
2627 if (pTransfer->State.uID == uID)
2628 return pTransfer;
2629 }
2630
2631 return NULL;
2632}
2633
2634/**
2635 * Returns a specific Shared Clipboard transfer.
2636 *
2637 * @returns Shared Clipboard transfer, or NULL if not found.
2638 * @param pTransferCtx Transfer context to return transfer for.
2639 * @param uID ID of the transfer to return.
2640 */
2641PSHCLTRANSFER ShClTransferCtxGetTransfer(PSHCLTRANSFERCTX pTransferCtx, uint32_t uID)
2642{
2643 return shClTransferCtxGetTransferInternal(pTransferCtx, uID);
2644}
2645
2646/**
2647 * Returns the number of running Shared Clipboard transfers.
2648 *
2649 * @returns Number of running transfers.
2650 * @param pTransferCtx Transfer context to return number for.
2651 */
2652uint32_t ShClTransferCtxGetRunningTransfers(PSHCLTRANSFERCTX pTransferCtx)
2653{
2654 AssertPtrReturn(pTransferCtx, 0);
2655 return pTransferCtx->cRunning;
2656}
2657
2658/**
2659 * Returns the number of total Shared Clipboard transfers.
2660 *
2661 * @returns Number of total transfers.
2662 * @param pTransferCtx Transfer context to return number for.
2663 */
2664uint32_t ShClTransferCtxGetTotalTransfers(PSHCLTRANSFERCTX pTransferCtx)
2665{
2666 AssertPtrReturn(pTransferCtx, 0);
2667 return pTransferCtx->cTransfers;
2668}
2669
2670/**
2671 * Registers a shared Clipboard transfer with a transfer context, i.e. allocates a transfer ID.
2672 *
2673 * @return VBox status code.
2674 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers
2675 * is reached.
2676 * @param pTransferCtx Transfer context to register transfer to.
2677 * @param pTransfer Transfer to register.
2678 * @param pidTransfer Where to return the transfer ID on success. Optional.
2679 */
2680int ShClTransferCtxTransferRegister(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, uint32_t *pidTransfer)
2681{
2682 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2683 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2684 /* pidTransfer is optional. */
2685
2686 /*
2687 * Pick a random bit as starting point. If it's in use, search forward
2688 * for a free one, wrapping around. We've reserved both the zero'th and
2689 * max-1 IDs.
2690 */
2691 uint32_t idTransfer = RTRandU32Ex(1, VBOX_SHCL_MAX_TRANSFERS - 2);
2692
2693 if (!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer))
2694 { /* likely */ }
2695 else if (pTransferCtx->cTransfers < VBOX_SHCL_MAX_TRANSFERS - 2 /* First and last are not used */)
2696 {
2697 /* Forward search. */
2698 int iHit = ASMBitNextClear(&pTransferCtx->bmTransferIds[0], VBOX_SHCL_MAX_TRANSFERS, idTransfer);
2699 if (iHit < 0)
2700 iHit = ASMBitFirstClear(&pTransferCtx->bmTransferIds[0], VBOX_SHCL_MAX_TRANSFERS);
2701 AssertLogRelMsgReturn(iHit >= 0, ("Transfer count: %RU16\n", pTransferCtx->cTransfers), VERR_SHCLPB_MAX_TRANSFERS_REACHED);
2702 idTransfer = iHit;
2703 AssertLogRelMsgReturn(!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer), ("idObject=%#x\n", idTransfer), VERR_INTERNAL_ERROR_2);
2704 }
2705 else
2706 {
2707 LogFunc(("Maximum number of transfers reached (%RU16 transfers)\n", pTransferCtx->cTransfers));
2708 return VERR_SHCLPB_MAX_TRANSFERS_REACHED;
2709 }
2710
2711 Log2Func(("pTransfer=%p, idTransfer=%RU32 (%RU16 transfers)\n", pTransfer, idTransfer, pTransferCtx->cTransfers));
2712
2713 RTListAppend(&pTransferCtx->List, &pTransfer->Node);
2714
2715 pTransferCtx->cTransfers++;
2716
2717 if (pidTransfer)
2718 *pidTransfer = idTransfer;
2719
2720 return VINF_SUCCESS;
2721}
2722
2723/**
2724 * Registers a shared Clipboard transfer with a transfer contextby specifying an ID for the transfer.
2725 *
2726 * @return VBox status code.
2727 * @retval VERR_ALREADY_EXISTS if a transfer with the given ID already exists.
2728 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers for this context has been reached.
2729 * @param pTransferCtx Transfer context to register transfer to.
2730 * @param pTransfer Transfer to register.
2731 * @param idTransfer Transfer ID to use for registration.
2732 */
2733int ShClTransferCtxTransferRegisterByIndex(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, uint32_t idTransfer)
2734{
2735 LogFlowFunc(("cTransfers=%RU16, idTransfer=%RU32\n", pTransferCtx->cTransfers, idTransfer));
2736
2737 if (pTransferCtx->cTransfers < VBOX_SHCL_MAX_TRANSFERS - 2 /* First and last are not used */)
2738 {
2739 if (!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer))
2740 {
2741 RTListAppend(&pTransferCtx->List, &pTransfer->Node);
2742
2743 pTransferCtx->cTransfers++;
2744 return VINF_SUCCESS;
2745 }
2746
2747 return VERR_ALREADY_EXISTS;
2748 }
2749
2750 LogFunc(("Maximum number of transfers reached (%RU16 transfers)\n", pTransferCtx->cTransfers));
2751 return VERR_SHCLPB_MAX_TRANSFERS_REACHED;
2752}
2753
2754/**
2755 * Unregisters a transfer from an Transfer context.
2756 *
2757 * @retval VINF_SUCCESS on success.
2758 * @retval VERR_NOT_FOUND if the transfer ID was not found.
2759 * @param pTransferCtx Transfer context to unregister transfer from.
2760 * @param idTransfer Transfer ID to unregister.
2761 */
2762int ShClTransferCtxTransferUnregister(PSHCLTRANSFERCTX pTransferCtx, uint32_t idTransfer)
2763{
2764 int rc = VINF_SUCCESS;
2765 AssertMsgStmt(ASMBitTestAndClear(&pTransferCtx->bmTransferIds, idTransfer), ("idTransfer=%#x\n", idTransfer), rc = VERR_NOT_FOUND);
2766
2767 LogFlowFunc(("idTransfer=%RU32\n", idTransfer));
2768
2769 PSHCLTRANSFER pTransfer = shClTransferCtxGetTransferInternal(pTransferCtx, idTransfer);
2770 if (pTransfer)
2771 {
2772 RTListNodeRemove(&pTransfer->Node);
2773
2774 Assert(pTransferCtx->cTransfers);
2775 pTransferCtx->cTransfers--;
2776
2777 Assert(pTransferCtx->cTransfers >= pTransferCtx->cRunning);
2778
2779 LogFlowFunc(("Now %RU32 transfers left\n", pTransferCtx->cTransfers));
2780 }
2781 else
2782 rc = VERR_NOT_FOUND;
2783
2784 LogFlowFuncLeaveRC(rc);
2785 return rc;
2786}
2787
2788/**
2789 * Cleans up all associated transfers which are not needed (anymore).
2790 * This can be due to transfers which only have been announced but not / never being run.
2791 *
2792 * @param pTransferCtx Transfer context to cleanup transfers for.
2793 */
2794void ShClTransferCtxCleanup(PSHCLTRANSFERCTX pTransferCtx)
2795{
2796 AssertPtrReturnVoid(pTransferCtx);
2797
2798 LogFlowFunc(("pTransferCtx=%p, cTransfers=%RU16 cRunning=%RU16\n",
2799 pTransferCtx, pTransferCtx->cTransfers, pTransferCtx->cRunning));
2800
2801 if (pTransferCtx->cTransfers == 0)
2802 return;
2803
2804 /* Remove all transfers which are not in a running state (e.g. only announced). */
2805 PSHCLTRANSFER pTransfer, pTransferNext;
2806 RTListForEachSafe(&pTransferCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
2807 {
2808 if (ShClTransferGetStatus(pTransfer) != SHCLTRANSFERSTATUS_STARTED)
2809 {
2810 ShClTransferDestroy(pTransfer);
2811 RTListNodeRemove(&pTransfer->Node);
2812
2813 RTMemFree(pTransfer);
2814 pTransfer = NULL;
2815
2816 Assert(pTransferCtx->cTransfers);
2817 pTransferCtx->cTransfers--;
2818 }
2819 }
2820}
2821
2822/**
2823 * Returns whether the maximum of concurrent transfers of a specific transfer contexthas been reached or not.
2824 *
2825 * @returns \c if maximum has been reached, \c false if not.
2826 * @param pTransferCtx Transfer context to determine value for.
2827 */
2828bool ShClTransferCtxTransfersMaximumReached(PSHCLTRANSFERCTX pTransferCtx)
2829{
2830 AssertPtrReturn(pTransferCtx, true);
2831
2832 LogFlowFunc(("cRunning=%RU32, cMaxRunning=%RU32\n", pTransferCtx->cRunning, pTransferCtx->cMaxRunning));
2833
2834 Assert(pTransferCtx->cRunning <= pTransferCtx->cMaxRunning);
2835 return pTransferCtx->cRunning == pTransferCtx->cMaxRunning;
2836}
2837
2838/**
2839 * Copies file system objinfo from IPRT to Shared Clipboard format.
2840 *
2841 * @param pDst The Shared Clipboard structure to convert data to.
2842 * @param pSrc The IPRT structure to convert data from.
2843 */
2844void ShClFsObjFromIPRT(PSHCLFSOBJINFO pDst, PCRTFSOBJINFO pSrc)
2845{
2846 pDst->cbObject = pSrc->cbObject;
2847 pDst->cbAllocated = pSrc->cbAllocated;
2848 pDst->AccessTime = pSrc->AccessTime;
2849 pDst->ModificationTime = pSrc->ModificationTime;
2850 pDst->ChangeTime = pSrc->ChangeTime;
2851 pDst->BirthTime = pSrc->BirthTime;
2852 pDst->Attr.fMode = pSrc->Attr.fMode;
2853 /* Clear bits which we don't pass through for security reasons. */
2854 pDst->Attr.fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
2855 RT_ZERO(pDst->Attr.u);
2856 switch (pSrc->Attr.enmAdditional)
2857 {
2858 default:
2859 case RTFSOBJATTRADD_NOTHING:
2860 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_NOTHING;
2861 break;
2862
2863 case RTFSOBJATTRADD_UNIX:
2864 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_UNIX;
2865 pDst->Attr.u.Unix.uid = pSrc->Attr.u.Unix.uid;
2866 pDst->Attr.u.Unix.gid = pSrc->Attr.u.Unix.gid;
2867 pDst->Attr.u.Unix.cHardlinks = pSrc->Attr.u.Unix.cHardlinks;
2868 pDst->Attr.u.Unix.INodeIdDevice = pSrc->Attr.u.Unix.INodeIdDevice;
2869 pDst->Attr.u.Unix.INodeId = pSrc->Attr.u.Unix.INodeId;
2870 pDst->Attr.u.Unix.fFlags = pSrc->Attr.u.Unix.fFlags;
2871 pDst->Attr.u.Unix.GenerationId = pSrc->Attr.u.Unix.GenerationId;
2872 pDst->Attr.u.Unix.Device = pSrc->Attr.u.Unix.Device;
2873 break;
2874
2875 case RTFSOBJATTRADD_EASIZE:
2876 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_EASIZE;
2877 pDst->Attr.u.EASize.cb = pSrc->Attr.u.EASize.cb;
2878 break;
2879 }
2880}
2881
2882/**
2883 * Converts Shared Clipboard create flags (see SharedClipboard-transfers.h) into IPRT create flags.
2884 *
2885 * @returns IPRT status code.
2886 * @param fWritable Whether the object is writable.
2887 * @param fShClFlags Shared clipboard create flags.
2888 * @param fMode File attributes.
2889 * @param handleInitial Initial handle.
2890 * @retval pfOpen Where to store the IPRT creation / open flags.
2891 *
2892 * @sa Initially taken from vbsfConvertFileOpenFlags().
2893 */
2894static int shClConvertFileCreateFlags(bool fWritable, unsigned fShClFlags, RTFMODE fMode,
2895 SHCLOBJHANDLE handleInitial, uint64_t *pfOpen)
2896{
2897 uint64_t fOpen = 0;
2898 int rc = VINF_SUCCESS;
2899
2900 if ( (fMode & RTFS_DOS_MASK) != 0
2901 && (fMode & RTFS_UNIX_MASK) == 0)
2902 {
2903 /* A DOS/Windows guest, make RTFS_UNIX_* from RTFS_DOS_*.
2904 * @todo this is based on rtFsModeNormalize/rtFsModeFromDos.
2905 * May be better to use RTFsModeNormalize here.
2906 */
2907 fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH;
2908 /* x for directories. */
2909 if (fMode & RTFS_DOS_DIRECTORY)
2910 fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
2911 /* writable? */
2912 if (!(fMode & RTFS_DOS_READONLY))
2913 fMode |= RTFS_UNIX_IWUSR | RTFS_UNIX_IWGRP | RTFS_UNIX_IWOTH;
2914
2915 /* Set the requested mode using only allowed bits. */
2916 fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
2917 }
2918 else
2919 {
2920 /* Old linux and solaris additions did not initialize the Info.Attr.fMode field
2921 * and it contained random bits from stack. Detect this using the handle field value
2922 * passed from the guest: old additions set it (incorrectly) to 0, new additions
2923 * set it to SHCLOBJHANDLE_INVALID(~0).
2924 */
2925 if (handleInitial == 0)
2926 {
2927 /* Old additions. Do nothing, use default mode. */
2928 }
2929 else
2930 {
2931 /* New additions or Windows additions. Set the requested mode using only allowed bits.
2932 * Note: Windows guest set RTFS_UNIX_MASK bits to 0, which means a default mode
2933 * will be set in fOpen.
2934 */
2935 fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
2936 }
2937 }
2938
2939 switch ((fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_RW))
2940 {
2941 default:
2942 case SHCL_OBJ_CF_ACCESS_NONE:
2943 {
2944#ifdef RT_OS_WINDOWS
2945 if ((fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_ATTR) != SHCL_OBJ_CF_ACCESS_ATTR_NONE)
2946 fOpen |= RTFILE_O_ATTR_ONLY;
2947 else
2948#endif
2949 fOpen |= RTFILE_O_READ;
2950 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_NONE\n"));
2951 break;
2952 }
2953
2954 case SHCL_OBJ_CF_ACCESS_READ:
2955 {
2956 fOpen |= RTFILE_O_READ;
2957 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_READ\n"));
2958 break;
2959 }
2960
2961 case SHCL_OBJ_CF_ACCESS_WRITE:
2962 {
2963 fOpen |= RTFILE_O_WRITE;
2964 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_WRITE\n"));
2965 break;
2966 }
2967
2968 case SHCL_OBJ_CF_ACCESS_READWRITE:
2969 {
2970 fOpen |= RTFILE_O_READWRITE;
2971 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_READWRITE\n"));
2972 break;
2973 }
2974 }
2975
2976 if (fShClFlags & SHCL_OBJ_CF_ACCESS_APPEND)
2977 {
2978 fOpen |= RTFILE_O_APPEND;
2979 }
2980
2981 switch ((fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_ATTR))
2982 {
2983 default:
2984 case SHCL_OBJ_CF_ACCESS_ATTR_NONE:
2985 {
2986 fOpen |= RTFILE_O_ACCESS_ATTR_DEFAULT;
2987 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_NONE\n"));
2988 break;
2989 }
2990
2991 case SHCL_OBJ_CF_ACCESS_ATTR_READ:
2992 {
2993 fOpen |= RTFILE_O_ACCESS_ATTR_READ;
2994 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_READ\n"));
2995 break;
2996 }
2997
2998 case SHCL_OBJ_CF_ACCESS_ATTR_WRITE:
2999 {
3000 fOpen |= RTFILE_O_ACCESS_ATTR_WRITE;
3001 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_WRITE\n"));
3002 break;
3003 }
3004
3005 case SHCL_OBJ_CF_ACCESS_ATTR_READWRITE:
3006 {
3007 fOpen |= RTFILE_O_ACCESS_ATTR_READWRITE;
3008 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_READWRITE\n"));
3009 break;
3010 }
3011 }
3012
3013 /* Sharing mask */
3014 switch ((fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_DENY))
3015 {
3016 default:
3017 case SHCL_OBJ_CF_ACCESS_DENYNONE:
3018 fOpen |= RTFILE_O_DENY_NONE;
3019 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYNONE\n"));
3020 break;
3021
3022 case SHCL_OBJ_CF_ACCESS_DENYREAD:
3023 fOpen |= RTFILE_O_DENY_READ;
3024 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYREAD\n"));
3025 break;
3026
3027 case SHCL_OBJ_CF_ACCESS_DENYWRITE:
3028 fOpen |= RTFILE_O_DENY_WRITE;
3029 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYWRITE\n"));
3030 break;
3031
3032 case SHCL_OBJ_CF_ACCESS_DENYALL:
3033 fOpen |= RTFILE_O_DENY_ALL;
3034 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYALL\n"));
3035 break;
3036 }
3037
3038 /* Open/Create action mask */
3039 switch ((fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_EXISTS))
3040 {
3041 case SHCL_OBJ_CF_ACT_OPEN_IF_EXISTS:
3042 if (SHCL_OBJ_CF_ACT_CREATE_IF_NEW == (fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_NEW))
3043 {
3044 fOpen |= RTFILE_O_OPEN_CREATE;
3045 LogFlowFunc(("SHCL_OBJ_CF_ACT_OPEN_IF_EXISTS and SHCL_OBJ_CF_ACT_CREATE_IF_NEW\n"));
3046 }
3047 else if (SHCL_OBJ_CF_ACT_FAIL_IF_NEW == (fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_NEW))
3048 {
3049 fOpen |= RTFILE_O_OPEN;
3050 LogFlowFunc(("SHCL_OBJ_CF_ACT_OPEN_IF_EXISTS and SHCL_OBJ_CF_ACT_FAIL_IF_NEW\n"));
3051 }
3052 else
3053 {
3054 LogFlowFunc(("invalid open/create action combination\n"));
3055 rc = VERR_INVALID_PARAMETER;
3056 }
3057 break;
3058 case SHCL_OBJ_CF_ACT_FAIL_IF_EXISTS:
3059 if (SHCL_OBJ_CF_ACT_CREATE_IF_NEW == (fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_NEW))
3060 {
3061 fOpen |= RTFILE_O_CREATE;
3062 LogFlowFunc(("SHCL_OBJ_CF_ACT_FAIL_IF_EXISTS and SHCL_OBJ_CF_ACT_CREATE_IF_NEW\n"));
3063 }
3064 else
3065 {
3066 LogFlowFunc(("invalid open/create action combination\n"));
3067 rc = VERR_INVALID_PARAMETER;
3068 }
3069 break;
3070 case SHCL_OBJ_CF_ACT_REPLACE_IF_EXISTS:
3071 if (SHCL_OBJ_CF_ACT_CREATE_IF_NEW == (fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_NEW))
3072 {
3073 fOpen |= RTFILE_O_CREATE_REPLACE;
3074 LogFlowFunc(("SHCL_OBJ_CF_ACT_REPLACE_IF_EXISTS and SHCL_OBJ_CF_ACT_CREATE_IF_NEW\n"));
3075 }
3076 else if (SHCL_OBJ_CF_ACT_FAIL_IF_NEW == (fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_NEW))
3077 {
3078 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
3079 LogFlowFunc(("SHCL_OBJ_CF_ACT_REPLACE_IF_EXISTS and SHCL_OBJ_CF_ACT_FAIL_IF_NEW\n"));
3080 }
3081 else
3082 {
3083 LogFlowFunc(("invalid open/create action combination\n"));
3084 rc = VERR_INVALID_PARAMETER;
3085 }
3086 break;
3087 case SHCL_OBJ_CF_ACT_OVERWRITE_IF_EXISTS:
3088 if (SHCL_OBJ_CF_ACT_CREATE_IF_NEW == (fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_NEW))
3089 {
3090 fOpen |= RTFILE_O_CREATE_REPLACE;
3091 LogFlowFunc(("SHCL_OBJ_CF_ACT_OVERWRITE_IF_EXISTS and SHCL_OBJ_CF_ACT_CREATE_IF_NEW\n"));
3092 }
3093 else if (SHCL_OBJ_CF_ACT_FAIL_IF_NEW == (fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_NEW))
3094 {
3095 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
3096 LogFlowFunc(("SHCL_OBJ_CF_ACT_OVERWRITE_IF_EXISTS and SHCL_OBJ_CF_ACT_FAIL_IF_NEW\n"));
3097 }
3098 else
3099 {
3100 LogFlowFunc(("invalid open/create action combination\n"));
3101 rc = VERR_INVALID_PARAMETER;
3102 }
3103 break;
3104 default:
3105 {
3106 rc = VERR_INVALID_PARAMETER;
3107 LogFlowFunc(("SHCL_OBJ_CF_ACT_MASK_IF_EXISTS - invalid parameter\n"));
3108 break;
3109 }
3110 }
3111
3112 if (RT_SUCCESS(rc))
3113 {
3114 if (!fWritable)
3115 fOpen &= ~RTFILE_O_WRITE;
3116
3117 *pfOpen = fOpen;
3118 }
3119
3120 LogFlowFuncLeaveRC(rc);
3121 return rc;
3122}
3123
3124/**
3125 * Translates a Shared Clipboard transfer status (SHCLTRANSFERSTATUS_XXX) into a string.
3126 *
3127 * @returns Transfer status string name.
3128 * @param enmStatus The transfer status to translate.
3129 */
3130const char *ShClTransferStatusToStr(SHCLTRANSFERSTATUS enmStatus)
3131{
3132 switch (enmStatus)
3133 {
3134 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_NONE);
3135 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_INITIALIZED);
3136 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_STARTED);
3137 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_STOPPED);
3138 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_CANCELED);
3139 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_KILLED);
3140 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_ERROR);
3141 }
3142 return "Unknown";
3143}
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