VirtualBox

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

Last change on this file since 93497 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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