VirtualBox

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

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

Copyright year updates by scm.

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