VirtualBox

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

Last change on this file since 100144 was 99966, checked in by vboxsync, 21 months ago

Shared Clipboard: Use a define for clipboard timeouts. bugref:9437

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

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