VirtualBox

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

Last change on this file since 84985 was 84985, checked in by vboxsync, 4 years ago

DnD/VbglR3: Don't use RT_ZERO on non-POD types.

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