VirtualBox

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

Last change on this file since 85712 was 85712, checked in by vboxsync, 4 years ago

DnD: Added wire protocol support for querying and reporting guest / host features in host service and VbglR3. Untested.

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