VirtualBox

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

Last change on this file since 107697 was 107697, checked in by vboxsync, 10 days ago

Additions/common/VBoxGuest/lib/VBoxGuestR3LibDragAndDrop.cpp: Remove redundant condition, pvChunk is != NULL at this point, bugref:3409

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