VirtualBox

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

Last change on this file since 100467 was 100467, checked in by vboxsync, 21 months ago

Shared Clipboard: Renamed shClConvertFileCreateFlags() -> ShClTransferConvertFileCreateFlags() and moved ShClTransferConvertFileCreateFlags() + ShClTransferResolvePathAbs() to the generic transfers module. bugref:9437

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

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