/* $Id: VBoxGuestR3LibDragAndDrop.cpp 42261 2012-07-20 13:27:47Z vboxsync $ */ /** @file * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Drag & Drop. */ /* * Copyright (C) 2011-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the * VirtualBox OSE 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. */ /******************************************************************************* * Header Files * *******************************************************************************/ #include #include #include #include #include #include #include #include "VBGLR3Internal.h" #include "VBox/HostServices/DragAndDropSvc.h" #define VERBOSE 1 #if defined(VERBOSE) && defined(DEBUG_poetzsch) # include # define DO(s) RTPrintf s #else # define DO(s) do {} while(0) //# define DO(s) Log s #endif /* Here all the communication with the host over HGCM is handled platform * neutral. Also the receiving of URIs content (directory trees and files) is * done here. So the platform code of the guests, should not take care of that. * * Todo: * - Sending dirs/files in the G->H case * - Maybe the EOL converting of text mime-types (not fully sure, eventually * better done on the host side) */ /* Not really used at the moment (only one client is possible): */ uint32_t g_clientId = 0; /****************************************************************************** * Private internal functions * ******************************************************************************/ static int vbglR3DnDCreateDropDir(char* pszDropDir, size_t cbSize) { /* Validate input */ AssertPtrReturn(pszDropDir, VERR_INVALID_POINTER); AssertReturn(cbSize, VERR_INVALID_PARAMETER); /* Get the users document directory (usually $HOME/Documents). */ int rc = RTPathUserDocuments(pszDropDir, cbSize); if (RT_FAILURE(rc)) return rc; /* Append our base drop directory. */ rc = RTPathAppend(pszDropDir, cbSize, "VirtualBox Dropped Files"); if (RT_FAILURE(rc)) return rc; /* Create it when necessary. */ if (!RTDirExists(pszDropDir)) { rc = RTDirCreateFullPath(pszDropDir, RTFS_UNIX_IRWXU); if (RT_FAILURE(rc)) return rc; } /* The actually drop directory consist of the current time stamp and a * unique number when necessary. */ char pszTime[64]; RTTIMESPEC time; if (!RTTimeSpecToString(RTTimeNow(&time), pszTime, sizeof(pszTime))) return VERR_BUFFER_OVERFLOW; rc = RTPathAppend(pszDropDir, cbSize, pszTime); if (RT_FAILURE(rc)) return rc; /* Create it (only accessible by the current user) */ return RTDirCreateUniqueNumbered(pszDropDir, cbSize, RTFS_UNIX_IRWXU, 3, '-'); } static int vbglR3DnDQueryNextHostMessageType(uint32_t uClientId, uint32_t *puMsg, uint32_t *pcParms, bool fWait) { /* Validate input */ AssertPtrReturn(puMsg, VERR_INVALID_POINTER); AssertPtrReturn(pcParms, VERR_INVALID_POINTER); /* Initialize header */ DragAndDropSvc::VBOXDNDNEXTMSGMSG Msg; RT_ZERO(Msg); Msg.hdr.result = VERR_WRONG_ORDER; Msg.hdr.u32ClientID = uClientId; Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG; Msg.hdr.cParms = 3; /* Initialize parameter */ Msg.msg.SetUInt32(0); Msg.num_parms.SetUInt32(0); Msg.block.SetUInt32(fWait); /* Do request */ int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) { rc = Msg.hdr.result; if (RT_SUCCESS(rc)) { /* Fetch results */ rc = Msg.msg.GetUInt32(puMsg); AssertRC(rc); rc = Msg.num_parms.GetUInt32(pcParms); AssertRC(rc); } } return rc; } static int vbglR3DnDHGProcessActionMessage(uint32_t uClientId, uint32_t uMsg, uint32_t *puScreenId, uint32_t *puX, uint32_t *puY, uint32_t *puDefAction, uint32_t *puAllActions, char *pszFormats, uint32_t cbFormats, uint32_t *pcbFormatsRecv) { /* Validate input */ AssertPtrReturn(puScreenId, VERR_INVALID_POINTER); AssertPtrReturn(puX, VERR_INVALID_POINTER); AssertPtrReturn(puY, VERR_INVALID_POINTER); AssertPtrReturn(puDefAction, VERR_INVALID_POINTER); AssertPtrReturn(puAllActions, VERR_INVALID_POINTER); AssertPtrReturn(pszFormats, VERR_INVALID_POINTER); AssertReturn(cbFormats, VERR_INVALID_PARAMETER); AssertPtrReturn(pcbFormatsRecv, VERR_INVALID_POINTER); /* Initialize header */ DragAndDropSvc::VBOXDNDHGACTIONMSG Msg; RT_ZERO(Msg); Msg.hdr.u32ClientID = uClientId; Msg.hdr.u32Function = uMsg; Msg.hdr.cParms = 7; /* Initialize parameter */ Msg.uScreenId.SetUInt32(0); Msg.uX.SetUInt32(0); Msg.uY.SetUInt32(0); Msg.uDefAction.SetUInt32(0); Msg.uAllActions.SetUInt32(0); Msg.pvFormats.SetPtr(pszFormats, cbFormats); Msg.cFormats.SetUInt32(0); /* Do request */ int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) { rc = Msg.hdr.result; if (RT_SUCCESS(rc)) { /* Fetch results */ rc = Msg.uScreenId.GetUInt32(puScreenId); AssertRC(rc); rc = Msg.uX.GetUInt32(puX); AssertRC(rc); rc = Msg.uY.GetUInt32(puY); AssertRC(rc); rc = Msg.uDefAction.GetUInt32(puDefAction); AssertRC(rc); rc = Msg.uAllActions.GetUInt32(puAllActions); AssertRC(rc); rc = Msg.cFormats.GetUInt32(pcbFormatsRecv); AssertRC(rc); /* A little bit paranoia */ AssertReturn(cbFormats >= *pcbFormatsRecv, VERR_TOO_MUCH_DATA); } } return rc; } static int vbglR3DnDHGProcessLeaveMessage(uint32_t uClientId) { /* Initialize header */ DragAndDropSvc::VBOXDNDHGLEAVEMSG Msg; RT_ZERO(Msg); Msg.hdr.u32ClientID = uClientId; Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_EVT_LEAVE; Msg.hdr.cParms = 0; /* Do request */ int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) rc = Msg.hdr.result; return rc; } static int vbglR3DnDHGProcessCancelMessage(uint32_t uClientId) { /* Initialize header */ DragAndDropSvc::VBOXDNDHGCANCELMSG Msg; RT_ZERO(Msg); Msg.hdr.u32ClientID = uClientId; Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_EVT_CANCEL; Msg.hdr.cParms = 0; /* Do request */ int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) rc = Msg.hdr.result; return rc; } static int vbglR3DnDHGProcessSendDirMessage(uint32_t uClientId, char *pszDirname, uint32_t cbDirname, uint32_t *pcbDirnameRecv, uint32_t *pfMode) { /* Validate input */ AssertPtrReturn(pszDirname, VERR_INVALID_POINTER); AssertReturn(cbDirname, VERR_INVALID_PARAMETER); AssertPtrReturn(pcbDirnameRecv, VERR_INVALID_POINTER); AssertPtrReturn(pfMode, VERR_INVALID_POINTER); /* Initialize header */ DragAndDropSvc::VBOXDNDHGSENDDIRMSG Msg; RT_ZERO(Msg); Msg.hdr.u32ClientID = uClientId; Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_SND_DIR; Msg.hdr.cParms = 3; /* Initialize parameter */ Msg.pvName.SetPtr(pszDirname, cbDirname); Msg.cName.SetUInt32(0); Msg.fMode.SetUInt32(0); /* Do request */ int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) { rc = Msg.hdr.result; if (RT_SUCCESS(Msg.hdr.result)) { /* Fetch results */ rc = Msg.cName.GetUInt32(pcbDirnameRecv); AssertRC(rc); rc = Msg.fMode.GetUInt32(pfMode); AssertRC(rc); /* A little bit paranoia */ AssertReturn(cbDirname >= *pcbDirnameRecv, VERR_TOO_MUCH_DATA); } } return rc; } static int vbglR3DnDHGProcessSendFileMessage(uint32_t uClientId, char *pszFilename, uint32_t cbFilename, uint32_t *pcbFilenameRecv, void *pvData, uint32_t cbData, uint32_t *pcbDataRecv, uint32_t *pfMode) { /* Validate input */ AssertPtrReturn(pszFilename, VERR_INVALID_POINTER); AssertReturn(cbFilename, VERR_INVALID_PARAMETER); AssertPtrReturn(pcbFilenameRecv, VERR_INVALID_POINTER); AssertPtrReturn(pvData, VERR_INVALID_POINTER); AssertReturn(cbData, VERR_INVALID_PARAMETER); AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER); AssertPtrReturn(pfMode, VERR_INVALID_POINTER); /* Initialize header */ DragAndDropSvc::VBOXDNDHGSENDFILEMSG Msg; RT_ZERO(Msg); Msg.hdr.u32ClientID = uClientId; Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_SND_FILE; Msg.hdr.cParms = 5; /* Initialize parameter */ Msg.pvName.SetPtr(pszFilename, cbFilename); Msg.cName.SetUInt32(0); Msg.pvData.SetPtr(pvData, cbData); Msg.cData.SetUInt32(0); Msg.fMode.SetUInt32(0); /* Do request */ int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) { rc = Msg.hdr.result; if (RT_SUCCESS(rc)) { /* Fetch results */ rc = Msg.cName.GetUInt32(pcbFilenameRecv); AssertRC(rc); rc = Msg.cData.GetUInt32(pcbDataRecv); AssertRC(rc); rc = Msg.fMode.GetUInt32(pfMode); AssertRC(rc); /* A little bit paranoia */ AssertReturn(cbFilename >= *pcbFilenameRecv, VERR_TOO_MUCH_DATA); AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA); } } return rc; } static int vbglR3DnDHGProcessURIMessages(uint32_t uClientId, uint32_t *puScreenId, char *pszFormat, uint32_t cbFormat, uint32_t *pcbFormatRecv, void **ppvData, uint32_t cbData, size_t *pcbDataRecv) { /* Make a string list out of the uri data. */ RTCList uriList = RTCString(static_cast(*ppvData), *pcbDataRecv - 1).split("\r\n"); if (uriList.isEmpty()) return VINF_SUCCESS; uint32_t cbTmpData = _1M * 10; void *pvTmpData = RTMemAlloc(cbTmpData); if (!pvTmpData) return VERR_NO_MEMORY; /* Create and query the drop target directory. */ char pszDropDir[RTPATH_MAX]; int rc = vbglR3DnDCreateDropDir(pszDropDir, sizeof(pszDropDir)); if (RT_FAILURE(rc)) { RTMemFree(pvTmpData); return rc; } /* Patch the old drop data with the new drop directory, so the drop target * can find the files. */ RTCList guestUriList; for (size_t i = 0; i < uriList.size(); ++i) { const RTCString &strUri = uriList.at(i); /* Query the path component of a file URI. If this hasn't a * file scheme, null is returned. */ if (char *pszFilePath = RTUriFilePath(strUri.c_str(), URI_FILE_FORMAT_AUTO)) { RTCString strFullPath = RTCString().printf("%s%c%s", pszDropDir, RTPATH_SLASH, pszFilePath); char *pszNewUri = RTUriFileCreate(strFullPath.c_str()); if (pszNewUri) { guestUriList.append(pszNewUri); RTStrFree(pszNewUri); } } else guestUriList.append(strUri); } /* Cleanup the old data and write the new data back to the event. */ RTMemFree(*ppvData); RTCString newData = RTCString::join(guestUriList, "\r\n") + "\r\n"; *ppvData = RTStrDupN(newData.c_str(), newData.length()); *pcbDataRecv = newData.length() + 1; /* Lists for holding created files & directories in the case of a * rollback. */ RTCList guestDirList; RTCList guestFileList; char pszPathname[RTPATH_MAX]; uint32_t cbPathname = 0; bool fLoop = true; do { uint32_t uNextMsg; uint32_t cNextParms; rc = vbglR3DnDQueryNextHostMessageType(uClientId, &uNextMsg, &cNextParms, false); DO(("%Rrc - %d\n", rc , uNextMsg)); if (RT_SUCCESS(rc)) { switch(uNextMsg) { case DragAndDropSvc::HOST_DND_HG_SND_DIR: { uint32_t fMode = 0; rc = vbglR3DnDHGProcessSendDirMessage(uClientId, pszPathname, sizeof(pszPathname), &cbPathname, &fMode); if (RT_SUCCESS(rc)) { DO(("Got drop dir: %s - %o - %Rrc\n", pszPathname, fMode, rc)); char *pszNewDir = RTPathJoinA(pszDropDir, pszPathname); rc = RTDirCreate(pszNewDir, (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRWXU, 0); if (!guestDirList.contains(pszNewDir)) guestDirList.append(pszNewDir); } break; } case DragAndDropSvc::HOST_DND_HG_SND_FILE: { uint32_t cbDataRecv; uint32_t fMode = 0; rc = vbglR3DnDHGProcessSendFileMessage(uClientId, pszPathname, sizeof(pszPathname), &cbPathname, pvTmpData, cbTmpData, &cbDataRecv, &fMode); if (RT_SUCCESS(rc)) { char *pszNewFile = RTPathJoinA(pszDropDir, pszPathname); DO(("Got drop file: %s - %d - %o - %Rrc\n", pszPathname, cbDataRecv, fMode, rc)); RTFILE hFile; rc = RTFileOpen(&hFile, pszNewFile, RTFILE_O_WRITE | RTFILE_O_APPEND | RTFILE_O_DENY_ALL | RTFILE_O_OPEN_CREATE); if (RT_SUCCESS(rc)) { rc = RTFileSeek(hFile, 0, RTFILE_SEEK_END, NULL); if (RT_SUCCESS(rc)) { rc = RTFileWrite(hFile, pvTmpData, cbDataRecv, 0); /* Valid UNIX mode? */ if ( RT_SUCCESS(rc) && (fMode & RTFS_UNIX_MASK)) rc = RTFileSetMode(hFile, (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR); } RTFileClose(hFile); if (!guestFileList.contains(pszNewFile)) guestFileList.append(pszNewFile); } } break; } case DragAndDropSvc::HOST_DND_HG_EVT_CANCEL: { rc = vbglR3DnDHGProcessCancelMessage(uClientId); if (RT_SUCCESS(rc)) rc = VERR_CANCELLED; /* Break out of the loop. */ } default: fLoop = false; break; } } else { if (rc == VERR_NO_DATA) rc = VINF_SUCCESS; break; } }while(fLoop); RTMemFree(pvTmpData); /* Cleanup on failure or if the user has canceled. */ if (RT_FAILURE(rc)) { /* Remove any stuff created. */ for (size_t i = 0; i < guestFileList.size(); ++i) RTFileDelete(guestFileList.at(i).c_str()); for (size_t i = 0; i < guestDirList.size(); ++i) RTDirRemove(guestDirList.at(i).c_str()); RTDirRemove(pszDropDir); } return rc; } static int vbglR3DnDHGProcessDataMessageInternal(uint32_t uClientId, uint32_t *puScreenId, char *pszFormat, uint32_t cbFormat, uint32_t *pcbFormatRecv, void *pvData, uint32_t cbData, uint32_t *pcbDataRecv) { /* Validate input */ AssertPtrReturn(puScreenId, VERR_INVALID_POINTER); AssertPtrReturn(pszFormat, VERR_INVALID_POINTER); AssertReturn(cbFormat, VERR_INVALID_PARAMETER); AssertPtrReturn(pcbFormatRecv, VERR_INVALID_POINTER); AssertPtrReturn(pvData, VERR_INVALID_POINTER); AssertReturn(cbData, VERR_INVALID_PARAMETER); AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER); /* Initialize header */ DragAndDropSvc::VBOXDNDHGSENDDATAMSG Msg; RT_ZERO(Msg); Msg.hdr.u32ClientID = uClientId; Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_SND_DATA; Msg.hdr.cParms = 5; /* Initialize parameter */ Msg.uScreenId.SetUInt32(0); Msg.pvFormat.SetPtr(pszFormat, cbFormat); Msg.cFormat.SetUInt32(0); Msg.pvData.SetPtr(pvData, cbData); Msg.cData.SetUInt32(0); /* Do request */ int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) { rc = Msg.hdr.result; if ( RT_SUCCESS(rc) || rc == VERR_BUFFER_OVERFLOW) { /* Fetch results */ rc = Msg.uScreenId.GetUInt32(puScreenId); AssertRC(rc); rc = Msg.cFormat.GetUInt32(pcbFormatRecv); AssertRC(rc); rc = Msg.cData.GetUInt32(pcbDataRecv); AssertRC(rc); /* A little bit paranoia */ AssertReturn(cbFormat >= *pcbFormatRecv, VERR_TOO_MUCH_DATA); AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA); } } return rc; } static int vbglR3DnDHGProcessMoreDataMessageInternal(uint32_t uClientId, void *pvData, uint32_t cbData, uint32_t *pcbDataRecv) { /* Validate input */ AssertPtrReturn(pvData, VERR_INVALID_POINTER); AssertReturn(cbData, VERR_INVALID_PARAMETER); AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER); /* Initialize header */ DragAndDropSvc::VBOXDNDHGSENDMOREDATAMSG Msg; RT_ZERO(Msg); Msg.hdr.u32ClientID = g_clientId; Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_SND_MORE_DATA; Msg.hdr.cParms = 2; /* Initialize parameter */ Msg.pvData.SetPtr(pvData, cbData); Msg.cData.SetUInt32(0); /* Do request */ int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) { rc = Msg.hdr.result; if ( RT_SUCCESS(rc) || rc == VERR_BUFFER_OVERFLOW) { /* Fetch results */ rc = Msg.cData.GetUInt32(pcbDataRecv); AssertRC(rc); /* A little bit paranoia */ AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA); } } return rc; } static int vbglR3DnDHGProcessSendDataMessages(uint32_t uClientId, uint32_t *puScreenId, char *pszFormat, uint32_t cbFormat, uint32_t *pcbFormatRecv, void **ppvData, uint32_t cbData, size_t *pcbDataRecv) { uint32_t cbDataRecv = 0; int rc = vbglR3DnDHGProcessDataMessageInternal(uClientId, puScreenId, pszFormat, cbFormat, pcbFormatRecv, *ppvData, cbData, &cbDataRecv); size_t cbAllDataRecv = cbDataRecv; while (rc == VERR_BUFFER_OVERFLOW) { uint32_t uNextMsg; uint32_t cNextParms; rc = vbglR3DnDQueryNextHostMessageType(uClientId, &uNextMsg, &cNextParms, false); if (RT_SUCCESS(rc)) { switch(uNextMsg) { case DragAndDropSvc::HOST_DND_HG_SND_MORE_DATA: { *ppvData = RTMemRealloc(*ppvData, cbAllDataRecv + cbData); if (!*ppvData) { rc = VERR_NO_MEMORY; break; } rc = vbglR3DnDHGProcessMoreDataMessageInternal(uClientId, &((char*)*ppvData)[cbAllDataRecv], cbData, &cbDataRecv); cbAllDataRecv += cbDataRecv; break; } case DragAndDropSvc::HOST_DND_HG_EVT_CANCEL: default: { rc = vbglR3DnDHGProcessCancelMessage(uClientId); if (RT_SUCCESS(rc)) rc = VERR_CANCELLED; break; } } } } if (RT_SUCCESS(rc)) *pcbDataRecv = cbAllDataRecv; return rc; } static int vbglR3DnDHGProcessSendDataMessage(uint32_t uClientId, uint32_t *puScreenId, char *pszFormat, uint32_t cbFormat, uint32_t *pcbFormatRecv, void **ppvData, uint32_t cbData, size_t *pcbDataRecv) { int rc = vbglR3DnDHGProcessSendDataMessages(uClientId, puScreenId, pszFormat, cbFormat, pcbFormatRecv, ppvData, cbData, pcbDataRecv); if (RT_SUCCESS(rc)) { /* Check if this is a uri-event */ if (RTStrNICmp(pszFormat, "text/uri-list", *pcbFormatRecv) == 0) rc = vbglR3DnDHGProcessURIMessages(uClientId, puScreenId, pszFormat, cbFormat, pcbFormatRecv, ppvData, cbData, pcbDataRecv); } return rc; } static int vbglR3DnDGHProcessRequestPendingMessage(uint32_t uClientId, uint32_t *puScreenId) { /* Validate input */ AssertPtrReturn(puScreenId, VERR_INVALID_POINTER); /* Initialize header */ DragAndDropSvc::VBOXDNDGHREQPENDINGMSG Msg; RT_ZERO(Msg); Msg.hdr.u32ClientID = uClientId; Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_GH_REQ_PENDING; Msg.hdr.cParms = 1; /* Initialize parameter */ Msg.uScreenId.SetUInt32(0); /* Do request */ int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) { rc = Msg.hdr.result; if (RT_SUCCESS(rc)) { /* Fetch results */ rc = Msg.uScreenId.GetUInt32(puScreenId); AssertRC(rc); } } return rc; } static int vbglR3DnDGHProcessDroppedMessage(uint32_t uClientId, char *pszFormat, uint32_t cbFormat, uint32_t *pcbFormatRecv, uint32_t *puAction) { /* Validate input */ AssertPtrReturn(pszFormat, VERR_INVALID_POINTER); AssertReturn(cbFormat, VERR_INVALID_PARAMETER); AssertPtrReturn(pcbFormatRecv, VERR_INVALID_POINTER); AssertPtrReturn(puAction, VERR_INVALID_POINTER); /* Initialize header */ DragAndDropSvc::VBOXDNDGHDROPPEDMSG Msg; RT_ZERO(Msg); Msg.hdr.u32ClientID = uClientId; Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_GH_EVT_DROPPED; Msg.hdr.cParms = 3; /* Initialize parameter */ Msg.pvFormat.SetPtr(pszFormat, cbFormat); Msg.cFormat.SetUInt32(0); Msg.uAction.SetUInt32(0); /* Do request */ int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) { rc = Msg.hdr.result; if (RT_SUCCESS(rc)) { /* Fetch results */ rc = Msg.cFormat.GetUInt32(pcbFormatRecv); AssertRC(rc); rc = Msg.uAction.GetUInt32(puAction); AssertRC(rc); /* A little bit paranoia */ AssertReturn(cbFormat >= *pcbFormatRecv, VERR_TOO_MUCH_DATA); } } return rc; } /****************************************************************************** * Public functions * ******************************************************************************/ /** * Initialize Drag & Drop. * * This will enable the Drag & Drop events. * * @returns VBox status code. */ VBGLR3DECL(int) VbglR3DnDInit(void) { return VbglR3DnDConnect(&g_clientId); } /** * Terminate Drag and Drop. * * This will Drag and Drop events. * * @returns VBox status. */ VBGLR3DECL(int) VbglR3DnDTerm(void) { return VbglR3DnDDisconnect(g_clientId); } VBGLR3DECL(int) VbglR3DnDConnect(uint32_t *pu32ClientId) { /* Validate input */ AssertPtrReturn(pu32ClientId, VERR_INVALID_POINTER); /* Initialize header */ VBoxGuestHGCMConnectInfo Info; RT_ZERO(Info.Loc.u); Info.result = VERR_WRONG_ORDER; Info.u32ClientID = UINT32_MAX; /* try make valgrind shut up. */ /* Initialize parameter */ Info.Loc.type = VMMDevHGCMLoc_LocalHost_Existing; int rc = RTStrCopy(Info.Loc.u.host.achName, sizeof(Info.Loc.u.host.achName), "VBoxDragAndDropSvc"); if (RT_FAILURE(rc)) return rc; /* Do request */ rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CONNECT, &Info, sizeof(Info)); if (RT_SUCCESS(rc)) { rc = Info.result; if (RT_SUCCESS(rc)) *pu32ClientId = Info.u32ClientID; } return rc; } VBGLR3DECL(int) VbglR3DnDDisconnect(uint32_t u32ClientId) { /* Initialize header */ VBoxGuestHGCMDisconnectInfo Info; Info.result = VERR_WRONG_ORDER; Info.u32ClientID = u32ClientId; /* Do request */ int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_DISCONNECT, &Info, sizeof(Info)); if (RT_SUCCESS(rc)) rc = Info.result; return rc; } VBGLR3DECL(int) VbglR3DnDProcessNextMessage(CPVBGLR3DNDHGCMEVENT pEvent) { /* Validate input */ AssertPtrReturn(pEvent, VERR_INVALID_POINTER); uint32_t uMsg = 0; uint32_t uNumParms = 0; const uint32_t ccbFormats = _64K; const uint32_t ccbData = _1M; int rc = vbglR3DnDQueryNextHostMessageType(g_clientId, &uMsg, &uNumParms, true); if (RT_SUCCESS(rc)) { DO(("Got message %d\n", uMsg)); switch(uMsg) { case DragAndDropSvc::HOST_DND_HG_EVT_ENTER: case DragAndDropSvc::HOST_DND_HG_EVT_MOVE: case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED: { pEvent->uType = uMsg; pEvent->pszFormats = static_cast(RTMemAlloc(ccbFormats)); if (!pEvent->pszFormats) return VERR_NO_MEMORY; rc = vbglR3DnDHGProcessActionMessage(g_clientId, uMsg, &pEvent->uScreenId, &pEvent->u.a.uXpos, &pEvent->u.a.uYpos, &pEvent->u.a.uDefAction, &pEvent->u.a.uAllActions, pEvent->pszFormats, ccbFormats, &pEvent->cbFormats); break; } case DragAndDropSvc::HOST_DND_HG_EVT_LEAVE: { pEvent->uType = uMsg; rc = vbglR3DnDHGProcessLeaveMessage(g_clientId); break; } case DragAndDropSvc::HOST_DND_HG_SND_DATA: { pEvent->uType = uMsg; pEvent->pszFormats = static_cast(RTMemAlloc(ccbFormats)); if (!pEvent->pszFormats) return VERR_NO_MEMORY; pEvent->u.b.pvData = RTMemAlloc(ccbData); if (!pEvent->u.b.pvData) { RTMemFree(pEvent->pszFormats); pEvent->pszFormats = NULL; return VERR_NO_MEMORY; } rc = vbglR3DnDHGProcessSendDataMessage(g_clientId, &pEvent->uScreenId, pEvent->pszFormats, ccbFormats, &pEvent->cbFormats, &pEvent->u.b.pvData, ccbData, &pEvent->u.b.cbData); break; } #ifdef VBOX_WITH_DRAG_AND_DROP_GH case DragAndDropSvc::HOST_DND_GH_REQ_PENDING: { pEvent->uType = uMsg; rc = vbglR3DnDGHProcessRequestPendingMessage(g_clientId, &pEvent->uScreenId); break; } case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED: { pEvent->uType = uMsg; pEvent->pszFormats = static_cast(RTMemAlloc(ccbFormats)); if (!pEvent->pszFormats) return VERR_NO_MEMORY; rc = vbglR3DnDGHProcessDroppedMessage(g_clientId, pEvent->pszFormats, ccbFormats, &pEvent->cbFormats, &pEvent->u.a.uDefAction); break; } #endif /* VBOX_WITH_DRAG_AND_DROP_GH */ case DragAndDropSvc::HOST_DND_HG_EVT_CANCEL: { pEvent->uType = uMsg; rc = vbglR3DnDHGProcessCancelMessage(g_clientId); if (RT_SUCCESS(rc)) rc = VERR_CANCELLED; break; } default: AssertMsgFailedReturn(("Message %u isn't expected in this context", uMsg), VERR_INVALID_PARAMETER); break; } } return rc; } VBGLR3DECL(int) VbglR3DnDHGAcknowledgeOperation(uint32_t uAction) { DO(("ACK: %u\n", uAction)); /* Initialize header */ DragAndDropSvc::VBOXDNDHGACKOPMSG Msg; RT_ZERO(Msg); Msg.hdr.result = VERR_WRONG_ORDER; Msg.hdr.u32ClientID = g_clientId; Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_HG_ACK_OP; Msg.hdr.cParms = 1; /* Initialize parameter */ Msg.uAction.SetUInt32(uAction); /* Do request */ int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) rc = Msg.hdr.result; return rc; } VBGLR3DECL(int) VbglR3DnDHGRequestData(const char* pcszFormat) { DO(("DATA_REQ: '%s'\n", pcszFormat)); /* Validate input */ AssertPtrReturn(pcszFormat, VERR_INVALID_PARAMETER); /* Initialize header */ DragAndDropSvc::VBOXDNDHGREQDATAMSG Msg; RT_ZERO(Msg); Msg.hdr.result = VERR_WRONG_ORDER; Msg.hdr.u32ClientID = g_clientId; Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_HG_REQ_DATA; Msg.hdr.cParms = 1; /* Do request */ Msg.pFormat.SetPtr((void*)pcszFormat, (uint32_t)strlen(pcszFormat) + 1); int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) rc = Msg.hdr.result; return rc; } VBGLR3DECL(int) VbglR3DnDGHAcknowledgePending(uint32_t uDefAction, uint32_t uAllActions, const char* pcszFormat) { DO(("PEND: %u: %u (%s)\n", uDefAction, uAllActions, pcszFormat)); /* Validate input */ AssertPtrReturn(pcszFormat, VERR_INVALID_POINTER); /* Initialize header */ DragAndDropSvc::VBOXDNDGHACKPENDINGMSG Msg; RT_ZERO(Msg); Msg.hdr.result = VERR_WRONG_ORDER; Msg.hdr.u32ClientID = g_clientId; Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_ACK_PENDING; Msg.hdr.cParms = 3; /* Initialize parameter */ Msg.uDefAction.SetUInt32(uDefAction); Msg.uAllActions.SetUInt32(uAllActions); Msg.pFormat.SetPtr((void*)pcszFormat, static_cast(strlen(pcszFormat) + 1)); /* Do request */ int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) rc = Msg.hdr.result; return rc; } VBGLR3DECL(int) VbglR3DnDGHSendData(void *pvData, uint32_t cbData) { DO(("DATA: %x (%u)\n", pvData, cbData)); /* Validate input */ AssertPtrReturn(pvData, VERR_INVALID_POINTER); AssertReturn(cbData, VERR_INVALID_PARAMETER); /* Todo: URI support. Currently only data is send over to the host. For URI * support basically the same as in the H->G case (see * HostServices/DragAndDrop/dndmanager.h/cpp) has to be done: * 1. Parse the urilist * 2. Recursively send "create dir" and "transfer file" msg to the host * 3. Patch the urilist by removing all base dirnames * 4. On the host all needs to received and the urilist patched afterwards * to point to the new location */ /* Initialize header */ DragAndDropSvc::VBOXDNDGHSENDDATAMSG Msg; RT_ZERO(Msg); Msg.hdr.result = VERR_WRONG_ORDER; Msg.hdr.u32ClientID = g_clientId; Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_SND_DATA; Msg.hdr.cParms = 2; Msg.uSize.SetUInt32(cbData); int rc = VINF_SUCCESS; uint32_t cbMax = _1M; uint32_t cbSend = 0; while(cbSend < cbData) { /* Initialize parameter */ uint32_t cbToSend = RT_MIN(cbData - cbSend, cbMax); Msg.pData.SetPtr(static_cast(pvData) + cbSend, cbToSend); /* Do request */ rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) { rc = Msg.hdr.result; /* Did the host cancel the event? */ if (rc == VERR_CANCELLED) break; } else break; cbSend += cbToSend; // RTThreadSleep(500); } return rc; } VBGLR3DECL(int) VbglR3DnDGHErrorEvent(int rcOp) { DO(("GH_ERROR\n")); /* Initialize header */ DragAndDropSvc::VBOXDNDGHEVTERRORMSG Msg; RT_ZERO(Msg); Msg.hdr.result = VERR_WRONG_ORDER; Msg.hdr.u32ClientID = g_clientId; Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_EVT_ERROR; Msg.hdr.cParms = 1; /* Initialize parameter */ Msg.uRC.SetUInt32(rcOp); /* Do request */ int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) rc = Msg.hdr.result; return rc; }