VirtualBox

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

Last change on this file since 97436 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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