diff options
Diffstat (limited to 'src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp')
-rw-r--r-- | src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp | 2609 |
1 files changed, 2609 insertions, 0 deletions
diff --git a/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp new file mode 100644 index 00000000..35aaa70e --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibClipboard.cpp @@ -0,0 +1,2609 @@ +/* $Id: VBoxGuestR3LibClipboard.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Shared Clipboard. + */ + +/* + * Copyright (C) 2007-2022 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>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <VBox/GuestHost/SharedClipboard.h> +#include <VBox/GuestHost/clipboard-helper.h> +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS +# include <VBox/GuestHost/SharedClipboard-transfers.h> +#endif +#include <VBox/HostServices/VBoxClipboardSvc.h> +#include <VBox/err.h> +#include <iprt/assert.h> +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS +# include <iprt/dir.h> +# include <iprt/file.h> +# include <iprt/path.h> +#endif +#include <iprt/string.h> +#include <iprt/cpp/ministring.h> + +#include "VBoxGuestR3LibInternal.h" + + +/** + * Function naming convention: + * + * FunctionNameRecv = Receives a host message (request). + * FunctionNameReply = Replies to a host message (request). + * FunctionNameSend = Sends a guest message to the host. + */ + + +/********************************************************************************************************************************* +* Prototypes * +*********************************************************************************************************************************/ + + +/** + * Connects to the Shared Clipboard service, legacy version, do not use anymore. + * + * @returns VBox status code + * @param pidClient Where to put the client id on success. The client id + * must be passed to all the other clipboard calls. + */ +VBGLR3DECL(int) VbglR3ClipboardConnect(HGCMCLIENTID *pidClient) +{ + int rc = VbglR3HGCMConnect("VBoxSharedClipboard", pidClient); + if (RT_FAILURE(rc)) + { + if (rc == VERR_HGCM_SERVICE_NOT_FOUND) + LogRel(("Shared Clipboard: Unabled to connect, as host service was not found, skipping\n")); + else + LogRel(("Shared Clipboard: Unabled to connect to host service, rc=%Rrc\n", rc)); + } + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Connects to the Shared Clipboard service, extended version. + * + * @returns VBox status code. + * @param pCtx Command context. This will be initialized by this + * call. + * @param fGuestFeatures The guest features supported by this client, + * VBOX_SHCL_GF_0_XXX. + */ +VBGLR3DECL(int) VbglR3ClipboardConnectEx(PVBGLR3SHCLCMDCTX pCtx, uint64_t fGuestFeatures) +{ + /* + * Intialize the context structure. + */ + pCtx->idClient = 0; + pCtx->fGuestFeatures = fGuestFeatures; + pCtx->fHostFeatures = 0; + pCtx->fUseLegacyProtocol = true; + pCtx->cParmsRecived = 0; + pCtx->idContext = 0; + +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + /* Init callback table. */ + RT_ZERO(pCtx->Transfers.Callbacks); + /* Indicate that this guest supports Shared Clipboard file transfers. */ + pCtx->fGuestFeatures |= VBOX_SHCL_GF_0_TRANSFERS; +# ifdef RT_OS_WINDOWS + /* Indicate that on Windows guest OSes we have our own IDataObject implementation which + * integrates nicely into the guest's Windows Explorer showing / handling the Shared Clipboard file transfers. */ + pCtx->fGuestFeatures |= VBOX_SHCL_GF_0_TRANSFERS_FRONTEND; +# endif + pCtx->Transfers.cbChunkSize = VBOX_SHCL_DEFAULT_CHUNK_SIZE; /** @todo Make this configurable. */ + pCtx->Transfers.cbMaxChunkSize = VBOX_SHCL_MAX_CHUNK_SIZE; /** @todo Ditto. */ +#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */ + + /* + * First step is connecting to the HGCM service. + */ + int rc = VbglR3ClipboardConnect(&pCtx->idClient); + if (RT_SUCCESS(rc)) + { + /* + * Next is reporting our features. If this fails, assume older host. + */ + rc = VbglR3ClipboardReportFeatures(pCtx->idClient, pCtx->fGuestFeatures, &pCtx->fHostFeatures); + if (RT_SUCCESS(rc)) + { + LogRel2(("Shared Clipboard: Guest features: %#RX64 - Host features: %#RX64\n", + pCtx->fGuestFeatures, pCtx->fHostFeatures)); + + if ( (pCtx->fHostFeatures & VBOX_SHCL_HF_0_CONTEXT_ID) + && (pCtx->fGuestFeatures & VBOX_SHCL_GF_0_CONTEXT_ID) ) + { + pCtx->fUseLegacyProtocol = false; + +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + if ( (pCtx->fHostFeatures & VBOX_SHCL_HF_0_TRANSFERS) + && (pCtx->fGuestFeatures & VBOX_SHCL_GF_0_TRANSFERS) ) + { + VBoxShClParmNegotiateChunkSize MsgChunkSize; + do + { + VBGL_HGCM_HDR_INIT(&MsgChunkSize.hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_NEGOTIATE_CHUNK_SIZE, + VBOX_SHCL_CPARMS_NEGOTIATE_CHUNK_SIZE); + MsgChunkSize.cb32MaxChunkSize.SetUInt32(pCtx->Transfers.cbMaxChunkSize); + MsgChunkSize.cb32ChunkSize.SetUInt32(0); /* If set to 0, let the host choose. */ + rc = VbglR3HGCMCall(&MsgChunkSize.hdr, sizeof(MsgChunkSize)); + } while (rc == VERR_INTERRUPTED); + if (RT_SUCCESS(rc)) + { + Assert(MsgChunkSize.cb32ChunkSize.type == VMMDevHGCMParmType_32bit); + pCtx->Transfers.cbChunkSize = RT_MIN(MsgChunkSize.cb32ChunkSize.u.value32, pCtx->Transfers.cbChunkSize); + Assert(MsgChunkSize.cb32MaxChunkSize.type == VMMDevHGCMParmType_32bit); + pCtx->Transfers.cbMaxChunkSize = RT_MIN(MsgChunkSize.cb32MaxChunkSize.u.value32, pCtx->Transfers.cbMaxChunkSize); + + LogRel2(("Shared Clipboard: Using chunk size %RU32 (maximum is %RU32)\n", + pCtx->Transfers.cbChunkSize, pCtx->Transfers.cbMaxChunkSize)); + } + } + else + { + if (!(pCtx->fHostFeatures & VBOX_SHCL_HF_0_TRANSFERS)) + LogRel2(("Shared Clipboard: Host does not support transfers\n")); + } +#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */ + } + else + { + if (!(pCtx->fHostFeatures & VBOX_SHCL_HF_0_CONTEXT_ID)) + LogRel(("Shared Clipboard: Host does not support context IDs, using legacy protocol\n")); + + pCtx->fUseLegacyProtocol = true; + } + } + else + { + AssertLogRelMsg(rc == VERR_NOT_SUPPORTED || rc == VERR_NOT_IMPLEMENTED, + ("Reporting features failed: %Rrc\n", rc)); + pCtx->fUseLegacyProtocol = true; + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Reports features to the host and retrieve host feature set. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3ClipboardConnect(). + * @param fGuestFeatures Features to report, VBOX_SHCL_GF_XXX. + * @param pfHostFeatures Where to store the features VBOX_SHCL_HF_XXX. + */ +VBGLR3DECL(int) VbglR3ClipboardReportFeatures(uint32_t idClient, uint64_t fGuestFeatures, uint64_t *pfHostFeatures) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter f64Features0; + HGCMFunctionParameter f64Features1; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_REPORT_FEATURES, 2); + VbglHGCMParmUInt64Set(&Msg.f64Features0, fGuestFeatures); + VbglHGCMParmUInt64Set(&Msg.f64Features1, VBOX_SHCL_GF_1_MUST_BE_ONE); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit); + Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit); + if (Msg.f64Features1.u.value64 & VBOX_SHCL_GF_1_MUST_BE_ONE) + rc = VERR_NOT_SUPPORTED; + else if (pfHostFeatures) + *pfHostFeatures = Msg.f64Features0.u.value64; + break; + } + } while (rc == VERR_INTERRUPTED); + return rc; + +} + + +/** + * Disconnects from the Shared Clipboard service, legacy version, do not use anymore. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + */ +VBGLR3DECL(int) VbglR3ClipboardDisconnect(HGCMCLIENTID idClient) +{ + return VbglR3HGCMDisconnect(idClient); +} + + +/** + * Disconnects from the Shared Clipboard service, extended version. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + */ +VBGLR3DECL(int) VbglR3ClipboardDisconnectEx(PVBGLR3SHCLCMDCTX pCtx) +{ + int rc = VbglR3ClipboardDisconnect(pCtx->idClient); + if (RT_SUCCESS(rc)) + { + pCtx->idClient = 0; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Receives reported formats from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the + * connection. + * @param pfFormats Where to store the received formats from the host. + */ +static int vbglR3ClipboardFormatsReportRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLFORMATS pfFormats) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pfFormats, VERR_INVALID_POINTER); + + *pfFormats = 0; + + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter id64Context; + HGCMFunctionParameter f32Formats; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_GET, 2); + Msg.id64Context.SetUInt32(VBOX_SHCL_HOST_MSG_FORMATS_REPORT); + Msg.f32Formats.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.f32Formats.GetUInt32(pfFormats); + AssertRC(rc); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Fetches a VBOX_SHCL_HOST_MSG_READ_DATA_CID message. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pfFormat Where to return the requested format. + */ +static int vbglR3ClipboardFetchReadDataCid(PVBGLR3SHCLCMDCTX pCtx, PSHCLFORMAT pfFormat) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pfFormat, VERR_INVALID_POINTER); + + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter id64Context; + HGCMFunctionParameter f32Format; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_GET, 2); + Msg.id64Context.SetUInt64(VBOX_SHCL_HOST_MSG_READ_DATA_CID); + Msg.f32Format.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.id64Context.GetUInt64(&pCtx->idContext); + AssertRC(rc); + int rc2 = Msg.f32Format.GetUInt32(pfFormat); + AssertRCStmt(rc2, rc = rc2); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Fetches a VBOX_SHCL_HOST_MSG_READ_DATA message. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pfFormat Where to return the requested format. + */ +static int vbglR3ClipboardFetchReadData(PVBGLR3SHCLCMDCTX pCtx, PSHCLFORMAT pfFormat) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pfFormat, VERR_INVALID_POINTER); + + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter id32Msg; + HGCMFunctionParameter f32Format; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_GET, 2); + Msg.id32Msg.SetUInt32(VBOX_SHCL_HOST_MSG_READ_DATA); + Msg.f32Format.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.f32Format.GetUInt32(pfFormat); + AssertRC(rc); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Get a host message, legacy version (which does not have VBOX_SHCL_GUEST_FN_MSG_GET). Do not use anymore. + * + * Note: This is the old message which still is being used for the non-URI Shared Clipboard transfers, + * to not break compatibility with older additions / VBox versions. + * + * This will block until a message becomes available. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param pidMsg Where to store the message id. + * @param pfFormats Where to store the format(s) the message applies to. + */ +VBGLR3DECL(int) VbglR3ClipboardGetHostMsgOld(HGCMCLIENTID idClient, uint32_t *pidMsg, uint32_t *pfFormats) +{ + VBoxShClGetHostMsgOld Msg; + + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHCL_GUEST_FN_MSG_OLD_GET_WAIT, VBOX_SHCL_CPARMS_GET_HOST_MSG_OLD); + VbglHGCMParmUInt32Set(&Msg.msg, 0); + VbglHGCMParmUInt32Set(&Msg.formats, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = VbglHGCMParmUInt32Get(&Msg.msg, pidMsg); + if (RT_SUCCESS(rc)) + { + rc2 = VbglHGCMParmUInt32Get(&Msg.formats, pfFormats); + if (RT_SUCCESS(rc2)) + return rc; + } + rc = rc2; + } + *pidMsg = UINT32_MAX - 1; + *pfFormats = UINT32_MAX; + return rc; +} + + +/** + * Reads data from the host clipboard. + * + * Legacy function, do not use anymore. + * + * @returns VBox status code. + * @retval VINF_BUFFER_OVERFLOW If there is more data available than the caller provided buffer space for. + * + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param fFormat The format we're requesting the data in. + * @param pvData Where to store the data. + * @param cbData The size of the buffer pointed to by \a pvData. + * @param pcbRead The actual size of the host clipboard data. May be larger than \a cbData. + */ +VBGLR3DECL(int) VbglR3ClipboardReadData(HGCMCLIENTID idClient, uint32_t fFormat, void *pvData, uint32_t cbData, + uint32_t *pcbRead) +{ + LogFlowFuncEnter(); + + struct + { + VBGLIOCHGCMCALL Hdr; + VBoxShClParmDataRead Parms; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_DATA_READ, VBOX_SHCL_CPARMS_DATA_READ); + VbglHGCMParmUInt32Set(&Msg.Parms.f32Format, fFormat); + VbglHGCMParmPtrSet( &Msg.Parms.pData, pvData, cbData); + VbglHGCMParmUInt32Set(&Msg.Parms.cb32Needed, 0); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + uint32_t cbRead; + rc = VbglHGCMParmUInt32Get(&Msg.Parms.cb32Needed, &cbRead); + if (RT_SUCCESS(rc)) + { + LogFlowFunc(("cbRead=%RU32\n", cbRead)); + + if (cbRead > cbData) + rc = VINF_BUFFER_OVERFLOW; + + *pcbRead = cbRead; + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/** + * Reads clipboard data from the host clipboard. + * + * @returns VBox status code. + * @retval VINF_BUFFER_OVERFLOW If there is more data available than the caller provided buffer space for. + * + * @param pCtx The command context returned by VbglR3ClipboardConnectEx(). + * @param uFormat Clipboard format of clipboard data to be read. + * @param pvData Buffer where to store the read data. + * @param cbData Size (in bytes) of data buffer where to store the read data. + * @param pcbRead The actual size of the host clipboard data. + */ +VBGLR3DECL(int) VbglR3ClipboardReadDataEx(PVBGLR3SHCLCMDCTX pCtx, + SHCLFORMAT uFormat, void *pvData, uint32_t cbData, uint32_t *pcbRead) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + return VbglR3ClipboardReadData(pCtx->idClient, uFormat, pvData, cbData, pcbRead); +} + + +/** + * Query the host features. + * + * @returns VBox status code. + * @param idClient The client ID returned by VbglR3ClipboardConnect(). + * @param pfHostFeatures Where to store the host feature, VBOX_SHCL_HF_XXX. + */ +VBGLR3DECL(int) VbglR3ClipboardQueryFeatures(uint32_t idClient, uint64_t *pfHostFeatures) +{ + int rc; + do + { + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter f64Features0; + HGCMFunctionParameter f64Features1; + } Msg; + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_QUERY_FEATURES, 2); + VbglHGCMParmUInt64Set(&Msg.f64Features0, 0); + VbglHGCMParmUInt64Set(&Msg.f64Features1, RT_BIT_64(63)); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit); + Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit); + if (Msg.f64Features1.u.value64 & RT_BIT_64(63)) + rc = VERR_NOT_SUPPORTED; + else if (pfHostFeatures) + *pfHostFeatures = Msg.f64Features0.u.value64; + break; + } + } while (rc == VERR_INTERRUPTED); + return rc; + +} + +/** + * Peeks at the next host message, waiting for one to turn up. + * + * This glosses over the difference between new (6.1) and old (1.3.2) host + * service versions, however it does so by abusing @a pcParameters, so don't use + * it directly when in legacy mode, always pass it on to + * VbglR3ClipboardEventGetNext() or VbglR3ClipboardEventGetNextEx(). + * + * @returns VBox status code. + * @retval VERR_INTERRUPTED if interrupted. Does the necessary cleanup, so + * caller just have to repeat this call. + * @retval VERR_VM_RESTORED if the VM has been restored (idRestoreCheck). + * + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pidMsg Where to store the message id. + * @param pcParameters Where to store the number of parameters which will + * be received in a second call to the host. + * @param pidRestoreCheck Pointer to the VbglR3GetSessionId() variable to use + * for the VM restore check. Optional. + * + * @note Restore check is only performed optimally with a 6.0 host. + */ +VBGLR3DECL(int) VbglR3ClipboardMsgPeekWait(PVBGLR3SHCLCMDCTX pCtx, uint32_t *pidMsg, + uint32_t *pcParameters, uint64_t *pidRestoreCheck) +{ + AssertPtrReturn(pidMsg, VERR_INVALID_POINTER); + AssertPtrReturn(pcParameters, VERR_INVALID_POINTER); + + struct + { + VBGLIOCHGCMCALL Hdr; + HGCMFunctionParameter idMsg; /* Doubles as restore check on input. */ + HGCMFunctionParameter cParameters; + } Msg; + int rc; + if (!pCtx->fUseLegacyProtocol) + { + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_PEEK_WAIT, 2); + VbglHGCMParmUInt64Set(&Msg.idMsg, pidRestoreCheck ? *pidRestoreCheck : 0); + VbglHGCMParmUInt32Set(&Msg.cParameters, 0); + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + LogFlowFunc(("VbglR3HGCMCall -> %Rrc\n", rc)); + if (RT_SUCCESS(rc)) + { + AssertMsgReturn( Msg.idMsg.type == VMMDevHGCMParmType_64bit + && Msg.cParameters.type == VMMDevHGCMParmType_32bit, + ("msg.type=%d num_parms.type=%d\n", Msg.idMsg.type, Msg.cParameters.type), + VERR_INTERNAL_ERROR_3); + + *pidMsg = (uint32_t)Msg.idMsg.u.value64; + *pcParameters = Msg.cParameters.u.value32; + return rc; + } + + /* + * If restored, update pidRestoreCheck. + */ + if (rc == VERR_VM_RESTORED && pidRestoreCheck) + *pidRestoreCheck = Msg.idMsg.u.value64; + } + else + { + /* + * We do some crude stuff here by putting the 2nd parameter (foramts) in the parameter count, + * however it's supposed to be passed directly to VbglR3ClipboardEventGetNext or + * VbglR3ClipboardEventGetNextEx, so that's fine... + */ + rc = VbglR3ClipboardGetHostMsgOld(pCtx->idClient, pidMsg, pcParameters); + if (RT_SUCCESS(rc)) + return rc; + } + + /* + * If interrupted we must cancel the call so it doesn't prevent us from making another one. + */ + if (rc == VERR_INTERRUPTED) + { + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_MSG_CANCEL, 0); + int rc2 = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg.Hdr)); + AssertRC(rc2); + } + + *pidMsg = UINT32_MAX - 1; + *pcParameters = UINT32_MAX - 2; + return rc; +} + +#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS + +/** + * Reads a root list header from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pRootListHdr Where to store the received root list header. + */ +static int vbglR3ClipboardRootListHdrRead(PVBGLR3SHCLCMDCTX pCtx, PSHCLROOTLISTHDR pRootListHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pRootListHdr, VERR_INVALID_POINTER); + + VBoxShClRootListHdrMsg Msg; + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_READ, VBOX_SHCL_CPARMS_ROOT_LIST_HDR_READ); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.fRoots.SetUInt32(0); + + Msg.cRoots.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.fRoots.GetUInt32(&pRootListHdr->fRoots); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + rc = Msg.cRoots.GetUInt32(&pRootListHdr->cRoots); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Reads a root list entry from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param uIndex Index of root list entry to read. + * @param pRootListEntry Where to store the root list entry read from the host. + */ +static int vbglR3ClipboardRootListEntryRead(PVBGLR3SHCLCMDCTX pCtx, uint32_t uIndex, PSHCLROOTLISTENTRY pRootListEntry) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pRootListEntry, VERR_INVALID_POINTER); + + VBoxShClRootListEntryMsg Msg; + + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_READ, VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_READ); + + Msg.Parms.uContext.SetUInt64(pCtx->idContext); + Msg.Parms.fInfo.SetUInt32(pRootListEntry->fInfo); + Msg.Parms.uIndex.SetUInt32(uIndex); + + Msg.szName.SetPtr(pRootListEntry->pszName, pRootListEntry->cbName); + Msg.cbInfo.SetUInt32(pRootListEntry->cbInfo); + Msg.pvInfo.SetPtr(pRootListEntry->pvInfo, pRootListEntry->cbInfo); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.Parms.fInfo.GetUInt32(&pRootListEntry->fInfo); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + uint32_t cbInfo = 0; + rc = Msg.cbInfo.GetUInt32(&cbInfo); AssertRC(rc); + if (pRootListEntry->cbInfo != cbInfo) + rc = VERR_INVALID_PARAMETER; + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Reads the root list from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param ppRootList Where to store the (allocated) root list. Must be free'd by the caller with + * SharedClipboardTransferRootListFree(). + */ +VBGLR3DECL(int) VbglR3ClipboardRootListRead(PVBGLR3SHCLCMDCTX pCtx, PSHCLROOTLIST *ppRootList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(ppRootList, VERR_INVALID_POINTER); + + int rc; + + PSHCLROOTLIST pRootList = ShClTransferRootListAlloc(); + if (pRootList) + { + SHCLROOTLISTHDR srcRootListHdr; + rc = vbglR3ClipboardRootListHdrRead(pCtx, &srcRootListHdr); + if (RT_SUCCESS(rc)) + { + pRootList->Hdr.cRoots = srcRootListHdr.cRoots; + pRootList->Hdr.fRoots = 0; /** @todo Implement this. */ + + if (srcRootListHdr.cRoots) + { + pRootList->paEntries = + (PSHCLROOTLISTENTRY)RTMemAllocZ(srcRootListHdr.cRoots * sizeof(SHCLROOTLISTENTRY)); + if (pRootList->paEntries) + { + for (uint32_t i = 0; i < srcRootListHdr.cRoots; i++) + { + SHCLROOTLISTENTRY *pEntry = &pRootList->paEntries[i]; + AssertPtr(pEntry); + + rc = ShClTransferRootListEntryInit(pEntry); + if (RT_SUCCESS(rc)) + rc = vbglR3ClipboardRootListEntryRead(pCtx, i, pEntry); + + if (RT_FAILURE(rc)) + break; + } + } + else + rc = VERR_NO_MEMORY; + } + } + + if (RT_SUCCESS(rc)) + { + *ppRootList = pRootList; + } + else + ShClTransferRootListFree(pRootList); + } + else + rc = VERR_NO_MEMORY; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a transfer status from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pEnmDir Where to store the transfer direction for the reported transfer. + * @param pReport Where to store the transfer (status) report. + */ +VBGLR3DECL(int) VbglR3ClipboarTransferStatusRecv(PVBGLR3SHCLCMDCTX pCtx, + PSHCLTRANSFERDIR pEnmDir, PSHCLTRANSFERREPORT pReport) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pReport, VERR_INVALID_POINTER); + AssertPtrReturn(pEnmDir, VERR_INVALID_POINTER); + + VBoxShClTransferStatusMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_TRANSFER_STATUS); + + Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_STATUS); + Msg.enmDir.SetUInt32(0); + Msg.enmStatus.SetUInt32(0); + Msg.rc.SetUInt32(0); + Msg.fFlags.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uContext.GetUInt64(&pCtx->idContext); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + rc = Msg.enmDir.GetUInt32((uint32_t *)pEnmDir); + AssertRC(rc); + } + if (RT_SUCCESS(rc)) + { + rc = Msg.enmStatus.GetUInt32(&pReport->uStatus); + AssertRC(rc); + } + if (RT_SUCCESS(rc)) + { + rc = Msg.rc.GetUInt32((uint32_t *)&pReport->rc); + AssertRC(rc); + } + if (RT_SUCCESS(rc)) + { + rc = Msg.fFlags.GetUInt32(&pReport->fFlags); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to a transfer report from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pTransfer Transfer of report to reply to. + * @param uStatus Tranfer status to reply. + * @param rcTransfer Result code (rc) to reply. + */ +VBGLR3DECL(int) VbglR3ClipboardTransferStatusReply(PVBGLR3SHCLCMDCTX pCtx, PSHCLTRANSFER pTransfer, + SHCLTRANSFERSTATUS uStatus, int rcTransfer) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pTransfer, VERR_INVALID_POINTER); + + RT_NOREF(pTransfer); + + VBoxShClReplyMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_TRANSFER_STATUS); + Msg.rc.SetUInt32((uint32_t )rcTransfer); /* int vs. uint32_t */ + Msg.pvPayload.SetPtr(NULL, 0); + + Msg.u.TransferStatus.enmStatus.SetUInt32((uint32_t)uStatus); + + LogFlowFunc(("%s\n", ShClTransferStatusToStr(uStatus))); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to read a root list header from the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pfRoots Where to store the root list header flags to use, requested by the host. + */ +VBGLR3DECL(int) VbglR3ClipboardRootListHdrReadReq(PVBGLR3SHCLCMDCTX pCtx, uint32_t *pfRoots) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pfRoots, VERR_INVALID_POINTER); + + VBoxShClRootListReadReqMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_ROOT_LIST_HDR_READ_REQ); + + Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_READ); + Msg.ReqParms.fRoots.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.fRoots.GetUInt32(pfRoots); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to a root list header request. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pRootListHdr Root lsit header to reply to the host. + */ +VBGLR3DECL(int) VbglR3ClipboardRootListHdrReadReply(PVBGLR3SHCLCMDCTX pCtx, PSHCLROOTLISTHDR pRootListHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pRootListHdr, VERR_INVALID_POINTER); + + VBoxShClRootListHdrMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_ROOT_LIST_HDR_WRITE, VBOX_SHCL_CPARMS_ROOT_LIST_HDR_WRITE); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.fRoots.SetUInt32(pRootListHdr->fRoots); + + Msg.cRoots.SetUInt32(pRootListHdr->cRoots); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to read a root list entry from the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param puIndex Where to return the index of the root list entry the host wants to read. + * @param pfInfo Where to return the read flags the host wants to use. + */ +VBGLR3DECL(int) VbglR3ClipboardRootListEntryReadReq(PVBGLR3SHCLCMDCTX pCtx, uint32_t *puIndex, uint32_t *pfInfo) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(puIndex, VERR_INVALID_POINTER); + AssertPtrReturn(pfInfo, VERR_INVALID_POINTER); + + VBoxShClRootListEntryReadReqMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_READ_REQ); + + Msg.Parms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_READ); + Msg.Parms.fInfo.SetUInt32(0); + Msg.Parms.uIndex.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.Parms.uContext.GetUInt64(&pCtx->idContext); AssertRC(rc); + if (RT_SUCCESS(rc)) + { + rc = Msg.Parms.fInfo.GetUInt32(pfInfo); + AssertRC(rc); + } + if (RT_SUCCESS(rc)) + { + rc = Msg.Parms.uIndex.GetUInt32(puIndex); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to a root list entry read request from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param uIndex Index of root list entry to reply. + * @param pEntry Actual root list entry to reply. + */ +VBGLR3DECL(int) VbglR3ClipboardRootListEntryReadReply(PVBGLR3SHCLCMDCTX pCtx, uint32_t uIndex, PSHCLROOTLISTENTRY pEntry) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pEntry, VERR_INVALID_POINTER); + + VBoxShClRootListEntryMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_ROOT_LIST_ENTRY_WRITE, VBOX_SHCL_CPARMS_ROOT_LIST_ENTRY_WRITE); + + Msg.Parms.uContext.SetUInt64(pCtx->idContext); + Msg.Parms.fInfo.SetUInt32(0); + Msg.Parms.uIndex.SetUInt32(uIndex); + + Msg.szName.SetPtr(pEntry->pszName, pEntry->cbName); + Msg.cbInfo.SetUInt32(pEntry->cbInfo); + Msg.pvInfo.SetPtr(pEntry->pvInfo, pEntry->cbInfo); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to open a list handle to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pOpenParms List open parameters to use for the open request. + * @param phList Where to return the list handle received from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListOpenSend(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTOPENPARMS pOpenParms, + PSHCLLISTHANDLE phList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pOpenParms, VERR_INVALID_POINTER); + AssertPtrReturn(phList, VERR_INVALID_POINTER); + + VBoxShClListOpenMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_OPEN, VBOX_SHCL_CPARMS_LIST_OPEN); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.fList.SetUInt32(0); + Msg.pvFilter.SetPtr(pOpenParms->pszFilter, pOpenParms->cbFilter); + Msg.pvPath.SetPtr(pOpenParms->pszPath, pOpenParms->cbPath); + Msg.uHandle.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uHandle.GetUInt64(phList); AssertRC(rc); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to open a list handle on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pOpenParms Where to store the open parameters the host wants to use for opening the list handle. + */ +VBGLR3DECL(int) VbglR3ClipboardListOpenRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTOPENPARMS pOpenParms) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pOpenParms, VERR_INVALID_POINTER); + + VBoxShClListOpenMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_OPEN); + + Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_OPEN); + Msg.fList.SetUInt32(0); + Msg.pvPath.SetPtr(pOpenParms->pszPath, pOpenParms->cbPath); + Msg.pvFilter.SetPtr(pOpenParms->pszFilter, pOpenParms->cbFilter); + Msg.uHandle.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + rc = Msg.fList.GetUInt32(&pOpenParms->fList); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to a list open request from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param rcReply Return code to reply to the host. + * @param hList List handle of (guest) list to reply to the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListOpenReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLLISTHANDLE hList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClReplyMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_LIST_OPEN); + Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */ + Msg.pvPayload.SetPtr(NULL, 0); + + Msg.u.ListOpen.uHandle.SetUInt64(hList); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to close a list handle on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param phList Where to store the list handle to close, received from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListCloseRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTHANDLE phList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(phList, VERR_INVALID_POINTER); + + VBoxShClListCloseMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_CLOSE); + + Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_CLOSE); + Msg.uHandle.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + { + rc = Msg.uHandle.GetUInt64(phList); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to a list handle close request from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param rcReply Return code to reply to the host. + * @param hList List handle the send the close reply for. + */ +VBGLR3DECL(int) VbglR3ClipboardListCloseReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLLISTHANDLE hList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClReplyMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_LIST_CLOSE); + Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */ + Msg.pvPayload.SetPtr(NULL, 0); + + Msg.u.ListOpen.uHandle.SetUInt64(hList); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to close a list handle to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hList List handle to request for closing on the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListCloseSend(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClListCloseMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_CLOSE, VBOX_SHCL_CPARMS_LIST_CLOSE); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.uHandle.SetUInt64(hList); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to read a list header to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hList List handle to read list header for. + * @param fFlags List header read flags to use. + * @param pListHdr Where to return the list header received from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListHdrRead(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList, uint32_t fFlags, + PSHCLLISTHDR pListHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pListHdr, VERR_INVALID_POINTER); + + VBoxShClListHdrMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_HDR_READ, VBOX_SHCL_CPARMS_LIST_HDR); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.uHandle.SetUInt64(hList); + Msg.ReqParms.fFlags.SetUInt32(fFlags); + + Msg.fFeatures.SetUInt32(0); + Msg.cbTotalSize.SetUInt32(0); + Msg.cTotalObjects.SetUInt64(0); + Msg.cbTotalSize.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.fFeatures.GetUInt32(&pListHdr->fFeatures); + if (RT_SUCCESS(rc)) + rc = Msg.cTotalObjects.GetUInt64(&pListHdr->cTotalObjects); + if (RT_SUCCESS(rc)) + rc = Msg.cbTotalSize.GetUInt64(&pListHdr->cbTotalSize); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to read a list header on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param phList Where to return the list handle to read list header for. + * @param pfFlags Where to return the List header read flags to use. + */ +VBGLR3DECL(int) VbglR3ClipboardListHdrReadRecvReq(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTHANDLE phList, uint32_t *pfFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(phList, VERR_INVALID_POINTER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + VBoxShClListHdrReadReqMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_HDR_READ_REQ); + + Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_READ); + Msg.ReqParms.uHandle.SetUInt64(0); + Msg.ReqParms.fFlags.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + rc = Msg.ReqParms.uHandle.GetUInt64(phList); + if (RT_SUCCESS(rc)) + rc = Msg.ReqParms.fFlags.GetUInt32(pfFlags); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends (writes) a list header to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hList List handle to write list header for. + * @param pListHdr List header to write. + */ +VBGLR3DECL(int) VbglR3ClipboardListHdrWrite(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList, + PSHCLLISTHDR pListHdr) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pListHdr, VERR_INVALID_POINTER); + + VBoxShClListHdrMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_HDR_WRITE, VBOX_SHCL_CPARMS_LIST_HDR); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.uHandle.SetUInt64(hList); + Msg.ReqParms.fFlags.SetUInt32(0); + + Msg.fFeatures.SetUInt32(0); + Msg.cbTotalSize.SetUInt32(pListHdr->fFeatures); + Msg.cTotalObjects.SetUInt64(pListHdr->cTotalObjects); + Msg.cbTotalSize.SetUInt64(pListHdr->cbTotalSize); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to read a list entry from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hList List handle to request to read a list entry for. + * @param pListEntry Where to return the list entry read from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardListEntryRead(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList, + PSHCLLISTENTRY pListEntry) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pListEntry, VERR_INVALID_POINTER); + + VBoxShClListEntryMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_ENTRY_READ, VBOX_SHCL_CPARMS_LIST_ENTRY); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.uHandle.SetUInt64(hList); + Msg.ReqParms.fInfo.SetUInt32(0); + + Msg.szName.SetPtr(pListEntry->pszName, pListEntry->cbName); + Msg.cbInfo.SetUInt32(pListEntry->cbInfo); + Msg.pvInfo.SetPtr(pListEntry->pvInfo, pListEntry->cbInfo); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.cbInfo.GetUInt32(&pListEntry->cbInfo); AssertRC(rc); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to read a list entry from the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param phList Where to return the list handle to read a list entry for. + * @param pfInfo Where to return the list read flags. + */ +VBGLR3DECL(int) VbglR3ClipboardListEntryReadRecvReq(PVBGLR3SHCLCMDCTX pCtx, PSHCLLISTHANDLE phList, uint32_t *pfInfo) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(phList, VERR_INVALID_POINTER); + AssertPtrReturn(pfInfo, VERR_INVALID_POINTER); + + VBoxShClListEntryReadReqMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_LIST_ENTRY_READ); + + Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ); + Msg.ReqParms.uHandle.SetUInt64(0); + Msg.ReqParms.fInfo.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.uHandle.GetUInt64(phList); + AssertRC(rc); + } + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.fInfo.GetUInt32(pfInfo); + AssertRC(rc); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends (writes) a list entry to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hList List handle to write a list etnry for. + * @param pListEntry List entry to write. + */ +VBGLR3DECL(int) VbglR3ClipboardListEntryWrite(PVBGLR3SHCLCMDCTX pCtx, SHCLLISTHANDLE hList, + PSHCLLISTENTRY pListEntry) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pListEntry, VERR_INVALID_POINTER); + + VBoxShClListEntryMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_LIST_ENTRY_WRITE, VBOX_SHCL_CPARMS_LIST_ENTRY); + + Msg.ReqParms.uContext.SetUInt64(pCtx->idContext); + Msg.ReqParms.uHandle.SetUInt64(hList); + Msg.ReqParms.fInfo.SetUInt32(pListEntry->fInfo); + + Msg.szName.SetPtr(pListEntry->pszName, pListEntry->cbName); + Msg.cbInfo.SetUInt32(pListEntry->cbInfo); + Msg.pvInfo.SetPtr(pListEntry->pvInfo, pListEntry->cbInfo); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to open an object on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pCreateParms Where to store the object open/create parameters received from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjOpenRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJOPENCREATEPARMS pCreateParms) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pCreateParms, VERR_INVALID_POINTER); + + VBoxShClObjOpenMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_OBJ_OPEN); + + Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_OPEN); + Msg.uHandle.SetUInt64(0); + Msg.szPath.SetPtr(pCreateParms->pszPath, pCreateParms->cbPath); + Msg.fCreate.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + rc = Msg.fCreate.GetUInt32(&pCreateParms->fCreate); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies a host request to open an object. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param rcReply Return code to reply to the host. + * @param hObj Object handle of opened object to reply to the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjOpenReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLOBJHANDLE hObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClReplyMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_OBJ_OPEN); + Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */ + Msg.pvPayload.SetPtr(NULL, 0); + + Msg.u.ObjOpen.uHandle.SetUInt64(hObj); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends an object open request to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param pCreateParms Object open/create parameters to use for opening the object on the host. + * @param phObj Where to return the object handle from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjOpenSend(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJOPENCREATEPARMS pCreateParms, + PSHCLOBJHANDLE phObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pCreateParms, VERR_INVALID_POINTER); + AssertPtrReturn(phObj, VERR_INVALID_POINTER); + + VBoxShClObjOpenMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_OBJ_OPEN, VBOX_SHCL_CPARMS_OBJ_OPEN); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.uHandle.SetUInt64(0); + Msg.szPath.SetPtr((void *)pCreateParms->pszPath, pCreateParms->cbPath); + Msg.fCreate.SetUInt32(pCreateParms->fCreate); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + Msg.uHandle.GetUInt64(phObj); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to close an object on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param phObj Where to return the object handle to close from the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjCloseRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJHANDLE phObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(phObj, VERR_INVALID_POINTER); + + VBoxShClObjCloseMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_OBJ_CLOSE); + + Msg.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_CLOSE); + Msg.uHandle.SetUInt64(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + rc = Msg.uHandle.GetUInt64(phObj); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Replies to an object open request from the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param rcReply Return code to reply to the host. + * @param hObj Object handle to reply to the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjCloseReply(PVBGLR3SHCLCMDCTX pCtx, int rcReply, SHCLOBJHANDLE hObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClReplyMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_REPLY, VBOX_SHCL_CPARMS_REPLY_MIN + 1); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.enmType.SetUInt32(VBOX_SHCL_REPLYMSGTYPE_OBJ_CLOSE); + Msg.rc.SetUInt32((uint32_t)rcReply); /** int vs. uint32_t */ + Msg.pvPayload.SetPtr(NULL, 0); + + Msg.u.ObjClose.uHandle.SetUInt64(hObj); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to close an object to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hObj Object handle to close on the host. + */ +VBGLR3DECL(int) VbglR3ClipboardObjCloseSend(PVBGLR3SHCLCMDCTX pCtx, SHCLOBJHANDLE hObj) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + VBoxShClObjCloseMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_OBJ_CLOSE, VBOX_SHCL_CPARMS_OBJ_CLOSE); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.uHandle.SetUInt64(hObj); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Receives a host request to read from an object on the guest. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param phObj Where to return the object handle to read from. + * @param pcbToRead Where to return the amount (in bytes) to read. + * @param pfFlags Where to return the read flags. + */ +VBGLR3DECL(int) VbglR3ClipboardObjReadRecv(PVBGLR3SHCLCMDCTX pCtx, PSHCLOBJHANDLE phObj, uint32_t *pcbToRead, + uint32_t *pfFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(phObj, VERR_INVALID_POINTER); + AssertPtrReturn(pcbToRead, VERR_INVALID_POINTER); + AssertPtrReturn(pfFlags, VERR_INVALID_POINTER); + + VBoxShClObjReadReqMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_MSG_GET, VBOX_SHCL_CPARMS_OBJ_READ_REQ); + + Msg.ReqParms.uContext.SetUInt64(VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_READ); + Msg.ReqParms.uHandle.SetUInt64(0); + Msg.ReqParms.cbToRead.SetUInt32(0); + Msg.ReqParms.fRead.SetUInt32(0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + rc = Msg.ReqParms.uContext.GetUInt64(&pCtx->idContext); + if (RT_SUCCESS(rc)) + rc = Msg.ReqParms.uHandle.GetUInt64(phObj); + if (RT_SUCCESS(rc)) + rc = Msg.ReqParms.cbToRead.GetUInt32(pcbToRead); + if (RT_SUCCESS(rc)) + rc = Msg.ReqParms.fRead.GetUInt32(pfFlags); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to read from an object to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hObj Object handle of object to read from. + * @param pvData Buffer where to store the read object data. + * @param cbData Size (in bytes) of buffer. + * @param pcbRead Where to store the amount (in bytes) read from the object. + */ +VBGLR3DECL(int) VbglR3ClipboardObjReadSend(PVBGLR3SHCLCMDCTX pCtx, SHCLOBJHANDLE hObj, + void *pvData, uint32_t cbData, uint32_t *pcbRead) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + /* pcbRead is optional. */ + + VBoxShClObjReadWriteMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_OBJ_READ, VBOX_SHCL_CPARMS_OBJ_READ); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.uHandle.SetUInt64(hObj); + Msg.cbData.SetUInt32(cbData); + Msg.pvData.SetPtr(pvData, cbData); + Msg.cbChecksum.SetUInt32(0); + Msg.pvChecksum.SetPtr(NULL, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Add checksum support. */ + + if (pcbRead) + { + rc = Msg.cbData.GetUInt32(pcbRead); AssertRC(rc); + AssertReturn(cbData >= *pcbRead, VERR_TOO_MUCH_DATA); + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends a request to write to an object to the host. + * + * @returns VBox status code. + * @param pCtx Shared Clipboard command context to use for the connection. + * @param hObj Object handle of object to write to. + * @param pvData Buffer of data to write to object. + * @param cbData Size (in bytes) of buffer. + * @param pcbWritten Where to store the amount (in bytes) written to the object. + */ +VBGLR3DECL(int) VbglR3ClipboardObjWriteSend(PVBGLR3SHCLCMDCTX pCtx, SHCLOBJHANDLE hObj, + void *pvData, uint32_t cbData, uint32_t *pcbWritten) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + /* cbData can be 0. */ + /* pcbWritten is optional. */ + + VBoxShClObjReadWriteMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->idClient, + VBOX_SHCL_GUEST_FN_OBJ_WRITE, VBOX_SHCL_CPARMS_OBJ_WRITE); + + Msg.uContext.SetUInt64(pCtx->idContext); + Msg.uHandle.SetUInt64(hObj); + Msg.pvData.SetPtr(pvData, cbData); + Msg.pvChecksum.SetPtr(NULL, 0); + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + /** @todo Add checksum support. */ + + if (pcbWritten) + *pcbWritten = cbData; /** @todo For now return all as being written. */ + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + + +/********************************************************************************************************************************* +* Transfer interface implementations * +*********************************************************************************************************************************/ + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceGetRoots(PSHCLTXPROVIDERCTX pCtx, PSHCLROOTLIST *ppRootList) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardRootListRead(pCmdCtx, ppRootList); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListOpen(PSHCLTXPROVIDERCTX pCtx, PSHCLLISTOPENPARMS pOpenParms, + PSHCLLISTHANDLE phList) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardListOpenSend(pCmdCtx, pOpenParms, phList); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListClose(PSHCLTXPROVIDERCTX pCtx, SHCLLISTHANDLE hList) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardListCloseSend(pCmdCtx, hList); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListHdrRead(PSHCLTXPROVIDERCTX pCtx, + SHCLLISTHANDLE hList, PSHCLLISTHDR pListHdr) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = ShClTransferListHdrInit(pListHdr); + if (RT_SUCCESS(rc)) + { + if (RT_SUCCESS(rc)) + { + rc = VbglR3ClipboardListHdrRead(pCmdCtx, hList, 0 /* fFlags */, pListHdr); + } + else + ShClTransferListHdrDestroy(pListHdr); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceListEntryRead(PSHCLTXPROVIDERCTX pCtx, + SHCLLISTHANDLE hList, PSHCLLISTENTRY pEntry) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardListEntryRead(pCmdCtx, hList, pEntry); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceObjOpen(PSHCLTXPROVIDERCTX pCtx, + PSHCLOBJOPENCREATEPARMS pCreateParms, PSHCLOBJHANDLE phObj) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardObjOpenSend(pCmdCtx, pCreateParms, phObj); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceObjClose(PSHCLTXPROVIDERCTX pCtx, SHCLOBJHANDLE hObj) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + int rc = VbglR3ClipboardObjCloseSend(pCmdCtx, hObj); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +static DECLCALLBACK(int) vbglR3ClipboardTransferIfaceObjRead(PSHCLTXPROVIDERCTX pCtx, + SHCLOBJHANDLE hObj, void *pvData, uint32_t cbData, + uint32_t fFlags, uint32_t *pcbRead) +{ + LogFlowFuncEnter(); + + PVBGLR3SHCLCMDCTX pCmdCtx = (PVBGLR3SHCLCMDCTX)pCtx->pvUser; + AssertPtr(pCmdCtx); + + RT_NOREF(fFlags); /* Not used yet. */ + + int rc = VbglR3ClipboardObjReadSend(pCmdCtx, hObj, pvData, cbData, pcbRead); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Starts a transfer on the guest side. + * + * @returns VBox status code. + * @param pCmdCtx Command context to use. + * @param pTransferCtx Transfer context to create transfer for. + * @param uTransferID ID to use for transfer to start. + * @param enmDir Direction of transfer to start. + * @param enmSource Source of transfer to start. + * @param ppTransfer Where to return the transfer object on success. Optional. + */ +static int vbglR3ClipboardTransferStart(PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCTX pTransferCtx, + SHCLTRANSFERID uTransferID, SHCLTRANSFERDIR enmDir, SHCLSOURCE enmSource, + PSHCLTRANSFER *ppTransfer) +{ + PSHCLTRANSFER pTransfer; + int rc = ShClTransferCreate(&pTransfer); + if (RT_SUCCESS(rc)) + { + ShClTransferSetCallbacks(pTransfer, &pCmdCtx->Transfers.Callbacks); + + rc = ShClTransferInit(pTransfer, enmDir, enmSource); + if (RT_SUCCESS(rc)) + { + rc = ShClTransferCtxTransferRegisterById(pTransferCtx, pTransfer, uTransferID); + if (RT_SUCCESS(rc)) + { + /* If this is a read transfer (reading data from host), set the interface to use + * our VbglR3 routines here. */ + if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE) + { + SHCLTXPROVIDERCREATIONCTX creationCtx; + RT_ZERO(creationCtx); + + creationCtx.Interface.pfnRootsGet = vbglR3ClipboardTransferIfaceGetRoots; + + creationCtx.Interface.pfnListOpen = vbglR3ClipboardTransferIfaceListOpen; + creationCtx.Interface.pfnListClose = vbglR3ClipboardTransferIfaceListClose; + creationCtx.Interface.pfnListHdrRead = vbglR3ClipboardTransferIfaceListHdrRead; + creationCtx.Interface.pfnListEntryRead = vbglR3ClipboardTransferIfaceListEntryRead; + + creationCtx.Interface.pfnObjOpen = vbglR3ClipboardTransferIfaceObjOpen; + creationCtx.Interface.pfnObjClose = vbglR3ClipboardTransferIfaceObjClose; + creationCtx.Interface.pfnObjRead = vbglR3ClipboardTransferIfaceObjRead; + + creationCtx.pvUser = pCmdCtx; + + rc = ShClTransferSetProviderIface(pTransfer, &creationCtx); + } + + if (RT_SUCCESS(rc)) + rc = ShClTransferStart(pTransfer); + } + + if (RT_FAILURE(rc)) + ShClTransferCtxTransferUnregister(pTransferCtx, uTransferID); + } + } + + if (RT_SUCCESS(rc)) + { + if (ppTransfer) + *ppTransfer = pTransfer; + + LogRel2(("Shared Clipboard: Transfer ID=%RU32 (%s %s) successfully started\n", + uTransferID, + enmDir == SHCLTRANSFERDIR_FROM_REMOTE ? "reading from" : "writing to", + enmSource == SHCLSOURCE_LOCAL ? "local" : "remote")); + } + else + LogRel(("Shared Clipboard: Unable to start transfer ID=%RU32, rc=%Rrc\n", uTransferID, rc)); + + /* Send a reply in any case. */ + int rc2 = VbglR3ClipboardTransferStatusReply(pCmdCtx, pTransfer, + RT_SUCCESS(rc) + ? SHCLTRANSFERSTATUS_STARTED : SHCLTRANSFERSTATUS_ERROR, rc); + if (RT_SUCCESS(rc)) + rc = rc2; + + if (RT_FAILURE(rc)) + { + ShClTransferDestroy(pTransfer); + pTransfer = NULL; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Stops a transfer on the guest side. + * + * @returns VBox status code, or VERR_NOT_FOUND if transfer has not been found. + * @param pCmdCtx Command context to use. + * @param pTransferCtx Transfer context to stop transfer for. + * @param uTransferID ID of transfer to stop. + */ +static int vbglR3ClipboardTransferStop(PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCTX pTransferCtx, + SHCLTRANSFERID uTransferID) +{ + int rc; + + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, uTransferID); + if (pTransfer) + { + rc = ShClTransferCtxTransferUnregister(pTransferCtx, uTransferID); + if (RT_SUCCESS(rc)) + { + LogRel2(("Shared Clipboard: Transfer ID=%RU32 successfully stopped\n", uTransferID)); + } + else + LogRel(("Shared Clipboard: Unable to stop transfer ID=%RU32, rc=%Rrc\n", uTransferID, rc)); + + /* Send a reply in any case. */ + int rc2 = VbglR3ClipboardTransferStatusReply(pCmdCtx, pTransfer, + RT_SUCCESS(rc) + ? SHCLTRANSFERSTATUS_STOPPED : SHCLTRANSFERSTATUS_ERROR, rc); + AssertRC(rc2); + } + else + rc = VERR_NOT_FOUND; + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sets transfer callbacks of a Shared Clipboard command context. + * + * @param pCmdCtx Command context to set callbacks for. + * @param pCallbacks Pointer to callback table to set. + */ +VBGLR3DECL(void) VbglR3ClipboardTransferSetCallbacks(PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCALLBACKTABLE pCallbacks) +{ + AssertPtrReturnVoid(pCmdCtx); + AssertPtrReturnVoid(pCallbacks); + + ShClTransferCopyCallbacks(&pCmdCtx->Transfers.Callbacks, pCallbacks); +} + +VBGLR3DECL(int) VbglR3ClipboardEventGetNextEx(uint32_t idMsg, uint32_t cParms, + PVBGLR3SHCLCMDCTX pCmdCtx, PSHCLTRANSFERCTX pTransferCtx, + PVBGLR3CLIPBOARDEVENT pEvent) +{ + AssertPtrReturn(pCmdCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pTransferCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pEvent, VERR_INVALID_POINTER); + + LogFunc(("Handling idMsg=%RU32 (%s), cParms=%RU32\n", idMsg, ShClHostMsgToStr(idMsg), cParms)); + + int rc; + if (!pCmdCtx->fUseLegacyProtocol) + { + bool fErrorSent = false; /* Whether an error has been reported back to the host already. */ + + switch (idMsg) + { + case VBOX_SHCL_HOST_MSG_TRANSFER_STATUS: + { + SHCLTRANSFERDIR enmDir; + SHCLTRANSFERREPORT transferReport; + rc = VbglR3ClipboarTransferStatusRecv(pCmdCtx, &enmDir, &transferReport); + if (RT_SUCCESS(rc)) + { + const SHCLTRANSFERID uTransferID = VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext); + + LogFlowFunc(("[Transfer %RU32] enmDir=%RU32, status=%s\n", + uTransferID, enmDir, ShClTransferStatusToStr(transferReport.uStatus))); + + switch (transferReport.uStatus) + { + case SHCLTRANSFERSTATUS_INITIALIZED: + RT_FALL_THROUGH(); + case SHCLTRANSFERSTATUS_STARTED: + { + SHCLSOURCE enmSource = SHCLSOURCE_INVALID; + + /* The host announces the transfer direction from its point of view, so inverse the direction here. */ + if (enmDir == SHCLTRANSFERDIR_TO_REMOTE) + { + enmDir = SHCLTRANSFERDIR_FROM_REMOTE; + enmSource = SHCLSOURCE_REMOTE; + } + else if (enmDir == SHCLTRANSFERDIR_FROM_REMOTE) + { + enmDir = SHCLTRANSFERDIR_TO_REMOTE; + enmSource = SHCLSOURCE_LOCAL; + } + else + AssertFailedBreakStmt(rc = VERR_INVALID_PARAMETER); + + rc = vbglR3ClipboardTransferStart(pCmdCtx, pTransferCtx, uTransferID, + enmDir, enmSource, NULL /* ppTransfer */); + break; + } + + case SHCLTRANSFERSTATUS_STOPPED: + RT_FALL_THROUGH(); + case SHCLTRANSFERSTATUS_CANCELED: + RT_FALL_THROUGH(); + case SHCLTRANSFERSTATUS_KILLED: + RT_FALL_THROUGH(); + case SHCLTRANSFERSTATUS_ERROR: + { + rc = vbglR3ClipboardTransferStop(pCmdCtx, pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + break; + } + + default: + rc = VERR_NOT_SUPPORTED; + break; + } + + if (RT_SUCCESS(rc)) + { + pEvent->u.TransferStatus.enmDir = enmDir; + pEvent->u.TransferStatus.Report = transferReport; + pEvent->u.TransferStatus.uID = VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext); + + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_TRANSFER_STATUS; + + LogRel2(("Shared Clipboard: Received status %s (rc=%Rrc) for transfer ID=%RU32\n", + ShClTransferStatusToStr(pEvent->u.TransferStatus.Report.uStatus), pEvent->u.TransferStatus.Report.rc, + pEvent->u.TransferStatus.uID)); + } + } + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_HDR_READ: + { + uint32_t fRoots; + rc = VbglR3ClipboardRootListHdrReadReq(pCmdCtx, &fRoots); + + /** @todo Validate / handle fRoots. */ + + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + SHCLROOTLISTHDR rootListHdr; + RT_ZERO(rootListHdr); + + rootListHdr.cRoots = ShClTransferRootsCount(pTransfer); + + LogFlowFunc(("cRoots=%RU32\n", rootListHdr.cRoots)); + + rc = VbglR3ClipboardRootListHdrReadReply(pCmdCtx, &rootListHdr); + } + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_ROOT_LIST_ENTRY_READ: + { + uint32_t uIndex; + uint32_t fInfo; + rc = VbglR3ClipboardRootListEntryReadReq(pCmdCtx, &uIndex, &fInfo); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + SHCLROOTLISTENTRY rootListEntry; + rc = ShClTransferRootsEntry(pTransfer, uIndex, &rootListEntry); + if (RT_SUCCESS(rc)) + rc = VbglR3ClipboardRootListEntryReadReply(pCmdCtx, uIndex, &rootListEntry); + } + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_OPEN: + { + SHCLLISTOPENPARMS openParmsList; + rc = ShClTransferListOpenParmsInit(&openParmsList); + if (RT_SUCCESS(rc)) + { + rc = VbglR3ClipboardListOpenRecv(pCmdCtx, &openParmsList); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + LogFlowFunc(("pszPath=%s\n", openParmsList.pszPath)); + + SHCLLISTHANDLE hList = SHCLLISTHANDLE_INVALID; + rc = ShClTransferListOpen(pTransfer, &openParmsList, &hList); + + /* Reply in any case. */ + int rc2 = VbglR3ClipboardListOpenReply(pCmdCtx, rc, hList); + AssertRC(rc2); + } + + ShClTransferListOpenParmsDestroy(&openParmsList); + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_CLOSE: + { + SHCLLISTHANDLE hList; + rc = VbglR3ClipboardListCloseRecv(pCmdCtx, &hList); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + rc = ShClTransferListClose(pTransfer, hList); + + /* Reply in any case. */ + int rc2 = VbglR3ClipboardListCloseReply(pCmdCtx, rc, hList); + AssertRC(rc2); + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_HDR_READ: + { + /** @todo Handle filter + list features. */ + + SHCLLISTHANDLE hList = SHCLLISTHANDLE_INVALID; + uint32_t fFlags = 0; + rc = VbglR3ClipboardListHdrReadRecvReq(pCmdCtx, &hList, &fFlags); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + SHCLLISTHDR hdrList; + rc = ShClTransferListGetHeader(pTransfer, hList, &hdrList); + if (RT_SUCCESS(rc)) + { + rc = VbglR3ClipboardListHdrWrite(pCmdCtx, hList, &hdrList); + + ShClTransferListHdrDestroy(&hdrList); + } + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ: + { + LogFlowFunc(("VBOX_SHCL_HOST_MSG_TRANSFER_LIST_ENTRY_READ\n")); + + SHCLLISTENTRY entryList; + rc = ShClTransferListEntryInit(&entryList); + if (RT_SUCCESS(rc)) + { + SHCLLISTHANDLE hList; + uint32_t fInfo; + rc = VbglR3ClipboardListEntryReadRecvReq(pCmdCtx, &hList, &fInfo); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + rc = ShClTransferListRead(pTransfer, hList, &entryList); + if (RT_SUCCESS(rc)) + { + PSHCLFSOBJINFO pObjInfo = (PSHCLFSOBJINFO)entryList.pvInfo; + Assert(entryList.cbInfo == sizeof(SHCLFSOBJINFO)); + + RT_NOREF(pObjInfo); + + LogFlowFunc(("\t%s (%RU64 bytes)\n", entryList.pszName, pObjInfo->cbObject)); + + rc = VbglR3ClipboardListEntryWrite(pCmdCtx, hList, &entryList); + } + } + + ShClTransferListEntryDestroy(&entryList); + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_OPEN: + { + SHCLOBJOPENCREATEPARMS openParms; + rc = ShClTransferObjOpenParmsInit(&openParms); + if (RT_SUCCESS(rc)) + { + rc = VbglR3ClipboardObjOpenRecv(pCmdCtx, &openParms); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + SHCLOBJHANDLE hObj; + rc = ShClTransferObjOpen(pTransfer, &openParms, &hObj); + + /* Reply in any case. */ + int rc2 = VbglR3ClipboardObjOpenReply(pCmdCtx, rc, hObj); + AssertRC(rc2); + } + + ShClTransferObjOpenParmsDestroy(&openParms); + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_CLOSE: + { + SHCLOBJHANDLE hObj; + rc = VbglR3ClipboardObjCloseRecv(pCmdCtx, &hObj); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + rc = ShClTransferObjClose(pTransfer, hObj); + + /* Reply in any case. */ + int rc2 = VbglR3ClipboardObjCloseReply(pCmdCtx, rc, hObj); + AssertRC(rc2); + } + + break; + } + + case VBOX_SHCL_HOST_MSG_TRANSFER_OBJ_READ: + { + SHCLOBJHANDLE hObj; + uint32_t cbBuf; + uint32_t fFlags; + rc = VbglR3ClipboardObjReadRecv(pCmdCtx, &hObj, &cbBuf, &fFlags); + if (RT_SUCCESS(rc)) + { + PSHCLTRANSFER pTransfer = ShClTransferCtxGetTransferById(pTransferCtx, + VBOX_SHCL_CONTEXTID_GET_TRANSFER(pCmdCtx->idContext)); + AssertPtrBreakStmt(pTransfer, rc = VERR_NOT_FOUND); + + AssertBreakStmt(pCmdCtx->Transfers.cbChunkSize, rc = VERR_INVALID_PARAMETER); + + const uint32_t cbToRead = RT_MIN(cbBuf, pCmdCtx->Transfers.cbChunkSize); + + LogFlowFunc(("hObj=%RU64, cbBuf=%RU32, fFlags=0x%x -> cbChunkSize=%RU32, cbToRead=%RU32\n", + hObj, cbBuf, fFlags, pCmdCtx->Transfers.cbChunkSize, cbToRead)); + + void *pvBuf = RTMemAlloc(cbToRead); + if (pvBuf) + { + uint32_t cbRead; + rc = ShClTransferObjRead(pTransfer, hObj, pvBuf, cbToRead, fFlags, &cbRead); + if (RT_SUCCESS(rc)) + rc = VbglR3ClipboardObjWriteSend(pCmdCtx, hObj, pvBuf, cbRead, NULL /* pcbWritten */); + + RTMemFree(pvBuf); + } + else + rc = VERR_NO_MEMORY; + } + + break; + } + + default: + { + rc = VbglR3ClipboardEventGetNext(idMsg, cParms, pCmdCtx, pEvent); + if (RT_FAILURE(rc)) + fErrorSent = true; + break; + } + } + + if ( !fErrorSent + && RT_FAILURE(rc)) + { + /* Report error back to the host. */ + int rc2 = VbglR3ClipboardWriteError(pCmdCtx->idClient, rc); + AssertRC(rc2); + } + } + else + { + /* + * This builds on what we did in VbglR3ClipboardMsgPeekWait, so + * !HACK ALERT! cParms is the format flag or flags. + */ + rc = VINF_SUCCESS; + switch (idMsg) + { + case VBOX_SHCL_HOST_MSG_FORMATS_REPORT: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS; + pEvent->u.fReportedFormats = cParms; + break; + + case VBOX_SHCL_HOST_MSG_READ_DATA: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA; + pEvent->u.fReadData = cParms; + break; + + case VBOX_SHCL_HOST_MSG_QUIT: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_QUIT; + break; + + default: + AssertMsgFailed(("%u (%#x)\n", idMsg, idMsg)); + rc = VERR_NOT_SUPPORTED; + break; + } + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +#endif /* VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS */ + +VBGLR3DECL(int) VbglR3ClipboardEventGetNext(uint32_t idMsg, uint32_t cParms, PVBGLR3SHCLCMDCTX pCtx, PVBGLR3CLIPBOARDEVENT pEvent) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pEvent, VERR_INVALID_POINTER); + + RT_NOREF(cParms); + + int rc; + if (!pCtx->fUseLegacyProtocol) + { + LogFunc(("Handling idMsg=%RU32 (%s)\n", idMsg, ShClHostMsgToStr(idMsg))); + switch (idMsg) + { + case VBOX_SHCL_HOST_MSG_FORMATS_REPORT: + { + rc = vbglR3ClipboardFormatsReportRecv(pCtx, &pEvent->u.fReportedFormats); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS; + break; + } + + case VBOX_SHCL_HOST_MSG_READ_DATA_CID: + { + rc = vbglR3ClipboardFetchReadDataCid(pCtx, &pEvent->u.fReadData); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA; + break; + } + + case VBOX_SHCL_HOST_MSG_READ_DATA: + { + rc = vbglR3ClipboardFetchReadData(pCtx, &pEvent->u.fReadData); + if (RT_SUCCESS(rc)) + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA; + break; + } + + case VBOX_SHCL_HOST_MSG_QUIT: + { + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_QUIT; + rc = VINF_SUCCESS; + break; + } + + default: + { + /** @todo r=bird: BUGBUG - need a skip command here! */ + rc = VERR_NOT_SUPPORTED; + break; + } + } + + if (RT_SUCCESS(rc)) + { + /* Copy over our command context to the event. */ + pEvent->cmdCtx = *pCtx; + } + else + { + /* Report error back to the host. */ + int rc2 = VbglR3ClipboardWriteError(pCtx->idClient, rc); + AssertRC(rc2); + } + } + else + { + /* + * This builds on what we did in VbglR3ClipboardMsgPeekWait, so + * !HACK ALERT! cParms is the format flag or flags. + */ + rc = VINF_SUCCESS; + switch (idMsg) + { + case VBOX_SHCL_HOST_MSG_FORMATS_REPORT: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_REPORT_FORMATS; + pEvent->u.fReportedFormats = cParms; + break; + + case VBOX_SHCL_HOST_MSG_READ_DATA: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_READ_DATA; + pEvent->u.fReadData = cParms; + break; + + case VBOX_SHCL_HOST_MSG_QUIT: + pEvent->enmType = VBGLR3CLIPBOARDEVENTTYPE_QUIT; + break; + + default: + AssertMsgFailed(("%u (%#x)\n", idMsg, idMsg)); + rc = VERR_NOT_SUPPORTED; + break; + } + pEvent->cmdCtx = *pCtx; + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Frees (destroys) a formerly allocated Shared Clipboard event. + * + * @returns IPRT status code. + * @param pEvent Event to free (destroy). + */ +VBGLR3DECL(void) VbglR3ClipboardEventFree(PVBGLR3CLIPBOARDEVENT pEvent) +{ + if (!pEvent) + return; + + /* Some messages require additional cleanup. */ + switch (pEvent->enmType) + { + default: + break; + } + + RTMemFree(pEvent); + pEvent = NULL; +} + +/** + * Reports (advertises) guest clipboard formats to the host. + * + * Legacy function, do not use anymore. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param fFormats The formats to report. + */ +VBGLR3DECL(int) VbglR3ClipboardReportFormats(HGCMCLIENTID idClient, uint32_t fFormats) +{ + struct + { + VBGLIOCHGCMCALL Hdr; + VBoxShClParmReportFormats Parms; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_REPORT_FORMATS, VBOX_SHCL_CPARMS_REPORT_FORMATS); + VbglHGCMParmUInt32Set(&Msg.Parms.f32Formats, fFormats); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends guest clipboard data to the host. + * + * Legacy function kept for compatibility, do not use anymore. + * + * This is usually called in reply to a VBOX_SHCL_HOST_MSG_READ_DATA message + * from the host. + * + * @returns VBox status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param fFormat The format of the data. + * @param pvData Pointer to the data to send. Can be NULL if @a cbData + * is zero. + * @param cbData Number of bytes of data to send. Zero is valid. + */ +VBGLR3DECL(int) VbglR3ClipboardWriteData(HGCMCLIENTID idClient, uint32_t fFormat, void *pv, uint32_t cb) +{ + LogFlowFuncEnter(); + + struct + { + VBGLIOCHGCMCALL Hdr; + VBoxShClParmDataWriteOld Parms; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, VBOX_SHCL_GUEST_FN_DATA_WRITE, VBOX_SHCL_CPARMS_DATA_WRITE_OLD); + VbglHGCMParmUInt32Set(&Msg.Parms.f32Format, fFormat); + VbglHGCMParmPtrSet(&Msg.Parms.pData, pv, cb); + + int rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Sends guest clipboard data to the host. + * + * This is usually called in reply to a VBOX_SHCL_HOST_MSG_READ_DATA message + * from the host. + * + * @returns VBox status code. + * @param pCtx The command context returned by VbglR3ClipboardConnectEx(). + * @param fFormat Clipboard format to send. + * @param pvData Pointer to the data to send. Can be NULL if @a cbData + * is zero. + * @param cbData Number of bytes of data to send. Zero is valid. + */ +VBGLR3DECL(int) VbglR3ClipboardWriteDataEx(PVBGLR3SHCLCMDCTX pCtx, SHCLFORMAT fFormat, void *pvData, uint32_t cbData) +{ + LogFlowFunc(("ENTER: fFormat=%#x pvData=%p cbData=%#x\n", fFormat, pvData, cbData)); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + if (cbData > 0) + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + + int rc; + if (pCtx->fUseLegacyProtocol) + rc = VbglR3ClipboardWriteData(pCtx->idClient, fFormat, pvData, cbData); + else + { + struct + { + VBGLIOCHGCMCALL Hdr; + VBoxShClParmDataWrite Parms; + } Msg; + + VBGL_HGCM_HDR_INIT(&Msg.Hdr, pCtx->idClient, VBOX_SHCL_GUEST_FN_DATA_WRITE, VBOX_SHCL_CPARMS_DATA_WRITE); + Msg.Parms.id64Context.SetUInt64(pCtx->idContext); + Msg.Parms.f32Format.SetUInt32(fFormat); + Msg.Parms.pData.SetPtr(pvData, cbData); + + LogFlowFunc(("CID=%RU32\n", pCtx->idContext)); + + rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg)); + } + + LogFlowFuncLeaveRC(rc); + return rc; +} + +/** + * Writes an error to the host. + * + * @returns IPRT status code. + * @param idClient The client id returned by VbglR3ClipboardConnect(). + * @param rcErr Error (IPRT-style) to send. + */ +VBGLR3DECL(int) VbglR3ClipboardWriteError(HGCMCLIENTID idClient, int rcErr) +{ + AssertReturn(idClient, VERR_INVALID_PARAMETER); + + VBoxShClWriteErrorMsg Msg; + VBGL_HGCM_HDR_INIT(&Msg.hdr, idClient, VBOX_SHCL_GUEST_FN_ERROR, VBOX_SHCL_CPARMS_ERROR); + + /** @todo Context ID not used yet. */ + Msg.uContext.SetUInt64(0); + Msg.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */ + + int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg)); + + if (RT_FAILURE(rc)) + LogFlowFunc(("Sending error %Rrc failed with rc=%Rrc\n", rcErr, rc)); + if (rc == VERR_NOT_SUPPORTED) + rc = VINF_SUCCESS; + + if (RT_FAILURE(rc)) + LogRel(("Shared Clipboard: Reporting error %Rrc to the host failed with %Rrc\n", rcErr, rc)); + + LogFlowFuncLeaveRC(rc); + return rc; +} + |