VirtualBox

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

Last change on this file since 98988 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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