VirtualBox

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

Last change on this file since 100382 was 100382, checked in by vboxsync, 22 months ago

Shared Clipboard: Linux build fixes. bugref:9437

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

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