VirtualBox

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

Last change on this file since 85584 was 85584, checked in by vboxsync, 5 years ago

DnD/VbglR3: Fixes for receiving raw meta data from host.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 66.6 KB
Line 
1/* $Id: VBoxGuestR3LibDragAndDrop.cpp 85584 2020-07-31 16:48:59Z 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->uProtocol >= 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 %RU32 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 pCtx->uProtocol = 3;
1016
1017 /*
1018 * Get the VM's session ID.
1019 * This is not fatal in case we're running with an ancient VBox version.
1020 */
1021 pCtx->uSessionID = 0;
1022 int rc2 = VbglR3GetSessionId(&pCtx->uSessionID); RT_NOREF(rc2);
1023 LogFlowFunc(("uSessionID=%RU64, rc=%Rrc\n", pCtx->uSessionID, rc2));
1024
1025 /*
1026 * Try sending the connect message to tell the protocol version to use.
1027 * Note: This might fail when the Guest Additions run on an older VBox host (< VBox 5.0) which
1028 * does not implement this command.
1029 */
1030 HGCMMsgConnect Msg;
1031 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_CONNECT, 3);
1032 /** @todo Context ID not used yet. */
1033 Msg.u.v3.uContext.SetUInt32(0);
1034 Msg.u.v3.uProtocol.SetUInt32(pCtx->uProtocol);
1035 Msg.u.v3.uFlags.SetUInt32(0); /* Unused at the moment. */
1036
1037 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1038 if (RT_SUCCESS(rc))
1039 {
1040 /* Set the protocol version we're going to use as told by the host. */
1041 rc = Msg.u.v3.uProtocol.GetUInt32(&pCtx->uProtocol); AssertRC(rc);
1042
1043 pCtx->cbMaxChunkSize = DND_DEFAULT_CHUNK_SIZE; /** @todo Use a scratch buffer on the heap? */
1044 }
1045 else
1046 pCtx->uProtocol = 0; /* We're using protocol v0 (initial draft) as a fallback. */
1047
1048 /** @todo Implement protocol feature flags. */
1049
1050 LogFlowFunc(("uClient=%RU32, uProtocol=%RU32, rc=%Rrc\n", pCtx->uClientID, pCtx->uProtocol, rc));
1051 return rc;
1052}
1053
1054/**
1055 * Disconnects a given DnD context from the DnD host service.
1056 *
1057 * @returns IPRT status code.
1058 * @param pCtx DnD context to disconnect.
1059 * The context is invalid afterwards on successful disconnection.
1060 */
1061VBGLR3DECL(int) VbglR3DnDDisconnect(PVBGLR3GUESTDNDCMDCTX pCtx)
1062{
1063 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1064 int rc = VbglR3HGCMDisconnect(pCtx->uClientID);
1065 if (RT_SUCCESS(rc))
1066 pCtx->uClientID = 0;
1067 return rc;
1068}
1069
1070/**
1071 * Receives the next upcoming DnD event.
1072 *
1073 * This is the main function DnD clients call in order to implement any DnD functionality.
1074 * The purpose of it is to abstract the actual DnD protocol handling as much as possible from
1075 * the clients -- those only need to react to certain events, regardless of how the underlying
1076 * protocol actually is working.
1077 *
1078 * @returns IPRT status code.
1079 * @param pCtx DnD context to work with.
1080 * @param ppEvent Next DnD event received on success; needs to be free'd by the client calling
1081 * VbglR3DnDEventFree() when done.
1082 */
1083VBGLR3DECL(int) VbglR3DnDEventGetNext(PVBGLR3GUESTDNDCMDCTX pCtx, PVBGLR3DNDEVENT *ppEvent)
1084{
1085 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1086 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
1087
1088 PVBGLR3DNDEVENT pEvent = (PVBGLR3DNDEVENT)RTMemAllocZ(sizeof(VBGLR3DNDEVENT));
1089 if (!pEvent)
1090 return VERR_NO_MEMORY;
1091
1092 uint32_t uMsg = 0;
1093 uint32_t cParms = 0;
1094 int rc = vbglR3DnDGetNextMsgType(pCtx, &uMsg, &cParms, true /* fWait */);
1095 if (RT_SUCCESS(rc))
1096 {
1097 /* Check for VM session change. */
1098 uint64_t uSessionID;
1099 int rc2 = VbglR3GetSessionId(&uSessionID);
1100 if ( RT_SUCCESS(rc2)
1101 && (uSessionID != pCtx->uSessionID))
1102 {
1103 LogFlowFunc(("VM session ID changed to %RU64\n", uSessionID));
1104
1105 rc = VbglR3DnDDisconnect(pCtx);
1106 if (RT_SUCCESS(rc))
1107 rc = VbglR3DnDConnect(pCtx);
1108 }
1109 }
1110
1111 if (RT_SUCCESS(rc))
1112 {
1113 LogFunc(("Handling uMsg=%RU32\n", uMsg));
1114
1115 switch(uMsg)
1116 {
1117 case HOST_DND_HG_EVT_ENTER:
1118 {
1119 rc = vbglR3DnDHGRecvAction(pCtx,
1120 uMsg,
1121 &pEvent->u.HG_Enter.uScreenID,
1122 NULL /* puXPos */,
1123 NULL /* puYPos */,
1124 NULL /* uDefAction */,
1125 &pEvent->u.HG_Enter.dndLstActionsAllowed,
1126 &pEvent->u.HG_Enter.pszFormats,
1127 &pEvent->u.HG_Enter.cbFormats);
1128 if (RT_SUCCESS(rc))
1129 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_ENTER;
1130 break;
1131 }
1132 case HOST_DND_HG_EVT_MOVE:
1133 {
1134 rc = vbglR3DnDHGRecvAction(pCtx,
1135 uMsg,
1136 NULL /* puScreenId */,
1137 &pEvent->u.HG_Move.uXpos,
1138 &pEvent->u.HG_Move.uYpos,
1139 &pEvent->u.HG_Move.dndActionDefault,
1140 NULL /* puAllActions */,
1141 NULL /* pszFormats */,
1142 NULL /* pcbFormats */);
1143 if (RT_SUCCESS(rc))
1144 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_MOVE;
1145 break;
1146 }
1147 case HOST_DND_HG_EVT_DROPPED:
1148 {
1149 rc = vbglR3DnDHGRecvAction(pCtx,
1150 uMsg,
1151 NULL /* puScreenId */,
1152 &pEvent->u.HG_Drop.uXpos,
1153 &pEvent->u.HG_Drop.uYpos,
1154 &pEvent->u.HG_Drop.dndActionDefault,
1155 NULL /* puAllActions */,
1156 NULL /* pszFormats */,
1157 NULL /* pcbFormats */);
1158 if (RT_SUCCESS(rc))
1159 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_DROP;
1160 break;
1161 }
1162 case HOST_DND_HG_EVT_LEAVE:
1163 {
1164 rc = vbglR3DnDHGRecvLeave(pCtx);
1165 if (RT_SUCCESS(rc))
1166 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_LEAVE;
1167 break;
1168 }
1169 case HOST_DND_HG_SND_DATA_HDR:
1170 {
1171 rc = vbglR3DnDHGRecvDataMain(pCtx, &pEvent->u.HG_Received.Meta);
1172 if (RT_SUCCESS(rc))
1173 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_RECEIVE;
1174 break;
1175 }
1176 case HOST_DND_HG_SND_DIR:
1177 RT_FALL_THROUGH();
1178 case HOST_DND_HG_SND_FILE_HDR:
1179 RT_FALL_THROUGH();
1180 case HOST_DND_HG_SND_FILE_DATA:
1181 {
1182 /*
1183 * All messages for this block are handled internally
1184 * by vbglR3DnDHGRecvDataMain(), see above.
1185 *
1186 * So if we land here our code is buggy.
1187 */
1188 rc = VERR_WRONG_ORDER;
1189 break;
1190 }
1191 case HOST_DND_CANCEL:
1192 {
1193 rc = vbglR3DnDHGRecvCancel(pCtx);
1194 if (RT_SUCCESS(rc))
1195 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_CANCEL;
1196 break;
1197 }
1198#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1199 case HOST_DND_GH_REQ_PENDING:
1200 {
1201 rc = vbglR3DnDGHRecvPending(pCtx, &pEvent->u.GH_IsPending.uScreenID);
1202 if (RT_SUCCESS(rc))
1203 pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_REQ_PENDING;
1204 break;
1205 }
1206 case HOST_DND_GH_EVT_DROPPED:
1207 {
1208 rc = vbglR3DnDGHRecvDropped(pCtx,
1209 &pEvent->u.GH_Drop.pszFormat,
1210 &pEvent->u.GH_Drop.cbFormat,
1211 &pEvent->u.GH_Drop.dndActionRequested);
1212 if (RT_SUCCESS(rc))
1213 pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_DROP;
1214 break;
1215 }
1216#endif
1217 default:
1218 {
1219 rc = VERR_NOT_SUPPORTED;
1220 break;
1221 }
1222 }
1223 }
1224
1225 if (RT_FAILURE(rc))
1226 {
1227 VbglR3DnDEventFree(pEvent);
1228 LogFlowFunc(("Failed with %Rrc\n", rc));
1229 }
1230 else
1231 *ppEvent = pEvent;
1232
1233 return rc;
1234}
1235
1236/**
1237 * Frees (destroys) a formerly allocated DnD event.
1238 *
1239 * @returns IPRT status code.
1240 * @param pEvent Event to free (destroy).
1241 */
1242VBGLR3DECL(void) VbglR3DnDEventFree(PVBGLR3DNDEVENT pEvent)
1243{
1244 if (!pEvent)
1245 return;
1246
1247 /* Some messages require additional cleanup. */
1248 switch (pEvent->enmType)
1249 {
1250 case VBGLR3DNDEVENTTYPE_HG_ENTER:
1251 {
1252 if (pEvent->u.HG_Enter.pszFormats)
1253 RTStrFree(pEvent->u.HG_Enter.pszFormats);
1254 break;
1255 }
1256
1257#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1258 case VBGLR3DNDEVENTTYPE_GH_DROP:
1259 {
1260 if (pEvent->u.GH_Drop.pszFormat)
1261 RTStrFree(pEvent->u.GH_Drop.pszFormat);
1262 break;
1263 }
1264#endif
1265 case VBGLR3DNDEVENTTYPE_HG_RECEIVE:
1266 {
1267 PVBGLR3GUESTDNDMETADATA pMeta = &pEvent->u.HG_Received.Meta;
1268 switch (pMeta->enmType)
1269 {
1270 case VBGLR3GUESTDNDMETADATATYPE_RAW:
1271 {
1272 if (pMeta->u.Raw.pvMeta)
1273 {
1274 Assert(pMeta->u.Raw.cbMeta);
1275 RTMemFree(pMeta->u.Raw.pvMeta);
1276 pMeta->u.Raw.cbMeta = 0;
1277 }
1278 break;
1279 }
1280
1281 case VBGLR3GUESTDNDMETADATATYPE_URI_LIST:
1282 {
1283 DnDTransferListDestroy(&pMeta->u.URI.Transfer);
1284 break;
1285 }
1286
1287 default:
1288 break;
1289 }
1290 break;
1291 }
1292
1293 default:
1294 break;
1295 }
1296
1297 RTMemFree(pEvent);
1298 pEvent = NULL;
1299}
1300
1301VBGLR3DECL(int) VbglR3DnDHGSendAckOp(PVBGLR3GUESTDNDCMDCTX pCtx, VBOXDNDACTION dndAction)
1302{
1303 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1304
1305 HGCMMsgHGAck Msg;
1306 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_HG_ACK_OP, 2);
1307 /** @todo Context ID not used yet. */
1308 Msg.u.v3.uContext.SetUInt32(0);
1309 Msg.u.v3.uAction.SetUInt32(dndAction);
1310
1311 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1312}
1313
1314/**
1315 * Host -> Guest
1316 * Requests the actual DnD data to be sent from the host.
1317 *
1318 * @returns IPRT status code.
1319 * @param pCtx DnD context to use.
1320 * @param pcszFormat Format to request the data from the host in.
1321 */
1322VBGLR3DECL(int) VbglR3DnDHGSendReqData(PVBGLR3GUESTDNDCMDCTX pCtx, const char* pcszFormat)
1323{
1324 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1325 AssertPtrReturn(pcszFormat, VERR_INVALID_POINTER);
1326 if (!RTStrIsValidEncoding(pcszFormat))
1327 return VERR_INVALID_PARAMETER;
1328
1329 const uint32_t cbFormat = (uint32_t)strlen(pcszFormat) + 1; /* Include termination */
1330
1331 HGCMMsgHGReqData Msg;
1332 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_HG_REQ_DATA, 3);
1333 /** @todo Context ID not used yet. */
1334 Msg.u.v3.uContext.SetUInt32(0);
1335 Msg.u.v3.pvFormat.SetPtr((void*)pcszFormat, cbFormat);
1336 Msg.u.v3.cbFormat.SetUInt32(cbFormat);
1337
1338 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1339}
1340
1341/**
1342 * Host -> Guest
1343 * Reports back its progress back to the host.
1344 *
1345 * @returns IPRT status code.
1346 * @param pCtx DnD context to use.
1347 * @param uStatus DnD status to report.
1348 * @param uPercent Overall progress (in percent) to report.
1349 * @param rcErr Error code (IPRT-style) to report.
1350 */
1351VBGLR3DECL(int) VbglR3DnDHGSendProgress(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uStatus, uint8_t uPercent, int rcErr)
1352{
1353 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1354 AssertReturn(uStatus > DND_PROGRESS_UNKNOWN, VERR_INVALID_PARAMETER);
1355
1356 HGCMMsgHGProgress Msg;
1357 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_HG_EVT_PROGRESS, 4);
1358 /** @todo Context ID not used yet. */
1359 Msg.u.v3.uContext.SetUInt32(0);
1360 Msg.u.v3.uStatus.SetUInt32(uStatus);
1361 Msg.u.v3.uPercent.SetUInt32(uPercent);
1362 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1363
1364 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1365}
1366
1367#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1368/**
1369 * Guest -> Host
1370 * Acknowledges that there currently is a drag'n drop operation in progress on the guest,
1371 * which eventually could be dragged over to the host.
1372 *
1373 * @returns IPRT status code.
1374 * @param pCtx DnD context to use.
1375 * @param dndActionDefault Default action for the operation to report.
1376 * @param dndLstActionsAllowed All available actions for the operation to report.
1377 * @param pcszFormats Available formats for the operation to report.
1378 * @param cbFormats Size (in bytes) of formats to report.
1379 */
1380VBGLR3DECL(int) VbglR3DnDGHSendAckPending(PVBGLR3GUESTDNDCMDCTX pCtx,
1381 VBOXDNDACTION dndActionDefault, VBOXDNDACTIONLIST dndLstActionsAllowed,
1382 const char* pcszFormats, uint32_t cbFormats)
1383{
1384 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1385 AssertPtrReturn(pcszFormats, VERR_INVALID_POINTER);
1386 AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
1387
1388 if (!RTStrIsValidEncoding(pcszFormats))
1389 return VERR_INVALID_UTF8_ENCODING;
1390
1391 HGCMMsgGHAckPending Msg;
1392 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_ACK_PENDING, 5);
1393 /** @todo Context ID not used yet. */
1394 Msg.u.v3.uContext.SetUInt32(0);
1395 Msg.u.v3.uDefAction.SetUInt32(dndActionDefault);
1396 Msg.u.v3.uAllActions.SetUInt32(dndLstActionsAllowed);
1397 Msg.u.v3.pvFormats.SetPtr((void*)pcszFormats, cbFormats);
1398 Msg.u.v3.cbFormats.SetUInt32(cbFormats);
1399
1400 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1401}
1402
1403/**
1404 * Guest -> Host
1405 * Utility function to send DnD data from guest to the host.
1406 *
1407 * @returns IPRT status code.
1408 * @param pCtx DnD context to use.
1409 * @param pvData Data block to send.
1410 * @param cbData Size (in bytes) of data block to send.
1411 * @param pDataHdr Data header to use -- needed for accounting.
1412 */
1413static int vbglR3DnDGHSendDataInternal(PVBGLR3GUESTDNDCMDCTX pCtx,
1414 void *pvData, uint64_t cbData, PVBOXDNDSNDDATAHDR pDataHdr)
1415{
1416 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1417 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1418 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1419 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
1420
1421 HGCMMsgGHSendDataHdr MsgHdr;
1422 VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DATA_HDR, 12);
1423 MsgHdr.uContext.SetUInt32(0); /** @todo Not used yet. */
1424 MsgHdr.uFlags.SetUInt32(0); /** @todo Not used yet. */
1425 MsgHdr.uScreenId.SetUInt32(0); /** @todo Not used for guest->host (yet). */
1426 MsgHdr.cbTotal.SetUInt64(pDataHdr->cbTotal);
1427 MsgHdr.cbMeta.SetUInt32(pDataHdr->cbMeta);
1428 MsgHdr.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
1429 MsgHdr.cbMetaFmt.SetUInt32(pDataHdr->cbMetaFmt);
1430 MsgHdr.cObjects.SetUInt64(pDataHdr->cObjects);
1431 MsgHdr.enmCompression.SetUInt32(0); /** @todo Not used yet. */
1432 MsgHdr.enmChecksumType.SetUInt32(RTDIGESTTYPE_INVALID); /** @todo Not used yet. */
1433 MsgHdr.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1434 MsgHdr.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1435
1436 int rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr));
1437
1438 LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU64, rc=%Rrc\n",
1439 pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects, rc));
1440
1441 if (RT_SUCCESS(rc))
1442 {
1443 HGCMMsgGHSendData MsgData;
1444 VBGL_HGCM_HDR_INIT(&MsgData.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DATA, 5);
1445 MsgData.u.v3.uContext.SetUInt32(0); /** @todo Not used yet. */
1446 MsgData.u.v3.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1447 MsgData.u.v3.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1448
1449 uint32_t cbCurChunk;
1450 const uint32_t cbMaxChunk = pCtx->cbMaxChunkSize;
1451 uint32_t cbSent = 0;
1452
1453 while (cbSent < cbData)
1454 {
1455 cbCurChunk = RT_MIN(cbData - cbSent, cbMaxChunk);
1456 MsgData.u.v3.pvData.SetPtr(static_cast<uint8_t *>(pvData) + cbSent, cbCurChunk);
1457 MsgData.u.v3.cbData.SetUInt32(cbCurChunk);
1458
1459 rc = VbglR3HGCMCall(&MsgData.hdr, sizeof(MsgData));
1460 if (RT_FAILURE(rc))
1461 break;
1462
1463 cbSent += cbCurChunk;
1464 }
1465
1466 LogFlowFunc(("cbMaxChunk=%RU32, cbData=%RU64, cbSent=%RU32, rc=%Rrc\n",
1467 cbMaxChunk, cbData, cbSent, rc));
1468
1469 if (RT_SUCCESS(rc))
1470 Assert(cbSent == cbData);
1471 }
1472
1473 LogFlowFuncLeaveRC(rc);
1474 return rc;
1475}
1476
1477/**
1478 * Guest -> Host
1479 * Utility function to send a guest directory to the host.
1480 *
1481 * @returns IPRT status code.
1482 * @param pCtx DnD context to use.
1483 * @param pObj transfer object containing the directory to send.
1484 */
1485static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DNDTRANSFEROBJECT *pObj)
1486{
1487 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1488 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1489 AssertReturn(DnDTransferObjectGetType(pObj) == DNDTRANSFEROBJTYPE_DIRECTORY, VERR_INVALID_PARAMETER);
1490
1491 const char *pcszPath = DnDTransferObjectGetDestPath(pObj);
1492 const size_t cbPath = RTStrNLen(pcszPath, RTPATH_MAX) + 1 /* Include termination. */;
1493 const RTFMODE fMode = DnDTransferObjectGetMode(pObj);
1494
1495 LogFlowFunc(("strDir=%s (%zu bytes), fMode=0x%x\n", pcszPath, cbPath, fMode));
1496
1497 if (cbPath > RTPATH_MAX + 1) /* Can't happen, but check anyway. */
1498 return VERR_INVALID_PARAMETER;
1499
1500 HGCMMsgGHSendDir Msg;
1501 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DIR, 4);
1502 /** @todo Context ID not used yet. */
1503 Msg.u.v3.uContext.SetUInt32(0);
1504 Msg.u.v3.pvName.SetPtr((void *)pcszPath, (uint32_t)cbPath);
1505 Msg.u.v3.cbName.SetUInt32((uint32_t)cbPath);
1506 Msg.u.v3.fMode.SetUInt32(fMode);
1507
1508 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1509}
1510
1511/**
1512 * Guest -> Host
1513 * Utility function to send a file from the guest to the host.
1514 *
1515 * @returns IPRT status code.
1516 * @param pCtx DnD context to use.
1517 * @param pObj Transfer object containing the file to send.
1518 */
1519static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFEROBJECT pObj)
1520{
1521 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1522 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1523 AssertReturn(DnDTransferObjectIsOpen(pObj) == false, VERR_INVALID_STATE);
1524 AssertReturn(DnDTransferObjectGetType(pObj) == DNDTRANSFEROBJTYPE_FILE, VERR_INVALID_PARAMETER);
1525
1526 uint64_t fOpen = RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE;
1527
1528 int rc = DnDTransferObjectOpen(pObj, fOpen, 0 /* fMode */, DNDTRANSFEROBJECT_FLAGS_NONE);
1529 if (RT_FAILURE(rc))
1530 return rc;
1531
1532 uint32_t cbBuf = pCtx->cbMaxChunkSize;
1533 AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
1534 void *pvBuf = RTMemAlloc(cbBuf); /** @todo Make this buffer part of PVBGLR3GUESTDNDCMDCTX? */
1535 if (!pvBuf)
1536 {
1537 DnDTransferObjectClose(pObj);
1538 return VERR_NO_MEMORY;
1539 }
1540
1541 const char *pcszPath = DnDTransferObjectGetDestPath(pObj);
1542 const size_t cchPath = RTStrNLen(pcszPath, RTPATH_MAX);
1543 const uint64_t cbSize = DnDTransferObjectGetSize(pObj);
1544 const RTFMODE fMode = DnDTransferObjectGetMode(pObj);
1545
1546 LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", pcszPath, cchPath, cbSize, fMode));
1547
1548 HGCMMsgGHSendFileHdr MsgHdr;
1549 VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_GH_SND_FILE_HDR, 6);
1550 MsgHdr.uContext.SetUInt32(0); /* Context ID; unused at the moment. */
1551 MsgHdr.pvName.SetPtr((void *)pcszPath, (uint32_t)(cchPath + 1)); /* Include termination. */
1552 MsgHdr.cbName.SetUInt32((uint32_t)(cchPath + 1)); /* Ditto. */
1553 MsgHdr.uFlags.SetUInt32(0); /* Flags; unused at the moment. */
1554 MsgHdr.fMode.SetUInt32(fMode); /* File mode */
1555 MsgHdr.cbTotal.SetUInt64(cbSize); /* File size (in bytes). */
1556
1557 rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr));
1558
1559 LogFlowFunc(("Sending file header resulted in %Rrc\n", rc));
1560
1561 if (RT_SUCCESS(rc))
1562 {
1563 /*
1564 * Send the actual file data, chunk by chunk.
1565 */
1566 HGCMMsgGHSendFileData Msg;
1567 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_FILE_DATA, 5);
1568 Msg.u.v3.uContext.SetUInt32(0);
1569 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
1570 Msg.u.v3.cbChecksum.SetUInt32(0);
1571
1572 uint64_t cbToReadTotal = cbSize;
1573 uint64_t cbWrittenTotal = 0;
1574 while (cbToReadTotal)
1575 {
1576 uint32_t cbToRead = RT_MIN(cbToReadTotal, cbBuf);
1577 uint32_t cbRead = 0;
1578 if (cbToRead)
1579 rc = DnDTransferObjectRead(pObj, pvBuf, cbToRead, &cbRead);
1580
1581 LogFlowFunc(("cbToReadTotal=%RU64, cbToRead=%RU32, cbRead=%RU32, rc=%Rrc\n",
1582 cbToReadTotal, cbToRead, cbRead, rc));
1583
1584 if ( RT_SUCCESS(rc)
1585 && cbRead)
1586 {
1587 Msg.u.v3.pvData.SetPtr(pvBuf, cbRead);
1588 Msg.u.v3.cbData.SetUInt32(cbRead);
1589 /** @todo Calculate + set checksums. */
1590
1591 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1592 }
1593
1594 if (RT_FAILURE(rc))
1595 {
1596 LogFlowFunc(("Reading failed with rc=%Rrc\n", rc));
1597 break;
1598 }
1599
1600 Assert(cbRead <= cbToReadTotal);
1601 cbToReadTotal -= cbRead;
1602 cbWrittenTotal += cbRead;
1603
1604 LogFlowFunc(("%RU64/%RU64 -- %RU8%%\n", cbWrittenTotal, cbSize, cbWrittenTotal * 100 / cbSize));
1605 };
1606 }
1607
1608 RTMemFree(pvBuf);
1609 DnDTransferObjectClose(pObj);
1610
1611 LogFlowFuncLeaveRC(rc);
1612 return rc;
1613}
1614
1615/**
1616 * Guest -> Host
1617 * Utility function to send a transfer object from guest to the host.
1618 *
1619 * @returns IPRT status code.
1620 * @param pCtx DnD context to use.
1621 * @param pObj Transfer object to send from guest to the host.
1622 */
1623static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFEROBJECT pObj)
1624{
1625 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1626 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1627
1628 int rc;
1629
1630 const DNDTRANSFEROBJTYPE enmType = DnDTransferObjectGetType(pObj);
1631
1632 switch (enmType)
1633 {
1634 case DNDTRANSFEROBJTYPE_DIRECTORY:
1635 rc = vbglR3DnDGHSendDir(pCtx, pObj);
1636 break;
1637
1638 case DNDTRANSFEROBJTYPE_FILE:
1639 rc = vbglR3DnDGHSendFile(pCtx, pObj);
1640 break;
1641
1642 default:
1643 AssertMsgFailed(("Object type %ld not implemented\n", enmType));
1644 rc = VERR_NOT_IMPLEMENTED;
1645 break;
1646 }
1647
1648 return rc;
1649}
1650
1651/**
1652 * Guest -> Host
1653 * Utility function to send raw data from guest to the host.
1654 *
1655 * @returns IPRT status code.
1656 * @param pCtx DnD context to use.
1657 * @param pvData Block to raw data to send.
1658 * @param cbData Size (in bytes) of raw data to send.
1659 */
1660static int vbglR3DnDGHSendRawData(PVBGLR3GUESTDNDCMDCTX pCtx, void *pvData, size_t cbData)
1661{
1662 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1663 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1664 /* cbData can be 0. */
1665
1666 VBOXDNDDATAHDR dataHdr;
1667 RT_ZERO(dataHdr);
1668
1669 /* For raw data only the total size is required to be specified. */
1670 dataHdr.cbTotal = cbData;
1671
1672 return vbglR3DnDGHSendDataInternal(pCtx, pvData, cbData, &dataHdr);
1673}
1674
1675/**
1676 * Guest -> Host
1677 * Utility function to send transfer data from guest to the host.
1678 *
1679 * @returns IPRT status code.
1680 * @param pCtx DnD context to use.
1681 * @param pTransferList Dnd transfer list to send.
1682 */
1683static int vbglR3DnDGHSendTransferData(PVBGLR3GUESTDNDCMDCTX pCtx, PDNDTRANSFERLIST pTransferList)
1684{
1685 AssertPtrReturn(pCtx,VERR_INVALID_POINTER);
1686 AssertPtrReturn(pTransferList, VERR_INVALID_POINTER);
1687
1688 /*
1689 * Send the (meta) data; in case of URIs it's the root entries of a
1690 * transfer list the host needs to know upfront to set up the drag'n drop operation.
1691 */
1692 char *pszList = NULL;
1693 size_t cbList;
1694 int rc = DnDTransferListGetRoots(pTransferList, DNDTRANSFERLISTFMT_URI, &pszList, &cbList);
1695 if (RT_FAILURE(rc))
1696 return rc;
1697
1698 void *pvURIList = (void *)pszList;
1699 uint32_t cbURLIist = (uint32_t)cbList;
1700
1701 /* The total size also contains the size of the meta data. */
1702 uint64_t cbTotal = cbURLIist;
1703 cbTotal += DnDTransferListObjTotalBytes(pTransferList);
1704
1705 /* We're going to send a transfer list in text format. */
1706 const char szMetaFmt[] = "text/uri-list";
1707 const uint32_t cbMetaFmt = (uint32_t)strlen(szMetaFmt) + 1; /* Include termination. */
1708
1709 VBOXDNDDATAHDR dataHdr;
1710 dataHdr.uFlags = 0; /* Flags not used yet. */
1711 dataHdr.cbTotal = cbTotal;
1712 dataHdr.cbMeta = cbURLIist;
1713 dataHdr.pvMetaFmt = (void *)szMetaFmt;
1714 dataHdr.cbMetaFmt = cbMetaFmt;
1715 dataHdr.cObjects = DnDTransferListObjCount(pTransferList);
1716
1717 rc = vbglR3DnDGHSendDataInternal(pCtx, pvURIList, cbURLIist, &dataHdr);
1718
1719 if (RT_SUCCESS(rc))
1720 {
1721 while (DnDTransferListObjCount(pTransferList))
1722 {
1723 PDNDTRANSFEROBJECT pObj = DnDTransferListObjGetFirst(pTransferList);
1724
1725 rc = vbglR3DnDGHSendURIObject(pCtx, pObj);
1726 if (RT_FAILURE(rc))
1727 break;
1728
1729 DnDTransferListObjRemoveFirst(pTransferList);
1730 }
1731
1732 Assert(DnDTransferListObjCount(pTransferList) == 0);
1733 }
1734
1735 return rc;
1736}
1737
1738/**
1739 * Guest -> Host
1740 * Sends data, which either can be raw or URI data, from guest to the host. This function
1741 * initiates the actual data transfer from guest to the host.
1742 *
1743 * @returns IPRT status code.
1744 * @param pCtx DnD context to use.
1745 * @param pszFormat In which format the data will be sent.
1746 * @param pvData Data block to send.
1747 * For URI data this must contain the absolute local URI paths, separated by DND_PATH_SEPARATOR.
1748 * @param cbData Size (in bytes) of data block to send.
1749 */
1750VBGLR3DECL(int) VbglR3DnDGHSendData(PVBGLR3GUESTDNDCMDCTX pCtx, const char *pszFormat, void *pvData, uint32_t cbData)
1751{
1752 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1753 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
1754 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1755 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1756
1757 LogFlowFunc(("pszFormat=%s, pvData=%p, cbData=%RU32\n", pszFormat, pvData, cbData));
1758
1759 int rc;
1760 if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat)))
1761 {
1762 DNDTRANSFERLIST lstTransfer;
1763 RT_ZERO(lstTransfer);
1764
1765 rc = DnDTransferListInit(&lstTransfer);
1766 if (RT_SUCCESS(rc))
1767 {
1768 /** @todo Add symlink support (DNDTRANSFERLIST_FLAGS_RESOLVE_SYMLINKS) here. */
1769 /** @todo Add lazy loading (DNDTRANSFERLIST_FLAGS_LAZY) here. */
1770 const DNDTRANSFERLISTFLAGS fFlags = DNDTRANSFERLIST_FLAGS_RECURSIVE;
1771
1772 rc = DnDTransferListAppendPathsFromBuffer(&lstTransfer, DNDTRANSFERLISTFMT_URI, (const char *)pvData, cbData,
1773 DND_PATH_SEPARATOR, fFlags);
1774 if (RT_SUCCESS(rc))
1775 rc = vbglR3DnDGHSendTransferData(pCtx, &lstTransfer);
1776 DnDTransferListDestroy(&lstTransfer);
1777 }
1778
1779 if (RT_FAILURE(rc))
1780 LogRel(("DnD: Sending guest meta data to host failed with %Rrc\n", rc));
1781 }
1782 else
1783 rc = vbglR3DnDGHSendRawData(pCtx, pvData, cbData);
1784
1785 if (RT_FAILURE(rc))
1786 {
1787 int rc2 = VbglR3DnDGHSendError(pCtx, rc);
1788 if (RT_FAILURE(rc2))
1789 LogFlowFunc(("Unable to send error (%Rrc) to host, rc=%Rrc\n", rc, rc2));
1790 }
1791
1792 return rc;
1793}
1794
1795/**
1796 * Guest -> Host
1797 * Send an error back to the host.
1798 *
1799 * @returns IPRT status code.
1800 * @param pCtx DnD context to use.
1801 * @param rcErr Error (IPRT-style) to send.
1802 */
1803VBGLR3DECL(int) VbglR3DnDGHSendError(PVBGLR3GUESTDNDCMDCTX pCtx, int rcErr)
1804{
1805 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1806
1807 HGCMMsgGHError Msg;
1808 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_EVT_ERROR, 2);
1809 /** @todo Context ID not used yet. */
1810 Msg.u.v3.uContext.SetUInt32(0);
1811 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1812
1813 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1814
1815 /*
1816 * Never return an error if the host did not accept the error at the current
1817 * time. This can be due to the host not having any appropriate callbacks
1818 * set which would handle that error.
1819 *
1820 * bird: Looks like VERR_NOT_SUPPORTED is what the host will return if it
1821 * doesn't an appropriate callback. The code used to ignore ALL errors
1822 * the host would return, also relevant ones.
1823 */
1824 if (RT_FAILURE(rc))
1825 LogFlowFunc(("Sending error %Rrc failed with rc=%Rrc\n", rcErr, rc));
1826 if (rc == VERR_NOT_SUPPORTED)
1827 rc = VINF_SUCCESS;
1828
1829 return rc;
1830}
1831#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1832
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