VirtualBox

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

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

Shared Clipboard: Renaming (ShClTransferCtxTransferUnregister() -> ShClTransferCtxTransferUnregisterById()). bugref:9437

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