VirtualBox

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

Last change on this file since 74439 was 74439, checked in by vboxsync, 7 years ago

DnD: Added typedefs for DNDACTION and DNDACTIONLIST to emphasize usage, did some renaming to clean things up.

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

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