VirtualBox

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

Last change on this file since 107044 was 106061, checked in by vboxsync, 2 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 109.0 KB
Line 
1/* $Id: clipboard-transfers.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * Shared Clipboard: Common clipboard transfer handling code.
4 */
5
6/*
7 * Copyright (C) 2019-2024 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, PFNSHCLTRANSFERTHREAD 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 a transfer object structure.
732 *
733 * @returns VBox status code.
734 * @param pObj Object structure to initialize.
735 */
736int ShClTransferObjInit(PSHCLTRANSFEROBJ pObj)
737{
738 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
739
740 pObj->hObj = NIL_SHCLOBJHANDLE;
741 pObj->enmType = SHCLOBJTYPE_INVALID;
742
743 pObj->pszPathLocalAbs = NULL;
744
745 RT_ZERO(pObj->u);
746
747 return VINF_SUCCESS;
748}
749
750/**
751 * Destroys a transfer object structure.
752 *
753 * @param pObj Object structure to destroy.
754 */
755void ShClTransferObjDestroy(PSHCLTRANSFEROBJ pObj)
756{
757 if (!pObj)
758 return;
759
760 if (pObj->pszPathLocalAbs)
761 {
762 RTStrFree(pObj->pszPathLocalAbs);
763 pObj->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 transfer object of a transfer.
844 *
845 * @returns Pointer to transfer object if found, or NULL if not found.
846 * @param pTransfer Clipboard transfer to get transfer object from.
847 * @param hObj Object handle of the object to get handle info for.
848 */
849PSHCLTRANSFEROBJ ShClTransferObjGet(PSHCLTRANSFER pTransfer, SHCLOBJHANDLE hObj)
850{
851 PSHCLTRANSFEROBJ pIt;
852 RTListForEach(&pTransfer->lstObj, pIt, SHCLTRANSFEROBJ, 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 pTransfer Clipboard transfer to destroy.
1165 * The pointer will be invalid after return.
1166 */
1167int ShClTransferDestroy(PSHCLTRANSFER pTransfer)
1168{
1169 if (!pTransfer)
1170 return VINF_SUCCESS;
1171
1172 if (!RTCritSectIsInitialized(&pTransfer->CritSect))
1173 return VINF_SUCCESS;
1174
1175 /* Must come before the refcount check below, as the callback might release a reference. */
1176 if (pTransfer->Callbacks.pfnOnDestroy)
1177 pTransfer->Callbacks.pfnOnDestroy(&pTransfer->CallbackCtx);
1178
1179 AssertMsgReturn(ASMAtomicReadU32(&pTransfer->cRefs) == 0,
1180 ("Number of references > 0 (%RU32)\n", pTransfer->cRefs), VERR_WRONG_ORDER);
1181
1182 LogFlowFuncEnter();
1183
1184 int rc = shClTransferThreadDestroy(pTransfer, SHCL_TIMEOUT_DEFAULT_MS);
1185 if (RT_FAILURE(rc))
1186 return rc;
1187
1188 ShClTransferReset(pTransfer);
1189
1190 if (RTCritSectIsInitialized(&pTransfer->CritSect))
1191 RTCritSectDelete(&pTransfer->CritSect);
1192
1193 rc = RTSemEventDestroy(pTransfer->StatusChangeEvent);
1194 AssertRCReturn(rc, rc);
1195 pTransfer->StatusChangeEvent = NIL_RTSEMEVENT;
1196
1197 ShClEventSourceDestroy(&pTransfer->Events);
1198
1199 RTMemFree(pTransfer);
1200 pTransfer = NULL;
1201
1202 LogFlowFuncLeave();
1203 return VINF_SUCCESS;
1204}
1205
1206/**
1207 * Initializes a clipboard transfer.
1208 *
1209 * @returns VBox status code.
1210 * @param pTransfer Transfer to initialize.
1211 */
1212int ShClTransferInit(PSHCLTRANSFER pTransfer)
1213{
1214 shClTransferLock(pTransfer);
1215
1216 AssertMsgReturnStmt(pTransfer->State.enmStatus < SHCLTRANSFERSTATUS_INITIALIZED,
1217 ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
1218 shClTransferUnlock(pTransfer), VERR_WRONG_ORDER);
1219
1220 pTransfer->cRefs = 0;
1221
1222 LogFlowFunc(("uID=%RU32, enmDir=%RU32, enmSource=%RU32\n",
1223 pTransfer->State.uID, pTransfer->State.enmDir, pTransfer->State.enmSource));
1224
1225 pTransfer->cListHandles = 0;
1226 pTransfer->uListHandleNext = 1;
1227
1228 pTransfer->cObjHandles = 0;
1229 pTransfer->uObjHandleNext = 1;
1230
1231 pTransfer->Thread.fStarted = false;
1232 pTransfer->Thread.fStop = false;
1233 pTransfer->Thread.fCancelled = false;
1234
1235 int rc = VINF_SUCCESS;
1236
1237 if (pTransfer->Callbacks.pfnOnInitialize)
1238 rc = pTransfer->Callbacks.pfnOnInitialize(&pTransfer->CallbackCtx);
1239 if (RT_SUCCESS(rc))
1240 {
1241 /* Sanity: Make sure that the transfer we're gonna report as INITIALIZED
1242 * actually has some root entries set, as the other side can query for those at any time then. */
1243 if (pTransfer->State.enmDir == SHCLTRANSFERDIR_TO_REMOTE)
1244 AssertMsgStmt(ShClTransferRootsCount(pTransfer), ("Transfer has no root entries set (yet)\n"), rc = VERR_WRONG_ORDER);
1245
1246 if (RT_SUCCESS(rc))
1247 rc = shClTransferSetStatus(pTransfer, SHCLTRANSFERSTATUS_INITIALIZED);
1248 }
1249
1250 shClTransferUnlock(pTransfer);
1251
1252 /* Note: Callback will be called after we unlocked the transfer, as the caller might access the transfer right away. */
1253 if ( RT_SUCCESS(rc)
1254 && pTransfer->Callbacks.pfnOnInitialized)
1255 pTransfer->Callbacks.pfnOnInitialized(&pTransfer->CallbackCtx);
1256
1257 if (RT_FAILURE(rc))
1258 LogRel(("Shared Clipboard: Initialziation of transfer failed with %Rrc\n", rc));
1259
1260 LogFlowFuncLeaveRC(rc);
1261 return rc;
1262}
1263
1264/**
1265 * Returns whether a transfer is in a running state or not.
1266 *
1267 * @returns @c true if in running state, or @c false if not.
1268 * @param pTransfer Clipboard transfer to return status for.
1269 */
1270bool ShClTransferIsRunning(PSHCLTRANSFER pTransfer)
1271{
1272 shClTransferLock(pTransfer);
1273
1274 bool const fRunning = pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_STARTED;
1275
1276 shClTransferUnlock(pTransfer);
1277
1278 return fRunning;
1279}
1280
1281/**
1282 * Returns whether a transfer has been (successfully) completed or not.
1283 *
1284 * @returns @c true if complete, or @c false if not.
1285 * @param pTransfer Clipboard transfer to return status for.
1286 */
1287bool ShClTransferIsComplete(PSHCLTRANSFER pTransfer)
1288{
1289 shClTransferLock(pTransfer);
1290
1291 bool const fCompleted = pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_COMPLETED;
1292
1293 shClTransferUnlock(pTransfer);
1294
1295 return fCompleted;
1296}
1297
1298/**
1299 * Returns whether a transfer has been aborted due to cancelling, killing or an error.
1300 *
1301 * @returns @c true if in aborted state, or @c false if not.
1302 * @param pTransfer Clipboard transfer to return status for.
1303 */
1304bool ShClTransferIsAborted(PSHCLTRANSFER pTransfer)
1305{
1306 shClTransferLock(pTransfer);
1307
1308 bool const fAborted = pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_CANCELED
1309 || pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_KILLED
1310 || pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_ERROR;
1311
1312 shClTransferUnlock(pTransfer);
1313
1314 return fAborted;
1315}
1316
1317/**
1318 * Locks a transfer.
1319 *
1320 * @param pTransfer Transfer to lock.
1321 */
1322DECLINLINE(void) shClTransferLock(PSHCLTRANSFER pTransfer)
1323{
1324 int rc2 = RTCritSectEnter(&pTransfer->CritSect);
1325 AssertRC(rc2);
1326}
1327
1328/**
1329 * Unlocks a transfer.
1330 *
1331 * @param pTransfer Transfer to unlock.
1332 */
1333DECLINLINE(void) shClTransferUnlock(PSHCLTRANSFER pTransfer)
1334{
1335 int rc2 = RTCritSectLeave(&pTransfer->CritSect);
1336 AssertRC(rc2);
1337}
1338
1339/**
1340 * Acquires a reference to this transfer.
1341 *
1342 * @returns New reference count.
1343 * @param pTransfer Transfer to acquire reference for.
1344 */
1345uint32_t ShClTransferAcquire(PSHCLTRANSFER pTransfer)
1346{
1347 return ASMAtomicIncU32(&pTransfer->cRefs);
1348}
1349
1350/**
1351 * Releases a reference to this transfer.
1352 *
1353 * @returns New reference count.
1354 * @param pTransfer Transfer to release reference for.
1355 */
1356uint32_t ShClTransferRelease(PSHCLTRANSFER pTransfer)
1357{
1358 const uint32_t cRefs = ASMAtomicDecU32(&pTransfer->cRefs);
1359 Assert(pTransfer->cRefs <= VBOX_SHCL_MAX_TRANSFERS); /* Not perfect, but better than nothing. */
1360 return cRefs;
1361}
1362
1363/**
1364 * Opens a transfer list.
1365 *
1366 * @returns VBox status code.
1367 * @param pTransfer Clipboard transfer to handle.
1368 * @param pOpenParms List open parameters to use for opening.
1369 * @param phList Where to store the List handle of opened list on success.
1370 */
1371int ShClTransferListOpen(PSHCLTRANSFER pTransfer, PSHCLLISTOPENPARMS pOpenParms,
1372 PSHCLLISTHANDLE phList)
1373{
1374 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1375 AssertPtrReturn(pOpenParms, VERR_INVALID_POINTER);
1376 AssertPtrReturn(phList, VERR_INVALID_POINTER);
1377
1378 if (pTransfer->cListHandles == pTransfer->cMaxListHandles)
1379 return VERR_SHCLPB_MAX_LISTS_REACHED;
1380
1381 int rc;
1382 if (pTransfer->ProviderIface.pfnListOpen)
1383 rc = pTransfer->ProviderIface.pfnListOpen(&pTransfer->ProviderCtx, pOpenParms, phList);
1384 else
1385 rc = VERR_NOT_SUPPORTED;
1386
1387 if (RT_FAILURE(rc))
1388 LogRel(("Shared Clipboard: Opening list '%s' (fiter '%s', flags %#x) failed with %Rrc\n",
1389 pOpenParms->pszPath, pOpenParms->pszFilter, pOpenParms->fList, rc));
1390
1391 LogFlowFuncLeaveRC(rc);
1392 return rc;
1393}
1394
1395/**
1396 * Closes a transfer list.
1397 *
1398 * @returns VBox status code.
1399 * @param pTransfer Clipboard transfer to handle.
1400 * @param hList Handle of list to close.
1401 */
1402int ShClTransferListClose(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList)
1403{
1404 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1405
1406 if (hList == NIL_SHCLLISTHANDLE)
1407 return VINF_SUCCESS;
1408
1409 int rc;
1410 if (pTransfer->ProviderIface.pfnListClose)
1411 rc = pTransfer->ProviderIface.pfnListClose(&pTransfer->ProviderCtx, hList);
1412 else
1413 rc = VERR_NOT_SUPPORTED;
1414
1415 if (RT_FAILURE(rc))
1416 LogRel(("Shared Clipboard: Closing list 0x%x entry failed with %Rrc\n", hList, rc));
1417
1418 LogFlowFuncLeaveRC(rc);
1419 return rc;
1420}
1421
1422/**
1423 * Retrieves the header of a transfer list.
1424 *
1425 * @returns VBox status code.
1426 * @param pTransfer Clipboard transfer to handle.
1427 * @param hList Handle of list to get header for.
1428 * @param pHdr Where to store the returned list header information.
1429 */
1430int ShClTransferListGetHeader(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList,
1431 PSHCLLISTHDR pHdr)
1432{
1433 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1434 AssertPtrReturn(pHdr, VERR_INVALID_POINTER);
1435
1436 LogFlowFunc(("hList=%RU64\n", hList));
1437
1438 int rc;
1439 if (pTransfer->ProviderIface.pfnListHdrRead)
1440 rc = pTransfer->ProviderIface.pfnListHdrRead(&pTransfer->ProviderCtx, hList, pHdr);
1441 else
1442 rc = VERR_NOT_SUPPORTED;
1443
1444 if (RT_FAILURE(rc))
1445 LogRel(("Shared Clipboard: Reading list header list 0x%x entry failed with %Rrc\n", hList, rc));
1446
1447 LogFlowFuncLeaveRC(rc);
1448 return rc;
1449}
1450
1451/**
1452 * Returns a specific list handle info of a clipboard transfer.
1453 *
1454 * @returns Pointer to list handle info if found, or NULL if not found.
1455 * @param pTransfer Clipboard transfer to get list handle info from.
1456 * @param hList List handle of the list to get handle info for.
1457 */
1458PSHCLLISTHANDLEINFO ShClTransferListGetByHandle(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList)
1459{
1460 PSHCLLISTHANDLEINFO pIt;
1461 RTListForEach(&pTransfer->lstHandles, pIt, SHCLLISTHANDLEINFO, Node) /** @todo Sloooow ... improve this. */
1462 {
1463 if (pIt->hList == hList)
1464 return pIt;
1465 }
1466
1467 return NULL;
1468}
1469
1470/**
1471 * Returns the a transfer object of a transfer list.
1472 *
1473 * Currently not implemented and wil return NULL.
1474 *
1475 * @returns Pointer to transfer object, or NULL if not found / invalid.
1476 * @param pTransfer Clipboard transfer to return transfer object for.
1477 * @param hList Handle of clipboard transfer list to get object for.
1478 * @param uIdx Index of object to get.
1479 */
1480PSHCLTRANSFEROBJ ShClTransferListGetObj(PSHCLTRANSFER pTransfer,
1481 SHCLLISTHANDLE hList, uint64_t uIdx)
1482{
1483 AssertPtrReturn(pTransfer, NULL);
1484
1485 RT_NOREF(hList, uIdx);
1486
1487 LogFlowFunc(("hList=%RU64\n", hList));
1488
1489 return NULL;
1490}
1491
1492/**
1493 * Reads a single transfer list entry.
1494 *
1495 * @returns VBox status code or VERR_NO_MORE_FILES if the end of the list has been reached.
1496 * @param pTransfer Clipboard transfer to handle.
1497 * @param hList List handle of list to read from.
1498 * @param pEntry Where to store the read information.
1499 */
1500int ShClTransferListRead(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList,
1501 PSHCLLISTENTRY pEntry)
1502{
1503 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1504 AssertPtrReturn(pEntry, VERR_INVALID_POINTER);
1505
1506 LogFlowFunc(("hList=%RU64\n", hList));
1507
1508 int rc;
1509 if (pTransfer->ProviderIface.pfnListEntryRead)
1510 rc = pTransfer->ProviderIface.pfnListEntryRead(&pTransfer->ProviderCtx, hList, pEntry);
1511 else
1512 rc = VERR_NOT_SUPPORTED;
1513
1514 if (RT_FAILURE(rc))
1515 LogRel(("Shared Clipboard: Reading list for list 0x%x entry failed with %Rrc\n", hList, rc));
1516
1517 LogFlowFuncLeaveRC(rc);
1518 return rc;
1519}
1520
1521/**
1522 * Writes a single transfer list entry.
1523 *
1524 * @returns VBox status code.
1525 * @param pTransfer Clipboard transfer to handle.
1526 * @param hList List handle of list to write to.
1527 * @param pEntry Entry information to write.
1528 */
1529int ShClTransferListWrite(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList,
1530 PSHCLLISTENTRY pEntry)
1531{
1532 RT_NOREF(pTransfer, hList, pEntry);
1533
1534 int rc = VINF_SUCCESS;
1535
1536#if 0
1537 if (pTransfer->ProviderIface.pfnListEntryWrite)
1538 rc = pTransfer->ProviderIface.pfnListEntryWrite(&pTransfer->ProviderCtx, hList, pEntry);
1539#endif
1540
1541 if (RT_FAILURE(rc))
1542 LogRel(("Shared Clipboard: Writing list entry to list 0x%x failed with %Rrc\n", hList, rc));
1543
1544 LogFlowFuncLeaveRC(rc);
1545 return rc;
1546}
1547
1548/**
1549 * Returns whether a given transfer list handle is valid or not.
1550 *
1551 * @returns \c true if list handle is valid, \c false if not.
1552 * @param pTransfer Clipboard transfer to handle.
1553 * @param hList List handle to check.
1554 */
1555bool ShClTransferListHandleIsValid(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList)
1556{
1557 bool fIsValid = false;
1558
1559 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1560 {
1561 fIsValid = ShClTransferListGetByHandle(pTransfer, hList) != NULL;
1562 }
1563 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1564 {
1565 AssertFailed(); /** @todo Implement. */
1566 }
1567 else
1568 AssertFailedStmt(fIsValid = false);
1569
1570 return fIsValid;
1571}
1572
1573/**
1574 * Copies a transfer callback table from source to destination.
1575 *
1576 * @param pCallbacksDst Callback destination.
1577 * @param pCallbacksSrc Callback source. If set to NULL, the
1578 * destination callback table will be unset.
1579 */
1580static void shClTransferCopyCallbacks(PSHCLTRANSFERCALLBACKS pCallbacksDst, PSHCLTRANSFERCALLBACKS pCallbacksSrc)
1581{
1582 AssertPtrReturnVoid(pCallbacksDst);
1583 /* pCallbacksSrc can be NULL */
1584
1585 if (pCallbacksSrc) /* Set */
1586 {
1587#define SET_CALLBACK(a_pfnCallback) \
1588 if (pCallbacksSrc->a_pfnCallback) \
1589 pCallbacksDst->a_pfnCallback = pCallbacksSrc->a_pfnCallback
1590
1591 SET_CALLBACK(pfnOnCreated);
1592 SET_CALLBACK(pfnOnInitialize);
1593 SET_CALLBACK(pfnOnInitialized);
1594 SET_CALLBACK(pfnOnDestroy);
1595 SET_CALLBACK(pfnOnStarted);
1596 SET_CALLBACK(pfnOnCompleted);
1597 SET_CALLBACK(pfnOnError);
1598 SET_CALLBACK(pfnOnRegistered);
1599 SET_CALLBACK(pfnOnUnregistered);
1600
1601#undef SET_CALLBACK
1602
1603 pCallbacksDst->pvUser = pCallbacksSrc->pvUser;
1604 pCallbacksDst->cbUser = pCallbacksSrc->cbUser;
1605 }
1606 else /* Unset */
1607 RT_BZERO(pCallbacksDst, sizeof(SHCLTRANSFERCALLBACKS));
1608}
1609
1610/**
1611 * Sets or unsets the callback table to be used for a clipboard transfer.
1612 *
1613 * @returns VBox status code.
1614 * @param pTransfer Clipboard transfer to set callbacks for.
1615 * @param pCallbacks Pointer to callback table to set. If set to NULL,
1616 * existing callbacks for this transfer will be unset.
1617 *
1618 * @note Must come before initializing the transfer via ShClTransferInit().
1619 */
1620static void shClTransferSetCallbacks(PSHCLTRANSFER pTransfer, PSHCLTRANSFERCALLBACKS pCallbacks)
1621{
1622 AssertPtrReturnVoid(pTransfer);
1623 /* pCallbacks can be NULL. */
1624
1625 shClTransferCopyCallbacks(&pTransfer->Callbacks, pCallbacks);
1626
1627 /* Make sure that the callback context has all values set according to the callback table.
1628 * This only needs to be done once, so do this here. */
1629 pTransfer->CallbackCtx.pTransfer = pTransfer;
1630 pTransfer->CallbackCtx.pvUser = pTransfer->Callbacks.pvUser;
1631 pTransfer->CallbackCtx.cbUser = pTransfer->Callbacks.cbUser;
1632}
1633
1634/**
1635 * Sets the transfer provider for a given transfer.
1636 *
1637 * @returns VBox status code.
1638 * @param pTransfer Transfer to create transfer provider for.
1639 * @param pProvider Provider to use.
1640 */
1641int ShClTransferSetProvider(PSHCLTRANSFER pTransfer, PSHCLTXPROVIDER pProvider)
1642{
1643 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1644 AssertPtrReturn(pProvider, VERR_INVALID_POINTER);
1645
1646 LogFlowFuncEnter();
1647
1648 int rc = VINF_SUCCESS;
1649
1650 pTransfer->ProviderIface = pProvider->Interface;
1651 pTransfer->ProviderCtx.pTransfer = pTransfer;
1652 pTransfer->ProviderCtx.pvUser = pProvider->pvUser;
1653 pTransfer->ProviderCtx.cbUser = pProvider->cbUser;
1654
1655 LogFlowFuncLeaveRC(rc);
1656 return rc;
1657}
1658
1659/**
1660 * Sets the current status.
1661 *
1662 * @returns VBox status code.
1663 * @param pTransfer Clipboard transfer to set status for.
1664 * @param enmStatus Status to set.
1665 *
1666 * @note Caller needs to take critical section.
1667 */
1668static int shClTransferSetStatus(PSHCLTRANSFER pTransfer, SHCLTRANSFERSTATUS enmStatus)
1669{
1670 Assert(RTCritSectIsOwner(&pTransfer->CritSect));
1671#if 0
1672 AssertMsgReturn(pTransfer->State.enmStatus != enmStatus,
1673 ("Setting the same status twice in a row (%#x), please report this!\n", enmStatus), VERR_WRONG_ORDER);
1674#endif
1675 pTransfer->State.enmStatus = enmStatus;
1676
1677 LogFlowFunc(("enmStatus=%s\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)));
1678
1679 return RTSemEventSignal(pTransfer->StatusChangeEvent);
1680}
1681
1682/**
1683 * Returns the number of transfer root list entries.
1684 *
1685 * @returns Root list entry count.
1686 * @param pTransfer Clipboard transfer to return root entry count for.
1687 */
1688uint64_t ShClTransferRootsCount(PSHCLTRANSFER pTransfer)
1689{
1690 AssertPtrReturn(pTransfer, 0);
1691
1692 shClTransferLock(pTransfer);
1693
1694 uint32_t const cRoots = pTransfer->lstRoots.Hdr.cEntries;
1695
1696 shClTransferUnlock(pTransfer);
1697
1698 return cRoots;
1699}
1700
1701/**
1702 * Resets the root list of a clipboard transfer.
1703 *
1704 * @param pTransfer Transfer to clear transfer root list for.
1705 *
1706 * @note Caller needs to take critical section.
1707 */
1708static void shClTransferRootsReset(PSHCLTRANSFER pTransfer)
1709{
1710 AssertPtrReturnVoid(pTransfer);
1711 Assert(RTCritSectIsOwner(&pTransfer->CritSect));
1712
1713 if (pTransfer->pszPathRootAbs)
1714 {
1715 RTStrFree(pTransfer->pszPathRootAbs);
1716 pTransfer->pszPathRootAbs = NULL;
1717 }
1718
1719 ShClTransferListDestroy(&pTransfer->lstRoots);
1720}
1721
1722/**
1723 * Resets a clipboard transfer.
1724 *
1725 * @param pTransfer Clipboard transfer to reset.
1726 */
1727void ShClTransferReset(PSHCLTRANSFER pTransfer)
1728{
1729 AssertPtrReturnVoid(pTransfer);
1730
1731 LogFlowFuncEnter();
1732
1733 shClTransferLock(pTransfer);
1734
1735 shClTransferRootsReset(pTransfer);
1736
1737 PSHCLLISTHANDLEINFO pItList, pItListNext;
1738 RTListForEachSafe(&pTransfer->lstHandles, pItList, pItListNext, SHCLLISTHANDLEINFO, Node)
1739 {
1740 ShClTransferListHandleInfoDestroy(pItList);
1741
1742 RTListNodeRemove(&pItList->Node);
1743
1744 RTMemFree(pItList);
1745 }
1746
1747 PSHCLTRANSFEROBJ pItObj, pItObjNext;
1748 RTListForEachSafe(&pTransfer->lstObj, pItObj, pItObjNext, SHCLTRANSFEROBJ, Node)
1749 {
1750 ShClTransferObjDestroy(pItObj);
1751
1752 RTListNodeRemove(&pItObj->Node);
1753
1754 RTMemFree(pItObj);
1755 }
1756
1757 shClTransferUnlock(pTransfer);
1758}
1759
1760/**
1761 * Get a specific root list entry.
1762 *
1763 * @returns Const pointer to root list entry if found, or NULL if not found..
1764 * @param pTransfer Clipboard transfer to get root list entry of.
1765 * @param uIndex Index (zero-based) of entry to get.
1766 */
1767PCSHCLLISTENTRY ShClTransferRootsEntryGet(PSHCLTRANSFER pTransfer, uint64_t uIndex)
1768{
1769 AssertPtrReturn(pTransfer, NULL);
1770
1771 shClTransferLock(pTransfer);
1772
1773 if (uIndex >= pTransfer->lstRoots.Hdr.cEntries)
1774 {
1775 shClTransferUnlock(pTransfer);
1776 return NULL;
1777 }
1778
1779 PCSHCLLISTENTRY pEntry = shClTransferListGetEntryById(&pTransfer->lstRoots, uIndex);
1780
1781 shClTransferUnlock(pTransfer);
1782
1783 return pEntry;
1784}
1785
1786/**
1787 * Reads the root entries of a clipboard transfer.
1788 *
1789 * This gives the provider interface the chance of reading root entries information.
1790 *
1791 * @returns VBox status code.
1792 * @param pTransfer Clipboard transfer to read root list for.
1793 */
1794int ShClTransferRootListRead(PSHCLTRANSFER pTransfer)
1795{
1796 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1797
1798 LogFlowFuncEnter();
1799
1800 int rc;
1801 if (pTransfer->ProviderIface.pfnRootListRead)
1802 rc = pTransfer->ProviderIface.pfnRootListRead(&pTransfer->ProviderCtx);
1803 else
1804 rc = VERR_NOT_SUPPORTED;
1805
1806 shClTransferLock(pTransfer);
1807
1808 /* Make sure that we have at least an empty root path set. */
1809 if ( RT_SUCCESS(rc)
1810 && !pTransfer->pszPathRootAbs)
1811 {
1812 if (RTStrAPrintf(&pTransfer->pszPathRootAbs, "") < 0)
1813 rc = VERR_NO_MEMORY;
1814 }
1815
1816 shClTransferUnlock(pTransfer);
1817
1818 LogFlowFuncLeaveRC(rc);
1819 return rc;
1820}
1821
1822/**
1823 * Set the root list entries for a given clipboard transfer, extended version.
1824 *
1825 * @returns VBox status code.
1826 * @param pTransfer Transfer to set transfer list entries for.
1827 * @param pszRoots String list (separated by \a pszSep) of root entries to set.
1828 * All entries must have the same root path.
1829 * @param cbRoots Size (in bytes) of string list. Includes zero terminator.
1830 * @param pszSep String separator to use for splitting up the root entries.
1831 *
1832 * @note Accepts local paths or URI string lists (absolute only).
1833 */
1834int ShClTransferRootsSetFromStringListEx(PSHCLTRANSFER pTransfer, const char *pszRoots, size_t cbRoots, const char *pszSep)
1835{
1836 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1837 AssertPtrReturn(pszRoots, VERR_INVALID_POINTER);
1838 AssertReturn(cbRoots, VERR_INVALID_PARAMETER);
1839 AssertPtrReturn(pszSep, VERR_INVALID_POINTER);
1840
1841#ifdef DEBUG_andy
1842 LogFlowFunc(("Data:\n%.*Rhxd\n", cbRoots, pszRoots));
1843#endif
1844
1845 if (!RTStrIsValidEncoding(pszRoots))
1846 return VERR_INVALID_UTF8_ENCODING;
1847
1848 int rc = VINF_SUCCESS;
1849
1850 shClTransferLock(pTransfer);
1851
1852 shClTransferRootsReset(pTransfer);
1853
1854 PSHCLLIST pLstRoots = &pTransfer->lstRoots;
1855 char *pszPathRootAbs = NULL;
1856 size_t cchPathRootAbs = 0;
1857
1858 RTCList<RTCString> lstRootEntries = RTCString(pszRoots, cbRoots).split(pszSep);
1859 if (!lstRootEntries.size())
1860 {
1861 shClTransferUnlock(pTransfer);
1862 return VINF_SUCCESS;
1863 }
1864
1865 for (size_t i = 0; i < lstRootEntries.size(); ++i)
1866 {
1867 char *pszPathCur = NULL;
1868
1869 char *pszPath = NULL;
1870 rc = RTUriFilePathEx(lstRootEntries.at(i).c_str(),
1871 RTPATH_STR_F_STYLE_UNIX, &pszPath, 0 /*cbPath*/, NULL /*pcchPath*/);
1872 if (RT_SUCCESS(rc))
1873 {
1874 pszPathCur = pszPath;
1875 pszPath = NULL; /* pszPath has ownership now. */
1876 }
1877 else if (rc == VERR_URI_NOT_FILE_SCHEME) /* Local file path? */
1878 {
1879 pszPathCur = RTStrDup(lstRootEntries.at(i).c_str());
1880 rc = VINF_SUCCESS;
1881 }
1882
1883 LogFlowFunc(("pszPathCur=%s\n", pszPathCur));
1884
1885 rc = ShClTransferValidatePath(pszPathCur, false /* fMustExist */);
1886 if (RT_FAILURE(rc))
1887 {
1888 RTStrFree(pszPathCur);
1889 break;
1890 }
1891
1892 /* No root path determined yet? */
1893 if (!pszPathRootAbs)
1894 {
1895 pszPathRootAbs = RTStrDup(pszPathCur);
1896 if (pszPathRootAbs)
1897 {
1898 RTPathStripFilename(pszPathRootAbs);
1899
1900 LogFlowFunc(("pszPathRootAbs=%s\n", pszPathRootAbs));
1901
1902 /* We don't want to have a relative directory here. */
1903 if (RTPathStartsWithRoot(pszPathRootAbs))
1904 {
1905 cchPathRootAbs = RTStrNLen(pszPathRootAbs, RTPATH_MAX);
1906 LogRel2(("Shared Clipboard: Transfer uses root '%s'\n", pszPathRootAbs));
1907 }
1908 else
1909 rc = VERR_PATH_IS_RELATIVE;
1910 }
1911 else
1912 rc = VERR_NO_MEMORY;
1913 }
1914
1915 if (RT_SUCCESS(rc))
1916 {
1917 PSHCLLISTENTRY pEntry;
1918 rc = ShClTransferListEntryAlloc(&pEntry);
1919 if (RT_SUCCESS(rc))
1920 {
1921 PSHCLFSOBJINFO pFsObjInfo = (PSHCLFSOBJINFO)RTMemAllocZ(sizeof(SHCLFSOBJINFO));
1922 if (pFsObjInfo)
1923 {
1924 if (pTransfer->State.enmDir == SHCLTRANSFERDIR_TO_REMOTE)
1925 rc = ShClFsObjInfoQueryLocal(pszPathCur, pFsObjInfo);
1926 if (RT_SUCCESS(rc))
1927 {
1928 /* Calculate the relative path within the root path. */
1929 Assert(RTStrNLen(pszPathCur, RTPATH_MAX) >= cchPathRootAbs); /* Sanity. */
1930 const char *pszPathRelToRoot = pszPathCur + cchPathRootAbs + 1 /* Skip slash */;
1931 if ( pszPathRelToRoot
1932 && *pszPathRelToRoot != '\0')
1933 {
1934 LogRel2(("Shared Clipboard: Adding list entry '%s'\n", pszPathRelToRoot));
1935
1936 rc = ShClTransferListEntryInitEx(pEntry, VBOX_SHCL_INFO_F_FSOBJINFO, pszPathRelToRoot,
1937 pFsObjInfo, sizeof(SHCLFSOBJINFO));
1938 if (RT_SUCCESS(rc))
1939 {
1940 rc = ShClTransferListAddEntry(pLstRoots, pEntry, true /* fAppend */);
1941 if (RT_SUCCESS(rc))
1942 pFsObjInfo = NULL; /* pEntry has ownership now. */
1943 }
1944 }
1945 else
1946 {
1947 LogRel(("Shared Clipboard: Unable to construct relative path for '%s' (root is '%s')\n",
1948 pszPathCur, pszPathRootAbs));
1949 rc = VERR_PATH_DOES_NOT_START_WITH_ROOT;
1950 }
1951 }
1952
1953 if (pFsObjInfo)
1954 {
1955 RTMemFree(pFsObjInfo);
1956 pFsObjInfo = NULL;
1957 }
1958 }
1959 else
1960 rc = VERR_NO_MEMORY;
1961
1962 if (RT_FAILURE(rc))
1963 ShClTransferListEntryFree(pEntry);
1964 }
1965 }
1966
1967 RTStrFree(pszPathCur);
1968 }
1969
1970 /* No (valid) root directory found? Bail out early. */
1971 if ( RT_SUCCESS(rc)
1972 && !pszPathRootAbs)
1973 rc = VERR_PATH_DOES_NOT_START_WITH_ROOT;
1974
1975 if (RT_SUCCESS(rc))
1976 {
1977 pTransfer->pszPathRootAbs = pszPathRootAbs;
1978 LogFlowFunc(("pszPathRootAbs=%s, cRoots=%zu\n", pTransfer->pszPathRootAbs, pTransfer->lstRoots.Hdr.cEntries));
1979 }
1980 else
1981 {
1982 LogRel(("Shared Clipboard: Unable to set roots for transfer, rc=%Rrc\n", rc));
1983 ShClTransferListDestroy(pLstRoots);
1984 RTStrFree(pszPathRootAbs);
1985 }
1986
1987 shClTransferUnlock(pTransfer);
1988
1989 LogFlowFuncLeaveRC(rc);
1990 return rc;
1991}
1992
1993/**
1994 * Sets the root list entries for a given clipboard transfer.
1995 *
1996 * @returns VBox status code.
1997 * @param pTransfer Transfer to set transfer list entries for.
1998 * @param pszRoots String list (separated by SHCL_TRANSFER_URI_LIST_SEP_STR) of root entries to set.
1999 * All entries must have the same root path.
2000 * @param cbRoots Size (in bytes) of string list. Includes zero terminator.
2001 *
2002 * @note Accepts local paths or URI string lists (absolute only).
2003 */
2004int ShClTransferRootsSetFromStringList(PSHCLTRANSFER pTransfer, const char *pszRoots, size_t cbRoots)
2005{
2006 return ShClTransferRootsSetFromStringListEx(pTransfer, pszRoots, cbRoots, SHCL_TRANSFER_URI_LIST_SEP_STR);
2007}
2008
2009/**
2010 * Sets the root list entries for a given clipboard transfer, UTF-16 (Unicode) version.
2011 *
2012 * @returns VBox status code.
2013 * @param pTransfer Transfer to set transfer list entries for.
2014 * @param pwszRoots Unicode string list (separated by CRLF) of root entries to set.
2015 * All entries must have the same root path.
2016 * @param cbRoots Size (in bytes) of string list. Includes zero terminator.
2017 *
2018 * @note Accepts local paths or URI string lists (absolute only).
2019 */
2020int ShClTransferRootsSetFromStringListUnicode(PSHCLTRANSFER pTransfer, PRTUTF16 pwszRoots, size_t cbRoots)
2021{
2022 AssertPtrReturn(pwszRoots, VERR_INVALID_POINTER);
2023 AssertReturn(cbRoots, VERR_INVALID_PARAMETER);
2024 AssertReturn(cbRoots % sizeof(RTUTF16) == 0, VERR_INVALID_PARAMETER);
2025
2026 size_t cwcRoots = cbRoots / sizeof(RTUTF16);
2027
2028 /* This may slightly overestimate the space needed. */
2029 size_t chDst = 0;
2030 int rc = ShClUtf16LenUtf8(pwszRoots, cwcRoots, &chDst);
2031 if (RT_SUCCESS(rc))
2032 {
2033 chDst++; /* Add space for terminator. */
2034
2035 char *pszDst = (char *)RTStrAlloc(chDst);
2036 if (pszDst)
2037 {
2038 size_t cbActual = 0;
2039 rc = ShClConvUtf16CRLFToUtf8LF(pwszRoots, cwcRoots, pszDst, chDst, &cbActual);
2040 if (RT_SUCCESS(rc))
2041 rc = ShClTransferRootsSetFromStringList(pTransfer, pszDst, cbActual + 1 /* Include terminator */);
2042
2043 RTStrFree(pszDst);
2044 }
2045 else
2046 rc = VERR_NO_MEMORY;
2047 }
2048
2049 return rc;
2050}
2051
2052/**
2053 * Sets a single path as a transfer root.
2054 *
2055 * @returns VBox status code.
2056 * @param pTransfer Transfer to set transfer list entries for.
2057 * @param pszPath Path to use as transfer root. Can be a single file or a directory.
2058 *
2059 * @note Convenience function, uses ShClTransferRootsInitFromStringList() internally.
2060 */
2061int ShClTransferRootsSetFromPath(PSHCLTRANSFER pTransfer, const char *pszPath)
2062{
2063 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2064 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
2065
2066 char *pszRoots = NULL;
2067 int rc = RTStrAAppend(&pszRoots, pszPath);
2068 AssertRCReturn(rc, rc);
2069 rc = RTStrAAppend(&pszRoots, SHCL_TRANSFER_URI_LIST_SEP_STR);
2070 AssertRCReturn(rc, rc);
2071 rc = ShClTransferRootsSetFromStringList(pTransfer, pszRoots, strlen(pszRoots) + 1 /* Include terminator */);
2072 RTStrFree(pszRoots);
2073 return rc;
2074}
2075
2076/**
2077 * Returns the clipboard transfer's ID.
2078 *
2079 * @returns The transfer's ID.
2080 * @param pTransfer Clipboard transfer to return ID for.
2081 */
2082SHCLTRANSFERID ShClTransferGetID(PSHCLTRANSFER pTransfer)
2083{
2084 AssertPtrReturn(pTransfer, 0);
2085
2086 shClTransferLock(pTransfer);
2087
2088 SHCLTRANSFERID const uID = pTransfer->State.uID;
2089
2090 shClTransferUnlock(pTransfer);
2091
2092 return uID;
2093}
2094
2095/**
2096 * Returns the clipboard transfer's direction.
2097 *
2098 * @returns The transfer's direction.
2099 * @param pTransfer Clipboard transfer to return direction for.
2100 */
2101SHCLTRANSFERDIR ShClTransferGetDir(PSHCLTRANSFER pTransfer)
2102{
2103 AssertPtrReturn(pTransfer, SHCLTRANSFERDIR_UNKNOWN);
2104
2105 shClTransferLock(pTransfer);
2106
2107 SHCLTRANSFERDIR const enmDir = pTransfer->State.enmDir;
2108
2109 shClTransferUnlock(pTransfer);
2110
2111 return enmDir;
2112}
2113
2114/**
2115 * Returns the absolute root path of a transfer.
2116 *
2117 * @returns VBox status code.
2118 * @param pTransfer Clipboard transfer to return absolute root path for.
2119 * @param pszPath Where to store the returned path.
2120 * @param cbPath Size (in bytes) of \a pszPath.
2121 */
2122int ShClTransferGetRootPathAbs(PSHCLTRANSFER pTransfer, char *pszPath, size_t cbPath)
2123{
2124 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2125
2126 shClTransferLock(pTransfer);
2127
2128 AssertMsgReturn(pTransfer->pszPathRootAbs, ("Transfer has no root path set (yet)\n"), VERR_WRONG_ORDER);
2129
2130 int const rc = RTStrCopy(pszPath, cbPath, pTransfer->pszPathRootAbs);
2131
2132 shClTransferUnlock(pTransfer);
2133
2134 return rc;
2135}
2136
2137/**
2138 * Returns the transfer's source.
2139 *
2140 * @returns The transfer's source.
2141 * @param pTransfer Clipboard transfer to return source for.
2142 */
2143SHCLSOURCE ShClTransferGetSource(PSHCLTRANSFER pTransfer)
2144{
2145 AssertPtrReturn(pTransfer, SHCLSOURCE_INVALID);
2146
2147 shClTransferLock(pTransfer);
2148
2149 SHCLSOURCE const enmSource = pTransfer->State.enmSource;
2150
2151 shClTransferUnlock(pTransfer);
2152
2153 return enmSource;
2154}
2155
2156/**
2157 * Returns the current transfer status.
2158 *
2159 * @returns Current transfer status.
2160 * @param pTransfer Clipboard transfer to return status for.
2161 *
2162 * @note Caller needs to take critical section.
2163 */
2164DECLINLINE(SHCLTRANSFERSTATUS) shClTransferGetStatusLocked(PSHCLTRANSFER pTransfer)
2165{
2166 Assert(RTCritSectIsOwner(&pTransfer->CritSect));
2167
2168 shClTransferLock(pTransfer);
2169
2170 SHCLTRANSFERSTATUS const enmStatus = pTransfer->State.enmStatus;
2171
2172 shClTransferUnlock(pTransfer);
2173
2174 return enmStatus;
2175}
2176
2177/**
2178 * Returns the current transfer status.
2179 *
2180 * @returns Current transfer status.
2181 * @param pTransfer Clipboard transfer to return status for.
2182 */
2183SHCLTRANSFERSTATUS ShClTransferGetStatus(PSHCLTRANSFER pTransfer)
2184{
2185 AssertPtrReturn(pTransfer, SHCLTRANSFERSTATUS_NONE);
2186
2187 shClTransferLock(pTransfer);
2188
2189 SHCLTRANSFERSTATUS const enmSts = shClTransferGetStatusLocked(pTransfer);
2190
2191 shClTransferUnlock(pTransfer);
2192
2193 return enmSts;
2194}
2195
2196/**
2197 * Runs a started clipboard transfer in a dedicated thread.
2198 *
2199 * @returns VBox status code.
2200 * @param pTransfer Clipboard transfer to run.
2201 * @param pfnThreadFunc Pointer to thread function to use.
2202 * @param pvUser Pointer to user-provided data. Optional.
2203 */
2204int ShClTransferRun(PSHCLTRANSFER pTransfer, PFNSHCLTRANSFERTHREAD pfnThreadFunc, void *pvUser)
2205{
2206 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2207 AssertPtrReturn(pfnThreadFunc, VERR_INVALID_POINTER);
2208 /* pvUser is optional. */
2209
2210 AssertMsgReturn(pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_STARTED,
2211 ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
2212 VERR_WRONG_ORDER);
2213
2214 int rc = shClTransferThreadCreate(pTransfer, pfnThreadFunc, pvUser);
2215
2216 LogFlowFuncLeaveRC(rc);
2217 return rc;
2218}
2219
2220/**
2221 * Starts an initialized transfer.
2222 *
2223 * @returns VBox status code.
2224 * @param pTransfer Clipboard transfer to start.
2225 */
2226int ShClTransferStart(PSHCLTRANSFER pTransfer)
2227{
2228 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2229
2230 LogFlowFuncEnter();
2231
2232 shClTransferLock(pTransfer);
2233
2234 /* Ready to start? */
2235 AssertMsgReturnStmt(pTransfer->ProviderIface.pfnRootListRead != NULL,
2236 ("No provider interface set (yet)\n"),
2237 shClTransferUnlock(pTransfer), VERR_WRONG_ORDER);
2238 AssertMsgReturnStmt(pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_INITIALIZED,
2239 ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
2240 shClTransferUnlock(pTransfer), VERR_WRONG_ORDER);
2241
2242 int rc = shClTransferSetStatus(pTransfer, SHCLTRANSFERSTATUS_STARTED);
2243
2244 shClTransferUnlock(pTransfer);
2245
2246 if (pTransfer->Callbacks.pfnOnStarted)
2247 pTransfer->Callbacks.pfnOnStarted(&pTransfer->CallbackCtx);
2248
2249 LogFlowFuncLeaveRC(rc);
2250 return rc;
2251}
2252
2253/**
2254 * Stops a started transfer.
2255 *
2256 * @returns VBox status code.
2257 * @param pTransfer Clipboard transfer to stop.
2258 */
2259int ShClTransferStop(PSHCLTRANSFER pTransfer)
2260{
2261 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2262
2263 LogFlowFuncEnter();
2264
2265 int rc = shClTransferThreadDestroy(pTransfer, SHCL_TIMEOUT_DEFAULT_MS);
2266
2267 LogFlowFuncLeaveRC(rc);
2268 return rc;
2269}
2270
2271/**
2272 * Completes a transfer (as successful).
2273 *
2274 * @returns VBox status code.
2275 * @param pTransfer Clipboard transfer to complete.
2276 */
2277int ShClTransferComplete(PSHCLTRANSFER pTransfer)
2278{
2279 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2280
2281 LogFlowFuncEnter();
2282
2283 shClTransferLock(pTransfer);
2284
2285 AssertMsgReturnStmt( pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_INITIALIZED
2286 || pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_STARTED,
2287 ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
2288 shClTransferUnlock(pTransfer), VERR_WRONG_ORDER);
2289
2290 int rc = shClTransferSetStatus(pTransfer, SHCLTRANSFERSTATUS_COMPLETED);
2291
2292 shClTransferUnlock(pTransfer);
2293
2294 if (pTransfer->Callbacks.pfnOnCompleted)
2295 pTransfer->Callbacks.pfnOnCompleted(&pTransfer->CallbackCtx, rc);
2296
2297 LogFlowFuncLeaveRC(rc);
2298 return rc;
2299}
2300
2301/**
2302 * Cancels or sets an error for a transfer.
2303 *
2304 * @returns VBox status code.
2305 * @param pTransfer Clipboard transfer to cancel or set error for.
2306 * @param rc Error code to set.
2307 * If set to VERR_CANCELLED, the transfer will be canceled.
2308 */
2309static int shClTransferCancelOrError(PSHCLTRANSFER pTransfer, int rc)
2310{
2311 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2312
2313 LogFlowFunc(("%Rrc\n", rc));
2314
2315 shClTransferLock(pTransfer);
2316
2317 int rc2;
2318
2319 if ( pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_INITIALIZED
2320 || pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_STARTED)
2321 {
2322 if (rc == VERR_CANCELLED)
2323 {
2324 rc2 = shClTransferSetStatus(pTransfer, SHCLTRANSFERSTATUS_CANCELED);
2325
2326 if (pTransfer->Callbacks.pfnOnCompleted)
2327 pTransfer->Callbacks.pfnOnCompleted(&pTransfer->CallbackCtx, VERR_CANCELLED);
2328 }
2329 else
2330 {
2331 rc2 = shClTransferSetStatus(pTransfer, SHCLTRANSFERSTATUS_ERROR);
2332
2333 if (pTransfer->Callbacks.pfnOnError)
2334 pTransfer->Callbacks.pfnOnError(&pTransfer->CallbackCtx, rc);
2335 }
2336 }
2337 else /* Nothing to do. */
2338 rc2 = VINF_SUCCESS;
2339
2340 shClTransferUnlock(pTransfer);
2341
2342 LogFlowFuncLeaveRC(rc2);
2343 return rc2;
2344}
2345
2346/**
2347 * Cancels a transfer.
2348 *
2349 * @returns VBox status code.
2350 * @param pTransfer Clipboard transfer to cancel.
2351 */
2352int ShClTransferCancel(PSHCLTRANSFER pTransfer)
2353{
2354 return shClTransferCancelOrError(pTransfer, VERR_CANCELLED);
2355}
2356
2357/**
2358 * Kills a transfer.
2359 *
2360 * Currently cancels it internally.
2361 *
2362 * @returns VBox status code.
2363 * @param pTransfer Clipboard transfer to kill.
2364 */
2365int ShClTransferKill(PSHCLTRANSFER pTransfer)
2366{
2367 return ShClTransferCancel(pTransfer);
2368}
2369
2370/**
2371 * Sets an error for a transfer.
2372 *
2373 * @returns VBox status code.
2374 * @param pTransfer Clipboard transfer to set error for.
2375 * @param rc Error code to set.
2376 */
2377int ShClTransferError(PSHCLTRANSFER pTransfer, int rc)
2378{
2379 return shClTransferCancelOrError(pTransfer, rc);
2380}
2381
2382/**
2383 * Internal struct for keeping a transfer thread context.
2384 */
2385typedef struct _SHCLTRANSFERTHREADCTX
2386{
2387 /** Pointer to transfer. */
2388 PSHCLTRANSFER pTransfer;
2389 /** User-supplied context data. Can be NULL if not being used. */
2390 void *pvUser;
2391 /** Pointer to thread function to use. */
2392 PFNSHCLTRANSFERTHREAD pfnThread;
2393} SHCLTRANSFERTHREADCTX;
2394/** Pointer to internal struct for keeping a transfer thread context. */
2395typedef SHCLTRANSFERTHREADCTX *PSHCLTRANSFERTHREADCTX;
2396
2397/**
2398 * Worker thread for a transfer.
2399 *
2400 * @returns VBox status code.
2401 * @param ThreadSelf Thread self handle. Not being used.
2402 * @param pvUser Context data of type PSHCLTRANSFERTHREADCTX.
2403 */
2404static DECLCALLBACK(int) shClTransferThreadWorker(RTTHREAD ThreadSelf, void *pvUser)
2405{
2406 RT_NOREF(ThreadSelf);
2407
2408 LogFlowFuncEnter();
2409
2410 SHCLTRANSFERTHREADCTX Ctx;
2411 memcpy(&Ctx, pvUser, sizeof(SHCLTRANSFERTHREADCTX));
2412
2413 LogFlowFunc(("pfnThread=%p, pTransfer=%p, pvUser=%p\n", Ctx.pfnThread, Ctx.pTransfer, Ctx.pvUser));
2414
2415 PSHCLTRANSFER pTransfer = Ctx.pTransfer;
2416
2417 shClTransferLock(pTransfer);
2418
2419 pTransfer->Thread.fStarted = true;
2420 pTransfer->Thread.fStop = false;
2421
2422 shClTransferUnlock(pTransfer);
2423
2424 RTThreadUserSignal(RTThreadSelf());
2425
2426 int rc = Ctx.pfnThread(pTransfer, Ctx.pvUser);
2427
2428 if (pTransfer->Callbacks.pfnOnCompleted)
2429 pTransfer->Callbacks.pfnOnCompleted(&pTransfer->CallbackCtx, rc);
2430
2431 LogFlowFuncLeaveRC(rc);
2432 return rc;
2433}
2434
2435/**
2436 * Creates a thread for a clipboard transfer.
2437 *
2438 * @returns VBox status code.
2439 * @param pTransfer Clipboard transfer to create thread for.
2440 * @param pfnThreadFunc Thread function to use for this transfer.
2441 * @param pvUser Pointer to user-provided data.
2442 */
2443static int shClTransferThreadCreate(PSHCLTRANSFER pTransfer, PFNSHCLTRANSFERTHREAD pfnThreadFunc, void *pvUser)
2444
2445{
2446 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2447
2448 shClTransferLock(pTransfer);
2449
2450 /* Already marked for stopping? */
2451 AssertMsgReturn(pTransfer->Thread.fStop == false,
2452 ("Transfer thread already marked for stopping"), VERR_WRONG_ORDER);
2453 /* Already started? */
2454 AssertMsgReturn(pTransfer->Thread.fStarted == false,
2455 ("Transfer thread already started"), VERR_WRONG_ORDER);
2456
2457 SHCLTRANSFERTHREADCTX Ctx = { pTransfer, pvUser, pfnThreadFunc };
2458
2459 /* Spawn a worker thread, so that we don't block the window thread for too long. */
2460 int rc = RTThreadCreate(&pTransfer->Thread.hThread, shClTransferThreadWorker,
2461 &Ctx, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
2462 "shcltx");
2463 if (RT_SUCCESS(rc))
2464 {
2465 shClTransferUnlock(pTransfer); /* Leave lock while waiting. */
2466
2467 int rc2 = RTThreadUserWait(pTransfer->Thread.hThread, SHCL_TIMEOUT_DEFAULT_MS);
2468 AssertRC(rc2);
2469
2470 shClTransferLock(pTransfer);
2471
2472 if (pTransfer->Thread.fStarted) /* Did the thread indicate that it started correctly? */
2473 {
2474 /* Nothing to do in here. */
2475 }
2476 else
2477 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
2478 }
2479
2480 shClTransferUnlock(pTransfer);
2481
2482 LogFlowFuncLeaveRC(rc);
2483 return rc;
2484}
2485
2486/**
2487 * Destroys the thread of a clipboard transfer.
2488 *
2489 * @returns VBox status code. Will return thread rc.
2490 * @param pTransfer Clipboard transfer to destroy thread for.
2491 * @param uTimeoutMs Timeout (in ms) to wait for thread destruction.
2492 */
2493static int shClTransferThreadDestroy(PSHCLTRANSFER pTransfer, RTMSINTERVAL uTimeoutMs)
2494{
2495 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2496
2497 shClTransferLock(pTransfer);
2498
2499 if (!pTransfer->Thread.fStarted)
2500 {
2501 shClTransferUnlock(pTransfer);
2502 return VINF_SUCCESS;
2503 }
2504
2505 LogFlowFuncEnter();
2506
2507 /* Set stop indicator. */
2508 pTransfer->Thread.fStop = true;
2509
2510 shClTransferUnlock(pTransfer); /* Leave lock while waiting. */
2511
2512 int rcThread = VERR_IPE_UNINITIALIZED_STATUS;
2513 Assert(pTransfer->Thread.hThread != NIL_RTTHREAD);
2514 int rc = RTThreadWait(pTransfer->Thread.hThread, uTimeoutMs, &rcThread);
2515
2516 LogFlowFunc(("Waiting for thread resulted in %Rrc (thread exited with %Rrc)\n", rc, rcThread));
2517
2518 if (RT_SUCCESS(rc))
2519 {
2520 pTransfer->Thread.fStarted = false;
2521 pTransfer->Thread.hThread = NIL_RTTHREAD;
2522
2523 rc = rcThread; /* Return the thread rc to the caller. */
2524 }
2525 else
2526 LogRel(("Shared Clipboard: Waiting for thread of transfer %RU16 failed with %Rrc\n", pTransfer->State.uID, rc));
2527
2528 LogFlowFuncLeaveRC(rc);
2529 return rc;
2530}
2531
2532/**
2533 * Waits for the transfer status to change, internal version.
2534 *
2535 * @returns VBox status code.
2536 * @param pTransfer Clipboard transfer to wait for.
2537 * @param msTimeout Timeout (in ms) to wait.
2538 * @param penmStatus Where to return the new (current) transfer status on success.
2539 * Optional and can be NULL.
2540 */
2541static int shClTransferWaitForStatusChangeInternal(PSHCLTRANSFER pTransfer, RTMSINTERVAL msTimeout, SHCLTRANSFERSTATUS *penmStatus)
2542{
2543 LogFlowFunc(("Waiting for status change (%RU32 timeout) ...\n", msTimeout));
2544
2545 int rc = RTSemEventWait(pTransfer->StatusChangeEvent, msTimeout);
2546 if (RT_SUCCESS(rc))
2547 {
2548 if (penmStatus)
2549 {
2550 shClTransferLock(pTransfer);
2551
2552 *penmStatus = pTransfer->State.enmStatus;
2553
2554 shClTransferUnlock(pTransfer);
2555 }
2556 }
2557
2558 return rc;
2559}
2560
2561/**
2562 * Waits for the transfer status to change.
2563 *
2564 * @returns VBox status code.
2565 * @param pTransfer Clipboard transfer to wait for.
2566 * @param msTimeout Timeout (in ms) to wait.
2567 * @param penmStatus Where to return the new (current) transfer status on success.
2568 * Optional and can be NULL.
2569 */
2570int ShClTransferWaitForStatusChange(PSHCLTRANSFER pTransfer, RTMSINTERVAL msTimeout, SHCLTRANSFERSTATUS *penmStatus)
2571{
2572 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2573
2574 int rc = shClTransferWaitForStatusChangeInternal(pTransfer, msTimeout, penmStatus);
2575
2576 LogFlowFuncLeaveRC(rc);
2577 return rc;
2578}
2579
2580/**
2581 * Waits for a specific transfer status.
2582 *
2583 * @returns VBox status code.
2584 * @param pTransfer Clipboard transfer to wait for.
2585 * @param msTimeout Timeout (in ms) to wait.
2586 * @param enmStatus Transfer status to wait for.
2587 */
2588int ShClTransferWaitForStatus(PSHCLTRANSFER pTransfer, RTMSINTERVAL msTimeout, SHCLTRANSFERSTATUS enmStatus)
2589{
2590 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2591
2592 int rc = VINF_SUCCESS;
2593
2594 uint64_t const tsStartMs = RTTimeMilliTS();
2595 uint64_t msLeft = msTimeout;
2596 for (;;)
2597 {
2598 SHCLTRANSFERSTATUS enmCurStatus;
2599 rc = shClTransferWaitForStatusChangeInternal(pTransfer, msLeft, &enmCurStatus);
2600 if (RT_FAILURE(rc))
2601 break;
2602
2603 if (enmCurStatus == enmStatus)
2604 break;
2605
2606 msLeft -= RT_MIN(msLeft, RTTimeMilliTS() - tsStartMs);
2607 if (msLeft == 0)
2608 {
2609 rc = VERR_TIMEOUT;
2610 break;
2611 }
2612 }
2613
2614 LogFlowFuncLeaveRC(rc);
2615 return rc;
2616}
2617
2618
2619/*********************************************************************************************************************************
2620 * Transfer Context *
2621 ********************************************************************************************************************************/
2622
2623/**
2624 * Locks a transfer context.
2625 *
2626 * @param pTransferCtx Transfer context to lock.
2627 */
2628DECLINLINE(void) shClTransferCtxLock(PSHCLTRANSFERCTX pTransferCtx)
2629{
2630 int rc2 = RTCritSectEnter(&pTransferCtx->CritSect);
2631 AssertRC(rc2);
2632}
2633
2634/**
2635 * Unlocks a transfer context.
2636 *
2637 * @param pTransferCtx Transfer context to unlock.
2638 */
2639DECLINLINE(void) shClTransferCtxUnlock(PSHCLTRANSFERCTX pTransferCtx)
2640{
2641 int rc2 = RTCritSectLeave(&pTransferCtx->CritSect);
2642 AssertRC(rc2);
2643}
2644
2645/**
2646 * Initializes a clipboard transfer context.
2647 *
2648 * @returns VBox status code.
2649 * @param pTransferCtx Transfer context to initialize.
2650 */
2651int ShClTransferCtxInit(PSHCLTRANSFERCTX pTransferCtx)
2652{
2653 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2654
2655 LogFlowFunc(("pTransferCtx=%p\n", pTransferCtx));
2656
2657 int rc = RTCritSectInit(&pTransferCtx->CritSect);
2658 if (RT_SUCCESS(rc))
2659 {
2660 rc = RTSemEventCreate(&pTransferCtx->ChangedEvent);
2661 if (RT_SUCCESS(rc))
2662 {
2663 RT_ZERO(pTransferCtx->ChangedEventData);
2664
2665 RTListInit(&pTransferCtx->List);
2666
2667 pTransferCtx->cTransfers = 0;
2668 pTransferCtx->cRunning = 0;
2669 pTransferCtx->cMaxRunning = 64; /** @todo Make this configurable? */
2670
2671 RT_ZERO(pTransferCtx->bmTransferIds);
2672
2673 ShClTransferCtxReset(pTransferCtx);
2674 }
2675 }
2676
2677 return VINF_SUCCESS;
2678}
2679
2680/**
2681 * Destroys a clipboard transfer context.
2682 *
2683 * @param pTransferCtx Transfer context to destroy.
2684 */
2685void ShClTransferCtxDestroy(PSHCLTRANSFERCTX pTransferCtx)
2686{
2687 if (!pTransferCtx)
2688 return;
2689
2690 LogFlowFunc(("pTransferCtx=%p\n", pTransferCtx));
2691
2692 shClTransferCtxLock(pTransferCtx);
2693
2694 PSHCLTRANSFER pTransfer, pTransferNext;
2695 RTListForEachSafe(&pTransferCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
2696 {
2697 shclTransferCtxTransferRemoveAndUnregister(pTransferCtx, pTransfer);
2698 ShClTransferDestroy(pTransfer);
2699 }
2700
2701 pTransferCtx->cRunning = 0;
2702 pTransferCtx->cTransfers = 0;
2703
2704 shClTransferCtxUnlock(pTransferCtx);
2705
2706 RTSemEventDestroy(pTransferCtx->ChangedEvent);
2707 pTransferCtx->ChangedEvent = NIL_RTSEMEVENT;
2708
2709 if (RTCritSectIsInitialized(&pTransferCtx->CritSect))
2710 RTCritSectDelete(&pTransferCtx->CritSect);
2711}
2712
2713/**
2714 * Resets a clipboard transfer context.
2715 *
2716 * @param pTransferCtx Transfer context to reset.
2717 */
2718void ShClTransferCtxReset(PSHCLTRANSFERCTX pTransferCtx)
2719{
2720 AssertPtrReturnVoid(pTransferCtx);
2721
2722 shClTransferCtxLock(pTransferCtx);
2723
2724 LogFlowFuncEnter();
2725
2726 PSHCLTRANSFER pTransfer;
2727 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node)
2728 ShClTransferReset(pTransfer);
2729
2730#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
2731 /** @todo Anything to do here? */
2732#endif
2733
2734 shClTransferCtxUnlock(pTransferCtx);
2735}
2736
2737/**
2738 * Signals a change event.
2739 *
2740 * @returns VBox status code.
2741 * @param pTransferCtx Transfer context to return transfer for.
2742 * @param fRegistered Whether a transfer got registered or unregistered.
2743 * @param pTransfer Transfer bound to the event.
2744 */
2745static int shClTransferCtxSignal(PSHCLTRANSFERCTX pTransferCtx, bool fRegistered, PSHCLTRANSFER pTransfer)
2746{
2747 Assert(RTCritSectIsOwner(&pTransferCtx->CritSect));
2748
2749 LogFlowFunc(("fRegistered=%RTbool, pTransfer=%p\n", fRegistered, pTransfer));
2750
2751 pTransferCtx->ChangedEventData.fRegistered = fRegistered;
2752 pTransferCtx->ChangedEventData.pTransfer = pTransfer;
2753
2754 return RTSemEventSignal(pTransferCtx->ChangedEvent);
2755}
2756
2757/**
2758 * Returns a specific clipboard transfer, internal version.
2759 *
2760 * @returns Clipboard transfer found, or NULL if not found.
2761 * @param pTransferCtx Transfer context to return transfer for.
2762 * @param idTransfer ID of the transfer to return.
2763 *
2764 * @note Caller needs to take critical section.
2765 */
2766static PSHCLTRANSFER shClTransferCtxGetTransferByIdInternal(PSHCLTRANSFERCTX pTransferCtx, SHCLTRANSFERID idTransfer)
2767{
2768 Assert(RTCritSectIsOwner(&pTransferCtx->CritSect));
2769
2770 PSHCLTRANSFER pTransfer;
2771 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node) /** @todo Slow, but works for now. */
2772 {
2773 if (pTransfer->State.uID == idTransfer)
2774 return pTransfer;
2775 }
2776
2777 return NULL;
2778}
2779
2780/**
2781 * Returns a specific clipboard transfer by index, internal version.
2782 *
2783 * @returns Clipboard transfer found, or NULL if not found.
2784 * @param pTransferCtx Transfer context to return transfer for.
2785 * @param uIdx Index of the transfer to return.
2786 *
2787 * @note Caller needs to take critical section.
2788 */
2789static PSHCLTRANSFER shClTransferCtxGetTransferByIndexInternal(PSHCLTRANSFERCTX pTransferCtx, uint32_t uIdx)
2790{
2791 Assert(RTCritSectIsOwner(&pTransferCtx->CritSect));
2792
2793 uint32_t idx = 0;
2794
2795 PSHCLTRANSFER pTransfer;
2796 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node) /** @todo Slow, but works for now. */
2797 {
2798 if (uIdx == idx)
2799 return pTransfer;
2800 idx++;
2801 }
2802
2803 return NULL;
2804}
2805
2806/**
2807 * Returns a clipboard transfer for a specific transfer ID.
2808 *
2809 * @returns Clipboard transfer found, or NULL if not found.
2810 * @param pTransferCtx Transfer context to return transfer for.
2811 * @param uID ID of the transfer to return.
2812 */
2813PSHCLTRANSFER ShClTransferCtxGetTransferById(PSHCLTRANSFERCTX pTransferCtx, uint32_t uID)
2814{
2815 shClTransferCtxLock(pTransferCtx);
2816
2817 PSHCLTRANSFER const pTransfer = shClTransferCtxGetTransferByIdInternal(pTransferCtx, uID);
2818
2819 shClTransferCtxUnlock(pTransferCtx);
2820
2821 return pTransfer;
2822}
2823
2824/**
2825 * Returns a clipboard transfer for a specific list index.
2826 *
2827 * @returns Clipboard transfer found, or NULL if not found.
2828 * @param pTransferCtx Transfer context to return transfer for.
2829 * @param uIdx List index of the transfer to return.
2830 */
2831PSHCLTRANSFER ShClTransferCtxGetTransferByIndex(PSHCLTRANSFERCTX pTransferCtx, uint32_t uIdx)
2832{
2833 shClTransferCtxLock(pTransferCtx);
2834
2835 PSHCLTRANSFER const pTransfer = shClTransferCtxGetTransferByIndexInternal(pTransferCtx, uIdx);
2836
2837 shClTransferCtxUnlock(pTransferCtx);
2838
2839 return pTransfer;
2840}
2841
2842
2843/**
2844 * Returns the last clipboard transfer registered.
2845 *
2846 * @returns Clipboard transfer found, or NULL if not found.
2847 * @param pTransferCtx Transfer context to return transfer for.
2848 */
2849PSHCLTRANSFER ShClTransferCtxGetTransferLast(PSHCLTRANSFERCTX pTransferCtx)
2850{
2851 shClTransferCtxLock(pTransferCtx);
2852
2853 PSHCLTRANSFER const pTransfer = RTListGetLast(&pTransferCtx->List, SHCLTRANSFER, Node);
2854
2855 shClTransferCtxUnlock(pTransferCtx);
2856
2857 return pTransfer;
2858}
2859
2860/**
2861 * Returns the number of running clipboard transfers for a given transfer context.
2862 *
2863 * @returns Number of running transfers.
2864 * @param pTransferCtx Transfer context to return number for.
2865 */
2866uint32_t ShClTransferCtxGetRunningTransfers(PSHCLTRANSFERCTX pTransferCtx)
2867{
2868 AssertPtrReturn(pTransferCtx, 0);
2869
2870 shClTransferCtxLock(pTransferCtx);
2871
2872 uint32_t const cRunning = pTransferCtx->cRunning;
2873
2874 shClTransferCtxUnlock(pTransferCtx);
2875
2876 return cRunning;
2877}
2878
2879/**
2880 * Returns the number of total clipboard transfers for a given transfer context.
2881 *
2882 * @returns Number of total transfers.
2883 * @param pTransferCtx Transfer context to return number for.
2884 */
2885uint32_t ShClTransferCtxGetTotalTransfers(PSHCLTRANSFERCTX pTransferCtx)
2886{
2887 AssertPtrReturn(pTransferCtx, 0);
2888
2889 shClTransferCtxLock(pTransferCtx);
2890
2891 uint32_t const cTransfers = pTransferCtx->cTransfers;
2892
2893 shClTransferCtxUnlock(pTransferCtx);
2894
2895 return cTransfers;
2896}
2897
2898static int shClTransferCreateIDInternal(PSHCLTRANSFERCTX pTransferCtx, SHCLTRANSFERID *pidTransfer)
2899{
2900 /*
2901 * Pick a random bit as starting point. If it's in use, search forward
2902 * for a free one, wrapping around. We've reserved both the zero'th and
2903 * max-1 IDs.
2904 */
2905 SHCLTRANSFERID idTransfer = RTRandU32Ex(1, VBOX_SHCL_MAX_TRANSFERS - 2);
2906
2907 if (!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer))
2908 { /* likely */ }
2909 else if (pTransferCtx->cTransfers < VBOX_SHCL_MAX_TRANSFERS - 2 /* First and last are not used */)
2910 {
2911 /* Forward search. */
2912 int iHit = ASMBitNextClear(&pTransferCtx->bmTransferIds[0], VBOX_SHCL_MAX_TRANSFERS, idTransfer);
2913 if (iHit < 0)
2914 iHit = ASMBitFirstClear(&pTransferCtx->bmTransferIds[0], VBOX_SHCL_MAX_TRANSFERS);
2915 AssertLogRelMsgReturn(iHit >= 0, ("Transfer count: %RU16\n", pTransferCtx->cTransfers), VERR_SHCLPB_MAX_TRANSFERS_REACHED);
2916 idTransfer = iHit;
2917 AssertLogRelMsgReturn(!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer), ("idObject=%#x\n", idTransfer), VERR_INTERNAL_ERROR_2);
2918 }
2919 else
2920 {
2921 LogFunc(("Maximum number of transfers reached (%RU16 transfers)\n", pTransferCtx->cTransfers));
2922 return VERR_SHCLPB_MAX_TRANSFERS_REACHED;
2923 }
2924
2925 *pidTransfer = idTransfer;
2926
2927 return VINF_SUCCESS;
2928}
2929
2930/**
2931 * Creates a new transfer ID for a given transfer context.
2932 *
2933 * @returns VBox status code.
2934 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers is reached.
2935 * @param pTransferCtx Transfer context to create transfer ID for.
2936 * @param pidTransfer Where to return the transfer ID on success.
2937 */
2938int ShClTransferCtxCreateId(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFERID pidTransfer)
2939{
2940 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2941 AssertPtrReturn(pidTransfer, VERR_INVALID_POINTER);
2942
2943 shClTransferCtxLock(pTransferCtx);
2944
2945 int rc = shClTransferCreateIDInternal(pTransferCtx, pidTransfer);
2946
2947 shClTransferCtxUnlock(pTransferCtx);
2948
2949 return rc;
2950}
2951
2952/**
2953 * Registers a clipboard transfer with a new transfer ID.
2954 *
2955 * @return VBox status code.
2956 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers is reached.
2957 * @param pTransferCtx Transfer context to register transfer to.
2958 * @param pTransfer Transfer to register. The context takes ownership of the transfer on success.
2959 * @param idTransfer Transfer ID to use for registering the given transfer.
2960 */
2961static int shClTransferCtxTransferRegisterExInternal(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, SHCLTRANSFERID idTransfer)
2962{
2963 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2964 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2965 Assert(idTransfer != NIL_SHCLTRANSFERID);
2966
2967 shClTransferCtxLock(pTransferCtx);
2968
2969 pTransfer->State.uID = idTransfer;
2970
2971 RTListAppend(&pTransferCtx->List, &pTransfer->Node);
2972
2973 pTransferCtx->cTransfers++;
2974
2975 Log2Func(("pTransfer=%p, idTransfer=%RU32 -- now %RU16 transfer(s)\n", pTransfer, idTransfer, pTransferCtx->cTransfers));
2976
2977 shClTransferCtxUnlock(pTransferCtx);
2978
2979 if (pTransfer->Callbacks.pfnOnRegistered)
2980 pTransfer->Callbacks.pfnOnRegistered(&pTransfer->CallbackCtx, pTransferCtx);
2981
2982 LogFlowFuncLeaveRC(VINF_SUCCESS);
2983 return VINF_SUCCESS;
2984}
2985
2986/**
2987 * Registers a clipboard transfer with a transfer context, i.e. allocates a transfer ID.
2988 *
2989 * @return VBox status code.
2990 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers for this context has been reached.
2991 * @param pTransferCtx Transfer context to register transfer to.
2992 * @param pTransfer Transfer to register. The context takes ownership of the transfer on success.
2993 * @param pidTransfer Where to return the transfer ID on success. Optional.
2994 */
2995int ShClTransferCtxRegister(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, PSHCLTRANSFERID pidTransfer)
2996{
2997 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2998 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2999
3000 shClTransferCtxLock(pTransferCtx);
3001
3002 SHCLTRANSFERID idTransfer;
3003 int rc = shClTransferCreateIDInternal(pTransferCtx, &idTransfer);
3004 if (RT_SUCCESS(rc))
3005 {
3006 rc = shClTransferCtxTransferRegisterExInternal(pTransferCtx, pTransfer, idTransfer);
3007 if (RT_SUCCESS(rc))
3008 {
3009 if (pidTransfer)
3010 *pidTransfer = idTransfer;
3011 }
3012 }
3013
3014 shClTransferCtxUnlock(pTransferCtx);
3015
3016 return rc;
3017}
3018
3019/**
3020 * Registers a clipboard transfer with a transfer context by specifying an ID for the transfer.
3021 *
3022 * @return VBox status code.
3023 * @retval VERR_ALREADY_EXISTS if a transfer with the given ID already exists.
3024 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers for this context has been reached.
3025 * @param pTransferCtx Transfer context to register transfer to.
3026 * @param pTransfer Transfer to register.
3027 * @param idTransfer Transfer ID to use for registration.
3028 *
3029 * @note This function ASSUMES you have created \a idTransfer with ShClTransferCtxCreateId().
3030 */
3031int ShClTransferCtxRegisterById(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, SHCLTRANSFERID idTransfer)
3032{
3033 shClTransferCtxLock(pTransferCtx);
3034
3035 if (pTransferCtx->cTransfers < VBOX_SHCL_MAX_TRANSFERS - 2 /* First and last are not used */)
3036 {
3037 RTListAppend(&pTransferCtx->List, &pTransfer->Node);
3038
3039 shClTransferLock(pTransfer);
3040
3041 pTransfer->State.uID = idTransfer;
3042
3043 shClTransferUnlock(pTransfer);
3044
3045 int rc = shClTransferCtxSignal(pTransferCtx, true /* fRegistered */, pTransfer);
3046
3047 pTransferCtx->cTransfers++;
3048
3049 LogFunc(("Registered transfer ID %RU16 -- now %RU16 transfers total\n", idTransfer, pTransferCtx->cTransfers));
3050
3051 shClTransferCtxUnlock(pTransferCtx);
3052
3053 if (pTransfer->Callbacks.pfnOnRegistered)
3054 pTransfer->Callbacks.pfnOnRegistered(&pTransfer->CallbackCtx, pTransferCtx);
3055
3056 return rc;
3057 }
3058
3059 LogFunc(("Maximum number of transfers reached (%RU16 transfers)\n", pTransferCtx->cTransfers));
3060
3061 shClTransferCtxUnlock(pTransferCtx);
3062
3063 return VERR_SHCLPB_MAX_TRANSFERS_REACHED;
3064}
3065
3066/**
3067 * Removes and unregisters a transfer from a transfer context.
3068 *
3069 * @param pTransferCtx Transfer context to remove transfer from.
3070 * @param pTransfer Transfer to remove.
3071 *
3072 * @note Caller needs to take critical section.
3073 */
3074static void shclTransferCtxTransferRemoveAndUnregister(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer)
3075{
3076 Assert(RTCritSectIsOwner(&pTransferCtx->CritSect));
3077
3078 RTListNodeRemove(&pTransfer->Node);
3079
3080 Assert(pTransferCtx->cTransfers);
3081 pTransferCtx->cTransfers--;
3082
3083 Assert(pTransferCtx->cTransfers >= pTransferCtx->cRunning);
3084
3085 shClTransferCtxUnlock(pTransferCtx);
3086
3087 if (pTransfer->Callbacks.pfnOnUnregistered)
3088 pTransfer->Callbacks.pfnOnUnregistered(&pTransfer->CallbackCtx, pTransferCtx);
3089
3090 shClTransferCtxLock(pTransferCtx);
3091
3092 LogFlowFunc(("Now %RU32 transfers left\n", pTransferCtx->cTransfers));
3093}
3094
3095/**
3096 * Unregisters a transfer from an transfer context, given by its ID.
3097 *
3098 * @retval VINF_SUCCESS on success.
3099 * @retval VERR_NOT_FOUND if the transfer ID was not found.
3100 * @param pTransferCtx Transfer context to unregister transfer from.
3101 * @param idTransfer Transfer ID to unregister.
3102 */
3103int ShClTransferCtxUnregisterById(PSHCLTRANSFERCTX pTransferCtx, SHCLTRANSFERID idTransfer)
3104{
3105 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
3106 AssertReturn(idTransfer, VERR_INVALID_PARAMETER);
3107
3108 shClTransferCtxLock(pTransferCtx);
3109
3110 int rc = VINF_SUCCESS;
3111
3112 LogFlowFunc(("idTransfer=%RU32\n", idTransfer));
3113
3114 if (ASMBitTestAndClear(&pTransferCtx->bmTransferIds, idTransfer))
3115 {
3116 PSHCLTRANSFER pTransfer = shClTransferCtxGetTransferByIdInternal(pTransferCtx, idTransfer);
3117 if (pTransfer)
3118 {
3119 shclTransferCtxTransferRemoveAndUnregister(pTransferCtx, pTransfer);
3120
3121 rc = shClTransferCtxSignal(pTransferCtx, false /* fRegistered */, pTransfer);
3122 }
3123 }
3124 else
3125 rc = VERR_NOT_FOUND;
3126
3127 shClTransferCtxUnlock(pTransferCtx);
3128
3129 LogFlowFuncLeaveRC(rc);
3130 return rc;
3131}
3132
3133/**
3134 * Waits for a transfer context event.
3135 *
3136 * @returns VBox status code.
3137 * @param pTransferCtx Transfer context to wait for.
3138 * @param msTimeout Timeout (in ms) to wait.
3139 * @param pEvent Where to return the event data on success.
3140 */
3141static int shClTransferCtxWaitInternal(PSHCLTRANSFERCTX pTransferCtx, RTMSINTERVAL msTimeout, PSHCLTRANSFERCTXEVENT pEvent)
3142{
3143 LogFlowFunc(("Waiting for transfer context change (%RU32 timeout) ...\n", msTimeout));
3144
3145 int rc = RTSemEventWait(pTransferCtx->ChangedEvent, msTimeout);
3146 if (RT_SUCCESS(rc))
3147 {
3148 shClTransferCtxLock(pTransferCtx);
3149
3150 memcpy(pEvent, &pTransferCtx->ChangedEventData, sizeof(SHCLTRANSFERCTXEVENT));
3151
3152 shClTransferCtxUnlock(pTransferCtx);
3153 }
3154
3155 LogFlowFuncLeaveRC(rc);
3156 return rc;
3157}
3158
3159/**
3160 * Waits for transfer to be (un-)registered.
3161 *
3162 * @returns VBox status code.
3163 * @param pTransferCtx Transfer context to wait for.
3164 * @param msTimeout Timeout (in ms) to wait.
3165 * @param fRegister Pass \c true for registering, or \c false for unregistering a transfer.
3166 * @param idTransfer Transfer ID to wait for.
3167 * Pass NIL_SHCLTRANSFERID for any transfer.
3168 * @param ppTransfer Where to return the transfer being (un-)registered. Optional and can be NULL.
3169 */
3170int ShClTransferCtxWait(PSHCLTRANSFERCTX pTransferCtx, RTMSINTERVAL msTimeout, bool fRegister, SHCLTRANSFERID idTransfer,
3171 PSHCLTRANSFER *ppTransfer)
3172{
3173 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
3174
3175 int rc = VERR_TIMEOUT;
3176
3177 uint64_t const tsStartMs = RTTimeMilliTS();
3178 uint64_t msLeft = msTimeout;
3179 for (;;)
3180 {
3181 SHCLTRANSFERCTXEVENT Event;
3182 rc = shClTransferCtxWaitInternal(pTransferCtx, msLeft, &Event);
3183 if (RT_FAILURE(rc))
3184 break;
3185
3186 shClTransferCtxLock(pTransferCtx);
3187
3188 if (Event.fRegistered == fRegister)
3189 {
3190 if ( idTransfer != NIL_SHCLTRANSFERID
3191 && Event.pTransfer
3192 && ShClTransferGetID(Event.pTransfer) == idTransfer)
3193 {
3194 if (ppTransfer)
3195 *ppTransfer = Event.pTransfer;
3196 rc = VINF_SUCCESS;
3197 }
3198 }
3199
3200 shClTransferCtxUnlock(pTransferCtx);
3201
3202 if (RT_SUCCESS(rc))
3203 break;
3204
3205 msLeft -= RT_MIN(msLeft, RTTimeMilliTS() - tsStartMs);
3206 if (msLeft == 0)
3207 break;
3208 }
3209
3210 LogFlowFuncLeaveRC(rc);
3211 return rc;
3212}
3213
3214/**
3215 * Cleans up all associated transfers which are not needed (anymore).
3216 * This can be due to transfers which only have been announced but not / never being run.
3217 *
3218 * @param pTransferCtx Transfer context to cleanup transfers for.
3219 */
3220void ShClTransferCtxCleanup(PSHCLTRANSFERCTX pTransferCtx)
3221{
3222 AssertPtrReturnVoid(pTransferCtx);
3223
3224 shClTransferCtxLock(pTransferCtx);
3225
3226 LogFlowFunc(("pTransferCtx=%p, cTransfers=%RU16 cRunning=%RU16\n",
3227 pTransferCtx, pTransferCtx->cTransfers, pTransferCtx->cRunning));
3228
3229 if (pTransferCtx->cTransfers == 0)
3230 {
3231 shClTransferCtxUnlock(pTransferCtx);
3232 return;
3233 }
3234
3235 /* Remove all transfers which are not in a running state (e.g. only announced). */
3236 PSHCLTRANSFER pTransfer, pTransferNext;
3237 RTListForEachSafe(&pTransferCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
3238 {
3239 shClTransferLock(pTransfer);
3240
3241 SHCLTRANSFERSTATUS const enmStatus = shClTransferGetStatusLocked(pTransfer);
3242 LogFlowFunc(("\tTransfer #%RU16: %s\n", pTransfer->State.uID, ShClTransferStatusToStr(enmStatus)));
3243
3244 if (enmStatus != SHCLTRANSFERSTATUS_STARTED)
3245 {
3246 shClTransferUnlock(pTransfer);
3247
3248 shclTransferCtxTransferRemoveAndUnregister(pTransferCtx, pTransfer);
3249 ShClTransferDestroy(pTransfer);
3250 }
3251 else
3252 shClTransferUnlock(pTransfer);
3253 }
3254
3255 shClTransferCtxUnlock(pTransferCtx);
3256}
3257
3258/**
3259 * Returns whether the maximum of concurrent transfers of a specific transfer contexthas been reached or not.
3260 *
3261 * @returns \c if maximum has been reached, \c false if not.
3262 * @param pTransferCtx Transfer context to determine value for.
3263 */
3264bool ShClTransferCtxIsMaximumReached(PSHCLTRANSFERCTX pTransferCtx)
3265{
3266 AssertPtrReturn(pTransferCtx, true);
3267
3268 shClTransferCtxLock(pTransferCtx);
3269
3270 LogFlowFunc(("cRunning=%RU32, cMaxRunning=%RU32\n", pTransferCtx->cRunning, pTransferCtx->cMaxRunning));
3271
3272 Assert(pTransferCtx->cRunning <= pTransferCtx->cMaxRunning);
3273 bool const fMaximumReached = pTransferCtx->cRunning == pTransferCtx->cMaxRunning;
3274
3275 shClTransferCtxUnlock(pTransferCtx);
3276
3277 return fMaximumReached;
3278}
3279
3280/**
3281 * Copies file system objinfo from IPRT to Shared Clipboard format.
3282 *
3283 * @return VBox status code.
3284 * @param pDst The Shared Clipboard structure to convert data to.
3285 * @param pSrc The IPRT structure to convert data from.
3286 */
3287int ShClFsObjInfoFromIPRT(PSHCLFSOBJINFO pDst, PCRTFSOBJINFO pSrc)
3288{
3289 AssertPtrReturn(pDst, VERR_INVALID_POINTER);
3290 AssertPtrReturn(pSrc, VERR_INVALID_POINTER);
3291
3292 pDst->cbObject = pSrc->cbObject;
3293 pDst->cbAllocated = pSrc->cbAllocated;
3294 pDst->AccessTime = pSrc->AccessTime;
3295 pDst->ModificationTime = pSrc->ModificationTime;
3296 pDst->ChangeTime = pSrc->ChangeTime;
3297 pDst->BirthTime = pSrc->BirthTime;
3298 pDst->Attr.fMode = pSrc->Attr.fMode;
3299 /* Clear bits which we don't pass through for security reasons. */
3300 pDst->Attr.fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
3301 RT_ZERO(pDst->Attr.u);
3302 switch (pSrc->Attr.enmAdditional)
3303 {
3304 default:
3305 RT_FALL_THROUGH();
3306 case RTFSOBJATTRADD_NOTHING:
3307 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_NOTHING;
3308 break;
3309
3310 case RTFSOBJATTRADD_UNIX:
3311 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_UNIX;
3312 pDst->Attr.u.Unix.uid = pSrc->Attr.u.Unix.uid;
3313 pDst->Attr.u.Unix.gid = pSrc->Attr.u.Unix.gid;
3314 pDst->Attr.u.Unix.cHardlinks = pSrc->Attr.u.Unix.cHardlinks;
3315 pDst->Attr.u.Unix.INodeIdDevice = pSrc->Attr.u.Unix.INodeIdDevice;
3316 pDst->Attr.u.Unix.INodeId = pSrc->Attr.u.Unix.INodeId;
3317 pDst->Attr.u.Unix.fFlags = pSrc->Attr.u.Unix.fFlags;
3318 pDst->Attr.u.Unix.GenerationId = pSrc->Attr.u.Unix.GenerationId;
3319 pDst->Attr.u.Unix.Device = pSrc->Attr.u.Unix.Device;
3320 break;
3321
3322 case RTFSOBJATTRADD_EASIZE:
3323 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_EASIZE;
3324 pDst->Attr.u.EASize.cb = pSrc->Attr.u.EASize.cb;
3325 break;
3326 }
3327
3328 return VINF_SUCCESS;
3329}
3330
3331/**
3332 * Queries local file system information from a given path.
3333 *
3334 * @returns VBox status code.
3335 * @param pszPath Path to query file system information for.
3336 * @param pObjInfo Where to return the queried file system information on success.
3337 */
3338int ShClFsObjInfoQueryLocal(const char *pszPath, PSHCLFSOBJINFO pObjInfo)
3339{
3340 RTFSOBJINFO objInfo;
3341 int rc = RTPathQueryInfo(pszPath, &objInfo,
3342#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
3343 RTFSOBJATTRADD_NOTHING
3344#else
3345 RTFSOBJATTRADD_UNIX
3346#endif
3347 );
3348 if (RT_SUCCESS(rc))
3349 rc = ShClFsObjInfoFromIPRT(pObjInfo, &objInfo);
3350
3351 return rc;
3352}
3353
3354/**
3355 * Translates a clipboard transfer status (SHCLTRANSFERSTATUS_XXX) into a string.
3356 *
3357 * @returns Transfer status string name.
3358 * @param enmStatus The transfer status to translate.
3359 */
3360const char *ShClTransferStatusToStr(SHCLTRANSFERSTATUS enmStatus)
3361{
3362 switch (enmStatus)
3363 {
3364 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_NONE);
3365 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_REQUESTED);
3366 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_INITIALIZED);
3367 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_UNINITIALIZED);
3368 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_STARTED);
3369 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_COMPLETED);
3370 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_CANCELED);
3371 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_KILLED);
3372 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_ERROR);
3373 }
3374 return "Unknown";
3375}
3376
3377/**
3378 * Transforms a path so that it can be sent over to the other party.
3379 *
3380 * @returns VBox status code.
3381 * @param pszPath Path to transform. Will be modified in place.
3382 * @param cbPath Size (in bytes) of \a pszPath.
3383 *
3384 * @note Shared Clipboard file paths always are sent over as UNIX-style paths.
3385 * Sending over back slashes ('\') could happen on non-Windows OSes as part of a path or file name.
3386 */
3387int ShClTransferTransformPath(char *pszPath, size_t cbPath)
3388{
3389#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
3390 RT_NOREF(cbPath);
3391 RTPathChangeToUnixSlashes(pszPath, true /* fForce */);
3392#else
3393 RT_NOREF(pszPath, cbPath);
3394#endif
3395 return VINF_SUCCESS;
3396}
3397
3398/**
3399 * Validates whether a given path matches our set of rules or not.
3400 *
3401 * @returns VBox status code.
3402 * @param pcszPath Path to validate.
3403 * @param fMustExist Whether the path to validate also must exist.
3404 */
3405int ShClTransferValidatePath(const char *pcszPath, bool fMustExist)
3406{
3407 int rc = VINF_SUCCESS;
3408
3409 if (!strlen(pcszPath))
3410 rc = VERR_INVALID_PARAMETER;
3411
3412 if ( RT_SUCCESS(rc)
3413 && !RTStrIsValidEncoding(pcszPath))
3414 {
3415 rc = VERR_INVALID_UTF8_ENCODING;
3416 }
3417
3418 if ( RT_SUCCESS(rc)
3419 && RTStrStr(pcszPath, ".."))
3420 {
3421 rc = VERR_INVALID_PARAMETER;
3422 }
3423
3424 if ( RT_SUCCESS(rc)
3425 && fMustExist)
3426 {
3427 RTFSOBJINFO objInfo;
3428 rc = RTPathQueryInfo(pcszPath, &objInfo, RTFSOBJATTRADD_NOTHING);
3429 if (RT_SUCCESS(rc))
3430 {
3431 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
3432 {
3433 if (!RTDirExists(pcszPath)) /* Path must exist. */
3434 rc = VERR_PATH_NOT_FOUND;
3435 }
3436 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
3437 {
3438 if (!RTFileExists(pcszPath)) /* File must exist. */
3439 rc = VERR_FILE_NOT_FOUND;
3440 }
3441 else /* Everything else (e.g. symbolic links) are not supported. */
3442 {
3443 LogRelMax(64, ("Shared Clipboard: Path '%s' contains a symbolic link or junction, which are not supported\n", pcszPath));
3444 rc = VERR_NOT_SUPPORTED;
3445 }
3446 }
3447 }
3448
3449 if (RT_FAILURE(rc))
3450 LogRelMax(64, ("Shared Clipboard: Validating path '%s' failed: %Rrc\n", pcszPath, rc));
3451
3452 LogFlowFuncLeaveRC(rc);
3453 return rc;
3454}
3455
3456/**
3457 * Resolves a relative path of a specific transfer to its absolute path.
3458 *
3459 * @returns VBox status code.
3460 * @param pTransfer Clipboard transfer to resolve path for.
3461 * @param pszPath Relative path to resolve.
3462 * Paths have to end with a (back-)slash, otherwise this is considered to be a file.
3463 * @param fFlags Resolve flags. Currently not used and must be 0.
3464 * @param ppszResolved Where to store the allocated resolved path. Must be free'd by the called using RTStrFree().
3465 */
3466int ShClTransferResolvePathAbs(PSHCLTRANSFER pTransfer, const char *pszPath, uint32_t fFlags, char **ppszResolved)
3467{
3468 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
3469 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
3470 AssertReturn (fFlags == 0, VERR_INVALID_PARAMETER);
3471 AssertPtrReturn(ppszResolved, VERR_INVALID_POINTER);
3472
3473 LogFlowFunc(("pszPath=%s, fFlags=%#x (pszPathRootAbs=%s, cRootListEntries=%RU64)\n",
3474 pszPath, fFlags, pTransfer->pszPathRootAbs, pTransfer->lstRoots.Hdr.cEntries));
3475
3476 int rc = ShClTransferValidatePath(pszPath, false /* fMustExist */);
3477 if (RT_SUCCESS(rc))
3478 {
3479 rc = VERR_PATH_NOT_FOUND; /* Play safe by default. */
3480
3481 PSHCLLISTENTRY pEntry;
3482 RTListForEach(&pTransfer->lstRoots.lstEntries, pEntry, SHCLLISTENTRY, Node)
3483 {
3484 LogFlowFunc(("\tpEntry->pszName=%s\n", pEntry->pszName));
3485
3486 if (RTStrStartsWith(pszPath, pEntry->pszName)) /* Case-sensitive! */
3487 {
3488 rc = VINF_SUCCESS;
3489 break;
3490 }
3491 }
3492
3493 if (RT_SUCCESS(rc))
3494 {
3495 char *pszPathAbs = RTPathJoinA(pTransfer->pszPathRootAbs, pszPath);
3496 if (pszPathAbs)
3497 {
3498 char szResolved[RTPATH_MAX];
3499 size_t cbResolved = sizeof(szResolved);
3500 rc = RTPathAbsEx(pTransfer->pszPathRootAbs, pszPathAbs, RTPATH_STR_F_STYLE_HOST, szResolved, &cbResolved);
3501
3502 RTStrFree(pszPathAbs);
3503 pszPathAbs = NULL;
3504
3505 if (RT_SUCCESS(rc))
3506 {
3507 LogRel2(("Shared Clipboard: Resolved: '%s' -> '%s'\n", pszPath, szResolved));
3508
3509 *ppszResolved = RTStrDup(szResolved);
3510 }
3511 }
3512 else
3513 rc = VERR_NO_MEMORY;
3514 }
3515 }
3516
3517 if (RT_FAILURE(rc))
3518 LogRel(("Shared Clipboard: Resolving absolute path for '%s' failed, rc=%Rrc\n", pszPath, rc));
3519
3520 LogFlowFuncLeaveRC(rc);
3521 return rc;
3522}
3523
3524/**
3525 * Converts Shared Clipboard create flags (see SharedClipboard-transfers.h) into IPRT create flags.
3526 *
3527 * @returns IPRT status code.
3528 * @param fShClFlags Shared clipboard create flags.
3529 * @param[out] pfOpen Where to store the RTFILE_O_XXX flags for
3530 * RTFileOpen.
3531 *
3532 * @sa Initially taken from vbsfConvertFileOpenFlags().
3533 */
3534int ShClTransferConvertFileCreateFlags(uint32_t fShClFlags, uint64_t *pfOpen)
3535{
3536 AssertMsgReturnStmt(!(fShClFlags & ~SHCL_OBJ_CF_VALID_MASK), ("%#x4\n", fShClFlags), *pfOpen = 0, VERR_INVALID_FLAGS);
3537
3538 uint64_t fOpen = 0;
3539
3540 switch (fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_RW)
3541 {
3542 case SHCL_OBJ_CF_ACCESS_NONE:
3543 {
3544#ifdef RT_OS_WINDOWS
3545 if ((fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_ATTR) != SHCL_OBJ_CF_ACCESS_ATTR_NONE)
3546 fOpen |= RTFILE_O_OPEN | RTFILE_O_ATTR_ONLY;
3547 else
3548#endif
3549 fOpen |= RTFILE_O_OPEN | RTFILE_O_READ;
3550 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_NONE\n"));
3551 break;
3552 }
3553
3554 case SHCL_OBJ_CF_ACCESS_READ:
3555 {
3556 fOpen |= RTFILE_O_OPEN | RTFILE_O_READ;
3557 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_READ\n"));
3558 break;
3559 }
3560
3561 default:
3562 AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE);
3563 }
3564
3565 switch (fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_ATTR)
3566 {
3567 case SHCL_OBJ_CF_ACCESS_ATTR_NONE:
3568 {
3569 fOpen |= RTFILE_O_ACCESS_ATTR_DEFAULT;
3570 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_NONE\n"));
3571 break;
3572 }
3573
3574 case SHCL_OBJ_CF_ACCESS_ATTR_READ:
3575 {
3576 fOpen |= RTFILE_O_ACCESS_ATTR_READ;
3577 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_READ\n"));
3578 break;
3579 }
3580
3581 default:
3582 AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE);
3583 }
3584
3585 /* Sharing mask */
3586 switch (fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_DENY)
3587 {
3588 case SHCL_OBJ_CF_ACCESS_DENYNONE:
3589 fOpen |= RTFILE_O_DENY_NONE;
3590 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYNONE\n"));
3591 break;
3592
3593 case SHCL_OBJ_CF_ACCESS_DENYWRITE:
3594 fOpen |= RTFILE_O_DENY_WRITE;
3595 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYWRITE\n"));
3596 break;
3597
3598 default:
3599 AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE);
3600 }
3601
3602 *pfOpen = fOpen;
3603
3604 LogFlowFuncLeaveRC(VINF_SUCCESS);
3605 return VINF_SUCCESS;
3606}
3607
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