VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp@ 97745

Last change on this file since 97745 was 97745, checked in by vboxsync, 2 years ago

DnD/VbglR3: Reverted r154805 (committed too much).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 70.3 KB
Line 
1/* $Id: VBoxGuestR3LibDragAndDrop.cpp 97745 2022-12-06 09:08:39Z vboxsync $ */
2/** @file
3 * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Drag & Drop.
4 */
5
6/*
7 * Copyright (C) 2011-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/path.h>
42#include <iprt/dir.h>
43#include <iprt/file.h>
44#include <iprt/uri.h>
45#include <iprt/thread.h>
46
47#include <iprt/cpp/list.h>
48#include <iprt/cpp/ministring.h>
49
50#ifdef LOG_GROUP
51 #undef LOG_GROUP
52#endif
53#define LOG_GROUP LOG_GROUP_GUEST_DND
54#include <VBox/log.h>
55
56#include <VBox/VBoxGuestLib.h>
57#include <VBox/GuestHost/DragAndDrop.h>
58#include <VBox/HostServices/DragAndDropSvc.h>
59
60using namespace DragAndDropSvc;
61
62#include "VBoxGuestR3LibInternal.h"
63
64
65/*********************************************************************************************************************************
66* Private internal functions *
67*********************************************************************************************************************************/
68
69/**
70 * Receives the next upcoming message for a given DnD context.
71 *
72 * @returns IPRT status code.
73 * Will return VERR_CANCELLED (implemented by the host service) if we need to bail out.
74 * @param pCtx DnD context to use.
75 * @param puMsg Where to store the message type.
76 * @param pcParms Where to store the number of parameters required for receiving the message.
77 * @param fWait Whether to wait (block) for a new message to arrive or not.
78 */
79static int vbglR3DnDGetNextMsgType(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puMsg, uint32_t *pcParms, bool fWait)
80{
81 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
82 AssertPtrReturn(puMsg, VERR_INVALID_POINTER);
83 AssertPtrReturn(pcParms, VERR_INVALID_POINTER);
84
85 int rc;
86
87 do
88 {
89 HGCMMsgGetNext Msg;
90 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GET_NEXT_HOST_MSG, 3);
91 Msg.uMsg.SetUInt32(0);
92 Msg.cParms.SetUInt32(0);
93 Msg.fBlock.SetUInt32(fWait ? 1 : 0);
94
95 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
96 if (RT_SUCCESS(rc))
97 {
98 rc = Msg.uMsg.GetUInt32(puMsg); AssertRC(rc);
99 rc = Msg.cParms.GetUInt32(pcParms); AssertRC(rc);
100 }
101
102 LogRel(("DnD: Received message %s (%#x) from host\n", DnDHostMsgToStr(*puMsg), *puMsg));
103
104 } while (rc == VERR_INTERRUPTED);
105
106 return rc;
107}
108
109/**
110 * Host -> Guest
111 * Utility function to receive a so-called "action message" from the host.
112 * Certain DnD messages use the same amount / sort of parameters and grouped as "action messages".
113 *
114 * @returns IPRT status code.
115 * @param pCtx DnD context to use.
116 * @param uMsg Which kind of message to receive.
117 * @param puScreenID Where to store the host screen ID the message is bound to. Optional.
118 * @param puX Where to store the absolute X coordinates. Optional.
119 * @param puY Where to store the absolute Y coordinates. Optional.
120 * @param puDefAction Where to store the default action to perform. Optional.
121 * @param puAllActions Where to store the available actions. Optional.
122 * @param ppszFormats Where to store List of formats. Optional.
123 * @param pcbFormats Size (in bytes) of where to store the list of formats. Optional.
124 *
125 * @todo r=andy Get rid of this function as soon as we resolved the protocol TODO #1.
126 * This was part of the initial protocol and needs to go.
127 */
128static int vbglR3DnDHGRecvAction(PVBGLR3GUESTDNDCMDCTX pCtx,
129 uint32_t uMsg,
130 uint32_t *puScreenID,
131 uint32_t *puX,
132 uint32_t *puY,
133 uint32_t *puDefAction,
134 uint32_t *puAllActions,
135 char **ppszFormats,
136 uint32_t *pcbFormats)
137{
138 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
139 /* The rest is optional. */
140
141 const uint32_t cbFormatsTmp = pCtx->cbMaxChunkSize;
142
143 char *pszFormatsTmp = static_cast<char *>(RTMemAlloc(cbFormatsTmp));
144 if (!pszFormatsTmp)
145 return VERR_NO_MEMORY;
146
147 HGCMMsgHGAction Msg;
148 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, uMsg, 8);
149 Msg.u.v3.uContext.SetUInt32(0);
150 Msg.u.v3.uScreenId.SetUInt32(0);
151 Msg.u.v3.uX.SetUInt32(0);
152 Msg.u.v3.uY.SetUInt32(0);
153 Msg.u.v3.uDefAction.SetUInt32(0);
154 Msg.u.v3.uAllActions.SetUInt32(0);
155 Msg.u.v3.pvFormats.SetPtr(pszFormatsTmp, cbFormatsTmp);
156 Msg.u.v3.cbFormats.SetUInt32(0);
157
158 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
159 if (RT_SUCCESS(rc))
160 {
161 /** @todo Context ID not used yet. */
162 if (RT_SUCCESS(rc) && puScreenID)
163 rc = Msg.u.v3.uScreenId.GetUInt32(puScreenID);
164 if (RT_SUCCESS(rc) && puX)
165 rc = Msg.u.v3.uX.GetUInt32(puX);
166 if (RT_SUCCESS(rc) && puY)
167 rc = Msg.u.v3.uY.GetUInt32(puY);
168 if (RT_SUCCESS(rc) && puDefAction)
169 rc = Msg.u.v3.uDefAction.GetUInt32(puDefAction);
170 if (RT_SUCCESS(rc) && puAllActions)
171 rc = Msg.u.v3.uAllActions.GetUInt32(puAllActions);
172 if (RT_SUCCESS(rc) && pcbFormats)
173 rc = Msg.u.v3.cbFormats.GetUInt32(pcbFormats);
174
175 if (RT_SUCCESS(rc))
176 {
177 if (ppszFormats)
178 {
179 *ppszFormats = RTStrDup(pszFormatsTmp);
180 if (!*ppszFormats)
181 rc = VERR_NO_MEMORY;
182 }
183 }
184 }
185
186 RTStrFree(pszFormatsTmp);
187
188 return rc;
189}
190
191/**
192 * Host -> Guest
193 * Utility function to receive a HOST_DND_FN_HG_EVT_LEAVE message from the host.
194 *
195 * @returns IPRT status code.
196 * @param pCtx DnD context to use.
197 */
198static int vbglR3DnDHGRecvLeave(PVBGLR3GUESTDNDCMDCTX pCtx)
199{
200 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
201
202 HGCMMsgHGLeave Msg;
203 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_EVT_LEAVE, 1);
204 /** @todo Context ID not used yet. */
205 Msg.u.v3.uContext.SetUInt32(0);
206
207 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
208}
209
210/**
211 * Host -> Guest
212 * Utility function to receive a HOST_DND_FN_HG_EVT_CANCEL message from the host.
213 *
214 * @returns IPRT status code.
215 * @param pCtx DnD context to use.
216 */
217static int vbglR3DnDHGRecvCancel(PVBGLR3GUESTDNDCMDCTX pCtx)
218{
219 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
220
221 HGCMMsgHGCancel Msg;
222 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_CANCEL, 1);
223 /** @todo Context ID not used yet. */
224 Msg.u.v3.uContext.SetUInt32(0);
225
226 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
227}
228
229/**
230 * Host -> Guest
231 * Utility function to receive a HOST_DND_FN_HG_SND_DIR message from the host.
232 *
233 * @returns IPRT status code.
234 * @param pCtx DnD context to use.
235 * @param pszDirname Where to store the directory name of the directory being created.
236 * @param cbDirname Size (in bytes) of where to store the directory name of the directory being created.
237 * @param pcbDirnameRecv Size (in bytes) of the actual directory name received.
238 * @param pfMode Where to store the directory creation mode.
239 */
240static int vbglR3DnDHGRecvDir(PVBGLR3GUESTDNDCMDCTX pCtx,
241 char *pszDirname,
242 uint32_t cbDirname,
243 uint32_t *pcbDirnameRecv,
244 uint32_t *pfMode)
245{
246 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
247 AssertPtrReturn(pszDirname, VERR_INVALID_POINTER);
248 AssertReturn(cbDirname, VERR_INVALID_PARAMETER);
249 AssertPtrReturn(pcbDirnameRecv, VERR_INVALID_POINTER);
250 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
251
252 HGCMMsgHGSendDir Msg;
253 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_DIR, 4);
254 /** @todo Context ID not used yet. */
255 Msg.u.v3.uContext.SetUInt32(0);
256 Msg.u.v3.pvName.SetPtr(pszDirname, cbDirname);
257 Msg.u.v3.cbName.SetUInt32(cbDirname);
258 Msg.u.v3.fMode.SetUInt32(0);
259
260 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
261 if (RT_SUCCESS(rc))
262 {
263 /** @todo Context ID not used yet. */
264 rc = Msg.u.v3.cbName.GetUInt32(pcbDirnameRecv); AssertRC(rc);
265 rc = Msg.u.v3.fMode.GetUInt32(pfMode); AssertRC(rc);
266
267 AssertReturn(cbDirname >= *pcbDirnameRecv, VERR_TOO_MUCH_DATA);
268 }
269
270 return rc;
271}
272
273/**
274 * Host -> Guest
275 * Utility function to receive a HOST_DND_FN_HG_SND_FILE_DATA message from the host.
276 *
277 * @returns IPRT status code.
278 * @param pCtx DnD context to use.
279 * @param pvData Where to store the file data chunk.
280 * @param cbData Size (in bytes) of where to store the data chunk.
281 * @param pcbDataRecv Size (in bytes) of the actual data chunk size received.
282 */
283static int vbglR3DnDHGRecvFileData(PVBGLR3GUESTDNDCMDCTX pCtx,
284 void *pvData,
285 uint32_t cbData,
286 uint32_t *pcbDataRecv)
287{
288 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
289 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
290 AssertReturn(cbData, VERR_INVALID_PARAMETER);
291 AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER);
292
293 HGCMMsgHGSendFileData Msg;
294 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_FILE_DATA, 5);
295 Msg.u.v3.uContext.SetUInt32(0);
296 Msg.u.v3.pvData.SetPtr(pvData, cbData);
297 Msg.u.v3.cbData.SetUInt32(0);
298 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
299 Msg.u.v3.cbChecksum.SetUInt32(0);
300
301 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
302 if (RT_SUCCESS(rc))
303 {
304 /** @todo Context ID not used yet. */
305 rc = Msg.u.v3.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
306 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
307 /** @todo Add checksum support. */
308 }
309
310 return rc;
311}
312
313/**
314 * Host -> Guest
315 * Utility function to receive the HOST_DND_FN_HG_SND_FILE_HDR message from the host.
316 *
317 * @returns IPRT status code.
318 * @param pCtx DnD context to use.
319 * @param pszFilename Where to store the file name of the file being transferred.
320 * @param cbFilename Size (in bytes) of where to store the file name of the file being transferred.
321 * @param puFlags File transfer flags. Currently not being used.
322 * @param pfMode Where to store the file creation mode.
323 * @param pcbTotal Where to store the file size (in bytes).
324 */
325static int vbglR3DnDHGRecvFileHdr(PVBGLR3GUESTDNDCMDCTX pCtx,
326 char *pszFilename,
327 uint32_t cbFilename,
328 uint32_t *puFlags,
329 uint32_t *pfMode,
330 uint64_t *pcbTotal)
331{
332 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
333 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
334 AssertReturn(cbFilename, VERR_INVALID_PARAMETER);
335 AssertPtrReturn(puFlags, VERR_INVALID_POINTER);
336 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
337 AssertReturn(pcbTotal, VERR_INVALID_POINTER);
338
339 HGCMMsgHGSendFileHdr Msg;
340 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_FILE_HDR, 6);
341 Msg.uContext.SetUInt32(0); /** @todo Not used yet. */
342 Msg.pvName.SetPtr(pszFilename, cbFilename);
343 Msg.cbName.SetUInt32(cbFilename);
344 Msg.uFlags.SetUInt32(0);
345 Msg.fMode.SetUInt32(0);
346 Msg.cbTotal.SetUInt64(0);
347
348 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
349 if (RT_SUCCESS(rc))
350 {
351 /** @todo Get context ID. */
352 rc = Msg.uFlags.GetUInt32(puFlags); AssertRC(rc);
353 rc = Msg.fMode.GetUInt32(pfMode); AssertRC(rc);
354 rc = Msg.cbTotal.GetUInt64(pcbTotal); AssertRC(rc);
355 }
356
357 return rc;
358}
359
360/**
361 * Host -> Guest
362 * Helper function for receiving URI data from the host. Do not call directly.
363 * This function also will take care of the file creation / locking on the guest.
364 *
365 * @returns IPRT status code.
366 * @param pCtx DnD context to use.
367 * @param pDataHdr DnD data header to use. Needed for accounting.
368 * @param pDroppedFiles Dropped files object to use for maintaining the file creation / locking.
369 */
370static int vbglR3DnDHGRecvURIData(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr, PDNDDROPPEDFILES pDroppedFiles)
371{
372 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
373 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
374 AssertPtrReturn(pDroppedFiles, VERR_INVALID_POINTER);
375
376 /* Only count the raw data minus the already received meta data. */
377 Assert(pDataHdr->cbTotal >= pDataHdr->cbMeta);
378 uint64_t cbToRecvBytes = pDataHdr->cbTotal - pDataHdr->cbMeta;
379 uint64_t cToRecvObjs = pDataHdr->cObjects;
380
381 LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64, (cbTotal=%RU64, cbMeta=%RU32)\n",
382 cbToRecvBytes, cToRecvObjs, pDataHdr->cbTotal, pDataHdr->cbMeta));
383
384 /* Anything to do at all? */
385 /* Note: Do not check for cbToRecvBytes == 0 here, as this might be just
386 * a bunch of 0-byte files to be transferred. */
387 if (!cToRecvObjs)
388 return VINF_SUCCESS;
389
390 LogRel2(("DnD: Receiving URI data started\n"));
391
392 /*
393 * Allocate temporary chunk buffer.
394 */
395 uint32_t cbChunkMax = pCtx->cbMaxChunkSize;
396 void *pvChunk = RTMemAlloc(cbChunkMax);
397 if (!pvChunk)
398 return VERR_NO_MEMORY;
399 uint32_t cbChunkRead = 0;
400
401 uint64_t cbFileSize = 0; /* Total file size (in bytes). */
402 uint64_t cbFileWritten = 0; /* Written bytes. */
403
404 const char *pszDropDir = DnDDroppedFilesGetDirAbs(pDroppedFiles);
405 AssertPtr(pszDropDir);
406
407 int rc;
408
409 /*
410 * Enter the main loop of retieving files + directories.
411 */
412 DNDTRANSFEROBJECT objCur;
413 RT_ZERO(objCur);
414
415 char szPathName[RTPATH_MAX] = { 0 };
416 uint32_t cbPathName = 0;
417 uint32_t fFlags = 0;
418 uint32_t fMode = 0;
419
420 do
421 {
422 LogFlowFunc(("Waiting for new message ...\n"));
423
424 uint32_t uNextMsg;
425 uint32_t cNextParms;
426 rc = vbglR3DnDGetNextMsgType(pCtx, &uNextMsg, &cNextParms, true /* fWait */);
427 if (RT_SUCCESS(rc))
428 {
429 LogFlowFunc(("uNextMsg=%RU32, cNextParms=%RU32\n", uNextMsg, cNextParms));
430
431 switch (uNextMsg)
432 {
433 case HOST_DND_FN_HG_SND_DIR:
434 {
435 rc = vbglR3DnDHGRecvDir(pCtx,
436 szPathName,
437 sizeof(szPathName),
438 &cbPathName,
439 &fMode);
440 LogFlowFunc(("HOST_DND_FN_HG_SND_DIR: "
441 "pszPathName=%s, cbPathName=%RU32, fMode=0x%x, rc=%Rrc\n",
442 szPathName, cbPathName, fMode, rc));
443
444 char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName);
445 if (pszPathAbs)
446 {
447#ifdef RT_OS_WINDOWS
448 uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL;
449#else
450 uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRWXU;
451#endif
452 rc = RTDirCreate(pszPathAbs, fCreationMode, 0);
453 if (RT_SUCCESS(rc))
454 rc = DnDDroppedFilesAddDir(pDroppedFiles, pszPathAbs);
455
456 if (RT_SUCCESS(rc))
457 {
458 Assert(cToRecvObjs);
459 cToRecvObjs--;
460 }
461
462 RTStrFree(pszPathAbs);
463 }
464 else
465 rc = VERR_NO_MEMORY;
466 break;
467 }
468 case HOST_DND_FN_HG_SND_FILE_HDR:
469 RT_FALL_THROUGH();
470 case HOST_DND_FN_HG_SND_FILE_DATA:
471 {
472 if (uNextMsg == HOST_DND_FN_HG_SND_FILE_HDR)
473 {
474 rc = vbglR3DnDHGRecvFileHdr(pCtx,
475 szPathName,
476 sizeof(szPathName),
477 &fFlags,
478 &fMode,
479 &cbFileSize);
480 LogFlowFunc(("HOST_DND_FN_HG_SND_FILE_HDR: "
481 "szPathName=%s, fFlags=0x%x, fMode=0x%x, cbFileSize=%RU64, rc=%Rrc\n",
482 szPathName, fFlags, fMode, cbFileSize, rc));
483 }
484 else
485 {
486 rc = vbglR3DnDHGRecvFileData(pCtx,
487 pvChunk,
488 cbChunkMax,
489 &cbChunkRead);
490 LogFlowFunc(("HOST_DND_FN_HG_SND_FILE_DATA: "
491 "cbChunkRead=%RU32, rc=%Rrc\n", cbChunkRead, rc));
492 }
493
494 if ( RT_SUCCESS(rc)
495 && uNextMsg == HOST_DND_FN_HG_SND_FILE_HDR)
496 {
497 char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName);
498 if (pszPathAbs)
499 {
500 LogFlowFunc(("Opening pszPathName=%s, cbPathName=%RU32, fMode=0x%x, cbFileSize=%zu\n",
501 szPathName, cbPathName, fMode, cbFileSize));
502
503 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE
504 | RTFILE_O_CREATE_REPLACE;
505
506 /* Is there already a file open, e.g. in transfer? */
507 if (!DnDTransferObjectIsOpen(&objCur))
508 {
509#ifdef RT_OS_WINDOWS
510 uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL;
511#else
512 uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR;
513#endif
514 rc = DnDTransferObjectInitEx(&objCur, DNDTRANSFEROBJTYPE_FILE,
515 pszDropDir /* Source (base) path */, szPathName /* Destination path */);
516 if (RT_SUCCESS(rc))
517 {
518 rc = DnDTransferObjectOpen(&objCur, fOpen, fCreationMode, DNDTRANSFEROBJECT_FLAGS_NONE);
519 if (RT_SUCCESS(rc))
520 {
521 rc = DnDDroppedFilesAddFile(pDroppedFiles, pszPathAbs);
522 if (RT_SUCCESS(rc))
523 {
524 cbFileWritten = 0;
525 DnDTransferObjectSetSize(&objCur, cbFileSize);
526 }
527 }
528 }
529 }
530 else
531 {
532 AssertMsgFailed(("ObjType=%RU32\n", DnDTransferObjectGetType(&objCur)));
533 rc = VERR_WRONG_ORDER;
534 }
535
536 RTStrFree(pszPathAbs);
537 }
538 else
539 rc = VERR_NO_MEMORY;
540 }
541
542 if ( RT_SUCCESS(rc)
543 && uNextMsg == HOST_DND_FN_HG_SND_FILE_DATA
544 && cbChunkRead)
545 {
546 uint32_t cbChunkWritten;
547 rc = DnDTransferObjectWrite(&objCur, pvChunk, cbChunkRead, &cbChunkWritten);
548 if (RT_SUCCESS(rc))
549 {
550 LogFlowFunc(("HOST_DND_FN_HG_SND_FILE_DATA: "
551 "cbChunkRead=%RU32, cbChunkWritten=%RU32, cbFileWritten=%RU64 cbFileSize=%RU64\n",
552 cbChunkRead, cbChunkWritten, cbFileWritten + cbChunkWritten, cbFileSize));
553
554 cbFileWritten += cbChunkWritten;
555
556 Assert(cbChunkRead <= cbToRecvBytes);
557 cbToRecvBytes -= cbChunkRead;
558 }
559 }
560
561 /* Data transfer complete? Close the file. */
562 bool fClose = DnDTransferObjectIsComplete(&objCur);
563 if (fClose)
564 {
565 Assert(cToRecvObjs);
566 cToRecvObjs--;
567 }
568
569 /* Only since protocol v2 we know the file size upfront. */
570 Assert(cbFileWritten <= cbFileSize);
571
572 if (fClose)
573 {
574 LogFlowFunc(("Closing file\n"));
575 DnDTransferObjectDestroy(&objCur);
576 }
577
578 break;
579 }
580 case HOST_DND_FN_CANCEL:
581 {
582 rc = vbglR3DnDHGRecvCancel(pCtx);
583 if (RT_SUCCESS(rc))
584 rc = VERR_CANCELLED;
585 break;
586 }
587 default:
588 {
589 LogRel(("DnD: Warning: Message %s (%#x) from host not supported\n", DnDGuestMsgToStr(uNextMsg), uNextMsg));
590 rc = VERR_NOT_SUPPORTED;
591 break;
592 }
593 }
594 }
595
596 if (RT_FAILURE(rc))
597 break;
598
599 LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64\n", cbToRecvBytes, cToRecvObjs));
600 if ( !cbToRecvBytes
601 && !cToRecvObjs)
602 {
603 break;
604 }
605
606 } while (RT_SUCCESS(rc));
607
608 LogFlowFunc(("Loop ended with %Rrc\n", rc));
609
610 /* All URI data processed? */
611 if (rc == VERR_NO_DATA)
612 rc = VINF_SUCCESS;
613
614 /* Delete temp buffer again. */
615 if (pvChunk)
616 RTMemFree(pvChunk);
617
618 /* Cleanup on failure or if the user has canceled the operation or
619 * something else went wrong. */
620 if (RT_FAILURE(rc))
621 {
622 LogRel(("DnD: Receiving URI data failed with %Rrc\n", rc));
623
624 DnDTransferObjectDestroy(&objCur);
625 DnDDroppedFilesRollback(pDroppedFiles);
626 }
627 else
628 {
629 LogRel2(("DnD: Receiving URI data finished\n"));
630
631 /** @todo Compare the transfer list with the dirs/files we really transferred. */
632 /** @todo Implement checksum verification, if any. */
633 }
634
635 /*
636 * Close the dropped files directory.
637 * Don't try to remove it here, however, as the files are being needed
638 * by the client's drag'n drop operation lateron.
639 */
640 int rc2 = DnDDroppedFilesReset(pDroppedFiles, false /* fRemoveDropDir */);
641 if (RT_FAILURE(rc2)) /* Not fatal, don't report back to host. */
642 LogFlowFunc(("Closing dropped files directory failed with %Rrc\n", rc2));
643
644 LogFlowFuncLeaveRC(rc);
645 return rc;
646}
647
648/**
649 * Host -> Guest
650 * Utility function to receive the HOST_DND_FN_HG_SND_DATA message from the host.
651 *
652 * @returns IPRT status code.
653 * @param pCtx DnD context to use.
654 * @param pDataHdr DnD data header to use. Need for accounting and stuff.
655 * @param pvData Where to store the received data from the host.
656 * @param cbData Size (in bytes) of where to store the received data.
657 * @param pcbDataRecv Where to store the received amount of data (in bytes).
658 */
659static int vbglR3DnDHGRecvDataRaw(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr,
660 void *pvData, uint32_t cbData, uint32_t *pcbDataRecv)
661{
662 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
663 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
664 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
665 AssertReturn(cbData, VERR_INVALID_PARAMETER);
666 AssertPtrNullReturn(pcbDataRecv, VERR_INVALID_POINTER);
667
668 LogFlowFunc(("pvDate=%p, cbData=%RU32\n", pvData, cbData));
669
670 HGCMMsgHGSendData Msg;
671 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_DATA, 5);
672 Msg.u.v3.uContext.SetUInt32(0);
673 Msg.u.v3.pvData.SetPtr(pvData, cbData);
674 Msg.u.v3.cbData.SetUInt32(0);
675 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
676 Msg.u.v3.cbChecksum.SetUInt32(0);
677
678 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
679 if (RT_SUCCESS(rc))
680 {
681 uint32_t cbDataRecv;
682 rc = Msg.u.v3.cbData.GetUInt32(&cbDataRecv);
683 AssertRC(rc);
684 if (RT_SUCCESS(rc))
685 {
686 /** @todo Use checksum for validating the received data. */
687 if (pcbDataRecv)
688 *pcbDataRecv = cbDataRecv;
689 LogFlowFuncLeaveRC(rc);
690 return rc;
691 }
692 }
693
694 /* failure */
695 LogFlowFuncLeaveRC(rc);
696 return rc;
697}
698
699/**
700 * Host -> Guest
701 * Utility function to receive the HOST_DND_FN_HG_SND_DATA_HDR message from the host.
702 *
703 * @returns IPRT status code.
704 * @param pCtx DnD context to use.
705 * @param pDataHdr Where to store the receivd DnD data header.
706 */
707static int vbglR3DnDHGRecvDataHdr(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr)
708{
709 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
710 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
711
712 Assert(pCtx->uProtocolDeprecated >= 3); /* Only for protocol v3 and up. */
713
714 HGCMMsgHGSendDataHdr Msg;
715 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_HG_SND_DATA_HDR, 12);
716 Msg.uContext.SetUInt32(0);
717 Msg.uFlags.SetUInt32(0);
718 Msg.uScreenId.SetUInt32(0);
719 Msg.cbTotal.SetUInt64(0);
720 Msg.cbMeta.SetUInt32(0);
721 Msg.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
722 Msg.cbMetaFmt.SetUInt32(0);
723 Msg.cObjects.SetUInt64(0);
724 Msg.enmCompression.SetUInt32(0);
725 Msg.enmChecksumType.SetUInt32(0);
726 Msg.pvChecksum.SetPtr(pDataHdr->pvChecksum, pDataHdr->cbChecksum);
727 Msg.cbChecksum.SetUInt32(0);
728
729 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
730 if (RT_SUCCESS(rc))
731 {
732 /* Msg.uContext not needed here. */
733 Msg.uFlags.GetUInt32(&pDataHdr->uFlags);
734 Msg.uScreenId.GetUInt32(&pDataHdr->uScreenId);
735 Msg.cbTotal.GetUInt64(&pDataHdr->cbTotal);
736 Msg.cbMeta.GetUInt32(&pDataHdr->cbMeta);
737 Msg.cbMetaFmt.GetUInt32(&pDataHdr->cbMetaFmt);
738 Msg.cObjects.GetUInt64(&pDataHdr->cObjects);
739 Msg.enmCompression.GetUInt32(&pDataHdr->enmCompression);
740 Msg.enmChecksumType.GetUInt32((uint32_t *)&pDataHdr->enmChecksumType);
741 Msg.cbChecksum.GetUInt32(&pDataHdr->cbChecksum);
742 }
743
744 LogFlowFuncLeaveRC(rc);
745 return rc;
746}
747
748/**
749 * Host -> Guest
750 * Helper function for receiving the actual DnD data from the host. Do not call directly.
751 *
752 * @returns IPRT status code.
753 * @param pCtx DnD context to use.
754 * @param pDataHdr Where to store the data header data.
755 * @param ppvData Returns the received meta data. Needs to be free'd by the caller.
756 * @param pcbData Where to store the size (in bytes) of the received meta data.
757 */
758static int vbglR3DnDHGRecvDataLoop(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr,
759 void **ppvData, uint64_t *pcbData)
760{
761 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
762 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
763 AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
764 AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
765
766 int rc;
767 uint32_t cbDataRecv;
768
769 LogFlowFuncEnter();
770
771 rc = vbglR3DnDHGRecvDataHdr(pCtx, pDataHdr);
772 if (RT_FAILURE(rc))
773 return rc;
774
775 LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU32\n", pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects));
776 if (pDataHdr->cbMeta)
777 {
778 uint64_t cbDataTmp = 0;
779 void *pvDataTmp = RTMemAlloc(pDataHdr->cbMeta);
780 if (!pvDataTmp)
781 rc = VERR_NO_MEMORY;
782
783 if (RT_SUCCESS(rc))
784 {
785 uint8_t *pvDataOff = (uint8_t *)pvDataTmp;
786 while (cbDataTmp < pDataHdr->cbMeta)
787 {
788 rc = vbglR3DnDHGRecvDataRaw(pCtx, pDataHdr,
789 pvDataOff, RT_MIN(pDataHdr->cbMeta - cbDataTmp, pCtx->cbMaxChunkSize),
790 &cbDataRecv);
791 if (RT_SUCCESS(rc))
792 {
793 LogFlowFunc(("cbDataRecv=%RU32, cbDataTmp=%RU64\n", cbDataRecv, cbDataTmp));
794 Assert(cbDataTmp + cbDataRecv <= pDataHdr->cbMeta);
795 cbDataTmp += cbDataRecv;
796 pvDataOff += cbDataRecv;
797 }
798 else
799 break;
800 }
801
802 if (RT_SUCCESS(rc))
803 {
804 Assert(cbDataTmp == pDataHdr->cbMeta);
805
806 LogFlowFunc(("Received %RU64 bytes of data\n", cbDataTmp));
807
808 *ppvData = pvDataTmp;
809 *pcbData = cbDataTmp;
810 }
811 else
812 RTMemFree(pvDataTmp);
813 }
814 }
815 else
816 {
817 *ppvData = NULL;
818 *pcbData = 0;
819 }
820
821 LogFlowFuncLeaveRC(rc);
822 return rc;
823}
824
825/**
826 * Host -> Guest
827 * Main function for receiving the actual DnD data from the host.
828 *
829 * @returns IPRT status code.
830 * @param pCtx DnD context to use.
831 * @param pMeta Where to store the actual meta data received from the host.
832 */
833static int vbglR3DnDHGRecvDataMain(PVBGLR3GUESTDNDCMDCTX pCtx,
834 PVBGLR3GUESTDNDMETADATA pMeta)
835{
836 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
837 AssertPtrReturn(pMeta, VERR_INVALID_POINTER);
838
839 AssertMsgReturn(pCtx->cbMaxChunkSize, ("Maximum chunk size must not be 0\n"), VERR_INVALID_PARAMETER);
840
841 VBOXDNDDATAHDR dataHdr;
842 RT_ZERO(dataHdr);
843 dataHdr.cbMetaFmt = pCtx->cbMaxChunkSize;
844 dataHdr.pvMetaFmt = RTMemAlloc(dataHdr.cbMetaFmt);
845 if (!dataHdr.pvMetaFmt)
846 return VERR_NO_MEMORY;
847
848 void *pvData = NULL;
849 uint64_t cbData = 0;
850
851 int rc = vbglR3DnDHGRecvDataLoop(pCtx, &dataHdr, &pvData, &cbData);
852 if (RT_SUCCESS(rc))
853 {
854 LogRel2(("DnD: Received %RU64 bytes meta data in format '%s'\n", cbData, (char *)dataHdr.pvMetaFmt));
855
856 /**
857 * Check if this is an URI event. If so, let VbglR3 do all the actual
858 * data transfer + file/directory creation internally without letting
859 * the caller know.
860 *
861 * This keeps the actual (guest OS-)dependent client (like VBoxClient /
862 * VBoxTray) small by not having too much redundant code.
863 */
864 Assert(dataHdr.cbMetaFmt);
865 AssertPtr(dataHdr.pvMetaFmt);
866 if (DnDMIMEHasFileURLs((char *)dataHdr.pvMetaFmt, dataHdr.cbMetaFmt)) /* URI data. */
867 {
868 DNDDROPPEDFILES droppedFiles;
869 RT_ZERO(droppedFiles);
870
871 rc = DnDDroppedFilesInit(&droppedFiles);
872 if (RT_SUCCESS(rc))
873 rc = DnDDroppedFilesOpenTemp(&droppedFiles, DNDURIDROPPEDFILE_FLAGS_NONE);
874
875 if (RT_FAILURE(rc))
876 {
877 LogRel(("DnD: Initializing dropped files directory failed with %Rrc\n", rc));
878 }
879 else
880 {
881 AssertPtr(pvData);
882 Assert(cbData);
883
884 /* Use the dropped files directory as the root directory for the current transfer. */
885 rc = DnDTransferListInitEx(&pMeta->u.URI.Transfer, DnDDroppedFilesGetDirAbs(&droppedFiles),
886 DNDTRANSFERLISTFMT_NATIVE);
887 if (RT_SUCCESS(rc))
888 {
889 rc = DnDTransferListAppendRootsFromBuffer(&pMeta->u.URI.Transfer, DNDTRANSFERLISTFMT_URI, (const char *)pvData, cbData,
890 DND_PATH_SEPARATOR_STR, 0 /* fFlags */);
891 if (RT_SUCCESS(rc))
892 {
893 rc = vbglR3DnDHGRecvURIData(pCtx, &dataHdr, &droppedFiles);
894 if (RT_SUCCESS(rc))
895 {
896 pMeta->enmType = VBGLR3GUESTDNDMETADATATYPE_URI_LIST;
897 }
898 }
899 }
900 }
901 }
902 else /* Raw data. */
903 {
904 pMeta->u.Raw.cbMeta = cbData;
905 pMeta->u.Raw.pvMeta = pvData;
906
907 pMeta->enmType = VBGLR3GUESTDNDMETADATATYPE_RAW;
908 }
909 }
910
911 if (dataHdr.pvMetaFmt)
912 RTMemFree(dataHdr.pvMetaFmt);
913
914 if (RT_FAILURE(rc))
915 {
916 if (pvData)
917 RTMemFree(pvData);
918
919 if (rc != VERR_CANCELLED)
920 {
921 LogRel(("DnD: Receiving data failed with %Rrc\n", rc));
922
923 int rc2 = VbglR3DnDHGSendProgress(pCtx, DND_PROGRESS_ERROR, 100 /* Percent */, rc);
924 if (RT_FAILURE(rc2))
925 LogRel(("DnD: Unable to send progress error %Rrc to host: %Rrc\n", rc, rc2));
926 }
927 }
928
929 LogFlowFuncLeaveRC(rc);
930 return rc;
931}
932
933#ifdef VBOX_WITH_DRAG_AND_DROP_GH
934/**
935 * Guest -> Host
936 * Utility function to receive the HOST_DND_FN_GH_REQ_PENDING message from the host.
937 *
938 * @returns IPRT status code.
939 * @param pCtx DnD context to use.
940 * @param puScreenID For which screen on the host the request is for. Optional.
941 */
942static int vbglR3DnDGHRecvPending(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puScreenID)
943{
944 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
945 /* pScreenID is optional. */
946
947 HGCMMsgGHReqPending Msg;
948 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_GH_REQ_PENDING, 2);
949 /** @todo Context ID not used yet. */
950 Msg.u.v3.uContext.SetUInt32(0);
951 Msg.u.v3.uScreenId.SetUInt32(0);
952
953 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
954 if (RT_SUCCESS(rc))
955 {
956 /** @todo Context ID not used yet. */
957 if (puScreenID)
958 rc = Msg.u.v3.uContext.GetUInt32(puScreenID);
959 }
960
961 return rc;
962}
963
964/**
965 * Guest -> Host
966 * Utility function to receive the HOST_DND_FN_GH_EVT_DROPPED message from the host.
967 *
968 * @returns IPRT status code.
969 * @param pCtx DnD context to use.
970 * @param ppszFormat Requested data format from the host. Optional.
971 * @param pcbFormat Size of requested data format (in bytes). Optional.
972 * @param puAction Requested action from the host. Optional.
973 */
974static int vbglR3DnDGHRecvDropped(PVBGLR3GUESTDNDCMDCTX pCtx,
975 char **ppszFormat,
976 uint32_t *pcbFormat,
977 uint32_t *puAction)
978{
979 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
980 /* The rest is optional. */
981
982 const uint32_t cbFormatTmp = pCtx->cbMaxChunkSize;
983
984 char *pszFormatTmp = static_cast<char *>(RTMemAlloc(cbFormatTmp));
985 if (!pszFormatTmp)
986 return VERR_NO_MEMORY;
987
988 HGCMMsgGHDropped Msg;
989 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_FN_GH_EVT_DROPPED, 4);
990 Msg.u.v3.uContext.SetUInt32(0);
991 Msg.u.v3.pvFormat.SetPtr(pszFormatTmp, cbFormatTmp);
992 Msg.u.v3.cbFormat.SetUInt32(0);
993 Msg.u.v3.uAction.SetUInt32(0);
994
995 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
996 if (RT_SUCCESS(rc))
997 {
998 /** @todo Context ID not used yet. */
999 if (pcbFormat)
1000 rc = Msg.u.v3.cbFormat.GetUInt32(pcbFormat);
1001 if (RT_SUCCESS(rc) && puAction)
1002 rc = Msg.u.v3.uAction.GetUInt32(puAction);
1003
1004 if (RT_SUCCESS(rc))
1005 {
1006 *ppszFormat = RTStrDup(pszFormatTmp);
1007 if (!*ppszFormat)
1008 rc = VERR_NO_MEMORY;
1009 }
1010 }
1011
1012 RTMemFree(pszFormatTmp);
1013
1014 return rc;
1015}
1016#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1017
1018
1019/*********************************************************************************************************************************
1020* Public functions *
1021*********************************************************************************************************************************/
1022
1023/**
1024 * Connects a DnD context to the DnD host service.
1025 *
1026 * @returns IPRT status code.
1027 * @param pCtx DnD context to connect.
1028 */
1029VBGLR3DECL(int) VbglR3DnDConnect(PVBGLR3GUESTDNDCMDCTX pCtx)
1030{
1031 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1032
1033 /* Initialize header */
1034 int rc = VbglR3HGCMConnect("VBoxDragAndDropSvc", &pCtx->uClientID);
1035 if (RT_FAILURE(rc))
1036 return rc;
1037 Assert(pCtx->uClientID);
1038
1039 /* Set the default protocol version we would like to use.
1040 * Deprecated since VBox 6.1.x, but let this set to 3 to (hopefully) not break things. */
1041 pCtx->uProtocolDeprecated = 3;
1042
1043 pCtx->fHostFeatures = VBOX_DND_HF_NONE;
1044 pCtx->fGuestFeatures = VBOX_DND_GF_NONE;
1045
1046 /*
1047 * Get the VM's session ID.
1048 * This is not fatal in case we're running with an ancient VBox version.
1049 */
1050 pCtx->uSessionID = 0;
1051 int rc2 = VbglR3GetSessionId(&pCtx->uSessionID); RT_NOREF(rc2);
1052 LogFlowFunc(("uSessionID=%RU64, rc=%Rrc\n", pCtx->uSessionID, rc2));
1053
1054 /*
1055 * Try sending the connect message to tell the protocol version to use.
1056 * Note: This might fail when the Guest Additions run on an older VBox host (< VBox 5.0) which
1057 * does not implement this command.
1058 */
1059 HGCMMsgConnect Msg;
1060 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_CONNECT, 3);
1061 Msg.u.v3.uContext.SetUInt32(0); /** @todo Context ID not used yet. */
1062 Msg.u.v3.uProtocol.SetUInt32(pCtx->uProtocolDeprecated); /* Deprecated since VBox 6.1.x. */
1063 Msg.u.v3.uFlags.SetUInt32(0); /* Unused at the moment. */
1064
1065 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1066 if (RT_SUCCESS(rc))
1067 {
1068 /* Set the protocol version we're going to use as told by the host. */
1069 rc = Msg.u.v3.uProtocol.GetUInt32(&pCtx->uProtocolDeprecated); AssertRC(rc);
1070
1071 /*
1072 * Next is reporting our features. If this fails, assume older host.
1073 */
1074 rc2 = VbglR3DnDReportFeatures(pCtx->uClientID, pCtx->fGuestFeatures, &pCtx->fHostFeatures);
1075 if (RT_SUCCESS(rc2))
1076 {
1077 LogRel2(("DnD: Guest features: %#RX64 - Host features: %#RX64\n",
1078 pCtx->fGuestFeatures, pCtx->fHostFeatures));
1079 }
1080 else /* Failing here is not fatal; might be running with an older host. */
1081 {
1082 AssertLogRelMsg(rc2 == VERR_NOT_SUPPORTED || rc2 == VERR_NOT_IMPLEMENTED,
1083 ("Reporting features failed: %Rrc\n", rc2));
1084 }
1085
1086 pCtx->cbMaxChunkSize = DND_DEFAULT_CHUNK_SIZE; /** @todo Use a scratch buffer on the heap? */
1087 }
1088 else
1089 pCtx->uProtocolDeprecated = 0; /* We're using protocol v0 (initial draft) as a fallback. */
1090
1091 LogFlowFunc(("uClient=%RU32, uProtocol=%RU32, rc=%Rrc\n", pCtx->uClientID, pCtx->uProtocolDeprecated, rc));
1092 return rc;
1093}
1094
1095/**
1096 * Disconnects a given DnD context from the DnD host service.
1097 *
1098 * @returns IPRT status code.
1099 * @param pCtx DnD context to disconnect.
1100 * The context is invalid afterwards on successful disconnection.
1101 */
1102VBGLR3DECL(int) VbglR3DnDDisconnect(PVBGLR3GUESTDNDCMDCTX pCtx)
1103{
1104 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1105
1106 if (!pCtx->uClientID) /* Already disconnected? Bail out early. */
1107 return VINF_SUCCESS;
1108
1109 int rc = VbglR3HGCMDisconnect(pCtx->uClientID);
1110 if (RT_SUCCESS(rc))
1111 pCtx->uClientID = 0;
1112
1113 return rc;
1114}
1115
1116/**
1117 * Reports features to the host and retrieve host feature set.
1118 *
1119 * @returns VBox status code.
1120 * @param idClient The client ID returned by VbglR3DnDConnect().
1121 * @param fGuestFeatures Features to report, VBOX_DND_GF_XXX.
1122 * @param pfHostFeatures Where to store the features VBOX_DND_HF_XXX.
1123 */
1124VBGLR3DECL(int) VbglR3DnDReportFeatures(uint32_t idClient, uint64_t fGuestFeatures, uint64_t *pfHostFeatures)
1125{
1126 int rc;
1127 do
1128 {
1129 struct
1130 {
1131 VBGLIOCHGCMCALL Hdr;
1132 HGCMFunctionParameter f64Features0;
1133 HGCMFunctionParameter f64Features1;
1134 } Msg;
1135 VBGL_HGCM_HDR_INIT(&Msg.Hdr, idClient, GUEST_DND_FN_REPORT_FEATURES, 2);
1136 VbglHGCMParmUInt64Set(&Msg.f64Features0, fGuestFeatures);
1137 VbglHGCMParmUInt64Set(&Msg.f64Features1, VBOX_DND_GF_1_MUST_BE_ONE);
1138
1139 rc = VbglR3HGCMCall(&Msg.Hdr, sizeof(Msg));
1140 if (RT_SUCCESS(rc))
1141 {
1142 Assert(Msg.f64Features0.type == VMMDevHGCMParmType_64bit);
1143 Assert(Msg.f64Features1.type == VMMDevHGCMParmType_64bit);
1144 if (Msg.f64Features1.u.value64 & VBOX_DND_GF_1_MUST_BE_ONE)
1145 rc = VERR_NOT_SUPPORTED;
1146 else if (pfHostFeatures)
1147 *pfHostFeatures = Msg.f64Features0.u.value64;
1148 break;
1149 }
1150 } while (rc == VERR_INTERRUPTED);
1151 return rc;
1152
1153}
1154
1155/**
1156 * Receives the next upcoming DnD event.
1157 *
1158 * This is the main function DnD clients call in order to implement any DnD functionality.
1159 * The purpose of it is to abstract the actual DnD protocol handling as much as possible from
1160 * the clients -- those only need to react to certain events, regardless of how the underlying
1161 * protocol actually is working.
1162 *
1163 * @returns IPRT status code.
1164 * @param pCtx DnD context to work with.
1165 * @param ppEvent Next DnD event received on success; needs to be free'd by the client calling
1166 * VbglR3DnDEventFree() when done.
1167 */
1168VBGLR3DECL(int) VbglR3DnDEventGetNext(PVBGLR3GUESTDNDCMDCTX pCtx, PVBGLR3DNDEVENT *ppEvent)
1169{
1170 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1171 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
1172
1173 PVBGLR3DNDEVENT pEvent = (PVBGLR3DNDEVENT)RTMemAllocZ(sizeof(VBGLR3DNDEVENT));
1174 if (!pEvent)
1175 return VERR_NO_MEMORY;
1176
1177 uint32_t uMsg = 0;
1178 uint32_t cParms = 0;
1179 int rc = vbglR3DnDGetNextMsgType(pCtx, &uMsg, &cParms, true /* fWait */);
1180 if (RT_SUCCESS(rc))
1181 {
1182 /* Check for VM session change. */
1183 uint64_t uSessionID;
1184 int rc2 = VbglR3GetSessionId(&uSessionID);
1185 if ( RT_SUCCESS(rc2)
1186 && (uSessionID != pCtx->uSessionID))
1187 {
1188 LogRel2(("DnD: VM session ID changed to %RU64\n", uSessionID));
1189 rc = VbglR3DnDDisconnect(pCtx);
1190 if (RT_SUCCESS(rc))
1191 rc = VbglR3DnDConnect(pCtx);
1192 }
1193 }
1194
1195 if (rc == VERR_CANCELLED) /* Host service told us that we have to bail out. */
1196 {
1197 LogRel2(("DnD: Host service requested termination\n"));
1198
1199 pEvent->enmType = VBGLR3DNDEVENTTYPE_QUIT;
1200 *ppEvent = pEvent;
1201
1202 return VINF_SUCCESS;
1203 }
1204
1205 if (RT_SUCCESS(rc))
1206 {
1207 LogFunc(("Handling uMsg=%RU32\n", uMsg));
1208
1209 switch(uMsg)
1210 {
1211 case HOST_DND_FN_HG_EVT_ENTER:
1212 {
1213 rc = vbglR3DnDHGRecvAction(pCtx,
1214 uMsg,
1215 &pEvent->u.HG_Enter.uScreenID,
1216 NULL /* puXPos */,
1217 NULL /* puYPos */,
1218 NULL /* uDefAction */,
1219 &pEvent->u.HG_Enter.dndLstActionsAllowed,
1220 &pEvent->u.HG_Enter.pszFormats,
1221 &pEvent->u.HG_Enter.cbFormats);
1222 if (RT_SUCCESS(rc))
1223 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_ENTER;
1224 break;
1225 }
1226 case HOST_DND_FN_HG_EVT_MOVE:
1227 {
1228 rc = vbglR3DnDHGRecvAction(pCtx,
1229 uMsg,
1230 NULL /* puScreenId */,
1231 &pEvent->u.HG_Move.uXpos,
1232 &pEvent->u.HG_Move.uYpos,
1233 &pEvent->u.HG_Move.dndActionDefault,
1234 NULL /* puAllActions */,
1235 NULL /* pszFormats */,
1236 NULL /* pcbFormats */);
1237 if (RT_SUCCESS(rc))
1238 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_MOVE;
1239 break;
1240 }
1241 case HOST_DND_FN_HG_EVT_DROPPED:
1242 {
1243 rc = vbglR3DnDHGRecvAction(pCtx,
1244 uMsg,
1245 NULL /* puScreenId */,
1246 &pEvent->u.HG_Drop.uXpos,
1247 &pEvent->u.HG_Drop.uYpos,
1248 &pEvent->u.HG_Drop.dndActionDefault,
1249 NULL /* puAllActions */,
1250 NULL /* pszFormats */,
1251 NULL /* pcbFormats */);
1252 if (RT_SUCCESS(rc))
1253 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_DROP;
1254 break;
1255 }
1256 case HOST_DND_FN_HG_EVT_LEAVE:
1257 {
1258 rc = vbglR3DnDHGRecvLeave(pCtx);
1259 if (RT_SUCCESS(rc))
1260 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_LEAVE;
1261 break;
1262 }
1263 case HOST_DND_FN_HG_SND_DATA_HDR:
1264 {
1265 rc = vbglR3DnDHGRecvDataMain(pCtx, &pEvent->u.HG_Received.Meta);
1266 if (RT_SUCCESS(rc))
1267 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_RECEIVE;
1268 break;
1269 }
1270 case HOST_DND_FN_HG_SND_DIR:
1271 RT_FALL_THROUGH();
1272 case HOST_DND_FN_HG_SND_FILE_HDR:
1273 RT_FALL_THROUGH();
1274 case HOST_DND_FN_HG_SND_FILE_DATA:
1275 {
1276 /*
1277 * All messages for this block are handled internally
1278 * by vbglR3DnDHGRecvDataMain(), see above.
1279 *
1280 * So if we land here our code is buggy.
1281 */
1282 rc = VERR_WRONG_ORDER;
1283 break;
1284 }
1285 case HOST_DND_FN_CANCEL:
1286 {
1287 rc = vbglR3DnDHGRecvCancel(pCtx);
1288 if (RT_SUCCESS(rc))
1289 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_CANCEL;
1290 break;
1291 }
1292#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1293 case HOST_DND_FN_GH_REQ_PENDING:
1294 {
1295 rc = vbglR3DnDGHRecvPending(pCtx, &pEvent->u.GH_IsPending.uScreenID);
1296 if (RT_SUCCESS(rc))
1297 pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_REQ_PENDING;
1298 break;
1299 }
1300 case HOST_DND_FN_GH_EVT_DROPPED:
1301 {
1302 rc = vbglR3DnDGHRecvDropped(pCtx,
1303 &pEvent->u.GH_Drop.pszFormat,
1304 &pEvent->u.GH_Drop.cbFormat,
1305 &pEvent->u.GH_Drop.dndActionRequested);
1306 if (RT_SUCCESS(rc))
1307 pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_DROP;
1308 break;
1309 }
1310#endif
1311 default:
1312 {
1313 rc = VERR_NOT_SUPPORTED;
1314 break;
1315 }
1316 }
1317 }
1318
1319 if (RT_FAILURE(rc))
1320 {
1321 VbglR3DnDEventFree(pEvent);
1322 LogRel(("DnD: Handling message %s (%#x) failed with %Rrc\n", DnDHostMsgToStr(uMsg), uMsg, rc));
1323 }
1324 else
1325 *ppEvent = pEvent;
1326
1327 return rc;
1328}
1329
1330/**
1331 * Frees (destroys) a formerly allocated DnD event.
1332 *
1333 * @returns IPRT status code.
1334 * @param pEvent Event to free (destroy).
1335 */
1336VBGLR3DECL(void) VbglR3DnDEventFree(PVBGLR3DNDEVENT pEvent)
1337{
1338 if (!pEvent)
1339 return;
1340
1341 /* Some messages require additional cleanup. */
1342 switch (pEvent->enmType)
1343 {
1344 case VBGLR3DNDEVENTTYPE_HG_ENTER:
1345 {
1346 if (pEvent->u.HG_Enter.pszFormats)
1347 RTStrFree(pEvent->u.HG_Enter.pszFormats);
1348 break;
1349 }
1350
1351#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1352 case VBGLR3DNDEVENTTYPE_GH_DROP:
1353 {
1354 if (pEvent->u.GH_Drop.pszFormat)
1355 RTStrFree(pEvent->u.GH_Drop.pszFormat);
1356 break;
1357 }
1358#endif
1359 case VBGLR3DNDEVENTTYPE_HG_RECEIVE:
1360 {
1361 PVBGLR3GUESTDNDMETADATA pMeta = &pEvent->u.HG_Received.Meta;
1362 switch (pMeta->enmType)
1363 {
1364 case VBGLR3GUESTDNDMETADATATYPE_RAW:
1365 {
1366 if (pMeta->u.Raw.pvMeta)
1367 {
1368 Assert(pMeta->u.Raw.cbMeta);
1369 RTMemFree(pMeta->u.Raw.pvMeta);
1370 pMeta->u.Raw.cbMeta = 0;
1371 }
1372 break;
1373 }
1374
1375 case VBGLR3GUESTDNDMETADATATYPE_URI_LIST:
1376 {
1377 DnDTransferListDestroy(&pMeta->u.URI.Transfer);
1378 break;
1379 }
1380
1381 default:
1382 break;
1383 }
1384 break;
1385 }
1386
1387 default:
1388 break;
1389 }
1390
1391 RTMemFree(pEvent);
1392 pEvent = NULL;
1393}
1394
1395VBGLR3DECL(int) VbglR3DnDHGSendAckOp(PVBGLR3GUESTDNDCMDCTX pCtx, VBOXDNDACTION dndAction)
1396{
1397 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1398
1399 HGCMMsgHGAck Msg;
1400 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_HG_ACK_OP, 2);
1401 /** @todo Context ID not used yet. */
1402 Msg.u.v3.uContext.SetUInt32(0);
1403 Msg.u.v3.uAction.SetUInt32(dndAction);
1404
1405 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1406}
1407
1408/**
1409 * Host -> Guest
1410 * Requests the actual DnD data to be sent from the host.
1411 *
1412 * @returns IPRT status code.
1413 * @param pCtx DnD context to use.
1414 * @param pcszFormat Format to request the data from the host in.
1415 */
1416VBGLR3DECL(int) VbglR3DnDHGSendReqData(PVBGLR3GUESTDNDCMDCTX pCtx, const char* pcszFormat)
1417{
1418 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1419 AssertPtrReturn(pcszFormat, VERR_INVALID_POINTER);
1420 if (!RTStrIsValidEncoding(pcszFormat))
1421 return VERR_INVALID_PARAMETER;
1422
1423 const uint32_t cbFormat = (uint32_t)strlen(pcszFormat) + 1; /* Include termination */
1424
1425 HGCMMsgHGReqData Msg;
1426 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_HG_REQ_DATA, 3);
1427 /** @todo Context ID not used yet. */
1428 Msg.u.v3.uContext.SetUInt32(0);
1429 Msg.u.v3.pvFormat.SetPtr((void*)pcszFormat, cbFormat);
1430 Msg.u.v3.cbFormat.SetUInt32(cbFormat);
1431
1432 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1433}
1434
1435/**
1436 * Host -> Guest
1437 * Reports back its progress back to the host.
1438 *
1439 * @returns IPRT status code.
1440 * @param pCtx DnD context to use.
1441 * @param uStatus DnD status to report.
1442 * @param uPercent Overall progress (in percent) to report.
1443 * @param rcErr Error code (IPRT-style) to report.
1444 */
1445VBGLR3DECL(int) VbglR3DnDHGSendProgress(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uStatus, uint8_t uPercent, int rcErr)
1446{
1447 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1448 AssertReturn(uStatus > DND_PROGRESS_UNKNOWN, VERR_INVALID_PARAMETER);
1449
1450 HGCMMsgHGProgress Msg;
1451 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_HG_EVT_PROGRESS, 4);
1452 /** @todo Context ID not used yet. */
1453 Msg.u.v3.uContext.SetUInt32(0);
1454 Msg.u.v3.uStatus.SetUInt32(uStatus);
1455 Msg.u.v3.uPercent.SetUInt32(uPercent);
1456 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1457
1458 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1459}
1460
1461#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1462/**
1463 * Guest -> Host
1464 * Acknowledges that there currently is a drag'n drop operation in progress on the guest,
1465 * which eventually could be dragged over to the host.
1466 *
1467 * @returns IPRT status code.
1468 * @param pCtx DnD context to use.
1469 * @param dndActionDefault Default action for the operation to report.
1470 * @param dndLstActionsAllowed All available actions for the operation to report.
1471 * @param pcszFormats Available formats for the operation to report.
1472 * @param cbFormats Size (in bytes) of formats to report.
1473 */
1474VBGLR3DECL(int) VbglR3DnDGHSendAckPending(PVBGLR3GUESTDNDCMDCTX pCtx,
1475 VBOXDNDACTION dndActionDefault, VBOXDNDACTIONLIST dndLstActionsAllowed,
1476 const char* pcszFormats, uint32_t cbFormats)
1477{
1478 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1479 AssertPtrReturn(pcszFormats, VERR_INVALID_POINTER);
1480 AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
1481
1482 if (!RTStrIsValidEncoding(pcszFormats))
1483 return VERR_INVALID_UTF8_ENCODING;
1484
1485 HGCMMsgGHAckPending Msg;
1486 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_ACK_PENDING, 5);
1487 /** @todo Context ID not used yet. */
1488 Msg.u.v3.uContext.SetUInt32(0);
1489 Msg.u.v3.uDefAction.SetUInt32(dndActionDefault);
1490 Msg.u.v3.uAllActions.SetUInt32(dndLstActionsAllowed);
1491 Msg.u.v3.pvFormats.SetPtr((void*)pcszFormats, cbFormats);
1492 Msg.u.v3.cbFormats.SetUInt32(cbFormats);
1493
1494 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1495}
1496
1497/**
1498 * Guest -> Host
1499 * Utility function to send DnD data from guest to the host.
1500 *
1501 * @returns IPRT status code.
1502 * @param pCtx DnD context to use.
1503 * @param pvData Data block to send.
1504 * @param cbData Size (in bytes) of data block to send.
1505 * @param pDataHdr Data header to use -- needed for accounting.
1506 */
1507static int vbglR3DnDGHSendDataInternal(PVBGLR3GUESTDNDCMDCTX pCtx,
1508 void *pvData, uint64_t cbData, PVBOXDNDSNDDATAHDR pDataHdr)
1509{
1510 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1511 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1512 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1513 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
1514
1515 HGCMMsgGHSendDataHdr MsgHdr;
1516 VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_DATA_HDR, 12);
1517 MsgHdr.uContext.SetUInt32(0); /** @todo Not used yet. */
1518 MsgHdr.uFlags.SetUInt32(0); /** @todo Not used yet. */
1519 MsgHdr.uScreenId.SetUInt32(0); /** @todo Not used for guest->host (yet). */
1520 MsgHdr.cbTotal.SetUInt64(pDataHdr->cbTotal);
1521 MsgHdr.cbMeta.SetUInt32(pDataHdr->cbMeta);
1522 MsgHdr.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
1523 MsgHdr.cbMetaFmt.SetUInt32(pDataHdr->cbMetaFmt);
1524 MsgHdr.cObjects.SetUInt64(pDataHdr->cObjects);
1525 MsgHdr.enmCompression.SetUInt32(0); /** @todo Not used yet. */
1526 MsgHdr.enmChecksumType.SetUInt32(RTDIGESTTYPE_INVALID); /** @todo Not used yet. */
1527 MsgHdr.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1528 MsgHdr.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1529
1530 int rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr));
1531
1532 LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU64, rc=%Rrc\n",
1533 pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects, rc));
1534
1535 if (RT_SUCCESS(rc))
1536 {
1537 HGCMMsgGHSendData MsgData;
1538 VBGL_HGCM_HDR_INIT(&MsgData.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_DATA, 5);
1539 MsgData.u.v3.uContext.SetUInt32(0); /** @todo Not used yet. */
1540 MsgData.u.v3.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1541 MsgData.u.v3.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1542
1543 uint32_t cbCurChunk;
1544 const uint32_t cbMaxChunk = pCtx->cbMaxChunkSize;
1545 uint32_t cbSent = 0;
1546
1547 while (cbSent < cbData)
1548 {
1549 cbCurChunk = RT_MIN(cbData - cbSent, cbMaxChunk);
1550 MsgData.u.v3.pvData.SetPtr(static_cast<uint8_t *>(pvData) + cbSent, cbCurChunk);
1551 MsgData.u.v3.cbData.SetUInt32(cbCurChunk);
1552
1553 rc = VbglR3HGCMCall(&MsgData.hdr, sizeof(MsgData));
1554 if (RT_FAILURE(rc))
1555 break;
1556
1557 cbSent += cbCurChunk;
1558 }
1559
1560 LogFlowFunc(("cbMaxChunk=%RU32, cbData=%RU64, cbSent=%RU32, rc=%Rrc\n",
1561 cbMaxChunk, cbData, cbSent, rc));
1562
1563 if (RT_SUCCESS(rc))
1564 Assert(cbSent == cbData);
1565 }
1566
1567 LogFlowFuncLeaveRC(rc);
1568 return rc;
1569}
1570
1571/**
1572 * Guest -> Host
1573 * Utility function to send a guest directory to the host.
1574 *
1575 * @returns IPRT status code.
1576 * @param pCtx DnD context to use.
1577 * @param pObj transfer object containing the directory to send.
1578 */
1579static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DNDTRANSFEROBJECT *pObj)
1580{
1581 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1582 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1583 AssertReturn(DnDTransferObjectGetType(pObj) == DNDTRANSFEROBJTYPE_DIRECTORY, VERR_INVALID_PARAMETER);
1584
1585 const char *pcszPath = DnDTransferObjectGetDestPath(pObj);
1586 const size_t cbPath = RTStrNLen(pcszPath, RTPATH_MAX) + 1 /* Include termination. */;
1587 const RTFMODE fMode = DnDTransferObjectGetMode(pObj);
1588
1589 LogFlowFunc(("strDir=%s (%zu bytes), fMode=0x%x\n", pcszPath, cbPath, fMode));
1590
1591 if (cbPath > RTPATH_MAX + 1) /* Can't happen, but check anyway. */
1592 return VERR_INVALID_PARAMETER;
1593
1594 HGCMMsgGHSendDir Msg;
1595 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_DIR, 4);
1596 /** @todo Context ID not used yet. */
1597 Msg.u.v3.uContext.SetUInt32(0);
1598 Msg.u.v3.pvName.SetPtr((void *)pcszPath, (uint32_t)cbPath);
1599 Msg.u.v3.cbName.SetUInt32((uint32_t)cbPath);
1600 Msg.u.v3.fMode.SetUInt32(fMode);
1601
1602 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1603}
1604
1605/**
1606 * Guest -> Host
1607 * Utility function to send a file from the guest to the host.
1608 *
1609 * @returns IPRT status code.
1610 * @param pCtx DnD context to use.
1611 * @param pObj Transfer object containing the file to send.
1612 */
1613static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFEROBJECT pObj)
1614{
1615 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1616 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1617 AssertReturn(DnDTransferObjectIsOpen(pObj) == false, VERR_INVALID_STATE);
1618 AssertReturn(DnDTransferObjectGetType(pObj) == DNDTRANSFEROBJTYPE_FILE, VERR_INVALID_PARAMETER);
1619
1620 uint64_t fOpen = RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE;
1621
1622 int rc = DnDTransferObjectOpen(pObj, fOpen, 0 /* fMode */, DNDTRANSFEROBJECT_FLAGS_NONE);
1623 if (RT_FAILURE(rc))
1624 return rc;
1625
1626 uint32_t cbBuf = pCtx->cbMaxChunkSize;
1627 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1628 void *pvBuf = RTMemAlloc(cbBuf); /** @todo Make this buffer part of PVBGLR3GUESTDNDCMDCTX? */
1629 if (!pvBuf)
1630 {
1631 DnDTransferObjectClose(pObj);
1632 return VERR_NO_MEMORY;
1633 }
1634
1635 const char *pcszPath = DnDTransferObjectGetDestPath(pObj);
1636 const size_t cchPath = RTStrNLen(pcszPath, RTPATH_MAX);
1637 const uint64_t cbSize = DnDTransferObjectGetSize(pObj);
1638 const RTFMODE fMode = DnDTransferObjectGetMode(pObj);
1639
1640 LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", pcszPath, cchPath, cbSize, fMode));
1641
1642 HGCMMsgGHSendFileHdr MsgHdr;
1643 VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_FILE_HDR, 6);
1644 MsgHdr.uContext.SetUInt32(0); /* Context ID; unused at the moment. */
1645 MsgHdr.pvName.SetPtr((void *)pcszPath, (uint32_t)(cchPath + 1)); /* Include termination. */
1646 MsgHdr.cbName.SetUInt32((uint32_t)(cchPath + 1)); /* Ditto. */
1647 MsgHdr.uFlags.SetUInt32(0); /* Flags; unused at the moment. */
1648 MsgHdr.fMode.SetUInt32(fMode); /* File mode */
1649 MsgHdr.cbTotal.SetUInt64(cbSize); /* File size (in bytes). */
1650
1651 rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr));
1652
1653 LogFlowFunc(("Sending file header resulted in %Rrc\n", rc));
1654
1655 if (RT_SUCCESS(rc))
1656 {
1657 /*
1658 * Send the actual file data, chunk by chunk.
1659 */
1660 HGCMMsgGHSendFileData Msg;
1661 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_SND_FILE_DATA, 5);
1662 Msg.u.v3.uContext.SetUInt32(0);
1663 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
1664 Msg.u.v3.cbChecksum.SetUInt32(0);
1665
1666 uint64_t cbToReadTotal = cbSize;
1667 uint64_t cbWrittenTotal = 0;
1668 while (cbToReadTotal)
1669 {
1670 uint32_t cbToRead = RT_MIN(cbToReadTotal, cbBuf);
1671 uint32_t cbRead = 0;
1672 if (cbToRead)
1673 rc = DnDTransferObjectRead(pObj, pvBuf, cbToRead, &cbRead);
1674
1675 LogFlowFunc(("cbToReadTotal=%RU64, cbToRead=%RU32, cbRead=%RU32, rc=%Rrc\n",
1676 cbToReadTotal, cbToRead, cbRead, rc));
1677
1678 if ( RT_SUCCESS(rc)
1679 && cbRead)
1680 {
1681 Msg.u.v3.pvData.SetPtr(pvBuf, cbRead);
1682 Msg.u.v3.cbData.SetUInt32(cbRead);
1683 /** @todo Calculate + set checksums. */
1684
1685 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1686 }
1687
1688 if (RT_FAILURE(rc))
1689 {
1690 LogFlowFunc(("Reading failed with rc=%Rrc\n", rc));
1691 break;
1692 }
1693
1694 Assert(cbRead <= cbToReadTotal);
1695 cbToReadTotal -= cbRead;
1696 cbWrittenTotal += cbRead;
1697
1698 LogFlowFunc(("%RU64/%RU64 -- %RU8%%\n", cbWrittenTotal, cbSize, cbWrittenTotal * 100 / cbSize));
1699 };
1700 }
1701
1702 RTMemFree(pvBuf);
1703 DnDTransferObjectClose(pObj);
1704
1705 LogFlowFuncLeaveRC(rc);
1706 return rc;
1707}
1708
1709/**
1710 * Guest -> Host
1711 * Utility function to send a transfer object from guest to the host.
1712 *
1713 * @returns IPRT status code.
1714 * @param pCtx DnD context to use.
1715 * @param pObj Transfer object to send from guest to the host.
1716 */
1717static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFEROBJECT pObj)
1718{
1719 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1720 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1721
1722 int rc;
1723
1724 const DNDTRANSFEROBJTYPE enmType = DnDTransferObjectGetType(pObj);
1725
1726 switch (enmType)
1727 {
1728 case DNDTRANSFEROBJTYPE_DIRECTORY:
1729 rc = vbglR3DnDGHSendDir(pCtx, pObj);
1730 break;
1731
1732 case DNDTRANSFEROBJTYPE_FILE:
1733 rc = vbglR3DnDGHSendFile(pCtx, pObj);
1734 break;
1735
1736 default:
1737 AssertMsgFailed(("Object type %ld not implemented\n", enmType));
1738 rc = VERR_NOT_IMPLEMENTED;
1739 break;
1740 }
1741
1742 return rc;
1743}
1744
1745/**
1746 * Guest -> Host
1747 * Utility function to send raw data from guest to the host.
1748 *
1749 * @returns IPRT status code.
1750 * @param pCtx DnD context to use.
1751 * @param pvData Block to raw data to send.
1752 * @param cbData Size (in bytes) of raw data to send.
1753 */
1754static int vbglR3DnDGHSendRawData(PVBGLR3GUESTDNDCMDCTX pCtx, void *pvData, size_t cbData)
1755{
1756 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1757 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1758 /* cbData can be 0. */
1759
1760 VBOXDNDDATAHDR dataHdr;
1761 RT_ZERO(dataHdr);
1762
1763 dataHdr.cbMeta = (uint32_t)cbData;
1764 dataHdr.cbTotal = cbData;
1765
1766 return vbglR3DnDGHSendDataInternal(pCtx, pvData, cbData, &dataHdr);
1767}
1768
1769/**
1770 * Guest -> Host
1771 * Utility function to send transfer data from guest to the host.
1772 *
1773 * @returns IPRT status code.
1774 * @param pCtx DnD context to use.
1775 * @param pTransferList Dnd transfer list to send.
1776 */
1777static int vbglR3DnDGHSendTransferData(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFERLIST pTransferList)
1778{
1779 AssertPtrReturn(pCtx,VERR_INVALID_POINTER);
1780 AssertPtrReturn(pTransferList, VERR_INVALID_POINTER);
1781
1782 /*
1783 * Send the (meta) data; in case of URIs it's the root entries of a
1784 * transfer list the host needs to know upfront to set up the drag'n drop operation.
1785 */
1786 char *pszList = NULL;
1787 size_t cbList;
1788 int rc = DnDTransferListGetRoots(pTransferList, DNDTRANSFERLISTFMT_URI, &pszList, &cbList);
1789 if (RT_FAILURE(rc))
1790 return rc;
1791
1792 void *pvURIList = (void *)pszList;
1793 uint32_t cbURLIist = (uint32_t)cbList;
1794
1795 /* The total size also contains the size of the meta data. */
1796 uint64_t cbTotal = cbURLIist;
1797 cbTotal += DnDTransferListObjTotalBytes(pTransferList);
1798
1799 /* We're going to send a transfer list in text format. */
1800 const char szMetaFmt[] = "text/uri-list";
1801 const uint32_t cbMetaFmt = (uint32_t)strlen(szMetaFmt) + 1; /* Include termination. */
1802
1803 VBOXDNDDATAHDR dataHdr;
1804 dataHdr.uFlags = 0; /* Flags not used yet. */
1805 dataHdr.cbTotal = cbTotal;
1806 dataHdr.cbMeta = cbURLIist;
1807 dataHdr.pvMetaFmt = (void *)szMetaFmt;
1808 dataHdr.cbMetaFmt = cbMetaFmt;
1809 dataHdr.cObjects = DnDTransferListObjCount(pTransferList);
1810
1811 rc = vbglR3DnDGHSendDataInternal(pCtx, pvURIList, cbURLIist, &dataHdr);
1812
1813 if (RT_SUCCESS(rc))
1814 {
1815 while (DnDTransferListObjCount(pTransferList))
1816 {
1817 PDNDTRANSFEROBJECT pObj = DnDTransferListObjGetFirst(pTransferList);
1818
1819 rc = vbglR3DnDGHSendURIObject(pCtx, pObj);
1820 if (RT_FAILURE(rc))
1821 break;
1822
1823 DnDTransferListObjRemoveFirst(pTransferList);
1824 }
1825
1826 Assert(DnDTransferListObjCount(pTransferList) == 0);
1827 }
1828
1829 return rc;
1830}
1831
1832/**
1833 * Guest -> Host
1834 * Sends data, which either can be raw or URI data, from guest to the host. This function
1835 * initiates the actual data transfer from guest to the host.
1836 *
1837 * @returns IPRT status code.
1838 * @param pCtx DnD context to use.
1839 * @param pszFormat In which format the data will be sent.
1840 * @param pvData Data block to send.
1841 * For URI data this must contain the absolute local URI paths, separated by DND_PATH_SEPARATOR_STR.
1842 * @param cbData Size (in bytes) of data block to send.
1843 */
1844VBGLR3DECL(int) VbglR3DnDGHSendData(PVBGLR3GUESTDNDCMDCTX pCtx, const char *pszFormat, void *pvData, uint32_t cbData)
1845{
1846 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1847 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
1848 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1849 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1850
1851 LogFlowFunc(("pszFormat=%s, pvData=%p, cbData=%RU32\n", pszFormat, pvData, cbData));
1852
1853 LogRel2(("DnD: Sending %RU32 bytes meta data in format '%s'\n", cbData, pszFormat));
1854
1855 int rc;
1856 if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat)))
1857 {
1858 DNDTRANSFERLIST lstTransfer;
1859 RT_ZERO(lstTransfer);
1860
1861 rc = DnDTransferListInit(&lstTransfer);
1862 if (RT_SUCCESS(rc))
1863 {
1864 /** @todo Add symlink support (DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS) here. */
1865 /** @todo Add lazy loading (DNDTRANSFERLIST_FLAGS_LAZY) here. */
1866 const DNDTRANSFERLISTFLAGS fFlags = DNDTRANSFERLIST_FLAGS_RECURSIVE;
1867
1868 rc = DnDTransferListAppendPathsFromBuffer(&lstTransfer, DNDTRANSFERLISTFMT_URI, (const char *)pvData, cbData,
1869 DND_PATH_SEPARATOR_STR, fFlags);
1870 if (RT_SUCCESS(rc))
1871 rc = vbglR3DnDGHSendTransferData(pCtx, &lstTransfer);
1872 DnDTransferListDestroy(&lstTransfer);
1873 }
1874 }
1875 else
1876 rc = vbglR3DnDGHSendRawData(pCtx, pvData, cbData);
1877
1878 if (RT_FAILURE(rc))
1879 {
1880 LogRel(("DnD: Sending data failed with rc=%Rrc\n", rc));
1881
1882 if (rc != VERR_CANCELLED)
1883 {
1884 int rc2 = VbglR3DnDGHSendError(pCtx, rc);
1885 if (RT_FAILURE(rc2))
1886 LogFlowFunc(("Unable to send error (%Rrc) to host, rc=%Rrc\n", rc, rc2));
1887 }
1888 }
1889
1890 return rc;
1891}
1892
1893/**
1894 * Guest -> Host
1895 * Send an error back to the host.
1896 *
1897 * @returns IPRT status code.
1898 * @param pCtx DnD context to use.
1899 * @param rcErr Error (IPRT-style) to send.
1900 */
1901VBGLR3DECL(int) VbglR3DnDGHSendError(PVBGLR3GUESTDNDCMDCTX pCtx, int rcErr)
1902{
1903 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1904
1905 HGCMMsgGHError Msg;
1906 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_FN_GH_EVT_ERROR, 2);
1907 /** @todo Context ID not used yet. */
1908 Msg.u.v3.uContext.SetUInt32(0);
1909 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1910
1911 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1912
1913 /*
1914 * Never return an error if the host did not accept the error at the current
1915 * time. This can be due to the host not having any appropriate callbacks
1916 * set which would handle that error.
1917 *
1918 * bird: Looks like VERR_NOT_SUPPORTED is what the host will return if it
1919 * doesn't an appropriate callback. The code used to ignore ALL errors
1920 * the host would return, also relevant ones.
1921 */
1922 if (RT_FAILURE(rc))
1923 LogFlowFunc(("Sending error %Rrc failed with rc=%Rrc\n", rcErr, rc));
1924 if (rc == VERR_NOT_SUPPORTED)
1925 rc = VINF_SUCCESS;
1926
1927 return rc;
1928}
1929#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1930
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette