VirtualBox

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

Last change on this file since 85271 was 85029, checked in by vboxsync, 5 years ago

DnD/VbglR3: Also handle HOST_DND_HG_SND_FILE_HDR in VbglR3DnDEventGetNext().

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