VirtualBox

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

Last change on this file since 75803 was 74714, checked in by vboxsync, 6 years ago

DnD: Cleaned up DnDURIObject API / internal state handling.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.0 KB
Line 
1/* $Id: VBoxGuestR3LibDragAndDrop.cpp 74714 2018-10-09 11:50:22Z 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.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/path.h>
32#include <iprt/dir.h>
33#include <iprt/file.h>
34#include <iprt/uri.h>
35#include <iprt/thread.h>
36
37#include <iprt/cpp/list.h>
38#include <iprt/cpp/ministring.h>
39
40#ifdef LOG_GROUP
41 #undef LOG_GROUP
42#endif
43#define LOG_GROUP LOG_GROUP_GUEST_DND
44#include <VBox/log.h>
45
46#include <VBox/VBoxGuestLib.h>
47#include <VBox/GuestHost/DragAndDrop.h>
48#include <VBox/HostServices/DragAndDropSvc.h>
49
50using namespace DragAndDropSvc;
51
52#include "VBoxGuestR3LibInternal.h"
53
54
55/*********************************************************************************************************************************
56* Private internal functions *
57*********************************************************************************************************************************/
58
59/**
60 * Receives the next upcoming message for a given DnD context.
61 *
62 * @returns IPRT status code.
63 * @param pCtx DnD context to use.
64 * @param puMsg Where to store the message type.
65 * @param pcParms Where to store the number of parameters required for receiving the message.
66 * @param fWait Whether to wait (block) for a new message to arrive or not.
67 */
68static int vbglR3DnDGetNextMsgType(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puMsg, uint32_t *pcParms, bool fWait)
69{
70 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
71 AssertPtrReturn(puMsg, VERR_INVALID_POINTER);
72 AssertPtrReturn(pcParms, VERR_INVALID_POINTER);
73
74 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::Type_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::View_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.SetFromURIData(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.GetRootEntries(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\n", uSessionID));
1189 }
1190 }
1191
1192 if (RT_SUCCESS(rc))
1193 {
1194 LogFunc(("Handling uMsg=%RU32\n", uMsg));
1195
1196 switch(uMsg)
1197 {
1198 case HOST_DND_HG_EVT_ENTER:
1199 {
1200 rc = vbglR3DnDHGRecvAction(pCtx,
1201 uMsg,
1202 &pEvent->u.HG_Enter.uScreenID,
1203 NULL /* puXPos */,
1204 NULL /* puYPos */,
1205 NULL /* uDefAction */,
1206 &pEvent->u.HG_Enter.dndLstActionsAllowed,
1207 &pEvent->u.HG_Enter.pszFormats,
1208 &pEvent->u.HG_Enter.cbFormats);
1209 if (RT_SUCCESS(rc))
1210 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_ENTER;
1211 break;
1212 }
1213 case HOST_DND_HG_EVT_MOVE:
1214 {
1215 rc = vbglR3DnDHGRecvAction(pCtx,
1216 uMsg,
1217 NULL /* puScreenId */,
1218 &pEvent->u.HG_Move.uXpos,
1219 &pEvent->u.HG_Move.uYpos,
1220 &pEvent->u.HG_Move.dndActionDefault,
1221 NULL /* puAllActions */,
1222 NULL /* pszFormats */,
1223 NULL /* pcbFormats */);
1224 if (RT_SUCCESS(rc))
1225 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_MOVE;
1226 break;
1227 }
1228 case HOST_DND_HG_EVT_DROPPED:
1229 {
1230 rc = vbglR3DnDHGRecvAction(pCtx,
1231 uMsg,
1232 NULL /* puScreenId */,
1233 &pEvent->u.HG_Drop.uXpos,
1234 &pEvent->u.HG_Drop.uYpos,
1235 &pEvent->u.HG_Drop.dndActionDefault,
1236 NULL /* puAllActions */,
1237 NULL /* pszFormats */,
1238 NULL /* pcbFormats */);
1239 if (RT_SUCCESS(rc))
1240 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_DROP;
1241 break;
1242 }
1243 case HOST_DND_HG_EVT_LEAVE:
1244 {
1245 rc = vbglR3DnDHGRecvLeave(pCtx);
1246 if (RT_SUCCESS(rc))
1247 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_LEAVE;
1248 break;
1249 }
1250 case HOST_DND_HG_SND_DATA_HDR:
1251 {
1252 rc = vbglR3DnDHGRecvDataMain(pCtx, &pEvent->u.HG_Received.Meta);
1253 if (RT_SUCCESS(rc))
1254 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_RECEIVE;
1255 break;
1256 }
1257 case HOST_DND_HG_SND_DIR:
1258 RT_FALL_THROUGH();
1259 case HOST_DND_HG_SND_FILE_DATA:
1260 {
1261 /*
1262 * All messages in this case are handled internally
1263 * by vbglR3DnDHGRecvDataMain() and must be specified
1264 * by preceeding HOST_DND_HG_SND_DATA or HOST_DND_HG_SND_DATA_HDR calls.
1265 */
1266 rc = VERR_WRONG_ORDER;
1267 break;
1268 }
1269 case HOST_DND_HG_EVT_CANCEL:
1270 {
1271 rc = vbglR3DnDHGRecvCancel(pCtx);
1272 if (RT_SUCCESS(rc))
1273 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_CANCEL;
1274 break;
1275 }
1276#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1277 case HOST_DND_GH_REQ_PENDING:
1278 {
1279 rc = vbglR3DnDGHRecvPending(pCtx, &pEvent->u.GH_IsPending.uScreenID);
1280 if (RT_SUCCESS(rc))
1281 pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_REQ_PENDING;
1282 break;
1283 }
1284 case HOST_DND_GH_EVT_DROPPED:
1285 {
1286 rc = vbglR3DnDGHRecvDropped(pCtx,
1287 &pEvent->u.GH_Drop.pszFormat,
1288 &pEvent->u.GH_Drop.cbFormat,
1289 &pEvent->u.GH_Drop.dndActionRequested);
1290 if (RT_SUCCESS(rc))
1291 pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_DROP;
1292 break;
1293 }
1294#endif
1295 default:
1296 {
1297 rc = VERR_NOT_SUPPORTED;
1298 break;
1299 }
1300 }
1301 }
1302
1303 if (RT_FAILURE(rc))
1304 {
1305 VbglR3DnDEventFree(pEvent);
1306 LogFlowFunc(("Failed with %Rrc\n", rc));
1307 }
1308 else
1309 *ppEvent = pEvent;
1310
1311 return rc;
1312}
1313
1314/**
1315 * Frees (destroys) a formerly allocated DnD event.
1316 *
1317 * @returns IPRT status code.
1318 * @param pEvent Event to free (destroy).
1319 */
1320VBGLR3DECL(void) VbglR3DnDEventFree(PVBGLR3DNDEVENT pEvent)
1321{
1322 if (!pEvent)
1323 return;
1324
1325 /* Some messages require additional cleanup. */
1326 switch (pEvent->enmType)
1327 {
1328 case VBGLR3DNDEVENTTYPE_HG_ENTER:
1329 {
1330 if (pEvent->u.HG_Enter.pszFormats)
1331 RTStrFree(pEvent->u.HG_Enter.pszFormats);
1332 break;
1333 }
1334
1335#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1336 case VBGLR3DNDEVENTTYPE_GH_DROP:
1337 {
1338 if (pEvent->u.GH_Drop.pszFormat)
1339 RTStrFree(pEvent->u.GH_Drop.pszFormat);
1340 break;
1341 }
1342#endif
1343 case VBGLR3DNDEVENTTYPE_HG_RECEIVE:
1344 {
1345 PVBGLR3GUESTDNDMETADATA pMeta = &pEvent->u.HG_Received.Meta;
1346 if (pMeta->pvMeta)
1347 {
1348 Assert(pMeta->cbMeta);
1349 RTMemFree(pMeta->pvMeta);
1350 pMeta->cbMeta = 0;
1351 }
1352 break;
1353 }
1354
1355 default:
1356 break;
1357 }
1358
1359 RTMemFree(pEvent);
1360 pEvent = NULL;
1361}
1362
1363VBGLR3DECL(int) VbglR3DnDHGSendAckOp(PVBGLR3GUESTDNDCMDCTX pCtx, VBOXDNDACTION dndAction)
1364{
1365 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1366
1367 VBOXDNDHGACKOPMSG Msg;
1368 RT_ZERO(Msg);
1369
1370 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_HG_ACK_OP, 2);
1371 /** @todo Context ID not used yet. */
1372 Msg.u.v3.uContext.SetUInt32(0);
1373 Msg.u.v3.uAction.SetUInt32(dndAction);
1374
1375 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1376}
1377
1378/**
1379 * Host -> Guest
1380 * Requests the actual DnD data to be sent from the host.
1381 *
1382 * @returns IPRT status code.
1383 * @param pCtx DnD context to use.
1384 * @param pcszFormat Format to request the data from the host in.
1385 */
1386VBGLR3DECL(int) VbglR3DnDHGSendReqData(PVBGLR3GUESTDNDCMDCTX pCtx, const char* pcszFormat)
1387{
1388 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1389 AssertPtrReturn(pcszFormat, VERR_INVALID_POINTER);
1390 if (!RTStrIsValidEncoding(pcszFormat))
1391 return VERR_INVALID_PARAMETER;
1392
1393 const uint32_t cbFormat = (uint32_t)strlen(pcszFormat) + 1; /* Include termination */
1394
1395 VBOXDNDHGREQDATAMSG Msg;
1396 RT_ZERO(Msg);
1397
1398 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_HG_REQ_DATA, 3);
1399 /** @todo Context ID not used yet. */
1400 Msg.u.v3.uContext.SetUInt32(0);
1401 Msg.u.v3.pvFormat.SetPtr((void*)pcszFormat, cbFormat);
1402 Msg.u.v3.cbFormat.SetUInt32(cbFormat);
1403
1404 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1405}
1406
1407/**
1408 * Host -> Guest
1409 * Reports back its progress back to the host.
1410 *
1411 * @returns IPRT status code.
1412 * @param pCtx DnD context to use.
1413 * @param uStatus DnD status to report.
1414 * @param uPercent Overall progress (in percent) to report.
1415 * @param rcErr Error code (IPRT-style) to report.
1416 */
1417VBGLR3DECL(int) VbglR3DnDHGSendProgress(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uStatus, uint8_t uPercent, int rcErr)
1418{
1419 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1420 AssertReturn(uStatus > DND_PROGRESS_UNKNOWN, VERR_INVALID_PARAMETER);
1421
1422 VBOXDNDHGEVTPROGRESSMSG Msg;
1423 RT_ZERO(Msg);
1424
1425 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_HG_EVT_PROGRESS, 4);
1426 /** @todo Context ID not used yet. */
1427 Msg.u.v3.uContext.SetUInt32(0);
1428 Msg.u.v3.uStatus.SetUInt32(uStatus);
1429 Msg.u.v3.uPercent.SetUInt32(uPercent);
1430 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1431
1432 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1433}
1434
1435#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1436/**
1437 * Guest -> Host
1438 * Acknowledges that there currently is a drag'n drop operation in progress on the guest,
1439 * which eventually could be dragged over to the host.
1440 *
1441 * @returns IPRT status code.
1442 * @param pCtx DnD context to use.
1443 * @param dndActionDefault Default action for the operation to report.
1444 * @param dndLstActionsAllowed All available actions for the operation to report.
1445 * @param pcszFormats Available formats for the operation to report.
1446 * @param cbFormats Size (in bytes) of formats to report.
1447 */
1448VBGLR3DECL(int) VbglR3DnDGHSendAckPending(PVBGLR3GUESTDNDCMDCTX pCtx,
1449 VBOXDNDACTION dndActionDefault, VBOXDNDACTIONLIST dndLstActionsAllowed,
1450 const char* pcszFormats, uint32_t cbFormats)
1451{
1452 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1453 AssertPtrReturn(pcszFormats, VERR_INVALID_POINTER);
1454 AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
1455
1456 if (!RTStrIsValidEncoding(pcszFormats))
1457 return VERR_INVALID_UTF8_ENCODING;
1458
1459 VBOXDNDGHACKPENDINGMSG Msg;
1460 RT_ZERO(Msg);
1461
1462 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_ACK_PENDING, 5);
1463 /** @todo Context ID not used yet. */
1464 Msg.u.v3.uContext.SetUInt32(0);
1465 Msg.u.v3.uDefAction.SetUInt32(dndActionDefault);
1466 Msg.u.v3.uAllActions.SetUInt32(dndLstActionsAllowed);
1467 Msg.u.v3.pvFormats.SetPtr((void*)pcszFormats, cbFormats);
1468 Msg.u.v3.cbFormats.SetUInt32(cbFormats);
1469
1470 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1471}
1472
1473/**
1474 * Guest -> Host
1475 * Utility function to send DnD data from guest to the host.
1476 *
1477 * @returns IPRT status code.
1478 * @param pCtx DnD context to use.
1479 * @param pvData Data block to send.
1480 * @param cbData Size (in bytes) of data block to send.
1481 * @param pDataHdr Data header to use -- needed for accounting.
1482 */
1483static int vbglR3DnDGHSendDataInternal(PVBGLR3GUESTDNDCMDCTX pCtx,
1484 void *pvData, uint64_t cbData, PVBOXDNDSNDDATAHDR pDataHdr)
1485{
1486 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1487 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1488 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1489 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
1490
1491 VBOXDNDGHSENDDATAHDRMSG MsgHdr;
1492 RT_ZERO(MsgHdr);
1493
1494 VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DATA_HDR, 12);
1495 MsgHdr.uContext.SetUInt32(0); /** @todo Not used yet. */
1496 MsgHdr.uFlags.SetUInt32(0); /** @todo Not used yet. */
1497 MsgHdr.uScreenId.SetUInt32(0); /** @todo Not used for guest->host (yet). */
1498 MsgHdr.cbTotal.SetUInt64(pDataHdr->cbTotal);
1499 MsgHdr.cbMeta.SetUInt32(pDataHdr->cbMeta);
1500 MsgHdr.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
1501 MsgHdr.cbMetaFmt.SetUInt32(pDataHdr->cbMetaFmt);
1502 MsgHdr.cObjects.SetUInt64(pDataHdr->cObjects);
1503 MsgHdr.enmCompression.SetUInt32(0); /** @todo Not used yet. */
1504 MsgHdr.enmChecksumType.SetUInt32(RTDIGESTTYPE_INVALID); /** @todo Not used yet. */
1505 MsgHdr.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1506 MsgHdr.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1507
1508 int rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr));
1509
1510 LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU64, rc=%Rrc\n",
1511 pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects, rc));
1512
1513 if (RT_SUCCESS(rc))
1514 {
1515 VBOXDNDGHSENDDATAMSG MsgData;
1516 RT_ZERO(MsgData);
1517
1518 VBGL_HGCM_HDR_INIT(&MsgData.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DATA, 5);
1519 MsgData.u.v3.uContext.SetUInt32(0); /** @todo Not used yet. */
1520 MsgData.u.v3.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1521 MsgData.u.v3.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1522
1523 uint32_t cbCurChunk;
1524 const uint32_t cbMaxChunk = pCtx->cbMaxChunkSize;
1525 uint32_t cbSent = 0;
1526
1527 HGCMFunctionParameter *pParm = &MsgData.u.v3.pvData;
1528
1529 while (cbSent < cbData)
1530 {
1531 cbCurChunk = RT_MIN(cbData - cbSent, cbMaxChunk);
1532 pParm->SetPtr(static_cast<uint8_t *>(pvData) + cbSent, cbCurChunk);
1533
1534 MsgData.u.v3.cbData.SetUInt32(cbCurChunk);
1535
1536 rc = VbglR3HGCMCall(&MsgData.hdr, sizeof(MsgData));
1537 if (RT_FAILURE(rc))
1538 break;
1539
1540 cbSent += cbCurChunk;
1541 }
1542
1543 LogFlowFunc(("cbMaxChunk=%RU32, cbData=%RU64, cbSent=%RU32, rc=%Rrc\n",
1544 cbMaxChunk, cbData, cbSent, rc));
1545
1546 if (RT_SUCCESS(rc))
1547 Assert(cbSent == cbData);
1548 }
1549
1550 LogFlowFuncLeaveRC(rc);
1551 return rc;
1552}
1553
1554/**
1555 * Guest -> Host
1556 * Utility function to send a guest directory to the host.
1557 *
1558 * @returns IPRT status code.
1559 * @param pCtx DnD context to use.
1560 * @param pObj URI object containing the directory to send.
1561 */
1562static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
1563{
1564 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1565 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1566 AssertReturn(pObj->GetType() == DnDURIObject::Type_Directory, VERR_INVALID_PARAMETER);
1567
1568 RTCString strPath = pObj->GetDestPathAbs();
1569 LogFlowFunc(("strDir=%s (%zu), fMode=0x%x\n",
1570 strPath.c_str(), strPath.length(), pObj->GetMode()));
1571
1572 if (strPath.length() > RTPATH_MAX)
1573 return VERR_INVALID_PARAMETER;
1574
1575 const uint32_t cbPath = (uint32_t)strPath.length() + 1; /* Include termination. */
1576
1577 VBOXDNDGHSENDDIRMSG Msg;
1578 RT_ZERO(Msg);
1579
1580 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DIR, 4);
1581 /** @todo Context ID not used yet. */
1582 Msg.u.v3.uContext.SetUInt32(0);
1583 Msg.u.v3.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)cbPath);
1584 Msg.u.v3.cbName.SetUInt32((uint32_t)cbPath);
1585 Msg.u.v3.fMode.SetUInt32(pObj->GetMode());
1586
1587 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1588}
1589
1590/**
1591 * Guest -> Host
1592 * Utility function to send a file from the guest to the host.
1593 *
1594 * @returns IPRT status code.
1595 * @param pCtx DnD context to use.
1596 * @param pObj URI object containing the file to send.
1597 */
1598static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
1599{
1600 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1601 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1602 AssertReturn(pObj->GetType() == DnDURIObject::Type_File, VERR_INVALID_PARAMETER);
1603 AssertReturn(pObj->IsOpen(), VERR_INVALID_STATE);
1604
1605 uint32_t cbBuf = _64K; /** @todo Make this configurable? */
1606 void *pvBuf = RTMemAlloc(cbBuf); /** @todo Make this buffer part of PVBGLR3GUESTDNDCMDCTX? */
1607 if (!pvBuf)
1608 return VERR_NO_MEMORY;
1609
1610 RTCString strPath = pObj->GetDestPathAbs();
1611
1612 LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", strPath.c_str(), strPath.length(),
1613 pObj->GetSize(), pObj->GetMode()));
1614
1615 VBOXDNDGHSENDFILEHDRMSG MsgHdr;
1616 RT_ZERO(MsgHdr);
1617
1618 VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_GH_SND_FILE_HDR, 6);
1619 MsgHdr.uContext.SetUInt32(0); /* Context ID; unused at the moment. */
1620 MsgHdr.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1));
1621 MsgHdr.cbName.SetUInt32((uint32_t)(strPath.length() + 1));
1622 MsgHdr.uFlags.SetUInt32(0); /* Flags; unused at the moment. */
1623 MsgHdr.fMode.SetUInt32(pObj->GetMode()); /* File mode */
1624 MsgHdr.cbTotal.SetUInt64(pObj->GetSize()); /* File size (in bytes). */
1625
1626 int rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr));
1627
1628 LogFlowFunc(("Sending file header resulted in %Rrc\n", rc));
1629
1630 if (RT_SUCCESS(rc))
1631 {
1632 /*
1633 * Send the actual file data, chunk by chunk.
1634 */
1635 VBOXDNDGHSENDFILEDATAMSG Msg;
1636 RT_ZERO(Msg);
1637
1638 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_FILE_DATA, 5);
1639 Msg.u.v3.uContext.SetUInt32(0);
1640 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
1641 Msg.u.v3.cbChecksum.SetUInt32(0);
1642
1643 uint64_t cbToReadTotal = pObj->GetSize();
1644 uint64_t cbWrittenTotal = 0;
1645 while (cbToReadTotal)
1646 {
1647 uint32_t cbToRead = RT_MIN(cbToReadTotal, cbBuf);
1648 uint32_t cbRead = 0;
1649 if (cbToRead)
1650 rc = pObj->Read(pvBuf, cbToRead, &cbRead);
1651
1652 LogFlowFunc(("cbToReadTotal=%RU64, cbToRead=%RU32, cbRead=%RU32, rc=%Rrc\n",
1653 cbToReadTotal, cbToRead, cbRead, rc));
1654
1655 if ( RT_SUCCESS(rc)
1656 && cbRead)
1657 {
1658 Msg.u.v3.pvData.SetPtr(pvBuf, cbRead);
1659 Msg.u.v3.cbData.SetUInt32(cbRead);
1660 /** @todo Calculate + set checksums. */
1661
1662 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1663 }
1664
1665 if (RT_FAILURE(rc))
1666 {
1667 LogFlowFunc(("Reading failed with rc=%Rrc\n", rc));
1668 break;
1669 }
1670
1671 Assert(cbRead <= cbToReadTotal);
1672 cbToReadTotal -= cbRead;
1673 cbWrittenTotal += cbRead;
1674
1675 LogFlowFunc(("%RU64/%RU64 -- %RU8%%\n", cbWrittenTotal, pObj->GetSize(), cbWrittenTotal * 100 / pObj->GetSize()));
1676 };
1677 }
1678
1679 RTMemFree(pvBuf);
1680
1681 LogFlowFuncLeaveRC(rc);
1682 return rc;
1683}
1684
1685/**
1686 * Guest -> Host
1687 * Utility function to send an URI object from guest to the host.
1688 *
1689 * @returns IPRT status code.
1690 * @param pCtx DnD context to use.
1691 * @param pObj URI object to send from guest to the host.
1692 */
1693static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
1694{
1695 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1696 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1697
1698 int rc;
1699
1700 switch (pObj->GetType())
1701 {
1702 case DnDURIObject::Type_Directory:
1703 rc = vbglR3DnDGHSendDir(pCtx, pObj);
1704 break;
1705
1706 case DnDURIObject::Type_File:
1707 rc = vbglR3DnDGHSendFile(pCtx, pObj);
1708 break;
1709
1710 default:
1711 AssertMsgFailed(("Object type %ld not implemented\n", pObj->GetType()));
1712 rc = VERR_NOT_IMPLEMENTED;
1713 break;
1714 }
1715
1716 return rc;
1717}
1718
1719/**
1720 * Guest -> Host
1721 * Utility function to send raw data from guest to the host.
1722 *
1723 * @returns IPRT status code.
1724 * @param pCtx DnD context to use.
1725 * @param pvData Block to raw data to send.
1726 * @param cbData Size (in bytes) of raw data to send.
1727 */
1728static int vbglR3DnDGHSendRawData(PVBGLR3GUESTDNDCMDCTX pCtx, void *pvData, size_t cbData)
1729{
1730 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1731 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1732 /* cbData can be 0. */
1733
1734 VBOXDNDDATAHDR dataHdr;
1735 RT_ZERO(dataHdr);
1736
1737 /* For raw data only the total size is required to be specified. */
1738 dataHdr.cbTotal = cbData;
1739
1740 return vbglR3DnDGHSendDataInternal(pCtx, pvData, cbData, &dataHdr);
1741}
1742
1743/**
1744 * Guest -> Host
1745 * Utility function to send URI data from guest to the host.
1746 *
1747 * @returns IPRT status code.
1748 * @param pCtx DnD context to use.
1749 * @param pvData Block to URI data to send.
1750 * @param cbData Size (in bytes) of URI data to send.
1751 */
1752static int vbglR3DnDGHSendURIData(PVBGLR3GUESTDNDCMDCTX pCtx, const void *pvData, size_t cbData)
1753{
1754 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1755 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1756 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1757
1758 RTCList<RTCString> lstPaths =
1759 RTCString((const char *)pvData, cbData).split("\r\n");
1760
1761 /** @todo Add symlink support (DNDURILIST_FLAGS_RESOLVE_SYMLINKS) here. */
1762 /** @todo Add lazy loading (DNDURILIST_FLAGS_LAZY) here. */
1763 uint32_t fFlags = DNDURILIST_FLAGS_KEEP_OPEN;
1764
1765 DnDURIList lstURI;
1766 int rc = lstURI.AppendURIPathsFromList(lstPaths, fFlags);
1767 if (RT_SUCCESS(rc))
1768 {
1769 /*
1770 * Send the (meta) data; in case of URIs it's the (non-recursive) file/directory
1771 * URI list the host needs to know upfront to set up the drag'n drop operation.
1772 */
1773 RTCString strRootDest = lstURI.GetRootEntries();
1774 if (strRootDest.isNotEmpty())
1775 {
1776 void *pvURIList = (void *)strRootDest.c_str(); /* URI root list. */
1777 uint32_t cbURLIist = (uint32_t)strRootDest.length() + 1; /* Include string termination. */
1778
1779 /* The total size also contains the size of the meta data. */
1780 uint64_t cbTotal = cbURLIist;
1781 cbTotal += lstURI.GetTotalBytes();
1782
1783 /* We're going to send an URI list in text format. */
1784 const char szMetaFmt[] = "text/uri-list";
1785 const uint32_t cbMetaFmt = (uint32_t)strlen(szMetaFmt) + 1; /* Include termination. */
1786
1787 VBOXDNDDATAHDR dataHdr;
1788 dataHdr.uFlags = 0; /* Flags not used yet. */
1789 dataHdr.cbTotal = cbTotal;
1790 dataHdr.cbMeta = cbURLIist;
1791 dataHdr.pvMetaFmt = (void *)szMetaFmt;
1792 dataHdr.cbMetaFmt = cbMetaFmt;
1793 dataHdr.cObjects = lstURI.GetTotalCount();
1794
1795 rc = vbglR3DnDGHSendDataInternal(pCtx,
1796 pvURIList, cbURLIist, &dataHdr);
1797 }
1798 else
1799 rc = VERR_INVALID_PARAMETER;
1800 }
1801
1802 if (RT_SUCCESS(rc))
1803 {
1804 while (!lstURI.IsEmpty())
1805 {
1806 DnDURIObject *pNextObj = lstURI.First();
1807
1808 rc = vbglR3DnDGHSendURIObject(pCtx, pNextObj);
1809 if (RT_FAILURE(rc))
1810 break;
1811
1812 lstURI.RemoveFirst();
1813 }
1814 }
1815
1816 return rc;
1817}
1818
1819/**
1820 * Guest -> Host
1821 * Sends data, which either can be raw or URI data, from guest to the host. This function
1822 * initiates the actual data transfer from guest to the host.
1823 *
1824 * @returns IPRT status code.
1825 * @param pCtx DnD context to use.
1826 * @param pszFormat In which format the data will be sent.
1827 * @param pvData Data block to send.
1828 * @param cbData Size (in bytes) of data block to send.
1829 */
1830VBGLR3DECL(int) VbglR3DnDGHSendData(PVBGLR3GUESTDNDCMDCTX pCtx, const char *pszFormat, void *pvData, uint32_t cbData)
1831{
1832 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1833 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
1834 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1835 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1836
1837 LogFlowFunc(("pszFormat=%s, pvData=%p, cbData=%RU32\n", pszFormat, pvData, cbData));
1838
1839 int rc;
1840 if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat)))
1841 {
1842 /* Send file data. */
1843 rc = vbglR3DnDGHSendURIData(pCtx, pvData, cbData);
1844 }
1845 else
1846 rc = vbglR3DnDGHSendRawData(pCtx, pvData, cbData);
1847
1848 if (RT_FAILURE(rc))
1849 {
1850 int rc2 = VbglR3DnDGHSendError(pCtx, rc);
1851 if (RT_FAILURE(rc2))
1852 LogFlowFunc(("Unable to send error (%Rrc) to host, rc=%Rrc\n", rc, rc2));
1853 }
1854
1855 return rc;
1856}
1857
1858/**
1859 * Guest -> Host
1860 * Send an error back to the host.
1861 *
1862 * @returns IPRT status code.
1863 * @param pCtx DnD context to use.
1864 * @param rcErr Error (IPRT-style) to send.
1865 */
1866VBGLR3DECL(int) VbglR3DnDGHSendError(PVBGLR3GUESTDNDCMDCTX pCtx, int rcErr)
1867{
1868 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1869
1870 VBOXDNDGHEVTERRORMSG Msg;
1871 RT_ZERO(Msg);
1872
1873 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_EVT_ERROR, 2);
1874 /** @todo Context ID not used yet. */
1875 Msg.u.v3.uContext.SetUInt32(0);
1876 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1877
1878 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1879
1880 /*
1881 * Never return an error if the host did not accept the error at the current
1882 * time. This can be due to the host not having any appropriate callbacks
1883 * set which would handle that error.
1884 *
1885 * bird: Looks like VERR_NOT_SUPPORTED is what the host will return if it
1886 * doesn't an appropriate callback. The code used to ignore ALL errors
1887 * the host would return, also relevant ones.
1888 */
1889 if (RT_FAILURE(rc))
1890 LogFlowFunc(("Sending error %Rrc failed with rc=%Rrc\n", rcErr, rc));
1891 if (rc == VERR_NOT_SUPPORTED)
1892 rc = VINF_SUCCESS;
1893
1894 return rc;
1895}
1896#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1897
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