VirtualBox

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

Last change on this file since 91933 was 87658, checked in by vboxsync, 3 years ago

Shared Clipboard/Transfers: Rearranged parameter lists of ShClTransferObj[Read|Write](). ​bugref:9437

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

© 2023 Oracle
ContactPrivacy policyTerms of Use