VirtualBox

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

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

Shared Clipboard/Transfers: Use VERR_INVALID_UTF8_ENCODING in ShClTransferRootsSet().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 93.0 KB
Line 
1/* $Id: clipboard-transfers.cpp 81318 2019-10-17 14:23:54Z 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 sharedClipboardTransferThreadCreate(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser);
34static int sharedClipboardTransferThreadDestroy(PSHCLTRANSFER pTransfer, RTMSINTERVAL uTimeoutMs);
35static PSHCLTRANSFER sharedClipboardTransferCtxGetTransferInternal(PSHCLTRANSFERCTX pTransferCtx, uint32_t uIdx);
36static int sharedClipboardConvertFileCreateFlags(bool fWritable, unsigned fShClFlags, RTFMODE fMode,
37 SHCLOBJHANDLE handleInitial, uint64_t *pfOpen);
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 SharedClipboardTransferListHdrDup(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 SharedClipboardTransferObjCtxIsValid(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 */
764inline PSHCLOBJHANDLEINFO sharedClipboardTransferObjectGet(PSHCLTRANSFER pTransfer,
765 SHCLOBJHANDLE hObj)
766{
767 PSHCLOBJHANDLEINFO pIt;
768 RTListForEach(&pTransfer->lstObj, pIt, SHCLOBJHANDLEINFO, Node) /** @todo Slooow ...but works for now. */
769 {
770 if (pIt->hObj == hObj)
771 return pIt;
772 }
773
774 return NULL;
775}
776
777/**
778 * Opens a transfer object.
779 *
780 * @returns VBox status code.
781 * @param pTransfer Clipboard transfer to open the object for.
782 * @param pOpenCreateParms Open / create parameters of transfer object to open / create.
783 * @param phObj Where to store the handle of transfer object opened on success.
784 */
785int ShClTransferObjOpen(PSHCLTRANSFER pTransfer, PSHCLOBJOPENCREATEPARMS pOpenCreateParms,
786 PSHCLOBJHANDLE phObj)
787{
788 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
789 AssertPtrReturn(pOpenCreateParms, VERR_INVALID_POINTER);
790 AssertPtrReturn(phObj, VERR_INVALID_POINTER);
791
792 int rc = VINF_SUCCESS;
793
794 AssertMsgReturn(pTransfer->pszPathRootAbs, ("Transfer has no root path set\n"), VERR_INVALID_PARAMETER);
795 AssertMsgReturn(pOpenCreateParms->pszPath, ("No path in open/create params set\n"), VERR_INVALID_PARAMETER);
796
797 LogFlowFunc(("pszPath=%s, fCreate=0x%x\n", pOpenCreateParms->pszPath, pOpenCreateParms->fCreate));
798
799 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
800 {
801 PSHCLOBJHANDLEINFO pInfo
802 = (PSHCLOBJHANDLEINFO)RTMemAllocZ(sizeof(SHCLOBJHANDLEINFO));
803 if (pInfo)
804 {
805 rc = ShClTransferObjHandleInfoInit(pInfo);
806 if (RT_SUCCESS(rc))
807 {
808 const bool fWritable = true; /** @todo Fix this. */
809
810 uint64_t fOpen;
811 rc = sharedClipboardConvertFileCreateFlags(fWritable,
812 pOpenCreateParms->fCreate, pOpenCreateParms->ObjInfo.Attr.fMode,
813 SHCLOBJHANDLE_INVALID, &fOpen);
814 if (RT_SUCCESS(rc))
815 {
816 pInfo->pszPathLocalAbs = RTPathJoinA(pTransfer->pszPathRootAbs, pOpenCreateParms->pszPath);
817 if (pInfo->pszPathLocalAbs)
818 {
819 rc = RTFileOpen(&pInfo->u.Local.hFile, pInfo->pszPathLocalAbs, fOpen);
820 if (RT_SUCCESS(rc))
821 {
822 LogRel2(("Shared Clipboard: Opened file '%s'\n", pInfo->pszPathLocalAbs));
823 }
824 else
825 LogRel(("Shared Clipboard: Error opening file '%s', rc=%Rrc\n", pInfo->pszPathLocalAbs, rc));
826 }
827 else
828 rc = VERR_NO_MEMORY;
829 }
830 }
831
832 if (RT_SUCCESS(rc))
833 {
834 pInfo->hObj = pTransfer->uObjHandleNext++;
835 pInfo->enmType = SHCLOBJTYPE_FILE;
836
837 RTListAppend(&pTransfer->lstObj, &pInfo->Node);
838
839 *phObj = pInfo->hObj;
840 }
841 else
842 {
843 ShClTransferObjHandleInfoDestroy(pInfo);
844 RTMemFree(pInfo);
845 }
846 }
847 else
848 rc = VERR_NO_MEMORY;
849 }
850 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
851 {
852 if (pTransfer->ProviderIface.pfnObjOpen)
853 {
854 rc = pTransfer->ProviderIface.pfnObjOpen(&pTransfer->ProviderCtx, pOpenCreateParms, phObj);
855 }
856 else
857 rc = VERR_NOT_SUPPORTED;
858 }
859
860 LogFlowFuncLeaveRC(rc);
861 return rc;
862}
863
864/**
865 * Closes a transfer object.
866 *
867 * @returns VBox status code.
868 * @param pTransfer Clipboard transfer that contains the object to close.
869 * @param hObj Handle of transfer object to close.
870 */
871int ShClTransferObjClose(PSHCLTRANSFER pTransfer, SHCLOBJHANDLE hObj)
872{
873 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
874
875 int rc = VINF_SUCCESS;
876
877 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
878 {
879 PSHCLOBJHANDLEINFO pInfo = sharedClipboardTransferObjectGet(pTransfer, hObj);
880 if (pInfo)
881 {
882 switch (pInfo->enmType)
883 {
884 case SHCLOBJTYPE_DIRECTORY:
885 {
886 rc = RTDirClose(pInfo->u.Local.hDir);
887 if (RT_SUCCESS(rc))
888 {
889 pInfo->u.Local.hDir = NIL_RTDIR;
890
891 LogRel2(("Shared Clipboard: Closed directory '%s'\n", pInfo->pszPathLocalAbs));
892 }
893 else
894 LogRel(("Shared Clipboard: Closing directory '%s' failed with %Rrc\n", pInfo->pszPathLocalAbs, rc));
895 break;
896 }
897
898 case SHCLOBJTYPE_FILE:
899 {
900 rc = RTFileClose(pInfo->u.Local.hFile);
901 if (RT_SUCCESS(rc))
902 {
903 pInfo->u.Local.hFile = NIL_RTFILE;
904
905 LogRel2(("Shared Clipboard: Closed file '%s'\n", pInfo->pszPathLocalAbs));
906 }
907 else
908 LogRel(("Shared Clipboard: Closing file '%s' failed with %Rrc\n", pInfo->pszPathLocalAbs, rc));
909 break;
910 }
911
912 default:
913 rc = VERR_NOT_IMPLEMENTED;
914 break;
915 }
916
917 RTListNodeRemove(&pInfo->Node);
918
919 ShClTransferObjHandleInfoDestroy(pInfo);
920
921 RTMemFree(pInfo);
922 pInfo = NULL;
923 }
924 else
925 rc = VERR_NOT_FOUND;
926 }
927 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
928 {
929 if (pTransfer->ProviderIface.pfnObjClose)
930 {
931 rc = pTransfer->ProviderIface.pfnObjClose(&pTransfer->ProviderCtx, hObj);
932 }
933 else
934 rc = VERR_NOT_SUPPORTED;
935 }
936
937 LogFlowFuncLeaveRC(rc);
938 return rc;
939}
940
941/**
942 * Reads from a transfer object.
943 *
944 * @returns VBox status code.
945 * @param pTransfer Clipboard transfer that contains the object to read from.
946 * @param hObj Handle of transfer object to read from.
947 * @param pvBuf Buffer for where to store the read data.
948 * @param cbBuf Size (in bytes) of buffer.
949 * @param pcbRead How much bytes were read on success. Optional.
950 */
951int ShClTransferObjRead(PSHCLTRANSFER pTransfer,
952 SHCLOBJHANDLE hObj, void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead, uint32_t fFlags)
953{
954 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
955 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
956 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
957 /* pcbRead is optional. */
958 /** @todo Validate fFlags. */
959
960 int rc = VINF_SUCCESS;
961
962 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
963 {
964 PSHCLOBJHANDLEINFO pInfo = sharedClipboardTransferObjectGet(pTransfer, hObj);
965 if (pInfo)
966 {
967 switch (pInfo->enmType)
968 {
969 case SHCLOBJTYPE_FILE:
970 {
971 size_t cbRead;
972 rc = RTFileRead(pInfo->u.Local.hFile, pvBuf, cbBuf, &cbRead);
973 if (RT_SUCCESS(rc))
974 {
975 if (pcbRead)
976 *pcbRead = (uint32_t)cbRead;
977 }
978 break;
979 }
980
981 default:
982 rc = VERR_NOT_SUPPORTED;
983 break;
984 }
985 }
986 else
987 rc = VERR_NOT_FOUND;
988 }
989 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
990 {
991 if (pTransfer->ProviderIface.pfnObjRead)
992 {
993 rc = pTransfer->ProviderIface.pfnObjRead(&pTransfer->ProviderCtx, hObj, pvBuf, cbBuf, fFlags, pcbRead);
994 }
995 else
996 rc = VERR_NOT_SUPPORTED;
997 }
998
999 LogFlowFuncLeaveRC(rc);
1000 return rc;
1001}
1002
1003/**
1004 * Writes to a transfer object.
1005 *
1006 * @returns VBox status code.
1007 * @param pTransfer Clipboard transfer that contains the object to write to.
1008 * @param hObj Handle of transfer object to write to.
1009 * @param pvBuf Buffer of data to write.
1010 * @param cbBuf Size (in bytes) of buffer to write.
1011 * @param pcbWritten How much bytes were writtenon success. Optional.
1012 */
1013int ShClTransferObjWrite(PSHCLTRANSFER pTransfer,
1014 SHCLOBJHANDLE hObj, void *pvBuf, uint32_t cbBuf, uint32_t *pcbWritten,
1015 uint32_t fFlags)
1016{
1017 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1018 AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
1019 AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
1020 /* pcbWritten is optional. */
1021
1022 int rc = VINF_SUCCESS;
1023
1024 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1025 {
1026 PSHCLOBJHANDLEINFO pInfo = sharedClipboardTransferObjectGet(pTransfer, hObj);
1027 if (pInfo)
1028 {
1029 switch (pInfo->enmType)
1030 {
1031 case SHCLOBJTYPE_FILE:
1032 {
1033 rc = RTFileWrite(pInfo->u.Local.hFile, pvBuf, cbBuf, (size_t *)pcbWritten);
1034 break;
1035 }
1036
1037 default:
1038 rc = VERR_NOT_SUPPORTED;
1039 break;
1040 }
1041 }
1042 else
1043 rc = VERR_NOT_FOUND;
1044 }
1045 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1046 {
1047 if (pTransfer->ProviderIface.pfnObjWrite)
1048 {
1049 rc = pTransfer->ProviderIface.pfnObjWrite(&pTransfer->ProviderCtx, hObj, pvBuf, cbBuf, fFlags, pcbWritten);
1050 }
1051 else
1052 rc = VERR_NOT_SUPPORTED;
1053 }
1054
1055 LogFlowFuncLeaveRC(rc);
1056 return rc;
1057}
1058
1059/**
1060 * Duplicaates a transfer object data chunk.
1061 *
1062 * @returns Duplicated object data chunk on success, or NULL on failure.
1063 * @param pDataChunk transfer object data chunk to duplicate.
1064 */
1065PSHCLOBJDATACHUNK ShClTransferObjDataChunkDup(PSHCLOBJDATACHUNK pDataChunk)
1066{
1067 if (!pDataChunk)
1068 return NULL;
1069
1070 PSHCLOBJDATACHUNK pDataChunkDup = (PSHCLOBJDATACHUNK)RTMemAllocZ(sizeof(SHCLOBJDATACHUNK));
1071 if (!pDataChunkDup)
1072 return NULL;
1073
1074 if (pDataChunk->pvData)
1075 {
1076 Assert(pDataChunk->cbData);
1077
1078 pDataChunkDup->uHandle = pDataChunk->uHandle;
1079 pDataChunkDup->pvData = RTMemDup(pDataChunk->pvData, pDataChunk->cbData);
1080 pDataChunkDup->cbData = pDataChunk->cbData;
1081 }
1082
1083 return pDataChunkDup;
1084}
1085
1086/**
1087 * Destroys a transfer object data chunk.
1088 *
1089 * @param pDataChunk transfer object data chunk to destroy.
1090 */
1091void ShClTransferObjDataChunkDestroy(PSHCLOBJDATACHUNK pDataChunk)
1092{
1093 if (!pDataChunk)
1094 return;
1095
1096 if (pDataChunk->pvData)
1097 {
1098 Assert(pDataChunk->cbData);
1099
1100 RTMemFree(pDataChunk->pvData);
1101
1102 pDataChunk->pvData = NULL;
1103 pDataChunk->cbData = 0;
1104 }
1105
1106 pDataChunk->uHandle = 0;
1107}
1108
1109/**
1110 * Frees a transfer object data chunk.
1111 *
1112 * @param pDataChunk transfer object data chunk to free. The handed-in pointer will
1113 * be invalid after calling this function.
1114 */
1115void ShClTransferObjDataChunkFree(PSHCLOBJDATACHUNK pDataChunk)
1116{
1117 if (!pDataChunk)
1118 return;
1119
1120 ShClTransferObjDataChunkDestroy(pDataChunk);
1121
1122 RTMemFree(pDataChunk);
1123 pDataChunk = NULL;
1124}
1125
1126/**
1127 * Creates an Clipboard transfer.
1128 *
1129 * @returns VBox status code.
1130 * @param ppTransfer Where to return the created Shared Clipboard transfer struct.
1131 * Must be destroyed by SharedClipboardTransferDestroy().
1132 */
1133int ShClTransferCreate(PSHCLTRANSFER *ppTransfer)
1134{
1135 AssertPtrReturn(ppTransfer, VERR_INVALID_POINTER);
1136
1137 LogFlowFuncEnter();
1138
1139 PSHCLTRANSFER pTransfer = (PSHCLTRANSFER)RTMemAlloc(sizeof(SHCLTRANSFER));
1140 if (!pTransfer)
1141 return VERR_NO_MEMORY;
1142
1143 int rc = VINF_SUCCESS;
1144
1145 pTransfer->State.uID = 0;
1146 pTransfer->State.enmStatus = SHCLTRANSFERSTATUS_NONE;
1147 pTransfer->State.enmDir = SHCLTRANSFERDIR_UNKNOWN;
1148 pTransfer->State.enmSource = SHCLSOURCE_INVALID;
1149
1150 pTransfer->pArea = NULL; /* Will be created later if needed. */
1151
1152 pTransfer->Thread.hThread = NIL_RTTHREAD;
1153 pTransfer->Thread.fCancelled = false;
1154 pTransfer->Thread.fStarted = false;
1155 pTransfer->Thread.fStop = false;
1156
1157 pTransfer->pszPathRootAbs = NULL;
1158
1159 pTransfer->uListHandleNext = 1;
1160 pTransfer->uObjHandleNext = 1;
1161
1162 pTransfer->uTimeoutMs = 30 * 1000; /* 30s timeout by default. */
1163 pTransfer->cbMaxChunkSize = _64K; /** @todo Make this configurable. */
1164
1165 pTransfer->pvUser = NULL;
1166 pTransfer->cbUser = 0;
1167
1168 RT_ZERO(pTransfer->Callbacks);
1169
1170 RTListInit(&pTransfer->lstList);
1171 RTListInit(&pTransfer->lstObj);
1172
1173 pTransfer->cRoots = 0;
1174 RTListInit(&pTransfer->lstRoots);
1175
1176 RT_ZERO(pTransfer->Events);
1177
1178 if (RT_SUCCESS(rc))
1179 {
1180 *ppTransfer = pTransfer;
1181 }
1182 else
1183 {
1184 if (pTransfer)
1185 {
1186 ShClTransferDestroy(pTransfer);
1187 RTMemFree(pTransfer);
1188 }
1189 }
1190
1191 LogFlowFuncLeaveRC(rc);
1192 return rc;
1193}
1194
1195/**
1196 * Destroys an Clipboard transfer context struct.
1197 *
1198 * @returns VBox status code.
1199 * @param pTransferCtx Clipboard transfer to destroy.
1200 */
1201int ShClTransferDestroy(PSHCLTRANSFER pTransfer)
1202{
1203 if (!pTransfer)
1204 return VINF_SUCCESS;
1205
1206 LogFlowFuncEnter();
1207
1208 int rc = sharedClipboardTransferThreadDestroy(pTransfer, 30 * 1000 /* Timeout in ms */);
1209 if (RT_FAILURE(rc))
1210 return rc;
1211
1212 RTStrFree(pTransfer->pszPathRootAbs);
1213
1214 ShClEventSourceDestroy(&pTransfer->Events);
1215
1216 PSHCLLISTHANDLEINFO pItList, pItListNext;
1217 RTListForEachSafe(&pTransfer->lstList, pItList, pItListNext, SHCLLISTHANDLEINFO, Node)
1218 {
1219 ShClTransferListHandleInfoDestroy(pItList);
1220
1221 RTListNodeRemove(&pItList->Node);
1222
1223 RTMemFree(pItList);
1224 }
1225
1226 PSHCLOBJHANDLEINFO pItObj, pItObjNext;
1227 RTListForEachSafe(&pTransfer->lstObj, pItObj, pItObjNext, SHCLOBJHANDLEINFO, Node)
1228 {
1229 ShClTransferObjHandleInfoDestroy(pItObj);
1230
1231 RTListNodeRemove(&pItObj->Node);
1232
1233 RTMemFree(pItObj);
1234 }
1235
1236 LogFlowFuncLeave();
1237 return VINF_SUCCESS;
1238}
1239
1240/**
1241 * Initializes a shared Clipboard transfer object.
1242 *
1243 * @returns VBox status code.
1244 * @param pTransfer Transfer to initialize.
1245 * @param uID ID to use for the transfer. Can be set to 0 if not important.
1246 * @param enmDir Specifies the transfer direction of this transfer.
1247 * @param enmSource Specifies the data source of the transfer.
1248 */
1249int ShClTransferInit(PSHCLTRANSFER pTransfer,
1250 uint32_t uID, SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource)
1251{
1252 pTransfer->State.uID = uID;
1253 pTransfer->State.enmDir = enmDir;
1254 pTransfer->State.enmSource = enmSource;
1255
1256 LogFlowFunc(("uID=%RU32, enmDir=%RU32, enmSource=%RU32\n",
1257 pTransfer->State.uID, pTransfer->State.enmDir, pTransfer->State.enmSource));
1258
1259 int rc = ShClEventSourceCreate(&pTransfer->Events, pTransfer->State.uID);
1260 if (RT_SUCCESS(rc))
1261 {
1262 pTransfer->State.enmStatus = SHCLTRANSFERSTATUS_INITIALIZED; /* Now we're ready to run. */
1263
1264 if (pTransfer->Callbacks.pfnTransferInitialize)
1265 {
1266 SHCLTRANSFERCALLBACKDATA Data = { pTransfer, pTransfer->Callbacks.pvUser, pTransfer->Callbacks.cbUser };
1267 rc = pTransfer->Callbacks.pfnTransferInitialize(&Data);
1268 }
1269 }
1270
1271 LogFlowFuncLeaveRC(rc);
1272 return rc;
1273}
1274
1275int ShClTransferOpen(PSHCLTRANSFER pTransfer)
1276{
1277 int rc = VINF_SUCCESS;
1278
1279 if (pTransfer->ProviderIface.pfnTransferOpen)
1280 rc = pTransfer->ProviderIface.pfnTransferOpen(&pTransfer->ProviderCtx);
1281
1282 LogFlowFuncLeaveRC(rc);
1283 return rc;
1284}
1285
1286int ShClTransferClose(PSHCLTRANSFER pTransfer)
1287{
1288 int rc = VINF_SUCCESS;
1289
1290 if (pTransfer->ProviderIface.pfnTransferClose)
1291 rc = pTransfer->ProviderIface.pfnTransferClose(&pTransfer->ProviderCtx);
1292
1293 LogFlowFuncLeaveRC(rc);
1294 return rc;
1295}
1296
1297/**
1298 * Returns a specific list handle info of a transfer.
1299 *
1300 * @returns Pointer to list handle info if found, or NULL if not found.
1301 * @param pTransfer Clipboard transfer to get list handle info from.
1302 * @param hList List handle of the list to get handle info for.
1303 */
1304inline PSHCLLISTHANDLEINFO sharedClipboardTransferListGetByHandle(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList)
1305{
1306 PSHCLLISTHANDLEINFO pIt;
1307 RTListForEach(&pTransfer->lstList, pIt, SHCLLISTHANDLEINFO, Node) /** @todo Sloooow ... improve this. */
1308 {
1309 if (pIt->hList == hList)
1310 return pIt;
1311 }
1312
1313 return NULL;
1314}
1315
1316/**
1317 * Creates a new list handle (local only).
1318 *
1319 * @returns New List handle on success, or SHCLLISTHANDLE_INVALID on error.
1320 * @param pTransfer Clipboard transfer to create new list handle for.
1321 */
1322inline SHCLLISTHANDLE sharedClipboardTransferListHandleNew(PSHCLTRANSFER pTransfer)
1323{
1324 return pTransfer->uListHandleNext++; /** @todo Good enough for now. Improve this later. */
1325}
1326
1327/**
1328 * Resolves a relative path of a specific transfer to its absolute path.
1329 *
1330 * @returns VBox status code.
1331 * @param pTransfer Clipboard transfer to resolve path for.
1332 * @param pszPath Path to resolve.
1333 * @param fFlags Resolve flags. Currently not used and must be 0.
1334 * @param ppszResolved Where to store the allocated resolved path. Must be free'd by the called using RTStrFree().
1335 */
1336static int shClTransferResolvePathAbs(PSHCLTRANSFER pTransfer, const char *pszPath, uint32_t fFlags,
1337 char **ppszResolved)
1338{
1339 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1340 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1341 AssertReturn (fFlags == 0, VERR_INVALID_PARAMETER);
1342
1343 /* Paranoia. */
1344 if ( !strlen(pTransfer->pszPathRootAbs)
1345 || !RTStrIsValidEncoding(pTransfer->pszPathRootAbs)
1346 || !RTStrCmp(pTransfer->pszPathRootAbs, ".")
1347 || !RTStrCmp(pTransfer->pszPathRootAbs, "..")
1348 || !RTPathStartsWithRoot(pTransfer->pszPathRootAbs))
1349 {
1350 return VERR_INVALID_PARAMETER;
1351 }
1352
1353 int rc;
1354
1355 char *pszResolved = RTPathJoinA(pTransfer->pszPathRootAbs, pszPath);
1356 if (pszResolved)
1357 {
1358 LogFlowFunc(("pszPathRootAbs=%s, pszPath=%s -> %s\n", pTransfer->pszPathRootAbs, pszPath, pszResolved));
1359
1360 *ppszResolved = pszResolved;
1361
1362 rc = VINF_SUCCESS;
1363 }
1364 else
1365 rc = VERR_NO_MEMORY;
1366
1367 LogFlowFuncLeaveRC(rc);
1368 return rc;
1369}
1370
1371/**
1372 * Opens a list.
1373 *
1374 * @returns VBox status code.
1375 * @param pTransfer Clipboard transfer to handle.
1376 * @param pOpenParms List open parameters to use for opening.
1377 * @param phList Where to store the List handle of opened list on success.
1378 */
1379int ShClTransferListOpen(PSHCLTRANSFER pTransfer, PSHCLLISTOPENPARMS pOpenParms,
1380 PSHCLLISTHANDLE phList)
1381{
1382 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1383 AssertPtrReturn(pOpenParms, VERR_INVALID_POINTER);
1384 AssertPtrReturn(phList, VERR_INVALID_POINTER);
1385
1386 int rc;
1387
1388 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1389 {
1390 LogFlowFunc(("pszPath=%s\n", pOpenParms->pszPath));
1391
1392 char *pszPathAbs;
1393 rc = shClTransferResolvePathAbs(pTransfer, pOpenParms->pszPath, 0 /* fFlags */, &pszPathAbs);
1394 if (RT_SUCCESS(rc))
1395 {
1396 PSHCLLISTHANDLEINFO pInfo
1397 = (PSHCLLISTHANDLEINFO)RTMemAlloc(sizeof(SHCLLISTHANDLEINFO));
1398 if (pInfo)
1399 {
1400 rc = ShClTransferListHandleInfoInit(pInfo);
1401 if (RT_SUCCESS(rc))
1402 {
1403 RTFSOBJINFO objInfo;
1404 rc = RTPathQueryInfo(pOpenParms->pszPath, &objInfo, RTFSOBJATTRADD_NOTHING);
1405 if (RT_SUCCESS(rc))
1406 {
1407 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1408 {
1409 rc = RTDirOpen(&pInfo->u.Local.hDir, pszPathAbs);
1410 if (RT_SUCCESS(rc))
1411 {
1412 pInfo->enmType = SHCLOBJTYPE_DIRECTORY;
1413
1414 LogRel2(("Shared Clipboard: Opening directory '%s'\n", pszPathAbs));
1415 }
1416 else
1417 LogRel(("Shared Clipboard: Opening directory '%s' failed with %Rrc\n", pInfo->pszPathLocalAbs, rc));
1418
1419 }
1420 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1421 {
1422 rc = RTFileOpen(&pInfo->u.Local.hFile, pszPathAbs,
1423 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
1424 if (RT_SUCCESS(rc))
1425 {
1426 pInfo->enmType = SHCLOBJTYPE_FILE;
1427
1428 LogRel2(("Shared Clipboard: Opening file '%s'\n", pszPathAbs));
1429 }
1430 else
1431 LogRel(("Shared Clipboard: Opening file '%s' failed with %Rrc\n", pInfo->pszPathLocalAbs, rc));
1432 }
1433 else
1434 rc = VERR_NOT_SUPPORTED;
1435
1436 if (RT_SUCCESS(rc))
1437 {
1438 pInfo->hList = sharedClipboardTransferListHandleNew(pTransfer);
1439 pInfo->pszPathLocalAbs = RTStrDup(pszPathAbs);
1440 if (pInfo->pszPathLocalAbs)
1441 {
1442 RTListAppend(&pTransfer->lstList, &pInfo->Node);
1443
1444 LogFlowFunc(("pszPathLocalAbs=%s, hList=%RU64\n", pInfo->pszPathLocalAbs, pInfo->hList));
1445
1446 *phList = pInfo->hList;
1447 }
1448 else
1449 rc = VERR_NO_MEMORY;
1450 }
1451 else
1452 {
1453 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1454 {
1455 if (RTDirIsValid(pInfo->u.Local.hDir))
1456 RTDirClose(pInfo->u.Local.hDir);
1457 }
1458 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1459 {
1460 if (RTFileIsValid(pInfo->u.Local.hFile))
1461 RTFileClose(pInfo->u.Local.hFile);
1462 }
1463 }
1464 }
1465
1466 if (RT_FAILURE(rc))
1467 ShClTransferListHandleInfoDestroy(pInfo);
1468 }
1469
1470 if (RT_FAILURE(rc))
1471 {
1472 RTMemFree(pInfo);
1473 pInfo = NULL;
1474 }
1475 }
1476 else
1477 rc = VERR_NO_MEMORY;
1478
1479 RTStrFree(pszPathAbs);
1480 }
1481 }
1482 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1483 {
1484 if (pTransfer->ProviderIface.pfnListOpen)
1485 {
1486 rc = pTransfer->ProviderIface.pfnListOpen(&pTransfer->ProviderCtx, pOpenParms, phList);
1487 }
1488 else
1489 rc = VERR_NOT_SUPPORTED;
1490 }
1491 else
1492 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1493
1494 LogFlowFuncLeaveRC(rc);
1495 return rc;
1496}
1497
1498/**
1499 * Closes a list.
1500 *
1501 * @returns VBox status code.
1502 * @param pTransfer Clipboard transfer to handle.
1503 * @param hList Handle of list to close.
1504 */
1505int ShClTransferListClose(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList)
1506{
1507 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1508
1509 if (hList == SHCLLISTHANDLE_INVALID)
1510 return VINF_SUCCESS;
1511
1512 int rc = VINF_SUCCESS;
1513
1514 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1515 {
1516 PSHCLLISTHANDLEINFO pInfo = sharedClipboardTransferListGetByHandle(pTransfer, hList);
1517 if (pInfo)
1518 {
1519 switch (pInfo->enmType)
1520 {
1521 case SHCLOBJTYPE_DIRECTORY:
1522 {
1523 if (RTDirIsValid(pInfo->u.Local.hDir))
1524 {
1525 RTDirClose(pInfo->u.Local.hDir);
1526 pInfo->u.Local.hDir = NIL_RTDIR;
1527 }
1528 break;
1529 }
1530
1531 default:
1532 rc = VERR_NOT_SUPPORTED;
1533 break;
1534 }
1535
1536 RTListNodeRemove(&pInfo->Node);
1537
1538 RTMemFree(pInfo);
1539 }
1540 else
1541 rc = VERR_NOT_FOUND;
1542 }
1543 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1544 {
1545 if (pTransfer->ProviderIface.pfnListClose)
1546 {
1547 rc = pTransfer->ProviderIface.pfnListClose(&pTransfer->ProviderCtx, hList);
1548 }
1549 else
1550 rc = VERR_NOT_SUPPORTED;
1551 }
1552
1553 LogFlowFuncLeaveRC(rc);
1554 return rc;
1555}
1556
1557/**
1558 * Adds a file to a list heaer.
1559 *
1560 * @returns VBox status code.
1561 * @param pHdr List header to add file to.
1562 * @param pszPath Path of file to add.
1563 */
1564static int sharedClipboardTransferListHdrAddFile(PSHCLLISTHDR pHdr, const char *pszPath)
1565{
1566 uint64_t cbSize = 0;
1567 int rc = RTFileQuerySizeByPath(pszPath, &cbSize);
1568 if (RT_SUCCESS(rc))
1569 {
1570 pHdr->cbTotalSize += cbSize;
1571 pHdr->cTotalObjects++;
1572 }
1573
1574 LogFlowFuncLeaveRC(rc);
1575 return rc;
1576}
1577
1578/**
1579 * Builds a list header, internal version.
1580 *
1581 * @returns VBox status code.
1582 * @param pHdr Where to store the build list header.
1583 * @param pcszSrcPath Source path of list.
1584 * @param pcszDstPath Destination path of list.
1585 * @param pcszDstBase Destination base path.
1586 * @param cchDstBase Number of charaters of destination base path.
1587 */
1588static int sharedClipboardTransferListHdrFromDir(PSHCLLISTHDR pHdr, const char *pcszPathAbs)
1589{
1590 AssertPtrReturn(pcszPathAbs, VERR_INVALID_POINTER);
1591
1592 LogFlowFunc(("pcszPathAbs=%s\n", pcszPathAbs));
1593
1594 RTFSOBJINFO objInfo;
1595 int rc = RTPathQueryInfo(pcszPathAbs, &objInfo, RTFSOBJATTRADD_NOTHING);
1596 if (RT_SUCCESS(rc))
1597 {
1598 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1599 {
1600 RTDIR hDir;
1601 rc = RTDirOpen(&hDir, pcszPathAbs);
1602 if (RT_SUCCESS(rc))
1603 {
1604 size_t cbDirEntry = 0;
1605 PRTDIRENTRYEX pDirEntry = NULL;
1606 do
1607 {
1608 /* Retrieve the next directory entry. */
1609 rc = RTDirReadExA(hDir, &pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1610 if (RT_FAILURE(rc))
1611 {
1612 if (rc == VERR_NO_MORE_FILES)
1613 rc = VINF_SUCCESS;
1614 break;
1615 }
1616
1617 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
1618 {
1619 case RTFS_TYPE_DIRECTORY:
1620 {
1621 /* Skip "." and ".." entries. */
1622 if (RTDirEntryExIsStdDotLink(pDirEntry))
1623 break;
1624
1625 pHdr->cTotalObjects++;
1626 break;
1627 }
1628 case RTFS_TYPE_FILE:
1629 {
1630 char *pszSrc = RTPathJoinA(pcszPathAbs, pDirEntry->szName);
1631 if (pszSrc)
1632 {
1633 rc = sharedClipboardTransferListHdrAddFile(pHdr, pszSrc);
1634 RTStrFree(pszSrc);
1635 }
1636 else
1637 rc = VERR_NO_MEMORY;
1638 break;
1639 }
1640 case RTFS_TYPE_SYMLINK:
1641 {
1642 /** @todo Not implemented yet. */
1643 }
1644
1645 default:
1646 break;
1647 }
1648
1649 } while (RT_SUCCESS(rc));
1650
1651 RTDirReadExAFree(&pDirEntry, &cbDirEntry);
1652 RTDirClose(hDir);
1653 }
1654 }
1655 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1656 {
1657 rc = sharedClipboardTransferListHdrAddFile(pHdr, pcszPathAbs);
1658 }
1659 else if (RTFS_IS_SYMLINK(objInfo.Attr.fMode))
1660 {
1661 /** @todo Not implemented yet. */
1662 }
1663 else
1664 rc = VERR_NOT_SUPPORTED;
1665 }
1666
1667 LogFlowFuncLeaveRC(rc);
1668 return rc;
1669}
1670
1671/**
1672 * Retrieves the header of a Shared Clipboard list.
1673 *
1674 * @returns VBox status code.
1675 * @param pTransfer Clipboard transfer to handle.
1676 * @param hList Handle of list to get header for.
1677 * @param pHdr Where to store the returned list header information.
1678 */
1679int ShClTransferListGetHeader(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList,
1680 PSHCLLISTHDR pHdr)
1681{
1682 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1683 AssertPtrReturn(pHdr, VERR_INVALID_POINTER);
1684
1685 int rc;
1686
1687 LogFlowFunc(("hList=%RU64\n", hList));
1688
1689 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1690 {
1691 PSHCLLISTHANDLEINFO pInfo = sharedClipboardTransferListGetByHandle(pTransfer, hList);
1692 if (pInfo)
1693 {
1694 rc = ShClTransferListHdrInit(pHdr);
1695 if (RT_SUCCESS(rc))
1696 {
1697 switch (pInfo->enmType)
1698 {
1699 case SHCLOBJTYPE_DIRECTORY:
1700 {
1701 LogFlowFunc(("DirAbs: %s\n", pInfo->pszPathLocalAbs));
1702
1703 rc = sharedClipboardTransferListHdrFromDir(pHdr, pInfo->pszPathLocalAbs);
1704 break;
1705 }
1706
1707 case SHCLOBJTYPE_FILE:
1708 {
1709 LogFlowFunc(("FileAbs: %s\n", pInfo->pszPathLocalAbs));
1710
1711 pHdr->cTotalObjects = 1;
1712
1713 RTFSOBJINFO objInfo;
1714 rc = RTFileQueryInfo(pInfo->u.Local.hFile, &objInfo, RTFSOBJATTRADD_NOTHING);
1715 if (RT_SUCCESS(rc))
1716 {
1717 pHdr->cbTotalSize = objInfo.cbObject;
1718 }
1719 break;
1720 }
1721
1722 default:
1723 rc = VERR_NOT_SUPPORTED;
1724 break;
1725 }
1726 }
1727
1728 LogFlowFunc(("cTotalObj=%RU64, cbTotalSize=%RU64\n", pHdr->cTotalObjects, pHdr->cbTotalSize));
1729 }
1730 else
1731 rc = VERR_NOT_FOUND;
1732 }
1733 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1734 {
1735 if (pTransfer->ProviderIface.pfnListHdrRead)
1736 {
1737 rc = pTransfer->ProviderIface.pfnListHdrRead(&pTransfer->ProviderCtx, hList, pHdr);
1738 }
1739 else
1740 rc = VERR_NOT_SUPPORTED;
1741 }
1742 else
1743 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1744
1745 LogFlowFuncLeaveRC(rc);
1746 return rc;
1747}
1748
1749/**
1750 * Returns the current transfer object for a Shared Clipboard transfer list.
1751 *
1752 * Currently not implemented and wil return NULL.
1753 *
1754 * @returns Pointer to transfer object, or NULL if not found / invalid.
1755 * @param pTransfer Clipboard transfer to return transfer object for.
1756 * @param hList Handle of Shared Clipboard transfer list to get object for.
1757 * @param uIdx Index of object to get.
1758 */
1759PSHCLTRANSFEROBJ ShClTransferListGetObj(PSHCLTRANSFER pTransfer,
1760 SHCLLISTHANDLE hList, uint64_t uIdx)
1761{
1762 AssertPtrReturn(pTransfer, NULL);
1763
1764 RT_NOREF(hList, uIdx);
1765
1766 LogFlowFunc(("hList=%RU64\n", hList));
1767
1768 return NULL;
1769}
1770
1771/**
1772 * Reads a single Shared Clipboard list entry.
1773 *
1774 * @returns VBox status code or VERR_NO_MORE_FILES if the end of the list has been reached.
1775 * @param pTransfer Clipboard transfer to handle.
1776 * @param hList List handle of list to read from.
1777 * @param pEntry Where to store the read information.
1778 */
1779int ShClTransferListRead(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList,
1780 PSHCLLISTENTRY pEntry)
1781{
1782 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1783 AssertPtrReturn(pEntry, VERR_INVALID_POINTER);
1784
1785 int rc = VINF_SUCCESS;
1786
1787 LogFlowFunc(("hList=%RU64\n", hList));
1788
1789 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1790 {
1791 PSHCLLISTHANDLEINFO pInfo = sharedClipboardTransferListGetByHandle(pTransfer, hList);
1792 if (pInfo)
1793 {
1794 switch (pInfo->enmType)
1795 {
1796 case SHCLOBJTYPE_DIRECTORY:
1797 {
1798 LogFlowFunc(("\tDirectory: %s\n", pInfo->pszPathLocalAbs));
1799
1800 for (;;)
1801 {
1802 bool fSkipEntry = false; /* Whether to skip an entry in the enumeration. */
1803
1804 size_t cbDirEntry = 0;
1805 PRTDIRENTRYEX pDirEntry = NULL;
1806 rc = RTDirReadExA(pInfo->u.Local.hDir, &pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1807 if (RT_SUCCESS(rc))
1808 {
1809 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
1810 {
1811 case RTFS_TYPE_DIRECTORY:
1812 {
1813 /* Skip "." and ".." entries. */
1814 if (RTDirEntryExIsStdDotLink(pDirEntry))
1815 {
1816 fSkipEntry = true;
1817 break;
1818 }
1819
1820 LogFlowFunc(("Directory: %s\n", pDirEntry->szName));
1821 break;
1822 }
1823
1824 case RTFS_TYPE_FILE:
1825 {
1826 LogFlowFunc(("File: %s\n", pDirEntry->szName));
1827 break;
1828 }
1829
1830 case RTFS_TYPE_SYMLINK:
1831 {
1832 rc = VERR_NOT_IMPLEMENTED; /** @todo Not implemented yet. */
1833 break;
1834 }
1835
1836 default:
1837 break;
1838 }
1839
1840 if ( RT_SUCCESS(rc)
1841 && !fSkipEntry)
1842 {
1843 rc = RTStrCopy(pEntry->pszName, pEntry->cbName, pDirEntry->szName);
1844 if (RT_SUCCESS(rc))
1845 {
1846 AssertPtr(pEntry->pvInfo);
1847 Assert (pEntry->cbInfo == sizeof(SHCLFSOBJINFO));
1848
1849 ShClFsObjFromIPRT(PSHCLFSOBJINFO(pEntry->pvInfo), &pDirEntry->Info);
1850 }
1851 }
1852
1853 RTDirReadExAFree(&pDirEntry, &cbDirEntry);
1854 }
1855
1856 if ( !fSkipEntry /* Do we have a valid entry? Bail out. */
1857 || RT_FAILURE(rc))
1858 {
1859 break;
1860 }
1861 }
1862
1863 break;
1864 }
1865
1866 case SHCLOBJTYPE_FILE:
1867 {
1868 LogFlowFunc(("\tSingle file: %s\n", pInfo->pszPathLocalAbs));
1869
1870 RTFSOBJINFO objInfo;
1871 rc = RTFileQueryInfo(pInfo->u.Local.hFile, &objInfo, RTFSOBJATTRADD_NOTHING);
1872 if (RT_SUCCESS(rc))
1873 {
1874 pEntry->pvInfo = (PSHCLFSOBJINFO)RTMemAlloc(sizeof(SHCLFSOBJINFO));
1875 if (pEntry->pvInfo)
1876 {
1877 rc = RTStrCopy(pEntry->pszName, pEntry->cbName, pInfo->pszPathLocalAbs);
1878 if (RT_SUCCESS(rc))
1879 {
1880 ShClFsObjFromIPRT(PSHCLFSOBJINFO(pEntry->pvInfo), &objInfo);
1881
1882 pEntry->cbInfo = sizeof(SHCLFSOBJINFO);
1883 pEntry->fInfo = VBOX_SHCL_INFO_FLAG_FSOBJINFO;
1884 }
1885 }
1886 else
1887 rc = VERR_NO_MEMORY;
1888 }
1889
1890 break;
1891 }
1892
1893 default:
1894 rc = VERR_NOT_SUPPORTED;
1895 break;
1896 }
1897 }
1898 else
1899 rc = VERR_NOT_FOUND;
1900 }
1901 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1902 {
1903 if (pTransfer->ProviderIface.pfnListEntryRead)
1904 rc = pTransfer->ProviderIface.pfnListEntryRead(&pTransfer->ProviderCtx, hList, pEntry);
1905 else
1906 rc = VERR_NOT_SUPPORTED;
1907 }
1908
1909 LogFlowFuncLeaveRC(rc);
1910 return rc;
1911}
1912
1913int ShClTransferListWrite(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList,
1914 PSHCLLISTENTRY pEntry)
1915{
1916 RT_NOREF(pTransfer, hList, pEntry);
1917
1918 int rc = VINF_SUCCESS;
1919
1920#if 0
1921 if (pTransfer->ProviderIface.pfnListEntryWrite)
1922 rc = pTransfer->ProviderIface.pfnListEntryWrite(&pTransfer->ProviderCtx, hList, pEntry);
1923#endif
1924
1925 LogFlowFuncLeaveRC(rc);
1926 return rc;
1927}
1928
1929/**
1930 * Returns whether a given list handle is valid or not.
1931 *
1932 * @returns \c true if list handle is valid, \c false if not.
1933 * @param pTransfer Clipboard transfer to handle.
1934 * @param hList List handle to check.
1935 */
1936bool ShClTransferListHandleIsValid(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList)
1937{
1938 bool fIsValid = false;
1939
1940 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1941 {
1942 fIsValid = sharedClipboardTransferListGetByHandle(pTransfer, hList) != NULL;
1943 }
1944 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1945 {
1946 AssertFailed(); /** @todo Implement. */
1947 }
1948
1949 return fIsValid;
1950}
1951
1952/**
1953 * Sets the transfer provider interface for a given transfer.
1954 *
1955 * @returns VBox status code.
1956 * @param pTransfer Transfer to create transfer provider for.
1957 * @param pCreationCtx Provider creation context to use for provider creation.
1958 */
1959int ShClTransferSetInterface(PSHCLTRANSFER pTransfer,
1960 PSHCLPROVIDERCREATIONCTX pCreationCtx)
1961{
1962 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1963 AssertPtrReturn(pCreationCtx, VERR_INVALID_POINTER);
1964
1965 LogFlowFuncEnter();
1966
1967 int rc = VINF_SUCCESS;
1968
1969 pTransfer->ProviderIface = pCreationCtx->Interface;
1970
1971#ifdef DEBUG
1972# define LOG_IFACE_PTR(a_Name) \
1973 LogFlowFunc(( #a_Name "=%p\n", pTransfer->ProviderIface.a_Name));
1974
1975 LOG_IFACE_PTR(pfnTransferOpen);
1976 LOG_IFACE_PTR(pfnTransferClose);
1977 LOG_IFACE_PTR(pfnRootsGet);
1978 LOG_IFACE_PTR(pfnListOpen);
1979 LOG_IFACE_PTR(pfnListClose);
1980
1981# undef LOG_IFACE_PTR
1982#endif
1983
1984 pTransfer->ProviderCtx.pTransfer = pTransfer;
1985 pTransfer->ProviderCtx.pvUser = pCreationCtx->pvUser;
1986
1987 LogFlowFuncLeaveRC(rc);
1988 return rc;
1989}
1990
1991/**
1992 * Clears (resets) the root list of a shared Clipboard transfer.
1993 *
1994 * @param pTransfer Transfer to clear transfer root list for.
1995 */
1996static void sharedClipboardTransferListRootsClear(PSHCLTRANSFER pTransfer)
1997{
1998 AssertPtrReturnVoid(pTransfer);
1999
2000 if (pTransfer->pszPathRootAbs)
2001 {
2002 RTStrFree(pTransfer->pszPathRootAbs);
2003 pTransfer->pszPathRootAbs = NULL;
2004 }
2005
2006 PSHCLLISTROOT pListRoot, pListRootNext;
2007 RTListForEachSafe(&pTransfer->lstRoots, pListRoot, pListRootNext, SHCLLISTROOT, Node)
2008 {
2009 RTStrFree(pListRoot->pszPathAbs);
2010
2011 RTListNodeRemove(&pListRoot->Node);
2012
2013 RTMemFree(pListRoot);
2014 pListRoot = NULL;
2015 }
2016
2017 pTransfer->cRoots = 0;
2018}
2019
2020/**
2021 * Resets a shared Clipboard transfer.
2022 *
2023 * @param pTransfer Clipboard transfer to reset.
2024 */
2025void ShClTransferReset(PSHCLTRANSFER pTransfer)
2026{
2027 AssertPtrReturnVoid(pTransfer);
2028
2029 LogFlowFuncEnter();
2030
2031 sharedClipboardTransferListRootsClear(pTransfer);
2032}
2033
2034/**
2035 * Returns the clipboard area for a Shared Clipboard transfer.
2036 *
2037 * @returns Current clipboard area, or NULL if none.
2038 * @param pTransfer Clipboard transfer to return clipboard area for.
2039 */
2040SharedClipboardArea *ShClTransferGetArea(PSHCLTRANSFER pTransfer)
2041{
2042 AssertPtrReturn(pTransfer, NULL);
2043
2044 return pTransfer->pArea;
2045}
2046
2047/**
2048 * Returns the number of transfer root list entries.
2049 *
2050 * @returns Root list entry count.
2051 * @param pTransfer Clipboard transfer to return root entry count for.
2052 */
2053uint32_t ShClTransferRootsCount(PSHCLTRANSFER pTransfer)
2054{
2055 AssertPtrReturn(pTransfer, 0);
2056
2057 return (uint32_t)pTransfer->cRoots;
2058}
2059
2060/**
2061 * Returns a specific root list entry of a transfer.
2062 *
2063 * @returns Pointer to root list entry if found, or NULL if not found.
2064 * @param pTransfer Clipboard transfer to get root list entry from.
2065 * @param uIdx Index of root list entry to return.
2066 */
2067inline PSHCLLISTROOT sharedClipboardTransferRootsGetInternal(PSHCLTRANSFER pTransfer, uint32_t uIdx)
2068{
2069 if (uIdx >= pTransfer->cRoots)
2070 return NULL;
2071
2072 PSHCLLISTROOT pIt = RTListGetFirst(&pTransfer->lstRoots, SHCLLISTROOT, Node);
2073 while (uIdx--) /** @todo Slow, but works for now. */
2074 pIt = RTListGetNext(&pTransfer->lstRoots, pIt, SHCLLISTROOT, Node);
2075
2076 return pIt;
2077}
2078
2079/**
2080 * Get a specific root list entry.
2081 *
2082 * @returns VBox status code.
2083 * @param pTransfer Clipboard transfer to get root list entry of.
2084 * @param uIndex Index (zero-based) of entry to get.
2085 * @param pEntry Where to store the returned entry on success.
2086 */
2087int ShClTransferRootsEntry(PSHCLTRANSFER pTransfer,
2088 uint64_t uIndex, PSHCLROOTLISTENTRY pEntry)
2089{
2090 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2091 AssertPtrReturn(pEntry, VERR_INVALID_POINTER);
2092
2093 if (uIndex >= pTransfer->cRoots)
2094 return VERR_INVALID_PARAMETER;
2095
2096 int rc;
2097
2098 PSHCLLISTROOT pRoot = sharedClipboardTransferRootsGetInternal(pTransfer, uIndex);
2099 AssertPtrReturn(pRoot, VERR_INVALID_PARAMETER);
2100
2101 /* Make sure that we only advertise relative source paths, not absolute ones. */
2102 const char *pcszSrcPath = pRoot->pszPathAbs;
2103
2104 char *pszFileName = RTPathFilename(pcszSrcPath);
2105 if (pszFileName)
2106 {
2107 Assert(pszFileName >= pcszSrcPath);
2108 size_t cchDstBase = pszFileName - pcszSrcPath;
2109 const char *pszDstPath = &pcszSrcPath[cchDstBase];
2110
2111 LogFlowFunc(("pcszSrcPath=%s, pszDstPath=%s\n", pcszSrcPath, pszDstPath));
2112
2113 rc = ShClTransferListEntryInit(pEntry);
2114 if (RT_SUCCESS(rc))
2115 {
2116 rc = RTStrCopy(pEntry->pszName, pEntry->cbName, pszDstPath);
2117 if (RT_SUCCESS(rc))
2118 {
2119 pEntry->cbInfo = sizeof(SHCLFSOBJINFO);
2120 pEntry->pvInfo = (PSHCLFSOBJINFO)RTMemAlloc(pEntry->cbInfo);
2121 if (pEntry->pvInfo)
2122 {
2123 RTFSOBJINFO fsObjInfo;
2124 rc = RTPathQueryInfo(pcszSrcPath, &fsObjInfo, RTFSOBJATTRADD_NOTHING);
2125 if (RT_SUCCESS(rc))
2126 {
2127 ShClFsObjFromIPRT(PSHCLFSOBJINFO(pEntry->pvInfo), &fsObjInfo);
2128
2129 pEntry->fInfo = VBOX_SHCL_INFO_FLAG_FSOBJINFO;
2130 }
2131 }
2132 else
2133 rc = VERR_NO_MEMORY;
2134 }
2135 }
2136 }
2137 else
2138 rc = VERR_INVALID_POINTER;
2139
2140 LogFlowFuncLeaveRC(rc);
2141 return rc;
2142}
2143
2144/**
2145 * Returns the root entries of a shared Clipboard transfer.
2146 *
2147 * @returns VBox status code.
2148 * @param pTransfer Clipboard transfer to return root entries for.
2149 * @param ppRootList Where to store the root list on success.
2150 */
2151int ShClTransferRootsGet(PSHCLTRANSFER pTransfer, PSHCLROOTLIST *ppRootList)
2152{
2153 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2154 AssertPtrReturn(ppRootList, VERR_INVALID_POINTER);
2155
2156 LogFlowFuncEnter();
2157
2158 int rc = VINF_SUCCESS;
2159
2160 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
2161 {
2162 PSHCLROOTLIST pRootList = ShClTransferRootListAlloc();
2163 if (!pRootList)
2164 return VERR_NO_MEMORY;
2165
2166 const uint64_t cRoots = (uint32_t)pTransfer->cRoots;
2167
2168 LogFlowFunc(("cRoots=%RU64\n", cRoots));
2169
2170 if (cRoots)
2171 {
2172 PSHCLROOTLISTENTRY paRootListEntries
2173 = (PSHCLROOTLISTENTRY)RTMemAllocZ(cRoots * sizeof(SHCLROOTLISTENTRY));
2174 if (paRootListEntries)
2175 {
2176 for (uint64_t i = 0; i < cRoots; ++i)
2177 {
2178 rc = ShClTransferRootsEntry(pTransfer, i, &paRootListEntries[i]);
2179 if (RT_FAILURE(rc))
2180 break;
2181 }
2182
2183 if (RT_SUCCESS(rc))
2184 pRootList->paEntries = paRootListEntries;
2185 }
2186 else
2187 rc = VERR_NO_MEMORY;
2188 }
2189 else
2190 rc = VERR_NOT_FOUND;
2191
2192 if (RT_SUCCESS(rc))
2193 {
2194 pRootList->Hdr.cRoots = cRoots;
2195 pRootList->Hdr.fRoots = 0; /** @todo Implement this. */
2196
2197 *ppRootList = pRootList;
2198 }
2199 }
2200 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
2201 {
2202 if (pTransfer->ProviderIface.pfnRootsGet)
2203 rc = pTransfer->ProviderIface.pfnRootsGet(&pTransfer->ProviderCtx, ppRootList);
2204 else
2205 rc = VERR_NOT_SUPPORTED;
2206 }
2207
2208 LogFlowFuncLeaveRC(rc);
2209 return rc;
2210}
2211
2212
2213/**
2214 * Sets transfer root list entries for a given transfer.
2215 *
2216 * @returns VBox status code.
2217 * @param pTransfer Transfer to set transfer list entries for.
2218 * @param pszRoots String list (separated by CRLF) of root entries to set.
2219 * All entries must have the same root path.
2220 * @param cbRoots Size (in bytes) of string list.
2221 */
2222int ShClTransferRootsSet(PSHCLTRANSFER pTransfer, const char *pszRoots, size_t cbRoots)
2223{
2224 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2225 AssertPtrReturn(pszRoots, VERR_INVALID_POINTER);
2226 AssertReturn(cbRoots, VERR_INVALID_PARAMETER);
2227
2228 if (!RTStrIsValidEncoding(pszRoots))
2229 return VERR_INVALID_UTF8_ENCODING;
2230
2231 int rc = VINF_SUCCESS;
2232
2233 sharedClipboardTransferListRootsClear(pTransfer);
2234
2235 char *pszPathRootAbs = NULL;
2236
2237 RTCList<RTCString> lstRootEntries = RTCString(pszRoots, cbRoots - 1).split("\r\n");
2238 for (size_t i = 0; i < lstRootEntries.size(); ++i)
2239 {
2240 PSHCLLISTROOT pListRoot = (PSHCLLISTROOT)RTMemAlloc(sizeof(SHCLLISTROOT));
2241 AssertPtrBreakStmt(pListRoot, rc = VERR_NO_MEMORY);
2242
2243 pListRoot->pszPathAbs = RTStrDup(lstRootEntries.at(i).c_str());
2244 AssertPtrBreakStmt(pListRoot->pszPathAbs, rc = VERR_NO_MEMORY);
2245
2246 if (!pszPathRootAbs)
2247 {
2248 pszPathRootAbs = RTStrDup(pListRoot->pszPathAbs);
2249 if (pszPathRootAbs)
2250 {
2251 RTPathStripFilename(pszPathRootAbs);
2252 LogFlowFunc(("pszPathRootAbs=%s\n", pszPathRootAbs));
2253 }
2254 else
2255 rc = VERR_NO_MEMORY;
2256 }
2257
2258 if (RT_FAILURE(rc))
2259 break;
2260
2261 /* Make sure all entries have the same root path. */
2262 if (!RTStrStartsWith(pListRoot->pszPathAbs, pszPathRootAbs))
2263 {
2264 rc = VERR_INVALID_PARAMETER;
2265 break;
2266 }
2267
2268 RTListAppend(&pTransfer->lstRoots, &pListRoot->Node);
2269
2270 pTransfer->cRoots++;
2271 }
2272
2273 /** @todo Entry rollback on failure? */
2274
2275 if (RT_SUCCESS(rc))
2276 {
2277 pTransfer->pszPathRootAbs = pszPathRootAbs;
2278 LogFlowFunc(("pszPathRootAbs=%s, cRoots=%zu\n", pTransfer->pszPathRootAbs, pTransfer->cRoots));
2279 }
2280
2281 LogFlowFuncLeaveRC(rc);
2282 return rc;
2283}
2284
2285/**
2286 * Returns the transfer's ID.
2287 *
2288 * @returns The transfer's ID.
2289 * @param pTransfer Clipboard transfer to return ID for.
2290 */
2291SHCLTRANSFERID ShClTransferGetID(PSHCLTRANSFER pTransfer)
2292{
2293 AssertPtrReturn(pTransfer, 0);
2294
2295 return pTransfer->State.uID;
2296}
2297
2298/**
2299 * Returns the transfer's direction.
2300 *
2301 * @returns The transfer's direction.
2302 * @param pTransfer Clipboard transfer to return direction for.
2303 */
2304SHCLTRANSFERDIR ShClTransferGetDir(PSHCLTRANSFER pTransfer)
2305{
2306 AssertPtrReturn(pTransfer, SHCLTRANSFERDIR_UNKNOWN);
2307
2308 return pTransfer->State.enmDir;
2309}
2310
2311/**
2312 * Returns the transfer's source.
2313 *
2314 * @returns The transfer's source.
2315 * @param pTransfer Clipboard transfer to return source for.
2316 */
2317SHCLSOURCE ShClTransferGetSource(PSHCLTRANSFER pTransfer)
2318{
2319 AssertPtrReturn(pTransfer, SHCLSOURCE_INVALID);
2320
2321 return pTransfer->State.enmSource;
2322}
2323
2324/**
2325 * Returns the current transfer status.
2326 *
2327 * @returns Current transfer status.
2328 * @param pTransfer Clipboard transfer to return status for.
2329 */
2330SHCLTRANSFERSTATUS ShClTransferGetStatus(PSHCLTRANSFER pTransfer)
2331{
2332 AssertPtrReturn(pTransfer, SHCLTRANSFERSTATUS_NONE);
2333
2334 return pTransfer->State.enmStatus;
2335}
2336
2337/**
2338 * Runs a started Shared Clipboard transfer in a dedicated thread.
2339 *
2340 * @returns VBox status code.
2341 * @param pTransfer Clipboard transfer to run.
2342 * @param pfnThreadFunc Pointer to thread function to use.
2343 * @param pvUser Pointer to user-provided data. Optional.
2344 */
2345int ShClTransferRun(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser)
2346{
2347 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2348 AssertPtrReturn(pfnThreadFunc, VERR_INVALID_POINTER);
2349 /* pvUser is optional. */
2350
2351 AssertMsgReturn(pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_STARTED,
2352 ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
2353 VERR_WRONG_ORDER);
2354
2355 int rc = sharedClipboardTransferThreadCreate(pTransfer, pfnThreadFunc, pvUser);
2356
2357 LogFlowFuncLeaveRC(rc);
2358 return rc;
2359}
2360
2361/**
2362 * Starts an initialized transfer.
2363 *
2364 * @returns VBox status code.
2365 * @param pTransfer Clipboard transfer to start.
2366 */
2367int ShClTransferStart(PSHCLTRANSFER pTransfer)
2368{
2369 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2370
2371 LogFlowFuncEnter();
2372
2373 /* Ready to start? */
2374 AssertMsgReturn(pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_INITIALIZED,
2375 ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
2376 VERR_WRONG_ORDER);
2377
2378 int rc;
2379
2380 if (pTransfer->Callbacks.pfnTransferStart)
2381 {
2382 SHCLTRANSFERCALLBACKDATA Data = { pTransfer, pTransfer->Callbacks.pvUser, pTransfer->Callbacks.cbUser };
2383 rc = pTransfer->Callbacks.pfnTransferStart(&Data);
2384 }
2385 else
2386 rc = VINF_SUCCESS;
2387
2388 if (RT_SUCCESS(rc))
2389 {
2390 pTransfer->State.enmStatus = SHCLTRANSFERSTATUS_STARTED;
2391 }
2392
2393 LogFlowFuncLeaveRC(rc);
2394 return rc;
2395}
2396
2397/**
2398 * Sets or unsets the callback table to be used for a Shared Clipboard transfer.
2399 *
2400 * @returns VBox status code.
2401 * @param pTransfer Clipboard transfer to set callbacks for.
2402 * @param pCallbacks Pointer to callback table to set.
2403 */
2404void ShClTransferSetCallbacks(PSHCLTRANSFER pTransfer,
2405 PSHCLTRANSFERCALLBACKS pCallbacks)
2406{
2407 AssertPtrReturnVoid(pTransfer);
2408 AssertPtrReturnVoid(pCallbacks);
2409
2410 LogFlowFunc(("pCallbacks=%p\n", pCallbacks));
2411
2412#define SET_CALLBACK(a_pfnCallback) \
2413 if (pCallbacks->a_pfnCallback) \
2414 pTransfer->Callbacks.a_pfnCallback = pCallbacks->a_pfnCallback
2415
2416 SET_CALLBACK(pfnTransferInitialize);
2417 SET_CALLBACK(pfnTransferStart);
2418 SET_CALLBACK(pfnListHeaderComplete);
2419 SET_CALLBACK(pfnListEntryComplete);
2420 SET_CALLBACK(pfnTransferCanceled);
2421 SET_CALLBACK(pfnTransferError);
2422
2423#undef SET_CALLBACK
2424
2425 pTransfer->Callbacks.pvUser = pCallbacks->pvUser;
2426 pTransfer->Callbacks.cbUser = pCallbacks->cbUser;
2427}
2428
2429/**
2430 * Creates a thread for a Shared Clipboard transfer.
2431 *
2432 * @returns VBox status code.
2433 * @param pTransfer Clipboard transfer to create thread for.
2434 * @param pfnThreadFunc Thread function to use for this transfer.
2435 * @param pvUser Pointer to user-provided data.
2436 */
2437static int sharedClipboardTransferThreadCreate(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser)
2438
2439{
2440 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2441
2442 /* Already marked for stopping? */
2443 AssertMsgReturn(pTransfer->Thread.fStop == false,
2444 ("Thransfer thread already marked for stopping"), VERR_WRONG_ORDER);
2445 /* Already started? */
2446 AssertMsgReturn(pTransfer->Thread.fStarted == false,
2447 ("Thransfer thread already started"), VERR_WRONG_ORDER);
2448
2449 /* Spawn a worker thread, so that we don't block the window thread for too long. */
2450 int rc = RTThreadCreate(&pTransfer->Thread.hThread, pfnThreadFunc,
2451 pvUser, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
2452 "shclp");
2453 if (RT_SUCCESS(rc))
2454 {
2455 int rc2 = RTThreadUserWait(pTransfer->Thread.hThread, 30 * 1000 /* Timeout in ms */);
2456 AssertRC(rc2);
2457
2458 if (pTransfer->Thread.fStarted) /* Did the thread indicate that it started correctly? */
2459 {
2460 /* Nothing to do in here. */
2461 }
2462 else
2463 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
2464 }
2465
2466 LogFlowFuncLeaveRC(rc);
2467 return rc;
2468}
2469
2470/**
2471 * Destroys a thread of a Shared Clipboard transfer.
2472 *
2473 * @returns VBox status code.
2474 * @param pTransfer Clipboard transfer to destroy thread for.
2475 * @param uTimeoutMs Timeout (in ms) to wait for thread creation.
2476 */
2477static int sharedClipboardTransferThreadDestroy(PSHCLTRANSFER pTransfer, RTMSINTERVAL uTimeoutMs)
2478{
2479 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2480
2481 if (pTransfer->Thread.hThread == NIL_RTTHREAD)
2482 return VINF_SUCCESS;
2483
2484 LogFlowFuncEnter();
2485
2486 /* Set stop indicator. */
2487 pTransfer->Thread.fStop = true;
2488
2489 int rcThread = VERR_WRONG_ORDER;
2490 int rc = RTThreadWait(pTransfer->Thread.hThread, uTimeoutMs, &rcThread);
2491
2492 LogFlowFunc(("Waiting for thread resulted in %Rrc (thread exited with %Rrc)\n", rc, rcThread));
2493
2494 return rc;
2495}
2496
2497/**
2498 * Initializes a Shared Clipboard transfer context.
2499 *
2500 * @returns VBox status code.
2501 * @param pTransferCtx Transfer context to initialize.
2502 */
2503int ShClTransferCtxInit(PSHCLTRANSFERCTX pTransferCtx)
2504{
2505 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2506
2507 LogFlowFunc(("pTransferCtx=%p\n", pTransferCtx));
2508
2509 int rc = RTCritSectInit(&pTransferCtx->CritSect);
2510 if (RT_SUCCESS(rc))
2511 {
2512 RTListInit(&pTransferCtx->List);
2513
2514 pTransferCtx->cTransfers = 0;
2515 pTransferCtx->cRunning = 0;
2516 pTransferCtx->cMaxRunning = UINT16_MAX; /** @todo Make this configurable? */
2517
2518 RT_ZERO(pTransferCtx->bmTransferIds);
2519
2520 ShClTransferCtxReset(pTransferCtx);
2521 }
2522
2523 return VINF_SUCCESS;
2524}
2525
2526/**
2527 * Destroys a shared Clipboard transfer context struct.
2528 *
2529 * @param pTransferCtx Transfer context to destroy.
2530 */
2531void ShClTransferCtxDestroy(PSHCLTRANSFERCTX pTransferCtx)
2532{
2533 AssertPtrReturnVoid(pTransferCtx);
2534
2535 LogFlowFunc(("pTransferCtx=%p\n", pTransferCtx));
2536
2537 if (RTCritSectIsInitialized(&pTransferCtx->CritSect))
2538 RTCritSectDelete(&pTransferCtx->CritSect);
2539
2540 PSHCLTRANSFER pTransfer, pTransferNext;
2541 RTListForEachSafe(&pTransferCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
2542 {
2543 ShClTransferDestroy(pTransfer);
2544
2545 RTListNodeRemove(&pTransfer->Node);
2546
2547 RTMemFree(pTransfer);
2548 pTransfer = NULL;
2549 }
2550
2551 pTransferCtx->cRunning = 0;
2552 pTransferCtx->cTransfers = 0;
2553}
2554
2555/**
2556 * Resets a shared Clipboard transfer.
2557 *
2558 * @param pTransferCtx Transfer context to reset.
2559 */
2560void ShClTransferCtxReset(PSHCLTRANSFERCTX pTransferCtx)
2561{
2562 AssertPtrReturnVoid(pTransferCtx);
2563
2564 LogFlowFuncEnter();
2565
2566 PSHCLTRANSFER pTransfer;
2567 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node)
2568 ShClTransferReset(pTransfer);
2569}
2570
2571/**
2572 * Returns a specific Shared Clipboard transfer, internal version.
2573 *
2574 * @returns Shared Clipboard transfer, or NULL if not found.
2575 * @param pTransferCtx Transfer context to return transfer for.
2576 * @param uID ID of the transfer to return.
2577 */
2578static PSHCLTRANSFER sharedClipboardTransferCtxGetTransferInternal(PSHCLTRANSFERCTX pTransferCtx, uint32_t uID)
2579{
2580 PSHCLTRANSFER pTransfer;
2581 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node) /** @todo Slow, but works for now. */
2582 {
2583 if (pTransfer->State.uID == uID)
2584 return pTransfer;
2585 }
2586
2587 return NULL;
2588}
2589
2590/**
2591 * Returns a specific Shared Clipboard transfer.
2592 *
2593 * @returns Shared Clipboard transfer, or NULL if not found.
2594 * @param pTransferCtx Transfer context to return transfer for.
2595 * @param uID ID of the transfer to return.
2596 */
2597PSHCLTRANSFER ShClTransferCtxGetTransfer(PSHCLTRANSFERCTX pTransferCtx, uint32_t uID)
2598{
2599 return sharedClipboardTransferCtxGetTransferInternal(pTransferCtx, uID);
2600}
2601
2602/**
2603 * Returns the number of running Shared Clipboard transfers.
2604 *
2605 * @returns Number of running transfers.
2606 * @param pTransferCtx Transfer context to return number for.
2607 */
2608uint32_t ShClTransferCtxGetRunningTransfers(PSHCLTRANSFERCTX pTransferCtx)
2609{
2610 AssertPtrReturn(pTransferCtx, 0);
2611 return pTransferCtx->cRunning;
2612}
2613
2614/**
2615 * Returns the number of total Shared Clipboard transfers.
2616 *
2617 * @returns Number of total transfers.
2618 * @param pTransferCtx Transfer context to return number for.
2619 */
2620uint32_t ShClTransferCtxGetTotalTransfers(PSHCLTRANSFERCTX pTransferCtx)
2621{
2622 AssertPtrReturn(pTransferCtx, 0);
2623 return pTransferCtx->cTransfers;
2624}
2625
2626/**
2627 * Registers a shared Clipboard transfer with a transfer context, i.e. allocates a transfer ID.
2628 *
2629 * @return VBox status code.
2630 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers
2631 * is reached.
2632 * @param pTransferCtx Transfer context to register transfer to.
2633 * @param pTransfer Transfer to register.
2634 * @param pidTransfer Where to return the transfer ID on success. Optional.
2635 */
2636int ShClTransferCtxTransferRegister(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, uint32_t *pidTransfer)
2637{
2638 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2639 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2640 /* pidTransfer is optional. */
2641
2642 /*
2643 * Pick a random bit as starting point. If it's in use, search forward
2644 * for a free one, wrapping around. We've reserved both the zero'th and
2645 * max-1 IDs.
2646 */
2647 uint32_t idTransfer = RTRandU32Ex(1, VBOX_SHCL_MAX_TRANSFERS - 2);
2648
2649 if (!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer))
2650 { /* likely */ }
2651 else if (pTransferCtx->cTransfers < VBOX_SHCL_MAX_TRANSFERS - 2 /* First and last are not used */)
2652 {
2653 /* Forward search. */
2654 int iHit = ASMBitNextClear(&pTransferCtx->bmTransferIds[0], VBOX_SHCL_MAX_TRANSFERS, idTransfer);
2655 if (iHit < 0)
2656 iHit = ASMBitFirstClear(&pTransferCtx->bmTransferIds[0], VBOX_SHCL_MAX_TRANSFERS);
2657 AssertLogRelMsgReturn(iHit >= 0, ("Transfer count: %RU16\n", pTransferCtx->cTransfers), VERR_SHCLPB_MAX_TRANSFERS_REACHED);
2658 idTransfer = iHit;
2659 AssertLogRelMsgReturn(!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer), ("idObject=%#x\n", idTransfer), VERR_INTERNAL_ERROR_2);
2660 }
2661 else
2662 {
2663 LogFunc(("Maximum number of transfers reached (%RU16 transfers)\n", pTransferCtx->cTransfers));
2664 return VERR_SHCLPB_MAX_TRANSFERS_REACHED;
2665 }
2666
2667 Log2Func(("pTransfer=%p, idTransfer=%RU32 (%RU16 transfers)\n", pTransfer, idTransfer, pTransferCtx->cTransfers));
2668
2669 RTListAppend(&pTransferCtx->List, &pTransfer->Node);
2670
2671 pTransferCtx->cTransfers++;
2672
2673 if (pidTransfer)
2674 *pidTransfer = idTransfer;
2675
2676 return VINF_SUCCESS;
2677}
2678
2679/**
2680 * Registers a shared Clipboard transfer with a transfer contextby specifying an ID for the transfer.
2681 *
2682 * @return VBox status code.
2683 * @retval VERR_ALREADY_EXISTS if a transfer with the given ID already exists.
2684 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers for this context has been reached.
2685 * @param pTransferCtx Transfer context to register transfer to.
2686 * @param pTransfer Transfer to register.
2687 * @param idTransfer Transfer ID to use for registration.
2688 */
2689int ShClTransferCtxTransferRegisterByIndex(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, uint32_t idTransfer)
2690{
2691 LogFlowFunc(("cTransfers=%RU16, idTransfer=%RU32\n", pTransferCtx->cTransfers, idTransfer));
2692
2693 if (pTransferCtx->cTransfers < VBOX_SHCL_MAX_TRANSFERS - 2 /* First and last are not used */)
2694 {
2695 if (!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer))
2696 {
2697 RTListAppend(&pTransferCtx->List, &pTransfer->Node);
2698
2699 pTransferCtx->cTransfers++;
2700 return VINF_SUCCESS;
2701 }
2702
2703 return VERR_ALREADY_EXISTS;
2704 }
2705
2706 LogFunc(("Maximum number of transfers reached (%RU16 transfers)\n", pTransferCtx->cTransfers));
2707 return VERR_SHCLPB_MAX_TRANSFERS_REACHED;
2708}
2709
2710/**
2711 * Unregisters a transfer from an Transfer context.
2712 *
2713 * @retval VINF_SUCCESS on success.
2714 * @retval VERR_NOT_FOUND if the transfer ID was not found.
2715 * @param pTransferCtx Transfer context to unregister transfer from.
2716 * @param idTransfer Transfer ID to unregister.
2717 */
2718int ShClTransferCtxTransferUnregister(PSHCLTRANSFERCTX pTransferCtx, uint32_t idTransfer)
2719{
2720 int rc = VINF_SUCCESS;
2721 AssertMsgStmt(ASMBitTestAndClear(&pTransferCtx->bmTransferIds, idTransfer), ("idTransfer=%#x\n", idTransfer), rc = VERR_NOT_FOUND);
2722
2723 LogFlowFunc(("idTransfer=%RU32\n", idTransfer));
2724
2725 PSHCLTRANSFER pTransfer = sharedClipboardTransferCtxGetTransferInternal(pTransferCtx, idTransfer);
2726 if (pTransfer)
2727 {
2728 RTListNodeRemove(&pTransfer->Node);
2729
2730 Assert(pTransferCtx->cTransfers);
2731 pTransferCtx->cTransfers--;
2732
2733 Assert(pTransferCtx->cTransfers >= pTransferCtx->cRunning);
2734
2735 LogFlowFunc(("Now %RU32 transfers left\n", pTransferCtx->cTransfers));
2736 }
2737 else
2738 rc = VERR_NOT_FOUND;
2739
2740 LogFlowFuncLeaveRC(rc);
2741 return rc;
2742}
2743
2744/**
2745 * Cleans up all associated transfers which are not needed (anymore).
2746 * This can be due to transfers which only have been announced but not / never being run.
2747 *
2748 * @param pTransferCtx Transfer context to cleanup transfers for.
2749 */
2750void ShClTransferCtxCleanup(PSHCLTRANSFERCTX pTransferCtx)
2751{
2752 AssertPtrReturnVoid(pTransferCtx);
2753
2754 LogFlowFunc(("pTransferCtx=%p, cTransfers=%RU16 cRunning=%RU16\n",
2755 pTransferCtx, pTransferCtx->cTransfers, pTransferCtx->cRunning));
2756
2757 if (pTransferCtx->cTransfers == 0)
2758 return;
2759
2760 /* Remove all transfers which are not in a running state (e.g. only announced). */
2761 PSHCLTRANSFER pTransfer, pTransferNext;
2762 RTListForEachSafe(&pTransferCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
2763 {
2764 if (ShClTransferGetStatus(pTransfer) != SHCLTRANSFERSTATUS_STARTED)
2765 {
2766 ShClTransferDestroy(pTransfer);
2767 RTListNodeRemove(&pTransfer->Node);
2768
2769 RTMemFree(pTransfer);
2770 pTransfer = NULL;
2771
2772 Assert(pTransferCtx->cTransfers);
2773 pTransferCtx->cTransfers--;
2774 }
2775 }
2776}
2777
2778/**
2779 * Returns whether the maximum of concurrent transfers of a specific transfer contexthas been reached or not.
2780 *
2781 * @returns \c if maximum has been reached, \c false if not.
2782 * @param pTransferCtx Transfer context to determine value for.
2783 */
2784bool ShClTransferCtxTransfersMaximumReached(PSHCLTRANSFERCTX pTransferCtx)
2785{
2786 AssertPtrReturn(pTransferCtx, true);
2787
2788 LogFlowFunc(("cRunning=%RU32, cMaxRunning=%RU32\n", pTransferCtx->cRunning, pTransferCtx->cMaxRunning));
2789
2790 Assert(pTransferCtx->cRunning <= pTransferCtx->cMaxRunning);
2791 return pTransferCtx->cRunning == pTransferCtx->cMaxRunning;
2792}
2793
2794/**
2795 * Copies file system objinfo from IPRT to Shared Clipboard format.
2796 *
2797 * @param pDst The Shared Clipboard structure to convert data to.
2798 * @param pSrc The IPRT structure to convert data from.
2799 */
2800void ShClFsObjFromIPRT(PSHCLFSOBJINFO pDst, PCRTFSOBJINFO pSrc)
2801{
2802 pDst->cbObject = pSrc->cbObject;
2803 pDst->cbAllocated = pSrc->cbAllocated;
2804 pDst->AccessTime = pSrc->AccessTime;
2805 pDst->ModificationTime = pSrc->ModificationTime;
2806 pDst->ChangeTime = pSrc->ChangeTime;
2807 pDst->BirthTime = pSrc->BirthTime;
2808 pDst->Attr.fMode = pSrc->Attr.fMode;
2809 /* Clear bits which we don't pass through for security reasons. */
2810 pDst->Attr.fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
2811 RT_ZERO(pDst->Attr.u);
2812 switch (pSrc->Attr.enmAdditional)
2813 {
2814 default:
2815 case RTFSOBJATTRADD_NOTHING:
2816 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_NOTHING;
2817 break;
2818
2819 case RTFSOBJATTRADD_UNIX:
2820 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_UNIX;
2821 pDst->Attr.u.Unix.uid = pSrc->Attr.u.Unix.uid;
2822 pDst->Attr.u.Unix.gid = pSrc->Attr.u.Unix.gid;
2823 pDst->Attr.u.Unix.cHardlinks = pSrc->Attr.u.Unix.cHardlinks;
2824 pDst->Attr.u.Unix.INodeIdDevice = pSrc->Attr.u.Unix.INodeIdDevice;
2825 pDst->Attr.u.Unix.INodeId = pSrc->Attr.u.Unix.INodeId;
2826 pDst->Attr.u.Unix.fFlags = pSrc->Attr.u.Unix.fFlags;
2827 pDst->Attr.u.Unix.GenerationId = pSrc->Attr.u.Unix.GenerationId;
2828 pDst->Attr.u.Unix.Device = pSrc->Attr.u.Unix.Device;
2829 break;
2830
2831 case RTFSOBJATTRADD_EASIZE:
2832 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_EASIZE;
2833 pDst->Attr.u.EASize.cb = pSrc->Attr.u.EASize.cb;
2834 break;
2835 }
2836}
2837
2838/**
2839 * Converts Shared Clipboard create flags (see SharedClipboard-transfers.h) into IPRT create flags.
2840 *
2841 * @returns IPRT status code.
2842 * @param fWritable Whether the object is writable.
2843 * @param fShClFlags Shared clipboard create flags.
2844 * @param fMode File attributes.
2845 * @param handleInitial Initial handle.
2846 * @retval pfOpen Where to store the IPRT creation / open flags.
2847 *
2848 * @sa Initially taken from vbsfConvertFileOpenFlags().
2849 */
2850static int sharedClipboardConvertFileCreateFlags(bool fWritable, unsigned fShClFlags, RTFMODE fMode,
2851 SHCLOBJHANDLE handleInitial, uint64_t *pfOpen)
2852{
2853 uint64_t fOpen = 0;
2854 int rc = VINF_SUCCESS;
2855
2856 if ( (fMode & RTFS_DOS_MASK) != 0
2857 && (fMode & RTFS_UNIX_MASK) == 0)
2858 {
2859 /* A DOS/Windows guest, make RTFS_UNIX_* from RTFS_DOS_*.
2860 * @todo this is based on rtFsModeNormalize/rtFsModeFromDos.
2861 * May be better to use RTFsModeNormalize here.
2862 */
2863 fMode |= RTFS_UNIX_IRUSR | RTFS_UNIX_IRGRP | RTFS_UNIX_IROTH;
2864 /* x for directories. */
2865 if (fMode & RTFS_DOS_DIRECTORY)
2866 fMode |= RTFS_TYPE_DIRECTORY | RTFS_UNIX_IXUSR | RTFS_UNIX_IXGRP | RTFS_UNIX_IXOTH;
2867 /* writable? */
2868 if (!(fMode & RTFS_DOS_READONLY))
2869 fMode |= RTFS_UNIX_IWUSR | RTFS_UNIX_IWGRP | RTFS_UNIX_IWOTH;
2870
2871 /* Set the requested mode using only allowed bits. */
2872 fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
2873 }
2874 else
2875 {
2876 /* Old linux and solaris additions did not initialize the Info.Attr.fMode field
2877 * and it contained random bits from stack. Detect this using the handle field value
2878 * passed from the guest: old additions set it (incorrectly) to 0, new additions
2879 * set it to SHCLOBJHANDLE_INVALID(~0).
2880 */
2881 if (handleInitial == 0)
2882 {
2883 /* Old additions. Do nothing, use default mode. */
2884 }
2885 else
2886 {
2887 /* New additions or Windows additions. Set the requested mode using only allowed bits.
2888 * Note: Windows guest set RTFS_UNIX_MASK bits to 0, which means a default mode
2889 * will be set in fOpen.
2890 */
2891 fOpen |= ((fMode & RTFS_UNIX_MASK) << RTFILE_O_CREATE_MODE_SHIFT) & RTFILE_O_CREATE_MODE_MASK;
2892 }
2893 }
2894
2895 switch ((fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_RW))
2896 {
2897 default:
2898 case SHCL_OBJ_CF_ACCESS_NONE:
2899 {
2900#ifdef RT_OS_WINDOWS
2901 if ((fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_ATTR) != SHCL_OBJ_CF_ACCESS_ATTR_NONE)
2902 fOpen |= RTFILE_O_ATTR_ONLY;
2903 else
2904#endif
2905 fOpen |= RTFILE_O_READ;
2906 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_NONE\n"));
2907 break;
2908 }
2909
2910 case SHCL_OBJ_CF_ACCESS_READ:
2911 {
2912 fOpen |= RTFILE_O_READ;
2913 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_READ\n"));
2914 break;
2915 }
2916
2917 case SHCL_OBJ_CF_ACCESS_WRITE:
2918 {
2919 fOpen |= RTFILE_O_WRITE;
2920 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_WRITE\n"));
2921 break;
2922 }
2923
2924 case SHCL_OBJ_CF_ACCESS_READWRITE:
2925 {
2926 fOpen |= RTFILE_O_READWRITE;
2927 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_READWRITE\n"));
2928 break;
2929 }
2930 }
2931
2932 if (fShClFlags & SHCL_OBJ_CF_ACCESS_APPEND)
2933 {
2934 fOpen |= RTFILE_O_APPEND;
2935 }
2936
2937 switch ((fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_ATTR))
2938 {
2939 default:
2940 case SHCL_OBJ_CF_ACCESS_ATTR_NONE:
2941 {
2942 fOpen |= RTFILE_O_ACCESS_ATTR_DEFAULT;
2943 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_NONE\n"));
2944 break;
2945 }
2946
2947 case SHCL_OBJ_CF_ACCESS_ATTR_READ:
2948 {
2949 fOpen |= RTFILE_O_ACCESS_ATTR_READ;
2950 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_READ\n"));
2951 break;
2952 }
2953
2954 case SHCL_OBJ_CF_ACCESS_ATTR_WRITE:
2955 {
2956 fOpen |= RTFILE_O_ACCESS_ATTR_WRITE;
2957 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_WRITE\n"));
2958 break;
2959 }
2960
2961 case SHCL_OBJ_CF_ACCESS_ATTR_READWRITE:
2962 {
2963 fOpen |= RTFILE_O_ACCESS_ATTR_READWRITE;
2964 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_READWRITE\n"));
2965 break;
2966 }
2967 }
2968
2969 /* Sharing mask */
2970 switch ((fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_DENY))
2971 {
2972 default:
2973 case SHCL_OBJ_CF_ACCESS_DENYNONE:
2974 fOpen |= RTFILE_O_DENY_NONE;
2975 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYNONE\n"));
2976 break;
2977
2978 case SHCL_OBJ_CF_ACCESS_DENYREAD:
2979 fOpen |= RTFILE_O_DENY_READ;
2980 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYREAD\n"));
2981 break;
2982
2983 case SHCL_OBJ_CF_ACCESS_DENYWRITE:
2984 fOpen |= RTFILE_O_DENY_WRITE;
2985 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYWRITE\n"));
2986 break;
2987
2988 case SHCL_OBJ_CF_ACCESS_DENYALL:
2989 fOpen |= RTFILE_O_DENY_ALL;
2990 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYALL\n"));
2991 break;
2992 }
2993
2994 /* Open/Create action mask */
2995 switch ((fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_EXISTS))
2996 {
2997 case SHCL_OBJ_CF_ACT_OPEN_IF_EXISTS:
2998 if (SHCL_OBJ_CF_ACT_CREATE_IF_NEW == (fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_NEW))
2999 {
3000 fOpen |= RTFILE_O_OPEN_CREATE;
3001 LogFlowFunc(("SHCL_OBJ_CF_ACT_OPEN_IF_EXISTS and SHCL_OBJ_CF_ACT_CREATE_IF_NEW\n"));
3002 }
3003 else if (SHCL_OBJ_CF_ACT_FAIL_IF_NEW == (fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_NEW))
3004 {
3005 fOpen |= RTFILE_O_OPEN;
3006 LogFlowFunc(("SHCL_OBJ_CF_ACT_OPEN_IF_EXISTS and SHCL_OBJ_CF_ACT_FAIL_IF_NEW\n"));
3007 }
3008 else
3009 {
3010 LogFlowFunc(("invalid open/create action combination\n"));
3011 rc = VERR_INVALID_PARAMETER;
3012 }
3013 break;
3014 case SHCL_OBJ_CF_ACT_FAIL_IF_EXISTS:
3015 if (SHCL_OBJ_CF_ACT_CREATE_IF_NEW == (fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_NEW))
3016 {
3017 fOpen |= RTFILE_O_CREATE;
3018 LogFlowFunc(("SHCL_OBJ_CF_ACT_FAIL_IF_EXISTS and SHCL_OBJ_CF_ACT_CREATE_IF_NEW\n"));
3019 }
3020 else
3021 {
3022 LogFlowFunc(("invalid open/create action combination\n"));
3023 rc = VERR_INVALID_PARAMETER;
3024 }
3025 break;
3026 case SHCL_OBJ_CF_ACT_REPLACE_IF_EXISTS:
3027 if (SHCL_OBJ_CF_ACT_CREATE_IF_NEW == (fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_NEW))
3028 {
3029 fOpen |= RTFILE_O_CREATE_REPLACE;
3030 LogFlowFunc(("SHCL_OBJ_CF_ACT_REPLACE_IF_EXISTS and SHCL_OBJ_CF_ACT_CREATE_IF_NEW\n"));
3031 }
3032 else if (SHCL_OBJ_CF_ACT_FAIL_IF_NEW == (fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_NEW))
3033 {
3034 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
3035 LogFlowFunc(("SHCL_OBJ_CF_ACT_REPLACE_IF_EXISTS and SHCL_OBJ_CF_ACT_FAIL_IF_NEW\n"));
3036 }
3037 else
3038 {
3039 LogFlowFunc(("invalid open/create action combination\n"));
3040 rc = VERR_INVALID_PARAMETER;
3041 }
3042 break;
3043 case SHCL_OBJ_CF_ACT_OVERWRITE_IF_EXISTS:
3044 if (SHCL_OBJ_CF_ACT_CREATE_IF_NEW == (fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_NEW))
3045 {
3046 fOpen |= RTFILE_O_CREATE_REPLACE;
3047 LogFlowFunc(("SHCL_OBJ_CF_ACT_OVERWRITE_IF_EXISTS and SHCL_OBJ_CF_ACT_CREATE_IF_NEW\n"));
3048 }
3049 else if (SHCL_OBJ_CF_ACT_FAIL_IF_NEW == (fShClFlags & SHCL_OBJ_CF_ACT_MASK_IF_NEW))
3050 {
3051 fOpen |= RTFILE_O_OPEN | RTFILE_O_TRUNCATE;
3052 LogFlowFunc(("SHCL_OBJ_CF_ACT_OVERWRITE_IF_EXISTS and SHCL_OBJ_CF_ACT_FAIL_IF_NEW\n"));
3053 }
3054 else
3055 {
3056 LogFlowFunc(("invalid open/create action combination\n"));
3057 rc = VERR_INVALID_PARAMETER;
3058 }
3059 break;
3060 default:
3061 {
3062 rc = VERR_INVALID_PARAMETER;
3063 LogFlowFunc(("SHCL_OBJ_CF_ACT_MASK_IF_EXISTS - invalid parameter\n"));
3064 break;
3065 }
3066 }
3067
3068 if (RT_SUCCESS(rc))
3069 {
3070 if (!fWritable)
3071 fOpen &= ~RTFILE_O_WRITE;
3072
3073 *pfOpen = fOpen;
3074 }
3075
3076 LogFlowFuncLeaveRC(rc);
3077 return rc;
3078}
3079
3080/**
3081 * Translates a Shared Clipboard transfer status (SHCLTRANSFERSTATUS_XXX) into a string.
3082 *
3083 * @returns Transfer status string name.
3084 * @param enmStatus The transfer status to translate.
3085 */
3086const char *ShClTransferStatusToStr(SHCLTRANSFERSTATUS enmStatus)
3087{
3088 switch (enmStatus)
3089 {
3090 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_NONE);
3091 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_INITIALIZED);
3092 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_STARTED);
3093 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_STOPPED);
3094 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_CANCELED);
3095 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_KILLED);
3096 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_ERROR);
3097 }
3098 return "Unknown";
3099}
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