VirtualBox

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

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

DnD/VbglR3: Adapted to new DnDTransferListInitEx() call.

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