VirtualBox

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

Last change on this file since 82528 was 79499, checked in by vboxsync, 6 years ago

Shared Clipboard/URI: Build fix.

  • 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 79499 2019-07-03 13:35:39Z vboxsync $ */
2/** @file
3 * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Drag & Drop.
4 */
5
6/*
7 * Copyright (C) 2011-2019 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_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 rc;
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 && cbChunkRead)
538 {
539 uint32_t cbChunkWritten;
540 rc = objFile.Write(pvChunk, cbChunkRead, &cbChunkWritten);
541 if (RT_SUCCESS(rc))
542 {
543 LogFlowFunc(("HOST_DND_HG_SND_FILE_DATA "
544 "cbChunkRead=%RU32, cbChunkWritten=%RU32, cbFileWritten=%RU64 cbFileSize=%RU64\n",
545 cbChunkRead, cbChunkWritten, cbFileWritten + cbChunkWritten, cbFileSize));
546
547 cbFileWritten += cbChunkWritten;
548
549 Assert(cbChunkRead <= cbToRecvBytes);
550 cbToRecvBytes -= cbChunkRead;
551 }
552 }
553
554 /* Data transfer complete? Close the file. */
555 bool fClose = objFile.IsComplete();
556 if (fClose)
557 {
558 Assert(cToRecvObjs);
559 cToRecvObjs--;
560 }
561
562 /* Only since protocol v2 we know the file size upfront. */
563 Assert(cbFileWritten <= cbFileSize);
564
565 if (fClose)
566 {
567 LogFlowFunc(("Closing file\n"));
568 objFile.Close();
569 }
570
571 break;
572 }
573 case HOST_DND_CANCEL:
574 {
575 rc = vbglR3DnDHGRecvCancel(pCtx);
576 if (RT_SUCCESS(rc))
577 rc = VERR_CANCELLED;
578 break;
579 }
580 default:
581 {
582 LogFlowFunc(("Message %RU32 not supported\n", uNextMsg));
583 rc = VERR_NOT_SUPPORTED;
584 break;
585 }
586 }
587 }
588
589 if (RT_FAILURE(rc))
590 break;
591
592 LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64\n", cbToRecvBytes, cToRecvObjs));
593 if ( !cbToRecvBytes
594 && !cToRecvObjs)
595 {
596 break;
597 }
598
599 } while (RT_SUCCESS(rc));
600
601 LogFlowFunc(("Loop ended with %Rrc\n", rc));
602
603 /* All URI data processed? */
604 if (rc == VERR_NO_DATA)
605 rc = VINF_SUCCESS;
606
607 /* Delete temp buffer again. */
608 if (pvChunk)
609 RTMemFree(pvChunk);
610
611 /* Cleanup on failure or if the user has canceled the operation or
612 * something else went wrong. */
613 if (RT_FAILURE(rc))
614 {
615 objFile.Close();
616 pDroppedFiles->Rollback();
617 }
618 else
619 {
620 /** @todo Compare the URI list with the dirs/files we really transferred. */
621 /** @todo Implement checksum verification, if any. */
622 }
623
624 /*
625 * Close the dropped files directory.
626 * Don't try to remove it here, however, as the files are being needed
627 * by the client's drag'n drop operation lateron.
628 */
629 int rc2 = pDroppedFiles->Reset(false /* fRemoveDropDir */);
630 if (RT_FAILURE(rc2)) /* Not fatal, don't report back to host. */
631 LogFlowFunc(("Closing dropped files directory failed with %Rrc\n", rc2));
632
633 LogFlowFuncLeaveRC(rc);
634 return rc;
635}
636
637/**
638 * Host -> Guest
639 * Utility function to receive the HOST_DND_HG_SND_DATA message from the host.
640 *
641 * @returns IPRT status code.
642 * @param pCtx DnD context to use.
643 * @param pDataHdr DnD data header to use. Need for accounting and stuff.
644 * @param pvData Where to store the received data from the host.
645 * @param cbData Size (in bytes) of where to store the received data.
646 * @param pcbDataRecv Where to store the received amount of data (in bytes).
647 */
648static int vbglR3DnDHGRecvDataRaw(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr,
649 void *pvData, uint32_t cbData, uint32_t *pcbDataRecv)
650{
651 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
652 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
653 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
654 AssertReturn(cbData, VERR_INVALID_PARAMETER);
655 AssertPtrNullReturn(pcbDataRecv, VERR_INVALID_POINTER);
656
657 LogFlowFunc(("pvDate=%p, cbData=%RU32\n", pvData, cbData));
658
659 VBOXDNDHGSENDDATAMSG Msg;
660 RT_ZERO(Msg);
661
662 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_SND_DATA, 5);
663 Msg.u.v3.uContext.SetUInt32(0);
664 Msg.u.v3.pvData.SetPtr(pvData, cbData);
665 Msg.u.v3.cbData.SetUInt32(0);
666 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
667 Msg.u.v3.cbChecksum.SetUInt32(0);
668
669 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
670 if (RT_SUCCESS(rc))
671 {
672 uint32_t cbDataRecv;
673 rc = Msg.u.v3.cbData.GetUInt32(&cbDataRecv);
674 AssertRC(rc);
675 if (RT_SUCCESS(rc))
676 {
677 /** @todo Use checksum for validating the received data. */
678 if (pcbDataRecv)
679 *pcbDataRecv = cbDataRecv;
680 LogFlowFuncLeaveRC(rc);
681 return rc;
682 }
683 }
684
685 /* failure */
686 LogFlowFuncLeaveRC(rc);
687 return rc;
688}
689
690/**
691 * Host -> Guest
692 * Utility function to receive the HOST_DND_HG_SND_DATA_HDR message from the host.
693 *
694 * @returns IPRT status code.
695 * @param pCtx DnD context to use.
696 * @param pDataHdr Where to store the receivd DnD data header.
697 */
698static int vbglR3DnDHGRecvDataHdr(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr)
699{
700 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
701 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
702
703 Assert(pCtx->uProtocol >= 3); /* Only for protocol v3 and up. */
704
705 VBOXDNDHGSENDDATAHDRMSG Msg;
706 RT_ZERO(Msg);
707 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_SND_DATA_HDR, 12);
708 Msg.uContext.SetUInt32(0);
709 Msg.uFlags.SetUInt32(0);
710 Msg.uScreenId.SetUInt32(0);
711 Msg.cbTotal.SetUInt64(0);
712 Msg.cbMeta.SetUInt32(0);
713 Msg.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
714 Msg.cbMetaFmt.SetUInt32(0);
715 Msg.cObjects.SetUInt64(0);
716 Msg.enmCompression.SetUInt32(0);
717 Msg.enmChecksumType.SetUInt32(0);
718 Msg.pvChecksum.SetPtr(pDataHdr->pvChecksum, pDataHdr->cbChecksum);
719 Msg.cbChecksum.SetUInt32(0);
720
721 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
722 if (RT_SUCCESS(rc))
723 {
724 /* Msg.uContext not needed here. */
725 Msg.uFlags.GetUInt32(&pDataHdr->uFlags);
726 Msg.uScreenId.GetUInt32(&pDataHdr->uScreenId);
727 Msg.cbTotal.GetUInt64(&pDataHdr->cbTotal);
728 Msg.cbMeta.GetUInt32(&pDataHdr->cbMeta);
729 Msg.cbMetaFmt.GetUInt32(&pDataHdr->cbMetaFmt);
730 Msg.cObjects.GetUInt64(&pDataHdr->cObjects);
731 Msg.enmCompression.GetUInt32(&pDataHdr->enmCompression);
732 Msg.enmChecksumType.GetUInt32((uint32_t *)&pDataHdr->enmChecksumType);
733 Msg.cbChecksum.GetUInt32(&pDataHdr->cbChecksum);
734 }
735
736 LogFlowFuncLeaveRC(rc);
737 return rc;
738}
739
740/**
741 * Host -> Guest
742 * Helper function for receiving the actual DnD data from the host. Do not call directly.
743 *
744 * @returns IPRT status code.
745 * @param pCtx DnD context to use.
746 * @param pDataHdr Where to store the data header data.
747 * @param ppvData Returns the received meta data. Needs to be free'd by the caller.
748 * @param pcbData Where to store the size (in bytes) of the received meta data.
749 */
750static int vbglR3DnDHGRecvDataLoop(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr,
751 void **ppvData, uint64_t *pcbData)
752{
753 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
754 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
755 AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
756 AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
757
758 int rc;
759 uint32_t cbDataRecv;
760
761 LogFlowFuncEnter();
762
763 rc = vbglR3DnDHGRecvDataHdr(pCtx, pDataHdr);
764 if (RT_FAILURE(rc))
765 return rc;
766
767 LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU32\n", pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects));
768 if (pDataHdr->cbMeta)
769 {
770 uint64_t cbDataTmp = 0;
771 void *pvDataTmp = RTMemAlloc(pDataHdr->cbMeta);
772 if (!pvDataTmp)
773 rc = VERR_NO_MEMORY;
774
775 if (RT_SUCCESS(rc))
776 {
777 uint8_t *pvDataOff = (uint8_t *)pvDataTmp;
778 while (cbDataTmp < pDataHdr->cbMeta)
779 {
780 rc = vbglR3DnDHGRecvDataRaw(pCtx, pDataHdr,
781 pvDataOff, RT_MIN(pDataHdr->cbMeta - cbDataTmp, pCtx->cbMaxChunkSize),
782 &cbDataRecv);
783 if (RT_SUCCESS(rc))
784 {
785 LogFlowFunc(("cbDataRecv=%RU32, cbDataTmp=%RU64\n", cbDataRecv, cbDataTmp));
786 Assert(cbDataTmp + cbDataRecv <= pDataHdr->cbMeta);
787 cbDataTmp += cbDataRecv;
788 pvDataOff += cbDataRecv;
789 }
790 else
791 break;
792 }
793
794 if (RT_SUCCESS(rc))
795 {
796 Assert(cbDataTmp == pDataHdr->cbMeta);
797
798 LogFlowFunc(("Received %RU64 bytes of data\n", cbDataTmp));
799
800 *ppvData = pvDataTmp;
801 *pcbData = cbDataTmp;
802 }
803 else
804 RTMemFree(pvDataTmp);
805 }
806 }
807 else
808 {
809 *ppvData = NULL;
810 *pcbData = 0;
811 }
812
813 LogFlowFuncLeaveRC(rc);
814 return rc;
815}
816
817/**
818 * Host -> Guest
819 * Main function for receiving the actual DnD data from the host, extended version.
820 *
821 * @returns IPRT status code.
822 * @param pCtx DnD context to use.
823 * @param pEnmType Where to store the meta data type. Optional.
824 * @param ppvData Returns the received meta data. Needs to be free'd by the caller. Optional.
825 * @param pcbData Where to store the size (in bytes) of the received meta data. Optional.
826 */
827static int vbglR3DnDHGRecvDataMainEx(PVBGLR3GUESTDNDCMDCTX pCtx,
828 VBGLR3GUESTDNDMETADATATYPE *pEnmType,
829 void **ppvData,
830 uint32_t *pcbData)
831{
832 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
833 /* The rest is optional. */
834
835 VBOXDNDDATAHDR dataHdr;
836 RT_ZERO(dataHdr);
837
838 AssertMsg(pCtx->cbMaxChunkSize, ("Maximum chunk size must not be 0\n"));
839
840 dataHdr.cbMetaFmt = pCtx->cbMaxChunkSize;
841 dataHdr.pvMetaFmt = RTMemAlloc(dataHdr.cbMetaFmt);
842 if (!dataHdr.pvMetaFmt)
843 return VERR_NO_MEMORY;
844
845 DnDURIList lstURI;
846 DnDDroppedFiles droppedFiles;
847
848 void *pvData = NULL;
849 uint64_t cbData = 0;
850
851 int rc = vbglR3DnDHGRecvDataLoop(pCtx, &dataHdr, &pvData, &cbData);
852 if (RT_SUCCESS(rc))
853 {
854 /**
855 * Check if this is an URI event. If so, let VbglR3 do all the actual
856 * data transfer + file/directory creation internally without letting
857 * the caller know.
858 *
859 * This keeps the actual (guest OS-)dependent client (like VBoxClient /
860 * VBoxTray) small by not having too much redundant code.
861 */
862 Assert(dataHdr.cbMetaFmt);
863 AssertPtr(dataHdr.pvMetaFmt);
864 if (DnDMIMEHasFileURLs((char *)dataHdr.pvMetaFmt, dataHdr.cbMetaFmt)) /* URI data. */
865 {
866 AssertPtr(pvData);
867 Assert(cbData);
868
869 rc = lstURI.SetFromURIData(pvData, cbData, 0 /* fFlags */);
870 if (RT_SUCCESS(rc))
871 rc = vbglR3DnDHGRecvURIData(pCtx, &dataHdr, &droppedFiles);
872
873 if (RT_SUCCESS(rc)) /** @todo Remove this block as soon as we hand in DnDURIList. */
874 {
875 if (pvData)
876 {
877 /* Reuse data buffer to fill in the transformed URI file list. */
878 RTMemFree(pvData);
879 pvData = NULL;
880 }
881
882 RTCString strData = lstURI.GetRootEntries(droppedFiles.GetDirAbs());
883 Assert(!strData.isEmpty());
884
885 cbData = strData.length() + 1;
886 LogFlowFunc(("URI list has %zu bytes\n", cbData));
887
888 pvData = RTMemAlloc(cbData);
889 if (pvData)
890 {
891 memcpy(pvData, strData.c_str(), cbData);
892
893 if (pEnmType)
894 *pEnmType = VBGLR3GUESTDNDMETADATATYPE_URI_LIST;
895 }
896 else
897 rc = VERR_NO_MEMORY;
898 }
899 }
900 else /* Raw data. */
901 {
902 if (pEnmType)
903 *pEnmType = VBGLR3GUESTDNDMETADATATYPE_RAW;
904 }
905 }
906
907 if (dataHdr.pvMetaFmt)
908 RTMemFree(dataHdr.pvMetaFmt);
909
910 if (RT_SUCCESS(rc))
911 {
912 if ( pvData
913 && cbData)
914 {
915 if (pcbData)
916 *pcbData = cbData;
917 if (ppvData)
918 *ppvData = pvData;
919 else
920 RTMemFree(pvData);
921 }
922 }
923 else if ( RT_FAILURE(rc)
924 && rc != VERR_CANCELLED)
925 {
926 if (pvData)
927 RTMemFree(pvData);
928
929 int rc2 = VbglR3DnDHGSendProgress(pCtx, DND_PROGRESS_ERROR, 100 /* Percent */, rc);
930 if (RT_FAILURE(rc2))
931 LogFlowFunc(("Unable to send progress error %Rrc to host: %Rrc\n", rc, rc2));
932 }
933
934 LogFlowFuncLeaveRC(rc);
935 return rc;
936}
937
938/**
939 * Host -> Guest
940 * Main function for receiving the actual DnD data from the host.
941 *
942 * @returns IPRT status code.
943 * @param pCtx DnD context to use.
944 * @param pMeta Where to store the actual meta data received from the host.
945 */
946static int vbglR3DnDHGRecvDataMain(PVBGLR3GUESTDNDCMDCTX pCtx,
947 PVBGLR3GUESTDNDMETADATA pMeta)
948{
949 AssertPtrReturn(pMeta, VERR_INVALID_POINTER);
950
951 int rc = vbglR3DnDHGRecvDataMainEx(pCtx,
952 &pMeta->enmType,
953 &pMeta->pvMeta,
954 &pMeta->cbMeta);
955 return rc;
956}
957
958#ifdef VBOX_WITH_DRAG_AND_DROP_GH
959/**
960 * Guest -> Host
961 * Utility function to receive the HOST_DND_GH_REQ_PENDING message from the host.
962 *
963 * @returns IPRT status code.
964 * @param pCtx DnD context to use.
965 * @param puScreenID For which screen on the host the request is for. Optional.
966 */
967static int vbglR3DnDGHRecvPending(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puScreenID)
968{
969 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
970 /* pScreenID is optional. */
971
972 VBOXDNDGHREQPENDINGMSG Msg;
973 RT_ZERO(Msg);
974
975 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_GH_REQ_PENDING, 2);
976 /** @todo Context ID not used yet. */
977 Msg.u.v3.uContext.SetUInt32(0);
978 Msg.u.v3.uScreenId.SetUInt32(0);
979
980 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
981 if (RT_SUCCESS(rc))
982 {
983 /** @todo Context ID not used yet. */
984 if (puScreenID)
985 rc = Msg.u.v3.uContext.GetUInt32(puScreenID);
986 }
987
988 return rc;
989}
990
991/**
992 * Guest -> Host
993 * Utility function to receive the HOST_DND_GH_EVT_DROPPED message from the host.
994 *
995 * @returns IPRT status code.
996 * @param pCtx DnD context to use.
997 * @param ppszFormat Requested data format from the host. Optional.
998 * @param pcbFormat Size of requested data format (in bytes). Optional.
999 * @param puAction Requested action from the host. Optional.
1000 */
1001static int vbglR3DnDGHRecvDropped(PVBGLR3GUESTDNDCMDCTX pCtx,
1002 char **ppszFormat,
1003 uint32_t *pcbFormat,
1004 uint32_t *puAction)
1005{
1006 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1007 /* The rest is optional. */
1008
1009 const uint32_t cbFormatTmp = pCtx->cbMaxChunkSize;
1010
1011 char *pszFormatTmp = static_cast<char *>(RTMemAlloc(cbFormatTmp));
1012 if (!pszFormatTmp)
1013 return VERR_NO_MEMORY;
1014
1015 VBOXDNDGHDROPPEDMSG Msg;
1016 RT_ZERO(Msg);
1017
1018 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_GH_EVT_DROPPED, 4);
1019 Msg.u.v3.uContext.SetUInt32(0);
1020 Msg.u.v3.pvFormat.SetPtr(pszFormatTmp, cbFormatTmp);
1021 Msg.u.v3.cbFormat.SetUInt32(0);
1022 Msg.u.v3.uAction.SetUInt32(0);
1023
1024 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1025 if (RT_SUCCESS(rc))
1026 {
1027 /** @todo Context ID not used yet. */
1028 if (pcbFormat)
1029 rc = Msg.u.v3.cbFormat.GetUInt32(pcbFormat);
1030 if (RT_SUCCESS(rc) && puAction)
1031 rc = Msg.u.v3.uAction.GetUInt32(puAction);
1032
1033 if (RT_SUCCESS(rc))
1034 {
1035 *ppszFormat = RTStrDup(pszFormatTmp);
1036 if (!*ppszFormat)
1037 rc = VERR_NO_MEMORY;
1038 }
1039 }
1040
1041 RTMemFree(pszFormatTmp);
1042
1043 return rc;
1044}
1045#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1046
1047
1048/*********************************************************************************************************************************
1049* Public functions *
1050*********************************************************************************************************************************/
1051
1052/**
1053 * Connects a DnD context to the DnD host service.
1054 *
1055 * @returns IPRT status code.
1056 * @param pCtx DnD context to connect.
1057 */
1058VBGLR3DECL(int) VbglR3DnDConnect(PVBGLR3GUESTDNDCMDCTX pCtx)
1059{
1060 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1061
1062 /* Initialize header */
1063 int rc = VbglR3HGCMConnect("VBoxDragAndDropSvc", &pCtx->uClientID);
1064 if (RT_FAILURE(rc))
1065 return rc;
1066
1067 /* Set the default protocol version to use. */
1068 pCtx->uProtocol = 3;
1069 Assert(pCtx->uClientID);
1070
1071 /*
1072 * Get the VM's session ID.
1073 * This is not fatal in case we're running with an ancient VBox version.
1074 */
1075 pCtx->uSessionID = 0;
1076 int rc2 = VbglR3GetSessionId(&pCtx->uSessionID);
1077 LogFlowFunc(("uSessionID=%RU64, rc=%Rrc\n", pCtx->uSessionID, rc2));
1078
1079 /*
1080 * Check if the host is >= VBox 5.0 which in case supports GUEST_DND_CONNECT.
1081 */
1082 bool fSupportsConnectReq = false;
1083 if (RT_SUCCESS(rc))
1084 {
1085 /* The guest property service might not be available. Not fatal. */
1086 uint32_t uGuestPropSvcClientID;
1087 rc2 = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
1088 if (RT_SUCCESS(rc2))
1089 {
1090 char *pszHostVersion;
1091 rc2 = VbglR3GuestPropReadValueAlloc(uGuestPropSvcClientID, "/VirtualBox/HostInfo/VBoxVer", &pszHostVersion);
1092 if (RT_SUCCESS(rc2))
1093 {
1094 fSupportsConnectReq = RTStrVersionCompare(pszHostVersion, "5.0") >= 0;
1095 LogFlowFunc(("pszHostVersion=%s, fSupportsConnectReq=%RTbool\n", pszHostVersion, fSupportsConnectReq));
1096 VbglR3GuestPropReadValueFree(pszHostVersion);
1097 }
1098
1099 VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
1100 }
1101
1102 if (RT_FAILURE(rc2))
1103 LogFlowFunc(("Retrieving host version failed with rc=%Rrc\n", rc2));
1104 }
1105
1106 if (fSupportsConnectReq)
1107 {
1108 /*
1109 * Try sending the connect message to tell the protocol version to use.
1110 * Note: This might fail when the Guest Additions run on an older VBox host (< VBox 5.0) which
1111 * does not implement this command.
1112 */
1113 VBOXDNDCONNECTMSG Msg;
1114 RT_ZERO(Msg);
1115
1116 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_CONNECT, 3);
1117 /** @todo Context ID not used yet. */
1118 Msg.u.v3.uContext.SetUInt32(0);
1119 Msg.u.v3.uProtocol.SetUInt32(pCtx->uProtocol);
1120 Msg.u.v3.uFlags.SetUInt32(0); /* Unused at the moment. */
1121
1122 rc2 = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1123 if (RT_FAILURE(rc2))
1124 fSupportsConnectReq = false;
1125
1126 LogFlowFunc(("Connection request ended with rc=%Rrc\n", rc2));
1127 }
1128
1129 if (fSupportsConnectReq)
1130 {
1131 pCtx->cbMaxChunkSize = _64K; /** @todo Use a scratch buffer on the heap? */
1132 }
1133 else /* GUEST_DND_CONNECT not supported; the user needs to upgrade the host. */
1134 rc = VERR_NOT_SUPPORTED;
1135
1136 LogFlowFunc(("uClient=%RU32, uProtocol=%RU32, rc=%Rrc\n", pCtx->uClientID, pCtx->uProtocol, rc));
1137 return rc;
1138}
1139
1140/**
1141 * Disconnects a given DnD context from the DnD host service.
1142 *
1143 * @returns IPRT status code.
1144 * @param pCtx DnD context to disconnect.
1145 * The context is invalid afterwards on successful disconnection.
1146 */
1147VBGLR3DECL(int) VbglR3DnDDisconnect(PVBGLR3GUESTDNDCMDCTX pCtx)
1148{
1149 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1150 int rc = VbglR3HGCMDisconnect(pCtx->uClientID);
1151 if (RT_SUCCESS(rc))
1152 pCtx->uClientID = 0;
1153 return rc;
1154}
1155
1156/**
1157 * Receives the next upcoming DnD event.
1158 *
1159 * This is the main function DnD clients call in order to implement any DnD functionality.
1160 * The purpose of it is to abstract the actual DnD protocol handling as much as possible from
1161 * the clients -- those only need to react to certain events, regardless of how the underlying
1162 * protocol actually is working.
1163 *
1164 * @returns IPRT status code.
1165 * @param pCtx DnD context to work with.
1166 * @param ppEvent Next DnD event received on success; needs to be free'd by the client calling
1167 * VbglR3DnDEventFree() when done.
1168 */
1169VBGLR3DECL(int) VbglR3DnDEventGetNext(PVBGLR3GUESTDNDCMDCTX pCtx, PVBGLR3DNDEVENT *ppEvent)
1170{
1171 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1172 AssertPtrReturn(ppEvent, VERR_INVALID_POINTER);
1173
1174 PVBGLR3DNDEVENT pEvent = (PVBGLR3DNDEVENT)RTMemAllocZ(sizeof(VBGLR3DNDEVENT));
1175 if (!pEvent)
1176 return VERR_NO_MEMORY;
1177
1178 uint32_t uMsg = 0;
1179 uint32_t cParms = 0;
1180 int rc = vbglR3DnDGetNextMsgType(pCtx, &uMsg, &cParms, true /* fWait */);
1181 if (RT_SUCCESS(rc))
1182 {
1183 /* Check for VM session change. */
1184 uint64_t uSessionID;
1185 int rc2 = VbglR3GetSessionId(&uSessionID);
1186 if ( RT_SUCCESS(rc2)
1187 && (uSessionID != pCtx->uSessionID))
1188 {
1189 LogFlowFunc(("VM session ID changed to %RU64\n", uSessionID));
1190 }
1191 }
1192
1193 if (RT_SUCCESS(rc))
1194 {
1195 LogFunc(("Handling uMsg=%RU32\n", uMsg));
1196
1197 switch(uMsg)
1198 {
1199 case HOST_DND_HG_EVT_ENTER:
1200 {
1201 rc = vbglR3DnDHGRecvAction(pCtx,
1202 uMsg,
1203 &pEvent->u.HG_Enter.uScreenID,
1204 NULL /* puXPos */,
1205 NULL /* puYPos */,
1206 NULL /* uDefAction */,
1207 &pEvent->u.HG_Enter.dndLstActionsAllowed,
1208 &pEvent->u.HG_Enter.pszFormats,
1209 &pEvent->u.HG_Enter.cbFormats);
1210 if (RT_SUCCESS(rc))
1211 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_ENTER;
1212 break;
1213 }
1214 case HOST_DND_HG_EVT_MOVE:
1215 {
1216 rc = vbglR3DnDHGRecvAction(pCtx,
1217 uMsg,
1218 NULL /* puScreenId */,
1219 &pEvent->u.HG_Move.uXpos,
1220 &pEvent->u.HG_Move.uYpos,
1221 &pEvent->u.HG_Move.dndActionDefault,
1222 NULL /* puAllActions */,
1223 NULL /* pszFormats */,
1224 NULL /* pcbFormats */);
1225 if (RT_SUCCESS(rc))
1226 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_MOVE;
1227 break;
1228 }
1229 case HOST_DND_HG_EVT_DROPPED:
1230 {
1231 rc = vbglR3DnDHGRecvAction(pCtx,
1232 uMsg,
1233 NULL /* puScreenId */,
1234 &pEvent->u.HG_Drop.uXpos,
1235 &pEvent->u.HG_Drop.uYpos,
1236 &pEvent->u.HG_Drop.dndActionDefault,
1237 NULL /* puAllActions */,
1238 NULL /* pszFormats */,
1239 NULL /* pcbFormats */);
1240 if (RT_SUCCESS(rc))
1241 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_DROP;
1242 break;
1243 }
1244 case HOST_DND_HG_EVT_LEAVE:
1245 {
1246 rc = vbglR3DnDHGRecvLeave(pCtx);
1247 if (RT_SUCCESS(rc))
1248 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_LEAVE;
1249 break;
1250 }
1251 case HOST_DND_HG_SND_DATA_HDR:
1252 {
1253 rc = vbglR3DnDHGRecvDataMain(pCtx, &pEvent->u.HG_Received.Meta);
1254 if (RT_SUCCESS(rc))
1255 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_RECEIVE;
1256 break;
1257 }
1258 case HOST_DND_HG_SND_DIR:
1259 RT_FALL_THROUGH();
1260 case HOST_DND_HG_SND_FILE_DATA:
1261 {
1262 /*
1263 * All messages in this case are handled internally
1264 * by vbglR3DnDHGRecvDataMain() and must be specified
1265 * by preceeding HOST_DND_HG_SND_DATA or HOST_DND_HG_SND_DATA_HDR calls.
1266 */
1267 rc = VERR_WRONG_ORDER;
1268 break;
1269 }
1270 case HOST_DND_CANCEL:
1271 {
1272 rc = vbglR3DnDHGRecvCancel(pCtx);
1273 if (RT_SUCCESS(rc))
1274 pEvent->enmType = VBGLR3DNDEVENTTYPE_HG_CANCEL;
1275 break;
1276 }
1277#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1278 case HOST_DND_GH_REQ_PENDING:
1279 {
1280 rc = vbglR3DnDGHRecvPending(pCtx, &pEvent->u.GH_IsPending.uScreenID);
1281 if (RT_SUCCESS(rc))
1282 pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_REQ_PENDING;
1283 break;
1284 }
1285 case HOST_DND_GH_EVT_DROPPED:
1286 {
1287 rc = vbglR3DnDGHRecvDropped(pCtx,
1288 &pEvent->u.GH_Drop.pszFormat,
1289 &pEvent->u.GH_Drop.cbFormat,
1290 &pEvent->u.GH_Drop.dndActionRequested);
1291 if (RT_SUCCESS(rc))
1292 pEvent->enmType = VBGLR3DNDEVENTTYPE_GH_DROP;
1293 break;
1294 }
1295#endif
1296 default:
1297 {
1298 rc = VERR_NOT_SUPPORTED;
1299 break;
1300 }
1301 }
1302 }
1303
1304 if (RT_FAILURE(rc))
1305 {
1306 VbglR3DnDEventFree(pEvent);
1307 LogFlowFunc(("Failed with %Rrc\n", rc));
1308 }
1309 else
1310 *ppEvent = pEvent;
1311
1312 return rc;
1313}
1314
1315/**
1316 * Frees (destroys) a formerly allocated DnD event.
1317 *
1318 * @returns IPRT status code.
1319 * @param pEvent Event to free (destroy).
1320 */
1321VBGLR3DECL(void) VbglR3DnDEventFree(PVBGLR3DNDEVENT pEvent)
1322{
1323 if (!pEvent)
1324 return;
1325
1326 /* Some messages require additional cleanup. */
1327 switch (pEvent->enmType)
1328 {
1329 case VBGLR3DNDEVENTTYPE_HG_ENTER:
1330 {
1331 if (pEvent->u.HG_Enter.pszFormats)
1332 RTStrFree(pEvent->u.HG_Enter.pszFormats);
1333 break;
1334 }
1335
1336#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1337 case VBGLR3DNDEVENTTYPE_GH_DROP:
1338 {
1339 if (pEvent->u.GH_Drop.pszFormat)
1340 RTStrFree(pEvent->u.GH_Drop.pszFormat);
1341 break;
1342 }
1343#endif
1344 case VBGLR3DNDEVENTTYPE_HG_RECEIVE:
1345 {
1346 PVBGLR3GUESTDNDMETADATA pMeta = &pEvent->u.HG_Received.Meta;
1347 if (pMeta->pvMeta)
1348 {
1349 Assert(pMeta->cbMeta);
1350 RTMemFree(pMeta->pvMeta);
1351 pMeta->cbMeta = 0;
1352 }
1353 break;
1354 }
1355
1356 default:
1357 break;
1358 }
1359
1360 RTMemFree(pEvent);
1361 pEvent = NULL;
1362}
1363
1364VBGLR3DECL(int) VbglR3DnDHGSendAckOp(PVBGLR3GUESTDNDCMDCTX pCtx, VBOXDNDACTION dndAction)
1365{
1366 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1367
1368 VBOXDNDHGACKOPMSG Msg;
1369 RT_ZERO(Msg);
1370
1371 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_HG_ACK_OP, 2);
1372 /** @todo Context ID not used yet. */
1373 Msg.u.v3.uContext.SetUInt32(0);
1374 Msg.u.v3.uAction.SetUInt32(dndAction);
1375
1376 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1377}
1378
1379/**
1380 * Host -> Guest
1381 * Requests the actual DnD data to be sent from the host.
1382 *
1383 * @returns IPRT status code.
1384 * @param pCtx DnD context to use.
1385 * @param pcszFormat Format to request the data from the host in.
1386 */
1387VBGLR3DECL(int) VbglR3DnDHGSendReqData(PVBGLR3GUESTDNDCMDCTX pCtx, const char* pcszFormat)
1388{
1389 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1390 AssertPtrReturn(pcszFormat, VERR_INVALID_POINTER);
1391 if (!RTStrIsValidEncoding(pcszFormat))
1392 return VERR_INVALID_PARAMETER;
1393
1394 const uint32_t cbFormat = (uint32_t)strlen(pcszFormat) + 1; /* Include termination */
1395
1396 VBOXDNDHGREQDATAMSG Msg;
1397 RT_ZERO(Msg);
1398
1399 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_HG_REQ_DATA, 3);
1400 /** @todo Context ID not used yet. */
1401 Msg.u.v3.uContext.SetUInt32(0);
1402 Msg.u.v3.pvFormat.SetPtr((void*)pcszFormat, cbFormat);
1403 Msg.u.v3.cbFormat.SetUInt32(cbFormat);
1404
1405 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1406}
1407
1408/**
1409 * Host -> Guest
1410 * Reports back its progress back to the host.
1411 *
1412 * @returns IPRT status code.
1413 * @param pCtx DnD context to use.
1414 * @param uStatus DnD status to report.
1415 * @param uPercent Overall progress (in percent) to report.
1416 * @param rcErr Error code (IPRT-style) to report.
1417 */
1418VBGLR3DECL(int) VbglR3DnDHGSendProgress(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uStatus, uint8_t uPercent, int rcErr)
1419{
1420 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1421 AssertReturn(uStatus > DND_PROGRESS_UNKNOWN, VERR_INVALID_PARAMETER);
1422
1423 VBOXDNDHGEVTPROGRESSMSG Msg;
1424 RT_ZERO(Msg);
1425
1426 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_HG_EVT_PROGRESS, 4);
1427 /** @todo Context ID not used yet. */
1428 Msg.u.v3.uContext.SetUInt32(0);
1429 Msg.u.v3.uStatus.SetUInt32(uStatus);
1430 Msg.u.v3.uPercent.SetUInt32(uPercent);
1431 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1432
1433 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1434}
1435
1436#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1437/**
1438 * Guest -> Host
1439 * Acknowledges that there currently is a drag'n drop operation in progress on the guest,
1440 * which eventually could be dragged over to the host.
1441 *
1442 * @returns IPRT status code.
1443 * @param pCtx DnD context to use.
1444 * @param dndActionDefault Default action for the operation to report.
1445 * @param dndLstActionsAllowed All available actions for the operation to report.
1446 * @param pcszFormats Available formats for the operation to report.
1447 * @param cbFormats Size (in bytes) of formats to report.
1448 */
1449VBGLR3DECL(int) VbglR3DnDGHSendAckPending(PVBGLR3GUESTDNDCMDCTX pCtx,
1450 VBOXDNDACTION dndActionDefault, VBOXDNDACTIONLIST dndLstActionsAllowed,
1451 const char* pcszFormats, uint32_t cbFormats)
1452{
1453 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1454 AssertPtrReturn(pcszFormats, VERR_INVALID_POINTER);
1455 AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
1456
1457 if (!RTStrIsValidEncoding(pcszFormats))
1458 return VERR_INVALID_UTF8_ENCODING;
1459
1460 VBOXDNDGHACKPENDINGMSG Msg;
1461 RT_ZERO(Msg);
1462
1463 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_ACK_PENDING, 5);
1464 /** @todo Context ID not used yet. */
1465 Msg.u.v3.uContext.SetUInt32(0);
1466 Msg.u.v3.uDefAction.SetUInt32(dndActionDefault);
1467 Msg.u.v3.uAllActions.SetUInt32(dndLstActionsAllowed);
1468 Msg.u.v3.pvFormats.SetPtr((void*)pcszFormats, cbFormats);
1469 Msg.u.v3.cbFormats.SetUInt32(cbFormats);
1470
1471 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1472}
1473
1474/**
1475 * Guest -> Host
1476 * Utility function to send DnD data from guest to the host.
1477 *
1478 * @returns IPRT status code.
1479 * @param pCtx DnD context to use.
1480 * @param pvData Data block to send.
1481 * @param cbData Size (in bytes) of data block to send.
1482 * @param pDataHdr Data header to use -- needed for accounting.
1483 */
1484static int vbglR3DnDGHSendDataInternal(PVBGLR3GUESTDNDCMDCTX pCtx,
1485 void *pvData, uint64_t cbData, PVBOXDNDSNDDATAHDR pDataHdr)
1486{
1487 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1488 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1489 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1490 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
1491
1492 VBOXDNDGHSENDDATAHDRMSG MsgHdr;
1493 RT_ZERO(MsgHdr);
1494
1495 VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DATA_HDR, 12);
1496 MsgHdr.uContext.SetUInt32(0); /** @todo Not used yet. */
1497 MsgHdr.uFlags.SetUInt32(0); /** @todo Not used yet. */
1498 MsgHdr.uScreenId.SetUInt32(0); /** @todo Not used for guest->host (yet). */
1499 MsgHdr.cbTotal.SetUInt64(pDataHdr->cbTotal);
1500 MsgHdr.cbMeta.SetUInt32(pDataHdr->cbMeta);
1501 MsgHdr.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
1502 MsgHdr.cbMetaFmt.SetUInt32(pDataHdr->cbMetaFmt);
1503 MsgHdr.cObjects.SetUInt64(pDataHdr->cObjects);
1504 MsgHdr.enmCompression.SetUInt32(0); /** @todo Not used yet. */
1505 MsgHdr.enmChecksumType.SetUInt32(RTDIGESTTYPE_INVALID); /** @todo Not used yet. */
1506 MsgHdr.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1507 MsgHdr.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1508
1509 int rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr));
1510
1511 LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU64, rc=%Rrc\n",
1512 pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects, rc));
1513
1514 if (RT_SUCCESS(rc))
1515 {
1516 VBOXDNDGHSENDDATAMSG MsgData;
1517 RT_ZERO(MsgData);
1518
1519 VBGL_HGCM_HDR_INIT(&MsgData.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DATA, 5);
1520 MsgData.u.v3.uContext.SetUInt32(0); /** @todo Not used yet. */
1521 MsgData.u.v3.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1522 MsgData.u.v3.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1523
1524 uint32_t cbCurChunk;
1525 const uint32_t cbMaxChunk = pCtx->cbMaxChunkSize;
1526 uint32_t cbSent = 0;
1527
1528 HGCMFunctionParameter *pParm = &MsgData.u.v3.pvData;
1529
1530 while (cbSent < cbData)
1531 {
1532 cbCurChunk = RT_MIN(cbData - cbSent, cbMaxChunk);
1533 pParm->SetPtr(static_cast<uint8_t *>(pvData) + cbSent, cbCurChunk);
1534
1535 MsgData.u.v3.cbData.SetUInt32(cbCurChunk);
1536
1537 rc = VbglR3HGCMCall(&MsgData.hdr, sizeof(MsgData));
1538 if (RT_FAILURE(rc))
1539 break;
1540
1541 cbSent += cbCurChunk;
1542 }
1543
1544 LogFlowFunc(("cbMaxChunk=%RU32, cbData=%RU64, cbSent=%RU32, rc=%Rrc\n",
1545 cbMaxChunk, cbData, cbSent, rc));
1546
1547 if (RT_SUCCESS(rc))
1548 Assert(cbSent == cbData);
1549 }
1550
1551 LogFlowFuncLeaveRC(rc);
1552 return rc;
1553}
1554
1555/**
1556 * Guest -> Host
1557 * Utility function to send a guest directory to the host.
1558 *
1559 * @returns IPRT status code.
1560 * @param pCtx DnD context to use.
1561 * @param pObj URI object containing the directory to send.
1562 */
1563static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
1564{
1565 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1566 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1567 AssertReturn(pObj->GetType() == DnDURIObject::Type_Directory, VERR_INVALID_PARAMETER);
1568
1569 RTCString strPath = pObj->GetDestPathAbs();
1570 LogFlowFunc(("strDir=%s (%zu), fMode=0x%x\n",
1571 strPath.c_str(), strPath.length(), pObj->GetMode()));
1572
1573 if (strPath.length() > RTPATH_MAX)
1574 return VERR_INVALID_PARAMETER;
1575
1576 const uint32_t cbPath = (uint32_t)strPath.length() + 1; /* Include termination. */
1577
1578 VBOXDNDGHSENDDIRMSG Msg;
1579 RT_ZERO(Msg);
1580
1581 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DIR, 4);
1582 /** @todo Context ID not used yet. */
1583 Msg.u.v3.uContext.SetUInt32(0);
1584 Msg.u.v3.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)cbPath);
1585 Msg.u.v3.cbName.SetUInt32((uint32_t)cbPath);
1586 Msg.u.v3.fMode.SetUInt32(pObj->GetMode());
1587
1588 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1589}
1590
1591/**
1592 * Guest -> Host
1593 * Utility function to send a file from the guest to the host.
1594 *
1595 * @returns IPRT status code.
1596 * @param pCtx DnD context to use.
1597 * @param pObj URI object containing the file to send.
1598 */
1599static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
1600{
1601 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1602 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1603 AssertReturn(pObj->GetType() == DnDURIObject::Type_File, VERR_INVALID_PARAMETER);
1604 AssertReturn(pObj->IsOpen(), VERR_INVALID_STATE);
1605
1606 uint32_t cbBuf = _64K; /** @todo Make this configurable? */
1607 void *pvBuf = RTMemAlloc(cbBuf); /** @todo Make this buffer part of PVBGLR3GUESTDNDCMDCTX? */
1608 if (!pvBuf)
1609 return VERR_NO_MEMORY;
1610
1611 RTCString strPath = pObj->GetDestPathAbs();
1612
1613 LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", strPath.c_str(), strPath.length(),
1614 pObj->GetSize(), pObj->GetMode()));
1615
1616 VBOXDNDGHSENDFILEHDRMSG MsgHdr;
1617 RT_ZERO(MsgHdr);
1618
1619 VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_GH_SND_FILE_HDR, 6);
1620 MsgHdr.uContext.SetUInt32(0); /* Context ID; unused at the moment. */
1621 MsgHdr.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1));
1622 MsgHdr.cbName.SetUInt32((uint32_t)(strPath.length() + 1));
1623 MsgHdr.uFlags.SetUInt32(0); /* Flags; unused at the moment. */
1624 MsgHdr.fMode.SetUInt32(pObj->GetMode()); /* File mode */
1625 MsgHdr.cbTotal.SetUInt64(pObj->GetSize()); /* File size (in bytes). */
1626
1627 int rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr));
1628
1629 LogFlowFunc(("Sending file header resulted in %Rrc\n", rc));
1630
1631 if (RT_SUCCESS(rc))
1632 {
1633 /*
1634 * Send the actual file data, chunk by chunk.
1635 */
1636 VBOXDNDGHSENDFILEDATAMSG Msg;
1637 RT_ZERO(Msg);
1638
1639 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_FILE_DATA, 5);
1640 Msg.u.v3.uContext.SetUInt32(0);
1641 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
1642 Msg.u.v3.cbChecksum.SetUInt32(0);
1643
1644 uint64_t cbToReadTotal = pObj->GetSize();
1645 uint64_t cbWrittenTotal = 0;
1646 while (cbToReadTotal)
1647 {
1648 uint32_t cbToRead = RT_MIN(cbToReadTotal, cbBuf);
1649 uint32_t cbRead = 0;
1650 if (cbToRead)
1651 rc = pObj->Read(pvBuf, cbToRead, &cbRead);
1652
1653 LogFlowFunc(("cbToReadTotal=%RU64, cbToRead=%RU32, cbRead=%RU32, rc=%Rrc\n",
1654 cbToReadTotal, cbToRead, cbRead, rc));
1655
1656 if ( RT_SUCCESS(rc)
1657 && cbRead)
1658 {
1659 Msg.u.v3.pvData.SetPtr(pvBuf, cbRead);
1660 Msg.u.v3.cbData.SetUInt32(cbRead);
1661 /** @todo Calculate + set checksums. */
1662
1663 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1664 }
1665
1666 if (RT_FAILURE(rc))
1667 {
1668 LogFlowFunc(("Reading failed with rc=%Rrc\n", rc));
1669 break;
1670 }
1671
1672 Assert(cbRead <= cbToReadTotal);
1673 cbToReadTotal -= cbRead;
1674 cbWrittenTotal += cbRead;
1675
1676 LogFlowFunc(("%RU64/%RU64 -- %RU8%%\n", cbWrittenTotal, pObj->GetSize(), cbWrittenTotal * 100 / pObj->GetSize()));
1677 };
1678 }
1679
1680 RTMemFree(pvBuf);
1681
1682 LogFlowFuncLeaveRC(rc);
1683 return rc;
1684}
1685
1686/**
1687 * Guest -> Host
1688 * Utility function to send an URI object from guest to the host.
1689 *
1690 * @returns IPRT status code.
1691 * @param pCtx DnD context to use.
1692 * @param pObj URI object to send from guest to the host.
1693 */
1694static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
1695{
1696 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1697 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1698
1699 int rc;
1700
1701 switch (pObj->GetType())
1702 {
1703 case DnDURIObject::Type_Directory:
1704 rc = vbglR3DnDGHSendDir(pCtx, pObj);
1705 break;
1706
1707 case DnDURIObject::Type_File:
1708 rc = vbglR3DnDGHSendFile(pCtx, pObj);
1709 break;
1710
1711 default:
1712 AssertMsgFailed(("Object type %ld not implemented\n", pObj->GetType()));
1713 rc = VERR_NOT_IMPLEMENTED;
1714 break;
1715 }
1716
1717 return rc;
1718}
1719
1720/**
1721 * Guest -> Host
1722 * Utility function to send raw data from guest to the host.
1723 *
1724 * @returns IPRT status code.
1725 * @param pCtx DnD context to use.
1726 * @param pvData Block to raw data to send.
1727 * @param cbData Size (in bytes) of raw data to send.
1728 */
1729static int vbglR3DnDGHSendRawData(PVBGLR3GUESTDNDCMDCTX pCtx, void *pvData, size_t cbData)
1730{
1731 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1732 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1733 /* cbData can be 0. */
1734
1735 VBOXDNDDATAHDR dataHdr;
1736 RT_ZERO(dataHdr);
1737
1738 /* For raw data only the total size is required to be specified. */
1739 dataHdr.cbTotal = cbData;
1740
1741 return vbglR3DnDGHSendDataInternal(pCtx, pvData, cbData, &dataHdr);
1742}
1743
1744/**
1745 * Guest -> Host
1746 * Utility function to send URI data from guest to the host.
1747 *
1748 * @returns IPRT status code.
1749 * @param pCtx DnD context to use.
1750 * @param pvData Block to URI data to send.
1751 * @param cbData Size (in bytes) of URI data to send.
1752 */
1753static int vbglR3DnDGHSendURIData(PVBGLR3GUESTDNDCMDCTX pCtx, const void *pvData, size_t cbData)
1754{
1755 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1756 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1757 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1758
1759 RTCList<RTCString> lstPaths =
1760 RTCString((const char *)pvData, cbData).split("\r\n");
1761
1762 /** @todo Add symlink support (DNDURILIST_FLAGS_RESOLVE_SYMLINKS) here. */
1763 /** @todo Add lazy loading (DNDURILIST_FLAGS_LAZY) here. */
1764 uint32_t fFlags = DNDURILIST_FLAGS_KEEP_OPEN;
1765
1766 DnDURIList lstURI;
1767 int rc = lstURI.AppendURIPathsFromList(lstPaths, fFlags);
1768 if (RT_SUCCESS(rc))
1769 {
1770 /*
1771 * Send the (meta) data; in case of URIs it's the (non-recursive) file/directory
1772 * URI list the host needs to know upfront to set up the drag'n drop operation.
1773 */
1774 RTCString strRootDest = lstURI.GetRootEntries();
1775 if (strRootDest.isNotEmpty())
1776 {
1777 void *pvURIList = (void *)strRootDest.c_str(); /* URI root list. */
1778 uint32_t cbURLIist = (uint32_t)strRootDest.length() + 1; /* Include string termination. */
1779
1780 /* The total size also contains the size of the meta data. */
1781 uint64_t cbTotal = cbURLIist;
1782 cbTotal += lstURI.GetTotalBytes();
1783
1784 /* We're going to send an URI list in text format. */
1785 const char szMetaFmt[] = "text/uri-list";
1786 const uint32_t cbMetaFmt = (uint32_t)strlen(szMetaFmt) + 1; /* Include termination. */
1787
1788 VBOXDNDDATAHDR dataHdr;
1789 dataHdr.uFlags = 0; /* Flags not used yet. */
1790 dataHdr.cbTotal = cbTotal;
1791 dataHdr.cbMeta = cbURLIist;
1792 dataHdr.pvMetaFmt = (void *)szMetaFmt;
1793 dataHdr.cbMetaFmt = cbMetaFmt;
1794 dataHdr.cObjects = lstURI.GetTotalCount();
1795
1796 rc = vbglR3DnDGHSendDataInternal(pCtx,
1797 pvURIList, cbURLIist, &dataHdr);
1798 }
1799 else
1800 rc = VERR_INVALID_PARAMETER;
1801 }
1802
1803 if (RT_SUCCESS(rc))
1804 {
1805 while (!lstURI.IsEmpty())
1806 {
1807 DnDURIObject *pNextObj = lstURI.First();
1808
1809 rc = vbglR3DnDGHSendURIObject(pCtx, pNextObj);
1810 if (RT_FAILURE(rc))
1811 break;
1812
1813 lstURI.RemoveFirst();
1814 }
1815 }
1816
1817 return rc;
1818}
1819
1820/**
1821 * Guest -> Host
1822 * Sends data, which either can be raw or URI data, from guest to the host. This function
1823 * initiates the actual data transfer from guest to the host.
1824 *
1825 * @returns IPRT status code.
1826 * @param pCtx DnD context to use.
1827 * @param pszFormat In which format the data will be sent.
1828 * @param pvData Data block to send.
1829 * @param cbData Size (in bytes) of data block to send.
1830 */
1831VBGLR3DECL(int) VbglR3DnDGHSendData(PVBGLR3GUESTDNDCMDCTX pCtx, const char *pszFormat, void *pvData, uint32_t cbData)
1832{
1833 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1834 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
1835 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1836 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1837
1838 LogFlowFunc(("pszFormat=%s, pvData=%p, cbData=%RU32\n", pszFormat, pvData, cbData));
1839
1840 int rc;
1841 if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat)))
1842 {
1843 /* Send file data. */
1844 rc = vbglR3DnDGHSendURIData(pCtx, pvData, cbData);
1845 }
1846 else
1847 rc = vbglR3DnDGHSendRawData(pCtx, pvData, cbData);
1848
1849 if (RT_FAILURE(rc))
1850 {
1851 int rc2 = VbglR3DnDGHSendError(pCtx, rc);
1852 if (RT_FAILURE(rc2))
1853 LogFlowFunc(("Unable to send error (%Rrc) to host, rc=%Rrc\n", rc, rc2));
1854 }
1855
1856 return rc;
1857}
1858
1859/**
1860 * Guest -> Host
1861 * Send an error back to the host.
1862 *
1863 * @returns IPRT status code.
1864 * @param pCtx DnD context to use.
1865 * @param rcErr Error (IPRT-style) to send.
1866 */
1867VBGLR3DECL(int) VbglR3DnDGHSendError(PVBGLR3GUESTDNDCMDCTX pCtx, int rcErr)
1868{
1869 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1870
1871 VBOXDNDGHEVTERRORMSG Msg;
1872 RT_ZERO(Msg);
1873
1874 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_EVT_ERROR, 2);
1875 /** @todo Context ID not used yet. */
1876 Msg.u.v3.uContext.SetUInt32(0);
1877 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1878
1879 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1880
1881 /*
1882 * Never return an error if the host did not accept the error at the current
1883 * time. This can be due to the host not having any appropriate callbacks
1884 * set which would handle that error.
1885 *
1886 * bird: Looks like VERR_NOT_SUPPORTED is what the host will return if it
1887 * doesn't an appropriate callback. The code used to ignore ALL errors
1888 * the host would return, also relevant ones.
1889 */
1890 if (RT_FAILURE(rc))
1891 LogFlowFunc(("Sending error %Rrc failed with rc=%Rrc\n", rcErr, rc));
1892 if (rc == VERR_NOT_SUPPORTED)
1893 rc = VINF_SUCCESS;
1894
1895 return rc;
1896}
1897#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1898
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