diff options
Diffstat (limited to 'src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp')
-rw-r--r-- | src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp | 2067 |
1 files changed, 2067 insertions, 0 deletions
diff --git a/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp b/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp new file mode 100644 index 00000000..3f36f170 --- /dev/null +++ b/src/VBox/HostServices/SharedClipboard/VBoxSharedClipboardSvc-transfers.cpp @@ -0,0 +1,2067 @@ +/* $Id: VBoxSharedClipboardSvc-transfers.cpp $ */ +/** @file + * Shared Clipboard Service - Internal code for transfer (list) handling. + */ + +/* + * 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 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_SHARED_CLIPBOARD +#include <VBox/log.h> + +#include <VBox/err.h> + +#include <VBox/GuestHost/clipboard-helper.h> +#include <VBox/HostServices/VBoxClipboardSvc.h> +#include <VBox/HostServices/VBoxClipboardExt.h> + +#include <VBox/AssertGuest.h> +#include <iprt/dir.h> +#include <iprt/file.h> +#include <iprt/path.h> + +#include "VBoxSharedClipboardSvc-internal.h" +#include "VBoxSharedClipboardSvc-transfers.h" + + +/********************************************************************************************************************************* +* Externals * +*********************************************************************************************************************************/ +extern uint32_t g_fTransferMode; +extern SHCLEXTSTATE g_ExtState; +extern PVBOXHGCMSVCHELPERS g_pHelpers; +extern ClipboardClientMap g_mapClients; +extern ClipboardClientQueue g_listClientsDeferred; + + +/********************************************************************************************************************************* +* Prototypes * +*********************************************************************************************************************************/ +static int shClSvcTransferSetListOpen(uint32_t cParms, VBOXHGCMSVCPARM aParms[], + uint64_t idCtx, PSHCLLISTOPENPARMS pOpenParms); +static int shClSvcTransferSetListClose(uint32_t cParms, VBOXHGCMSVCPARM aParms[], + uint64_t idCtx, SHCLLISTHANDLE hList); + + +/********************************************************************************************************************************* +* Provider implementation * +*********************************************************************************************************************************/ + +/** + * Resets all transfers of a Shared Clipboard client. + * + * @param pClient Client to reset transfers for. + */ +void shClSvcClientTransfersReset(PSHCLCLIENT pClient) +{ + if (!pClient) + return; + + LogFlowFuncEnter(); + + /* Make sure to let the backend know that all transfers are getting destroyed. */ + uint32_t uIdx = 0; + PSHCLTRANSFER pTransfer; + while ((pTransfer = ShClTransferCtxGetTransferByIndex(&pClient->Transfers.Ctx, uIdx++))) + ShClBackendTransferDestroy(pClient->pBackend, pClient, pTransfer); + + ShClTransferCtxDestroy(&pClient->Transfers.Ctx); +} + + +/********************************************************************************************************************************* +* Provider interface implementation * +*********************************************************************************************************************************/ + +DECLCALLBACK(int) shClSvcTransferIfaceGetRoots(PSHCLTXPROVIDERCTX pCtx, PSHCLROOTLIST *ppRootList) +{ + LogFlowFuncEnter(); + + PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser; + AssertPtr(pClient); + + int rc; + + PSHCLCLIENTMSG pMsgHdr = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_READ, + VBOX_SHCL_CPARMS_ROOT_LIST_HDR_READ_REQ); + if (pMsgHdr) + { + PSHCLEVENT pEvent; + rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent); + if (RT_SUCCESS(rc)) + { + HGCMSvcSetU64(&pMsgHdr->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, + pCtx->pTransfer->State.uID, pEvent->idEvent)); + HGCMSvcSetU32(&pMsgHdr->aParms[1], 0 /* fRoots */); + + shClSvcClientLock(pClient); + + shClSvcMsgAdd(pClient, pMsgHdr, true /* fAppend */); + rc = shClSvcClientWakeup(pClient); + + shClSvcClientUnlock(pClient); + + if (RT_SUCCESS(rc)) + { + PSHCLEVENTPAYLOAD pPayloadHdr; + rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayloadHdr); + if (RT_SUCCESS(rc)) + { + PSHCLROOTLISTHDR pSrcRootListHdr = (PSHCLROOTLISTHDR)pPayloadHdr->pvData; + Assert(pPayloadHdr->cbData == sizeof(SHCLROOTLISTHDR)); + + LogFlowFunc(("cRoots=%RU32, fRoots=0x%x\n", pSrcRootListHdr->cRoots, pSrcRootListHdr->fRoots)); + + PSHCLROOTLIST pRootList = ShClTransferRootListAlloc(); + if (pRootList) + { + if (pSrcRootListHdr->cRoots) + { + pRootList->paEntries = + (PSHCLROOTLISTENTRY)RTMemAllocZ(pSrcRootListHdr->cRoots * sizeof(SHCLROOTLISTENTRY)); + + if (pRootList->paEntries) + { + for (uint32_t i = 0; i < pSrcRootListHdr->cRoots; i++) + { + PSHCLCLIENTMSG pMsgEntry = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_READ, + VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_READ_REQ); + + PSHCLEVENT pEvRoot; + rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvRoot); + if (RT_SUCCESS(rc)) + { + HGCMSvcSetU64(&pMsgEntry->aParms[0], + VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uClientID, + pCtx->pTransfer->State.uID, pEvRoot->idEvent)); + HGCMSvcSetU32(&pMsgEntry->aParms[1], 0 /* fRoots */); + HGCMSvcSetU32(&pMsgEntry->aParms[2], i /* uIndex */); + + shClSvcClientLock(pClient); + shClSvcMsgAdd(pClient, pMsgEntry, true /* fAppend */); + shClSvcClientUnlock(pClient); + + PSHCLEVENTPAYLOAD pPayloadEntry; + rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayloadEntry); + if (RT_FAILURE(rc)) + break; + + PSHCLROOTLISTENTRY pSrcRootListEntry = (PSHCLROOTLISTENTRY)pPayloadEntry->pvData; + Assert(pPayloadEntry->cbData == sizeof(SHCLROOTLISTENTRY)); + + rc = ShClTransferListEntryCopy(&pRootList->paEntries[i], pSrcRootListEntry); + + ShClPayloadFree(pPayloadEntry); + + ShClEventRelease(pEvRoot); + pEvRoot = NULL; + } + else + rc = VERR_SHCLPB_MAX_EVENTS_REACHED; + + if (RT_FAILURE(rc)) + break; + } + } + else + rc = VERR_NO_MEMORY; + } + + if (RT_SUCCESS(rc)) + { + pRootList->Hdr.cRoots = pSrcRootListHdr->cRoots; + pRootList->Hdr.fRoots = 0; /** @todo Implement this. */ + + *ppRootList = pRootList; + } + else + ShClTransferRootListFree(pRootList); + + ShClPayloadFree(pPayloadHdr); + } + else + rc = VERR_NO_MEMORY; + } + } + + ShClEventRelease(pEvent); + } + else + rc = VERR_SHCLPB_MAX_EVENTS_REACHED; + } + else + rc = VERR_NO_MEMORY; + + LogFlowFuncLeave(); + return rc; +} + +DECLCALLBACK(int) shClSvcTransferIfaceListOpen(PSHCLTXPROVIDERCTX pCtx, + PSHCLLISTOPENPARMS pOpenParms, PSHCLLISTHANDLE phList) +{ + LogFlowFuncEnter(); + + PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser; + AssertPtr(pClient); + + int rc; + + PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_LIST_OPEN, + VBOX_SHCL_CPARMS_LIST_OPEN); + if (pMsg) + { + PSHCLEVENT pEvent; + rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent); + if (RT_SUCCESS(rc)) + { + pMsg->idCtx = VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, pCtx->pTransfer->State.uID, + pEvent->idEvent); + + rc = shClSvcTransferSetListOpen(pMsg->cParms, pMsg->aParms, pMsg->idCtx, pOpenParms); + if (RT_SUCCESS(rc)) + { + shClSvcMsgAdd(pClient, pMsg, true /* fAppend */); + + rc = shClSvcClientWakeup(pClient); + if (RT_SUCCESS(rc)) + { + PSHCLEVENTPAYLOAD pPayload; + rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload); + if (RT_SUCCESS(rc)) + { + Assert(pPayload->cbData == sizeof(SHCLREPLY)); + + PSHCLREPLY pReply = (PSHCLREPLY)pPayload->pvData; + AssertPtr(pReply); + + Assert(pReply->uType == VBOX_SHCL_REPLYMSGTYPE_LIST_OPEN); + + LogFlowFunc(("hList=%RU64\n", pReply->u.ListOpen.uHandle)); + + *phList = pReply->u.ListOpen.uHandle; + + ShClPayloadFree(pPayload); + } + } + } + + ShClEventRelease(pEvent); + } + else + rc = VERR_SHCLPB_MAX_EVENTS_REACHED; + } + else + rc = VERR_NO_MEMORY; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +DECLCALLBACK(int) shClSvcTransferIfaceListClose(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList) +{ + LogFlowFuncEnter(); + + PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser; + AssertPtr(pClient); + + int rc; + + PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_LIST_CLOSE, + VBOX_SHCL_CPARMS_LIST_CLOSE); + if (pMsg) + { + PSHCLEVENT pEvent; + rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent); + if (RT_SUCCESS(rc)) + { + pMsg->idCtx = VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, pCtx->pTransfer->State.uID, + pEvent->idEvent); + + rc = shClSvcTransferSetListClose(pMsg->cParms, pMsg->aParms, pMsg->idCtx, hList); + if (RT_SUCCESS(rc)) + { + shClSvcMsgAdd(pClient, pMsg, true /* fAppend */); + + rc = shClSvcClientWakeup(pClient); + if (RT_SUCCESS(rc)) + { + PSHCLEVENTPAYLOAD pPayload; + rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload); + if (RT_SUCCESS(rc)) + ShClPayloadFree(pPayload); + } + } + + ShClEventRelease(pEvent); + } + else + rc = VERR_SHCLPB_MAX_EVENTS_REACHED; + } + else + rc = VERR_NO_MEMORY; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +DECLCALLBACK(int) shClSvcTransferIfaceListHdrRead(PSHCLTXPROVIDERCTX pCtx, + SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr) +{ + LogFlowFuncEnter(); + + PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser; + AssertPtr(pClient); + + int rc; + + PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_READ, + VBOX_SHCL_CPARMS_LIST_HDR_READ_REQ); + if (pMsg) + { + PSHCLEVENT pEvent; + rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent); + if (RT_SUCCESS(rc)) + { + HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, + pCtx->pTransfer->State.uID, pEvent->idEvent)); + HGCMSvcSetU64(&pMsg->aParms[1], hList); + HGCMSvcSetU32(&pMsg->aParms[2], 0 /* fFlags */); + + shClSvcMsgAdd(pClient, pMsg, true /* fAppend */); + + rc = shClSvcClientWakeup(pClient); + if (RT_SUCCESS(rc)) + { + PSHCLEVENTPAYLOAD pPayload; + rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload); + if (RT_SUCCESS(rc)) + { + Assert(pPayload->cbData == sizeof(SHCLLISTHDR)); + + *pListHdr = *(PSHCLLISTHDR)pPayload->pvData; + + ShClPayloadFree(pPayload); + } + } + + ShClEventRelease(pEvent); + } + else + rc = VERR_SHCLPB_MAX_EVENTS_REACHED; + } + else + rc = VERR_NO_MEMORY; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +DECLCALLBACK(int) shClSvcTransferIfaceListHdrWrite(PSHCLTXPROVIDERCTX pCtx, + SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr) +{ + RT_NOREF(pCtx, hList, pListHdr); + + LogFlowFuncEnter(); + + return VERR_NOT_IMPLEMENTED; +} + +DECLCALLBACK(int) shClSvcTransferIfaceListEntryRead(PSHCLTXPROVIDERCTX pCtx, + SHCLLISTHANDLE hList, PSHCLLISTENTRY pListEntry) +{ + LogFlowFuncEnter(); + + PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser; + AssertPtr(pClient); + + int rc; + + PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ, + VBOX_SHCL_CPARMS_LIST_ENTRY_READ); + if (pMsg) + { + PSHCLEVENT pEvent; + rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent); + if (RT_SUCCESS(rc)) + { + HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, + pCtx->pTransfer->State.uID, pEvent->idEvent)); + HGCMSvcSetU64(&pMsg->aParms[1], hList); + HGCMSvcSetU32(&pMsg->aParms[2], 0 /* fInfo */); + + shClSvcMsgAdd(pClient, pMsg, true /* fAppend */); + + rc = shClSvcClientWakeup(pClient); + if (RT_SUCCESS(rc)) + { + PSHCLEVENTPAYLOAD pPayload; + rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload); + if (RT_SUCCESS(rc)) + { + Assert(pPayload->cbData == sizeof(SHCLLISTENTRY)); + + rc = ShClTransferListEntryCopy(pListEntry, (PSHCLLISTENTRY)pPayload->pvData); + + ShClPayloadFree(pPayload); + } + } + + ShClEventRelease(pEvent); + } + else + rc = VERR_SHCLPB_MAX_EVENTS_REACHED; + } + else + rc = VERR_NO_MEMORY; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +DECLCALLBACK(int) shClSvcTransferIfaceListEntryWrite(PSHCLTXPROVIDERCTX pCtx, + SHCLLISTHANDLE hList, PSHCLLISTENTRY pListEntry) +{ + RT_NOREF(pCtx, hList, pListEntry); + + LogFlowFuncEnter(); + + return VERR_NOT_IMPLEMENTED; +} + +int shClSvcTransferIfaceObjOpen(PSHCLTXPROVIDERCTX pCtx, PSHCLOBJOPENCREATEPARMS pCreateParms, + PSHCLOBJHANDLE phObj) +{ + LogFlowFuncEnter(); + + PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser; + AssertPtr(pClient); + + int rc; + + PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_OPEN, + VBOX_SHCL_CPARMS_OBJ_OPEN); + if (pMsg) + { + PSHCLEVENT pEvent; + rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent); + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("pszPath=%s, fCreate=0x%x\n", pCreateParms->pszPath, pCreateParms->fCreate)); + + const uint32_t cbPath = (uint32_t)strlen(pCreateParms->pszPath) + 1; /* Include terminating zero */ + + HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, + pCtx->pTransfer->State.uID, pEvent->idEvent)); + HGCMSvcSetU64(&pMsg->aParms[1], 0); /* uHandle */ + HGCMSvcSetU32(&pMsg->aParms[2], cbPath); + HGCMSvcSetPv (&pMsg->aParms[3], pCreateParms->pszPath, cbPath); + HGCMSvcSetU32(&pMsg->aParms[4], pCreateParms->fCreate); + + shClSvcMsgAdd(pClient, pMsg, true /* fAppend */); + + rc = shClSvcClientWakeup(pClient); + if (RT_SUCCESS(rc)) + { + PSHCLEVENTPAYLOAD pPayload; + rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload); + if (RT_SUCCESS(rc)) + { + Assert(pPayload->cbData == sizeof(SHCLREPLY)); + + PSHCLREPLY pReply = (PSHCLREPLY)pPayload->pvData; + AssertPtr(pReply); + + Assert(pReply->uType == VBOX_SHCL_REPLYMSGTYPE_OBJ_OPEN); + + LogFlowFunc(("hObj=%RU64\n", pReply->u.ObjOpen.uHandle)); + + *phObj = pReply->u.ObjOpen.uHandle; + + ShClPayloadFree(pPayload); + } + } + + ShClEventRelease(pEvent); + } + else + rc = VERR_SHCLPB_MAX_EVENTS_REACHED; + } + else + rc = VERR_NO_MEMORY; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +int shClSvcTransferIfaceObjClose(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj) +{ + LogFlowFuncEnter(); + + PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser; + AssertPtr(pClient); + + int rc; + + PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_CLOSE, + VBOX_SHCL_CPARMS_OBJ_CLOSE); + if (pMsg) + { + PSHCLEVENT pEvent; + rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent); + if (RT_SUCCESS(rc)) + { + HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, + pCtx->pTransfer->State.uID, pEvent->idEvent)); + HGCMSvcSetU64(&pMsg->aParms[1], hObj); + + shClSvcMsgAdd(pClient, pMsg, true /* fAppend */); + + rc = shClSvcClientWakeup(pClient); + if (RT_SUCCESS(rc)) + { + PSHCLEVENTPAYLOAD pPayload; + rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload); + if (RT_SUCCESS(rc)) + { + Assert(pPayload->cbData == sizeof(SHCLREPLY)); +#ifdef VBOX_STRICT + PSHCLREPLY pReply = (PSHCLREPLY)pPayload->pvData; + AssertPtr(pReply); + + Assert(pReply->uType == VBOX_SHCL_REPLYMSGTYPE_OBJ_CLOSE); + + LogFlowFunc(("hObj=%RU64\n", pReply->u.ObjClose.uHandle)); +#endif + ShClPayloadFree(pPayload); + } + } + + ShClEventRelease(pEvent); + } + else + rc = VERR_SHCLPB_MAX_EVENTS_REACHED; + } + else + rc = VERR_NO_MEMORY; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +int shClSvcTransferIfaceObjRead(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj, + void *pvData, uint32_t cbData, uint32_t fFlags, uint32_t *pcbRead) +{ + LogFlowFuncEnter(); + + PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser; + AssertPtr(pClient); + + int rc; + + PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_READ, + VBOX_SHCL_CPARMS_OBJ_READ_REQ); + if (pMsg) + { + PSHCLEVENT pEvent; + rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent); + if (RT_SUCCESS(rc)) + { + HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, + pCtx->pTransfer->State.uID, pEvent->idEvent)); + HGCMSvcSetU64(&pMsg->aParms[1], hObj); + HGCMSvcSetU32(&pMsg->aParms[2], cbData); + HGCMSvcSetU32(&pMsg->aParms[3], fFlags); + + shClSvcMsgAdd(pClient, pMsg, true /* fAppend */); + + rc = shClSvcClientWakeup(pClient); + if (RT_SUCCESS(rc)) + { + PSHCLEVENTPAYLOAD pPayload; + rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload); + if (RT_SUCCESS(rc)) + { + Assert(pPayload->cbData == sizeof(SHCLOBJDATACHUNK)); + + PSHCLOBJDATACHUNK pDataChunk = (PSHCLOBJDATACHUNK)pPayload->pvData; + AssertPtr(pDataChunk); + + const uint32_t cbRead = RT_MIN(cbData, pDataChunk->cbData); + + memcpy(pvData, pDataChunk->pvData, cbRead); + + if (pcbRead) + *pcbRead = cbRead; + + ShClPayloadFree(pPayload); + } + } + + ShClEventRelease(pEvent); + } + else + rc = VERR_SHCLPB_MAX_EVENTS_REACHED; + } + else + rc = VERR_NO_MEMORY; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +int shClSvcTransferIfaceObjWrite(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj, + void *pvData, uint32_t cbData, uint32_t fFlags, uint32_t *pcbWritten) +{ + LogFlowFuncEnter(); + + PSHCLCLIENT pClient = (PSHCLCLIENT)pCtx->pvUser; + AssertPtr(pClient); + + int rc; + + PSHCLCLIENTMSG pMsg = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_WRITE, + VBOX_SHCL_CPARMS_OBJ_WRITE); + if (pMsg) + { + PSHCLEVENT pEvent; + rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent); + if (RT_SUCCESS(rc)) + { + HGCMSvcSetU64(&pMsg->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, + pCtx->pTransfer->State.uID, pEvent->idEvent)); + HGCMSvcSetU64(&pMsg->aParms[1], hObj); + HGCMSvcSetU64(&pMsg->aParms[2], cbData); + HGCMSvcSetU64(&pMsg->aParms[3], fFlags); + + shClSvcMsgAdd(pClient, pMsg, true /* fAppend */); + + rc = shClSvcClientWakeup(pClient); + if (RT_SUCCESS(rc)) + { + PSHCLEVENTPAYLOAD pPayload; + rc = ShClEventWait(pEvent, pCtx->pTransfer->uTimeoutMs, &pPayload); + if (RT_SUCCESS(rc)) + { + const uint32_t cbRead = RT_MIN(cbData, pPayload->cbData); + + memcpy(pvData, pPayload->pvData, cbRead); + + if (pcbWritten) + *pcbWritten = cbRead; + + ShClPayloadFree(pPayload); + } + } + + ShClEventRelease(pEvent); + } + else + rc = VERR_SHCLPB_MAX_EVENTS_REACHED; + } + else + rc = VERR_NO_MEMORY; + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/********************************************************************************************************************************* +* HGCM getters / setters * +*********************************************************************************************************************************/ + +/** + * Returns whether a HGCM message is allowed in a certain service mode or not. + * + * @returns \c true if message is allowed, \c false if not. + * @param uMode Service mode to check allowance for. + * @param uMsg HGCM message to check allowance for. + */ +bool shClSvcTransferMsgIsAllowed(uint32_t uMode, uint32_t uMsg) +{ + const bool fHostToGuest = uMode == VBOX_SHCL_MODE_HOST_TO_GUEST + || uMode == VBOX_SHCL_MODE_BIDIRECTIONAL; + + const bool fGuestToHost = uMode == VBOX_SHCL_MODE_GUEST_TO_HOST + || uMode == VBOX_SHCL_MODE_BIDIRECTIONAL; + + bool fAllowed = false; /* If in doubt, don't allow. */ + + switch (uMsg) + { + case VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_WRITE: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_WRITE: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_LIST_HDR_WRITE: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_LIST_ENTRY_WRITE: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_OBJ_WRITE: + fAllowed = fGuestToHost; + break; + + case VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_READ: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_READ: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_LIST_HDR_READ: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_LIST_ENTRY_READ: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_OBJ_READ: + fAllowed = fHostToGuest; + break; + + case VBOX_SHCL_GUEST_FN_CONNECT: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_NEGOTIATE_CHUNK_SIZE: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_MSG_PEEK_NOWAIT: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_REPORT_FEATURES: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_QUERY_FEATURES: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_MSG_GET: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_REPLY: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_MSG_CANCEL: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_ERROR: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_LIST_OPEN: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_LIST_CLOSE: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_OBJ_OPEN: + RT_FALL_THROUGH(); + case VBOX_SHCL_GUEST_FN_OBJ_CLOSE: + fAllowed = fHostToGuest || fGuestToHost; + break; + + default: + break; + } + + LogFlowFunc(("uMsg=%RU32 (%s), uMode=%RU32 -> fAllowed=%RTbool\n", uMsg, ShClGuestMsgToStr(uMsg), uMode, fAllowed)); + return fAllowed; +} + +/** + * Gets a transfer message reply from HGCM service parameters. + * + * @returns VBox status code. + * @param cParms Number of HGCM parameters supplied in \a aParms. + * @param aParms Array of HGCM parameters. + * @param pReply Where to store the reply. + */ +static int shClSvcTransferGetReply(uint32_t cParms, VBOXHGCMSVCPARM aParms[], + PSHCLREPLY pReply) +{ + int rc; + + if (cParms >= VBOX_SHCL_CPARMS_REPLY_MIN) + { + /* aParms[0] has the context ID. */ + rc = HGCMSvcGetU32(&aParms[1], &pReply->uType); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&aParms[2], &pReply->rc); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&aParms[3], &pReply->pvPayload, &pReply->cbPayload); + + if (RT_SUCCESS(rc)) + { + rc = VERR_INVALID_PARAMETER; /* Play safe. */ + + const unsigned idxParm = VBOX_SHCL_CPARMS_REPLY_MIN; + + switch (pReply->uType) + { + case VBOX_SHCL_REPLYMSGTYPE_TRANSFER_STATUS: + { + if (cParms > idxParm) + rc = HGCMSvcGetU32(&aParms[idxParm], &pReply->u.TransferStatus.uStatus); + + LogFlowFunc(("uTransferStatus=%RU32\n", pReply->u.TransferStatus.uStatus)); + break; + } + + case VBOX_SHCL_REPLYMSGTYPE_LIST_OPEN: + { + if (cParms > idxParm) + rc = HGCMSvcGetU64(&aParms[idxParm], &pReply->u.ListOpen.uHandle); + + LogFlowFunc(("hListOpen=%RU64\n", pReply->u.ListOpen.uHandle)); + break; + } + + case VBOX_SHCL_REPLYMSGTYPE_LIST_CLOSE: + { + if (cParms > idxParm) + rc = HGCMSvcGetU64(&aParms[idxParm], &pReply->u.ListClose.uHandle); + + LogFlowFunc(("hListClose=%RU64\n", pReply->u.ListClose.uHandle)); + break; + } + + case VBOX_SHCL_REPLYMSGTYPE_OBJ_OPEN: + { + if (cParms > idxParm) + rc = HGCMSvcGetU64(&aParms[idxParm], &pReply->u.ObjOpen.uHandle); + + LogFlowFunc(("hObjOpen=%RU64\n", pReply->u.ObjOpen.uHandle)); + break; + } + + case VBOX_SHCL_REPLYMSGTYPE_OBJ_CLOSE: + { + if (cParms > idxParm) + rc = HGCMSvcGetU64(&aParms[idxParm], &pReply->u.ObjClose.uHandle); + + LogFlowFunc(("hObjClose=%RU64\n", pReply->u.ObjClose.uHandle)); + break; + } + + default: + rc = VERR_NOT_SUPPORTED; + break; + } + } + } + else + rc = VERR_INVALID_PARAMETER; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Gets a transfer root list header from HGCM service parameters. + * + * @returns VBox status code. + * @param cParms Number of HGCM parameters supplied in \a aParms. + * @param aParms Array of HGCM parameters. + * @param pRootLstHdr Where to store the transfer root list header on success. + */ +static int shClSvcTransferGetRootListHdr(uint32_t cParms, VBOXHGCMSVCPARM aParms[], + PSHCLROOTLISTHDR pRootLstHdr) +{ + int rc; + + if (cParms == VBOX_SHCL_CPARMS_ROOT_LIST_HDR_WRITE) + { + rc = HGCMSvcGetU32(&aParms[1], &pRootLstHdr->fRoots); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&aParms[2], &pRootLstHdr->cRoots); + } + else + rc = VERR_INVALID_PARAMETER; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Gets a transfer root list entry from HGCM service parameters. + * + * @returns VBox status code. + * @param cParms Number of HGCM parameters supplied in \a aParms. + * @param aParms Array of HGCM parameters. + * @param pListEntry Where to store the root list entry. + */ +static int shClSvcTransferGetRootListEntry(uint32_t cParms, VBOXHGCMSVCPARM aParms[], + PSHCLROOTLISTENTRY pListEntry) +{ + int rc; + + if (cParms == VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_WRITE) + { + rc = HGCMSvcGetU32(&aParms[1], &pListEntry->fInfo); + /* Note: aParms[2] contains the entry index, currently being ignored. */ + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&aParms[3], (void **)&pListEntry->pszName, &pListEntry->cbName); + if (RT_SUCCESS(rc)) + { + uint32_t cbInfo; + rc = HGCMSvcGetU32(&aParms[4], &cbInfo); + if (RT_SUCCESS(rc)) + { + rc = HGCMSvcGetPv(&aParms[5], &pListEntry->pvInfo, &pListEntry->cbInfo); + AssertReturn(cbInfo == pListEntry->cbInfo, VERR_INVALID_PARAMETER); + } + } + } + else + rc = VERR_INVALID_PARAMETER; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Gets a transfer list open request from HGCM service parameters. + * + * @returns VBox status code. + * @param cParms Number of HGCM parameters supplied in \a aParms. + * @param aParms Array of HGCM parameters. + * @param pOpenParms Where to store the open parameters of the request. + */ +static int shClSvcTransferGetListOpen(uint32_t cParms, VBOXHGCMSVCPARM aParms[], + PSHCLLISTOPENPARMS pOpenParms) +{ + int rc; + + if (cParms == VBOX_SHCL_CPARMS_LIST_OPEN) + { + rc = HGCMSvcGetU32(&aParms[1], &pOpenParms->fList); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetStr(&aParms[2], &pOpenParms->pszFilter, &pOpenParms->cbFilter); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetStr(&aParms[3], &pOpenParms->pszPath, &pOpenParms->cbPath); + + /** @todo Some more validation. */ + } + else + rc = VERR_INVALID_PARAMETER; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sets a transfer list open request to HGCM service parameters. + * + * @returns VBox status code. + * @param cParms Number of HGCM parameters supplied in \a aParms. + * @param aParms Array of HGCM parameters. + * @param idCtx Context ID to use. + * @param pOpenParms List open parameters to set. + */ +static int shClSvcTransferSetListOpen(uint32_t cParms, VBOXHGCMSVCPARM aParms[], + uint64_t idCtx, PSHCLLISTOPENPARMS pOpenParms) +{ + int rc; + + if (cParms == VBOX_SHCL_CPARMS_LIST_OPEN) + { + HGCMSvcSetU64(&aParms[0], idCtx); + HGCMSvcSetU32(&aParms[1], pOpenParms->fList); + HGCMSvcSetPv (&aParms[2], pOpenParms->pszFilter, pOpenParms->cbFilter); + HGCMSvcSetPv (&aParms[3], pOpenParms->pszPath, pOpenParms->cbPath); + HGCMSvcSetU64(&aParms[4], 0); /* OUT: uHandle */ + + rc = VINF_SUCCESS; + } + else + rc = VERR_INVALID_PARAMETER; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sets a transfer list close request to HGCM service parameters. + * + * @returns VBox status code. + * @param cParms Number of HGCM parameters supplied in \a aParms. + * @param aParms Array of HGCM parameters. + * @param idCtx Context ID to use. + * @param hList Handle of list to close. + */ +static int shClSvcTransferSetListClose(uint32_t cParms, VBOXHGCMSVCPARM aParms[], + uint64_t idCtx, SHCLLISTHANDLE hList) +{ + int rc; + + if (cParms == VBOX_SHCL_CPARMS_LIST_CLOSE) + { + HGCMSvcSetU64(&aParms[0], idCtx); + HGCMSvcSetU64(&aParms[1], hList); + + rc = VINF_SUCCESS; + } + else + rc = VERR_INVALID_PARAMETER; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Gets a transfer list header from HGCM service parameters. + * + * @returns VBox status code. + * @param cParms Number of HGCM parameters supplied in \a aParms. + * @param aParms Array of HGCM parameters. + * @param phList Where to store the list handle. + * @param pListHdr Where to store the list header. + */ +static int shClSvcTransferGetListHdr(uint32_t cParms, VBOXHGCMSVCPARM aParms[], + PSHCLLISTHANDLE phList, PSHCLLISTHDR pListHdr) +{ + int rc; + + if (cParms == VBOX_SHCL_CPARMS_LIST_HDR) + { + rc = HGCMSvcGetU64(&aParms[1], phList); + /* Note: Flags (aParms[2]) not used here. */ + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&aParms[3], &pListHdr->fFeatures); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU64(&aParms[4], &pListHdr->cTotalObjects); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU64(&aParms[5], &pListHdr->cbTotalSize); + + if (RT_SUCCESS(rc)) + { + /** @todo Validate pvMetaFmt + cbMetaFmt. */ + /** @todo Validate header checksum. */ + } + } + else + rc = VERR_INVALID_PARAMETER; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sets a transfer list header to HGCM service parameters. + * + * @returns VBox status code. + * @param cParms Number of HGCM parameters supplied in \a aParms. + * @param aParms Array of HGCM parameters. + * @param pListHdr Pointer to list header to set. + */ +static int shClSvcTransferSetListHdr(uint32_t cParms, VBOXHGCMSVCPARM aParms[], PSHCLLISTHDR pListHdr) +{ + int rc; + + if (cParms == VBOX_SHCL_CPARMS_LIST_HDR) + { + /** @todo Set pvMetaFmt + cbMetaFmt. */ + /** @todo Calculate header checksum. */ + + HGCMSvcSetU32(&aParms[3], pListHdr->fFeatures); + HGCMSvcSetU64(&aParms[4], pListHdr->cTotalObjects); + HGCMSvcSetU64(&aParms[5], pListHdr->cbTotalSize); + + rc = VINF_SUCCESS; + } + else + rc = VERR_INVALID_PARAMETER; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Gets a transfer list entry from HGCM service parameters. + * + * @returns VBox status code. + * @param cParms Number of HGCM parameters supplied in \a aParms. + * @param aParms Array of HGCM parameters. + * @param phList Where to store the list handle. + * @param pListEntry Where to store the list entry. + */ +static int shClSvcTransferGetListEntry(uint32_t cParms, VBOXHGCMSVCPARM aParms[], + PSHCLLISTHANDLE phList, PSHCLLISTENTRY pListEntry) +{ + int rc; + + if (cParms == VBOX_SHCL_CPARMS_LIST_ENTRY) + { + rc = HGCMSvcGetU64(&aParms[1], phList); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&aParms[2], &pListEntry->fInfo); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&aParms[3], (void **)&pListEntry->pszName, &pListEntry->cbName); + if (RT_SUCCESS(rc)) + { + uint32_t cbInfo; + rc = HGCMSvcGetU32(&aParms[4], &cbInfo); + if (RT_SUCCESS(rc)) + { + rc = HGCMSvcGetPv(&aParms[5], &pListEntry->pvInfo, &pListEntry->cbInfo); + AssertReturn(cbInfo == pListEntry->cbInfo, VERR_INVALID_PARAMETER); + } + } + + if (RT_SUCCESS(rc)) + { + if (!ShClTransferListEntryIsValid(pListEntry)) + rc = VERR_INVALID_PARAMETER; + } + } + else + rc = VERR_INVALID_PARAMETER; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sets a Shared Clipboard list entry to HGCM service parameters. + * + * @returns VBox status code. + * @param cParms Number of HGCM parameters supplied in \a aParms. + * @param aParms Array of HGCM parameters. + * @param pListEntry Pointer list entry to set. + */ +static int shClSvcTransferSetListEntry(uint32_t cParms, VBOXHGCMSVCPARM aParms[], + PSHCLLISTENTRY pListEntry) +{ + int rc; + + /* Sanity. */ + AssertReturn(ShClTransferListEntryIsValid(pListEntry), VERR_INVALID_PARAMETER); + + if (cParms == VBOX_SHCL_CPARMS_LIST_ENTRY) + { + HGCMSvcSetPv (&aParms[3], pListEntry->pszName, pListEntry->cbName); + HGCMSvcSetU32(&aParms[4], pListEntry->cbInfo); + HGCMSvcSetPv (&aParms[5], pListEntry->pvInfo, pListEntry->cbInfo); + + rc = VINF_SUCCESS; + } + else + rc = VERR_INVALID_PARAMETER; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Gets a transfer object data chunk from HGCM service parameters. + * + * @returns VBox status code. + * @param cParms Number of HGCM parameters supplied in \a aParms. + * @param aParms Array of HGCM parameters. + * @param pDataChunk Where to store the object data chunk data. + */ +static int shClSvcTransferGetObjDataChunk(uint32_t cParms, VBOXHGCMSVCPARM aParms[], PSHCLOBJDATACHUNK pDataChunk) +{ + AssertPtrReturn(aParms, VERR_INVALID_PARAMETER); + AssertPtrReturn(pDataChunk, VERR_INVALID_PARAMETER); + + int rc; + + if (cParms == VBOX_SHCL_CPARMS_OBJ_WRITE) + { + rc = HGCMSvcGetU64(&aParms[1], &pDataChunk->uHandle); + if (RT_SUCCESS(rc)) + { + uint32_t cbData; + rc = HGCMSvcGetU32(&aParms[2], &cbData); + if (RT_SUCCESS(rc)) + { + rc = HGCMSvcGetPv(&aParms[3], &pDataChunk->pvData, &pDataChunk->cbData); + AssertReturn(cbData == pDataChunk->cbData, VERR_INVALID_PARAMETER); + + /** @todo Implement checksum handling. */ + } + } + } + else + rc = VERR_INVALID_PARAMETER; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Handles a guest reply (VBOX_SHCL_GUEST_FN_REPLY) message. + * + * @returns VBox status code. + * @param pClient Pointer to associated client. + * @param pTransfer Pointer to transfer to handle guest reply for. + * @param cParms Number of function parameters supplied. + * @param aParms Array function parameters supplied. + */ +static int shClSvcTransferHandleReply(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, + uint32_t cParms, VBOXHGCMSVCPARM aParms[]) +{ + RT_NOREF(pClient, pTransfer); + + int rc; + + uint32_t cbReply = sizeof(SHCLREPLY); + PSHCLREPLY pReply = (PSHCLREPLY)RTMemAlloc(cbReply); + if (pReply) + { + rc = shClSvcTransferGetReply(cParms, aParms, pReply); + if (RT_SUCCESS(rc)) + { + PSHCLEVENTPAYLOAD pPayload + = (PSHCLEVENTPAYLOAD)RTMemAlloc(sizeof(SHCLEVENTPAYLOAD)); + if (pPayload) + { + pPayload->pvData = pReply; + pPayload->cbData = cbReply; + + switch (pReply->uType) + { + case VBOX_SHCL_REPLYMSGTYPE_TRANSFER_STATUS: + RT_FALL_THROUGH(); + case VBOX_SHCL_REPLYMSGTYPE_LIST_OPEN: + RT_FALL_THROUGH(); + case VBOX_SHCL_REPLYMSGTYPE_LIST_CLOSE: + RT_FALL_THROUGH(); + case VBOX_SHCL_REPLYMSGTYPE_OBJ_OPEN: + RT_FALL_THROUGH(); + case VBOX_SHCL_REPLYMSGTYPE_OBJ_CLOSE: + { + uint64_t uCID; + rc = HGCMSvcGetU64(&aParms[0], &uCID); + if (RT_SUCCESS(rc)) + { + const PSHCLEVENT pEvent + = ShClEventSourceGetFromId(&pClient->EventSrc, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID)); + if (pEvent) + { + LogFlowFunc(("uCID=%RU64 -> idEvent=%RU32\n", uCID, pEvent->idEvent)); + + rc = ShClEventSignal(pEvent, pPayload); + } + /** @todo Silently skip? */ + } + break; + } + + default: + rc = VERR_NOT_FOUND; + break; + } + + if (RT_FAILURE(rc)) + { + if (pPayload) + RTMemFree(pPayload); + } + } + else + rc = VERR_NO_MEMORY; + } + } + else + rc = VERR_NO_MEMORY; + + if (RT_FAILURE(rc)) + { + if (pReply) + RTMemFree(pReply); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Transfer client (guest) handler for the Shared Clipboard host service. + * + * @returns VBox status code, or VINF_HGCM_ASYNC_EXECUTE if returning to the client will be deferred. + * @param pClient Pointer to associated client. + * @param callHandle The client's call handle of this call. + * @param u32Function Function number being called. + * @param cParms Number of function parameters supplied. + * @param aParms Array function parameters supplied. + * @param tsArrival Timestamp of arrival. + */ +int shClSvcTransferHandler(PSHCLCLIENT pClient, + VBOXHGCMCALLHANDLE callHandle, + uint32_t u32Function, + uint32_t cParms, + VBOXHGCMSVCPARM aParms[], + uint64_t tsArrival) +{ + RT_NOREF(callHandle, aParms, tsArrival); + + LogFlowFunc(("uClient=%RU32, u32Function=%RU32 (%s), cParms=%RU32, g_ExtState.pfnExtension=%p\n", + pClient->State.uClientID, u32Function, ShClGuestMsgToStr(u32Function), cParms, g_ExtState.pfnExtension)); + + /* Check if we've the right mode set. */ + if (!shClSvcTransferMsgIsAllowed(ShClSvcGetMode(), u32Function)) + { + LogFunc(("Wrong clipboard mode, denying access\n")); + return VERR_ACCESS_DENIED; + } + + int rc = VERR_INVALID_PARAMETER; /* Play safe by default. */ + + /* + * Pre-check: For certain messages we need to make sure that a (right) transfer is present. + */ + uint64_t uCID = 0; /* Context ID */ + PSHCLTRANSFER pTransfer = NULL; + + switch (u32Function) + { + default: + { + if (!ShClTransferCtxGetTotalTransfers(&pClient->Transfers.Ctx)) + { + LogFunc(("No transfers found\n")); + rc = VERR_SHCLPB_TRANSFER_ID_NOT_FOUND; + break; + } + + if (cParms < 1) + break; + + rc = HGCMSvcGetU64(&aParms[0], &uCID); + if (RT_FAILURE(rc)) + break; + + const SHCLTRANSFERID uTransferID = VBOX_SHCL_CONTEXTID_GET_TRANSFER(uCID); + + pTransfer = ShClTransferCtxGetTransferById(&pClient->Transfers.Ctx, uTransferID); + if (!pTransfer) + { + LogFunc(("Transfer with ID %RU16 not found\n", uTransferID)); + rc = VERR_SHCLPB_TRANSFER_ID_NOT_FOUND; + } + break; + } + } + + if (RT_FAILURE(rc)) + return rc; + + rc = VERR_INVALID_PARAMETER; /* Play safe. */ + + switch (u32Function) + { + case VBOX_SHCL_GUEST_FN_REPLY: + { + rc = shClSvcTransferHandleReply(pClient, pTransfer, cParms, aParms); + break; + } + + case VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_READ: + { + if (cParms != VBOX_SHCL_CPARMS_ROOT_LIST_HDR_READ) + break; + + if ( ShClTransferGetSource(pTransfer) == SHCLSOURCE_LOCAL + && ShClTransferGetDir(pTransfer) == SHCLTRANSFERDIR_TO_REMOTE) + { + /* Get roots if this is a local write transfer (host -> guest). */ + rc = ShClBackendTransferGetRoots(pClient->pBackend, pClient, pTransfer); + } + else + { + rc = VERR_INVALID_PARAMETER; + break; + } + + SHCLROOTLISTHDR rootListHdr; + RT_ZERO(rootListHdr); + + rootListHdr.cRoots = ShClTransferRootsCount(pTransfer); + + HGCMSvcSetU64(&aParms[0], 0 /* Context ID */); + HGCMSvcSetU32(&aParms[1], rootListHdr.fRoots); + HGCMSvcSetU32(&aParms[2], rootListHdr.cRoots); + + rc = VINF_SUCCESS; + break; + } + + case VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_WRITE: + { + SHCLROOTLISTHDR lstHdr; + rc = shClSvcTransferGetRootListHdr(cParms, aParms, &lstHdr); + if (RT_SUCCESS(rc)) + { + void *pvData = ShClTransferRootListHdrDup(&lstHdr); + uint32_t cbData = sizeof(SHCLROOTLISTHDR); + + const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pClient->EventSrc, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID)); + if (pEvent) + { + PSHCLEVENTPAYLOAD pPayload; + rc = ShClPayloadAlloc(pEvent->idEvent, pvData, cbData, &pPayload); + if (RT_SUCCESS(rc)) + { + rc = ShClEventSignal(pEvent, pPayload); + if (RT_FAILURE(rc)) + ShClPayloadFree(pPayload); + } + } + else + rc = VERR_SHCLPB_EVENT_ID_NOT_FOUND; + } + break; + } + + case VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_READ: + { + if (cParms != VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_READ) + break; + + /* aParms[1] contains fInfo flags, currently unused. */ + uint32_t uIndex; + rc = HGCMSvcGetU32(&aParms[2], &uIndex); + if (RT_SUCCESS(rc)) + { + SHCLROOTLISTENTRY rootListEntry; + rc = ShClTransferRootsEntry(pTransfer, uIndex, &rootListEntry); + if (RT_SUCCESS(rc)) + { + HGCMSvcSetPv (&aParms[3], rootListEntry.pszName, rootListEntry.cbName); + HGCMSvcSetU32(&aParms[4], rootListEntry.cbInfo); + HGCMSvcSetPv (&aParms[5], rootListEntry.pvInfo, rootListEntry.cbInfo); + } + } + break; + } + + case VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_WRITE: + { + SHCLROOTLISTENTRY lstEntry; + rc = shClSvcTransferGetRootListEntry(cParms, aParms, &lstEntry); + if (RT_SUCCESS(rc)) + { + void *pvData = ShClTransferRootListEntryDup(&lstEntry); + uint32_t cbData = sizeof(SHCLROOTLISTENTRY); + + const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pClient->EventSrc, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID)); + if (pEvent) + { + PSHCLEVENTPAYLOAD pPayload; + rc = ShClPayloadAlloc(pEvent->idEvent, pvData, cbData, &pPayload); + if (RT_SUCCESS(rc)) + { + rc = ShClEventSignal(pEvent, pPayload); + if (RT_FAILURE(rc)) + ShClPayloadFree(pPayload); + } + } + else + rc = VERR_SHCLPB_EVENT_ID_NOT_FOUND; + } + break; + } + + case VBOX_SHCL_GUEST_FN_LIST_OPEN: + { + SHCLLISTOPENPARMS listOpenParms; + rc = shClSvcTransferGetListOpen(cParms, aParms, &listOpenParms); + if (RT_SUCCESS(rc)) + { + SHCLLISTHANDLE hList; + rc = ShClTransferListOpen(pTransfer, &listOpenParms, &hList); + if (RT_SUCCESS(rc)) + { + /* Return list handle. */ + HGCMSvcSetU64(&aParms[6], hList); + } + } + break; + } + + case VBOX_SHCL_GUEST_FN_LIST_CLOSE: + { + if (cParms != VBOX_SHCL_CPARMS_LIST_CLOSE) + break; + + SHCLLISTHANDLE hList; + rc = HGCMSvcGetU64(&aParms[1], &hList); + if (RT_SUCCESS(rc)) + { + rc = ShClTransferListClose(pTransfer, hList); + } + break; + } + + case VBOX_SHCL_GUEST_FN_LIST_HDR_READ: + { + if (cParms != VBOX_SHCL_CPARMS_LIST_HDR) + break; + + SHCLLISTHANDLE hList; + rc = HGCMSvcGetU64(&aParms[1], &hList); /* Get list handle. */ + if (RT_SUCCESS(rc)) + { + SHCLLISTHDR hdrList; + rc = ShClTransferListGetHeader(pTransfer, hList, &hdrList); + if (RT_SUCCESS(rc)) + rc = shClSvcTransferSetListHdr(cParms, aParms, &hdrList); + } + break; + } + + case VBOX_SHCL_GUEST_FN_LIST_HDR_WRITE: + { + SHCLLISTHDR hdrList; + rc = ShClTransferListHdrInit(&hdrList); + if (RT_SUCCESS(rc)) + { + SHCLLISTHANDLE hList; + rc = shClSvcTransferGetListHdr(cParms, aParms, &hList, &hdrList); + if (RT_SUCCESS(rc)) + { + void *pvData = ShClTransferListHdrDup(&hdrList); + uint32_t cbData = sizeof(SHCLLISTHDR); + + const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pClient->EventSrc, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID)); + if (pEvent) + { + PSHCLEVENTPAYLOAD pPayload; + rc = ShClPayloadAlloc(pEvent->idEvent, pvData, cbData, &pPayload); + if (RT_SUCCESS(rc)) + { + rc = ShClEventSignal(pEvent, pPayload); + if (RT_FAILURE(rc)) + ShClPayloadFree(pPayload); + } + } + else + rc = VERR_SHCLPB_EVENT_ID_NOT_FOUND; + } + } + break; + } + + case VBOX_SHCL_GUEST_FN_LIST_ENTRY_READ: + { + if (cParms != VBOX_SHCL_CPARMS_LIST_ENTRY) + break; + + SHCLLISTHANDLE hList; + rc = HGCMSvcGetU64(&aParms[1], &hList); /* Get list handle. */ + if (RT_SUCCESS(rc)) + { + SHCLLISTENTRY entryList; + rc = ShClTransferListEntryInit(&entryList); + if (RT_SUCCESS(rc)) + { + rc = ShClTransferListRead(pTransfer, hList, &entryList); + if (RT_SUCCESS(rc)) + rc = shClSvcTransferSetListEntry(cParms, aParms, &entryList); + } + } + break; + } + + case VBOX_SHCL_GUEST_FN_LIST_ENTRY_WRITE: + { + SHCLLISTENTRY entryList; + rc = ShClTransferListEntryInit(&entryList); + if (RT_SUCCESS(rc)) + { + SHCLLISTHANDLE hList; + rc = shClSvcTransferGetListEntry(cParms, aParms, &hList, &entryList); + if (RT_SUCCESS(rc)) + { + void *pvData = ShClTransferListEntryDup(&entryList); + uint32_t cbData = sizeof(SHCLLISTENTRY); + + const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pClient->EventSrc, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID)); + if (pEvent) + { + PSHCLEVENTPAYLOAD pPayload; + rc = ShClPayloadAlloc(pEvent->idEvent, pvData, cbData, &pPayload); + if (RT_SUCCESS(rc)) + { + rc = ShClEventSignal(pEvent, pPayload); + if (RT_FAILURE(rc)) + ShClPayloadFree(pPayload); + } + } + else + rc = VERR_SHCLPB_EVENT_ID_NOT_FOUND; + } + } + break; + } + + case VBOX_SHCL_GUEST_FN_OBJ_OPEN: + { + ASSERT_GUEST_STMT_BREAK(cParms == VBOX_SHCL_CPARMS_OBJ_OPEN, VERR_WRONG_PARAMETER_COUNT); + + SHCLOBJOPENCREATEPARMS openCreateParms; + RT_ZERO(openCreateParms); + + /* aParms[1] will return the object handle on success; see below. */ + rc = HGCMSvcGetStr(&aParms[2], &openCreateParms.pszPath, &openCreateParms.cbPath); + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&aParms[3], &openCreateParms.fCreate); + + if (RT_SUCCESS(rc)) + { + SHCLOBJHANDLE hObj; + rc = ShClTransferObjOpen(pTransfer, &openCreateParms, &hObj); + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("hObj=%RU64\n", hObj)); + + HGCMSvcSetU64(&aParms[1], hObj); + } + } + break; + } + + case VBOX_SHCL_GUEST_FN_OBJ_CLOSE: + { + if (cParms != VBOX_SHCL_CPARMS_OBJ_CLOSE) + break; + + SHCLOBJHANDLE hObj; + rc = HGCMSvcGetU64(&aParms[1], &hObj); /* Get object handle. */ + if (RT_SUCCESS(rc)) + rc = ShClTransferObjClose(pTransfer, hObj); + break; + } + + case VBOX_SHCL_GUEST_FN_OBJ_READ: + { + if (cParms != VBOX_SHCL_CPARMS_OBJ_READ) + break; + + SHCLOBJHANDLE hObj; + rc = HGCMSvcGetU64(&aParms[1], &hObj); /* Get object handle. */ + + uint32_t cbToRead = 0; + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetU32(&aParms[2], &cbToRead); + + void *pvBuf = NULL; + uint32_t cbBuf = 0; + if (RT_SUCCESS(rc)) + rc = HGCMSvcGetPv(&aParms[3], &pvBuf, &cbBuf); + + LogFlowFunc(("hObj=%RU64, cbBuf=%RU32, cbToRead=%RU32, rc=%Rrc\n", hObj, cbBuf, cbToRead, rc)); + + if ( RT_SUCCESS(rc) + && ( !cbBuf + || !cbToRead + || cbBuf < cbToRead + ) + ) + { + rc = VERR_INVALID_PARAMETER; + } + + if (RT_SUCCESS(rc)) + { + uint32_t cbRead; + rc = ShClTransferObjRead(pTransfer, hObj, pvBuf, cbToRead, 0 /* fFlags */, &cbRead); + if (RT_SUCCESS(rc)) + { + HGCMSvcSetU32(&aParms[3], cbRead); + + /** @todo Implement checksum support. */ + } + } + break; + } + + case VBOX_SHCL_GUEST_FN_OBJ_WRITE: + { + SHCLOBJDATACHUNK dataChunk; + rc = shClSvcTransferGetObjDataChunk(cParms, aParms, &dataChunk); + if (RT_SUCCESS(rc)) + { + void *pvData = ShClTransferObjDataChunkDup(&dataChunk); + uint32_t cbData = sizeof(SHCLOBJDATACHUNK); + + const PSHCLEVENT pEvent = ShClEventSourceGetFromId(&pClient->EventSrc, VBOX_SHCL_CONTEXTID_GET_EVENT(uCID)); + if (pEvent) + { + PSHCLEVENTPAYLOAD pPayload; + rc = ShClPayloadAlloc(pEvent->idEvent, pvData, cbData, &pPayload); + if (RT_SUCCESS(rc)) + { + rc = ShClEventSignal(pEvent, pPayload); + if (RT_FAILURE(rc)) + ShClPayloadFree(pPayload); + } + } + else + rc = VERR_SHCLPB_EVENT_ID_NOT_FOUND; + } + + break; + } + + default: + rc = VERR_NOT_IMPLEMENTED; + break; + } + + LogFlowFunc(("[Client %RU32] Returning rc=%Rrc\n", pClient->State.uClientID, rc)); + return rc; +} + +/** + * Transfer host handler for the Shared Clipboard host service. + * + * @returns VBox status code. + * @param u32Function Function number being called. + * @param cParms Number of function parameters supplied. + * @param aParms Array function parameters supplied. + */ +int shClSvcTransferHostHandler(uint32_t u32Function, + uint32_t cParms, + VBOXHGCMSVCPARM aParms[]) +{ + RT_NOREF(cParms, aParms); + + int rc = VERR_NOT_IMPLEMENTED; /* Play safe. */ + + switch (u32Function) + { + case VBOX_SHCL_HOST_FN_CANCEL: /** @todo Implement this. */ + break; + + case VBOX_SHCL_HOST_FN_ERROR: /** @todo Implement this. */ + break; + + default: + break; + + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +int shClSvcTransferHostMsgHandler(PSHCLCLIENT pClient, PSHCLCLIENTMSG pMsg) +{ + RT_NOREF(pClient); + + int rc; + + switch (pMsg->idMsg) + { + default: + rc = VINF_SUCCESS; + break; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Reports a transfer status to the guest. + * + * @returns VBox status code. + * @param pClient Client that owns the transfer. + * @param pTransfer Transfer to report status for. + * @param uStatus Status to report. + * @param rcTransfer Result code to report. Optional and depending on status. + * @param ppEvent Where to return the wait event on success. Optional. + * Must be released by the caller with ShClEventRelease(). + */ +int shClSvcTransferSendStatus(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer, SHCLTRANSFERSTATUS uStatus, + int rcTransfer, PSHCLEVENT *ppEvent) +{ + AssertPtrReturn(pClient, VERR_INVALID_POINTER); + AssertPtrReturn(pTransfer, VERR_INVALID_POINTER); + /* ppEvent is optional. */ + + PSHCLCLIENTMSG pMsgReadData = shClSvcMsgAlloc(pClient, VBOX_SHCL_HOST_MSG_TRANSFER_STATUS, + VBOX_SHCL_CPARMS_TRANSFER_STATUS); + if (!pMsgReadData) + return VERR_NO_MEMORY; + + PSHCLEVENT pEvent; + int rc = ShClEventSourceGenerateAndRegisterEvent(&pClient->EventSrc, &pEvent); + if (RT_SUCCESS(rc)) + { + HGCMSvcSetU64(&pMsgReadData->aParms[0], VBOX_SHCL_CONTEXTID_MAKE(pClient->State.uSessionID, + pTransfer->State.uID, pEvent->idEvent)); + HGCMSvcSetU32(&pMsgReadData->aParms[1], pTransfer->State.enmDir); + HGCMSvcSetU32(&pMsgReadData->aParms[2], uStatus); + HGCMSvcSetU32(&pMsgReadData->aParms[3], (uint32_t)rcTransfer); /** @todo uint32_t vs. int. */ + HGCMSvcSetU32(&pMsgReadData->aParms[4], 0 /* fFlags, unused */); + + shClSvcMsgAdd(pClient, pMsgReadData, true /* fAppend */); + + rc = shClSvcClientWakeup(pClient); + if (RT_SUCCESS(rc)) + { + LogRel2(("Shared Clipboard: Reported status %s (rc=%Rrc) of transfer %RU32 to guest\n", + ShClTransferStatusToStr(uStatus), rcTransfer, pTransfer->State.uID)); + + if (ppEvent) + { + *ppEvent = pEvent; /* Takes ownership. */ + } + else /* If event is not consumed by the caller, release the event again. */ + ShClEventRelease(pEvent); + } + else + ShClEventRelease(pEvent); + } + else + rc = VERR_SHCLPB_MAX_EVENTS_REACHED; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Starts a new transfer, waiting for acknowledgement by the guest side. + * + * @note Assumes that the client's critical section is taken. + * + * @returns VBox status code. + * @param pClient Client that owns the transfer. + * @param enmDir Transfer direction to start. + * @param enmSource Transfer source to start. + * @param ppTransfer Where to return the created transfer on success. Optional. + */ +int shClSvcTransferStart(PSHCLCLIENT pClient, + SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource, + PSHCLTRANSFER *ppTransfer) +{ + AssertPtrReturn(pClient, VERR_INVALID_POINTER); + /* ppTransfer is optional. */ + + LogFlowFuncEnter(); + + PSHCLTRANSFERCTX pTxCtx = &pClient->Transfers.Ctx; + + ShClTransferCtxCleanup(pTxCtx); + + int rc; + + if (!ShClTransferCtxTransfersMaximumReached(pTxCtx)) + { + LogRel2(("Shared Clipboard: Starting %s transfer ...\n", enmDir == SHCLTRANSFERDIR_FROM_REMOTE ? "read" : "write")); + + PSHCLTRANSFER pTransfer; + rc = ShClTransferCreate(&pTransfer); + if (RT_SUCCESS(rc)) + { + SHCLTXPROVIDERCREATIONCTX creationCtx; + RT_ZERO(creationCtx); + + if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE) + { + creationCtx.Interface.pfnRootsGet = shClSvcTransferIfaceGetRoots; + + creationCtx.Interface.pfnListOpen = shClSvcTransferIfaceListOpen; + creationCtx.Interface.pfnListClose = shClSvcTransferIfaceListClose; + creationCtx.Interface.pfnListHdrRead = shClSvcTransferIfaceListHdrRead; + creationCtx.Interface.pfnListEntryRead = shClSvcTransferIfaceListEntryRead; + + creationCtx.Interface.pfnObjOpen = shClSvcTransferIfaceObjOpen; + creationCtx.Interface.pfnObjClose = shClSvcTransferIfaceObjClose; + creationCtx.Interface.pfnObjRead = shClSvcTransferIfaceObjRead; + } + else if (enmDir == SHCLTRANSFERDIR_TO_REMOTE) + { + creationCtx.Interface.pfnListHdrWrite = shClSvcTransferIfaceListHdrWrite; + creationCtx.Interface.pfnListEntryWrite = shClSvcTransferIfaceListEntryWrite; + creationCtx.Interface.pfnObjWrite = shClSvcTransferIfaceObjWrite; + } + else + AssertFailed(); + + creationCtx.enmSource = pClient->State.enmSource; + creationCtx.pvUser = pClient; + + rc = ShClTransferSetProviderIface(pTransfer, &creationCtx); + if (RT_SUCCESS(rc)) + { + rc = ShClTransferInit(pTransfer, enmDir, enmSource); + if (RT_SUCCESS(rc)) + { + SHCLTRANSFERID uTransferID = 0; + rc = ShClTransferCtxTransferRegister(pTxCtx, pTransfer, &uTransferID); + if (RT_SUCCESS(rc)) + { + rc = ShClBackendTransferCreate(pClient->pBackend, pClient, pTransfer); + if (RT_SUCCESS(rc)) + { + if (RT_SUCCESS(rc)) + rc = ShClTransferStart(pTransfer); + + if (RT_SUCCESS(rc)) + { + PSHCLEVENT pEvent; + rc = shClSvcTransferSendStatus(pClient, pTransfer, + SHCLTRANSFERSTATUS_INITIALIZED, VINF_SUCCESS, &pEvent); + if (RT_SUCCESS(rc)) + { + LogRel2(("Shared Clipboard: Waiting for start of transfer %RU32 on guest ...\n", + pTransfer->State.uID)); + + /* Leave the client's critical section before waiting. */ + RTCritSectLeave(&pClient->CritSect); + + PSHCLEVENTPAYLOAD pPayload = NULL; + rc = ShClEventWait(pEvent, pTransfer->uTimeoutMs, &pPayload); + if (RT_SUCCESS(rc)) + { + Assert(pPayload->cbData == sizeof(SHCLREPLY)); + PSHCLREPLY pReply = (PSHCLREPLY)pPayload->pvData; + AssertPtr(pReply); + + Assert(pReply->uType == VBOX_SHCL_REPLYMSGTYPE_TRANSFER_STATUS); + + if (pReply->u.TransferStatus.uStatus == SHCLTRANSFERSTATUS_STARTED) + { + LogRel2(("Shared Clipboard: Started transfer %RU32 on guest\n", pTransfer->State.uID)); + } + else + LogRel(("Shared Clipboard: Guest reported status %s (error %Rrc) while starting transfer %RU32\n", + ShClTransferStatusToStr(pReply->u.TransferStatus.uStatus), + pReply->rc, pTransfer->State.uID)); + + rc = pReply->rc; /* Set guest rc. */ + } + else + LogRel(("Shared Clipboard: Unable to start transfer %RU32 on guest, rc=%Rrc\n", + pTransfer->State.uID, rc)); + + ShClPayloadFree(pPayload); + ShClEventRelease(pEvent); + + /* Re-enter the client's critical section again. */ + RTCritSectEnter(&pClient->CritSect); + } + } + } + } + + if (RT_FAILURE(rc)) + ShClTransferCtxTransferUnregister(pTxCtx, uTransferID); + } + } + + if (RT_FAILURE(rc)) + { + ShClBackendTransferDestroy(pClient->pBackend, pClient, pTransfer); + ShClTransferDestroy(pTransfer); + + RTMemFree(pTransfer); + pTransfer = NULL; + } + else + { + if (ppTransfer) + *ppTransfer = pTransfer; + } + } + + if (RT_FAILURE(rc)) + LogRel(("Shared Clipboard: Starting transfer failed with %Rrc\n", rc)); + } + else + rc = VERR_SHCLPB_MAX_TRANSFERS_REACHED; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Stops (and destroys) a transfer, communicating the status to the guest side. + * + * @returns VBox status code. + * @param pClient Client that owns the transfer. + * @param pTransfer Transfer to stop. + */ +int shClSvcTransferStop(PSHCLCLIENT pClient, PSHCLTRANSFER pTransfer) +{ + PSHCLEVENT pEvent; + int rc = shClSvcTransferSendStatus(pClient, pTransfer, + SHCLTRANSFERSTATUS_STOPPED, VINF_SUCCESS, &pEvent); + if (RT_SUCCESS(rc)) + { + LogRel2(("Shared Clipboard: Waiting for stop of transfer %RU32 on guest ...\n", pTransfer->State.uID)); + + rc = ShClEventWait(pEvent, pTransfer->uTimeoutMs, NULL /* ppPayload */); + if (RT_SUCCESS(rc)) + LogRel2(("Shared Clipboard: Stopped transfer %RU32 on guest\n", pTransfer->State.uID)); + + ShClEventRelease(pEvent); + } + + if (RT_FAILURE(rc)) + LogRel(("Shared Clipboard: Unable to stop transfer %RU32 on guest, rc=%Rrc\n", + pTransfer->State.uID, rc)); + + /* Regardless of whether the guest was able to report back and/or stop the transfer, remove the transfer on the host + * so that we don't risk of having stale transfers here. */ + int rc2 = ShClTransferCtxTransferUnregister(&pClient->Transfers.Ctx, ShClTransferGetID(pTransfer)); + if (RT_SUCCESS(rc2)) + { + ShClBackendTransferDestroy(pClient->pBackend, pClient, pTransfer); + ShClTransferDestroy(pTransfer); + pTransfer = NULL; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sets the host service's (file) transfer mode. + * + * @returns VBox status code. + * @param fMode Transfer mode to set. + */ +int shClSvcTransferModeSet(uint32_t fMode) +{ + if (fMode & ~VBOX_SHCL_TRANSFER_MODE_VALID_MASK) + return VERR_INVALID_FLAGS; + + g_fTransferMode = fMode; + +#ifdef DEBUG_andy +g_fTransferMode = VBOX_SHCL_TRANSFER_MODE_ENABLED; +#endif + + LogRel2(("Shared Clipboard: File transfers are now %s\n", + g_fTransferMode != VBOX_SHCL_TRANSFER_MODE_DISABLED ? "enabled" : "disabled")); + + /* If file transfers are being disabled, make sure to also reset (destroy) all pending transfers. */ + if (g_fTransferMode == VBOX_SHCL_TRANSFER_MODE_DISABLED) + { + ClipboardClientMap::const_iterator itClient = g_mapClients.begin(); + while (itClient != g_mapClients.end()) + { + PSHCLCLIENT pClient = itClient->second; + AssertPtr(pClient); + + shClSvcClientTransfersReset(pClient); + + ++itClient; + } + } + + LogFlowFuncLeaveRC(VINF_SUCCESS); + return VINF_SUCCESS; +} |