VirtualBox

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

Last change on this file since 87611 was 87611, checked in by vboxsync, 4 years ago

Shared Clipboard/Transfers: More callback code. ​bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 92.9 KB
Line 
1/* $Id: clipboard-transfers.cpp 87611 2021-02-04 16:31:28Z 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);
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 = 64; /** @todo Make this dynamic. */
400 pParms->pszFilter = RTStrAlloc(pParms->cbFilter);
401
402 pParms->cbPath = RTPATH_MAX;
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 * Sets or unsets the callback table to be used for a Shared Clipboard transfer.
2019 *
2020 * @returns VBox status code.
2021 * @param pTransfer Clipboard transfer to set callbacks for.
2022 * @param pCallbacks Pointer to callback table to set.
2023 */
2024void ShClTransferSetCallbacks(PSHCLTRANSFER pTransfer,
2025 PSHCLTRANSFERCALLBACKTABLE pCallbacks)
2026{
2027 AssertPtrReturnVoid(pTransfer);
2028 AssertPtrReturnVoid(pCallbacks);
2029
2030 LogFlowFunc(("pCallbacks=%p\n", pCallbacks));
2031
2032#define SET_CALLBACK(a_pfnCallback) \
2033 if (pCallbacks->a_pfnCallback) \
2034 pTransfer->Callbacks.a_pfnCallback = pCallbacks->a_pfnCallback
2035
2036 SET_CALLBACK(pfnOnInitialize);
2037 SET_CALLBACK(pfnOnStart);
2038 SET_CALLBACK(pfnOnCompleted);
2039 SET_CALLBACK(pfnOnError);
2040 SET_CALLBACK(pfnOnRegistered);
2041 SET_CALLBACK(pfnOnUnregistered);
2042
2043#undef SET_CALLBACK
2044
2045 pTransfer->Callbacks.pvUser = pCallbacks->pvUser;
2046 pTransfer->Callbacks.cbUser = pCallbacks->cbUser;
2047}
2048
2049/**
2050 * Sets the transfer provider interface for a given transfer.
2051 *
2052 * @returns VBox status code.
2053 * @param pTransfer Transfer to create transfer provider for.
2054 * @param pCreationCtx Provider creation context to use for provider creation.
2055 */
2056int ShClTransferSetProviderIface(PSHCLTRANSFER pTransfer,
2057 PSHCLTXPROVIDERCREATIONCTX pCreationCtx)
2058{
2059 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2060 AssertPtrReturn(pCreationCtx, VERR_INVALID_POINTER);
2061
2062 LogFlowFuncEnter();
2063
2064 int rc = VINF_SUCCESS;
2065
2066 pTransfer->ProviderIface = pCreationCtx->Interface;
2067 pTransfer->ProviderCtx.pTransfer = pTransfer;
2068 pTransfer->ProviderCtx.pvUser = pCreationCtx->pvUser;
2069
2070 LogFlowFuncLeaveRC(rc);
2071 return rc;
2072}
2073
2074/**
2075 * Clears (resets) the root list of a Shared Clipboard transfer.
2076 *
2077 * @param pTransfer Transfer to clear transfer root list for.
2078 */
2079static void shClTransferListRootsClear(PSHCLTRANSFER pTransfer)
2080{
2081 AssertPtrReturnVoid(pTransfer);
2082
2083 if (pTransfer->pszPathRootAbs)
2084 {
2085 RTStrFree(pTransfer->pszPathRootAbs);
2086 pTransfer->pszPathRootAbs = NULL;
2087 }
2088
2089 PSHCLLISTROOT pListRoot, pListRootNext;
2090 RTListForEachSafe(&pTransfer->lstRoots, pListRoot, pListRootNext, SHCLLISTROOT, Node)
2091 {
2092 RTStrFree(pListRoot->pszPathAbs);
2093
2094 RTListNodeRemove(&pListRoot->Node);
2095
2096 RTMemFree(pListRoot);
2097 pListRoot = NULL;
2098 }
2099
2100 pTransfer->cRoots = 0;
2101}
2102
2103/**
2104 * Resets a Shared Clipboard transfer.
2105 *
2106 * @param pTransfer Clipboard transfer to reset.
2107 */
2108void ShClTransferReset(PSHCLTRANSFER pTransfer)
2109{
2110 AssertPtrReturnVoid(pTransfer);
2111
2112 LogFlowFuncEnter();
2113
2114 shClTransferListRootsClear(pTransfer);
2115
2116 PSHCLLISTHANDLEINFO pItList, pItListNext;
2117 RTListForEachSafe(&pTransfer->lstList, pItList, pItListNext, SHCLLISTHANDLEINFO, Node)
2118 {
2119 ShClTransferListHandleInfoDestroy(pItList);
2120
2121 RTListNodeRemove(&pItList->Node);
2122
2123 RTMemFree(pItList);
2124 }
2125
2126 PSHCLOBJHANDLEINFO pItObj, pItObjNext;
2127 RTListForEachSafe(&pTransfer->lstObj, pItObj, pItObjNext, SHCLOBJHANDLEINFO, Node)
2128 {
2129 ShClTransferObjHandleInfoDestroy(pItObj);
2130
2131 RTListNodeRemove(&pItObj->Node);
2132
2133 RTMemFree(pItObj);
2134 }
2135}
2136
2137/**
2138 * Returns the number of transfer root list entries.
2139 *
2140 * @returns Root list entry count.
2141 * @param pTransfer Clipboard transfer to return root entry count for.
2142 */
2143uint32_t ShClTransferRootsCount(PSHCLTRANSFER pTransfer)
2144{
2145 AssertPtrReturn(pTransfer, 0);
2146
2147 LogFlowFunc(("[Transfer %RU32] cRoots=%RU64\n", pTransfer->State.uID, pTransfer->cRoots));
2148 return (uint32_t)pTransfer->cRoots;
2149}
2150
2151/**
2152 * Returns a specific root list entry of a transfer.
2153 *
2154 * @returns Pointer to root list entry if found, or NULL if not found.
2155 * @param pTransfer Clipboard transfer to get root list entry from.
2156 * @param uIdx Index of root list entry to return.
2157 */
2158DECLINLINE(PSHCLLISTROOT) shClTransferRootsGetInternal(PSHCLTRANSFER pTransfer, uint32_t uIdx)
2159{
2160 if (uIdx >= pTransfer->cRoots)
2161 return NULL;
2162
2163 PSHCLLISTROOT pIt = RTListGetFirst(&pTransfer->lstRoots, SHCLLISTROOT, Node);
2164 while (uIdx--) /** @todo Slow, but works for now. */
2165 pIt = RTListGetNext(&pTransfer->lstRoots, pIt, SHCLLISTROOT, Node);
2166
2167 return pIt;
2168}
2169
2170/**
2171 * Get a specific root list entry.
2172 *
2173 * @returns VBox status code.
2174 * @param pTransfer Clipboard transfer to get root list entry of.
2175 * @param uIndex Index (zero-based) of entry to get.
2176 * @param pEntry Where to store the returned entry on success.
2177 */
2178int ShClTransferRootsEntry(PSHCLTRANSFER pTransfer,
2179 uint64_t uIndex, PSHCLROOTLISTENTRY pEntry)
2180{
2181 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2182 AssertPtrReturn(pEntry, VERR_INVALID_POINTER);
2183
2184 if (uIndex >= pTransfer->cRoots)
2185 return VERR_INVALID_PARAMETER;
2186
2187 int rc;
2188
2189 PSHCLLISTROOT pRoot = shClTransferRootsGetInternal(pTransfer, uIndex);
2190 AssertPtrReturn(pRoot, VERR_INVALID_PARAMETER);
2191
2192 /* Make sure that we only advertise relative source paths, not absolute ones. */
2193 const char *pcszSrcPath = pRoot->pszPathAbs;
2194
2195 char *pszFileName = RTPathFilename(pcszSrcPath);
2196 if (pszFileName)
2197 {
2198 Assert(pszFileName >= pcszSrcPath);
2199 size_t cchDstBase = pszFileName - pcszSrcPath;
2200 const char *pszDstPath = &pcszSrcPath[cchDstBase];
2201
2202 LogFlowFunc(("pcszSrcPath=%s, pszDstPath=%s\n", pcszSrcPath, pszDstPath));
2203
2204 rc = ShClTransferListEntryInit(pEntry);
2205 if (RT_SUCCESS(rc))
2206 {
2207 rc = RTStrCopy(pEntry->pszName, pEntry->cbName, pszDstPath);
2208 if (RT_SUCCESS(rc))
2209 {
2210 pEntry->cbInfo = sizeof(SHCLFSOBJINFO);
2211 pEntry->pvInfo = (PSHCLFSOBJINFO)RTMemAlloc(pEntry->cbInfo);
2212 if (pEntry->pvInfo)
2213 {
2214 RTFSOBJINFO fsObjInfo;
2215 rc = RTPathQueryInfo(pcszSrcPath, &fsObjInfo, RTFSOBJATTRADD_NOTHING);
2216 if (RT_SUCCESS(rc))
2217 {
2218 ShClFsObjFromIPRT(PSHCLFSOBJINFO(pEntry->pvInfo), &fsObjInfo);
2219
2220 pEntry->fInfo = VBOX_SHCL_INFO_FLAG_FSOBJINFO;
2221 }
2222 }
2223 else
2224 rc = VERR_NO_MEMORY;
2225 }
2226 }
2227 }
2228 else
2229 rc = VERR_INVALID_POINTER;
2230
2231 LogFlowFuncLeaveRC(rc);
2232 return rc;
2233}
2234
2235/**
2236 * Returns the root entries of a Shared Clipboard transfer.
2237 *
2238 * @returns VBox status code.
2239 * @param pTransfer Clipboard transfer to return root entries for.
2240 * @param ppRootList Where to store the root list on success.
2241 */
2242int ShClTransferRootsGet(PSHCLTRANSFER pTransfer, PSHCLROOTLIST *ppRootList)
2243{
2244 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2245 AssertPtrReturn(ppRootList, VERR_INVALID_POINTER);
2246
2247 LogFlowFuncEnter();
2248
2249 int rc = VINF_SUCCESS;
2250
2251 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
2252 {
2253 PSHCLROOTLIST pRootList = ShClTransferRootListAlloc();
2254 if (!pRootList)
2255 return VERR_NO_MEMORY;
2256
2257 const uint64_t cRoots = (uint32_t)pTransfer->cRoots;
2258
2259 LogFlowFunc(("cRoots=%RU64\n", cRoots));
2260
2261 if (cRoots)
2262 {
2263 PSHCLROOTLISTENTRY paRootListEntries
2264 = (PSHCLROOTLISTENTRY)RTMemAllocZ(cRoots * sizeof(SHCLROOTLISTENTRY));
2265 if (paRootListEntries)
2266 {
2267 for (uint64_t i = 0; i < cRoots; ++i)
2268 {
2269 rc = ShClTransferRootsEntry(pTransfer, i, &paRootListEntries[i]);
2270 if (RT_FAILURE(rc))
2271 break;
2272 }
2273
2274 if (RT_SUCCESS(rc))
2275 pRootList->paEntries = paRootListEntries;
2276 }
2277 else
2278 rc = VERR_NO_MEMORY;
2279 }
2280 else
2281 rc = VERR_NOT_FOUND;
2282
2283 if (RT_SUCCESS(rc))
2284 {
2285 pRootList->Hdr.cRoots = cRoots;
2286 pRootList->Hdr.fRoots = 0; /** @todo Implement this. */
2287
2288 *ppRootList = pRootList;
2289 }
2290 }
2291 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
2292 {
2293 if (pTransfer->ProviderIface.pfnRootsGet)
2294 rc = pTransfer->ProviderIface.pfnRootsGet(&pTransfer->ProviderCtx, ppRootList);
2295 else
2296 rc = VERR_NOT_SUPPORTED;
2297 }
2298 else
2299 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
2300
2301 LogFlowFuncLeaveRC(rc);
2302 return rc;
2303}
2304
2305/**
2306 * Sets transfer root list entries for a given transfer.
2307 *
2308 * @returns VBox status code.
2309 * @param pTransfer Transfer to set transfer list entries for.
2310 * @param pszRoots String list (separated by CRLF) of root entries to set.
2311 * All entries must have the same root path.
2312 * @param cbRoots Size (in bytes) of string list.
2313 */
2314int ShClTransferRootsSet(PSHCLTRANSFER pTransfer, const char *pszRoots, size_t cbRoots)
2315{
2316 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2317 AssertPtrReturn(pszRoots, VERR_INVALID_POINTER);
2318 AssertReturn(cbRoots, VERR_INVALID_PARAMETER);
2319
2320 if (!RTStrIsValidEncoding(pszRoots))
2321 return VERR_INVALID_UTF8_ENCODING;
2322
2323 int rc = VINF_SUCCESS;
2324
2325 shClTransferListRootsClear(pTransfer);
2326
2327 char *pszPathRootAbs = NULL;
2328
2329 RTCList<RTCString> lstRootEntries = RTCString(pszRoots, cbRoots - 1).split("\r\n");
2330 for (size_t i = 0; i < lstRootEntries.size(); ++i)
2331 {
2332 PSHCLLISTROOT pListRoot = (PSHCLLISTROOT)RTMemAlloc(sizeof(SHCLLISTROOT));
2333 AssertPtrBreakStmt(pListRoot, rc = VERR_NO_MEMORY);
2334
2335 const char *pszPathCur = RTStrDup(lstRootEntries.at(i).c_str());
2336
2337 LogFlowFunc(("pszPathCur=%s\n", pszPathCur));
2338
2339 /* No root path determined yet? */
2340 if (!pszPathRootAbs)
2341 {
2342 pszPathRootAbs = RTStrDup(pszPathCur);
2343 if (pszPathRootAbs)
2344 {
2345 RTPathStripFilename(pszPathRootAbs);
2346
2347 LogFlowFunc(("pszPathRootAbs=%s\n", pszPathRootAbs));
2348
2349 /* We don't want to have a relative directory here. */
2350 if (RTPathStartsWithRoot(pszPathRootAbs))
2351 {
2352 rc = shClTransferValidatePath(pszPathRootAbs, true /* Path must exist */);
2353 }
2354 else
2355 rc = VERR_INVALID_PARAMETER;
2356 }
2357 else
2358 rc = VERR_NO_MEMORY;
2359 }
2360
2361 if (RT_FAILURE(rc))
2362 break;
2363
2364 pListRoot->pszPathAbs = RTStrDup(pszPathCur);
2365 if (!pListRoot->pszPathAbs)
2366 {
2367 rc = VERR_NO_MEMORY;
2368 break;
2369 }
2370
2371 RTListAppend(&pTransfer->lstRoots, &pListRoot->Node);
2372
2373 pTransfer->cRoots++;
2374 }
2375
2376 /* No (valid) root directory found? Bail out early. */
2377 if (!pszPathRootAbs)
2378 rc = VERR_PATH_NOT_FOUND;
2379
2380 if (RT_SUCCESS(rc))
2381 {
2382 /*
2383 * Step 2:
2384 * Go through the created list and make sure all entries have the same root path.
2385 */
2386 PSHCLLISTROOT pListRoot;
2387 RTListForEach(&pTransfer->lstRoots, pListRoot, SHCLLISTROOT, Node)
2388 {
2389 if (!RTStrStartsWith(pListRoot->pszPathAbs, pszPathRootAbs))
2390 {
2391 rc = VERR_INVALID_PARAMETER;
2392 break;
2393 }
2394
2395 rc = shClTransferValidatePath(pListRoot->pszPathAbs, true /* Path must exist */);
2396 if (RT_FAILURE(rc))
2397 break;
2398 }
2399 }
2400
2401 /** @todo Entry rollback on failure? */
2402
2403 if (RT_SUCCESS(rc))
2404 {
2405 pTransfer->pszPathRootAbs = pszPathRootAbs;
2406 LogFlowFunc(("pszPathRootAbs=%s, cRoots=%zu\n", pTransfer->pszPathRootAbs, pTransfer->cRoots));
2407
2408 LogRel2(("Shared Clipboard: Transfer uses root '%s'\n", pTransfer->pszPathRootAbs));
2409 }
2410 else
2411 {
2412 LogRel(("Shared Clipboard: Unable to set roots for transfer, rc=%Rrc\n", rc));
2413 RTStrFree(pszPathRootAbs);
2414 }
2415
2416 LogFlowFuncLeaveRC(rc);
2417 return rc;
2418}
2419
2420/**
2421 * Returns the transfer's ID.
2422 *
2423 * @returns The transfer's ID.
2424 * @param pTransfer Clipboard transfer to return ID for.
2425 */
2426SHCLTRANSFERID ShClTransferGetID(PSHCLTRANSFER pTransfer)
2427{
2428 AssertPtrReturn(pTransfer, 0);
2429
2430 return pTransfer->State.uID;
2431}
2432
2433/**
2434 * Returns the transfer's direction.
2435 *
2436 * @returns The transfer's direction.
2437 * @param pTransfer Clipboard transfer to return direction for.
2438 */
2439SHCLTRANSFERDIR ShClTransferGetDir(PSHCLTRANSFER pTransfer)
2440{
2441 AssertPtrReturn(pTransfer, SHCLTRANSFERDIR_UNKNOWN);
2442
2443 LogFlowFunc(("[Transfer %RU32] enmDir=%RU32\n", pTransfer->State.uID, pTransfer->State.enmDir));
2444 return pTransfer->State.enmDir;
2445}
2446
2447/**
2448 * Returns the transfer's source.
2449 *
2450 * @returns The transfer's source.
2451 * @param pTransfer Clipboard transfer to return source for.
2452 */
2453SHCLSOURCE ShClTransferGetSource(PSHCLTRANSFER pTransfer)
2454{
2455 AssertPtrReturn(pTransfer, SHCLSOURCE_INVALID);
2456
2457 LogFlowFunc(("[Transfer %RU32] enmSource=%RU32\n", pTransfer->State.uID, pTransfer->State.enmSource));
2458 return pTransfer->State.enmSource;
2459}
2460
2461/**
2462 * Returns the current transfer status.
2463 *
2464 * @returns Current transfer status.
2465 * @param pTransfer Clipboard transfer to return status for.
2466 */
2467SHCLTRANSFERSTATUS ShClTransferGetStatus(PSHCLTRANSFER pTransfer)
2468{
2469 AssertPtrReturn(pTransfer, SHCLTRANSFERSTATUS_NONE);
2470
2471 LogFlowFunc(("[Transfer %RU32] enmStatus=%RU32\n", pTransfer->State.uID, pTransfer->State.enmStatus));
2472 return pTransfer->State.enmStatus;
2473}
2474
2475/**
2476 * Runs a started Shared Clipboard transfer in a dedicated thread.
2477 *
2478 * @returns VBox status code.
2479 * @param pTransfer Clipboard transfer to run.
2480 * @param pfnThreadFunc Pointer to thread function to use.
2481 * @param pvUser Pointer to user-provided data. Optional.
2482 */
2483int ShClTransferRun(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser)
2484{
2485 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2486 AssertPtrReturn(pfnThreadFunc, VERR_INVALID_POINTER);
2487 /* pvUser is optional. */
2488
2489 AssertMsgReturn(pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_STARTED,
2490 ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
2491 VERR_WRONG_ORDER);
2492
2493 int rc = shClTransferThreadCreate(pTransfer, pfnThreadFunc, pvUser);
2494
2495 LogFlowFuncLeaveRC(rc);
2496 return rc;
2497}
2498
2499/**
2500 * Starts an initialized transfer.
2501 *
2502 * @returns VBox status code.
2503 * @param pTransfer Clipboard transfer to start.
2504 */
2505int ShClTransferStart(PSHCLTRANSFER pTransfer)
2506{
2507 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2508
2509 LogFlowFuncEnter();
2510
2511 /* Ready to start? */
2512 AssertMsgReturn(pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_INITIALIZED,
2513 ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
2514 VERR_WRONG_ORDER);
2515
2516 int rc;
2517
2518 if (pTransfer->Callbacks.pfnOnStart)
2519 {
2520 rc = pTransfer->Callbacks.pfnOnStart(&pTransfer->CallbackCtx);
2521 }
2522 else
2523 rc = VINF_SUCCESS;
2524
2525 if (RT_SUCCESS(rc))
2526 {
2527 pTransfer->State.enmStatus = SHCLTRANSFERSTATUS_STARTED;
2528 }
2529
2530 LogFlowFuncLeaveRC(rc);
2531 return rc;
2532}
2533
2534/**
2535 * Creates a thread for a Shared Clipboard transfer.
2536 *
2537 * @returns VBox status code.
2538 * @param pTransfer Clipboard transfer to create thread for.
2539 * @param pfnThreadFunc Thread function to use for this transfer.
2540 * @param pvUser Pointer to user-provided data.
2541 */
2542static int shClTransferThreadCreate(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser)
2543
2544{
2545 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2546
2547 /* Already marked for stopping? */
2548 AssertMsgReturn(pTransfer->Thread.fStop == false,
2549 ("Transfer thread already marked for stopping"), VERR_WRONG_ORDER);
2550 /* Already started? */
2551 AssertMsgReturn(pTransfer->Thread.fStarted == false,
2552 ("Transfer thread already started"), VERR_WRONG_ORDER);
2553
2554 /* Spawn a worker thread, so that we don't block the window thread for too long. */
2555 int rc = RTThreadCreate(&pTransfer->Thread.hThread, pfnThreadFunc,
2556 pvUser, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
2557 "shclp");
2558 if (RT_SUCCESS(rc))
2559 {
2560 int rc2 = RTThreadUserWait(pTransfer->Thread.hThread, 30 * 1000 /* Timeout in ms */);
2561 AssertRC(rc2);
2562
2563 if (pTransfer->Thread.fStarted) /* Did the thread indicate that it started correctly? */
2564 {
2565 /* Nothing to do in here. */
2566 }
2567 else
2568 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
2569 }
2570
2571 LogFlowFuncLeaveRC(rc);
2572 return rc;
2573}
2574
2575/**
2576 * Destroys a thread of a Shared Clipboard transfer.
2577 *
2578 * @returns VBox status code.
2579 * @param pTransfer Clipboard transfer to destroy thread for.
2580 * @param uTimeoutMs Timeout (in ms) to wait for thread creation.
2581 */
2582static int shClTransferThreadDestroy(PSHCLTRANSFER pTransfer, RTMSINTERVAL uTimeoutMs)
2583{
2584 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2585
2586 if (pTransfer->Thread.hThread == NIL_RTTHREAD)
2587 return VINF_SUCCESS;
2588
2589 LogFlowFuncEnter();
2590
2591 /* Set stop indicator. */
2592 pTransfer->Thread.fStop = true;
2593
2594 int rcThread = VERR_WRONG_ORDER;
2595 int rc = RTThreadWait(pTransfer->Thread.hThread, uTimeoutMs, &rcThread);
2596
2597 LogFlowFunc(("Waiting for thread resulted in %Rrc (thread exited with %Rrc)\n", rc, rcThread));
2598
2599 return rc;
2600}
2601
2602/**
2603 * Initializes a Shared Clipboard transfer context.
2604 *
2605 * @returns VBox status code.
2606 * @param pTransferCtx Transfer context to initialize.
2607 */
2608int ShClTransferCtxInit(PSHCLTRANSFERCTX pTransferCtx)
2609{
2610 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2611
2612 LogFlowFunc(("pTransferCtx=%p\n", pTransferCtx));
2613
2614 int rc = RTCritSectInit(&pTransferCtx->CritSect);
2615 if (RT_SUCCESS(rc))
2616 {
2617 RTListInit(&pTransferCtx->List);
2618
2619 pTransferCtx->cTransfers = 0;
2620 pTransferCtx->cRunning = 0;
2621 pTransferCtx->cMaxRunning = 64; /** @todo Make this configurable? */
2622
2623 RT_ZERO(pTransferCtx->bmTransferIds);
2624
2625#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
2626 ShClTransferHttpServerInit(&pTransferCtx->HttpServer);
2627#endif
2628 ShClTransferCtxReset(pTransferCtx);
2629 }
2630
2631 return VINF_SUCCESS;
2632}
2633
2634/**
2635 * Destroys a Shared Clipboard transfer context struct.
2636 *
2637 * @param pTransferCtx Transfer context to destroy.
2638 */
2639void ShClTransferCtxDestroy(PSHCLTRANSFERCTX pTransferCtx)
2640{
2641 if (!pTransferCtx)
2642 return;
2643
2644 LogFlowFunc(("pTransferCtx=%p\n", pTransferCtx));
2645
2646 if (RTCritSectIsInitialized(&pTransferCtx->CritSect))
2647 RTCritSectDelete(&pTransferCtx->CritSect);
2648
2649 PSHCLTRANSFER pTransfer, pTransferNext;
2650 RTListForEachSafe(&pTransferCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
2651 {
2652 ShClTransferDestroy(pTransfer);
2653
2654 shclTransferCtxTransferRemoveAndUnregister(pTransferCtx, pTransfer);
2655
2656 RTMemFree(pTransfer);
2657 pTransfer = NULL;
2658 }
2659
2660 pTransferCtx->cRunning = 0;
2661 pTransferCtx->cTransfers = 0;
2662}
2663
2664/**
2665 * Resets a Shared Clipboard transfer.
2666 *
2667 * @param pTransferCtx Transfer context to reset.
2668 */
2669void ShClTransferCtxReset(PSHCLTRANSFERCTX pTransferCtx)
2670{
2671 AssertPtrReturnVoid(pTransferCtx);
2672
2673 LogFlowFuncEnter();
2674
2675 PSHCLTRANSFER pTransfer;
2676 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node)
2677 ShClTransferReset(pTransfer);
2678
2679#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
2680 /** @todo Anything to do here? */
2681#endif
2682}
2683
2684/**
2685 * Returns a specific Shared Clipboard transfer, internal version.
2686 *
2687 * @returns Shared Clipboard transfer, or NULL if not found.
2688 * @param pTransferCtx Transfer context to return transfer for.
2689 * @param uID ID of the transfer to return.
2690 */
2691static PSHCLTRANSFER shClTransferCtxGetTransferByIdInternal(PSHCLTRANSFERCTX pTransferCtx, uint32_t uID)
2692{
2693 PSHCLTRANSFER pTransfer;
2694 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node) /** @todo Slow, but works for now. */
2695 {
2696 if (pTransfer->State.uID == uID)
2697 return pTransfer;
2698 }
2699
2700 return NULL;
2701}
2702
2703/**
2704 * Returns a specific Shared Clipboard transfer by index, internal version.
2705 *
2706 * @returns Shared Clipboard transfer, or NULL if not found.
2707 * @param pTransferCtx Transfer context to return transfer for.
2708 * @param uIdx Index of the transfer to return.
2709 */
2710static PSHCLTRANSFER shClTransferCtxGetTransferByIndexInternal(PSHCLTRANSFERCTX pTransferCtx, uint32_t uIdx)
2711{
2712 uint32_t idx = 0;
2713
2714 PSHCLTRANSFER pTransfer;
2715 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node) /** @todo Slow, but works for now. */
2716 {
2717 if (uIdx == idx)
2718 return pTransfer;
2719 idx++;
2720 }
2721
2722 return NULL;
2723}
2724
2725/**
2726 * Returns a Shared Clipboard transfer for a specific transfer ID.
2727 *
2728 * @returns Shared Clipboard transfer, or NULL if not found.
2729 * @param pTransferCtx Transfer context to return transfer for.
2730 * @param uID ID of the transfer to return.
2731 */
2732PSHCLTRANSFER ShClTransferCtxGetTransferById(PSHCLTRANSFERCTX pTransferCtx, uint32_t uID)
2733{
2734 return shClTransferCtxGetTransferByIdInternal(pTransferCtx, uID);
2735}
2736
2737/**
2738 * Returns a Shared Clipboard transfer for a specific list index.
2739 *
2740 * @returns Shared Clipboard transfer, or NULL if not found.
2741 * @param pTransferCtx Transfer context to return transfer for.
2742 * @param uIdx List index of the transfer to return.
2743 */
2744PSHCLTRANSFER ShClTransferCtxGetTransferByIndex(PSHCLTRANSFERCTX pTransferCtx, uint32_t uIdx)
2745{
2746 return shClTransferCtxGetTransferByIndexInternal(pTransferCtx, uIdx);
2747}
2748
2749/**
2750 * Returns the number of running Shared Clipboard transfers.
2751 *
2752 * @returns Number of running transfers.
2753 * @param pTransferCtx Transfer context to return number for.
2754 */
2755uint32_t ShClTransferCtxGetRunningTransfers(PSHCLTRANSFERCTX pTransferCtx)
2756{
2757 AssertPtrReturn(pTransferCtx, 0);
2758 return pTransferCtx->cRunning;
2759}
2760
2761/**
2762 * Returns the number of total Shared Clipboard transfers.
2763 *
2764 * @returns Number of total transfers.
2765 * @param pTransferCtx Transfer context to return number for.
2766 */
2767uint32_t ShClTransferCtxGetTotalTransfers(PSHCLTRANSFERCTX pTransferCtx)
2768{
2769 AssertPtrReturn(pTransferCtx, 0);
2770 return pTransferCtx->cTransfers;
2771}
2772
2773/**
2774 * Registers a Shared Clipboard transfer with a transfer context, i.e. allocates a transfer ID.
2775 *
2776 * @return VBox status code.
2777 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers
2778 * is reached.
2779 * @param pTransferCtx Transfer context to register transfer to.
2780 * @param pTransfer Transfer to register.
2781 * @param pidTransfer Where to return the transfer ID on success. Optional.
2782 */
2783int ShClTransferCtxTransferRegister(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, SHCLTRANSFERID *pidTransfer)
2784{
2785 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2786 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2787 /* pidTransfer is optional. */
2788
2789 /*
2790 * Pick a random bit as starting point. If it's in use, search forward
2791 * for a free one, wrapping around. We've reserved both the zero'th and
2792 * max-1 IDs.
2793 */
2794 SHCLTRANSFERID idTransfer = RTRandU32Ex(1, VBOX_SHCL_MAX_TRANSFERS - 2);
2795
2796 if (!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer))
2797 { /* likely */ }
2798 else if (pTransferCtx->cTransfers < VBOX_SHCL_MAX_TRANSFERS - 2 /* First and last are not used */)
2799 {
2800 /* Forward search. */
2801 int iHit = ASMBitNextClear(&pTransferCtx->bmTransferIds[0], VBOX_SHCL_MAX_TRANSFERS, idTransfer);
2802 if (iHit < 0)
2803 iHit = ASMBitFirstClear(&pTransferCtx->bmTransferIds[0], VBOX_SHCL_MAX_TRANSFERS);
2804 AssertLogRelMsgReturn(iHit >= 0, ("Transfer count: %RU16\n", pTransferCtx->cTransfers), VERR_SHCLPB_MAX_TRANSFERS_REACHED);
2805 idTransfer = iHit;
2806 AssertLogRelMsgReturn(!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer), ("idObject=%#x\n", idTransfer), VERR_INTERNAL_ERROR_2);
2807 }
2808 else
2809 {
2810 LogFunc(("Maximum number of transfers reached (%RU16 transfers)\n", pTransferCtx->cTransfers));
2811 return VERR_SHCLPB_MAX_TRANSFERS_REACHED;
2812 }
2813
2814 Log2Func(("pTransfer=%p, idTransfer=%RU32 (%RU16 transfers)\n", pTransfer, idTransfer, pTransferCtx->cTransfers));
2815
2816 pTransfer->State.uID = idTransfer;
2817
2818 RTListAppend(&pTransferCtx->List, &pTransfer->Node);
2819
2820 pTransferCtx->cTransfers++;
2821
2822 if (pTransfer->Callbacks.pfnOnRegistered)
2823 pTransfer->Callbacks.pfnOnRegistered(&pTransfer->CallbackCtx, pTransferCtx);
2824
2825 if (pidTransfer)
2826 *pidTransfer = idTransfer;
2827
2828 LogFlowFuncLeaveRC(VINF_SUCCESS);
2829 return VINF_SUCCESS;
2830}
2831
2832/**
2833 * Registers a Shared Clipboard transfer with a transfer context by specifying an ID for the transfer.
2834 *
2835 * @return VBox status code.
2836 * @retval VERR_ALREADY_EXISTS if a transfer with the given ID already exists.
2837 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers for this context has been reached.
2838 * @param pTransferCtx Transfer context to register transfer to.
2839 * @param pTransfer Transfer to register.
2840 * @param idTransfer Transfer ID to use for registration.
2841 */
2842int ShClTransferCtxTransferRegisterById(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, SHCLTRANSFERID idTransfer)
2843{
2844 LogFlowFunc(("cTransfers=%RU16, idTransfer=%RU32\n", pTransferCtx->cTransfers, idTransfer));
2845
2846 if (pTransferCtx->cTransfers < VBOX_SHCL_MAX_TRANSFERS - 2 /* First and last are not used */)
2847 {
2848 if (!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer))
2849 {
2850 RTListAppend(&pTransferCtx->List, &pTransfer->Node);
2851
2852 pTransfer->State.uID = idTransfer;
2853
2854 if (pTransfer->Callbacks.pfnOnRegistered)
2855 pTransfer->Callbacks.pfnOnRegistered(&pTransfer->CallbackCtx, pTransferCtx);
2856
2857 pTransferCtx->cTransfers++;
2858 return VINF_SUCCESS;
2859 }
2860
2861 return VERR_ALREADY_EXISTS;
2862 }
2863
2864 LogFunc(("Maximum number of transfers reached (%RU16 transfers)\n", pTransferCtx->cTransfers));
2865 return VERR_SHCLPB_MAX_TRANSFERS_REACHED;
2866}
2867
2868/**
2869 * Removes and unregisters a transfer from a transfer context.
2870 *
2871 * @param pTransferCtx Transfer context to remove transfer from.
2872 * @param pTransfer Transfer to remove.
2873 */
2874static void shclTransferCtxTransferRemoveAndUnregister(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer)
2875{
2876 RTListNodeRemove(&pTransfer->Node);
2877
2878 Assert(pTransferCtx->cTransfers);
2879 pTransferCtx->cTransfers--;
2880
2881 Assert(pTransferCtx->cTransfers >= pTransferCtx->cRunning);
2882
2883 if (pTransfer->Callbacks.pfnOnUnregistered)
2884 pTransfer->Callbacks.pfnOnUnregistered(&pTransfer->CallbackCtx, pTransferCtx);
2885
2886 LogFlowFunc(("Now %RU32 transfers left\n", pTransferCtx->cTransfers));
2887}
2888
2889/**
2890 * Unregisters a transfer from an Transfer context.
2891 *
2892 * @retval VINF_SUCCESS on success.
2893 * @retval VERR_NOT_FOUND if the transfer ID was not found.
2894 * @param pTransferCtx Transfer context to unregister transfer from.
2895 * @param idTransfer Transfer ID to unregister.
2896 */
2897int ShClTransferCtxTransferUnregister(PSHCLTRANSFERCTX pTransferCtx, SHCLTRANSFERID idTransfer)
2898{
2899 int rc = VINF_SUCCESS;
2900 AssertMsgStmt(ASMBitTestAndClear(&pTransferCtx->bmTransferIds, idTransfer), ("idTransfer=%#x\n", idTransfer), rc = VERR_NOT_FOUND);
2901
2902 LogFlowFunc(("idTransfer=%RU32\n", idTransfer));
2903
2904 PSHCLTRANSFER pTransfer = shClTransferCtxGetTransferByIdInternal(pTransferCtx, idTransfer);
2905 if (pTransfer)
2906 {
2907 shclTransferCtxTransferRemoveAndUnregister(pTransferCtx, pTransfer);
2908 }
2909 else
2910 rc = VERR_NOT_FOUND;
2911
2912 LogFlowFuncLeaveRC(rc);
2913 return rc;
2914}
2915
2916/**
2917 * Cleans up all associated transfers which are not needed (anymore).
2918 * This can be due to transfers which only have been announced but not / never being run.
2919 *
2920 * @param pTransferCtx Transfer context to cleanup transfers for.
2921 */
2922void ShClTransferCtxCleanup(PSHCLTRANSFERCTX pTransferCtx)
2923{
2924 AssertPtrReturnVoid(pTransferCtx);
2925
2926 LogFlowFunc(("pTransferCtx=%p, cTransfers=%RU16 cRunning=%RU16\n",
2927 pTransferCtx, pTransferCtx->cTransfers, pTransferCtx->cRunning));
2928
2929 if (pTransferCtx->cTransfers == 0)
2930 return;
2931
2932 /* Remove all transfers which are not in a running state (e.g. only announced). */
2933 PSHCLTRANSFER pTransfer, pTransferNext;
2934 RTListForEachSafe(&pTransferCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
2935 {
2936 if (ShClTransferGetStatus(pTransfer) != SHCLTRANSFERSTATUS_STARTED)
2937 {
2938 shclTransferCtxTransferRemoveAndUnregister(pTransferCtx, pTransfer);
2939
2940 ShClTransferDestroy(pTransfer);
2941
2942 RTMemFree(pTransfer);
2943 pTransfer = NULL;
2944 }
2945 }
2946}
2947
2948/**
2949 * Returns whether the maximum of concurrent transfers of a specific transfer contexthas been reached or not.
2950 *
2951 * @returns \c if maximum has been reached, \c false if not.
2952 * @param pTransferCtx Transfer context to determine value for.
2953 */
2954bool ShClTransferCtxTransfersMaximumReached(PSHCLTRANSFERCTX pTransferCtx)
2955{
2956 AssertPtrReturn(pTransferCtx, true);
2957
2958 LogFlowFunc(("cRunning=%RU32, cMaxRunning=%RU32\n", pTransferCtx->cRunning, pTransferCtx->cMaxRunning));
2959
2960 Assert(pTransferCtx->cRunning <= pTransferCtx->cMaxRunning);
2961 return pTransferCtx->cRunning == pTransferCtx->cMaxRunning;
2962}
2963
2964/**
2965 * Copies file system objinfo from IPRT to Shared Clipboard format.
2966 *
2967 * @param pDst The Shared Clipboard structure to convert data to.
2968 * @param pSrc The IPRT structure to convert data from.
2969 */
2970void ShClFsObjFromIPRT(PSHCLFSOBJINFO pDst, PCRTFSOBJINFO pSrc)
2971{
2972 pDst->cbObject = pSrc->cbObject;
2973 pDst->cbAllocated = pSrc->cbAllocated;
2974 pDst->AccessTime = pSrc->AccessTime;
2975 pDst->ModificationTime = pSrc->ModificationTime;
2976 pDst->ChangeTime = pSrc->ChangeTime;
2977 pDst->BirthTime = pSrc->BirthTime;
2978 pDst->Attr.fMode = pSrc->Attr.fMode;
2979 /* Clear bits which we don't pass through for security reasons. */
2980 pDst->Attr.fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
2981 RT_ZERO(pDst->Attr.u);
2982 switch (pSrc->Attr.enmAdditional)
2983 {
2984 default:
2985 case RTFSOBJATTRADD_NOTHING:
2986 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_NOTHING;
2987 break;
2988
2989 case RTFSOBJATTRADD_UNIX:
2990 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_UNIX;
2991 pDst->Attr.u.Unix.uid = pSrc->Attr.u.Unix.uid;
2992 pDst->Attr.u.Unix.gid = pSrc->Attr.u.Unix.gid;
2993 pDst->Attr.u.Unix.cHardlinks = pSrc->Attr.u.Unix.cHardlinks;
2994 pDst->Attr.u.Unix.INodeIdDevice = pSrc->Attr.u.Unix.INodeIdDevice;
2995 pDst->Attr.u.Unix.INodeId = pSrc->Attr.u.Unix.INodeId;
2996 pDst->Attr.u.Unix.fFlags = pSrc->Attr.u.Unix.fFlags;
2997 pDst->Attr.u.Unix.GenerationId = pSrc->Attr.u.Unix.GenerationId;
2998 pDst->Attr.u.Unix.Device = pSrc->Attr.u.Unix.Device;
2999 break;
3000
3001 case RTFSOBJATTRADD_EASIZE:
3002 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_EASIZE;
3003 pDst->Attr.u.EASize.cb = pSrc->Attr.u.EASize.cb;
3004 break;
3005 }
3006}
3007
3008/**
3009 * Converts Shared Clipboard create flags (see SharedClipboard-transfers.h) into IPRT create flags.
3010 *
3011 * @returns IPRT status code.
3012 * @param fShClFlags Shared clipboard create flags.
3013 * @param[out] pfOpen Where to store the RTFILE_O_XXX flags for
3014 * RTFileOpen.
3015 *
3016 * @sa Initially taken from vbsfConvertFileOpenFlags().
3017 */
3018static int shClConvertFileCreateFlags(uint32_t fShClFlags, uint64_t *pfOpen)
3019{
3020 AssertMsgReturnStmt(!(fShClFlags & ~SHCL_OBJ_CF_VALID_MASK), ("%#x4\n", fShClFlags), *pfOpen = 0, VERR_INVALID_FLAGS);
3021
3022 uint64_t fOpen = 0;
3023
3024 switch (fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_RW)
3025 {
3026 case SHCL_OBJ_CF_ACCESS_NONE:
3027 {
3028#ifdef RT_OS_WINDOWS
3029 if ((fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_ATTR) != SHCL_OBJ_CF_ACCESS_ATTR_NONE)
3030 fOpen |= RTFILE_O_ATTR_ONLY;
3031 else
3032#endif
3033 fOpen |= RTFILE_O_READ;
3034 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_NONE\n"));
3035 break;
3036 }
3037
3038 case SHCL_OBJ_CF_ACCESS_READ:
3039 {
3040 fOpen |= RTFILE_O_READ;
3041 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_READ\n"));
3042 break;
3043 }
3044
3045 default:
3046 AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE);
3047 }
3048
3049 switch (fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_ATTR)
3050 {
3051 case SHCL_OBJ_CF_ACCESS_ATTR_NONE:
3052 {
3053 fOpen |= RTFILE_O_ACCESS_ATTR_DEFAULT;
3054 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_NONE\n"));
3055 break;
3056 }
3057
3058 case SHCL_OBJ_CF_ACCESS_ATTR_READ:
3059 {
3060 fOpen |= RTFILE_O_ACCESS_ATTR_READ;
3061 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_READ\n"));
3062 break;
3063 }
3064
3065 default:
3066 AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE);
3067 }
3068
3069 /* Sharing mask */
3070 switch (fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_DENY)
3071 {
3072 case SHCL_OBJ_CF_ACCESS_DENYNONE:
3073 fOpen |= RTFILE_O_DENY_NONE;
3074 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYNONE\n"));
3075 break;
3076
3077 case SHCL_OBJ_CF_ACCESS_DENYWRITE:
3078 fOpen |= RTFILE_O_DENY_WRITE;
3079 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYWRITE\n"));
3080 break;
3081
3082 default:
3083 AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE);
3084 }
3085
3086 *pfOpen = fOpen;
3087
3088 LogFlowFuncLeaveRC(VINF_SUCCESS);
3089 return VINF_SUCCESS;
3090}
3091
3092/**
3093 * Translates a Shared Clipboard transfer status (SHCLTRANSFERSTATUS_XXX) into a string.
3094 *
3095 * @returns Transfer status string name.
3096 * @param enmStatus The transfer status to translate.
3097 */
3098const char *ShClTransferStatusToStr(SHCLTRANSFERSTATUS enmStatus)
3099{
3100 switch (enmStatus)
3101 {
3102 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_NONE);
3103 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_INITIALIZED);
3104 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_STARTED);
3105 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_STOPPED);
3106 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_CANCELED);
3107 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_KILLED);
3108 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_ERROR);
3109 }
3110 return "Unknown";
3111}
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