VirtualBox

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

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

Shared Clipboard/Transfers: Added ShClTransferCreateEx() and some defines for transfer defaults, more documentation for struct typedefs. bugref:9437

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