VirtualBox

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

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

DnD/VbglR3: Removed code paths for handling older protocols (< v3).

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