summaryrefslogtreecommitdiffstats
path: root/src/VBox/GuestHost/SharedClipboard/clipboard-transfers.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/GuestHost/SharedClipboard/clipboard-transfers.cpp
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/GuestHost/SharedClipboard/clipboard-transfers.cpp')
-rw-r--r--src/VBox/GuestHost/SharedClipboard/clipboard-transfers.cpp3132
1 files changed, 3132 insertions, 0 deletions
diff --git a/src/VBox/GuestHost/SharedClipboard/clipboard-transfers.cpp b/src/VBox/GuestHost/SharedClipboard/clipboard-transfers.cpp
new file mode 100644
index 00000000..f0a0453d
--- /dev/null
+++ b/src/VBox/GuestHost/SharedClipboard/clipboard-transfers.cpp
@@ -0,0 +1,3132 @@
+/* $Id: clipboard-transfers.cpp $ */
+/** @file
+ * Shared Clipboard: Common Shared Clipboard transfer handling code.
+ */
+
+/*
+ * Copyright (C) 2019-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD
+#include <VBox/log.h>
+
+#include <iprt/dir.h>
+#include <iprt/file.h>
+#include <iprt/list.h>
+#include <iprt/path.h>
+#include <iprt/rand.h>
+#include <iprt/semaphore.h>
+
+#include <VBox/err.h>
+#include <VBox/HostServices/VBoxClipboardSvc.h>
+#include <VBox/GuestHost/SharedClipboard-transfers.h>
+
+
+static int shClTransferThreadCreate(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser);
+static int shClTransferThreadDestroy(PSHCLTRANSFER pTransfer, RTMSINTERVAL uTimeoutMs);
+
+static void shclTransferCtxTransferRemoveAndUnregister(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer);
+static PSHCLTRANSFER shClTransferCtxGetTransferByIdInternal(PSHCLTRANSFERCTX pTransferCtx, uint32_t uId);
+static PSHCLTRANSFER shClTransferCtxGetTransferByIndexInternal(PSHCLTRANSFERCTX pTransferCtx, uint32_t uIdx);
+static int shClConvertFileCreateFlags(uint32_t fShClFlags, uint64_t *pfOpen);
+static int shClTransferResolvePathAbs(PSHCLTRANSFER pTransfer, const char *pszPath, uint32_t fFlags, char **ppszResolved);
+
+/** @todo Split this file up in different modules. */
+
+/**
+ * Allocates a new transfer root list.
+ *
+ * @returns Allocated transfer root list on success, or NULL on failure.
+ */
+PSHCLROOTLIST ShClTransferRootListAlloc(void)
+{
+ PSHCLROOTLIST pRootList = (PSHCLROOTLIST)RTMemAllocZ(sizeof(SHCLROOTLIST));
+
+ return pRootList;
+}
+
+/**
+ * Frees a transfer root list.
+ *
+ * @param pRootList transfer root list to free. The pointer will be
+ * invalid after returning from this function.
+ */
+void ShClTransferRootListFree(PSHCLROOTLIST pRootList)
+{
+ if (!pRootList)
+ return;
+
+ for (uint32_t i = 0; i < pRootList->Hdr.cRoots; i++)
+ ShClTransferListEntryInit(&pRootList->paEntries[i]);
+
+ RTMemFree(pRootList);
+ pRootList = NULL;
+}
+
+/**
+ * Initializes a transfer root list header.
+ *
+ * @returns VBox status code.
+ * @param pRootLstHdr Root list header to initialize.
+ */
+int ShClTransferRootListHdrInit(PSHCLROOTLISTHDR pRootLstHdr)
+{
+ AssertPtrReturn(pRootLstHdr, VERR_INVALID_POINTER);
+
+ RT_BZERO(pRootLstHdr, sizeof(SHCLROOTLISTHDR));
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Destroys a transfer root list header.
+ *
+ * @param pRootLstHdr Root list header to destroy.
+ */
+void ShClTransferRootListHdrDestroy(PSHCLROOTLISTHDR pRootLstHdr)
+{
+ if (!pRootLstHdr)
+ return;
+
+ pRootLstHdr->fRoots = 0;
+ pRootLstHdr->cRoots = 0;
+}
+
+/**
+ * Duplicates a transfer list header.
+ *
+ * @returns Duplicated transfer list header on success, or NULL on failure.
+ * @param pRootLstHdr Root list header to duplicate.
+ */
+PSHCLROOTLISTHDR ShClTransferRootListHdrDup(PSHCLROOTLISTHDR pRootLstHdr)
+{
+ AssertPtrReturn(pRootLstHdr, NULL);
+
+ int rc = VINF_SUCCESS;
+
+ PSHCLROOTLISTHDR pRootsDup = (PSHCLROOTLISTHDR)RTMemAllocZ(sizeof(SHCLROOTLISTHDR));
+ if (pRootsDup)
+ {
+ *pRootsDup = *pRootLstHdr;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ if (RT_FAILURE(rc))
+ {
+ ShClTransferRootListHdrDestroy(pRootsDup);
+ pRootsDup = NULL;
+ }
+
+ return pRootsDup;
+}
+
+/**
+ * (Deep) Copies a clipboard root list entry structure.
+ *
+ * @returns VBox status code.
+ * @param pDst Where to copy the source root list entry to.
+ * @param pSrc Source root list entry to copy.
+ */
+int ShClTransferRootListEntryCopy(PSHCLROOTLISTENTRY pDst, PSHCLROOTLISTENTRY pSrc)
+{
+ return ShClTransferListEntryCopy(pDst, pSrc);
+}
+
+/**
+ * Initializes a clipboard root list entry structure.
+ *
+ * @param pRootListEntry Clipboard root list entry structure to destroy.
+ */
+int ShClTransferRootListEntryInit(PSHCLROOTLISTENTRY pRootListEntry)
+{
+ return ShClTransferListEntryInit(pRootListEntry);
+}
+
+/**
+ * Destroys a clipboard root list entry structure.
+ *
+ * @param pRootListEntry Clipboard root list entry structure to destroy.
+ */
+void ShClTransferRootListEntryDestroy(PSHCLROOTLISTENTRY pRootListEntry)
+{
+ return ShClTransferListEntryDestroy(pRootListEntry);
+}
+
+/**
+ * Duplicates (allocates) a clipboard root list entry structure.
+ *
+ * @returns Duplicated clipboard root list entry structure on success.
+ * @param pRootListEntry Clipboard root list entry to duplicate.
+ */
+PSHCLROOTLISTENTRY ShClTransferRootListEntryDup(PSHCLROOTLISTENTRY pRootListEntry)
+{
+ return ShClTransferListEntryDup(pRootListEntry);
+}
+
+/**
+ * Initializes an list handle info structure.
+ *
+ * @returns VBox status code.
+ * @param pInfo List handle info structure to initialize.
+ */
+int ShClTransferListHandleInfoInit(PSHCLLISTHANDLEINFO pInfo)
+{
+ AssertPtrReturn(pInfo, VERR_INVALID_POINTER);
+
+ pInfo->hList = SHCLLISTHANDLE_INVALID;
+ pInfo->enmType = SHCLOBJTYPE_INVALID;
+
+ pInfo->pszPathLocalAbs = NULL;
+
+ RT_ZERO(pInfo->u);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Destroys a list handle info structure.
+ *
+ * @param pInfo List handle info structure to destroy.
+ */
+void ShClTransferListHandleInfoDestroy(PSHCLLISTHANDLEINFO pInfo)
+{
+ if (!pInfo)
+ return;
+
+ if (pInfo->pszPathLocalAbs)
+ {
+ RTStrFree(pInfo->pszPathLocalAbs);
+ pInfo->pszPathLocalAbs = NULL;
+ }
+}
+
+/**
+ * Allocates a transfer list header structure.
+ *
+ * @returns VBox status code.
+ * @param ppListHdr Where to store the allocated transfer list header structure on success.
+ */
+int ShClTransferListHdrAlloc(PSHCLLISTHDR *ppListHdr)
+{
+ int rc;
+
+ PSHCLLISTHDR pListHdr = (PSHCLLISTHDR)RTMemAllocZ(sizeof(SHCLLISTHDR));
+ if (pListHdr)
+ {
+ *ppListHdr = pListHdr;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Frees a transfer list header structure.
+ *
+ * @param pListEntry Transfer list header structure to free.
+ */
+void ShClTransferListHdrFree(PSHCLLISTHDR pListHdr)
+{
+ if (!pListHdr)
+ return;
+
+ LogFlowFuncEnter();
+
+ ShClTransferListHdrDestroy(pListHdr);
+
+ RTMemFree(pListHdr);
+ pListHdr = NULL;
+}
+
+/**
+ * Duplicates (allocates) a transfer list header structure.
+ *
+ * @returns Duplicated transfer list header structure on success.
+ * @param pListHdr Transfer list header to duplicate.
+ */
+PSHCLLISTHDR ShClTransferListHdrDup(PSHCLLISTHDR pListHdr)
+{
+ AssertPtrReturn(pListHdr, NULL);
+
+ PSHCLLISTHDR pListHdrDup = (PSHCLLISTHDR)RTMemAlloc(sizeof(SHCLLISTHDR));
+ if (pListHdrDup)
+ {
+ *pListHdrDup = *pListHdr;
+ }
+
+ return pListHdrDup;
+}
+
+/**
+ * Initializes a transfer list header structure.
+ *
+ * @returns VBox status code.
+ * @param pListHdr Transfer list header struct to initialize.
+ */
+int ShClTransferListHdrInit(PSHCLLISTHDR pListHdr)
+{
+ AssertPtrReturn(pListHdr, VERR_INVALID_POINTER);
+
+ LogFlowFuncEnter();
+
+ ShClTransferListHdrReset(pListHdr);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Destroys a transfer list header structure.
+ *
+ * @param pListHdr Transfer list header struct to destroy.
+ */
+void ShClTransferListHdrDestroy(PSHCLLISTHDR pListHdr)
+{
+ if (!pListHdr)
+ return;
+
+ LogFlowFuncEnter();
+}
+
+/**
+ * Resets a transfer list header structure.
+ *
+ * @returns VBox status code.
+ * @param pListHdr Transfer list header struct to reset.
+ */
+void ShClTransferListHdrReset(PSHCLLISTHDR pListHdr)
+{
+ AssertPtrReturnVoid(pListHdr);
+
+ LogFlowFuncEnter();
+
+ RT_BZERO(pListHdr, sizeof(SHCLLISTHDR));
+}
+
+/**
+ * Returns whether a given transfer list header is valid or not.
+ *
+ * @returns \c true if valid, \c false if not.
+ * @param pListHdr Transfer list header to validate.
+ */
+bool ShClTransferListHdrIsValid(PSHCLLISTHDR pListHdr)
+{
+ RT_NOREF(pListHdr);
+ return true; /** @todo Implement this. */
+}
+
+int ShClTransferListOpenParmsCopy(PSHCLLISTOPENPARMS pDst, PSHCLLISTOPENPARMS pSrc)
+{
+ AssertPtrReturn(pDst, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSrc, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+
+ if (pSrc->pszFilter)
+ {
+ pDst->pszFilter = RTStrDup(pSrc->pszFilter);
+ if (!pDst->pszFilter)
+ rc = VERR_NO_MEMORY;
+ }
+
+ if ( RT_SUCCESS(rc)
+ && pSrc->pszPath)
+ {
+ pDst->pszPath = RTStrDup(pSrc->pszPath);
+ if (!pDst->pszPath)
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ pDst->fList = pDst->fList;
+ pDst->cbFilter = pSrc->cbFilter;
+ pDst->cbPath = pSrc->cbPath;
+ }
+
+ return rc;
+}
+
+/**
+ * Duplicates a transfer list open parameters structure.
+ *
+ * @returns Duplicated transfer list open parameters structure on success, or NULL on failure.
+ * @param pParms Transfer list open parameters structure to duplicate.
+ */
+PSHCLLISTOPENPARMS ShClTransferListOpenParmsDup(PSHCLLISTOPENPARMS pParms)
+{
+ AssertPtrReturn(pParms, NULL);
+
+ PSHCLLISTOPENPARMS pParmsDup = (PSHCLLISTOPENPARMS)RTMemAllocZ(sizeof(SHCLLISTOPENPARMS));
+ if (!pParmsDup)
+ return NULL;
+
+ int rc = ShClTransferListOpenParmsCopy(pParmsDup, pParms);
+ if (RT_FAILURE(rc))
+ {
+ ShClTransferListOpenParmsDestroy(pParmsDup);
+
+ RTMemFree(pParmsDup);
+ pParmsDup = NULL;
+ }
+
+ return pParmsDup;
+}
+
+/**
+ * Initializes a transfer list open parameters structure.
+ *
+ * @returns VBox status code.
+ * @param pParms Transfer list open parameters structure to initialize.
+ */
+int ShClTransferListOpenParmsInit(PSHCLLISTOPENPARMS pParms)
+{
+ AssertPtrReturn(pParms, VERR_INVALID_POINTER);
+
+ RT_BZERO(pParms, sizeof(SHCLLISTOPENPARMS));
+
+ pParms->cbFilter = SHCL_TRANSFER_PATH_MAX; /** @todo Make this dynamic. */
+ pParms->pszFilter = RTStrAlloc(pParms->cbFilter);
+
+ pParms->cbPath = SHCL_TRANSFER_PATH_MAX; /** @todo Make this dynamic. */
+ pParms->pszPath = RTStrAlloc(pParms->cbPath);
+
+ LogFlowFuncLeave();
+ return VINF_SUCCESS;
+}
+
+/**
+ * Destroys a transfer list open parameters structure.
+ *
+ * @param pParms Transfer list open parameters structure to destroy.
+ */
+void ShClTransferListOpenParmsDestroy(PSHCLLISTOPENPARMS pParms)
+{
+ if (!pParms)
+ return;
+
+ if (pParms->pszFilter)
+ {
+ RTStrFree(pParms->pszFilter);
+ pParms->pszFilter = NULL;
+ }
+
+ if (pParms->pszPath)
+ {
+ RTStrFree(pParms->pszPath);
+ pParms->pszPath = NULL;
+ }
+}
+
+/**
+ * Creates (allocates) and initializes a clipboard list entry structure.
+ *
+ * @param ppDirData Where to return the created clipboard list entry structure on success.
+ */
+int ShClTransferListEntryAlloc(PSHCLLISTENTRY *ppListEntry)
+{
+ PSHCLLISTENTRY pListEntry = (PSHCLLISTENTRY)RTMemAlloc(sizeof(SHCLLISTENTRY));
+ if (!pListEntry)
+ return VERR_NO_MEMORY;
+
+ int rc = ShClTransferListEntryInit(pListEntry);
+ if (RT_SUCCESS(rc))
+ *ppListEntry = pListEntry;
+
+ return rc;
+}
+
+/**
+ * Frees a clipboard list entry structure.
+ *
+ * @param pListEntry Clipboard list entry structure to free.
+ */
+void ShClTransferListEntryFree(PSHCLLISTENTRY pListEntry)
+{
+ if (!pListEntry)
+ return;
+
+ ShClTransferListEntryDestroy(pListEntry);
+ RTMemFree(pListEntry);
+}
+
+/**
+ * (Deep) Copies a clipboard list entry structure.
+ *
+ * @returns VBox status code.
+ * @param pListEntry Clipboard list entry to copy.
+ */
+int ShClTransferListEntryCopy(PSHCLLISTENTRY pDst, PSHCLLISTENTRY pSrc)
+{
+ AssertPtrReturn(pDst, VERR_INVALID_POINTER);
+ AssertPtrReturn(pSrc, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+
+ *pDst = *pSrc;
+
+ if (pSrc->pszName)
+ {
+ pDst->pszName = RTStrDup(pSrc->pszName);
+ if (!pDst->pszName)
+ rc = VERR_NO_MEMORY;
+ }
+
+ if ( RT_SUCCESS(rc)
+ && pSrc->pvInfo)
+ {
+ pDst->pvInfo = RTMemDup(pSrc->pvInfo, pSrc->cbInfo);
+ if (pDst->pvInfo)
+ {
+ pDst->cbInfo = pSrc->cbInfo;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ if (pDst->pvInfo)
+ {
+ RTMemFree(pDst->pvInfo);
+ pDst->pvInfo = NULL;
+ pDst->cbInfo = 0;
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * Duplicates (allocates) a clipboard list entry structure.
+ *
+ * @returns Duplicated clipboard list entry structure on success.
+ * @param pListEntry Clipboard list entry to duplicate.
+ */
+PSHCLLISTENTRY ShClTransferListEntryDup(PSHCLLISTENTRY pListEntry)
+{
+ AssertPtrReturn(pListEntry, NULL);
+
+ int rc = VINF_SUCCESS;
+
+ PSHCLLISTENTRY pListEntryDup = (PSHCLLISTENTRY)RTMemAllocZ(sizeof(SHCLLISTENTRY));
+ if (pListEntryDup)
+ rc = ShClTransferListEntryCopy(pListEntryDup, pListEntry);
+
+ if (RT_FAILURE(rc))
+ {
+ ShClTransferListEntryDestroy(pListEntryDup);
+
+ RTMemFree(pListEntryDup);
+ pListEntryDup = NULL;
+ }
+
+ return pListEntryDup;
+}
+
+/**
+ * Initializes a clipboard list entry structure.
+ *
+ * @returns VBox status code.
+ * @param pListEntry Clipboard list entry structure to initialize.
+ */
+int ShClTransferListEntryInit(PSHCLLISTENTRY pListEntry)
+{
+ AssertPtrReturn(pListEntry, VERR_INVALID_POINTER);
+
+ RT_BZERO(pListEntry, sizeof(SHCLLISTENTRY));
+
+ pListEntry->pszName = RTStrAlloc(SHCLLISTENTRY_MAX_NAME);
+ if (!pListEntry->pszName)
+ return VERR_NO_MEMORY;
+
+ pListEntry->cbName = SHCLLISTENTRY_MAX_NAME;
+
+ pListEntry->pvInfo = (PSHCLFSOBJINFO)RTMemAlloc(sizeof(SHCLFSOBJINFO));
+ if (pListEntry->pvInfo)
+ {
+ pListEntry->cbInfo = sizeof(SHCLFSOBJINFO);
+ pListEntry->fInfo = VBOX_SHCL_INFO_FLAG_FSOBJINFO;
+
+ return VINF_SUCCESS;
+ }
+
+ return VERR_NO_MEMORY;
+}
+
+/**
+ * Destroys a clipboard list entry structure.
+ *
+ * @param pListEntry Clipboard list entry structure to destroy.
+ */
+void ShClTransferListEntryDestroy(PSHCLLISTENTRY pListEntry)
+{
+ if (!pListEntry)
+ return;
+
+ if (pListEntry->pszName)
+ {
+ RTStrFree(pListEntry->pszName);
+
+ pListEntry->pszName = NULL;
+ pListEntry->cbName = 0;
+ }
+
+ if (pListEntry->pvInfo)
+ {
+ RTMemFree(pListEntry->pvInfo);
+ pListEntry->pvInfo = NULL;
+ pListEntry->cbInfo = 0;
+ }
+}
+
+/**
+ * Returns whether a given clipboard list entry is valid or not.
+ *
+ * @returns \c true if valid, \c false if not.
+ * @param pListEntry Clipboard list entry to validate.
+ */
+bool ShClTransferListEntryIsValid(PSHCLLISTENTRY pListEntry)
+{
+ AssertPtrReturn(pListEntry, false);
+
+ if ( !pListEntry->pszName
+ || !pListEntry->cbName
+ || strlen(pListEntry->pszName) == 0
+ || strlen(pListEntry->pszName) > pListEntry->cbName /* Includes zero termination */ - 1)
+ {
+ return false;
+ }
+
+ if (pListEntry->cbInfo) /* cbInfo / pvInfo is optional. */
+ {
+ if (!pListEntry->pvInfo)
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Initializes a transfer object context.
+ *
+ * @returns VBox status code.
+ * @param pObjCtx transfer object context to initialize.
+ */
+int ShClTransferObjCtxInit(PSHCLCLIENTTRANSFEROBJCTX pObjCtx)
+{
+ AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
+
+ LogFlowFuncEnter();
+
+ pObjCtx->uHandle = SHCLOBJHANDLE_INVALID;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Destroys a transfer object context.
+ *
+ * @param pObjCtx transfer object context to destroy.
+ */
+void ShClTransferObjCtxDestroy(PSHCLCLIENTTRANSFEROBJCTX pObjCtx)
+{
+ AssertPtrReturnVoid(pObjCtx);
+
+ LogFlowFuncEnter();
+}
+
+/**
+ * Returns if a transfer object context is valid or not.
+ *
+ * @returns \c true if valid, \c false if not.
+ * @param pObjCtx transfer object context to check.
+ */
+bool ShClTransferObjCtxIsValid(PSHCLCLIENTTRANSFEROBJCTX pObjCtx)
+{
+ return ( pObjCtx
+ && pObjCtx->uHandle != SHCLOBJHANDLE_INVALID);
+}
+
+/**
+ * Initializes an object handle info structure.
+ *
+ * @returns VBox status code.
+ * @param pInfo Object handle info structure to initialize.
+ */
+int ShClTransferObjHandleInfoInit(PSHCLOBJHANDLEINFO pInfo)
+{
+ AssertPtrReturn(pInfo, VERR_INVALID_POINTER);
+
+ pInfo->hObj = SHCLOBJHANDLE_INVALID;
+ pInfo->enmType = SHCLOBJTYPE_INVALID;
+
+ pInfo->pszPathLocalAbs = NULL;
+
+ RT_ZERO(pInfo->u);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Destroys an object handle info structure.
+ *
+ * @param pInfo Object handle info structure to destroy.
+ */
+void ShClTransferObjHandleInfoDestroy(PSHCLOBJHANDLEINFO pInfo)
+{
+ if (!pInfo)
+ return;
+
+ if (pInfo->pszPathLocalAbs)
+ {
+ RTStrFree(pInfo->pszPathLocalAbs);
+ pInfo->pszPathLocalAbs = NULL;
+ }
+}
+
+/**
+ * Initializes a transfer object open parameters structure.
+ *
+ * @returns VBox status code.
+ * @param pParms transfer object open parameters structure to initialize.
+ */
+int ShClTransferObjOpenParmsInit(PSHCLOBJOPENCREATEPARMS pParms)
+{
+ AssertPtrReturn(pParms, VERR_INVALID_POINTER);
+
+ int rc;
+
+ RT_BZERO(pParms, sizeof(SHCLOBJOPENCREATEPARMS));
+
+ pParms->cbPath = RTPATH_MAX; /** @todo Make this dynamic. */
+ pParms->pszPath = RTStrAlloc(pParms->cbPath);
+ if (pParms->pszPath)
+ {
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Copies a transfer object open parameters structure from source to destination.
+ *
+ * @returns VBox status code.
+ * @param pParmsDst Where to copy the source transfer object open parameters to.
+ * @param pParmsSrc Which source transfer object open parameters to copy.
+ */
+int ShClTransferObjOpenParmsCopy(PSHCLOBJOPENCREATEPARMS pParmsDst, PSHCLOBJOPENCREATEPARMS pParmsSrc)
+{
+ int rc;
+
+ *pParmsDst = *pParmsSrc;
+
+ if (pParmsSrc->pszPath)
+ {
+ Assert(pParmsSrc->cbPath);
+ pParmsDst->pszPath = RTStrDup(pParmsSrc->pszPath);
+ if (pParmsDst->pszPath)
+ {
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VINF_SUCCESS;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Destroys a transfer object open parameters structure.
+ *
+ * @param pParms transfer object open parameters structure to destroy.
+ */
+void ShClTransferObjOpenParmsDestroy(PSHCLOBJOPENCREATEPARMS pParms)
+{
+ if (!pParms)
+ return;
+
+ if (pParms->pszPath)
+ {
+ RTStrFree(pParms->pszPath);
+ pParms->pszPath = NULL;
+ }
+}
+
+/**
+ * Returns a specific object handle info of a transfer.
+ *
+ * @returns Pointer to object handle info if found, or NULL if not found.
+ * @param pTransfer Clipboard transfer to get object handle info from.
+ * @param hObj Object handle of the object to get handle info for.
+ */
+DECLINLINE(PSHCLOBJHANDLEINFO) shClTransferObjGet(PSHCLTRANSFER pTransfer, SHCLOBJHANDLE hObj)
+{
+ PSHCLOBJHANDLEINFO pIt;
+ RTListForEach(&pTransfer->lstObj, pIt, SHCLOBJHANDLEINFO, Node) /** @todo Slooow ...but works for now. */
+ {
+ if (pIt->hObj == hObj)
+ return pIt;
+ }
+
+ return NULL;
+}
+
+/**
+ * Opens a transfer object.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Clipboard transfer to open the object for.
+ * @param pOpenCreateParms Open / create parameters of transfer object to open / create.
+ * @param phObj Where to store the handle of transfer object opened on success.
+ */
+int ShClTransferObjOpen(PSHCLTRANSFER pTransfer, PSHCLOBJOPENCREATEPARMS pOpenCreateParms, PSHCLOBJHANDLE phObj)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+ AssertPtrReturn(pOpenCreateParms, VERR_INVALID_POINTER);
+ AssertPtrReturn(phObj, VERR_INVALID_POINTER);
+ AssertMsgReturn(pTransfer->pszPathRootAbs, ("Transfer has no root path set\n"), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(pOpenCreateParms->pszPath, ("No path in open/create params set\n"), VERR_INVALID_PARAMETER);
+
+ if (pTransfer->cObjHandles >= pTransfer->cMaxObjHandles)
+ return VERR_SHCLPB_MAX_OBJECTS_REACHED;
+
+ LogFlowFunc(("pszPath=%s, fCreate=0x%x\n", pOpenCreateParms->pszPath, pOpenCreateParms->fCreate));
+
+ int rc;
+ if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
+ {
+ PSHCLOBJHANDLEINFO pInfo = (PSHCLOBJHANDLEINFO)RTMemAllocZ(sizeof(SHCLOBJHANDLEINFO));
+ if (pInfo)
+ {
+ rc = ShClTransferObjHandleInfoInit(pInfo);
+ if (RT_SUCCESS(rc))
+ {
+ uint64_t fOpen;
+ rc = shClConvertFileCreateFlags(pOpenCreateParms->fCreate, &fOpen);
+ if (RT_SUCCESS(rc))
+ {
+ rc = shClTransferResolvePathAbs(pTransfer, pOpenCreateParms->pszPath, 0 /* fFlags */,
+ &pInfo->pszPathLocalAbs);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTFileOpen(&pInfo->u.Local.hFile, pInfo->pszPathLocalAbs, fOpen);
+ if (RT_SUCCESS(rc))
+ LogRel2(("Shared Clipboard: Opened file '%s'\n", pInfo->pszPathLocalAbs));
+ else
+ LogRel(("Shared Clipboard: Error opening file '%s': rc=%Rrc\n", pInfo->pszPathLocalAbs, rc));
+ }
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ pInfo->hObj = pTransfer->uObjHandleNext++;
+ pInfo->enmType = SHCLOBJTYPE_FILE;
+
+ RTListAppend(&pTransfer->lstObj, &pInfo->Node);
+ pTransfer->cObjHandles++;
+
+ LogFlowFunc(("cObjHandles=%RU32\n", pTransfer->cObjHandles));
+
+ *phObj = pInfo->hObj;
+ }
+ else
+ {
+ ShClTransferObjHandleInfoDestroy(pInfo);
+ RTMemFree(pInfo);
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
+ {
+ if (pTransfer->ProviderIface.pfnObjOpen)
+ rc = pTransfer->ProviderIface.pfnObjOpen(&pTransfer->ProviderCtx, pOpenCreateParms, phObj);
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else
+ AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Closes a transfer object.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Clipboard transfer that contains the object to close.
+ * @param hObj Handle of transfer object to close.
+ */
+int ShClTransferObjClose(PSHCLTRANSFER pTransfer, SHCLOBJHANDLE hObj)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+
+ if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
+ {
+ PSHCLOBJHANDLEINFO pInfo = shClTransferObjGet(pTransfer, hObj);
+ if (pInfo)
+ {
+ switch (pInfo->enmType)
+ {
+ case SHCLOBJTYPE_DIRECTORY:
+ {
+ rc = RTDirClose(pInfo->u.Local.hDir);
+ if (RT_SUCCESS(rc))
+ {
+ pInfo->u.Local.hDir = NIL_RTDIR;
+
+ LogRel2(("Shared Clipboard: Closed directory '%s'\n", pInfo->pszPathLocalAbs));
+ }
+ else
+ LogRel(("Shared Clipboard: Closing directory '%s' failed with %Rrc\n", pInfo->pszPathLocalAbs, rc));
+ break;
+ }
+
+ case SHCLOBJTYPE_FILE:
+ {
+ rc = RTFileClose(pInfo->u.Local.hFile);
+ if (RT_SUCCESS(rc))
+ {
+ pInfo->u.Local.hFile = NIL_RTFILE;
+
+ LogRel2(("Shared Clipboard: Closed file '%s'\n", pInfo->pszPathLocalAbs));
+ }
+ else
+ LogRel(("Shared Clipboard: Closing file '%s' failed with %Rrc\n", pInfo->pszPathLocalAbs, rc));
+ break;
+ }
+
+ default:
+ rc = VERR_NOT_IMPLEMENTED;
+ break;
+ }
+
+ RTListNodeRemove(&pInfo->Node);
+
+ Assert(pTransfer->cObjHandles);
+ pTransfer->cObjHandles--;
+
+ ShClTransferObjHandleInfoDestroy(pInfo);
+
+ RTMemFree(pInfo);
+ pInfo = NULL;
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+ else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
+ {
+ if (pTransfer->ProviderIface.pfnObjClose)
+ {
+ rc = pTransfer->ProviderIface.pfnObjClose(&pTransfer->ProviderCtx, hObj);
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else
+ AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Reads from a transfer object.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Clipboard transfer that contains the object to read from.
+ * @param hObj Handle of transfer object to read from.
+ * @param pvBuf Buffer for where to store the read data.
+ * @param cbBuf Size (in bytes) of buffer.
+ * @param fFlags Read flags. Optional.
+ * @param pcbRead Where to return how much bytes were read on success. Optional.
+ */
+int ShClTransferObjRead(PSHCLTRANSFER pTransfer,
+ SHCLOBJHANDLE hObj, void *pvBuf, uint32_t cbBuf, uint32_t fFlags, uint32_t *pcbRead)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
+ /* pcbRead is optional. */
+ /** @todo Validate fFlags. */
+
+ int rc = VINF_SUCCESS;
+
+ if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
+ {
+ PSHCLOBJHANDLEINFO pInfo = shClTransferObjGet(pTransfer, hObj);
+ if (pInfo)
+ {
+ switch (pInfo->enmType)
+ {
+ case SHCLOBJTYPE_FILE:
+ {
+ size_t cbRead;
+ rc = RTFileRead(pInfo->u.Local.hFile, pvBuf, cbBuf, &cbRead);
+ if (RT_SUCCESS(rc))
+ {
+ if (pcbRead)
+ *pcbRead = (uint32_t)cbRead;
+ }
+ break;
+ }
+
+ default:
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+ else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
+ {
+ if (pTransfer->ProviderIface.pfnObjRead)
+ {
+ rc = pTransfer->ProviderIface.pfnObjRead(&pTransfer->ProviderCtx, hObj, pvBuf, cbBuf, fFlags, pcbRead);
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else
+ AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Writes to a transfer object.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Clipboard transfer that contains the object to write to.
+ * @param hObj Handle of transfer object to write to.
+ * @param pvBuf Buffer of data to write.
+ * @param cbBuf Size (in bytes) of buffer to write.
+ * @param fFlags Write flags. Optional.
+ * @param pcbWritten How much bytes were writtenon success. Optional.
+ */
+int ShClTransferObjWrite(PSHCLTRANSFER pTransfer,
+ SHCLOBJHANDLE hObj, void *pvBuf, uint32_t cbBuf, uint32_t fFlags, uint32_t *pcbWritten)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn (cbBuf, VERR_INVALID_PARAMETER);
+ /* pcbWritten is optional. */
+
+ int rc = VINF_SUCCESS;
+
+ if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
+ {
+ PSHCLOBJHANDLEINFO pInfo = shClTransferObjGet(pTransfer, hObj);
+ if (pInfo)
+ {
+ switch (pInfo->enmType)
+ {
+ case SHCLOBJTYPE_FILE:
+ {
+ rc = RTFileWrite(pInfo->u.Local.hFile, pvBuf, cbBuf, (size_t *)pcbWritten);
+ break;
+ }
+
+ default:
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+ else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
+ {
+ if (pTransfer->ProviderIface.pfnObjWrite)
+ {
+ rc = pTransfer->ProviderIface.pfnObjWrite(&pTransfer->ProviderCtx, hObj, pvBuf, cbBuf, fFlags, pcbWritten);
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else
+ AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Duplicates a transfer object data chunk.
+ *
+ * @returns Duplicated object data chunk on success, or NULL on failure.
+ * @param pDataChunk transfer object data chunk to duplicate.
+ */
+PSHCLOBJDATACHUNK ShClTransferObjDataChunkDup(PSHCLOBJDATACHUNK pDataChunk)
+{
+ if (!pDataChunk)
+ return NULL;
+
+ PSHCLOBJDATACHUNK pDataChunkDup = (PSHCLOBJDATACHUNK)RTMemAllocZ(sizeof(SHCLOBJDATACHUNK));
+ if (!pDataChunkDup)
+ return NULL;
+
+ if (pDataChunk->pvData)
+ {
+ Assert(pDataChunk->cbData);
+
+ pDataChunkDup->uHandle = pDataChunk->uHandle;
+ pDataChunkDup->pvData = RTMemDup(pDataChunk->pvData, pDataChunk->cbData);
+ pDataChunkDup->cbData = pDataChunk->cbData;
+ }
+
+ return pDataChunkDup;
+}
+
+/**
+ * Destroys a transfer object data chunk.
+ *
+ * @param pDataChunk transfer object data chunk to destroy.
+ */
+void ShClTransferObjDataChunkDestroy(PSHCLOBJDATACHUNK pDataChunk)
+{
+ if (!pDataChunk)
+ return;
+
+ if (pDataChunk->pvData)
+ {
+ Assert(pDataChunk->cbData);
+
+ RTMemFree(pDataChunk->pvData);
+
+ pDataChunk->pvData = NULL;
+ pDataChunk->cbData = 0;
+ }
+
+ pDataChunk->uHandle = 0;
+}
+
+/**
+ * Frees a transfer object data chunk.
+ *
+ * @param pDataChunk transfer object data chunk to free. The handed-in pointer will
+ * be invalid after calling this function.
+ */
+void ShClTransferObjDataChunkFree(PSHCLOBJDATACHUNK pDataChunk)
+{
+ if (!pDataChunk)
+ return;
+
+ ShClTransferObjDataChunkDestroy(pDataChunk);
+
+ RTMemFree(pDataChunk);
+ pDataChunk = NULL;
+}
+
+/**
+ * Creates a clipboard transfer.
+ *
+ * @returns VBox status code.
+ * @param ppTransfer Where to return the created Shared Clipboard transfer struct.
+ * Must be destroyed by ShClTransferDestroy().
+ */
+int ShClTransferCreate(PSHCLTRANSFER *ppTransfer)
+{
+ AssertPtrReturn(ppTransfer, VERR_INVALID_POINTER);
+
+ LogFlowFuncEnter();
+
+ PSHCLTRANSFER pTransfer = (PSHCLTRANSFER)RTMemAllocZ(sizeof(SHCLTRANSFER));
+ AssertPtrReturn(pTransfer, VERR_NO_MEMORY);
+
+ pTransfer->State.uID = 0;
+ pTransfer->State.enmStatus = SHCLTRANSFERSTATUS_NONE;
+ pTransfer->State.enmDir = SHCLTRANSFERDIR_UNKNOWN;
+ pTransfer->State.enmSource = SHCLSOURCE_INVALID;
+
+ pTransfer->Thread.hThread = NIL_RTTHREAD;
+ pTransfer->Thread.fCancelled = false;
+ pTransfer->Thread.fStarted = false;
+ pTransfer->Thread.fStop = false;
+
+ pTransfer->pszPathRootAbs = NULL;
+
+#ifdef DEBUG_andy
+ pTransfer->uTimeoutMs = RT_MS_5SEC;
+#else
+ pTransfer->uTimeoutMs = RT_MS_30SEC;
+#endif
+ pTransfer->cbMaxChunkSize = _64K; /** @todo Make this configurable. */
+ pTransfer->cMaxListHandles = _4K; /** @todo Ditto. */
+ pTransfer->cMaxObjHandles = _4K; /** @todo Ditto. */
+
+ pTransfer->pvUser = NULL;
+ pTransfer->cbUser = 0;
+
+ RTListInit(&pTransfer->lstList);
+ RTListInit(&pTransfer->lstObj);
+
+ pTransfer->cRoots = 0;
+ RTListInit(&pTransfer->lstRoots);
+
+ int rc = ShClEventSourceCreate(&pTransfer->Events, 0 /* uID */);
+ if (RT_SUCCESS(rc))
+ {
+ *ppTransfer = pTransfer;
+ }
+ else
+ {
+ if (pTransfer)
+ {
+ ShClTransferDestroy(pTransfer);
+ RTMemFree(pTransfer);
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Destroys a clipboard transfer context struct.
+ *
+ * @returns VBox status code.
+ * @param pTransferCtx Clipboard transfer to destroy.
+ */
+int ShClTransferDestroy(PSHCLTRANSFER pTransfer)
+{
+ if (!pTransfer)
+ return VINF_SUCCESS;
+
+ LogFlowFuncEnter();
+
+ int rc = shClTransferThreadDestroy(pTransfer, 30 * 1000 /* Timeout in ms */);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ ShClTransferReset(pTransfer);
+
+ ShClEventSourceDestroy(&pTransfer->Events);
+
+ LogFlowFuncLeave();
+ return VINF_SUCCESS;
+}
+
+/**
+ * Initializes a Shared Clipboard transfer object.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Transfer to initialize.
+ * @param enmDir Specifies the transfer direction of this transfer.
+ * @param enmSource Specifies the data source of the transfer.
+ */
+int ShClTransferInit(PSHCLTRANSFER pTransfer, SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource)
+{
+ pTransfer->State.enmDir = enmDir;
+ pTransfer->State.enmSource = enmSource;
+
+ LogFlowFunc(("uID=%RU32, enmDir=%RU32, enmSource=%RU32\n",
+ pTransfer->State.uID, pTransfer->State.enmDir, pTransfer->State.enmSource));
+
+ pTransfer->State.enmStatus = SHCLTRANSFERSTATUS_INITIALIZED; /* Now we're ready to run. */
+
+ pTransfer->cListHandles = 0;
+ pTransfer->uListHandleNext = 1;
+
+ pTransfer->cObjHandles = 0;
+ pTransfer->uObjHandleNext = 1;
+
+ int rc = VINF_SUCCESS;
+
+ if (pTransfer->Callbacks.pfnOnInitialize)
+ rc = pTransfer->Callbacks.pfnOnInitialize(&pTransfer->CallbackCtx);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Returns a specific list handle info of a transfer.
+ *
+ * @returns Pointer to list handle info if found, or NULL if not found.
+ * @param pTransfer Clipboard transfer to get list handle info from.
+ * @param hList List handle of the list to get handle info for.
+ */
+DECLINLINE(PSHCLLISTHANDLEINFO) shClTransferListGetByHandle(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList)
+{
+ PSHCLLISTHANDLEINFO pIt;
+ RTListForEach(&pTransfer->lstList, pIt, SHCLLISTHANDLEINFO, Node) /** @todo Sloooow ... improve this. */
+ {
+ if (pIt->hList == hList)
+ return pIt;
+ }
+
+ return NULL;
+}
+
+/**
+ * Creates a new list handle (local only).
+ *
+ * @returns New List handle on success, or SHCLLISTHANDLE_INVALID on error.
+ * @param pTransfer Clipboard transfer to create new list handle for.
+ */
+DECLINLINE(SHCLLISTHANDLE) shClTransferListHandleNew(PSHCLTRANSFER pTransfer)
+{
+ return pTransfer->uListHandleNext++; /** @todo Good enough for now. Improve this later. */
+}
+
+/**
+ * Validates whether a given path matches our set of rules or not.
+ *
+ * @returns VBox status code.
+ * @param pcszPath Path to validate.
+ * @param fMustExist Whether the path to validate also must exist.
+ */
+static int shClTransferValidatePath(const char *pcszPath, bool fMustExist)
+{
+ int rc = VINF_SUCCESS;
+
+ if (!strlen(pcszPath))
+ rc = VERR_INVALID_PARAMETER;
+
+ if ( RT_SUCCESS(rc)
+ && !RTStrIsValidEncoding(pcszPath))
+ {
+ rc = VERR_INVALID_UTF8_ENCODING;
+ }
+
+ if ( RT_SUCCESS(rc)
+ && RTStrStr(pcszPath, ".."))
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ if ( RT_SUCCESS(rc)
+ && fMustExist)
+ {
+ RTFSOBJINFO objInfo;
+ rc = RTPathQueryInfo(pcszPath, &objInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
+ {
+ if (!RTDirExists(pcszPath)) /* Path must exist. */
+ rc = VERR_PATH_NOT_FOUND;
+ }
+ else if (RTFS_IS_FILE(objInfo.Attr.fMode))
+ {
+ if (!RTFileExists(pcszPath)) /* File must exist. */
+ rc = VERR_FILE_NOT_FOUND;
+ }
+ else /* Everything else (e.g. symbolic links) are not supported. */
+ {
+ LogRel2(("Shared Clipboard: Path '%s' contains a symbolic link or junktion, which are not supported\n", pcszPath));
+ rc = VERR_NOT_SUPPORTED;
+ }
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ LogRel2(("Shared Clipboard: Validating path '%s' failed: %Rrc\n", pcszPath, rc));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Resolves a relative path of a specific transfer to its absolute path.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Clipboard transfer to resolve path for.
+ * @param pszPath Path to resolve.
+ * @param fFlags Resolve flags. Currently not used and must be 0.
+ * @param ppszResolved Where to store the allocated resolved path. Must be free'd by the called using RTStrFree().
+ */
+static int shClTransferResolvePathAbs(PSHCLTRANSFER pTransfer, const char *pszPath, uint32_t fFlags,
+ char **ppszResolved)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn (fFlags == 0, VERR_INVALID_PARAMETER);
+
+ LogFlowFunc(("pszPathRootAbs=%s, pszPath=%s\n", pTransfer->pszPathRootAbs, pszPath));
+
+ int rc = shClTransferValidatePath(pszPath, false /* fMustExist */);
+ if (RT_SUCCESS(rc))
+ {
+ char *pszPathAbs = RTPathJoinA(pTransfer->pszPathRootAbs, pszPath);
+ if (pszPathAbs)
+ {
+ char szResolved[RTPATH_MAX];
+ size_t cbResolved = sizeof(szResolved);
+ rc = RTPathAbsEx(pTransfer->pszPathRootAbs, pszPathAbs, RTPATH_STR_F_STYLE_HOST, szResolved, &cbResolved);
+
+ RTStrFree(pszPathAbs);
+
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("pszResolved=%s\n", szResolved));
+
+ rc = VERR_PATH_NOT_FOUND; /* Play safe by default. */
+
+ /* Make sure the resolved path is part of the set of root entries. */
+ PSHCLLISTROOT pListRoot;
+ RTListForEach(&pTransfer->lstRoots, pListRoot, SHCLLISTROOT, Node)
+ {
+ if (RTPathStartsWith(szResolved, pListRoot->pszPathAbs))
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ *ppszResolved = RTStrDup(szResolved);
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(rc))
+ LogRel(("Shared Clipboard: Resolving absolute path '%s' failed, rc=%Rrc\n", pszPath, rc));
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Opens a list.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Clipboard transfer to handle.
+ * @param pOpenParms List open parameters to use for opening.
+ * @param phList Where to store the List handle of opened list on success.
+ */
+int ShClTransferListOpen(PSHCLTRANSFER pTransfer, PSHCLLISTOPENPARMS pOpenParms,
+ PSHCLLISTHANDLE phList)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+ AssertPtrReturn(pOpenParms, VERR_INVALID_POINTER);
+ AssertPtrReturn(phList, VERR_INVALID_POINTER);
+
+ int rc;
+
+ if (pTransfer->cListHandles == pTransfer->cMaxListHandles)
+ return VERR_SHCLPB_MAX_LISTS_REACHED;
+
+ if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
+ {
+ LogFlowFunc(("pszPath=%s\n", pOpenParms->pszPath));
+
+ PSHCLLISTHANDLEINFO pInfo
+ = (PSHCLLISTHANDLEINFO)RTMemAllocZ(sizeof(SHCLLISTHANDLEINFO));
+ if (pInfo)
+ {
+ rc = ShClTransferListHandleInfoInit(pInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = shClTransferResolvePathAbs(pTransfer, pOpenParms->pszPath, 0 /* fFlags */, &pInfo->pszPathLocalAbs);
+ if (RT_SUCCESS(rc))
+ {
+ RTFSOBJINFO objInfo;
+ rc = RTPathQueryInfo(pInfo->pszPathLocalAbs, &objInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
+ {
+ rc = RTDirOpen(&pInfo->u.Local.hDir, pInfo->pszPathLocalAbs);
+ if (RT_SUCCESS(rc))
+ {
+ pInfo->enmType = SHCLOBJTYPE_DIRECTORY;
+
+ LogRel2(("Shared Clipboard: Opening directory '%s'\n", pInfo->pszPathLocalAbs));
+ }
+ else
+ LogRel(("Shared Clipboard: Opening directory '%s' failed with %Rrc\n", pInfo->pszPathLocalAbs, rc));
+
+ }
+ else if (RTFS_IS_FILE(objInfo.Attr.fMode))
+ {
+ rc = RTFileOpen(&pInfo->u.Local.hFile, pInfo->pszPathLocalAbs,
+ RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
+ if (RT_SUCCESS(rc))
+ {
+ pInfo->enmType = SHCLOBJTYPE_FILE;
+
+ LogRel2(("Shared Clipboard: Opening file '%s'\n", pInfo->pszPathLocalAbs));
+ }
+ else
+ LogRel(("Shared Clipboard: Opening file '%s' failed with %Rrc\n", pInfo->pszPathLocalAbs, rc));
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ if (RT_SUCCESS(rc))
+ {
+ pInfo->hList = shClTransferListHandleNew(pTransfer);
+
+ RTListAppend(&pTransfer->lstList, &pInfo->Node);
+ pTransfer->cListHandles++;
+
+ if (phList)
+ *phList = pInfo->hList;
+
+ LogFlowFunc(("pszPathLocalAbs=%s, hList=%RU64, cListHandles=%RU32\n",
+ pInfo->pszPathLocalAbs, pInfo->hList, pTransfer->cListHandles));
+ }
+ else
+ {
+ if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
+ {
+ if (RTDirIsValid(pInfo->u.Local.hDir))
+ RTDirClose(pInfo->u.Local.hDir);
+ }
+ else if (RTFS_IS_FILE(objInfo.Attr.fMode))
+ {
+ if (RTFileIsValid(pInfo->u.Local.hFile))
+ RTFileClose(pInfo->u.Local.hFile);
+ }
+ }
+ }
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ ShClTransferListHandleInfoDestroy(pInfo);
+
+ RTMemFree(pInfo);
+ pInfo = NULL;
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
+ {
+ if (pTransfer->ProviderIface.pfnListOpen)
+ {
+ rc = pTransfer->ProviderIface.pfnListOpen(&pTransfer->ProviderCtx, pOpenParms, phList);
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else
+ AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Closes a list.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Clipboard transfer to handle.
+ * @param hList Handle of list to close.
+ */
+int ShClTransferListClose(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+
+ if (hList == SHCLLISTHANDLE_INVALID)
+ return VINF_SUCCESS;
+
+ int rc = VINF_SUCCESS;
+
+ if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
+ {
+ PSHCLLISTHANDLEINFO pInfo = shClTransferListGetByHandle(pTransfer, hList);
+ if (pInfo)
+ {
+ switch (pInfo->enmType)
+ {
+ case SHCLOBJTYPE_DIRECTORY:
+ {
+ if (RTDirIsValid(pInfo->u.Local.hDir))
+ {
+ RTDirClose(pInfo->u.Local.hDir);
+ pInfo->u.Local.hDir = NIL_RTDIR;
+ }
+ break;
+ }
+
+ default:
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+
+ RTListNodeRemove(&pInfo->Node);
+
+ Assert(pTransfer->cListHandles);
+ pTransfer->cListHandles--;
+
+ RTMemFree(pInfo);
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+ else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
+ {
+ if (pTransfer->ProviderIface.pfnListClose)
+ {
+ rc = pTransfer->ProviderIface.pfnListClose(&pTransfer->ProviderCtx, hList);
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Adds a file to a list heaer.
+ *
+ * @returns VBox status code.
+ * @param pHdr List header to add file to.
+ * @param pszPath Path of file to add.
+ */
+static int shclTransferListHdrAddFile(PSHCLLISTHDR pHdr, const char *pszPath)
+{
+ uint64_t cbSize = 0;
+ int rc = RTFileQuerySizeByPath(pszPath, &cbSize);
+ if (RT_SUCCESS(rc))
+ {
+ pHdr->cbTotalSize += cbSize;
+ pHdr->cTotalObjects++;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Builds a list header, internal version.
+ *
+ * @returns VBox status code.
+ * @param pHdr Where to store the build list header.
+ * @param pcszSrcPath Source path of list.
+ * @param pcszDstPath Destination path of list.
+ * @param pcszDstBase Destination base path.
+ * @param cchDstBase Number of charaters of destination base path.
+ */
+static int shclTransferListHdrFromDir(PSHCLLISTHDR pHdr, const char *pcszPathAbs)
+{
+ AssertPtrReturn(pcszPathAbs, VERR_INVALID_POINTER);
+
+ LogFlowFunc(("pcszPathAbs=%s\n", pcszPathAbs));
+
+ RTFSOBJINFO objInfo;
+ int rc = RTPathQueryInfo(pcszPathAbs, &objInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc))
+ {
+ if (RTFS_IS_DIRECTORY(objInfo.Attr.fMode))
+ {
+ RTDIR hDir;
+ rc = RTDirOpen(&hDir, pcszPathAbs);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbDirEntry = 0;
+ PRTDIRENTRYEX pDirEntry = NULL;
+ do
+ {
+ /* Retrieve the next directory entry. */
+ rc = RTDirReadExA(hDir, &pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_NO_MORE_FILES)
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
+ {
+ case RTFS_TYPE_DIRECTORY:
+ {
+ /* Skip "." and ".." entries. */
+ if (RTDirEntryExIsStdDotLink(pDirEntry))
+ break;
+
+ pHdr->cTotalObjects++;
+ break;
+ }
+ case RTFS_TYPE_FILE:
+ {
+ char *pszSrc = RTPathJoinA(pcszPathAbs, pDirEntry->szName);
+ if (pszSrc)
+ {
+ rc = shclTransferListHdrAddFile(pHdr, pszSrc);
+ RTStrFree(pszSrc);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ case RTFS_TYPE_SYMLINK:
+ {
+ /** @todo Not implemented yet. */
+ }
+
+ default:
+ break;
+ }
+
+ } while (RT_SUCCESS(rc));
+
+ RTDirReadExAFree(&pDirEntry, &cbDirEntry);
+ RTDirClose(hDir);
+ }
+ }
+ else if (RTFS_IS_FILE(objInfo.Attr.fMode))
+ {
+ rc = shclTransferListHdrAddFile(pHdr, pcszPathAbs);
+ }
+ else if (RTFS_IS_SYMLINK(objInfo.Attr.fMode))
+ {
+ /** @todo Not implemented yet. */
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Retrieves the header of a Shared Clipboard list.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Clipboard transfer to handle.
+ * @param hList Handle of list to get header for.
+ * @param pHdr Where to store the returned list header information.
+ */
+int ShClTransferListGetHeader(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList,
+ PSHCLLISTHDR pHdr)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHdr, VERR_INVALID_POINTER);
+
+ int rc;
+
+ LogFlowFunc(("hList=%RU64\n", hList));
+
+ if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
+ {
+ PSHCLLISTHANDLEINFO pInfo = shClTransferListGetByHandle(pTransfer, hList);
+ if (pInfo)
+ {
+ rc = ShClTransferListHdrInit(pHdr);
+ if (RT_SUCCESS(rc))
+ {
+ switch (pInfo->enmType)
+ {
+ case SHCLOBJTYPE_DIRECTORY:
+ {
+ LogFlowFunc(("DirAbs: %s\n", pInfo->pszPathLocalAbs));
+
+ rc = shclTransferListHdrFromDir(pHdr, pInfo->pszPathLocalAbs);
+ break;
+ }
+
+ case SHCLOBJTYPE_FILE:
+ {
+ LogFlowFunc(("FileAbs: %s\n", pInfo->pszPathLocalAbs));
+
+ pHdr->cTotalObjects = 1;
+
+ RTFSOBJINFO objInfo;
+ rc = RTFileQueryInfo(pInfo->u.Local.hFile, &objInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc))
+ {
+ pHdr->cbTotalSize = objInfo.cbObject;
+ }
+ break;
+ }
+
+ default:
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+
+ LogFlowFunc(("cTotalObj=%RU64, cbTotalSize=%RU64\n", pHdr->cTotalObjects, pHdr->cbTotalSize));
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+ else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
+ {
+ if (pTransfer->ProviderIface.pfnListHdrRead)
+ {
+ rc = pTransfer->ProviderIface.pfnListHdrRead(&pTransfer->ProviderCtx, hList, pHdr);
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else
+ AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Returns the current transfer object for a Shared Clipboard transfer list.
+ *
+ * Currently not implemented and wil return NULL.
+ *
+ * @returns Pointer to transfer object, or NULL if not found / invalid.
+ * @param pTransfer Clipboard transfer to return transfer object for.
+ * @param hList Handle of Shared Clipboard transfer list to get object for.
+ * @param uIdx Index of object to get.
+ */
+PSHCLTRANSFEROBJ ShClTransferListGetObj(PSHCLTRANSFER pTransfer,
+ SHCLLISTHANDLE hList, uint64_t uIdx)
+{
+ AssertPtrReturn(pTransfer, NULL);
+
+ RT_NOREF(hList, uIdx);
+
+ LogFlowFunc(("hList=%RU64\n", hList));
+
+ return NULL;
+}
+
+/**
+ * Reads a single Shared Clipboard list entry.
+ *
+ * @returns VBox status code or VERR_NO_MORE_FILES if the end of the list has been reached.
+ * @param pTransfer Clipboard transfer to handle.
+ * @param hList List handle of list to read from.
+ * @param pEntry Where to store the read information.
+ */
+int ShClTransferListRead(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList,
+ PSHCLLISTENTRY pEntry)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+ AssertPtrReturn(pEntry, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("hList=%RU64\n", hList));
+
+ if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
+ {
+ PSHCLLISTHANDLEINFO pInfo = shClTransferListGetByHandle(pTransfer, hList);
+ if (pInfo)
+ {
+ switch (pInfo->enmType)
+ {
+ case SHCLOBJTYPE_DIRECTORY:
+ {
+ LogFlowFunc(("\tDirectory: %s\n", pInfo->pszPathLocalAbs));
+
+ for (;;)
+ {
+ bool fSkipEntry = false; /* Whether to skip an entry in the enumeration. */
+
+ size_t cbDirEntry = 0;
+ PRTDIRENTRYEX pDirEntry = NULL;
+ rc = RTDirReadExA(pInfo->u.Local.hDir, &pDirEntry, &cbDirEntry, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc))
+ {
+ switch (pDirEntry->Info.Attr.fMode & RTFS_TYPE_MASK)
+ {
+ case RTFS_TYPE_DIRECTORY:
+ {
+ /* Skip "." and ".." entries. */
+ if (RTDirEntryExIsStdDotLink(pDirEntry))
+ {
+ fSkipEntry = true;
+ break;
+ }
+
+ LogFlowFunc(("Directory: %s\n", pDirEntry->szName));
+ break;
+ }
+
+ case RTFS_TYPE_FILE:
+ {
+ LogFlowFunc(("File: %s\n", pDirEntry->szName));
+ break;
+ }
+
+ case RTFS_TYPE_SYMLINK:
+ {
+ rc = VERR_NOT_IMPLEMENTED; /** @todo Not implemented yet. */
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if ( RT_SUCCESS(rc)
+ && !fSkipEntry)
+ {
+ rc = RTStrCopy(pEntry->pszName, pEntry->cbName, pDirEntry->szName);
+ if (RT_SUCCESS(rc))
+ {
+ pEntry->cbName = (uint32_t)strlen(pEntry->pszName) + 1; /* Include termination. */
+
+ AssertPtr(pEntry->pvInfo);
+ Assert (pEntry->cbInfo == sizeof(SHCLFSOBJINFO));
+
+ ShClFsObjFromIPRT(PSHCLFSOBJINFO(pEntry->pvInfo), &pDirEntry->Info);
+
+ LogFlowFunc(("Entry pszName=%s, pvInfo=%p, cbInfo=%RU32\n",
+ pEntry->pszName, pEntry->pvInfo, pEntry->cbInfo));
+ }
+ }
+
+ RTDirReadExAFree(&pDirEntry, &cbDirEntry);
+ }
+
+ if ( !fSkipEntry /* Do we have a valid entry? Bail out. */
+ || RT_FAILURE(rc))
+ {
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case SHCLOBJTYPE_FILE:
+ {
+ LogFlowFunc(("\tSingle file: %s\n", pInfo->pszPathLocalAbs));
+
+ RTFSOBJINFO objInfo;
+ rc = RTFileQueryInfo(pInfo->u.Local.hFile, &objInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc))
+ {
+ pEntry->pvInfo = (PSHCLFSOBJINFO)RTMemAlloc(sizeof(SHCLFSOBJINFO));
+ if (pEntry->pvInfo)
+ {
+ rc = RTStrCopy(pEntry->pszName, pEntry->cbName, pInfo->pszPathLocalAbs);
+ if (RT_SUCCESS(rc))
+ {
+ ShClFsObjFromIPRT(PSHCLFSOBJINFO(pEntry->pvInfo), &objInfo);
+
+ pEntry->cbInfo = sizeof(SHCLFSOBJINFO);
+ pEntry->fInfo = VBOX_SHCL_INFO_FLAG_FSOBJINFO;
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ break;
+ }
+
+ default:
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+ else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
+ {
+ if (pTransfer->ProviderIface.pfnListEntryRead)
+ rc = pTransfer->ProviderIface.pfnListEntryRead(&pTransfer->ProviderCtx, hList, pEntry);
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else
+ AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int ShClTransferListWrite(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList,
+ PSHCLLISTENTRY pEntry)
+{
+ RT_NOREF(pTransfer, hList, pEntry);
+
+ int rc = VINF_SUCCESS;
+
+#if 0
+ if (pTransfer->ProviderIface.pfnListEntryWrite)
+ rc = pTransfer->ProviderIface.pfnListEntryWrite(&pTransfer->ProviderCtx, hList, pEntry);
+#endif
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Returns whether a given list handle is valid or not.
+ *
+ * @returns \c true if list handle is valid, \c false if not.
+ * @param pTransfer Clipboard transfer to handle.
+ * @param hList List handle to check.
+ */
+bool ShClTransferListHandleIsValid(PSHCLTRANSFER pTransfer, SHCLLISTHANDLE hList)
+{
+ bool fIsValid = false;
+
+ if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
+ {
+ fIsValid = shClTransferListGetByHandle(pTransfer, hList) != NULL;
+ }
+ else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
+ {
+ AssertFailed(); /** @todo Implement. */
+ }
+ else
+ AssertFailedStmt(fIsValid = false);
+
+ return fIsValid;
+}
+
+/**
+ * Copies a transfer callback table from source to destination.
+ *
+ * @param pCallbacksDst Callback destination.
+ * @param pCallbacksSrc Callback source. If set to NULL, the
+ * destination callback table will be unset.
+ */
+void ShClTransferCopyCallbacks(PSHCLTRANSFERCALLBACKTABLE pCallbacksDst,
+ PSHCLTRANSFERCALLBACKTABLE pCallbacksSrc)
+{
+ AssertPtrReturnVoid(pCallbacksDst);
+
+ if (pCallbacksSrc) /* Set */
+ {
+#define SET_CALLBACK(a_pfnCallback) \
+ if (pCallbacksSrc->a_pfnCallback) \
+ pCallbacksDst->a_pfnCallback = pCallbacksSrc->a_pfnCallback
+
+ SET_CALLBACK(pfnOnInitialize);
+ SET_CALLBACK(pfnOnStart);
+ SET_CALLBACK(pfnOnCompleted);
+ SET_CALLBACK(pfnOnError);
+ SET_CALLBACK(pfnOnRegistered);
+ SET_CALLBACK(pfnOnUnregistered);
+
+#undef SET_CALLBACK
+
+ pCallbacksDst->pvUser = pCallbacksSrc->pvUser;
+ pCallbacksDst->cbUser = pCallbacksSrc->cbUser;
+ }
+ else /* Unset */
+ RT_BZERO(pCallbacksDst, sizeof(SHCLTRANSFERCALLBACKTABLE));
+}
+
+/**
+ * Sets or unsets the callback table to be used for a Shared Clipboard transfer.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Clipboard transfer to set callbacks for.
+ * @param pCallbacks Pointer to callback table to set. If set to NULL,
+ * existing callbacks for this transfer will be unset.
+ */
+void ShClTransferSetCallbacks(PSHCLTRANSFER pTransfer,
+ PSHCLTRANSFERCALLBACKTABLE pCallbacks)
+{
+ AssertPtrReturnVoid(pTransfer);
+ /* pCallbacks can be NULL. */
+
+ ShClTransferCopyCallbacks(&pTransfer->Callbacks, pCallbacks);
+}
+
+/**
+ * Sets the transfer provider interface for a given transfer.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Transfer to create transfer provider for.
+ * @param pCreationCtx Provider creation context to use for provider creation.
+ */
+int ShClTransferSetProviderIface(PSHCLTRANSFER pTransfer,
+ PSHCLTXPROVIDERCREATIONCTX pCreationCtx)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+ AssertPtrReturn(pCreationCtx, VERR_INVALID_POINTER);
+
+ LogFlowFuncEnter();
+
+ int rc = VINF_SUCCESS;
+
+ pTransfer->ProviderIface = pCreationCtx->Interface;
+ pTransfer->ProviderCtx.pTransfer = pTransfer;
+ pTransfer->ProviderCtx.pvUser = pCreationCtx->pvUser;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Clears (resets) the root list of a Shared Clipboard transfer.
+ *
+ * @param pTransfer Transfer to clear transfer root list for.
+ */
+static void shClTransferListRootsClear(PSHCLTRANSFER pTransfer)
+{
+ AssertPtrReturnVoid(pTransfer);
+
+ if (pTransfer->pszPathRootAbs)
+ {
+ RTStrFree(pTransfer->pszPathRootAbs);
+ pTransfer->pszPathRootAbs = NULL;
+ }
+
+ PSHCLLISTROOT pListRoot, pListRootNext;
+ RTListForEachSafe(&pTransfer->lstRoots, pListRoot, pListRootNext, SHCLLISTROOT, Node)
+ {
+ RTStrFree(pListRoot->pszPathAbs);
+
+ RTListNodeRemove(&pListRoot->Node);
+
+ RTMemFree(pListRoot);
+ pListRoot = NULL;
+ }
+
+ pTransfer->cRoots = 0;
+}
+
+/**
+ * Resets a Shared Clipboard transfer.
+ *
+ * @param pTransfer Clipboard transfer to reset.
+ */
+void ShClTransferReset(PSHCLTRANSFER pTransfer)
+{
+ AssertPtrReturnVoid(pTransfer);
+
+ LogFlowFuncEnter();
+
+ shClTransferListRootsClear(pTransfer);
+
+ PSHCLLISTHANDLEINFO pItList, pItListNext;
+ RTListForEachSafe(&pTransfer->lstList, pItList, pItListNext, SHCLLISTHANDLEINFO, Node)
+ {
+ ShClTransferListHandleInfoDestroy(pItList);
+
+ RTListNodeRemove(&pItList->Node);
+
+ RTMemFree(pItList);
+ }
+
+ PSHCLOBJHANDLEINFO pItObj, pItObjNext;
+ RTListForEachSafe(&pTransfer->lstObj, pItObj, pItObjNext, SHCLOBJHANDLEINFO, Node)
+ {
+ ShClTransferObjHandleInfoDestroy(pItObj);
+
+ RTListNodeRemove(&pItObj->Node);
+
+ RTMemFree(pItObj);
+ }
+}
+
+/**
+ * Returns the number of transfer root list entries.
+ *
+ * @returns Root list entry count.
+ * @param pTransfer Clipboard transfer to return root entry count for.
+ */
+uint32_t ShClTransferRootsCount(PSHCLTRANSFER pTransfer)
+{
+ AssertPtrReturn(pTransfer, 0);
+
+ LogFlowFunc(("[Transfer %RU32] cRoots=%RU64\n", pTransfer->State.uID, pTransfer->cRoots));
+ return (uint32_t)pTransfer->cRoots;
+}
+
+/**
+ * Returns a specific root list entry of a transfer.
+ *
+ * @returns Pointer to root list entry if found, or NULL if not found.
+ * @param pTransfer Clipboard transfer to get root list entry from.
+ * @param uIdx Index of root list entry to return.
+ */
+DECLINLINE(PSHCLLISTROOT) shClTransferRootsGetInternal(PSHCLTRANSFER pTransfer, uint32_t uIdx)
+{
+ if (uIdx >= pTransfer->cRoots)
+ return NULL;
+
+ PSHCLLISTROOT pIt = RTListGetFirst(&pTransfer->lstRoots, SHCLLISTROOT, Node);
+ while (uIdx--) /** @todo Slow, but works for now. */
+ pIt = RTListGetNext(&pTransfer->lstRoots, pIt, SHCLLISTROOT, Node);
+
+ return pIt;
+}
+
+/**
+ * Get a specific root list entry.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Clipboard transfer to get root list entry of.
+ * @param uIndex Index (zero-based) of entry to get.
+ * @param pEntry Where to store the returned entry on success.
+ */
+int ShClTransferRootsEntry(PSHCLTRANSFER pTransfer,
+ uint64_t uIndex, PSHCLROOTLISTENTRY pEntry)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+ AssertPtrReturn(pEntry, VERR_INVALID_POINTER);
+
+ if (uIndex >= pTransfer->cRoots)
+ return VERR_INVALID_PARAMETER;
+
+ int rc;
+
+ PSHCLLISTROOT pRoot = shClTransferRootsGetInternal(pTransfer, uIndex);
+ AssertPtrReturn(pRoot, VERR_INVALID_PARAMETER);
+
+ /* Make sure that we only advertise relative source paths, not absolute ones. */
+ const char *pcszSrcPath = pRoot->pszPathAbs;
+
+ char *pszFileName = RTPathFilename(pcszSrcPath);
+ if (pszFileName)
+ {
+ Assert(pszFileName >= pcszSrcPath);
+ size_t cchDstBase = pszFileName - pcszSrcPath;
+ const char *pszDstPath = &pcszSrcPath[cchDstBase];
+
+ LogFlowFunc(("pcszSrcPath=%s, pszDstPath=%s\n", pcszSrcPath, pszDstPath));
+
+ rc = ShClTransferListEntryInit(pEntry);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrCopy(pEntry->pszName, pEntry->cbName, pszDstPath);
+ if (RT_SUCCESS(rc))
+ {
+ pEntry->cbInfo = sizeof(SHCLFSOBJINFO);
+ pEntry->pvInfo = (PSHCLFSOBJINFO)RTMemAlloc(pEntry->cbInfo);
+ if (pEntry->pvInfo)
+ {
+ RTFSOBJINFO fsObjInfo;
+ rc = RTPathQueryInfo(pcszSrcPath, &fsObjInfo, RTFSOBJATTRADD_NOTHING);
+ if (RT_SUCCESS(rc))
+ {
+ ShClFsObjFromIPRT(PSHCLFSOBJINFO(pEntry->pvInfo), &fsObjInfo);
+
+ pEntry->fInfo = VBOX_SHCL_INFO_FLAG_FSOBJINFO;
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ }
+ else
+ rc = VERR_INVALID_POINTER;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Returns the root entries of a Shared Clipboard transfer.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Clipboard transfer to return root entries for.
+ * @param ppRootList Where to store the root list on success.
+ */
+int ShClTransferRootsGet(PSHCLTRANSFER pTransfer, PSHCLROOTLIST *ppRootList)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+ AssertPtrReturn(ppRootList, VERR_INVALID_POINTER);
+
+ LogFlowFuncEnter();
+
+ int rc = VINF_SUCCESS;
+
+ if (pTransfer->State.enmSource == SHCLSOURCE_LOCAL)
+ {
+ PSHCLROOTLIST pRootList = ShClTransferRootListAlloc();
+ if (!pRootList)
+ return VERR_NO_MEMORY;
+
+ const uint64_t cRoots = (uint32_t)pTransfer->cRoots;
+
+ LogFlowFunc(("cRoots=%RU64\n", cRoots));
+
+ if (cRoots)
+ {
+ PSHCLROOTLISTENTRY paRootListEntries
+ = (PSHCLROOTLISTENTRY)RTMemAllocZ(cRoots * sizeof(SHCLROOTLISTENTRY));
+ if (paRootListEntries)
+ {
+ for (uint64_t i = 0; i < cRoots; ++i)
+ {
+ rc = ShClTransferRootsEntry(pTransfer, i, &paRootListEntries[i]);
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ if (RT_SUCCESS(rc))
+ pRootList->paEntries = paRootListEntries;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_NOT_FOUND;
+
+ if (RT_SUCCESS(rc))
+ {
+ pRootList->Hdr.cRoots = cRoots;
+ pRootList->Hdr.fRoots = 0; /** @todo Implement this. */
+
+ *ppRootList = pRootList;
+ }
+ }
+ else if (pTransfer->State.enmSource == SHCLSOURCE_REMOTE)
+ {
+ if (pTransfer->ProviderIface.pfnRootsGet)
+ rc = pTransfer->ProviderIface.pfnRootsGet(&pTransfer->ProviderCtx, ppRootList);
+ else
+ rc = VERR_NOT_SUPPORTED;
+ }
+ else
+ AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Sets transfer root list entries for a given transfer.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Transfer to set transfer list entries for.
+ * @param pszRoots String list (separated by CRLF) of root entries to set.
+ * All entries must have the same root path.
+ * @param cbRoots Size (in bytes) of string list.
+ */
+int ShClTransferRootsSet(PSHCLTRANSFER pTransfer, const char *pszRoots, size_t cbRoots)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszRoots, VERR_INVALID_POINTER);
+ AssertReturn(cbRoots, VERR_INVALID_PARAMETER);
+
+ if (!RTStrIsValidEncoding(pszRoots))
+ return VERR_INVALID_UTF8_ENCODING;
+
+ int rc = VINF_SUCCESS;
+
+ shClTransferListRootsClear(pTransfer);
+
+ char *pszPathRootAbs = NULL;
+
+ RTCList<RTCString> lstRootEntries = RTCString(pszRoots, cbRoots - 1).split("\r\n");
+ for (size_t i = 0; i < lstRootEntries.size(); ++i)
+ {
+ PSHCLLISTROOT pListRoot = (PSHCLLISTROOT)RTMemAlloc(sizeof(SHCLLISTROOT));
+ AssertPtrBreakStmt(pListRoot, rc = VERR_NO_MEMORY);
+
+ const char *pszPathCur = RTStrDup(lstRootEntries.at(i).c_str());
+
+ LogFlowFunc(("pszPathCur=%s\n", pszPathCur));
+
+ /* No root path determined yet? */
+ if (!pszPathRootAbs)
+ {
+ pszPathRootAbs = RTStrDup(pszPathCur);
+ if (pszPathRootAbs)
+ {
+ RTPathStripFilename(pszPathRootAbs);
+
+ LogFlowFunc(("pszPathRootAbs=%s\n", pszPathRootAbs));
+
+ /* We don't want to have a relative directory here. */
+ if (RTPathStartsWithRoot(pszPathRootAbs))
+ {
+ rc = shClTransferValidatePath(pszPathRootAbs, true /* Path must exist */);
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(rc))
+ break;
+
+ pListRoot->pszPathAbs = RTStrDup(pszPathCur);
+ if (!pListRoot->pszPathAbs)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+
+ RTListAppend(&pTransfer->lstRoots, &pListRoot->Node);
+
+ pTransfer->cRoots++;
+ }
+
+ /* No (valid) root directory found? Bail out early. */
+ if (!pszPathRootAbs)
+ rc = VERR_PATH_NOT_FOUND;
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Step 2:
+ * Go through the created list and make sure all entries have the same root path.
+ */
+ PSHCLLISTROOT pListRoot;
+ RTListForEach(&pTransfer->lstRoots, pListRoot, SHCLLISTROOT, Node)
+ {
+ if (!RTStrStartsWith(pListRoot->pszPathAbs, pszPathRootAbs))
+ {
+ rc = VERR_INVALID_PARAMETER;
+ break;
+ }
+
+ rc = shClTransferValidatePath(pListRoot->pszPathAbs, true /* Path must exist */);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+
+ /** @todo Entry rollback on failure? */
+
+ if (RT_SUCCESS(rc))
+ {
+ pTransfer->pszPathRootAbs = pszPathRootAbs;
+ LogFlowFunc(("pszPathRootAbs=%s, cRoots=%zu\n", pTransfer->pszPathRootAbs, pTransfer->cRoots));
+
+ LogRel2(("Shared Clipboard: Transfer uses root '%s'\n", pTransfer->pszPathRootAbs));
+ }
+ else
+ {
+ LogRel(("Shared Clipboard: Unable to set roots for transfer, rc=%Rrc\n", rc));
+ RTStrFree(pszPathRootAbs);
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Returns the transfer's ID.
+ *
+ * @returns The transfer's ID.
+ * @param pTransfer Clipboard transfer to return ID for.
+ */
+SHCLTRANSFERID ShClTransferGetID(PSHCLTRANSFER pTransfer)
+{
+ AssertPtrReturn(pTransfer, 0);
+
+ return pTransfer->State.uID;
+}
+
+/**
+ * Returns the transfer's direction.
+ *
+ * @returns The transfer's direction.
+ * @param pTransfer Clipboard transfer to return direction for.
+ */
+SHCLTRANSFERDIR ShClTransferGetDir(PSHCLTRANSFER pTransfer)
+{
+ AssertPtrReturn(pTransfer, SHCLTRANSFERDIR_UNKNOWN);
+
+ LogFlowFunc(("[Transfer %RU32] enmDir=%RU32\n", pTransfer->State.uID, pTransfer->State.enmDir));
+ return pTransfer->State.enmDir;
+}
+
+/**
+ * Returns the transfer's source.
+ *
+ * @returns The transfer's source.
+ * @param pTransfer Clipboard transfer to return source for.
+ */
+SHCLSOURCE ShClTransferGetSource(PSHCLTRANSFER pTransfer)
+{
+ AssertPtrReturn(pTransfer, SHCLSOURCE_INVALID);
+
+ LogFlowFunc(("[Transfer %RU32] enmSource=%RU32\n", pTransfer->State.uID, pTransfer->State.enmSource));
+ return pTransfer->State.enmSource;
+}
+
+/**
+ * Returns the current transfer status.
+ *
+ * @returns Current transfer status.
+ * @param pTransfer Clipboard transfer to return status for.
+ */
+SHCLTRANSFERSTATUS ShClTransferGetStatus(PSHCLTRANSFER pTransfer)
+{
+ AssertPtrReturn(pTransfer, SHCLTRANSFERSTATUS_NONE);
+
+ LogFlowFunc(("[Transfer %RU32] enmStatus=%RU32\n", pTransfer->State.uID, pTransfer->State.enmStatus));
+ return pTransfer->State.enmStatus;
+}
+
+/**
+ * Runs a started Shared Clipboard transfer in a dedicated thread.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Clipboard transfer to run.
+ * @param pfnThreadFunc Pointer to thread function to use.
+ * @param pvUser Pointer to user-provided data. Optional.
+ */
+int ShClTransferRun(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfnThreadFunc, VERR_INVALID_POINTER);
+ /* pvUser is optional. */
+
+ AssertMsgReturn(pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_STARTED,
+ ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
+ VERR_WRONG_ORDER);
+
+ int rc = shClTransferThreadCreate(pTransfer, pfnThreadFunc, pvUser);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Starts an initialized transfer.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Clipboard transfer to start.
+ */
+int ShClTransferStart(PSHCLTRANSFER pTransfer)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+
+ LogFlowFuncEnter();
+
+ /* Ready to start? */
+ AssertMsgReturn(pTransfer->State.enmStatus == SHCLTRANSFERSTATUS_INITIALIZED,
+ ("Wrong status (currently is %s)\n", ShClTransferStatusToStr(pTransfer->State.enmStatus)),
+ VERR_WRONG_ORDER);
+
+ int rc;
+
+ if (pTransfer->Callbacks.pfnOnStart)
+ {
+ rc = pTransfer->Callbacks.pfnOnStart(&pTransfer->CallbackCtx);
+ }
+ else
+ rc = VINF_SUCCESS;
+
+ if (RT_SUCCESS(rc))
+ {
+ pTransfer->State.enmStatus = SHCLTRANSFERSTATUS_STARTED;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Creates a thread for a Shared Clipboard transfer.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Clipboard transfer to create thread for.
+ * @param pfnThreadFunc Thread function to use for this transfer.
+ * @param pvUser Pointer to user-provided data.
+ */
+static int shClTransferThreadCreate(PSHCLTRANSFER pTransfer, PFNRTTHREAD pfnThreadFunc, void *pvUser)
+
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+
+ /* Already marked for stopping? */
+ AssertMsgReturn(pTransfer->Thread.fStop == false,
+ ("Transfer thread already marked for stopping"), VERR_WRONG_ORDER);
+ /* Already started? */
+ AssertMsgReturn(pTransfer->Thread.fStarted == false,
+ ("Transfer thread already started"), VERR_WRONG_ORDER);
+
+ /* Spawn a worker thread, so that we don't block the window thread for too long. */
+ int rc = RTThreadCreate(&pTransfer->Thread.hThread, pfnThreadFunc,
+ pvUser, 0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE,
+ "shclp");
+ if (RT_SUCCESS(rc))
+ {
+ int rc2 = RTThreadUserWait(pTransfer->Thread.hThread, 30 * 1000 /* Timeout in ms */);
+ AssertRC(rc2);
+
+ if (pTransfer->Thread.fStarted) /* Did the thread indicate that it started correctly? */
+ {
+ /* Nothing to do in here. */
+ }
+ else
+ rc = VERR_GENERAL_FAILURE; /** @todo Find a better rc. */
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Destroys a thread of a Shared Clipboard transfer.
+ *
+ * @returns VBox status code.
+ * @param pTransfer Clipboard transfer to destroy thread for.
+ * @param uTimeoutMs Timeout (in ms) to wait for thread creation.
+ */
+static int shClTransferThreadDestroy(PSHCLTRANSFER pTransfer, RTMSINTERVAL uTimeoutMs)
+{
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+
+ if (pTransfer->Thread.hThread == NIL_RTTHREAD)
+ return VINF_SUCCESS;
+
+ LogFlowFuncEnter();
+
+ /* Set stop indicator. */
+ pTransfer->Thread.fStop = true;
+
+ int rcThread = VERR_WRONG_ORDER;
+ int rc = RTThreadWait(pTransfer->Thread.hThread, uTimeoutMs, &rcThread);
+
+ LogFlowFunc(("Waiting for thread resulted in %Rrc (thread exited with %Rrc)\n", rc, rcThread));
+
+ return rc;
+}
+
+/**
+ * Initializes a Shared Clipboard transfer context.
+ *
+ * @returns VBox status code.
+ * @param pTransferCtx Transfer context to initialize.
+ */
+int ShClTransferCtxInit(PSHCLTRANSFERCTX pTransferCtx)
+{
+ AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
+
+ LogFlowFunc(("pTransferCtx=%p\n", pTransferCtx));
+
+ int rc = RTCritSectInit(&pTransferCtx->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ RTListInit(&pTransferCtx->List);
+
+ pTransferCtx->cTransfers = 0;
+ pTransferCtx->cRunning = 0;
+ pTransferCtx->cMaxRunning = 64; /** @todo Make this configurable? */
+
+ RT_ZERO(pTransferCtx->bmTransferIds);
+
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
+ ShClTransferHttpServerInit(&pTransferCtx->HttpServer);
+#endif
+ ShClTransferCtxReset(pTransferCtx);
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Destroys a Shared Clipboard transfer context struct.
+ *
+ * @param pTransferCtx Transfer context to destroy.
+ */
+void ShClTransferCtxDestroy(PSHCLTRANSFERCTX pTransferCtx)
+{
+ if (!pTransferCtx)
+ return;
+
+ LogFlowFunc(("pTransferCtx=%p\n", pTransferCtx));
+
+ if (RTCritSectIsInitialized(&pTransferCtx->CritSect))
+ RTCritSectDelete(&pTransferCtx->CritSect);
+
+ PSHCLTRANSFER pTransfer, pTransferNext;
+ RTListForEachSafe(&pTransferCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
+ {
+ ShClTransferDestroy(pTransfer);
+
+ shclTransferCtxTransferRemoveAndUnregister(pTransferCtx, pTransfer);
+
+ RTMemFree(pTransfer);
+ pTransfer = NULL;
+ }
+
+ pTransferCtx->cRunning = 0;
+ pTransferCtx->cTransfers = 0;
+}
+
+/**
+ * Resets a Shared Clipboard transfer.
+ *
+ * @param pTransferCtx Transfer context to reset.
+ */
+void ShClTransferCtxReset(PSHCLTRANSFERCTX pTransferCtx)
+{
+ AssertPtrReturnVoid(pTransferCtx);
+
+ LogFlowFuncEnter();
+
+ PSHCLTRANSFER pTransfer;
+ RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node)
+ ShClTransferReset(pTransfer);
+
+#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS_HTTP
+ /** @todo Anything to do here? */
+#endif
+}
+
+/**
+ * Returns a specific Shared Clipboard transfer, internal version.
+ *
+ * @returns Shared Clipboard transfer, or NULL if not found.
+ * @param pTransferCtx Transfer context to return transfer for.
+ * @param uID ID of the transfer to return.
+ */
+static PSHCLTRANSFER shClTransferCtxGetTransferByIdInternal(PSHCLTRANSFERCTX pTransferCtx, uint32_t uID)
+{
+ PSHCLTRANSFER pTransfer;
+ RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node) /** @todo Slow, but works for now. */
+ {
+ if (pTransfer->State.uID == uID)
+ return pTransfer;
+ }
+
+ return NULL;
+}
+
+/**
+ * Returns a specific Shared Clipboard transfer by index, internal version.
+ *
+ * @returns Shared Clipboard transfer, or NULL if not found.
+ * @param pTransferCtx Transfer context to return transfer for.
+ * @param uIdx Index of the transfer to return.
+ */
+static PSHCLTRANSFER shClTransferCtxGetTransferByIndexInternal(PSHCLTRANSFERCTX pTransferCtx, uint32_t uIdx)
+{
+ uint32_t idx = 0;
+
+ PSHCLTRANSFER pTransfer;
+ RTListForEach(&pTransferCtx->List, pTransfer, SHCLTRANSFER, Node) /** @todo Slow, but works for now. */
+ {
+ if (uIdx == idx)
+ return pTransfer;
+ idx++;
+ }
+
+ return NULL;
+}
+
+/**
+ * Returns a Shared Clipboard transfer for a specific transfer ID.
+ *
+ * @returns Shared Clipboard transfer, or NULL if not found.
+ * @param pTransferCtx Transfer context to return transfer for.
+ * @param uID ID of the transfer to return.
+ */
+PSHCLTRANSFER ShClTransferCtxGetTransferById(PSHCLTRANSFERCTX pTransferCtx, uint32_t uID)
+{
+ return shClTransferCtxGetTransferByIdInternal(pTransferCtx, uID);
+}
+
+/**
+ * Returns a Shared Clipboard transfer for a specific list index.
+ *
+ * @returns Shared Clipboard transfer, or NULL if not found.
+ * @param pTransferCtx Transfer context to return transfer for.
+ * @param uIdx List index of the transfer to return.
+ */
+PSHCLTRANSFER ShClTransferCtxGetTransferByIndex(PSHCLTRANSFERCTX pTransferCtx, uint32_t uIdx)
+{
+ return shClTransferCtxGetTransferByIndexInternal(pTransferCtx, uIdx);
+}
+
+/**
+ * Returns the number of running Shared Clipboard transfers.
+ *
+ * @returns Number of running transfers.
+ * @param pTransferCtx Transfer context to return number for.
+ */
+uint32_t ShClTransferCtxGetRunningTransfers(PSHCLTRANSFERCTX pTransferCtx)
+{
+ AssertPtrReturn(pTransferCtx, 0);
+ return pTransferCtx->cRunning;
+}
+
+/**
+ * Returns the number of total Shared Clipboard transfers.
+ *
+ * @returns Number of total transfers.
+ * @param pTransferCtx Transfer context to return number for.
+ */
+uint32_t ShClTransferCtxGetTotalTransfers(PSHCLTRANSFERCTX pTransferCtx)
+{
+ AssertPtrReturn(pTransferCtx, 0);
+ return pTransferCtx->cTransfers;
+}
+
+/**
+ * Registers a Shared Clipboard transfer with a transfer context, i.e. allocates a transfer ID.
+ *
+ * @return VBox status code.
+ * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers
+ * is reached.
+ * @param pTransferCtx Transfer context to register transfer to.
+ * @param pTransfer Transfer to register.
+ * @param pidTransfer Where to return the transfer ID on success. Optional.
+ */
+int ShClTransferCtxTransferRegister(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, SHCLTRANSFERID *pidTransfer)
+{
+ AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER);
+ AssertPtrReturn(pTransfer, VERR_INVALID_POINTER);
+ /* pidTransfer is optional. */
+
+ /*
+ * Pick a random bit as starting point. If it's in use, search forward
+ * for a free one, wrapping around. We've reserved both the zero'th and
+ * max-1 IDs.
+ */
+ SHCLTRANSFERID idTransfer = RTRandU32Ex(1, VBOX_SHCL_MAX_TRANSFERS - 2);
+
+ if (!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer))
+ { /* likely */ }
+ else if (pTransferCtx->cTransfers < VBOX_SHCL_MAX_TRANSFERS - 2 /* First and last are not used */)
+ {
+ /* Forward search. */
+ int iHit = ASMBitNextClear(&pTransferCtx->bmTransferIds[0], VBOX_SHCL_MAX_TRANSFERS, idTransfer);
+ if (iHit < 0)
+ iHit = ASMBitFirstClear(&pTransferCtx->bmTransferIds[0], VBOX_SHCL_MAX_TRANSFERS);
+ AssertLogRelMsgReturn(iHit >= 0, ("Transfer count: %RU16\n", pTransferCtx->cTransfers), VERR_SHCLPB_MAX_TRANSFERS_REACHED);
+ idTransfer = iHit;
+ AssertLogRelMsgReturn(!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer), ("idObject=%#x\n", idTransfer), VERR_INTERNAL_ERROR_2);
+ }
+ else
+ {
+ LogFunc(("Maximum number of transfers reached (%RU16 transfers)\n", pTransferCtx->cTransfers));
+ return VERR_SHCLPB_MAX_TRANSFERS_REACHED;
+ }
+
+ Log2Func(("pTransfer=%p, idTransfer=%RU32 (%RU16 transfers)\n", pTransfer, idTransfer, pTransferCtx->cTransfers));
+
+ pTransfer->State.uID = idTransfer;
+
+ RTListAppend(&pTransferCtx->List, &pTransfer->Node);
+
+ pTransferCtx->cTransfers++;
+
+ if (pTransfer->Callbacks.pfnOnRegistered)
+ pTransfer->Callbacks.pfnOnRegistered(&pTransfer->CallbackCtx, pTransferCtx);
+
+ if (pidTransfer)
+ *pidTransfer = idTransfer;
+
+ LogFlowFuncLeaveRC(VINF_SUCCESS);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Registers a Shared Clipboard transfer with a transfer context by specifying an ID for the transfer.
+ *
+ * @return VBox status code.
+ * @retval VERR_ALREADY_EXISTS if a transfer with the given ID already exists.
+ * @retval VERR_SHCLPB_MAX_TRANSFERS_REACHED if the maximum of concurrent transfers for this context has been reached.
+ * @param pTransferCtx Transfer context to register transfer to.
+ * @param pTransfer Transfer to register.
+ * @param idTransfer Transfer ID to use for registration.
+ */
+int ShClTransferCtxTransferRegisterById(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer, SHCLTRANSFERID idTransfer)
+{
+ LogFlowFunc(("cTransfers=%RU16, idTransfer=%RU32\n", pTransferCtx->cTransfers, idTransfer));
+
+ if (pTransferCtx->cTransfers < VBOX_SHCL_MAX_TRANSFERS - 2 /* First and last are not used */)
+ {
+ if (!ASMBitTestAndSet(&pTransferCtx->bmTransferIds[0], idTransfer))
+ {
+ RTListAppend(&pTransferCtx->List, &pTransfer->Node);
+
+ pTransfer->State.uID = idTransfer;
+
+ if (pTransfer->Callbacks.pfnOnRegistered)
+ pTransfer->Callbacks.pfnOnRegistered(&pTransfer->CallbackCtx, pTransferCtx);
+
+ pTransferCtx->cTransfers++;
+ return VINF_SUCCESS;
+ }
+
+ return VERR_ALREADY_EXISTS;
+ }
+
+ LogFunc(("Maximum number of transfers reached (%RU16 transfers)\n", pTransferCtx->cTransfers));
+ return VERR_SHCLPB_MAX_TRANSFERS_REACHED;
+}
+
+/**
+ * Removes and unregisters a transfer from a transfer context.
+ *
+ * @param pTransferCtx Transfer context to remove transfer from.
+ * @param pTransfer Transfer to remove.
+ */
+static void shclTransferCtxTransferRemoveAndUnregister(PSHCLTRANSFERCTX pTransferCtx, PSHCLTRANSFER pTransfer)
+{
+ RTListNodeRemove(&pTransfer->Node);
+
+ Assert(pTransferCtx->cTransfers);
+ pTransferCtx->cTransfers--;
+
+ Assert(pTransferCtx->cTransfers >= pTransferCtx->cRunning);
+
+ if (pTransfer->Callbacks.pfnOnUnregistered)
+ pTransfer->Callbacks.pfnOnUnregistered(&pTransfer->CallbackCtx, pTransferCtx);
+
+ LogFlowFunc(("Now %RU32 transfers left\n", pTransferCtx->cTransfers));
+}
+
+/**
+ * Unregisters a transfer from an Transfer context.
+ *
+ * @retval VINF_SUCCESS on success.
+ * @retval VERR_NOT_FOUND if the transfer ID was not found.
+ * @param pTransferCtx Transfer context to unregister transfer from.
+ * @param idTransfer Transfer ID to unregister.
+ */
+int ShClTransferCtxTransferUnregister(PSHCLTRANSFERCTX pTransferCtx, SHCLTRANSFERID idTransfer)
+{
+ int rc = VINF_SUCCESS;
+ AssertMsgStmt(ASMBitTestAndClear(&pTransferCtx->bmTransferIds, idTransfer), ("idTransfer=%#x\n", idTransfer), rc = VERR_NOT_FOUND);
+
+ LogFlowFunc(("idTransfer=%RU32\n", idTransfer));
+
+ PSHCLTRANSFER pTransfer = shClTransferCtxGetTransferByIdInternal(pTransferCtx, idTransfer);
+ if (pTransfer)
+ {
+ shclTransferCtxTransferRemoveAndUnregister(pTransferCtx, pTransfer);
+ }
+ else
+ rc = VERR_NOT_FOUND;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/**
+ * Cleans up all associated transfers which are not needed (anymore).
+ * This can be due to transfers which only have been announced but not / never being run.
+ *
+ * @param pTransferCtx Transfer context to cleanup transfers for.
+ */
+void ShClTransferCtxCleanup(PSHCLTRANSFERCTX pTransferCtx)
+{
+ AssertPtrReturnVoid(pTransferCtx);
+
+ LogFlowFunc(("pTransferCtx=%p, cTransfers=%RU16 cRunning=%RU16\n",
+ pTransferCtx, pTransferCtx->cTransfers, pTransferCtx->cRunning));
+
+ if (pTransferCtx->cTransfers == 0)
+ return;
+
+ /* Remove all transfers which are not in a running state (e.g. only announced). */
+ PSHCLTRANSFER pTransfer, pTransferNext;
+ RTListForEachSafe(&pTransferCtx->List, pTransfer, pTransferNext, SHCLTRANSFER, Node)
+ {
+ if (ShClTransferGetStatus(pTransfer) != SHCLTRANSFERSTATUS_STARTED)
+ {
+ shclTransferCtxTransferRemoveAndUnregister(pTransferCtx, pTransfer);
+
+ ShClTransferDestroy(pTransfer);
+
+ RTMemFree(pTransfer);
+ pTransfer = NULL;
+ }
+ }
+}
+
+/**
+ * Returns whether the maximum of concurrent transfers of a specific transfer contexthas been reached or not.
+ *
+ * @returns \c if maximum has been reached, \c false if not.
+ * @param pTransferCtx Transfer context to determine value for.
+ */
+bool ShClTransferCtxTransfersMaximumReached(PSHCLTRANSFERCTX pTransferCtx)
+{
+ AssertPtrReturn(pTransferCtx, true);
+
+ LogFlowFunc(("cRunning=%RU32, cMaxRunning=%RU32\n", pTransferCtx->cRunning, pTransferCtx->cMaxRunning));
+
+ Assert(pTransferCtx->cRunning <= pTransferCtx->cMaxRunning);
+ return pTransferCtx->cRunning == pTransferCtx->cMaxRunning;
+}
+
+/**
+ * Copies file system objinfo from IPRT to Shared Clipboard format.
+ *
+ * @param pDst The Shared Clipboard structure to convert data to.
+ * @param pSrc The IPRT structure to convert data from.
+ */
+void ShClFsObjFromIPRT(PSHCLFSOBJINFO pDst, PCRTFSOBJINFO pSrc)
+{
+ pDst->cbObject = pSrc->cbObject;
+ pDst->cbAllocated = pSrc->cbAllocated;
+ pDst->AccessTime = pSrc->AccessTime;
+ pDst->ModificationTime = pSrc->ModificationTime;
+ pDst->ChangeTime = pSrc->ChangeTime;
+ pDst->BirthTime = pSrc->BirthTime;
+ pDst->Attr.fMode = pSrc->Attr.fMode;
+ /* Clear bits which we don't pass through for security reasons. */
+ pDst->Attr.fMode &= ~(RTFS_UNIX_ISUID | RTFS_UNIX_ISGID | RTFS_UNIX_ISTXT);
+ RT_ZERO(pDst->Attr.u);
+ switch (pSrc->Attr.enmAdditional)
+ {
+ default:
+ case RTFSOBJATTRADD_NOTHING:
+ pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_NOTHING;
+ break;
+
+ case RTFSOBJATTRADD_UNIX:
+ pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_UNIX;
+ pDst->Attr.u.Unix.uid = pSrc->Attr.u.Unix.uid;
+ pDst->Attr.u.Unix.gid = pSrc->Attr.u.Unix.gid;
+ pDst->Attr.u.Unix.cHardlinks = pSrc->Attr.u.Unix.cHardlinks;
+ pDst->Attr.u.Unix.INodeIdDevice = pSrc->Attr.u.Unix.INodeIdDevice;
+ pDst->Attr.u.Unix.INodeId = pSrc->Attr.u.Unix.INodeId;
+ pDst->Attr.u.Unix.fFlags = pSrc->Attr.u.Unix.fFlags;
+ pDst->Attr.u.Unix.GenerationId = pSrc->Attr.u.Unix.GenerationId;
+ pDst->Attr.u.Unix.Device = pSrc->Attr.u.Unix.Device;
+ break;
+
+ case RTFSOBJATTRADD_EASIZE:
+ pDst->Attr.enmAdditional = SHCLFSOBJATTRADD_EASIZE;
+ pDst->Attr.u.EASize.cb = pSrc->Attr.u.EASize.cb;
+ break;
+ }
+}
+
+/**
+ * Converts Shared Clipboard create flags (see SharedClipboard-transfers.h) into IPRT create flags.
+ *
+ * @returns IPRT status code.
+ * @param fShClFlags Shared clipboard create flags.
+ * @param[out] pfOpen Where to store the RTFILE_O_XXX flags for
+ * RTFileOpen.
+ *
+ * @sa Initially taken from vbsfConvertFileOpenFlags().
+ */
+static int shClConvertFileCreateFlags(uint32_t fShClFlags, uint64_t *pfOpen)
+{
+ AssertMsgReturnStmt(!(fShClFlags & ~SHCL_OBJ_CF_VALID_MASK), ("%#x4\n", fShClFlags), *pfOpen = 0, VERR_INVALID_FLAGS);
+
+ uint64_t fOpen = 0;
+
+ switch (fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_RW)
+ {
+ case SHCL_OBJ_CF_ACCESS_NONE:
+ {
+#ifdef RT_OS_WINDOWS
+ if ((fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_ATTR) != SHCL_OBJ_CF_ACCESS_ATTR_NONE)
+ fOpen |= RTFILE_O_OPEN | RTFILE_O_ATTR_ONLY;
+ else
+#endif
+ fOpen |= RTFILE_O_OPEN | RTFILE_O_READ;
+ LogFlowFunc(("SHCL_OBJ_CF_ACCESS_NONE\n"));
+ break;
+ }
+
+ case SHCL_OBJ_CF_ACCESS_READ:
+ {
+ fOpen |= RTFILE_O_OPEN | RTFILE_O_READ;
+ LogFlowFunc(("SHCL_OBJ_CF_ACCESS_READ\n"));
+ break;
+ }
+
+ default:
+ AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE);
+ }
+
+ switch (fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_ATTR)
+ {
+ case SHCL_OBJ_CF_ACCESS_ATTR_NONE:
+ {
+ fOpen |= RTFILE_O_ACCESS_ATTR_DEFAULT;
+ LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_NONE\n"));
+ break;
+ }
+
+ case SHCL_OBJ_CF_ACCESS_ATTR_READ:
+ {
+ fOpen |= RTFILE_O_ACCESS_ATTR_READ;
+ LogFlowFunc(("SHCL_OBJ_CF_ACCESS_ATTR_READ\n"));
+ break;
+ }
+
+ default:
+ AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE);
+ }
+
+ /* Sharing mask */
+ switch (fShClFlags & SHCL_OBJ_CF_ACCESS_MASK_DENY)
+ {
+ case SHCL_OBJ_CF_ACCESS_DENYNONE:
+ fOpen |= RTFILE_O_DENY_NONE;
+ LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYNONE\n"));
+ break;
+
+ case SHCL_OBJ_CF_ACCESS_DENYWRITE:
+ fOpen |= RTFILE_O_DENY_WRITE;
+ LogFlowFunc(("SHCL_OBJ_CF_ACCESS_DENYWRITE\n"));
+ break;
+
+ default:
+ AssertFailedReturn(VERR_IPE_NOT_REACHED_DEFAULT_CASE);
+ }
+
+ *pfOpen = fOpen;
+
+ LogFlowFuncLeaveRC(VINF_SUCCESS);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Translates a Shared Clipboard transfer status (SHCLTRANSFERSTATUS_XXX) into a string.
+ *
+ * @returns Transfer status string name.
+ * @param enmStatus The transfer status to translate.
+ */
+const char *ShClTransferStatusToStr(SHCLTRANSFERSTATUS enmStatus)
+{
+ switch (enmStatus)
+ {
+ RT_CASE_RET_STR(SHCLTRANSFERSTATUS_NONE);
+ RT_CASE_RET_STR(SHCLTRANSFERSTATUS_INITIALIZED);
+ RT_CASE_RET_STR(SHCLTRANSFERSTATUS_STARTED);
+ RT_CASE_RET_STR(SHCLTRANSFERSTATUS_STOPPED);
+ RT_CASE_RET_STR(SHCLTRANSFERSTATUS_CANCELED);
+ RT_CASE_RET_STR(SHCLTRANSFERSTATUS_KILLED);
+ RT_CASE_RET_STR(SHCLTRANSFERSTATUS_ERROR);
+ }
+ return "Unknown";
+}