VirtualBox

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

最後變更 在這個檔案從87653是 87653,由 vboxsync 提交於 4 年 前

Shared Clipboard/Transfers: Added ShClTransferCopyCallbacks(). ​bugref:9437

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette