VirtualBox

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

Last change on this file since 100509 was 100484, checked in by vboxsync, 19 months ago

Shared Clipboard: Logging nit. bugref:9437

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

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