VirtualBox

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

Last change on this file since 93967 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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