VirtualBox

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

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

Shared Clipboard/Transfers: Docs. bugref:9437

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 94.2 KB
Line 
1/* $Id: clipboard-transfers.cpp 99403 2023-04-14 14:57: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.
1168 *
1169 * @returns VBox status code.
1170 * @param ppTransfer Where to return the created clipboard transfer struct.
1171 * Must be destroyed by ShClTransferDestroy().
1172 */
1173int ShClTransferCreate(PSHCLTRANSFER *ppTransfer)
1174{
1175 AssertPtrReturn(ppTransfer, VERR_INVALID_POINTER);
1176
1177 LogFlowFuncEnter();
1178
1179 PSHCLTRANSFER pTransfer = (PSHCLTRANSFER)RTMemAllocZ(sizeof(SHCLTRANSFER));
1180 AssertPtrReturn(pTransfer, VERR_NO_MEMORY);
1181
1182 pTransfer->State.uID = 0;
1183 pTransfer->State.enmStatus = SHCLTRANSFERSTATUS_NONE;
1184 pTransfer->State.enmDir = SHCLTRANSFERDIR_UNKNOWN;
1185 pTransfer->State.enmSource = SHCLSOURCE_INVALID;
1186
1187 pTransfer->Thread.hThread = NIL_RTTHREAD;
1188 pTransfer->Thread.fCancelled = false;
1189 pTransfer->Thread.fStarted = false;
1190 pTransfer->Thread.fStop = false;
1191
1192 pTransfer->pszPathRootAbs = NULL;
1193
1194#ifdef DEBUG_andy
1195 pTransfer->uTimeoutMs = RT_MS_5SEC;
1196#else
1197 pTransfer->uTimeoutMs = RT_MS_30SEC;
1198#endif
1199 pTransfer->cbMaxChunkSize = _64K; /** @todo Make this configurable. */
1200 pTransfer->cMaxListHandles = _4K; /** @todo Ditto. */
1201 pTransfer->cMaxObjHandles = _4K; /** @todo Ditto. */
1202
1203 pTransfer->pvUser = NULL;
1204 pTransfer->cbUser = 0;
1205
1206 RTListInit(&pTransfer->lstList);
1207 RTListInit(&pTransfer->lstObj);
1208
1209 pTransfer->cRoots = 0;
1210 RTListInit(&pTransfer->lstRoots);
1211
1212 int rc = ShClEventSourceCreate(&pTransfer->Events, 0 /* uID */);
1213 if (RT_SUCCESS(rc))
1214 {
1215 *ppTransfer = pTransfer;
1216 }
1217 else
1218 {
1219 if (pTransfer)
1220 {
1221 ShClTransferDestroy(pTransfer);
1222 RTMemFree(pTransfer);
1223 }
1224 }
1225
1226 LogFlowFuncLeaveRC(rc);
1227 return rc;
1228}
1229
1230/**
1231 * Destroys a clipboard transfer.
1232 *
1233 * @returns VBox status code.
1234 * @param pTransferCtx Clipboard transfer to destroy.
1235 */
1236int ShClTransferDestroy(PSHCLTRANSFER pTransfer)
1237{
1238 if (!pTransfer)
1239 return VINF_SUCCESS;
1240
1241 LogFlowFuncEnter();
1242
1243 int rc = shClTransferThreadDestroy(pTransfer, 30 * 1000 /* Timeout in ms */);
1244 if (RT_FAILURE(rc))
1245 return rc;
1246
1247 ShClTransferReset(pTransfer);
1248
1249 ShClEventSourceDestroy(&pTransfer->Events);
1250
1251 LogFlowFuncLeave();
1252 return VINF_SUCCESS;
1253}
1254
1255/**
1256 * Initializes a clipboard transfer.
1257 *
1258 * @returns VBox status code.
1259 * @param pTransfer Transfer to initialize.
1260 * @param enmDir Specifies the transfer direction of this transfer.
1261 * @param enmSource Specifies the data source of the transfer.
1262 */
1263int ShClTransferInit(PSHCLTRANSFER pTransfer, SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource)
1264{
1265 pTransfer->State.enmDir = enmDir;
1266 pTransfer->State.enmSource = enmSource;
1267
1268 LogFlowFunc(("uID=%RU32, enmDir=%RU32, enmSource=%RU32\n",
1269 pTransfer->State.uID, pTransfer->State.enmDir, pTransfer->State.enmSource));
1270
1271 pTransfer->State.enmStatus = SHCLTRANSFERSTATUS_INITIALIZED; /* Now we're ready to run. */
1272
1273 pTransfer->cListHandles = 0;
1274 pTransfer->uListHandleNext = 1;
1275
1276 pTransfer->cObjHandles = 0;
1277 pTransfer->uObjHandleNext = 1;
1278
1279 int rc = VINF_SUCCESS;
1280
1281 if (pTransfer->Callbacks.pfnOnInitialize)
1282 rc = pTransfer->Callbacks.pfnOnInitialize(&pTransfer->CallbackCtx);
1283
1284 LogFlowFuncLeaveRC(rc);
1285 return rc;
1286}
1287
1288/**
1289 * Returns a specific list handle info of a clipboard transfer.
1290 *
1291 * @returns Pointer to list handle info if found, or NULL if not found.
1292 * @param pTransfer Clipboard transfer to get list handle info from.
1293 * @param hList List handle of the list to get handle info for.
1294 */
1295DECLINLINE(PSHCLLISTHANDLEINFO) shClTransferListGetByHandle(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList)
1296{
1297 PSHCLLISTHANDLEINFO pIt;
1298 RTListForEach(&pTransfer->lstList, pIt, SHCLLISTHANDLEINFO, Node) /** @todo Sloooow ... improve this. */
1299 {
1300 if (pIt->hList == hList)
1301 return pIt;
1302 }
1303
1304 return NULL;
1305}
1306
1307/**
1308 * Creates a new list handle (local only).
1309 *
1310 * @returns New List handle on success, or SHCLLISTHANDLE_INVALID on error.
1311 * @param pTransfer Clipboard transfer to create new list handle for.
1312 */
1313DECLINLINE(SHCLLISTHANDLE) shClTransferListHandleNew(PSHCLTRANSFER pTransfer)
1314{
1315 return pTransfer->uListHandleNext++; /** @todo Good enough for now. Improve this later. */
1316}
1317
1318/**
1319 * Validates whether a given path matches our set of rules or not.
1320 *
1321 * @returns VBox status code.
1322 * @param pcszPath Path to validate.
1323 * @param fMustExist Whether the path to validate also must exist.
1324 */
1325static int shClTransferValidatePath(const char *pcszPath, bool fMustExist)
1326{
1327 int rc = VINF_SUCCESS;
1328
1329 if (!strlen(pcszPath))
1330 rc = VERR_INVALID_PARAMETER;
1331
1332 if ( RT_SUCCESS(rc)
1333 && !RTStrIsValidEncoding(pcszPath))
1334 {
1335 rc = VERR_INVALID_UTF8_ENCODING;
1336 }
1337
1338 if ( RT_SUCCESS(rc)
1339 && RTStrStr(pcszPath, ".."))
1340 {
1341 rc = VERR_INVALID_PARAMETER;
1342 }
1343
1344 if ( RT_SUCCESS(rc)
1345 && fMustExist)
1346 {
1347 RTFSOBJINFO objInfo;
1348 rc = RTPathQueryInfo(pcszPath, &objInfo, RTFSOBJATTRADD_NOTHING);
1349 if (RT_SUCCESS(rc))
1350 {
1351 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1352 {
1353 if (!RTDirExists(pcszPath)) /* Path must exist. */
1354 rc = VERR_PATH_NOT_FOUND;
1355 }
1356 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1357 {
1358 if (!RTFileExists(pcszPath)) /* File must exist. */
1359 rc = VERR_FILE_NOT_FOUND;
1360 }
1361 else /* Everything else (e.g. symbolic links) are not supported. */
1362 {
1363 LogRel2(("Shared Clipboard: Path '%s' contains a symbolic link or junktion, which are not supported\n", pcszPath));
1364 rc = VERR_NOT_SUPPORTED;
1365 }
1366 }
1367 }
1368
1369 if (RT_FAILURE(rc))
1370 LogRel2(("Shared Clipboard: Validating path '%s' failed: %Rrc\n", pcszPath, rc));
1371
1372 LogFlowFuncLeaveRC(rc);
1373 return rc;
1374}
1375
1376/**
1377 * Resolves a relative path of a specific transfer to its absolute path.
1378 *
1379 * @returns VBox status code.
1380 * @param pTransfer Clipboard transfer to resolve path for.
1381 * @param pszPath Path to resolve.
1382 * @param fFlags Resolve flags. Currently not used and must be 0.
1383 * @param ppszResolved Where to store the allocated resolved path. Must be free'd by the called using RTStrFree().
1384 */
1385static int shClTransferResolvePathAbs(PSHCLTRANSFER pTransfer, const char *pszPath, uint32_t fFlags,
1386 char **ppszResolved)
1387{
1388 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1389 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1390 AssertReturn (fFlags == 0, VERR_INVALID_PARAMETER);
1391
1392 LogFlowFunc(("pszPathRootAbs=%s, pszPath=%s\n", pTransfer->pszPathRootAbs, pszPath));
1393
1394 int rc = shClTransferValidatePath(pszPath, false /* fMustExist */);
1395 if (RT_SUCCESS(rc))
1396 {
1397 char *pszPathAbs = RTPathJoinA(pTransfer->pszPathRootAbs, pszPath);
1398 if (pszPathAbs)
1399 {
1400 char szResolved[RTPATH_MAX];
1401 size_t cbResolved = sizeof(szResolved);
1402 rc = RTPathAbsEx(pTransfer->pszPathRootAbs, pszPathAbs, RTPATH_STR_F_STYLE_HOST, szResolved, &cbResolved);
1403
1404 RTStrFree(pszPathAbs);
1405
1406 if (RT_SUCCESS(rc))
1407 {
1408 LogFlowFunc(("pszResolved=%s\n", szResolved));
1409
1410 rc = VERR_PATH_NOT_FOUND; /* Play safe by default. */
1411
1412 /* Make sure the resolved path is part of the set of root entries. */
1413 PSHCLLISTROOT pListRoot;
1414 RTListForEach(&pTransfer->lstRoots, pListRoot, SHCLLISTROOT, Node)
1415 {
1416 if (RTPathStartsWith(szResolved, pListRoot->pszPathAbs))
1417 {
1418 rc = VINF_SUCCESS;
1419 break;
1420 }
1421 }
1422
1423 if (RT_SUCCESS(rc))
1424 *ppszResolved = RTStrDup(szResolved);
1425 }
1426 }
1427 else
1428 rc = VERR_NO_MEMORY;
1429 }
1430
1431 if (RT_FAILURE(rc))
1432 LogRel(("Shared Clipboard: Resolving absolute path '%s' failed, rc=%Rrc\n", pszPath, rc));
1433
1434 LogFlowFuncLeaveRC(rc);
1435 return rc;
1436}
1437
1438/**
1439 * Opens a transfer list.
1440 *
1441 * @returns VBox status code.
1442 * @param pTransfer Clipboard transfer to handle.
1443 * @param pOpenParms List open parameters to use for opening.
1444 * @param phList Where to store the List handle of opened list on success.
1445 */
1446int ShClTransferListOpen(PSHCLTRANSFER pTransfer, PSHCLLISTOPENPARMS pOpenParms,
1447 PSHCLLISTHANDLE phList)
1448{
1449 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1450 AssertPtrReturn(pOpenParms, VERR_INVALID_POINTER);
1451 AssertPtrReturn(phList, VERR_INVALID_POINTER);
1452
1453 int rc;
1454
1455 if (pTransfer->cListHandles == pTransfer->cMaxListHandles)
1456 return VERR_SHCLPB_MAX_LISTS_REACHED;
1457
1458 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1459 {
1460 LogFlowFunc(("pszPath=%s\n", pOpenParms->pszPath));
1461
1462 PSHCLLISTHANDLEINFO pInfo
1463 = (PSHCLLISTHANDLEINFO)RTMemAllocZ(sizeof(SHCLLISTHANDLEINFO));
1464 if (pInfo)
1465 {
1466 rc = ShClTransferListHandleInfoInit(pInfo);
1467 if (RT_SUCCESS(rc))
1468 {
1469 rc = shClTransferResolvePathAbs(pTransfer, pOpenParms->pszPath, 0 /* fFlags */, &pInfo->pszPathLocalAbs);
1470 if (RT_SUCCESS(rc))
1471 {
1472 RTFSOBJINFO objInfo;
1473 rc = RTPathQueryInfo(pInfo->pszPathLocalAbs, &objInfo, RTFSOBJATTRADD_NOTHING);
1474 if (RT_SUCCESS(rc))
1475 {
1476 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1477 {
1478 rc = RTDirOpen(&pInfo->u.Local.hDir, pInfo->pszPathLocalAbs);
1479 if (RT_SUCCESS(rc))
1480 {
1481 pInfo->enmType = SHCLOBJTYPE_DIRECTORY;
1482
1483 LogRel2(("Shared Clipboard: Opening directory '%s'\n", pInfo->pszPathLocalAbs));
1484 }
1485 else
1486 LogRel(("Shared Clipboard: Opening directory '%s' failed with %Rrc\n", pInfo->pszPathLocalAbs, rc));
1487
1488 }
1489 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1490 {
1491 rc = RTFileOpen(&pInfo->u.Local.hFile, pInfo->pszPathLocalAbs,
1492 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
1493 if (RT_SUCCESS(rc))
1494 {
1495 pInfo->enmType = SHCLOBJTYPE_FILE;
1496
1497 LogRel2(("Shared Clipboard: Opening file '%s'\n", pInfo->pszPathLocalAbs));
1498 }
1499 else
1500 LogRel(("Shared Clipboard: Opening file '%s' failed with %Rrc\n", pInfo->pszPathLocalAbs, rc));
1501 }
1502 else
1503 rc = VERR_NOT_SUPPORTED;
1504
1505 if (RT_SUCCESS(rc))
1506 {
1507 pInfo->hList = shClTransferListHandleNew(pTransfer);
1508
1509 RTListAppend(&pTransfer->lstList, &pInfo->Node);
1510 pTransfer->cListHandles++;
1511
1512 if (phList)
1513 *phList = pInfo->hList;
1514
1515 LogFlowFunc(("pszPathLocalAbs=%s, hList=%RU64, cListHandles=%RU32\n",
1516 pInfo->pszPathLocalAbs, pInfo->hList, pTransfer->cListHandles));
1517 }
1518 else
1519 {
1520 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1521 {
1522 if (RTDirIsValid(pInfo->u.Local.hDir))
1523 RTDirClose(pInfo->u.Local.hDir);
1524 }
1525 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1526 {
1527 if (RTFileIsValid(pInfo->u.Local.hFile))
1528 RTFileClose(pInfo->u.Local.hFile);
1529 }
1530 }
1531 }
1532 }
1533 }
1534
1535 if (RT_FAILURE(rc))
1536 {
1537 ShClTransferListHandleInfoDestroy(pInfo);
1538
1539 RTMemFree(pInfo);
1540 pInfo = NULL;
1541 }
1542 }
1543 else
1544 rc = VERR_NO_MEMORY;
1545 }
1546 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1547 {
1548 if (pTransfer->ProviderIface.pfnListOpen)
1549 {
1550 rc = pTransfer->ProviderIface.pfnListOpen(&pTransfer->ProviderCtx, pOpenParms, phList);
1551 }
1552 else
1553 rc = VERR_NOT_SUPPORTED;
1554 }
1555 else
1556 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1557
1558 LogFlowFuncLeaveRC(rc);
1559 return rc;
1560}
1561
1562/**
1563 * Closes a transfer list.
1564 *
1565 * @returns VBox status code.
1566 * @param pTransfer Clipboard transfer to handle.
1567 * @param hList Handle of list to close.
1568 */
1569int ShClTransferListClose(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList)
1570{
1571 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1572
1573 if (hList == SHCLLISTHANDLE_INVALID)
1574 return VINF_SUCCESS;
1575
1576 int rc = VINF_SUCCESS;
1577
1578 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1579 {
1580 PSHCLLISTHANDLEINFO pInfo = shClTransferListGetByHandle(pTransfer, hList);
1581 if (pInfo)
1582 {
1583 switch (pInfo->enmType)
1584 {
1585 case SHCLOBJTYPE_DIRECTORY:
1586 {
1587 if (RTDirIsValid(pInfo->u.Local.hDir))
1588 {
1589 RTDirClose(pInfo->u.Local.hDir);
1590 pInfo->u.Local.hDir = NIL_RTDIR;
1591 }
1592 break;
1593 }
1594
1595 default:
1596 rc = VERR_NOT_SUPPORTED;
1597 break;
1598 }
1599
1600 RTListNodeRemove(&pInfo->Node);
1601
1602 Assert(pTransfer->cListHandles);
1603 pTransfer->cListHandles--;
1604
1605 RTMemFree(pInfo);
1606 }
1607 else
1608 rc = VERR_NOT_FOUND;
1609 }
1610 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1611 {
1612 if (pTransfer->ProviderIface.pfnListClose)
1613 {
1614 rc = pTransfer->ProviderIface.pfnListClose(&pTransfer->ProviderCtx, hList);
1615 }
1616 else
1617 rc = VERR_NOT_SUPPORTED;
1618 }
1619
1620 LogFlowFuncLeaveRC(rc);
1621 return rc;
1622}
1623
1624/**
1625 * Adds a file to a transfer list header.
1626 *
1627 * @returns VBox status code.
1628 * @param pHdr List header to add file to.
1629 * @param pszPath Path of file to add.
1630 */
1631static int shclTransferListHdrAddFile(PSHCLLISTHDR pHdr, const char *pszPath)
1632{
1633 AssertPtrReturn(pHdr, VERR_INVALID_POINTER);
1634 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
1635
1636 uint64_t cbSize = 0;
1637 int rc = RTFileQuerySizeByPath(pszPath, &cbSize);
1638 if (RT_SUCCESS(rc))
1639 {
1640 pHdr->cbTotalSize += cbSize;
1641 pHdr->cTotalObjects++;
1642 }
1643
1644 LogFlowFuncLeaveRC(rc);
1645 return rc;
1646}
1647
1648/**
1649 * Builds a transfer list header, internal version.
1650 *
1651 * @returns VBox status code.
1652 * @param pHdr Where to store the build list header.
1653 * @param pcszPathAbs Absolute path to use for building the transfer list.
1654 */
1655static int shclTransferListHdrFromDir(PSHCLLISTHDR pHdr, const char *pcszPathAbs)
1656{
1657 AssertPtrReturn(pcszPathAbs, VERR_INVALID_POINTER);
1658
1659 LogFlowFunc(("pcszPathAbs=%s\n", pcszPathAbs));
1660
1661 RTFSOBJINFO objInfo;
1662 int rc = RTPathQueryInfo(pcszPathAbs, &objInfo, RTFSOBJATTRADD_NOTHING);
1663 if (RT_SUCCESS(rc))
1664 {
1665 if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
1666 {
1667 RTDIR hDir;
1668 rc = RTDirOpen(&hDir, pcszPathAbs);
1669 if (RT_SUCCESS(rc))
1670 {
1671 size_t cbDirEntry = 0;
1672 PRTDIRENTRYEX pDirEntry = NULL;
1673 do
1674 {
1675 /* Retrieve the next directory entry. */
1676 rc = RTDirReadExA(hDir, &pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1677 if (RT_FAILURE(rc))
1678 {
1679 if (rc == VERR_NO_MORE_FILES)
1680 rc = VINF_SUCCESS;
1681 break;
1682 }
1683
1684 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
1685 {
1686 case RTFS_TYPE_DIRECTORY:
1687 {
1688 /* Skip "." and ".." entries. */
1689 if (RTDirEntryExIsStdDotLink(pDirEntry))
1690 break;
1691
1692 pHdr->cTotalObjects++;
1693 break;
1694 }
1695 case RTFS_TYPE_FILE:
1696 {
1697 char *pszSrc = RTPathJoinA(pcszPathAbs, pDirEntry->szName);
1698 if (pszSrc)
1699 {
1700 rc = shclTransferListHdrAddFile(pHdr, pszSrc);
1701 RTStrFree(pszSrc);
1702 }
1703 else
1704 rc = VERR_NO_MEMORY;
1705 break;
1706 }
1707 case RTFS_TYPE_SYMLINK:
1708 {
1709 /** @todo Not implemented yet. */
1710 }
1711
1712 default:
1713 break;
1714 }
1715
1716 } while (RT_SUCCESS(rc));
1717
1718 RTDirReadExAFree(&pDirEntry, &cbDirEntry);
1719 RTDirClose(hDir);
1720 }
1721 }
1722 else if (RTFS_IS_FILE(objInfo.Attr.fMode))
1723 {
1724 rc = shclTransferListHdrAddFile(pHdr, pcszPathAbs);
1725 }
1726 else if (RTFS_IS_SYMLINK(objInfo.Attr.fMode))
1727 {
1728 /** @todo Not implemented yet. */
1729 }
1730 else
1731 rc = VERR_NOT_SUPPORTED;
1732 }
1733
1734 LogFlowFuncLeaveRC(rc);
1735 return rc;
1736}
1737
1738/**
1739 * Retrieves the header of a transfer list.
1740 *
1741 * @returns VBox status code.
1742 * @param pTransfer Clipboard transfer to handle.
1743 * @param hList Handle of list to get header for.
1744 * @param pHdr Where to store the returned list header information.
1745 */
1746int ShClTransferListGetHeader(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList,
1747 PSHCLLISTHDR pHdr)
1748{
1749 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1750 AssertPtrReturn(pHdr, VERR_INVALID_POINTER);
1751
1752 int rc;
1753
1754 LogFlowFunc(("hList=%RU64\n", hList));
1755
1756 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1757 {
1758 PSHCLLISTHANDLEINFO pInfo = shClTransferListGetByHandle(pTransfer, hList);
1759 if (pInfo)
1760 {
1761 rc = ShClTransferListHdrInit(pHdr);
1762 if (RT_SUCCESS(rc))
1763 {
1764 switch (pInfo->enmType)
1765 {
1766 case SHCLOBJTYPE_DIRECTORY:
1767 {
1768 LogFlowFunc(("DirAbs: %s\n", pInfo->pszPathLocalAbs));
1769
1770 rc = shclTransferListHdrFromDir(pHdr, pInfo->pszPathLocalAbs);
1771 break;
1772 }
1773
1774 case SHCLOBJTYPE_FILE:
1775 {
1776 LogFlowFunc(("FileAbs: %s\n", pInfo->pszPathLocalAbs));
1777
1778 pHdr->cTotalObjects = 1;
1779
1780 RTFSOBJINFO objInfo;
1781 rc = RTFileQueryInfo(pInfo->u.Local.hFile, &objInfo, RTFSOBJATTRADD_NOTHING);
1782 if (RT_SUCCESS(rc))
1783 {
1784 pHdr->cbTotalSize = objInfo.cbObject;
1785 }
1786 break;
1787 }
1788
1789 default:
1790 rc = VERR_NOT_SUPPORTED;
1791 break;
1792 }
1793 }
1794
1795 LogFlowFunc(("cTotalObj=%RU64, cbTotalSize=%RU64\n", pHdr->cTotalObjects, pHdr->cbTotalSize));
1796 }
1797 else
1798 rc = VERR_NOT_FOUND;
1799 }
1800 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1801 {
1802 if (pTransfer->ProviderIface.pfnListHdrRead)
1803 {
1804 rc = pTransfer->ProviderIface.pfnListHdrRead(&pTransfer->ProviderCtx, hList, pHdr);
1805 }
1806 else
1807 rc = VERR_NOT_SUPPORTED;
1808 }
1809 else
1810 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1811
1812 LogFlowFuncLeaveRC(rc);
1813 return rc;
1814}
1815
1816/**
1817 * Returns the current transfer object of a transfer list.
1818 *
1819 * Currently not implemented and wil return NULL.
1820 *
1821 * @returns Pointer to transfer object, or NULL if not found / invalid.
1822 * @param pTransfer Clipboard transfer to return transfer object for.
1823 * @param hList Handle of clipboard transfer list to get object for.
1824 * @param uIdx Index of object to get.
1825 */
1826PSHCLTRANSFEROBJ ShClTransferListGetObj(PSHCLTRANSFER pTransfer,
1827 SHCLLISTHANDLE hList, uint64_t uIdx)
1828{
1829 AssertPtrReturn(pTransfer, NULL);
1830
1831 RT_NOREF(hList, uIdx);
1832
1833 LogFlowFunc(("hList=%RU64\n", hList));
1834
1835 return NULL;
1836}
1837
1838/**
1839 * Reads a single transfer list entry.
1840 *
1841 * @returns VBox status code or VERR_NO_MORE_FILES if the end of the list has been reached.
1842 * @param pTransfer Clipboard transfer to handle.
1843 * @param hList List handle of list to read from.
1844 * @param pEntry Where to store the read information.
1845 */
1846int ShClTransferListRead(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList,
1847 PSHCLLISTENTRY pEntry)
1848{
1849 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
1850 AssertPtrReturn(pEntry, VERR_INVALID_POINTER);
1851
1852 int rc = VINF_SUCCESS;
1853
1854 LogFlowFunc(("hList=%RU64\n", hList));
1855
1856 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
1857 {
1858 PSHCLLISTHANDLEINFO pInfo = shClTransferListGetByHandle(pTransfer, hList);
1859 if (pInfo)
1860 {
1861 switch (pInfo->enmType)
1862 {
1863 case SHCLOBJTYPE_DIRECTORY:
1864 {
1865 LogFlowFunc(("\tDirectory: %s\n", pInfo->pszPathLocalAbs));
1866
1867 for (;;)
1868 {
1869 bool fSkipEntry = false; /* Whether to skip an entry in the enumeration. */
1870
1871 size_t cbDirEntry = 0;
1872 PRTDIRENTRYEX pDirEntry = NULL;
1873 rc = RTDirReadExA(pInfo->u.Local.hDir, &pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
1874 if (RT_SUCCESS(rc))
1875 {
1876 switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
1877 {
1878 case RTFS_TYPE_DIRECTORY:
1879 {
1880 /* Skip "." and ".." entries. */
1881 if (RTDirEntryExIsStdDotLink(pDirEntry))
1882 {
1883 fSkipEntry = true;
1884 break;
1885 }
1886
1887 LogFlowFunc(("Directory: %s\n", pDirEntry->szName));
1888 break;
1889 }
1890
1891 case RTFS_TYPE_FILE:
1892 {
1893 LogFlowFunc(("File: %s\n", pDirEntry->szName));
1894 break;
1895 }
1896
1897 case RTFS_TYPE_SYMLINK:
1898 {
1899 rc = VERR_NOT_IMPLEMENTED; /** @todo Not implemented yet. */
1900 break;
1901 }
1902
1903 default:
1904 break;
1905 }
1906
1907 if ( RT_SUCCESS(rc)
1908 && !fSkipEntry)
1909 {
1910 rc = RTStrCopy(pEntry->pszName, pEntry->cbName, pDirEntry->szName);
1911 if (RT_SUCCESS(rc))
1912 {
1913 pEntry->cbName = (uint32_t)strlen(pEntry->pszName) + 1; /* Include termination. */
1914
1915 AssertPtr(pEntry->pvInfo);
1916 Assert (pEntry->cbInfo == sizeof(SHCLFSOBJINFO));
1917
1918 ShClFsObjFromIPRT(PSHCLFSOBJINFO(pEntry->pvInfo), &pDirEntry->Info);
1919
1920 LogFlowFunc(("Entry pszName=%s, pvInfo=%p, cbInfo=%RU32\n",
1921 pEntry->pszName, pEntry->pvInfo, pEntry->cbInfo));
1922 }
1923 }
1924
1925 RTDirReadExAFree(&pDirEntry, &cbDirEntry);
1926 }
1927
1928 if ( !fSkipEntry /* Do we have a valid entry? Bail out. */
1929 || RT_FAILURE(rc))
1930 {
1931 break;
1932 }
1933 }
1934
1935 break;
1936 }
1937
1938 case SHCLOBJTYPE_FILE:
1939 {
1940 LogFlowFunc(("\tSingle file: %s\n", pInfo->pszPathLocalAbs));
1941
1942 RTFSOBJINFO objInfo;
1943 rc = RTFileQueryInfo(pInfo->u.Local.hFile, &objInfo, RTFSOBJATTRADD_NOTHING);
1944 if (RT_SUCCESS(rc))
1945 {
1946 pEntry->pvInfo = (PSHCLFSOBJINFO)RTMemAlloc(sizeof(SHCLFSOBJINFO));
1947 if (pEntry->pvInfo)
1948 {
1949 rc = RTStrCopy(pEntry->pszName, pEntry->cbName, pInfo->pszPathLocalAbs);
1950 if (RT_SUCCESS(rc))
1951 {
1952 ShClFsObjFromIPRT(PSHCLFSOBJINFO(pEntry->pvInfo), &objInfo);
1953
1954 pEntry->cbInfo = sizeof(SHCLFSOBJINFO);
1955 pEntry->fInfo = VBOX_SHCL_INFO_FLAG_FSOBJINFO;
1956 }
1957 }
1958 else
1959 rc = VERR_NO_MEMORY;
1960 }
1961
1962 break;
1963 }
1964
1965 default:
1966 rc = VERR_NOT_SUPPORTED;
1967 break;
1968 }
1969 }
1970 else
1971 rc = VERR_NOT_FOUND;
1972 }
1973 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
1974 {
1975 if (pTransfer->ProviderIface.pfnListEntryRead)
1976 rc = pTransfer->ProviderIface.pfnListEntryRead(&pTransfer->ProviderCtx, hList, pEntry);
1977 else
1978 rc = VERR_NOT_SUPPORTED;
1979 }
1980 else
1981 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1982
1983 LogFlowFuncLeaveRC(rc);
1984 return rc;
1985}
1986
1987int ShClTransferListWrite(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList,
1988 PSHCLLISTENTRY pEntry)
1989{
1990 RT_NOREF(pTransfer, hList, pEntry);
1991
1992 int rc = VINF_SUCCESS;
1993
1994#if 0
1995 if (pTransfer->ProviderIface.pfnListEntryWrite)
1996 rc = pTransfer->ProviderIface.pfnListEntryWrite(&pTransfer->ProviderCtx, hList, pEntry);
1997#endif
1998
1999 LogFlowFuncLeaveRC(rc);
2000 return rc;
2001}
2002
2003/**
2004 * Returns whether a given transfer list handle is valid or not.
2005 *
2006 * @returns \c true if list handle is valid, \c false if not.
2007 * @param pTransfer Clipboard transfer to handle.
2008 * @param hList List handle to check.
2009 */
2010bool ShClTransferListHandleIsValid(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList)
2011{
2012 bool fIsValid = false;
2013
2014 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
2015 {
2016 fIsValid = shClTransferListGetByHandle(pTransfer, hList) != NULL;
2017 }
2018 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
2019 {
2020 AssertFailed(); /** @todo Implement. */
2021 }
2022 else
2023 AssertFailedStmt(fIsValid = false);
2024
2025 return fIsValid;
2026}
2027
2028/**
2029 * Copies a transfer callback table from source to destination.
2030 *
2031 * @param pCallbacksDst Callback destination.
2032 * @param pCallbacksSrc Callback source. If set to NULL, the
2033 * destination callback table will be unset.
2034 */
2035void ShClTransferCopyCallbacks(PSHCLTRANSFERCALLBACKTABLE pCallbacksDst,
2036 PSHCLTRANSFERCALLBACKTABLE pCallbacksSrc)
2037{
2038 AssertPtrReturnVoid(pCallbacksDst);
2039
2040 if (pCallbacksSrc) /* Set */
2041 {
2042#define SET_CALLBACK(a_pfnCallback) \
2043 if (pCallbacksSrc->a_pfnCallback) \
2044 pCallbacksDst->a_pfnCallback = pCallbacksSrc->a_pfnCallback
2045
2046 SET_CALLBACK(pfnOnInitialize);
2047 SET_CALLBACK(pfnOnStart);
2048 SET_CALLBACK(pfnOnCompleted);
2049 SET_CALLBACK(pfnOnError);
2050 SET_CALLBACK(pfnOnRegistered);
2051 SET_CALLBACK(pfnOnUnregistered);
2052
2053#undef SET_CALLBACK
2054
2055 pCallbacksDst->pvUser = pCallbacksSrc->pvUser;
2056 pCallbacksDst->cbUser = pCallbacksSrc->cbUser;
2057 }
2058 else /* Unset */
2059 RT_BZERO(pCallbacksDst, sizeof(SHCLTRANSFERCALLBACKTABLE));
2060}
2061
2062/**
2063 * Sets or unsets the callback table to be used for a clipboard transfer.
2064 *
2065 * @returns VBox status code.
2066 * @param pTransfer Clipboard transfer to set callbacks for.
2067 * @param pCallbacks Pointer to callback table to set. If set to NULL,
2068 * existing callbacks for this transfer will be unset.
2069 */
2070void ShClTransferSetCallbacks(PSHCLTRANSFER pTransfer,
2071 PSHCLTRANSFERCALLBACKTABLE pCallbacks)
2072{
2073 AssertPtrReturnVoid(pTransfer);
2074 /* pCallbacks can be NULL. */
2075
2076 ShClTransferCopyCallbacks(&pTransfer->Callbacks, pCallbacks);
2077}
2078
2079/**
2080 * Sets the transfer provider interface for a given transfer.
2081 *
2082 * @returns VBox status code.
2083 * @param pTransfer Transfer to create transfer provider for.
2084 * @param pCreationCtx Provider creation context to use for provider creation.
2085 */
2086int ShClTransferSetProviderIface(PSHCLTRANSFER pTransfer,
2087 PSHCLTXPROVIDERCREATIONCTX pCreationCtx)
2088{
2089 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2090 AssertPtrReturn(pCreationCtx, VERR_INVALID_POINTER);
2091
2092 LogFlowFuncEnter();
2093
2094 int rc = VINF_SUCCESS;
2095
2096 pTransfer->ProviderIface = pCreationCtx->Interface;
2097 pTransfer->ProviderCtx.pTransfer = pTransfer;
2098 pTransfer->ProviderCtx.pvUser = pCreationCtx->pvUser;
2099
2100 LogFlowFuncLeaveRC(rc);
2101 return rc;
2102}
2103
2104/**
2105 * Clears (resets) the root list of a clipboard transfer.
2106 *
2107 * @param pTransfer Transfer to clear transfer root list for.
2108 */
2109static void shClTransferListRootsClear(PSHCLTRANSFER pTransfer)
2110{
2111 AssertPtrReturnVoid(pTransfer);
2112
2113 if (pTransfer->pszPathRootAbs)
2114 {
2115 RTStrFree(pTransfer->pszPathRootAbs);
2116 pTransfer->pszPathRootAbs = NULL;
2117 }
2118
2119 PSHCLLISTROOT pListRoot, pListRootNext;
2120 RTListForEachSafe(&pTransfer->lstRoots, pListRoot, pListRootNext, SHCLLISTROOT, Node)
2121 {
2122 RTStrFree(pListRoot->pszPathAbs);
2123
2124 RTListNodeRemove(&pListRoot->Node);
2125
2126 RTMemFree(pListRoot);
2127 pListRoot = NULL;
2128 }
2129
2130 pTransfer->cRoots = 0;
2131}
2132
2133/**
2134 * Resets a clipboard transfer.
2135 *
2136 * @param pTransfer Clipboard transfer to reset.
2137 */
2138void ShClTransferReset(PSHCLTRANSFER pTransfer)
2139{
2140 AssertPtrReturnVoid(pTransfer);
2141
2142 LogFlowFuncEnter();
2143
2144 shClTransferListRootsClear(pTransfer);
2145
2146 PSHCLLISTHANDLEINFO pItList, pItListNext;
2147 RTListForEachSafe(&pTransfer->lstList, pItList, pItListNext, SHCLLISTHANDLEINFO, Node)
2148 {
2149 ShClTransferListHandleInfoDestroy(pItList);
2150
2151 RTListNodeRemove(&pItList->Node);
2152
2153 RTMemFree(pItList);
2154 }
2155
2156 PSHCLOBJHANDLEINFO pItObj, pItObjNext;
2157 RTListForEachSafe(&pTransfer->lstObj, pItObj, pItObjNext, SHCLOBJHANDLEINFO, Node)
2158 {
2159 ShClTransferObjHandleInfoDestroy(pItObj);
2160
2161 RTListNodeRemove(&pItObj->Node);
2162
2163 RTMemFree(pItObj);
2164 }
2165}
2166
2167/**
2168 * Returns the number of transfer root list entries.
2169 *
2170 * @returns Root list entry count.
2171 * @param pTransfer Clipboard transfer to return root entry count for.
2172 */
2173uint32_t ShClTransferRootsCount(PSHCLTRANSFER pTransfer)
2174{
2175 AssertPtrReturn(pTransfer, 0);
2176
2177 LogFlowFunc(("[Transfer %RU32] cRoots=%RU64\n", pTransfer->State.uID, pTransfer->cRoots));
2178 return (uint32_t)pTransfer->cRoots;
2179}
2180
2181/**
2182 * Returns a specific root list entry of a transfer.
2183 *
2184 * @returns Pointer to root list entry if found, or NULL if not found.
2185 * @param pTransfer Clipboard transfer to get root list entry from.
2186 * @param uIdx Index of root list entry to return.
2187 */
2188DECLINLINE(PSHCLLISTROOT) shClTransferRootsGetInternal(PSHCLTRANSFER pTransfer, uint32_t uIdx)
2189{
2190 if (uIdx >= pTransfer->cRoots)
2191 return NULL;
2192
2193 PSHCLLISTROOT pIt = RTListGetFirst(&pTransfer->lstRoots, SHCLLISTROOT, Node);
2194 while (uIdx--) /** @todo Slow, but works for now. */
2195 pIt = RTListGetNext(&pTransfer->lstRoots, pIt, SHCLLISTROOT, Node);
2196
2197 return pIt;
2198}
2199
2200/**
2201 * Get a specific root list entry.
2202 *
2203 * @returns VBox status code.
2204 * @param pTransfer Clipboard transfer to get root list entry of.
2205 * @param uIndex Index (zero-based) of entry to get.
2206 * @param pEntry Where to store the returned entry on success.
2207 */
2208int ShClTransferRootsEntry(PSHCLTRANSFER pTransfer,
2209 uint64_t uIndex, PSHCLROOTLISTENTRY pEntry)
2210{
2211 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2212 AssertPtrReturn(pEntry, VERR_INVALID_POINTER);
2213
2214 if (uIndex >= pTransfer->cRoots)
2215 return VERR_INVALID_PARAMETER;
2216
2217 int rc;
2218
2219 PSHCLLISTROOT pRoot = shClTransferRootsGetInternal(pTransfer, uIndex);
2220 AssertPtrReturn(pRoot, VERR_INVALID_PARAMETER);
2221
2222 /* Make sure that we only advertise relative source paths, not absolute ones. */
2223 const char *pcszSrcPath = pRoot->pszPathAbs;
2224
2225 char *pszFileName = RTPathFilename(pcszSrcPath);
2226 if (pszFileName)
2227 {
2228 Assert(pszFileName >= pcszSrcPath);
2229 size_t cchDstBase = pszFileName - pcszSrcPath;
2230 const char *pszDstPath = &pcszSrcPath[cchDstBase];
2231
2232 LogFlowFunc(("pcszSrcPath=%s, pszDstPath=%s\n", pcszSrcPath, pszDstPath));
2233
2234 rc = ShClTransferListEntryInit(pEntry);
2235 if (RT_SUCCESS(rc))
2236 {
2237 rc = RTStrCopy(pEntry->pszName, pEntry->cbName, pszDstPath);
2238 if (RT_SUCCESS(rc))
2239 {
2240 pEntry->cbInfo = sizeof(SHCLFSOBJINFO);
2241 pEntry->pvInfo = (PSHCLFSOBJINFO)RTMemAlloc(pEntry->cbInfo);
2242 if (pEntry->pvInfo)
2243 {
2244 RTFSOBJINFO fsObjInfo;
2245 rc = RTPathQueryInfo(pcszSrcPath, &fsObjInfo, RTFSOBJATTRADD_NOTHING);
2246 if (RT_SUCCESS(rc))
2247 {
2248 ShClFsObjFromIPRT(PSHCLFSOBJINFO(pEntry->pvInfo), &fsObjInfo);
2249
2250 pEntry->fInfo = VBOX_SHCL_INFO_FLAG_FSOBJINFO;
2251 }
2252 }
2253 else
2254 rc = VERR_NO_MEMORY;
2255 }
2256 }
2257 }
2258 else
2259 rc = VERR_INVALID_POINTER;
2260
2261 LogFlowFuncLeaveRC(rc);
2262 return rc;
2263}
2264
2265/**
2266 * Returns the root entries of a clipboard transfer.
2267 *
2268 * @returns VBox status code.
2269 * @param pTransfer Clipboard transfer to return root entries for.
2270 * @param ppRootList Where to store the root list on success.
2271 */
2272int ShClTransferRootsGet(PSHCLTRANSFER pTransfer, PSHCLROOTLIST *ppRootList)
2273{
2274 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2275 AssertPtrReturn(ppRootList, VERR_INVALID_POINTER);
2276
2277 LogFlowFuncEnter();
2278
2279 int rc = VINF_SUCCESS;
2280
2281 if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
2282 {
2283 PSHCLROOTLIST pRootList = ShClTransferRootListAlloc();
2284 if (!pRootList)
2285 return VERR_NO_MEMORY;
2286
2287 const uint64_t cRoots = (uint32_t)pTransfer->cRoots;
2288
2289 LogFlowFunc(("cRoots=%RU64\n", cRoots));
2290
2291 if (cRoots)
2292 {
2293 PSHCLROOTLISTENTRY paRootListEntries
2294 = (PSHCLROOTLISTENTRY)RTMemAllocZ(cRoots * sizeof(SHCLROOTLISTENTRY));
2295 if (paRootListEntries)
2296 {
2297 for (uint64_t i = 0; i < cRoots; ++i)
2298 {
2299 rc = ShClTransferRootsEntry(pTransfer, i, &paRootListEntries[i]);
2300 if (RT_FAILURE(rc))
2301 break;
2302 }
2303
2304 if (RT_SUCCESS(rc))
2305 pRootList->paEntries = paRootListEntries;
2306 }
2307 else
2308 rc = VERR_NO_MEMORY;
2309 }
2310 else
2311 rc = VERR_NOT_FOUND;
2312
2313 if (RT_SUCCESS(rc))
2314 {
2315 pRootList->Hdr.cRoots = cRoots;
2316 pRootList->Hdr.fRoots = 0; /** @todo Implement this. */
2317
2318 *ppRootList = pRootList;
2319 }
2320 }
2321 else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
2322 {
2323 if (pTransfer->ProviderIface.pfnRootsGet)
2324 rc = pTransfer->ProviderIface.pfnRootsGet(&pTransfer->ProviderCtx, ppRootList);
2325 else
2326 rc = VERR_NOT_SUPPORTED;
2327 }
2328 else
2329 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
2330
2331 LogFlowFuncLeaveRC(rc);
2332 return rc;
2333}
2334
2335/**
2336 * Sets root list entries for a given clipboard transfer.
2337 *
2338 * @returns VBox status code.
2339 * @param pTransfer Transfer to set transfer list entries for.
2340 * @param pszRoots String list (separated by CRLF) of root entries to set.
2341 * All entries must have the same root path.
2342 * @param cbRoots Size (in bytes) of string list.
2343 */
2344int ShClTransferRootsSet(PSHCLTRANSFER pTransfer, const char *pszRoots, size_t cbRoots)
2345{
2346 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2347 AssertPtrReturn(pszRoots, VERR_INVALID_POINTER);
2348 AssertReturn(cbRoots, VERR_INVALID_PARAMETER);
2349
2350 if (!RTStrIsValidEncoding(pszRoots))
2351 return VERR_INVALID_UTF8_ENCODING;
2352
2353 int rc = VINF_SUCCESS;
2354
2355 shClTransferListRootsClear(pTransfer);
2356
2357 char *pszPathRootAbs = NULL;
2358
2359 RTCList<RTCString> lstRootEntries = RTCString(pszRoots, cbRoots - 1).split("\r\n");
2360 for (size_t i = 0; i < lstRootEntries.size(); ++i)
2361 {
2362 PSHCLLISTROOT pListRoot = (PSHCLLISTROOT)RTMemAlloc(sizeof(SHCLLISTROOT));
2363 AssertPtrBreakStmt(pListRoot, rc = VERR_NO_MEMORY);
2364
2365 const char *pszPathCur = RTStrDup(lstRootEntries.at(i).c_str());
2366
2367 LogFlowFunc(("pszPathCur=%s\n", pszPathCur));
2368
2369 /* No root path determined yet? */
2370 if (!pszPathRootAbs)
2371 {
2372 pszPathRootAbs = RTStrDup(pszPathCur);
2373 if (pszPathRootAbs)
2374 {
2375 RTPathStripFilename(pszPathRootAbs);
2376
2377 LogFlowFunc(("pszPathRootAbs=%s\n", pszPathRootAbs));
2378
2379 /* We don't want to have a relative directory here. */
2380 if (RTPathStartsWithRoot(pszPathRootAbs))
2381 {
2382 rc = shClTransferValidatePath(pszPathRootAbs, true /* Path must exist */);
2383 }
2384 else
2385 rc = VERR_INVALID_PARAMETER;
2386 }
2387 else
2388 rc = VERR_NO_MEMORY;
2389 }
2390
2391 if (RT_FAILURE(rc))
2392 break;
2393
2394 pListRoot->pszPathAbs = RTStrDup(pszPathCur);
2395 if (!pListRoot->pszPathAbs)
2396 {
2397 rc = VERR_NO_MEMORY;
2398 break;
2399 }
2400
2401 RTListAppend(&pTransfer->lstRoots, &pListRoot->Node);
2402
2403 pTransfer->cRoots++;
2404 }
2405
2406 /* No (valid) root directory found? Bail out early. */
2407 if (!pszPathRootAbs)
2408 rc = VERR_PATH_NOT_FOUND;
2409
2410 if (RT_SUCCESS(rc))
2411 {
2412 /*
2413 * Step 2:
2414 * Go through the created list and make sure all entries have the same root path.
2415 */
2416 PSHCLLISTROOT pListRoot;
2417 RTListForEach(&pTransfer->lstRoots, pListRoot, SHCLLISTROOT, Node)
2418 {
2419 if (!RTStrStartsWith(pListRoot->pszPathAbs, pszPathRootAbs))
2420 {
2421 rc = VERR_INVALID_PARAMETER;
2422 break;
2423 }
2424
2425 rc = shClTransferValidatePath(pListRoot->pszPathAbs, true /* Path must exist */);
2426 if (RT_FAILURE(rc))
2427 break;
2428 }
2429 }
2430
2431 /** @todo Entry rollback on failure? */
2432
2433 if (RT_SUCCESS(rc))
2434 {
2435 pTransfer->pszPathRootAbs = pszPathRootAbs;
2436 LogFlowFunc(("pszPathRootAbs=%s, cRoots=%zu\n", pTransfer->pszPathRootAbs, pTransfer->cRoots));
2437
2438 LogRel2(("Shared Clipboard: Transfer uses root '%s'\n", pTransfer->pszPathRootAbs));
2439 }
2440 else
2441 {
2442 LogRel(("Shared Clipboard: Unable to set roots for transfer, rc=%Rrc\n", rc));
2443 RTStrFree(pszPathRootAbs);
2444 }
2445
2446 LogFlowFuncLeaveRC(rc);
2447 return rc;
2448}
2449
2450/**
2451 * Returns the clipboard transfer's ID.
2452 *
2453 * @returns The transfer's ID.
2454 * @param pTransfer Clipboard transfer to return ID for.
2455 */
2456SHCLTRANSFERID ShClTransferGetID(PSHCLTRANSFER pTransfer)
2457{
2458 AssertPtrReturn(pTransfer, 0);
2459
2460 return pTransfer->State.uID;
2461}
2462
2463/**
2464 * Returns the clipboard transfer's direction.
2465 *
2466 * @returns The transfer's direction.
2467 * @param pTransfer Clipboard transfer to return direction for.
2468 */
2469SHCLTRANSFERDIR ShClTransferGetDir(PSHCLTRANSFER pTransfer)
2470{
2471 AssertPtrReturn(pTransfer, SHCLTRANSFERDIR_UNKNOWN);
2472
2473 LogFlowFunc(("[Transfer %RU32] enmDir=%RU32\n", pTransfer->State.uID, pTransfer->State.enmDir));
2474 return pTransfer->State.enmDir;
2475}
2476
2477/**
2478 * Returns the transfer's source.
2479 *
2480 * @returns The transfer's source.
2481 * @param pTransfer Clipboard transfer to return source for.
2482 */
2483SHCLSOURCE ShClTransferGetSource(PSHCLTRANSFER pTransfer)
2484{
2485 AssertPtrReturn(pTransfer, SHCLSOURCE_INVALID);
2486
2487 LogFlowFunc(("[Transfer %RU32] enmSource=%RU32\n", pTransfer->State.uID, pTransfer->State.enmSource));
2488 return pTransfer->State.enmSource;
2489}
2490
2491/**
2492 * Returns the current transfer status.
2493 *
2494 * @returns Current transfer status.
2495 * @param pTransfer Clipboard transfer to return status for.
2496 */
2497SHCLTRANSFERSTATUS ShClTransferGetStatus(PSHCLTRANSFER pTransfer)
2498{
2499 AssertPtrReturn(pTransfer, SHCLTRANSFERSTATUS_NONE);
2500
2501 LogFlowFunc(("[Transfer %RU32] enmStatus=%RU32\n", pTransfer->State.uID, pTransfer->State.enmStatus));
2502 return pTransfer->State.enmStatus;
2503}
2504
2505/**
2506 * Runs a started clipboard transfer in a dedicated thread.
2507 *
2508 * @returns VBox status code.
2509 * @param pTransfer Clipboard transfer to run.
2510 * @param pfnThreadFunc Pointer to thread function to use.
2511 * @param pvUser Pointer to user-provided data. Optional.
2512 */
2513int ShClTransferRun(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser)
2514{
2515 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2516 AssertPtrReturn(pfnThreadFunc, VERR_INVALID_POINTER);
2517 /* pvUser is optional. */
2518
2519 AssertMsgReturn(pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_STARTED,
2520 ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
2521 VERR_WRONG_ORDER);
2522
2523 int rc = shClTransferThreadCreate(pTransfer, pfnThreadFunc, pvUser);
2524
2525 LogFlowFuncLeaveRC(rc);
2526 return rc;
2527}
2528
2529/**
2530 * Starts an initialized transfer.
2531 *
2532 * @returns VBox status code.
2533 * @param pTransfer Clipboard transfer to start.
2534 */
2535int ShClTransferStart(PSHCLTRANSFER pTransfer)
2536{
2537 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2538
2539 LogFlowFuncEnter();
2540
2541 /* Ready to start? */
2542 AssertMsgReturn(pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_INITIALIZED,
2543 ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
2544 VERR_WRONG_ORDER);
2545
2546 int rc;
2547
2548 if (pTransfer->Callbacks.pfnOnStart)
2549 {
2550 rc = pTransfer->Callbacks.pfnOnStart(&pTransfer->CallbackCtx);
2551 }
2552 else
2553 rc = VINF_SUCCESS;
2554
2555 if (RT_SUCCESS(rc))
2556 {
2557 pTransfer->State.enmStatus = SHCLTRANSFERSTATUS_STARTED;
2558 }
2559
2560 LogFlowFuncLeaveRC(rc);
2561 return rc;
2562}
2563
2564/**
2565 * Creates a thread for a clipboard transfer.
2566 *
2567 * @returns VBox status code.
2568 * @param pTransfer Clipboard transfer to create thread for.
2569 * @param pfnThreadFunc Thread function to use for this transfer.
2570 * @param pvUser Pointer to user-provided data.
2571 */
2572static int shClTransferThreadCreate(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser)
2573
2574{
2575 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2576
2577 /* Already marked for stopping? */
2578 AssertMsgReturn(pTransfer->Thread.fStop == false,
2579 ("Transfer thread already marked for stopping"), VERR_WRONG_ORDER);
2580 /* Already started? */
2581 AssertMsgReturn(pTransfer->Thread.fStarted == false,
2582 ("Transfer thread already started"), VERR_WRONG_ORDER);
2583
2584 /* Spawn a worker thread, so that we don't block the window thread for too long. */
2585 int rc = RTThreadCreate(&pTransfer->Thread.hThread, pfnThreadFunc,
2586 pvUser, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
2587 "shclp");
2588 if (RT_SUCCESS(rc))
2589 {
2590 int rc2 = RTThreadUserWait(pTransfer->Thread.hThread, 30 * 1000 /* Timeout in ms */);
2591 AssertRC(rc2);
2592
2593 if (pTransfer->Thread.fStarted) /* Did the thread indicate that it started correctly? */
2594 {
2595 /* Nothing to do in here. */
2596 }
2597 else
2598 rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
2599 }
2600
2601 LogFlowFuncLeaveRC(rc);
2602 return rc;
2603}
2604
2605/**
2606 * Destroys the thread of a clipboard transfer.
2607 *
2608 * @returns VBox status code.
2609 * @param pTransfer Clipboard transfer to destroy thread for.
2610 * @param uTimeoutMs Timeout (in ms) to wait for thread creation.
2611 */
2612static int shClTransferThreadDestroy(PSHCLTRANSFER pTransfer, RTMSINTERVAL uTimeoutMs)
2613{
2614 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2615
2616 if (pTransfer->Thread.hThread == NIL_RTTHREAD)
2617 return VINF_SUCCESS;
2618
2619 LogFlowFuncEnter();
2620
2621 /* Set stop indicator. */
2622 pTransfer->Thread.fStop = true;
2623
2624 int rcThread = VERR_WRONG_ORDER;
2625 int rc = RTThreadWait(pTransfer->Thread.hThread, uTimeoutMs, &rcThread);
2626
2627 LogFlowFunc(("Waiting for thread resulted in %Rrc (thread exited with %Rrc)\n", rc, rcThread));
2628
2629 return rc;
2630}
2631
2632/**
2633 * Initializes a clipboard transfer context.
2634 *
2635 * @returns VBox status code.
2636 * @param pTransferCtx Transfer context to initialize.
2637 */
2638int ShClTransferCtxInit(PSHCLTRANSFERCTX pTransferCtx)
2639{
2640 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2641
2642 LogFlowFunc(("pTransferCtx=%p\n", pTransferCtx));
2643
2644 int rc = RTCritSectInit(&pTransferCtx->CritSect);
2645 if (RT_SUCCESS(rc))
2646 {
2647 RTListInit(&pTransferCtx->List);
2648
2649 pTransferCtx->cTransfers = 0;
2650 pTransferCtx->cRunning = 0;
2651 pTransferCtx->cMaxRunning = 64; /** @todo Make this configurable? */
2652
2653 RT_ZERO(pTransferCtx->bmTransferIds);
2654
2655#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
2656 ShClTransferHttpServerInit(&pTransferCtx->HttpServer);
2657#endif
2658 ShClTransferCtxReset(pTransferCtx);
2659 }
2660
2661 return VINF_SUCCESS;
2662}
2663
2664/**
2665 * Destroys a clipboard transfer context.
2666 *
2667 * @param pTransferCtx Transfer context to destroy.
2668 */
2669void ShClTransferCtxDestroy(PSHCLTRANSFERCTX pTransferCtx)
2670{
2671 if (!pTransferCtx)
2672 return;
2673
2674 LogFlowFunc(("pTransferCtx=%p\n", pTransferCtx));
2675
2676 if (RTCritSectIsInitialized(&pTransferCtx->CritSect))
2677 RTCritSectDelete(&pTransferCtx->CritSect);
2678
2679 PSHCLTRANSFER pTransfer, pTransferNext;
2680 RTListForEachSafe(&pTransferCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
2681 {
2682 ShClTransferDestroy(pTransfer);
2683
2684 shclTransferCtxTransferRemoveAndUnregister(pTransferCtx, pTransfer);
2685
2686 RTMemFree(pTransfer);
2687 pTransfer = NULL;
2688 }
2689
2690 pTransferCtx->cRunning = 0;
2691 pTransferCtx->cTransfers = 0;
2692}
2693
2694/**
2695 * Resets a clipboard transfer context.
2696 *
2697 * @param pTransferCtx Transfer context to reset.
2698 */
2699void ShClTransferCtxReset(PSHCLTRANSFERCTX pTransferCtx)
2700{
2701 AssertPtrReturnVoid(pTransferCtx);
2702
2703 LogFlowFuncEnter();
2704
2705 PSHCLTRANSFER pTransfer;
2706 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node)
2707 ShClTransferReset(pTransfer);
2708
2709#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
2710 /** @todo Anything to do here? */
2711#endif
2712}
2713
2714/**
2715 * Returns a specific clipboard transfer, internal version.
2716 *
2717 * @returns Clipboard transfer found, or NULL if not found.
2718 * @param pTransferCtx Transfer context to return transfer for.
2719 * @param uID ID of the transfer to return.
2720 */
2721static PSHCLTRANSFER shClTransferCtxGetTransferByIdInternal(PSHCLTRANSFERCTX pTransferCtx, uint32_t uID)
2722{
2723 PSHCLTRANSFER pTransfer;
2724 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node) /** @todo Slow, but works for now. */
2725 {
2726 if (pTransfer->State.uID == uID)
2727 return pTransfer;
2728 }
2729
2730 return NULL;
2731}
2732
2733/**
2734 * Returns a specific clipboard transfer by index, internal version.
2735 *
2736 * @returns Clipboard transfer found, or NULL if not found.
2737 * @param pTransferCtx Transfer context to return transfer for.
2738 * @param uIdx Index of the transfer to return.
2739 */
2740static PSHCLTRANSFER shClTransferCtxGetTransferByIndexInternal(PSHCLTRANSFERCTX pTransferCtx, uint32_t uIdx)
2741{
2742 uint32_t idx = 0;
2743
2744 PSHCLTRANSFER pTransfer;
2745 RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node) /** @todo Slow, but works for now. */
2746 {
2747 if (uIdx == idx)
2748 return pTransfer;
2749 idx++;
2750 }
2751
2752 return NULL;
2753}
2754
2755/**
2756 * Returns a clipboard transfer for a specific transfer ID.
2757 *
2758 * @returns Clipboard transfer found, or NULL if not found.
2759 * @param pTransferCtx Transfer context to return transfer for.
2760 * @param uID ID of the transfer to return.
2761 */
2762PSHCLTRANSFER ShClTransferCtxGetTransferById(PSHCLTRANSFERCTX pTransferCtx, uint32_t uID)
2763{
2764 return shClTransferCtxGetTransferByIdInternal(pTransferCtx, uID);
2765}
2766
2767/**
2768 * Returns a clipboard transfer for a specific list index.
2769 *
2770 * @returns Clipboard transfer found, or NULL if not found.
2771 * @param pTransferCtx Transfer context to return transfer for.
2772 * @param uIdx List index of the transfer to return.
2773 */
2774PSHCLTRANSFER ShClTransferCtxGetTransferByIndex(PSHCLTRANSFERCTX pTransferCtx, uint32_t uIdx)
2775{
2776 return shClTransferCtxGetTransferByIndexInternal(pTransferCtx, uIdx);
2777}
2778
2779/**
2780 * Returns the number of running clipboard transfers for a given transfer context.
2781 *
2782 * @returns Number of running transfers.
2783 * @param pTransferCtx Transfer context to return number for.
2784 */
2785uint32_t ShClTransferCtxGetRunningTransfers(PSHCLTRANSFERCTX pTransferCtx)
2786{
2787 AssertPtrReturn(pTransferCtx, 0);
2788 return pTransferCtx->cRunning;
2789}
2790
2791/**
2792 * Returns the number of total clipboard transfers for a given transfer context.
2793 *
2794 * @returns Number of total transfers.
2795 * @param pTransferCtx Transfer context to return number for.
2796 */
2797uint32_t ShClTransferCtxGetTotalTransfers(PSHCLTRANSFERCTX pTransferCtx)
2798{
2799 AssertPtrReturn(pTransferCtx, 0);
2800 return pTransferCtx->cTransfers;
2801}
2802
2803/**
2804 * Registers a clipboard transfer with a transfer context, i.e. allocates a transfer ID.
2805 *
2806 * @return VBox status code.
2807 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers
2808 * is reached.
2809 * @param pTransferCtx Transfer context to register transfer to.
2810 * @param pTransfer Transfer to register.
2811 * @param pidTransfer Where to return the transfer ID on success. Optional.
2812 */
2813int ShClTransferCtxTransferRegister(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, SHCLTRANSFERID *pidTransfer)
2814{
2815 AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
2816 AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
2817 /* pidTransfer is optional. */
2818
2819 /*
2820 * Pick a random bit as starting point. If it's in use, search forward
2821 * for a free one, wrapping around. We've reserved both the zero'th and
2822 * max-1 IDs.
2823 */
2824 SHCLTRANSFERID idTransfer = RTRandU32Ex(1, VBOX_SHCL_MAX_TRANSFERS - 2);
2825
2826 if (!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer))
2827 { /* likely */ }
2828 else if (pTransferCtx->cTransfers < VBOX_SHCL_MAX_TRANSFERS - 2 /* First and last are not used */)
2829 {
2830 /* Forward search. */
2831 int iHit = ASMBitNextClear(&pTransferCtx->bmTransferIds[0], VBOX_SHCL_MAX_TRANSFERS, idTransfer);
2832 if (iHit < 0)
2833 iHit = ASMBitFirstClear(&pTransferCtx->bmTransferIds[0], VBOX_SHCL_MAX_TRANSFERS);
2834 AssertLogRelMsgReturn(iHit >= 0, ("Transfer count: %RU16\n", pTransferCtx->cTransfers), VERR_SHCLPB_MAX_TRANSFERS_REACHED);
2835 idTransfer = iHit;
2836 AssertLogRelMsgReturn(!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer), ("idObject=%#x\n", idTransfer), VERR_INTERNAL_ERROR_2);
2837 }
2838 else
2839 {
2840 LogFunc(("Maximum number of transfers reached (%RU16 transfers)\n", pTransferCtx->cTransfers));
2841 return VERR_SHCLPB_MAX_TRANSFERS_REACHED;
2842 }
2843
2844 Log2Func(("pTransfer=%p, idTransfer=%RU32 (%RU16 transfers)\n", pTransfer, idTransfer, pTransferCtx->cTransfers));
2845
2846 pTransfer->State.uID = idTransfer;
2847
2848 RTListAppend(&pTransferCtx->List, &pTransfer->Node);
2849
2850 pTransferCtx->cTransfers++;
2851
2852 if (pTransfer->Callbacks.pfnOnRegistered)
2853 pTransfer->Callbacks.pfnOnRegistered(&pTransfer->CallbackCtx, pTransferCtx);
2854
2855 if (pidTransfer)
2856 *pidTransfer = idTransfer;
2857
2858 LogFlowFuncLeaveRC(VINF_SUCCESS);
2859 return VINF_SUCCESS;
2860}
2861
2862/**
2863 * Registers a clipboard transfer with a transfer context by specifying an ID for the transfer.
2864 *
2865 * @return VBox status code.
2866 * @retval VERR_ALREADY_EXISTS if a transfer with the given ID already exists.
2867 * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers for this context has been reached.
2868 * @param pTransferCtx Transfer context to register transfer to.
2869 * @param pTransfer Transfer to register.
2870 * @param idTransfer Transfer ID to use for registration.
2871 */
2872int ShClTransferCtxTransferRegisterById(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, SHCLTRANSFERID idTransfer)
2873{
2874 LogFlowFunc(("cTransfers=%RU16, idTransfer=%RU32\n", pTransferCtx->cTransfers, idTransfer));
2875
2876 if (pTransferCtx->cTransfers < VBOX_SHCL_MAX_TRANSFERS - 2 /* First and last are not used */)
2877 {
2878 if (!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer))
2879 {
2880 RTListAppend(&pTransferCtx->List, &pTransfer->Node);
2881
2882 pTransfer->State.uID = idTransfer;
2883
2884 if (pTransfer->Callbacks.pfnOnRegistered)
2885 pTransfer->Callbacks.pfnOnRegistered(&pTransfer->CallbackCtx, pTransferCtx);
2886
2887 pTransferCtx->cTransfers++;
2888 return VINF_SUCCESS;
2889 }
2890
2891 return VERR_ALREADY_EXISTS;
2892 }
2893
2894 LogFunc(("Maximum number of transfers reached (%RU16 transfers)\n", pTransferCtx->cTransfers));
2895 return VERR_SHCLPB_MAX_TRANSFERS_REACHED;
2896}
2897
2898/**
2899 * Removes and unregisters a transfer from a transfer context.
2900 *
2901 * @param pTransferCtx Transfer context to remove transfer from.
2902 * @param pTransfer Transfer to remove.
2903 */
2904static void shclTransferCtxTransferRemoveAndUnregister(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer)
2905{
2906 RTListNodeRemove(&pTransfer->Node);
2907
2908 Assert(pTransferCtx->cTransfers);
2909 pTransferCtx->cTransfers--;
2910
2911 Assert(pTransferCtx->cTransfers >= pTransferCtx->cRunning);
2912
2913 if (pTransfer->Callbacks.pfnOnUnregistered)
2914 pTransfer->Callbacks.pfnOnUnregistered(&pTransfer->CallbackCtx, pTransferCtx);
2915
2916 LogFlowFunc(("Now %RU32 transfers left\n", pTransferCtx->cTransfers));
2917}
2918
2919/**
2920 * Unregisters a transfer from an Transfer context.
2921 *
2922 * @retval VINF_SUCCESS on success.
2923 * @retval VERR_NOT_FOUND if the transfer ID was not found.
2924 * @param pTransferCtx Transfer context to unregister transfer from.
2925 * @param idTransfer Transfer ID to unregister.
2926 */
2927int ShClTransferCtxTransferUnregister(PSHCLTRANSFERCTX pTransferCtx, SHCLTRANSFERID idTransfer)
2928{
2929 int rc = VINF_SUCCESS;
2930 AssertMsgStmt(ASMBitTestAndClear(&pTransferCtx->bmTransferIds, idTransfer), ("idTransfer=%#x\n", idTransfer), rc = VERR_NOT_FOUND);
2931
2932 LogFlowFunc(("idTransfer=%RU32\n", idTransfer));
2933
2934 PSHCLTRANSFER pTransfer = shClTransferCtxGetTransferByIdInternal(pTransferCtx, idTransfer);
2935 if (pTransfer)
2936 {
2937 shclTransferCtxTransferRemoveAndUnregister(pTransferCtx, pTransfer);
2938 }
2939 else
2940 rc = VERR_NOT_FOUND;
2941
2942 LogFlowFuncLeaveRC(rc);
2943 return rc;
2944}
2945
2946/**
2947 * Cleans up all associated transfers which are not needed (anymore).
2948 * This can be due to transfers which only have been announced but not / never being run.
2949 *
2950 * @param pTransferCtx Transfer context to cleanup transfers for.
2951 */
2952void ShClTransferCtxCleanup(PSHCLTRANSFERCTX pTransferCtx)
2953{
2954 AssertPtrReturnVoid(pTransferCtx);
2955
2956 LogFlowFunc(("pTransferCtx=%p, cTransfers=%RU16 cRunning=%RU16\n",
2957 pTransferCtx, pTransferCtx->cTransfers, pTransferCtx->cRunning));
2958
2959 if (pTransferCtx->cTransfers == 0)
2960 return;
2961
2962 /* Remove all transfers which are not in a running state (e.g. only announced). */
2963 PSHCLTRANSFER pTransfer, pTransferNext;
2964 RTListForEachSafe(&pTransferCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
2965 {
2966 if (ShClTransferGetStatus(pTransfer) != SHCLTRANSFERSTATUS_STARTED)
2967 {
2968 shclTransferCtxTransferRemoveAndUnregister(pTransferCtx, pTransfer);
2969
2970 ShClTransferDestroy(pTransfer);
2971
2972 RTMemFree(pTransfer);
2973 pTransfer = NULL;
2974 }
2975 }
2976}
2977
2978/**
2979 * Returns whether the maximum of concurrent transfers of a specific transfer contexthas been reached or not.
2980 *
2981 * @returns \c if maximum has been reached, \c false if not.
2982 * @param pTransferCtx Transfer context to determine value for.
2983 */
2984bool ShClTransferCtxTransfersMaximumReached(PSHCLTRANSFERCTX pTransferCtx)
2985{
2986 AssertPtrReturn(pTransferCtx, true);
2987
2988 LogFlowFunc(("cRunning=%RU32, cMaxRunning=%RU32\n", pTransferCtx->cRunning, pTransferCtx->cMaxRunning));
2989
2990 Assert(pTransferCtx->cRunning <= pTransferCtx->cMaxRunning);
2991 return pTransferCtx->cRunning == pTransferCtx->cMaxRunning;
2992}
2993
2994/**
2995 * Copies file system objinfo from IPRT to Shared Clipboard format.
2996 *
2997 * @param pDst The Shared Clipboard structure to convert data to.
2998 * @param pSrc The IPRT structure to convert data from.
2999 */
3000void ShClFsObjFromIPRT(PSHCLFSOBJINFO pDst, PCRTFSOBJINFO pSrc)
3001{
3002 pDst->cbObject = pSrc->cbObject;
3003 pDst->cbAllocated = pSrc->cbAllocated;
3004 pDst->AccessTime = pSrc->AccessTime;
3005 pDst->ModificationTime = pSrc->ModificationTime;
3006 pDst->ChangeTime = pSrc->ChangeTime;
3007 pDst->BirthTime = pSrc->BirthTime;
3008 pDst->Attr.fMode = pSrc->Attr.fMode;
3009 /* Clear bits which we don't pass through for security reasons. */
3010 pDst->Attr.fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
3011 RT_ZERO(pDst->Attr.u);
3012 switch (pSrc->Attr.enmAdditional)
3013 {
3014 default:
3015 case RTFSOBJATTRADD_NOTHING:
3016 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_NOTHING;
3017 break;
3018
3019 case RTFSOBJATTRADD_UNIX:
3020 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_UNIX;
3021 pDst->Attr.u.Unix.uid = pSrc->Attr.u.Unix.uid;
3022 pDst->Attr.u.Unix.gid = pSrc->Attr.u.Unix.gid;
3023 pDst->Attr.u.Unix.cHardlinks = pSrc->Attr.u.Unix.cHardlinks;
3024 pDst->Attr.u.Unix.INodeIdDevice = pSrc->Attr.u.Unix.INodeIdDevice;
3025 pDst->Attr.u.Unix.INodeId = pSrc->Attr.u.Unix.INodeId;
3026 pDst->Attr.u.Unix.fFlags = pSrc->Attr.u.Unix.fFlags;
3027 pDst->Attr.u.Unix.GenerationId = pSrc->Attr.u.Unix.GenerationId;
3028 pDst->Attr.u.Unix.Device = pSrc->Attr.u.Unix.Device;
3029 break;
3030
3031 case RTFSOBJATTRADD_EASIZE:
3032 pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_EASIZE;
3033 pDst->Attr.u.EASize.cb = pSrc->Attr.u.EASize.cb;
3034 break;
3035 }
3036}
3037
3038/**
3039 * Converts Shared Clipboard create flags (see SharedClipboard-transfers.h) into IPRT create flags.
3040 *
3041 * @returns IPRT status code.
3042 * @param fShClFlags Shared clipboard create flags.
3043 * @param[out] pfOpen Where to store the RTFILE_O_XXX flags for
3044 * RTFileOpen.
3045 *
3046 * @sa Initially taken from vbsfConvertFileOpenFlags().
3047 */
3048static int shClConvertFileCreateFlags(uint32_t fShClFlags, uint64_t *pfOpen)
3049{
3050 AssertMsgReturnStmt(!(fShClFlags & ~SHCL_OBJ_CF_VALID_MASK), ("%#x4\n", fShClFlags), *pfOpen = 0, VERR_INVALID_FLAGS);
3051
3052 uint64_t fOpen = 0;
3053
3054 switch (fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_RW)
3055 {
3056 case SHCL_OBJ_CF_ACCESS_NONE:
3057 {
3058#ifdef RT_OS_WINDOWS
3059 if ((fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_ATTR) != SHCL_OBJ_CF_ACCESS_ATTR_NONE)
3060 fOpen |= RTFILE_O_OPEN | RTFILE_O_ATTR_ONLY;
3061 else
3062#endif
3063 fOpen |= RTFILE_O_OPEN | RTFILE_O_READ;
3064 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_NONE\n"));
3065 break;
3066 }
3067
3068 case SHCL_OBJ_CF_ACCESS_READ:
3069 {
3070 fOpen |= RTFILE_O_OPEN | RTFILE_O_READ;
3071 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_READ\n"));
3072 break;
3073 }
3074
3075 default:
3076 AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE);
3077 }
3078
3079 switch (fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_ATTR)
3080 {
3081 case SHCL_OBJ_CF_ACCESS_ATTR_NONE:
3082 {
3083 fOpen |= RTFILE_O_ACCESS_ATTR_DEFAULT;
3084 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_NONE\n"));
3085 break;
3086 }
3087
3088 case SHCL_OBJ_CF_ACCESS_ATTR_READ:
3089 {
3090 fOpen |= RTFILE_O_ACCESS_ATTR_READ;
3091 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_READ\n"));
3092 break;
3093 }
3094
3095 default:
3096 AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE);
3097 }
3098
3099 /* Sharing mask */
3100 switch (fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_DENY)
3101 {
3102 case SHCL_OBJ_CF_ACCESS_DENYNONE:
3103 fOpen |= RTFILE_O_DENY_NONE;
3104 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYNONE\n"));
3105 break;
3106
3107 case SHCL_OBJ_CF_ACCESS_DENYWRITE:
3108 fOpen |= RTFILE_O_DENY_WRITE;
3109 LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYWRITE\n"));
3110 break;
3111
3112 default:
3113 AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE);
3114 }
3115
3116 *pfOpen = fOpen;
3117
3118 LogFlowFuncLeaveRC(VINF_SUCCESS);
3119 return VINF_SUCCESS;
3120}
3121
3122/**
3123 * Translates a clipboard transfer status (SHCLTRANSFERSTATUS_XXX) into a string.
3124 *
3125 * @returns Transfer status string name.
3126 * @param enmStatus The transfer status to translate.
3127 */
3128const char *ShClTransferStatusToStr(SHCLTRANSFERSTATUS enmStatus)
3129{
3130 switch (enmStatus)
3131 {
3132 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_NONE);
3133 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_INITIALIZED);
3134 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_STARTED);
3135 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_STOPPED);
3136 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_CANCELED);
3137 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_KILLED);
3138 RT_CASE_RET_STR(SHCLTRANSFERSTATUS_ERROR);
3139 }
3140 return "Unknown";
3141}
3142
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