VirtualBox

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

Last change on this file since 100385 was 100385, 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 100385 2023-07-05 09:51:33Z 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#ifdef DEBUG_andy
1742 LogFlowFunc(("Data:\n%.*Rhxd\n", cbRoots, pszRoots));
1743#endif
1744
1745 if (!RTStrIsValidEncoding(pszRoots))
1746 return VERR_INVALID_UTF8_ENCODING;
1747
1748 int rc = VINF_SUCCESS;
1749
1750 shClTransferLock(pTransfer);
1751
1752 shClTransferRootsReset(pTransfer);
1753
1754 PSHCLLIST pLstRoots = &pTransfer->lstRoots;
1755 char *pszPathRootAbs = NULL;
1756
1757 RTCList<RTCString> lstRootEntries = RTCString(pszRoots, cbRoots).split(SHCL_TRANSFER_URI_LIST_SEP_STR);
1758 if (!lstRootEntries.size())
1759 {
1760 shClTransferUnlock(pTransfer);
1761 return VINF_SUCCESS;
1762 }
1763
1764 for (size_t i = 0; i < lstRootEntries.size(); ++i)
1765 {
1766 char *pszPathCur = NULL;
1767
1768 char *pszPath = NULL;
1769 rc = RTUriFilePathEx(lstRootEntries.at(i).c_str(),
1770 RTPATH_STR_F_STYLE_UNIX, &pszPath, 0 /*cbPath*/, NULL /*pcchPath*/);
1771 if (RT_SUCCESS(rc))
1772 {
1773 pszPathCur = pszPath;
1774 pszPath = NULL; /* pszPath has ownership now. */
1775 }
1776 else if (rc == VERR_URI_NOT_FILE_SCHEME) /* Local file path? */
1777 {
1778 pszPathCur = RTStrDup(lstRootEntries.at(i).c_str());
1779 rc = VINF_SUCCESS;
1780 }
1781
1782 LogFlowFunc(("pszPathCur=%s\n", pszPathCur));
1783
1784 rc = ShClTransferValidatePath(pszPathCur, false /* fMustExist */);
1785 if (RT_FAILURE(rc))
1786 {
1787 RT_BREAKPOINT();
1788 break;
1789 }
1790
1791 /* No root path determined yet? */
1792 if (!pszPathRootAbs)
1793 {
1794 pszPathRootAbs = RTStrDup(pszPathCur);
1795 if (pszPathRootAbs)
1796 {
1797 RTPathStripFilename(pszPathRootAbs);
1798
1799 LogFlowFunc(("pszPathRootAbs=%s\n", pszPathRootAbs));
1800
1801 /* We don't want to have a relative directory here. */
1802 if (RTPathStartsWithRoot(pszPathRootAbs))
1803 {
1804 rc = ShClTransferValidatePath(pszPathRootAbs, true /* Path must exist */);
1805 }
1806 else
1807 rc = VERR_PATH_IS_RELATIVE;
1808 }
1809 else
1810 rc = VERR_NO_MEMORY;
1811 }
1812
1813 if (RT_SUCCESS(rc))
1814 {
1815 PSHCLLISTENTRY pEntry;
1816 rc = ShClTransferListEntryAlloc(&pEntry);
1817 if (RT_SUCCESS(rc))
1818 {
1819 PSHCLFSOBJINFO pFsObjInfo = (PSHCLFSOBJINFO )RTMemAllocZ(sizeof(SHCLFSOBJINFO));
1820 if (pFsObjInfo)
1821 {
1822 if (pTransfer->State.enmDir == SHCLTRANSFERDIR_TO_REMOTE)
1823 rc = ShClFsObjInfoQueryLocal(pszPathCur, pFsObjInfo);
1824 if (RT_SUCCESS(rc))
1825 {
1826 /* Calculate the relative path within the root path. */
1827 const char *pszPathRelToRoot = &pszPathRootAbs[strlen(pszPathRootAbs) + 1 /* Skip terminator or (back)slash. */];
1828 if ( pszPathRelToRoot
1829 && *pszPathRelToRoot != '\0')
1830 {
1831 LogFlowFunc(("pszPathRelToRoot=%s\n", pszPathRelToRoot));
1832
1833 rc = ShClTransferListEntryInitEx(pEntry, VBOX_SHCL_INFO_F_FSOBJINFO, pszPathRelToRoot,
1834 pFsObjInfo, sizeof(SHCLFSOBJINFO));
1835 if (RT_SUCCESS(rc))
1836 {
1837 rc = ShClTransferListAddEntry(pLstRoots, pEntry, true /* fAppend */);
1838 if (RT_SUCCESS(rc))
1839 {
1840 pFsObjInfo = NULL; /* pEntry has ownership now. */
1841 }
1842 }
1843 }
1844 else
1845 LogRel(("Shared Clipboard: Unable to construct relative path for '%s' (root is '%s')\n",
1846 pszPathCur, pszPathRootAbs));
1847 }
1848
1849 if (pFsObjInfo)
1850 {
1851 RTMemFree(pFsObjInfo);
1852 pFsObjInfo = NULL;
1853 }
1854 }
1855 else
1856 rc = VERR_NO_MEMORY;
1857
1858 if (RT_FAILURE(rc))
1859 ShClTransferListEntryFree(pEntry);
1860 }
1861 }
1862
1863 RTStrFree(pszPathCur);
1864 }
1865
1866 /* No (valid) root directory found? Bail out early. */
1867 if (!pszPathRootAbs)
1868 rc = VERR_PATH_DOES_NOT_START_WITH_ROOT;
1869
1870 if (RT_SUCCESS(rc))
1871 {
1872 pTransfer->pszPathRootAbs = pszPathRootAbs;
1873 LogFlowFunc(("pszPathRootAbs=%s, cRoots=%zu\n", pTransfer->pszPathRootAbs, pTransfer->lstRoots.Hdr.cEntries));
1874
1875 LogRel2(("Shared Clipboard: Transfer uses root '%s'\n", pTransfer->pszPathRootAbs));
1876 }
1877 else
1878 {
1879 LogRel(("Shared Clipboard: Unable to set roots for transfer, rc=%Rrc\n", rc));
1880 ShClTransferListDestroy(pLstRoots);
1881 RTStrFree(pszPathRootAbs);
1882 }
1883
1884 shClTransferUnlock(pTransfer);
1885
1886 LogFlowFuncLeaveRC(rc);
1887 return rc;
1888}
1889
1890/**
1891 * Initializes the root list entries for a given clipboard transfer, UTF-16 (Unicode) version.
1892 *
1893 * @returns VBox status code.
1894 * @param pTransfer Transfer to set transfer list entries for.
1895 * @param pwszRoots Unicode string list (separated by CRLF) of root entries to set.
1896 * All entries must have the same root path.
1897 * @param cbRoots Size (in bytes) of string list. Includes zero terminator.
1898 *
1899 * @note Accepts local paths or URI string lists (absolute only).
1900 */
1901int ShClTransferRootsInitFromStringListUnicode(PSHCLTRANSFER pTransfer, PRTUTF16 pwszRoots, size_t cbRoots)
1902{
1903 AssertPtrReturn(pwszRoots, VERR_INVALID_POINTER);
1904 AssertReturn(cbRoots, VERR_INVALID_PARAMETER);
1905 AssertReturn(cbRoots % sizeof(RTUTF16) == 0, VERR_INVALID_PARAMETER);
1906
1907 size_t cwcRoots = cbRoots / sizeof(RTUTF16);
1908
1909 /* This may slightly overestimate the space needed. */
1910 size_t chDst = 0;
1911 int rc = ShClUtf16LenUtf8(pwszRoots, cwcRoots, &chDst);
1912 if (RT_SUCCESS(rc))
1913 {
1914 chDst++; /* Add space for terminator. */
1915
1916 char *pszDst = (char *)RTStrAlloc(chDst);
1917 if (pszDst)
1918 {
1919 size_t cbActual = 0;
1920 rc = ShClConvUtf16CRLFToUtf8LF(pwszRoots, cwcRoots, pszDst, chDst, &cbActual);
1921 if (RT_SUCCESS(rc))
1922 rc = ShClTransferRootsInitFromStringList(pTransfer, pszDst, cbActual + 1 /* Include terminator */);
1923
1924 RTStrFree(pszDst);
1925 }
1926 else
1927 rc = VERR_NO_MEMORY;
1928 }
1929
1930 return rc;
1931}
1932
1933/**
1934 * Initializes a single file as a transfer root.
1935 *
1936 * @returns VBox status code.
1937 * @param pTransfer Transfer to set transfer list entries for.
1938 * @param pszFile File to use as transfer root.
1939 *
1940 * @note Convenience function, uses ShClTransferRootsSet() internally.
1941 */
1942int ShClTransferRootsInitFromFile(PSHCLTRANSFER pTransfer, const char *pszFile)
1943{
1944 char *pszRoots = NULL;
1945
1946 LogFlowFuncEnter();
1947
1948 int rc = RTStrAAppend(&pszRoots, pszFile);
1949 AssertRCReturn(rc, rc);
1950 rc = RTStrAAppend(&pszRoots, "\r\n");
1951 AssertRCReturn(rc, rc);
1952 rc = ShClTransferRootsInitFromStringList(pTransfer, pszRoots, strlen(pszRoots) + 1 /* Include terminator */);
1953 RTStrFree(pszRoots);
1954 return rc;
1955}
1956
1957/**
1958 * Returns the clipboard transfer's ID.
1959 *
1960 * @returns The transfer's ID.
1961 * @param pTransfer Clipboard transfer to return ID for.
1962 */
1963SHCLTRANSFERID ShClTransferGetID(PSHCLTRANSFER pTransfer)
1964{
1965 AssertPtrReturn(pTransfer, 0);
1966
1967 shClTransferLock(pTransfer);
1968
1969 SHCLTRANSFERID const uID = pTransfer->State.uID;
1970
1971 shClTransferUnlock(pTransfer);
1972
1973 return uID;
1974}
1975
1976/**
1977 * Returns the clipboard transfer's direction.
1978 *
1979 * @returns The transfer's direction.
1980 * @param pTransfer Clipboard transfer to return direction for.
1981 */
1982SHCLTRANSFERDIR ShClTransferGetDir(PSHCLTRANSFER pTransfer)
1983{
1984 AssertPtrReturn(pTransfer, SHCLTRANSFERDIR_UNKNOWN);
1985
1986 shClTransferLock(pTransfer);
1987
1988 SHCLTRANSFERDIR const enmDir = pTransfer->State.enmDir;
1989
1990 shClTransferUnlock(pTransfer);
1991
1992 return enmDir;
1993}
1994
1995/**
1996 * Returns the absolute root path of a transfer.
1997 *
1998 * @returns VBox status code.
1999 * @param pTransfer Clipboard transfer to return absolute root path for.
2000 * @param pszPath Where to store the returned path.
2001 * @param cbPath Size (in bytes) of \a pszPath.
2002 */
2003int ShClTransferGetRootPathAbs(PSHCLTRANSFER pTransfer, char *pszPath, size_t cbPath)
2004{
2005 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2006
2007 shClTransferLock(pTransfer);
2008
2009 AssertMsgReturn(pTransfer->pszPathRootAbs, ("Transfer has no root path set (yet)\n"), VERR_WRONG_ORDER);
2010
2011 int const rc = RTStrCopy(pszPath, cbPath, pTransfer->pszPathRootAbs);
2012
2013 shClTransferUnlock(pTransfer);
2014
2015 return rc;
2016}
2017
2018/**
2019 * Returns the transfer's source.
2020 *
2021 * @returns The transfer's source.
2022 * @param pTransfer Clipboard transfer to return source for.
2023 */
2024SHCLSOURCE ShClTransferGetSource(PSHCLTRANSFER pTransfer)
2025{
2026 AssertPtrReturn(pTransfer, SHCLSOURCE_INVALID);
2027
2028 shClTransferLock(pTransfer);
2029
2030 SHCLSOURCE const enmSource = pTransfer->State.enmSource;
2031
2032 shClTransferUnlock(pTransfer);
2033
2034 return enmSource;
2035}
2036
2037/**
2038 * Returns the current transfer status.
2039 *
2040 * @returns Current transfer status.
2041 * @param pTransfer Clipboard transfer to return status for.
2042 *
2043 * @note Caller needs to take critical section.
2044 */
2045DECLINLINE(SHCLTRANSFERSTATUS) shClTransferGetStatusLocked(PSHCLTRANSFER pTransfer)
2046{
2047 Assert(RTCritSectIsOwner(&pTransfer->CritSect));
2048
2049 shClTransferLock(pTransfer);
2050
2051 SHCLTRANSFERSTATUS const enmStatus = pTransfer->State.enmStatus;
2052
2053 shClTransferUnlock(pTransfer);
2054
2055 return enmStatus;
2056}
2057
2058/**
2059 * Returns the current transfer status.
2060 *
2061 * @returns Current transfer status.
2062 * @param pTransfer Clipboard transfer to return status for.
2063 */
2064SHCLTRANSFERSTATUS ShClTransferGetStatus(PSHCLTRANSFER pTransfer)
2065{
2066 AssertPtrReturn(pTransfer, SHCLTRANSFERSTATUS_NONE);
2067
2068 shClTransferLock(pTransfer);
2069
2070 SHCLTRANSFERSTATUS const enmSts = shClTransferGetStatusLocked(pTransfer);
2071
2072 shClTransferUnlock(pTransfer);
2073
2074 return enmSts;
2075}
2076
2077/**
2078 * Runs a started clipboard transfer in a dedicated thread.
2079 *
2080 * @returns VBox status code.
2081 * @param pTransfer Clipboard transfer to run.
2082 * @param pfnThreadFunc Pointer to thread function to use.
2083 * @param pvUser Pointer to user-provided data. Optional.
2084 */
2085int ShClTransferRun(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser)
2086{
2087 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2088 AssertPtrReturn(pfnThreadFunc, VERR_INVALID_POINTER);
2089 /* pvUser is optional. */
2090
2091 AssertMsgReturn(pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_STARTED,
2092 ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
2093 VERR_WRONG_ORDER);
2094
2095 int rc = shClTransferThreadCreate(pTransfer, pfnThreadFunc, pvUser);
2096
2097 LogFlowFuncLeaveRC(rc);
2098 return rc;
2099}
2100
2101/**
2102 * Starts an initialized transfer.
2103 *
2104 * @returns VBox status code.
2105 * @param pTransfer Clipboard transfer to start.
2106 */
2107int ShClTransferStart(PSHCLTRANSFER pTransfer)
2108{
2109 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2110
2111 LogFlowFuncEnter();
2112
2113 shClTransferLock(pTransfer);
2114
2115 /* Ready to start? */
2116 AssertMsgReturnStmt(pTransfer->ProviderIface.pfnRootListRead != NULL,
2117 ("No provider interface set (yet)\n"),
2118 shClTransferUnlock(pTransfer), VERR_WRONG_ORDER);
2119 AssertMsgReturnStmt(pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_INITIALIZED,
2120 ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
2121 shClTransferUnlock(pTransfer), VERR_WRONG_ORDER);
2122
2123 int rc = shClTransferSetStatus(pTransfer, SHCLTRANSFERSTATUS_STARTED);
2124
2125 shClTransferUnlock(pTransfer);
2126
2127 if (pTransfer->Callbacks.pfnOnStarted)
2128 pTransfer->Callbacks.pfnOnStarted(&pTransfer->CallbackCtx);
2129
2130 LogFlowFuncLeaveRC(rc);
2131 return rc;
2132}
2133
2134/**
2135 * Creates a thread for a clipboard transfer.
2136 *
2137 * @returns VBox status code.
2138 * @param pTransfer Clipboard transfer to create thread for.
2139 * @param pfnThreadFunc Thread function to use for this transfer.
2140 * @param pvUser Pointer to user-provided data.
2141 */
2142static int shClTransferThreadCreate(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser)
2143
2144{
2145 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2146
2147 shClTransferLock(pTransfer);
2148
2149 /* Already marked for stopping? */
2150 AssertMsgReturn(pTransfer->Thread.fStop == false,
2151 ("Transfer thread already marked for stopping"), VERR_WRONG_ORDER);
2152 /* Already started? */
2153 AssertMsgReturn(pTransfer->Thread.fStarted == false,
2154 ("Transfer thread already started"), VERR_WRONG_ORDER);
2155
2156 /* Spawn a worker thread, so that we don't block the window thread for too long. */
2157 int rc = RTThreadCreate(&pTransfer->Thread.hThread, pfnThreadFunc,
2158 pvUser, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
2159 "shclptx");
2160 if (RT_SUCCESS(rc))
2161 {
2162 shClTransferUnlock(pTransfer); /* Leave lock while waiting. */
2163
2164 int rc2 = RTThreadUserWait(pTransfer->Thread.hThread, SHCL_TIMEOUT_DEFAULT_MS);
2165 AssertRC(rc2);
2166
2167 shClTransferLock(pTransfer);
2168
2169 if (pTransfer->Thread.fStarted) /* Did the thread indicate that it started correctly? */
2170 {
2171 /* Nothing to do in here. */
2172 }
2173 else
2174 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
2175 }
2176
2177 shClTransferUnlock(pTransfer);
2178
2179 LogFlowFuncLeaveRC(rc);
2180 return rc;
2181}
2182
2183/**
2184 * Destroys the thread of a clipboard transfer.
2185 *
2186 * @returns VBox status code.
2187 * @param pTransfer Clipboard transfer to destroy thread for.
2188 * @param uTimeoutMs Timeout (in ms) to wait for thread creation.
2189 */
2190static int shClTransferThreadDestroy(PSHCLTRANSFER pTransfer, RTMSINTERVAL uTimeoutMs)
2191{
2192 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2193
2194 shClTransferLock(pTransfer);
2195
2196 if (pTransfer->Thread.hThread == NIL_RTTHREAD)
2197 {
2198 shClTransferUnlock(pTransfer);
2199 return VINF_SUCCESS;
2200 }
2201
2202 LogFlowFuncEnter();
2203
2204 /* Set stop indicator. */
2205 pTransfer->Thread.fStop = true;
2206
2207 shClTransferUnlock(pTransfer); /* Leave lock while waiting. */
2208
2209 int rcThread = VERR_WRONG_ORDER;
2210 int rc = RTThreadWait(pTransfer->Thread.hThread, uTimeoutMs, &rcThread);
2211
2212 LogFlowFunc(("Waiting for thread resulted in %Rrc (thread exited with %Rrc)\n", rc, rcThread));
2213
2214 return rc;
2215}
2216
2217/**
2218 * Waits for the transfer status to change, internal version.
2219 *
2220 * @returns VBox status code.
2221 * @param pTransfer Clipboard transfer to wait for.
2222 * @param msTimeout Timeout (in ms) to wait.
2223 * @param penmStatus Where to return the new (current) transfer status on success.
2224 * Optional and can be NULL.
2225 */
2226static int shClTransferWaitForStatusChangeInternal(PSHCLTRANSFER pTransfer, RTMSINTERVAL msTimeout, SHCLTRANSFERSTATUS *penmStatus)
2227{
2228 LogFlowFunc(("Waiting for status change (%RU32 timeout) ...\n", msTimeout));
2229
2230 int rc = RTSemEventWait(pTransfer->StatusChangeEvent, msTimeout);
2231 if (RT_SUCCESS(rc))
2232 {
2233 if (penmStatus)
2234 {
2235 shClTransferLock(pTransfer);
2236
2237 *penmStatus = pTransfer->State.enmStatus;
2238
2239 shClTransferUnlock(pTransfer);
2240 }
2241 }
2242
2243 return rc;
2244}
2245
2246/**
2247 * Waits for the transfer status to change.
2248 *
2249 * @returns VBox status code.
2250 * @param pTransfer Clipboard transfer to wait for.
2251 * @param msTimeout Timeout (in ms) to wait.
2252 * @param penmStatus Where to return the new (current) transfer status on success.
2253 * Optional and can be NULL.
2254 */
2255int ShClTransferWaitForStatusChange(PSHCLTRANSFER pTransfer, RTMSINTERVAL msTimeout, SHCLTRANSFERSTATUS *penmStatus)
2256{
2257 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2258
2259 int rc = shClTransferWaitForStatusChangeInternal(pTransfer, msTimeout, penmStatus);
2260
2261 LogFlowFuncLeaveRC(rc);
2262 return rc;
2263}
2264
2265/**
2266 * Waits for a specific transfer status.
2267 *
2268 * @returns VBox status code.
2269 * @param pTransfer Clipboard transfer to wait for.
2270 * @param msTimeout Timeout (in ms) to wait.
2271 * @param enmStatus Transfer status to wait for.
2272 */
2273int ShClTransferWaitForStatus(PSHCLTRANSFER pTransfer, RTMSINTERVAL msTimeout, SHCLTRANSFERSTATUS enmStatus)
2274{
2275 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2276
2277 int rc = VINF_SUCCESS;
2278
2279 uint64_t const tsStartMs = RTTimeMilliTS();
2280 uint64_t msLeft = msTimeout;
2281 for (;;)
2282 {
2283 SHCLTRANSFERSTATUS enmCurStatus;
2284 rc = shClTransferWaitForStatusChangeInternal(pTransfer, msLeft, &enmCurStatus);
2285 if (RT_FAILURE(rc))
2286 break;
2287
2288 if (enmCurStatus == enmStatus)
2289 break;
2290
2291 msLeft -= RT_MIN(msLeft, RTTimeMilliTS() - tsStartMs);
2292 if (msLeft == 0)
2293 {
2294 rc = VERR_TIMEOUT;
2295 break;
2296 }
2297 }
2298
2299 LogFlowFuncLeaveRC(rc);
2300 return rc;
2301}
2302
2303
2304/*********************************************************************************************************************************
2305 * Transfer Context *
2306 ********************************************************************************************************************************/
2307
2308/**
2309 * Locks a transfer context.
2310 *
2311 * @param pTransferCtx Transfer context to lock.
2312 */
2313DECLINLINE(void) shClTransferCtxLock(PSHCLTRANSFERCTX pTransferCtx)
2314{
2315 int rc2 = RTCritSectEnter(&pTransferCtx->CritSect);
2316 AssertRC(rc2);
2317}
2318
2319/**
2320 * Unlocks a transfer context.
2321 *
2322 * @param pTransferCtx Transfer context to unlock.
2323 */
2324DECLINLINE(void) shClTransferCtxUnlock(PSHCLTRANSFERCTX pTransferCtx)
2325{
2326 int rc2 = RTCritSectLeave(&pTransferCtx->CritSect);
2327 AssertRC(rc2);
2328}
2329
2330/**
2331 * Initializes a clipboard transfer context.
2332 *
2333 * @returns VBox status code.
2334 * @param pTransferCtx Transfer context to initialize.
2335 */
2336int ShClTransferCtxInit(PSHCLTRANSFERCTX pTransferCtx)
2337{
2338 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2339
2340 LogFlowFunc(("pTransferCtx=%p\n", pTransferCtx));
2341
2342 int rc = RTCritSectInit(&pTransferCtx->CritSect);
2343 if (RT_SUCCESS(rc))
2344 {
2345 rc = RTSemEventCreate(&pTransferCtx->ChangedEvent);
2346 if (RT_SUCCESS(rc))
2347 {
2348 RT_ZERO(pTransferCtx->ChangedEventData);
2349
2350 RTListInit(&pTransferCtx->List);
2351
2352 pTransferCtx->cTransfers = 0;
2353 pTransferCtx->cRunning = 0;
2354 pTransferCtx->cMaxRunning = 64; /** @todo Make this configurable? */
2355
2356 RT_ZERO(pTransferCtx->bmTransferIds);
2357
2358 ShClTransferCtxReset(pTransferCtx);
2359 }
2360 }
2361
2362 return VINF_SUCCESS;
2363}
2364
2365/**
2366 * Destroys a clipboard transfer context.
2367 *
2368 * @param pTransferCtx Transfer context to destroy.
2369 */
2370void ShClTransferCtxDestroy(PSHCLTRANSFERCTX pTransferCtx)
2371{
2372 if (!pTransferCtx)
2373 return;
2374
2375 LogFlowFunc(("pTransferCtx=%p\n", pTransferCtx));
2376
2377 shClTransferCtxLock(pTransferCtx);
2378
2379 PSHCLTRANSFER pTransfer, pTransferNext;
2380 RTListForEachSafe(&pTransferCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
2381 {
2382 ShClTransferDestroy(pTransfer);
2383
2384 shclTransferCtxTransferRemoveAndUnregister(pTransferCtx, pTransfer);
2385
2386 RTMemFree(pTransfer);
2387 pTransfer = NULL;
2388 }
2389
2390 pTransferCtx->cRunning = 0;
2391 pTransferCtx->cTransfers = 0;
2392
2393 shClTransferCtxUnlock(pTransferCtx);
2394
2395 RTSemEventDestroy(pTransferCtx->ChangedEvent);
2396 pTransferCtx->ChangedEvent = NIL_RTSEMEVENT;
2397
2398 if (RTCritSectIsInitialized(&pTransferCtx->CritSect))
2399 RTCritSectDelete(&pTransferCtx->CritSect);
2400}
2401
2402/**
2403 * Resets a clipboard transfer context.
2404 *
2405 * @param pTransferCtx Transfer context to reset.
2406 */
2407void ShClTransferCtxReset(PSHCLTRANSFERCTX pTransferCtx)
2408{
2409 AssertPtrReturnVoid(pTransferCtx);
2410
2411 shClTransferCtxLock(pTransferCtx);
2412
2413 LogFlowFuncEnter();
2414
2415 PSHCLTRANSFER pTransfer;
2416 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node)
2417 ShClTransferReset(pTransfer);
2418
2419#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
2420 /** @todo Anything to do here? */
2421#endif
2422
2423 shClTransferCtxUnlock(pTransferCtx);
2424}
2425
2426/**
2427 * Signals a change event.
2428 *
2429 * @returns VBox status code.
2430 * @param pTransferCtx Transfer context to return transfer for.
2431 * @param fRegistered Whether a transfer got registered or unregistered.
2432 * @param pTransfer Transfer bound to the event.
2433 */
2434static int shClTransferCtxSignal(PSHCLTRANSFERCTX pTransferCtx, bool fRegistered, PSHCLTRANSFER pTransfer)
2435{
2436 Assert(RTCritSectIsOwner(&pTransferCtx->CritSect));
2437
2438 LogFlowFunc(("fRegistered=%RTbool, pTransfer=%p\n", fRegistered, pTransfer));
2439
2440 pTransferCtx->ChangedEventData.fRegistered = fRegistered;
2441 pTransferCtx->ChangedEventData.pTransfer = pTransfer;
2442
2443 return RTSemEventSignal(pTransferCtx->ChangedEvent);
2444}
2445
2446/**
2447 * Returns a specific clipboard transfer, internal version.
2448 *
2449 * @returns Clipboard transfer found, or NULL if not found.
2450 * @param pTransferCtx Transfer context to return transfer for.
2451 * @param idTransfer ID of the transfer to return.
2452 *
2453 * @note Caller needs to take critical section.
2454 */
2455static PSHCLTRANSFER shClTransferCtxGetTransferByIdInternal(PSHCLTRANSFERCTX pTransferCtx, SHCLTRANSFERID idTransfer)
2456{
2457 Assert(RTCritSectIsOwner(&pTransferCtx->CritSect));
2458
2459 PSHCLTRANSFER pTransfer;
2460 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node) /** @todo Slow, but works for now. */
2461 {
2462 if (pTransfer->State.uID == idTransfer)
2463 return pTransfer;
2464 }
2465
2466 return NULL;
2467}
2468
2469/**
2470 * Returns a specific clipboard transfer by index, internal version.
2471 *
2472 * @returns Clipboard transfer found, or NULL if not found.
2473 * @param pTransferCtx Transfer context to return transfer for.
2474 * @param uIdx Index of the transfer to return.
2475 *
2476 * @note Caller needs to take critical section.
2477 */
2478static PSHCLTRANSFER shClTransferCtxGetTransferByIndexInternal(PSHCLTRANSFERCTX pTransferCtx, uint32_t uIdx)
2479{
2480 Assert(RTCritSectIsOwner(&pTransferCtx->CritSect));
2481
2482 uint32_t idx = 0;
2483
2484 PSHCLTRANSFER pTransfer;
2485 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node) /** @todo Slow, but works for now. */
2486 {
2487 if (uIdx == idx)
2488 return pTransfer;
2489 idx++;
2490 }
2491
2492 return NULL;
2493}
2494
2495/**
2496 * Returns a clipboard transfer for a specific transfer ID.
2497 *
2498 * @returns Clipboard transfer found, or NULL if not found.
2499 * @param pTransferCtx Transfer context to return transfer for.
2500 * @param uID ID of the transfer to return.
2501 */
2502PSHCLTRANSFER ShClTransferCtxGetTransferById(PSHCLTRANSFERCTX pTransferCtx, uint32_t uID)
2503{
2504 shClTransferCtxLock(pTransferCtx);
2505
2506 PSHCLTRANSFER const pTransfer = shClTransferCtxGetTransferByIdInternal(pTransferCtx, uID);
2507
2508 shClTransferCtxUnlock(pTransferCtx);
2509
2510 return pTransfer;
2511}
2512
2513/**
2514 * Returns a clipboard transfer for a specific list index.
2515 *
2516 * @returns Clipboard transfer found, or NULL if not found.
2517 * @param pTransferCtx Transfer context to return transfer for.
2518 * @param uIdx List index of the transfer to return.
2519 */
2520PSHCLTRANSFER ShClTransferCtxGetTransferByIndex(PSHCLTRANSFERCTX pTransferCtx, uint32_t uIdx)
2521{
2522 shClTransferCtxLock(pTransferCtx);
2523
2524 PSHCLTRANSFER const pTransfer = shClTransferCtxGetTransferByIndexInternal(pTransferCtx, uIdx);
2525
2526 shClTransferCtxUnlock(pTransferCtx);
2527
2528 return pTransfer;
2529}
2530
2531
2532/**
2533 * Returns the last clipboard transfer registered.
2534 *
2535 * @returns Clipboard transfer found, or NULL if not found.
2536 * @param pTransferCtx Transfer context to return transfer for.
2537 */
2538PSHCLTRANSFER ShClTransferCtxGetTransferLast(PSHCLTRANSFERCTX pTransferCtx)
2539{
2540 shClTransferCtxLock(pTransferCtx);
2541
2542 PSHCLTRANSFER const pTransfer = RTListGetLast(&pTransferCtx->List, SHCLTRANSFER, Node);
2543
2544 shClTransferCtxUnlock(pTransferCtx);
2545
2546 return pTransfer;
2547}
2548
2549/**
2550 * Returns the number of running clipboard transfers for a given transfer context.
2551 *
2552 * @returns Number of running transfers.
2553 * @param pTransferCtx Transfer context to return number for.
2554 */
2555uint32_t ShClTransferCtxGetRunningTransfers(PSHCLTRANSFERCTX pTransferCtx)
2556{
2557 AssertPtrReturn(pTransferCtx, 0);
2558
2559 shClTransferCtxLock(pTransferCtx);
2560
2561 uint32_t const cRunning = pTransferCtx->cRunning;
2562
2563 shClTransferCtxUnlock(pTransferCtx);
2564
2565 return cRunning;
2566}
2567
2568/**
2569 * Returns the number of total clipboard transfers for a given transfer context.
2570 *
2571 * @returns Number of total transfers.
2572 * @param pTransferCtx Transfer context to return number for.
2573 */
2574uint32_t ShClTransferCtxGetTotalTransfers(PSHCLTRANSFERCTX pTransferCtx)
2575{
2576 AssertPtrReturn(pTransferCtx, 0);
2577
2578 shClTransferCtxLock(pTransferCtx);
2579
2580 uint32_t const cTransfers = pTransferCtx->cTransfers;
2581
2582 shClTransferCtxUnlock(pTransferCtx);
2583
2584 return cTransfers;
2585}
2586
2587static int shClTransferCreateIDInternal(PSHCLTRANSFERCTX pTransferCtx, SHCLTRANSFERID *pidTransfer)
2588{
2589 /*
2590 * Pick a random bit as starting point. If it's in use, search forward
2591 * for a free one, wrapping around. We've reserved both the zero'th and
2592 * max-1 IDs.
2593 */
2594 SHCLTRANSFERID idTransfer = RTRandU32Ex(1, VBOX_SHCL_MAX_TRANSFERS - 2);
2595
2596 if (!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer))
2597 { /* likely */ }
2598 else if (pTransferCtx->cTransfers < VBOX_SHCL_MAX_TRANSFERS - 2 /* First and last are not used */)
2599 {
2600 /* Forward search. */
2601 int iHit = ASMBitNextClear(&pTransferCtx->bmTransferIds[0], VBOX_SHCL_MAX_TRANSFERS, idTransfer);
2602 if (iHit < 0)
2603 iHit = ASMBitFirstClear(&pTransferCtx->bmTransferIds[0], VBOX_SHCL_MAX_TRANSFERS);
2604 AssertLogRelMsgReturn(iHit >= 0, ("Transfer count: %RU16\n", pTransferCtx->cTransfers), VERR_SHCLPB_MAX_TRANSFERS_REACHED);
2605 idTransfer = iHit;
2606 AssertLogRelMsgReturn(!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer), ("idObject=%#x\n", idTransfer), VERR_INTERNAL_ERROR_2);
2607 }
2608 else
2609 {
2610 LogFunc(("Maximum number of transfers reached (%RU16 transfers)\n", pTransferCtx->cTransfers));
2611 return VERR_SHCLPB_MAX_TRANSFERS_REACHED;
2612 }
2613
2614 *pidTransfer = idTransfer;
2615
2616 return VINF_SUCCESS;
2617}
2618
2619/**
2620 * Creates a new transfer ID for a given transfer context.
2621 *
2622 * @returns VBox status code.
2623 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers is reached.
2624 * @param pTransferCtx Transfer context to create transfer ID for.
2625 * @param pidTransfer Where to return the transfer ID on success.
2626 */
2627int ShClTransferCtxCreateId(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFERID pidTransfer)
2628{
2629 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2630 AssertPtrReturn(pidTransfer, VERR_INVALID_POINTER);
2631
2632 shClTransferCtxLock(pTransferCtx);
2633
2634 int rc = shClTransferCreateIDInternal(pTransferCtx, pidTransfer);
2635
2636 shClTransferCtxUnlock(pTransferCtx);
2637
2638 return rc;
2639}
2640
2641/**
2642 * Registers a clipboard transfer with a new transfer ID.
2643 *
2644 * @return VBox status code.
2645 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers is reached.
2646 * @param pTransferCtx Transfer context to register transfer to.
2647 * @param pTransfer Transfer to register. The context takes ownership of the transfer on success.
2648 * @param idTransfer Transfer ID to use for registering the given transfer.
2649 */
2650static int shClTransferCtxTransferRegisterExInternal(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, SHCLTRANSFERID idTransfer)
2651{
2652 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2653 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2654 Assert(idTransfer != NIL_SHCLTRANSFERID);
2655
2656 shClTransferCtxLock(pTransferCtx);
2657
2658 pTransfer->State.uID = idTransfer;
2659
2660 RTListAppend(&pTransferCtx->List, &pTransfer->Node);
2661
2662 pTransferCtx->cTransfers++;
2663
2664 Log2Func(("pTransfer=%p, idTransfer=%RU32 -- now %RU16 transfer(s)\n", pTransfer, idTransfer, pTransferCtx->cTransfers));
2665
2666 shClTransferCtxUnlock(pTransferCtx);
2667
2668 if (pTransfer->Callbacks.pfnOnRegistered)
2669 pTransfer->Callbacks.pfnOnRegistered(&pTransfer->CallbackCtx, pTransferCtx);
2670
2671 LogFlowFuncLeaveRC(VINF_SUCCESS);
2672 return VINF_SUCCESS;
2673}
2674
2675/**
2676 * Registers a clipboard transfer with a transfer context, i.e. allocates a transfer ID.
2677 *
2678 * @return VBox status code.
2679 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers for this context has been reached.
2680 * @param pTransferCtx Transfer context to register transfer to.
2681 * @param pTransfer Transfer to register. The context takes ownership of the transfer on success.
2682 * @param pidTransfer Where to return the transfer ID on success. Optional.
2683 */
2684int ShClTransferCtxRegister(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, PSHCLTRANSFERID pidTransfer)
2685{
2686 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2687 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2688
2689 shClTransferCtxLock(pTransferCtx);
2690
2691 SHCLTRANSFERID idTransfer;
2692 int rc = shClTransferCreateIDInternal(pTransferCtx, &idTransfer);
2693 if (RT_SUCCESS(rc))
2694 {
2695 rc = shClTransferCtxTransferRegisterExInternal(pTransferCtx, pTransfer, idTransfer);
2696 if (RT_SUCCESS(rc))
2697 {
2698 if (pidTransfer)
2699 *pidTransfer = idTransfer;
2700 }
2701 }
2702
2703 shClTransferCtxUnlock(pTransferCtx);
2704
2705 return rc;
2706}
2707
2708/**
2709 * Registers a clipboard transfer with a transfer context by specifying an ID for the transfer.
2710 *
2711 * @return VBox status code.
2712 * @retval VERR_ALREADY_EXISTS if a transfer with the given ID already exists.
2713 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers for this context has been reached.
2714 * @param pTransferCtx Transfer context to register transfer to.
2715 * @param pTransfer Transfer to register.
2716 * @param idTransfer Transfer ID to use for registration.
2717 *
2718 * @note This function ASSUMES you have created \a idTransfer with ShClTransferCtxCreateId().
2719 */
2720int ShClTransferCtxRegisterById(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, SHCLTRANSFERID idTransfer)
2721{
2722 shClTransferCtxLock(pTransferCtx);
2723
2724 if (pTransferCtx->cTransfers < VBOX_SHCL_MAX_TRANSFERS - 2 /* First and last are not used */)
2725 {
2726 RTListAppend(&pTransferCtx->List, &pTransfer->Node);
2727
2728 shClTransferLock(pTransfer);
2729
2730 pTransfer->State.uID = idTransfer;
2731
2732 shClTransferUnlock(pTransfer);
2733
2734 int rc = shClTransferCtxSignal(pTransferCtx, true /* fRegistered */, pTransfer);
2735
2736 pTransferCtx->cTransfers++;
2737
2738 LogFunc(("Registered transfer ID %RU16 -- now %RU16 transfers total\n", idTransfer, pTransferCtx->cTransfers));
2739
2740 shClTransferCtxUnlock(pTransferCtx);
2741
2742 if (pTransfer->Callbacks.pfnOnRegistered)
2743 pTransfer->Callbacks.pfnOnRegistered(&pTransfer->CallbackCtx, pTransferCtx);
2744
2745 return rc;
2746 }
2747
2748 LogFunc(("Maximum number of transfers reached (%RU16 transfers)\n", pTransferCtx->cTransfers));
2749
2750 shClTransferCtxUnlock(pTransferCtx);
2751
2752 return VERR_SHCLPB_MAX_TRANSFERS_REACHED;
2753}
2754
2755/**
2756 * Removes and unregisters a transfer from a transfer context.
2757 *
2758 * @param pTransferCtx Transfer context to remove transfer from.
2759 * @param pTransfer Transfer to remove.
2760 *
2761 * @note Caller needs to take critical section.
2762 */
2763static void shclTransferCtxTransferRemoveAndUnregister(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer)
2764{
2765 Assert(RTCritSectIsOwner(&pTransferCtx->CritSect));
2766
2767 RTListNodeRemove(&pTransfer->Node);
2768
2769 Assert(pTransferCtx->cTransfers);
2770 pTransferCtx->cTransfers--;
2771
2772 Assert(pTransferCtx->cTransfers >= pTransferCtx->cRunning);
2773
2774 shClTransferCtxUnlock(pTransferCtx);
2775
2776 if (pTransfer->Callbacks.pfnOnUnregistered)
2777 pTransfer->Callbacks.pfnOnUnregistered(&pTransfer->CallbackCtx, pTransferCtx);
2778
2779 shClTransferCtxLock(pTransferCtx);
2780
2781 LogFlowFunc(("Now %RU32 transfers left\n", pTransferCtx->cTransfers));
2782}
2783
2784/**
2785 * Unregisters a transfer from an transfer context, given by its ID.
2786 *
2787 * @retval VINF_SUCCESS on success.
2788 * @retval VERR_NOT_FOUND if the transfer ID was not found.
2789 * @param pTransferCtx Transfer context to unregister transfer from.
2790 * @param idTransfer Transfer ID to unregister.
2791 */
2792int ShClTransferCtxUnregisterById(PSHCLTRANSFERCTX pTransferCtx, SHCLTRANSFERID idTransfer)
2793{
2794 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2795 AssertReturn(idTransfer, VERR_INVALID_PARAMETER);
2796
2797 shClTransferCtxLock(pTransferCtx);
2798
2799 int rc = VINF_SUCCESS;
2800
2801 LogFlowFunc(("idTransfer=%RU32\n", idTransfer));
2802
2803 if (ASMBitTestAndClear(&pTransferCtx->bmTransferIds, idTransfer), ("idTransfer=%#x\n", idTransfer))
2804 {
2805 PSHCLTRANSFER pTransfer = shClTransferCtxGetTransferByIdInternal(pTransferCtx, idTransfer);
2806 AssertPtr(pTransfer);
2807 if (pTransfer)
2808 {
2809 shclTransferCtxTransferRemoveAndUnregister(pTransferCtx, pTransfer);
2810
2811 rc = shClTransferCtxSignal(pTransferCtx, false /* fRegistered */, pTransfer);
2812 }
2813 }
2814 else
2815 rc = VERR_NOT_FOUND;
2816
2817 shClTransferCtxUnlock(pTransferCtx);
2818
2819 LogFlowFuncLeaveRC(rc);
2820 return rc;
2821}
2822
2823/**
2824 * Waits for a transfer context event.
2825 *
2826 * @returns VBox status code.
2827 * @param pTransferCtx Transfer context to wait for.
2828 * @param msTimeout Timeout (in ms) to wait.
2829 * @param pEvent Where to return the event data on success.
2830 */
2831static int shClTransferCtxWaitInternal(PSHCLTRANSFERCTX pTransferCtx, RTMSINTERVAL msTimeout, PSHCLTRANSFERCTXEVENT pEvent)
2832{
2833 LogFlowFunc(("Waiting for transfer context change (%RU32 timeout) ...\n", msTimeout));
2834
2835 int rc = RTSemEventWait(pTransferCtx->ChangedEvent, msTimeout);
2836 if (RT_SUCCESS(rc))
2837 {
2838 shClTransferCtxLock(pTransferCtx);
2839
2840 memcpy(pEvent, &pTransferCtx->ChangedEventData, sizeof(SHCLTRANSFERCTXEVENT));
2841
2842 shClTransferCtxUnlock(pTransferCtx);
2843 }
2844
2845 LogFlowFuncLeaveRC(rc);
2846 return rc;
2847}
2848
2849/**
2850 * Waits for transfer to be (un-)registered.
2851 *
2852 * @returns VBox status code.
2853 * @param pTransferCtx Transfer context to wait for.
2854 * @param msTimeout Timeout (in ms) to wait.
2855 * @param fRegister Pass \c true for registering, or \c false for unregistering a transfer.
2856 * @param idTransfer Transfer ID to wait for.
2857 * Pass NIL_SHCLTRANSFERID for any transfer.
2858 * @param ppTransfer Where to return the transfer being (un-)registered. Optional and can be NULL.
2859 */
2860int ShClTransferCtxWait(PSHCLTRANSFERCTX pTransferCtx, RTMSINTERVAL msTimeout, bool fRegister, SHCLTRANSFERID idTransfer,
2861 PSHCLTRANSFER *ppTransfer)
2862{
2863 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2864
2865 int rc = VERR_TIMEOUT;
2866
2867 uint64_t const tsStartMs = RTTimeMilliTS();
2868 uint64_t msLeft = msTimeout;
2869 for (;;)
2870 {
2871 SHCLTRANSFERCTXEVENT Event;
2872 rc = shClTransferCtxWaitInternal(pTransferCtx, msLeft, &Event);
2873 if (RT_FAILURE(rc))
2874 break;
2875
2876 shClTransferCtxLock(pTransferCtx);
2877
2878 if (Event.fRegistered == fRegister)
2879 {
2880 if ( idTransfer != NIL_SHCLTRANSFERID
2881 && Event.pTransfer
2882 && ShClTransferGetID(Event.pTransfer) == idTransfer)
2883 {
2884 if (ppTransfer)
2885 *ppTransfer = Event.pTransfer;
2886 rc = VINF_SUCCESS;
2887 }
2888 }
2889
2890 shClTransferCtxUnlock(pTransferCtx);
2891
2892 if (RT_SUCCESS(rc))
2893 break;
2894
2895 msLeft -= RT_MIN(msLeft, RTTimeMilliTS() - tsStartMs);
2896 if (msLeft == 0)
2897 break;
2898 }
2899
2900 LogFlowFuncLeaveRC(rc);
2901 return rc;
2902}
2903
2904/**
2905 * Cleans up all associated transfers which are not needed (anymore).
2906 * This can be due to transfers which only have been announced but not / never being run.
2907 *
2908 * @param pTransferCtx Transfer context to cleanup transfers for.
2909 */
2910void ShClTransferCtxCleanup(PSHCLTRANSFERCTX pTransferCtx)
2911{
2912 AssertPtrReturnVoid(pTransferCtx);
2913
2914 shClTransferCtxLock(pTransferCtx);
2915
2916 LogFlowFunc(("pTransferCtx=%p, cTransfers=%RU16 cRunning=%RU16\n",
2917 pTransferCtx, pTransferCtx->cTransfers, pTransferCtx->cRunning));
2918
2919 if (pTransferCtx->cTransfers == 0)
2920 {
2921 shClTransferCtxUnlock(pTransferCtx);
2922 return;
2923 }
2924
2925 /* Remove all transfers which are not in a running state (e.g. only announced). */
2926 PSHCLTRANSFER pTransfer, pTransferNext;
2927 RTListForEachSafe(&pTransferCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
2928 {
2929 shClTransferLock(pTransfer);
2930
2931 SHCLTRANSFERSTATUS const enmStatus = shClTransferGetStatusLocked(pTransfer);
2932 LogFlowFunc(("\tTransfer #%RU16: %s\n", pTransfer->State.uID, ShClTransferStatusToStr(enmStatus)));
2933
2934 if (enmStatus != SHCLTRANSFERSTATUS_STARTED)
2935 {
2936 shClTransferUnlock(pTransfer);
2937
2938 shclTransferCtxTransferRemoveAndUnregister(pTransferCtx, pTransfer);
2939
2940 ShClTransferDestroy(pTransfer);
2941
2942 RTMemFree(pTransfer);
2943 pTransfer = NULL;
2944 }
2945 else
2946 shClTransferUnlock(pTransfer);
2947 }
2948
2949 shClTransferCtxUnlock(pTransferCtx);
2950}
2951
2952/**
2953 * Returns whether the maximum of concurrent transfers of a specific transfer contexthas been reached or not.
2954 *
2955 * @returns \c if maximum has been reached, \c false if not.
2956 * @param pTransferCtx Transfer context to determine value for.
2957 */
2958bool ShClTransferCtxIsMaximumReached(PSHCLTRANSFERCTX pTransferCtx)
2959{
2960 AssertPtrReturn(pTransferCtx, true);
2961
2962 shClTransferCtxLock(pTransferCtx);
2963
2964 LogFlowFunc(("cRunning=%RU32, cMaxRunning=%RU32\n", pTransferCtx->cRunning, pTransferCtx->cMaxRunning));
2965
2966 Assert(pTransferCtx->cRunning <= pTransferCtx->cMaxRunning);
2967 bool const fMaximumReached = pTransferCtx->cRunning == pTransferCtx->cMaxRunning;
2968
2969 shClTransferCtxUnlock(pTransferCtx);
2970
2971 return fMaximumReached;
2972}
2973
2974/**
2975 * Copies file system objinfo from IPRT to Shared Clipboard format.
2976 *
2977 * @return VBox status code.
2978 * @param pDst The Shared Clipboard structure to convert data to.
2979 * @param pSrc The IPRT structure to convert data from.
2980 */
2981int ShClFsObjInfoFromIPRT(PSHCLFSOBJINFO pDst, PCRTFSOBJINFO pSrc)
2982{
2983 AssertPtrReturn(pDst, VERR_INVALID_POINTER);
2984 AssertPtrReturn(pSrc, VERR_INVALID_POINTER);
2985
2986 pDst->cbObject = pSrc->cbObject;
2987 pDst->cbAllocated = pSrc->cbAllocated;
2988 pDst->AccessTime = pSrc->AccessTime;
2989 pDst->ModificationTime = pSrc->ModificationTime;
2990 pDst->ChangeTime = pSrc->ChangeTime;
2991 pDst->BirthTime = pSrc->BirthTime;
2992 pDst->Attr.fMode = pSrc->Attr.fMode;
2993 /* Clear bits which we don't pass through for security reasons. */
2994 pDst->Attr.fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
2995 RT_ZERO(pDst->Attr.u);
2996 switch (pSrc->Attr.enmAdditional)
2997 {
2998 default:
2999 RT_FALL_THROUGH();
3000 case RTFSOBJATTRADD_NOTHING:
3001 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_NOTHING;
3002 break;
3003
3004 case RTFSOBJATTRADD_UNIX:
3005 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_UNIX;
3006 pDst->Attr.u.Unix.uid = pSrc->Attr.u.Unix.uid;
3007 pDst->Attr.u.Unix.gid = pSrc->Attr.u.Unix.gid;
3008 pDst->Attr.u.Unix.cHardlinks = pSrc->Attr.u.Unix.cHardlinks;
3009 pDst->Attr.u.Unix.INodeIdDevice = pSrc->Attr.u.Unix.INodeIdDevice;
3010 pDst->Attr.u.Unix.INodeId = pSrc->Attr.u.Unix.INodeId;
3011 pDst->Attr.u.Unix.fFlags = pSrc->Attr.u.Unix.fFlags;
3012 pDst->Attr.u.Unix.GenerationId = pSrc->Attr.u.Unix.GenerationId;
3013 pDst->Attr.u.Unix.Device = pSrc->Attr.u.Unix.Device;
3014 break;
3015
3016 case RTFSOBJATTRADD_EASIZE:
3017 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_EASIZE;
3018 pDst->Attr.u.EASize.cb = pSrc->Attr.u.EASize.cb;
3019 break;
3020 }
3021
3022 return VINF_SUCCESS;
3023}
3024
3025/**
3026 * Queries local file system information from a given path.
3027 *
3028 * @returns VBox status code.
3029 * @param pszPath Path to query file system information for.
3030 * @param pObjInfo Where to return the queried file system information on success.
3031 */
3032int ShClFsObjInfoQueryLocal(const char *pszPath, PSHCLFSOBJINFO pObjInfo)
3033{
3034 RTFSOBJINFO objInfo;
3035 int rc = RTPathQueryInfo(pszPath, &objInfo,
3036#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
3037 RTFSOBJATTRADD_NOTHING
3038#else
3039 RTFSOBJATTRADD_UNIX
3040#endif
3041 );
3042 if (RT_SUCCESS(rc))
3043 rc = ShClFsObjInfoFromIPRT(pObjInfo, &objInfo);
3044
3045 return rc;
3046}
3047
3048/**
3049 * Translates a clipboard transfer status (SHCLTRANSFERSTATUS_XXX) into a string.
3050 *
3051 * @returns Transfer status string name.
3052 * @param enmStatus The transfer status to translate.
3053 */
3054const char *ShClTransferStatusToStr(SHCLTRANSFERSTATUS enmStatus)
3055{
3056 switch (enmStatus)
3057 {
3058 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_NONE);
3059 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_REQUESTED);
3060 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_INITIALIZED);
3061 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_UNINITIALIZED);
3062 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_STARTED);
3063 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_STOPPED);
3064 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_CANCELED);
3065 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_KILLED);
3066 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_ERROR);
3067 }
3068 return "Unknown";
3069}
3070
3071/**
3072 * Validates whether a given path matches our set of rules or not.
3073 *
3074 * @returns VBox status code.
3075 * @param pcszPath Path to validate.
3076 * @param fMustExist Whether the path to validate also must exist.
3077 */
3078int ShClTransferValidatePath(const char *pcszPath, bool fMustExist)
3079{
3080 int rc = VINF_SUCCESS;
3081
3082 if (!strlen(pcszPath))
3083 rc = VERR_INVALID_PARAMETER;
3084
3085 if ( RT_SUCCESS(rc)
3086 && !RTStrIsValidEncoding(pcszPath))
3087 {
3088 rc = VERR_INVALID_UTF8_ENCODING;
3089 }
3090
3091 if ( RT_SUCCESS(rc)
3092 && RTStrStr(pcszPath, ".."))
3093 {
3094 rc = VERR_INVALID_PARAMETER;
3095 }
3096
3097 if ( RT_SUCCESS(rc)
3098 && fMustExist)
3099 {
3100 RTFSOBJINFO objInfo;
3101 rc = RTPathQueryInfo(pcszPath, &objInfo, RTFSOBJATTRADD_NOTHING);
3102 if (RT_SUCCESS(rc))
3103 {
3104 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
3105 {
3106 if (!RTDirExists(pcszPath)) /* Path must exist. */
3107 rc = VERR_PATH_NOT_FOUND;
3108 }
3109 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
3110 {
3111 if (!RTFileExists(pcszPath)) /* File must exist. */
3112 rc = VERR_FILE_NOT_FOUND;
3113 }
3114 else /* Everything else (e.g. symbolic links) are not supported. */
3115 {
3116 LogRelMax(64, ("Shared Clipboard: Path '%s' contains a symbolic link or junction, which are not supported\n", pcszPath));
3117 rc = VERR_NOT_SUPPORTED;
3118 }
3119 }
3120 }
3121
3122 if (RT_FAILURE(rc))
3123 LogRelMax(64, ("Shared Clipboard: Validating path '%s' failed: %Rrc\n", pcszPath, rc));
3124
3125 LogFlowFuncLeaveRC(rc);
3126 return rc;
3127}
3128
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