VirtualBox

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

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

DnD/VbglR3: Documentation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 84.0 KB
Line 
1/* $Id: VBoxGuestR3LibDragAndDrop.cpp 74333 2018-09-18 09:19:57Z vboxsync $ */
2/** @file
3 * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Drag & Drop.
4 */
5
6/*
7 * Copyright (C) 2011-2018 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/path.h>
32#include <iprt/dir.h>
33#include <iprt/file.h>
34#include <iprt/uri.h>
35#include <iprt/thread.h>
36
37#include <iprt/cpp/list.h>
38#include <iprt/cpp/ministring.h>
39
40#ifdef LOG_GROUP
41 #undef LOG_GROUP
42#endif
43#define LOG_GROUP LOG_GROUP_GUEST_DND
44#include <VBox/log.h>
45
46#include <VBox/VBoxGuestLib.h>
47#include <VBox/GuestHost/DragAndDrop.h>
48#include <VBox/HostServices/DragAndDropSvc.h>
49
50using namespace DragAndDropSvc;
51
52#include "VBoxGuestR3LibInternal.h"
53
54
55/*********************************************************************************************************************************
56* Private internal functions *
57*********************************************************************************************************************************/
58
59/**
60 * Receives the next upcoming message for a given DnD context.
61 *
62 * @returns IPRT status code.
63 * @param pCtx DnD context to use.
64 * @param puMsg Where to store the message type.
65 * @param pcParms Where to store the number of parameters required for receiving the message.
66 * @param fWait Whether to wait (block) for a new message to arrive or not.
67 */
68static int vbglR3DnDGetNextMsgType(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puMsg, uint32_t *pcParms, bool fWait)
69{
70 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
71 AssertPtrReturn(puMsg, VERR_INVALID_POINTER);
72 AssertPtrReturn(pcParms, VERR_INVALID_POINTER);
73
74 VBOXDNDNEXTMSGMSG Msg;
75 RT_ZERO(Msg);
76 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GET_NEXT_HOST_MSG, 3);
77 Msg.uMsg.SetUInt32(0);
78 Msg.cParms.SetUInt32(0);
79 Msg.fBlock.SetUInt32(fWait ? 1 : 0);
80
81 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
82 if (RT_SUCCESS(rc))
83 {
84 rc = Msg.uMsg.GetUInt32(puMsg); AssertRC(rc);
85 rc = Msg.cParms.GetUInt32(pcParms); AssertRC(rc);
86 }
87
88 return rc;
89}
90
91/**
92 * Host -> Guest
93 * Utility function to receive a so-called "action message" from the host.
94 * Certain DnD messages use the same amount / sort of parameters and grouped as "action messages".
95 *
96 * @returns IPRT status code.
97 * @param pCtx DnD context to use.
98 * @param uMsg Which kind of message to receive.
99 * @param puScreenID Where to store the host screen ID the message is bound to.
100 * @param puX Where to store the absolute X coordinates.
101 * @param puY Where to store the absolute Y coordinates.
102 * @param puDefAction Where to store the default action to perform.
103 * @param puAllActions Where to store the available actions.
104 * @param ppszFormats Where to store List of formats.
105 * @param pcbFormats Size (in bytes) of where to store the list of formats.
106 *
107 * @todo r=andy Get rid of this function as soon as we resolved the protocol TODO #1.
108 * This was part of the initial protocol and needs to go.
109 */
110static int vbglR3DnDHGRecvAction(PVBGLR3GUESTDNDCMDCTX pCtx,
111 uint32_t uMsg,
112 uint32_t *puScreenId,
113 uint32_t *puX,
114 uint32_t *puY,
115 uint32_t *puDefAction,
116 uint32_t *puAllActions,
117 char *pszFormats,
118 uint32_t cbFormats,
119 uint32_t *pcbFormatsRecv)
120{
121 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
122 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
123 AssertPtrReturn(puX, VERR_INVALID_POINTER);
124 AssertPtrReturn(puY, VERR_INVALID_POINTER);
125 AssertPtrReturn(puDefAction, VERR_INVALID_POINTER);
126 AssertPtrReturn(puAllActions, VERR_INVALID_POINTER);
127 AssertPtrReturn(pszFormats, VERR_INVALID_POINTER);
128 AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
129 AssertPtrReturn(pcbFormatsRecv, VERR_INVALID_POINTER);
130
131 VBOXDNDHGACTIONMSG Msg;
132 RT_ZERO(Msg);
133 if (pCtx->uProtocol < 3)
134 {
135 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, uMsg, 7);
136 Msg.u.v1.uScreenId.SetUInt32(0);
137 Msg.u.v1.uX.SetUInt32(0);
138 Msg.u.v1.uY.SetUInt32(0);
139 Msg.u.v1.uDefAction.SetUInt32(0);
140 Msg.u.v1.uAllActions.SetUInt32(0);
141 Msg.u.v1.pvFormats.SetPtr(pszFormats, cbFormats);
142 Msg.u.v1.cbFormats.SetUInt32(0);
143 }
144 else
145 {
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(pszFormats, cbFormats);
154 Msg.u.v3.cbFormats.SetUInt32(0);
155 }
156
157 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
158 if (RT_SUCCESS(rc))
159 {
160 if (pCtx->uProtocol < 3)
161 {
162 rc = Msg.u.v1.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
163 rc = Msg.u.v1.uX.GetUInt32(puX); AssertRC(rc);
164 rc = Msg.u.v1.uY.GetUInt32(puY); AssertRC(rc);
165 rc = Msg.u.v1.uDefAction.GetUInt32(puDefAction); AssertRC(rc);
166 rc = Msg.u.v1.uAllActions.GetUInt32(puAllActions); AssertRC(rc);
167 rc = Msg.u.v1.cbFormats.GetUInt32(pcbFormatsRecv); AssertRC(rc);
168 }
169 else
170 {
171 /** @todo Context ID not used yet. */
172 rc = Msg.u.v3.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
173 rc = Msg.u.v3.uX.GetUInt32(puX); AssertRC(rc);
174 rc = Msg.u.v3.uY.GetUInt32(puY); AssertRC(rc);
175 rc = Msg.u.v3.uDefAction.GetUInt32(puDefAction); AssertRC(rc);
176 rc = Msg.u.v3.uAllActions.GetUInt32(puAllActions); AssertRC(rc);
177 rc = Msg.u.v3.cbFormats.GetUInt32(pcbFormatsRecv); AssertRC(rc);
178 }
179
180 AssertReturn(cbFormats >= *pcbFormatsRecv, VERR_TOO_MUCH_DATA);
181 }
182
183 return rc;
184}
185
186/**
187 * Host -> Guest
188 * Utility function to receive a HOST_DND_HG_EVT_LEAVE message from the host.
189 *
190 * @returns IPRT status code.
191 * @param pCtx DnD context to use.
192 */
193static int vbglR3DnDHGRecvLeave(PVBGLR3GUESTDNDCMDCTX pCtx)
194{
195 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
196
197 VBOXDNDHGLEAVEMSG Msg;
198 RT_ZERO(Msg);
199 if (pCtx->uProtocol < 3)
200 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_EVT_LEAVE, 0);
201 else
202 {
203 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_EVT_LEAVE, 1);
204 /** @todo Context ID not used yet. */
205 Msg.u.v3.uContext.SetUInt32(0);
206 }
207
208 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
209}
210
211/**
212 * Host -> Guest
213 * Utility function to receive a HOST_DND_HG_EVT_CANCEL message from the host.
214 *
215 * @returns IPRT status code.
216 * @param pCtx DnD context to use.
217 */
218static int vbglR3DnDHGRecvCancel(PVBGLR3GUESTDNDCMDCTX pCtx)
219{
220 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
221
222 VBOXDNDHGCANCELMSG Msg;
223 RT_ZERO(Msg);
224 if (pCtx->uProtocol < 3)
225 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_EVT_CANCEL, 0);
226 else
227 {
228 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_EVT_CANCEL, 1);
229 /** @todo Context ID not used yet. */
230 Msg.u.v3.uContext.SetUInt32(0);
231 }
232
233 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
234}
235
236/**
237 * Host -> Guest
238 * Utility function to receive a HOST_DND_HG_SND_DIR message from the host.
239 *
240 * @returns IPRT status code.
241 * @param pCtx DnD context to use.
242 * @param pszDirname Where to store the directory name of the directory being created.
243 * @param cbDirname Size (in bytes) of where to store the directory name of the directory being created.
244 * @param pcbDirnameRecv Size (in bytes) of the actual directory name received.
245 * @param pfMode Where to store the directory creation mode.
246 */
247static int vbglR3DnDHGRecvDir(PVBGLR3GUESTDNDCMDCTX pCtx,
248 char *pszDirname,
249 uint32_t cbDirname,
250 uint32_t *pcbDirnameRecv,
251 uint32_t *pfMode)
252{
253 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
254 AssertPtrReturn(pszDirname, VERR_INVALID_POINTER);
255 AssertReturn(cbDirname, VERR_INVALID_PARAMETER);
256 AssertPtrReturn(pcbDirnameRecv, VERR_INVALID_POINTER);
257 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
258
259 VBOXDNDHGSENDDIRMSG Msg;
260 RT_ZERO(Msg);
261 if (pCtx->uProtocol < 3)
262 {
263 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_SND_DIR, 3);
264 Msg.u.v1.pvName.SetPtr(pszDirname, cbDirname);
265 Msg.u.v1.cbName.SetUInt32(cbDirname);
266 Msg.u.v1.fMode.SetUInt32(0);
267 }
268 else
269 {
270 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_SND_DIR, 4);
271 /** @todo Context ID not used yet. */
272 Msg.u.v3.uContext.SetUInt32(0);
273 Msg.u.v3.pvName.SetPtr(pszDirname, cbDirname);
274 Msg.u.v3.cbName.SetUInt32(cbDirname);
275 Msg.u.v3.fMode.SetUInt32(0);
276 }
277
278 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
279 if (RT_SUCCESS(rc))
280 {
281 if (pCtx->uProtocol < 3)
282 {
283 rc = Msg.u.v1.cbName.GetUInt32(pcbDirnameRecv); AssertRC(rc);
284 rc = Msg.u.v1.fMode.GetUInt32(pfMode); AssertRC(rc);
285 }
286 else
287 {
288 /** @todo Context ID not used yet. */
289 rc = Msg.u.v3.cbName.GetUInt32(pcbDirnameRecv); AssertRC(rc);
290 rc = Msg.u.v3.fMode.GetUInt32(pfMode); AssertRC(rc);
291 }
292
293 AssertReturn(cbDirname >= *pcbDirnameRecv, VERR_TOO_MUCH_DATA);
294 }
295
296 return rc;
297}
298
299/**
300 * Host -> Guest
301 * Utility function to receive a HOST_DND_HG_SND_FILE_DATA message from the host.
302 *
303 * @returns IPRT status code.
304 * @param pCtx DnD context to use.
305 * @param pszFilename Where to store the file name of the file being transferred.
306 * Only needed for protocol v1.
307 * @param cbFilename Size (in bytes) of where to store the file name of the file being transferred.
308 * Only needed for protocol v1.
309 * @param pcbFilenameRev Size (in bytes) of the actual file name received.
310 * Only needed for protocol v1.
311 * @param pvData Where to store the file data chunk.
312 * @param cbData Size (in bytes) of where to store the data chunk.
313 * @param pcbDataRecv Size (in bytes) of the actual data chunk size received.
314 * @param pfMode Where to store the file creation mode.
315 * Only needed for protocol v1.
316 */
317static int vbglR3DnDHGRecvFileData(PVBGLR3GUESTDNDCMDCTX pCtx,
318 char *pszFilename,
319 uint32_t cbFilename,
320 uint32_t *pcbFilenameRecv,
321 void *pvData,
322 uint32_t cbData,
323 uint32_t *pcbDataRecv,
324 uint32_t *pfMode)
325{
326 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
327 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
328 AssertReturn(cbFilename, VERR_INVALID_PARAMETER);
329 AssertPtrReturn(pcbFilenameRecv, VERR_INVALID_POINTER);
330 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
331 AssertReturn(cbData, VERR_INVALID_PARAMETER);
332 AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER);
333 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
334
335 VBOXDNDHGSENDFILEDATAMSG Msg;
336 RT_ZERO(Msg);
337 if (pCtx->uProtocol <= 1)
338 {
339 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_SND_FILE_DATA, 5);
340 Msg.u.v1.pvName.SetPtr(pszFilename, cbFilename);
341 Msg.u.v1.cbName.SetUInt32(0);
342 Msg.u.v1.pvData.SetPtr(pvData, cbData);
343 Msg.u.v1.cbData.SetUInt32(0);
344 Msg.u.v1.fMode.SetUInt32(0);
345 }
346 else if (pCtx->uProtocol == 2)
347 {
348 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_SND_FILE_DATA, 3);
349 Msg.u.v2.uContext.SetUInt32(0);
350 Msg.u.v2.pvData.SetPtr(pvData, cbData);
351 Msg.u.v2.cbData.SetUInt32(cbData);
352 }
353 else if (pCtx->uProtocol >= 3)
354 {
355 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_SND_FILE_DATA, 5);
356 Msg.u.v3.uContext.SetUInt32(0);
357 Msg.u.v3.pvData.SetPtr(pvData, cbData);
358 Msg.u.v3.cbData.SetUInt32(0);
359 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
360 Msg.u.v3.cbChecksum.SetUInt32(0);
361 }
362 else
363 AssertMsgFailed(("Protocol %RU32 not implemented\n", pCtx->uProtocol));
364
365 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
366 if (RT_SUCCESS(rc))
367 {
368 if (pCtx->uProtocol <= 1)
369 {
370 rc = Msg.u.v1.cbName.GetUInt32(pcbFilenameRecv); AssertRC(rc);
371 rc = Msg.u.v1.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
372 rc = Msg.u.v1.fMode.GetUInt32(pfMode); AssertRC(rc);
373
374 AssertReturn(cbFilename >= *pcbFilenameRecv, VERR_TOO_MUCH_DATA);
375 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
376 }
377 else if (pCtx->uProtocol == 2)
378 {
379 /** @todo Context ID not used yet. */
380 rc = Msg.u.v2.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
381 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
382 }
383 else if (pCtx->uProtocol >= 3)
384 {
385 /** @todo Context ID not used yet. */
386 rc = Msg.u.v3.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
387 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
388 /** @todo Add checksum support. */
389 }
390 else
391 AssertMsgFailed(("Protocol %RU32 not implemented\n", pCtx->uProtocol));
392 }
393
394 return rc;
395}
396
397/**
398 * Host -> Guest
399 * Utility function to receive the HOST_DND_HG_SND_FILE_HDR message from the host.
400 *
401 * @returns IPRT status code.
402 * @param pCtx DnD context to use.
403 * @param pszFilename Where to store the file name of the file being transferred.
404 * @param cbFilename Size (in bytes) of where to store the file name of the file being transferred.
405 * @param puFlags File transfer flags. Currently not being used.
406 * @param pfMode Where to store the file creation mode.
407 * @param pcbTotal Where to store the file size (in bytes).
408 */
409static int vbglR3DnDHGRecvFileHdr(PVBGLR3GUESTDNDCMDCTX pCtx,
410 char *pszFilename,
411 uint32_t cbFilename,
412 uint32_t *puFlags,
413 uint32_t *pfMode,
414 uint64_t *pcbTotal)
415{
416 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
417 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
418 AssertReturn(cbFilename, VERR_INVALID_PARAMETER);
419 AssertPtrReturn(puFlags, VERR_INVALID_POINTER);
420 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
421 AssertReturn(pcbTotal, VERR_INVALID_POINTER);
422
423 VBOXDNDHGSENDFILEHDRMSG Msg;
424 RT_ZERO(Msg);
425 int rc;
426 if (pCtx->uProtocol <= 1)
427 rc = VERR_NOT_SUPPORTED;
428 else
429 {
430 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_SND_FILE_HDR, 6);
431 Msg.uContext.SetUInt32(0); /** @todo Not used yet. */
432 Msg.pvName.SetPtr(pszFilename, cbFilename);
433 Msg.cbName.SetUInt32(cbFilename);
434 Msg.uFlags.SetUInt32(0);
435 Msg.fMode.SetUInt32(0);
436 Msg.cbTotal.SetUInt64(0);
437
438 rc = VINF_SUCCESS;
439 }
440
441 if (RT_SUCCESS(rc))
442 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
443 if (RT_SUCCESS(rc))
444 {
445 /** @todo Get context ID. */
446 rc = Msg.uFlags.GetUInt32(puFlags); AssertRC(rc);
447 rc = Msg.fMode.GetUInt32(pfMode); AssertRC(rc);
448 rc = Msg.cbTotal.GetUInt64(pcbTotal); AssertRC(rc);
449 }
450
451 return rc;
452}
453
454/**
455 * Host -> Guest
456 * Helper function for receiving URI data from the host. Do not call directly.
457 * This function also will take care of the file creation / locking on the guest.
458 *
459 * @returns IPRT status code.
460 * @param pCtx DnD context to use.
461 * @param pDataHdr DnD data header to use. Needed for accounting.
462 * @param pDroppedFiles Dropped files object to use for maintaining the file creation / locking.
463 */
464static int vbglR3DnDHGRecvURIData(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr, DnDDroppedFiles *pDroppedFiles)
465{
466 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
467 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
468 AssertPtrReturn(pDroppedFiles, VERR_INVALID_POINTER);
469
470 /* Only count the raw data minus the already received meta data. */
471 Assert(pDataHdr->cbTotal >= pDataHdr->cbMeta);
472 uint64_t cbToRecvBytes = pDataHdr->cbTotal - pDataHdr->cbMeta;
473 uint64_t cToRecvObjs = pDataHdr->cObjects;
474
475 LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64, (cbTotal=%RU64, cbMeta=%RU32)\n",
476 cbToRecvBytes, cToRecvObjs, pDataHdr->cbTotal, pDataHdr->cbMeta));
477
478 /*
479 * Only do accounting for protocol v3 and up.
480 * The older protocols did not have any data accounting available, so
481 * we simply tried to receive as much data as available and bail out.
482 */
483 const bool fDoAccounting = pCtx->uProtocol >= 3;
484
485 /* Anything to do at all? */
486 if (fDoAccounting)
487 {
488 /* Note: Do not check for cbToRecvBytes == 0 here, as this might be just
489 * a bunch of 0-byte files to be transferred. */
490 if (!cToRecvObjs)
491 return VINF_SUCCESS;
492 }
493
494 /*
495 * Allocate temporary chunk buffer.
496 */
497 uint32_t cbChunkMax = pCtx->cbMaxChunkSize;
498 void *pvChunk = RTMemAlloc(cbChunkMax);
499 if (!pvChunk)
500 return VERR_NO_MEMORY;
501 uint32_t cbChunkRead = 0;
502
503 uint64_t cbFileSize = 0; /* Total file size (in bytes). */
504 uint64_t cbFileWritten = 0; /* Written bytes. */
505
506 /*
507 * Create and query the (unique) drop target directory in the user's temporary directory.
508 */
509 int rc = pDroppedFiles->OpenTemp(0 /* fFlags */);
510 if (RT_FAILURE(rc))
511 {
512 RTMemFree(pvChunk);
513 return VERR_NO_MEMORY;
514 }
515
516 const char *pszDropDir = pDroppedFiles->GetDirAbs();
517 AssertPtr(pszDropDir);
518
519 /*
520 * Enter the main loop of retieving files + directories.
521 */
522 DnDURIObject objFile(DnDURIObject::File);
523
524 char szPathName[RTPATH_MAX] = { 0 };
525 uint32_t cbPathName = 0;
526 uint32_t fFlags = 0;
527 uint32_t fMode = 0;
528
529 /*
530 * Only wait for new incoming commands for protocol v3 and up.
531 * The older protocols did not have any data accounting available, so
532 * we simply tried to receive as much data as available and bail out.
533 */
534 const bool fWait = pCtx->uProtocol >= 3;
535
536 do
537 {
538 LogFlowFunc(("Wating for new message ...\n"));
539
540 uint32_t uNextMsg;
541 uint32_t cNextParms;
542 rc = vbglR3DnDGetNextMsgType(pCtx, &uNextMsg, &cNextParms, fWait);
543 if (RT_SUCCESS(rc))
544 {
545 LogFlowFunc(("uNextMsg=%RU32, cNextParms=%RU32\n", uNextMsg, cNextParms));
546
547 switch (uNextMsg)
548 {
549 case HOST_DND_HG_SND_DIR:
550 {
551 rc = vbglR3DnDHGRecvDir(pCtx,
552 szPathName,
553 sizeof(szPathName),
554 &cbPathName,
555 &fMode);
556 LogFlowFunc(("HOST_DND_HG_SND_DIR pszPathName=%s, cbPathName=%RU32, fMode=0x%x, rc=%Rrc\n",
557 szPathName, cbPathName, fMode, rc));
558
559 char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName);
560 if (pszPathAbs)
561 {
562#ifdef RT_OS_WINDOWS
563 uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL;
564#else
565 uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRWXU;
566#endif
567 rc = RTDirCreate(pszPathAbs, fCreationMode, 0);
568 if (RT_SUCCESS(rc))
569 rc = pDroppedFiles->AddDir(pszPathAbs);
570
571 if ( RT_SUCCESS(rc)
572 && fDoAccounting)
573 {
574 Assert(cToRecvObjs);
575 cToRecvObjs--;
576 }
577
578 RTStrFree(pszPathAbs);
579 }
580 else
581 rc = VERR_NO_MEMORY;
582 break;
583 }
584 case HOST_DND_HG_SND_FILE_HDR:
585 case HOST_DND_HG_SND_FILE_DATA:
586 {
587 if (uNextMsg == HOST_DND_HG_SND_FILE_HDR)
588 {
589 rc = vbglR3DnDHGRecvFileHdr(pCtx,
590 szPathName,
591 sizeof(szPathName),
592 &fFlags,
593 &fMode,
594 &cbFileSize);
595 LogFlowFunc(("HOST_DND_HG_SND_FILE_HDR: "
596 "szPathName=%s, fFlags=0x%x, fMode=0x%x, cbFileSize=%RU64, rc=%Rrc\n",
597 szPathName, fFlags, fMode, cbFileSize, rc));
598 }
599 else
600 {
601 rc = vbglR3DnDHGRecvFileData(pCtx,
602 szPathName,
603 sizeof(szPathName),
604 &cbPathName,
605 pvChunk,
606 cbChunkMax,
607 &cbChunkRead,
608 &fMode);
609
610 LogFlowFunc(("HOST_DND_HG_SND_FILE_DATA: "
611 "szPathName=%s, cbPathName=%RU32, cbChunkRead=%RU32, fMode=0x%x, rc=%Rrc\n",
612 szPathName, cbPathName, cbChunkRead, fMode, rc));
613 }
614
615 if ( RT_SUCCESS(rc)
616 && ( uNextMsg == HOST_DND_HG_SND_FILE_HDR
617 /* Protocol v1 always sends the file name, so opening the file every time. */
618 || pCtx->uProtocol <= 1)
619 )
620 {
621 char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName);
622 if (pszPathAbs)
623 {
624 LogFlowFunc(("Opening pszPathName=%s, cbPathName=%RU32, fMode=0x%x, cbFileSize=%zu\n",
625 szPathName, cbPathName, fMode, cbFileSize));
626
627 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
628 if (pCtx->uProtocol <= 1)
629 fOpen |= RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND;
630 else
631 fOpen |= RTFILE_O_CREATE_REPLACE;
632
633 /* Is there already a file open, e.g. in transfer? */
634 if (!objFile.IsOpen())
635 {
636 RTCString strPathAbs(pszPathAbs);
637#ifdef RT_OS_WINDOWS
638 uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL;
639#else
640 uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR;
641#endif
642 rc = objFile.OpenEx(strPathAbs, DnDURIObject::File, DnDURIObject::Target, fOpen, fCreationMode);
643 if (RT_SUCCESS(rc))
644 {
645 rc = pDroppedFiles->AddFile(strPathAbs.c_str());
646 if (RT_SUCCESS(rc))
647 {
648 cbFileWritten = 0;
649
650 if (pCtx->uProtocol >= 2) /* Set the expected file size. */
651 objFile.SetSize(cbFileSize);
652 }
653 }
654 }
655 else
656 {
657 AssertMsgFailed(("ObjType=%RU32, Proto=%RU32\n", objFile.GetType(), pCtx->uProtocol));
658 rc = VERR_WRONG_ORDER;
659 }
660
661 RTStrFree(pszPathAbs);
662 }
663 else
664 rc = VERR_NO_MEMORY;
665 }
666
667 if ( RT_SUCCESS(rc)
668 && uNextMsg == HOST_DND_HG_SND_FILE_DATA)
669 {
670 uint32_t cbChunkWritten;
671 rc = objFile.Write(pvChunk, cbChunkRead, &cbChunkWritten);
672 if (RT_SUCCESS(rc))
673 {
674 LogFlowFunc(("HOST_DND_HG_SND_FILE_DATA "
675 "cbChunkRead=%RU32, cbChunkWritten=%RU32, cbFileWritten=%RU64 cbFileSize=%RU64\n",
676 cbChunkRead, cbChunkWritten, cbFileWritten + cbChunkWritten, cbFileSize));
677
678 cbFileWritten += cbChunkWritten;
679
680 if (fDoAccounting)
681 {
682 Assert(cbChunkRead <= cbToRecvBytes);
683 cbToRecvBytes -= cbChunkRead;
684 }
685 }
686 }
687
688 bool fClose = false;
689 if (pCtx->uProtocol >= 2)
690 {
691 /* Data transfer complete? Close the file. */
692 fClose = objFile.IsComplete();
693 if ( fClose
694 && fDoAccounting)
695 {
696 Assert(cToRecvObjs);
697 cToRecvObjs--;
698 }
699
700 /* Only since protocol v2 we know the file size upfront. */
701 Assert(cbFileWritten <= cbFileSize);
702 }
703 else
704 fClose = true; /* Always close the file after each chunk. */
705
706 if (fClose)
707 {
708 LogFlowFunc(("Closing file\n"));
709 objFile.Close();
710 }
711
712 break;
713 }
714 case HOST_DND_HG_EVT_CANCEL:
715 {
716 rc = vbglR3DnDHGRecvCancel(pCtx);
717 if (RT_SUCCESS(rc))
718 rc = VERR_CANCELLED;
719 break;
720 }
721 default:
722 {
723 LogFlowFunc(("Message %RU32 not supported\n", uNextMsg));
724 rc = VERR_NOT_SUPPORTED;
725 break;
726 }
727 }
728 }
729
730 if (RT_FAILURE(rc))
731 break;
732
733 if (fDoAccounting)
734 {
735 LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64\n", cbToRecvBytes, cToRecvObjs));
736 if ( !cbToRecvBytes
737 && !cToRecvObjs)
738 {
739 break;
740 }
741 }
742
743 } while (RT_SUCCESS(rc));
744
745 LogFlowFunc(("Loop ended with %Rrc\n", rc));
746
747 /* All URI data processed? */
748 if (rc == VERR_NO_DATA)
749 rc = VINF_SUCCESS;
750
751 /* Delete temp buffer again. */
752 if (pvChunk)
753 RTMemFree(pvChunk);
754
755 /* Cleanup on failure or if the user has canceled the operation or
756 * something else went wrong. */
757 if (RT_FAILURE(rc))
758 {
759 objFile.Close();
760 pDroppedFiles->Rollback();
761 }
762 else
763 {
764 /** @todo Compare the URI list with the dirs/files we really transferred. */
765 /** @todo Implement checksum verification, if any. */
766 }
767
768 /*
769 * Close the dropped files directory.
770 * Don't try to remove it here, however, as the files are being needed
771 * by the client's drag'n drop operation lateron.
772 */
773 int rc2 = pDroppedFiles->Reset(false /* fRemoveDropDir */);
774 if (RT_FAILURE(rc2)) /* Not fatal, don't report back to host. */
775 LogFlowFunc(("Closing dropped files directory failed with %Rrc\n", rc2));
776
777 LogFlowFuncLeaveRC(rc);
778 return rc;
779}
780
781/**
782 * Host -> Guest
783 * Utility function to receive the HOST_DND_HG_SND_DATA message from the host.
784 *
785 * @returns IPRT status code.
786 * @param pCtx DnD context to use.
787 * @param pDataHdr DnD data header to use. Need for accounting and stuff.
788 * @param pvData Where to store the received data from the host.
789 * @param cbData Size (in bytes) of where to store the received data.
790 * @param pcbDataRecv Where to store the received amount of data (in bytes).
791 */
792static int vbglR3DnDHGRecvDataRaw(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr,
793 void *pvData, uint32_t cbData, uint32_t *pcbDataRecv)
794{
795 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
796 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
797 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
798 AssertReturn(cbData, VERR_INVALID_PARAMETER);
799 AssertPtrNullReturn(pcbDataRecv, VERR_INVALID_POINTER);
800
801 LogFlowFunc(("pvDate=%p, cbData=%RU32\n", pvData, cbData));
802
803 VBOXDNDHGSENDDATAMSG Msg;
804 RT_ZERO(Msg);
805 int rc;
806 if (pCtx->uProtocol < 3)
807 {
808 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_SND_DATA, 5);
809 Msg.u.v1.uScreenId.SetUInt32(0);
810 Msg.u.v1.pvFormat.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
811 Msg.u.v1.cbFormat.SetUInt32(0);
812 Msg.u.v1.pvData.SetPtr(pvData, cbData);
813 Msg.u.v1.cbData.SetUInt32(0);
814
815 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
816 if ( RT_SUCCESS(rc)
817 || rc == VERR_BUFFER_OVERFLOW)
818 {
819 /** @todo r=bird: The VERR_BUFFER_OVERFLOW case is probably broken as the
820 * status isn't returned to the caller (vbglR3DnDHGRecvDataLoop).
821 * This was the case before fixing the uninitalized variable. As
822 * other V0-2 protocol functions have been marked deprecated, it's
823 * probably a good idea to just remove this code and tell the 1-2 users
824 * to upgrade the host instead. Unused and untested weird code like this
825 * is just hard+costly to maintain and liability.
826 * (VERR_BUFFER_OVERFLOW == weird, no disrespect intended) */
827
828 /* Unmarshal the whole message first. */
829 rc = Msg.u.v1.uScreenId.GetUInt32(&pDataHdr->uScreenId);
830 AssertRC(rc);
831 if (RT_SUCCESS(rc))
832 {
833 uint32_t cbFormatRecv;
834 rc = Msg.u.v1.cbFormat.GetUInt32(&cbFormatRecv);
835 AssertRC(rc);
836 if (RT_SUCCESS(rc))
837 {
838 uint32_t cbDataRecv;
839 rc = Msg.u.v1.cbData.GetUInt32(&cbDataRecv);
840 AssertRC(rc);
841 if (RT_SUCCESS(rc))
842 {
843 /*
844 * In case of VERR_BUFFER_OVERFLOW get the data sizes required
845 * for the format + data blocks.
846 */
847 if ( cbFormatRecv >= pDataHdr->cbMetaFmt
848 || cbDataRecv >= pDataHdr->cbMeta)
849 rc = VERR_TOO_MUCH_DATA;
850 else
851 {
852 pDataHdr->cbMetaFmt = cbFormatRecv;
853 if (pcbDataRecv)
854 *pcbDataRecv = cbDataRecv;
855 LogFlowFuncLeaveRC(rc);
856 return rc;
857 }
858 }
859 }
860 }
861 }
862 }
863 else /* Protocol v3 and up. */
864 {
865 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_SND_DATA, 5);
866 Msg.u.v3.uContext.SetUInt32(0);
867 Msg.u.v3.pvData.SetPtr(pvData, cbData);
868 Msg.u.v3.cbData.SetUInt32(0);
869 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
870 Msg.u.v3.cbChecksum.SetUInt32(0);
871
872 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
873 if (RT_SUCCESS(rc))
874 {
875 uint32_t cbDataRecv;
876 rc = Msg.u.v3.cbData.GetUInt32(&cbDataRecv);
877 AssertRC(rc);
878 if (RT_SUCCESS(rc))
879 {
880 /** @todo Use checksum for validating the received data. */
881 if (pcbDataRecv)
882 *pcbDataRecv = cbDataRecv;
883 LogFlowFuncLeaveRC(rc);
884 return rc;
885 }
886 }
887 }
888
889 /* failure */
890 LogFlowFuncLeaveRC(rc);
891 return rc;
892}
893
894/**
895 * Host -> Guest
896 * Utility function to receive the HOST_DND_HG_SND_DATA_HDR message from the host.
897 *
898 * @returns IPRT status code.
899 * @param pCtx DnD context to use.
900 * @param pDataHdr Where to store the receivd DnD data header.
901 */
902static int vbglR3DnDHGRecvDataHdr(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr)
903{
904 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
905 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
906
907 Assert(pCtx->uProtocol >= 3); /* Only for protocol v3 and up. */
908
909 VBOXDNDHGSENDDATAHDRMSG Msg;
910 RT_ZERO(Msg);
911 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_SND_DATA_HDR, 12);
912 Msg.uContext.SetUInt32(0);
913 Msg.uFlags.SetUInt32(0);
914 Msg.uScreenId.SetUInt32(0);
915 Msg.cbTotal.SetUInt64(0);
916 Msg.cbMeta.SetUInt32(0);
917 Msg.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
918 Msg.cbMetaFmt.SetUInt32(0);
919 Msg.cObjects.SetUInt64(0);
920 Msg.enmCompression.SetUInt32(0);
921 Msg.enmChecksumType.SetUInt32(0);
922 Msg.pvChecksum.SetPtr(pDataHdr->pvChecksum, pDataHdr->cbChecksum);
923 Msg.cbChecksum.SetUInt32(0);
924
925 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
926 if (RT_SUCCESS(rc))
927 {
928 /* Msg.uContext not needed here. */
929 Msg.uFlags.GetUInt32(&pDataHdr->uFlags);
930 Msg.uScreenId.GetUInt32(&pDataHdr->uScreenId);
931 Msg.cbTotal.GetUInt64(&pDataHdr->cbTotal);
932 Msg.cbMeta.GetUInt32(&pDataHdr->cbMeta);
933 Msg.cbMetaFmt.GetUInt32(&pDataHdr->cbMetaFmt);
934 Msg.cObjects.GetUInt64(&pDataHdr->cObjects);
935 Msg.enmCompression.GetUInt32(&pDataHdr->enmCompression);
936 Msg.enmChecksumType.GetUInt32((uint32_t *)&pDataHdr->enmChecksumType);
937 Msg.cbChecksum.GetUInt32(&pDataHdr->cbChecksum);
938 }
939
940 LogFlowFuncLeaveRC(rc);
941 return rc;
942}
943
944/**
945 * Host -> Guest
946 * Helper function for receiving the actual DnD data from the host. Do not call directly.
947 *
948 * @returns IPRT status code.
949 * @param pCtx DnD context to use.
950 * @param pvData Where to store the receivd DnD data.
951 * @param cbData Size (in bytes) of where to store the received DnD data.
952 * @param pcbDataRecv Where to store how much bytes of data actually was received.
953 *
954 * @remark Deprecated function and not being used since protocl 3 anymore; will be removed.
955 */
956static int vbglR3DnDHGRecvMoreData(PVBGLR3GUESTDNDCMDCTX pCtx, void *pvData, uint32_t cbData, uint32_t *pcbDataRecv)
957{
958 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
959 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
960 AssertReturn(cbData, VERR_INVALID_PARAMETER);
961 AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER);
962
963 VBOXDNDHGSENDMOREDATAMSG Msg;
964 RT_ZERO(Msg);
965 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_HG_SND_MORE_DATA, 2);
966 Msg.pvData.SetPtr(pvData, cbData);
967 Msg.cbData.SetUInt32(0);
968
969 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
970 if ( RT_SUCCESS(rc)
971 || rc == VERR_BUFFER_OVERFLOW)
972 {
973 rc = Msg.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
974 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
975 }
976 return rc;
977}
978
979/**
980 * Host -> Guest
981 * Helper function for receiving the actual DnD data from the host. Do not call directly.
982 *
983 * @returns IPRT status code.
984 * @param pCtx DnD context to use.
985 * @param pDataHdr Where to store the data header data.
986 * @param ppvData Returns the received meta data. Needs to be free'd by the caller.
987 * @param pcbData Where to store the size (in bytes) of the received meta data.
988 */
989static int vbglR3DnDHGRecvDataLoop(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr,
990 void **ppvData, uint64_t *pcbData)
991{
992 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
993 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
994 AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
995 AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
996
997 int rc;
998 uint32_t cbDataRecv;
999
1000 LogFlowFuncEnter();
1001
1002 if (pCtx->uProtocol < 3)
1003 {
1004 uint64_t cbDataTmp = pCtx->cbMaxChunkSize;
1005 void *pvDataTmp = RTMemAlloc(cbDataTmp);
1006
1007 if (!cbDataTmp)
1008 return VERR_NO_MEMORY;
1009
1010 /*
1011 * Protocols < v3 contain the header information in every HOST_DND_HG_SND_DATA
1012 * message, so do the actual retrieving immediately.
1013 *
1014 * Also, the initial implementation used VERR_BUFFER_OVERFLOW as a return code to
1015 * indicate that there will be more data coming in after the initial data chunk. There
1016 * was no way of telling the total data size upfront (in form of a header or some such),
1017 * so also handle this case to not break backwards compatibility.
1018 */
1019 rc = vbglR3DnDHGRecvDataRaw(pCtx, pDataHdr, pvDataTmp, pCtx->cbMaxChunkSize, &cbDataRecv);
1020
1021 /* See comment above. */
1022 while (rc == VERR_BUFFER_OVERFLOW)
1023 {
1024 uint32_t uNextMsg;
1025 uint32_t cNextParms;
1026 rc = vbglR3DnDGetNextMsgType(pCtx, &uNextMsg, &cNextParms, false /* fBlock */);
1027 if (RT_SUCCESS(rc))
1028 {
1029 switch(uNextMsg)
1030 {
1031 case HOST_DND_HG_SND_MORE_DATA:
1032 {
1033 /** @todo r=andy Don't use reallocate here; can go wrong with *really* big URI lists.
1034 * Instead send as many URI entries as possible per chunk and add those entries
1035 * to our to-process list for immediata processing. Repeat the step after processing then. */
1036 LogFlowFunc(("HOST_DND_HG_SND_MORE_DATA cbDataTotal: %RU64 -> %RU64\n",
1037 cbDataTmp, cbDataTmp + pCtx->cbMaxChunkSize));
1038 void *pvDataNew = RTMemRealloc(pvDataTmp, cbDataTmp + pCtx->cbMaxChunkSize);
1039 if (!pvDataNew)
1040 {
1041 rc = VERR_NO_MEMORY;
1042 break;
1043 }
1044
1045 pvDataTmp = pvDataNew;
1046
1047 uint8_t *pvDataOff = (uint8_t *)pvDataTmp + cbDataTmp;
1048 rc = vbglR3DnDHGRecvMoreData(pCtx, pvDataOff, pCtx->cbMaxChunkSize, &cbDataRecv);
1049 if ( RT_SUCCESS(rc)
1050 || rc == VERR_BUFFER_OVERFLOW) /* Still can return VERR_BUFFER_OVERFLOW. */
1051 {
1052 cbDataTmp += cbDataRecv;
1053 }
1054 break;
1055 }
1056 case HOST_DND_HG_EVT_CANCEL:
1057 default:
1058 {
1059 rc = vbglR3DnDHGRecvCancel(pCtx);
1060 if (RT_SUCCESS(rc))
1061 rc = VERR_CANCELLED;
1062 break;
1063 }
1064 }
1065 }
1066 }
1067
1068 if (RT_SUCCESS(rc))
1069 {
1070 /* There was no way of telling the total data size upfront
1071 * (in form of a header or some such), so set the total data size here. */
1072 pDataHdr->cbTotal = cbDataTmp;
1073
1074 *ppvData = pvDataTmp;
1075 *pcbData = cbDataTmp;
1076 }
1077 else
1078 RTMemFree(pvDataTmp);
1079 }
1080 else /* Protocol v3 and up. */
1081 {
1082 rc = vbglR3DnDHGRecvDataHdr(pCtx, pDataHdr);
1083 if (RT_SUCCESS(rc))
1084 {
1085 LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32\n", pDataHdr->cbTotal, pDataHdr->cbMeta));
1086 if (pDataHdr->cbMeta)
1087 {
1088 uint64_t cbDataTmp = 0;
1089 void *pvDataTmp = RTMemAlloc(pDataHdr->cbMeta);
1090 if (!pvDataTmp)
1091 rc = VERR_NO_MEMORY;
1092
1093 if (RT_SUCCESS(rc))
1094 {
1095 uint8_t *pvDataOff = (uint8_t *)pvDataTmp;
1096 while (cbDataTmp < pDataHdr->cbMeta)
1097 {
1098 rc = vbglR3DnDHGRecvDataRaw(pCtx, pDataHdr,
1099 pvDataOff, RT_MIN(pDataHdr->cbMeta - cbDataTmp, pCtx->cbMaxChunkSize),
1100 &cbDataRecv);
1101 if (RT_SUCCESS(rc))
1102 {
1103 LogFlowFunc(("cbDataRecv=%RU32, cbDataTmp=%RU64\n", cbDataRecv, cbDataTmp));
1104 Assert(cbDataTmp + cbDataRecv <= pDataHdr->cbMeta);
1105 cbDataTmp += cbDataRecv;
1106 pvDataOff += cbDataRecv;
1107 }
1108 else
1109 break;
1110 }
1111
1112 if (RT_SUCCESS(rc))
1113 {
1114 Assert(cbDataTmp == pDataHdr->cbMeta);
1115
1116 LogFlowFunc(("Received %RU64 bytes of data\n", cbDataTmp));
1117
1118 *ppvData = pvDataTmp;
1119 *pcbData = cbDataTmp;
1120 }
1121 else
1122 RTMemFree(pvDataTmp);
1123 }
1124 }
1125 else
1126 {
1127 *ppvData = NULL;
1128 *pcbData = 0;
1129 }
1130 }
1131 }
1132
1133 LogFlowFuncLeaveRC(rc);
1134 return rc;
1135}
1136
1137/**
1138 * Host -> Guest
1139 * Main function for receiving the actual DnD data from the host, extended version.
1140 *
1141 * @returns IPRT status code.
1142 * @param pCtx DnD context to use.
1143 * @param pEnmType Where to store the meta data type.
1144 * @param ppvData Returns the received meta data. Needs to be free'd by the caller.
1145 * @param pcbData Where to store the size (in bytes) of the received meta data.
1146 */
1147static int vbglR3DnDHGRecvDataMain(PVBGLR3GUESTDNDCMDCTX pCtx,
1148 uint32_t *puScreenId,
1149 char **ppszFormat,
1150 uint32_t *pcbFormat,
1151 void **ppvData,
1152 uint32_t *pcbData)
1153{
1154 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1155 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
1156 AssertPtrReturn(ppszFormat, VERR_INVALID_POINTER);
1157 AssertPtrReturn(pcbFormat, VERR_INVALID_POINTER);
1158 AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
1159 AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
1160
1161 VBOXDNDDATAHDR dataHdr; /** @todo See todo above. */
1162 RT_ZERO(dataHdr);
1163
1164 dataHdr.cbMetaFmt = _64K; /** @todo Make this configurable? */
1165 dataHdr.pvMetaFmt = RTMemAlloc(dataHdr.cbMetaFmt);
1166 if (!dataHdr.pvMetaFmt)
1167 return VERR_NO_MEMORY;
1168
1169 DnDURIList lstURI;
1170 DnDDroppedFiles droppedFiles;
1171
1172 void *pvData; /** @todo See todo above. */
1173 uint64_t cbData; /** @todo See todo above. */
1174
1175 int rc = vbglR3DnDHGRecvDataLoop(pCtx, &dataHdr, &pvData, &cbData);
1176 if (RT_SUCCESS(rc))
1177 {
1178 /**
1179 * Check if this is an URI event. If so, let VbglR3 do all the actual
1180 * data transfer + file/directory creation internally without letting
1181 * the caller know.
1182 *
1183 * This keeps the actual (guest OS-)dependent client (like VBoxClient /
1184 * VBoxTray) small by not having too much redundant code.
1185 */
1186 Assert(dataHdr.cbMetaFmt);
1187 AssertPtr(dataHdr.pvMetaFmt);
1188 if (DnDMIMEHasFileURLs((char *)dataHdr.pvMetaFmt, dataHdr.cbMetaFmt))
1189 {
1190 AssertPtr(pvData);
1191 Assert(cbData);
1192 rc = lstURI.RootFromURIData(pvData, cbData, 0 /* fFlags */);
1193 if (RT_SUCCESS(rc))
1194 rc = vbglR3DnDHGRecvURIData(pCtx, &dataHdr, &droppedFiles);
1195
1196 if (RT_SUCCESS(rc)) /** @todo Remove this block as soon as we hand in DnDURIList. */
1197 {
1198 if (pvData)
1199 {
1200 /* Reuse data buffer to fill in the transformed URI file list. */
1201 RTMemFree(pvData);
1202 pvData = NULL;
1203 }
1204
1205 RTCString strData = lstURI.RootToString(droppedFiles.GetDirAbs());
1206 Assert(!strData.isEmpty());
1207
1208 cbData = strData.length() + 1;
1209 LogFlowFunc(("URI list has %zu bytes\n", cbData));
1210
1211 pvData = RTMemAlloc(cbData);
1212 if (pvData)
1213 {
1214 memcpy(pvData, strData.c_str(), cbData);
1215 }
1216 else
1217 rc = VERR_NO_MEMORY;
1218 }
1219 }
1220 else /* Raw data. */
1221 {
1222 const uint32_t cbDataRaw = dataHdr.cbMetaFmt;
1223 if (cbData >= cbDataRaw)
1224 {
1225 if (cbDataRaw)
1226 memcpy(pvData, dataHdr.pvMetaFmt, cbDataRaw);
1227 cbData = cbDataRaw;
1228 }
1229 else
1230 rc = VERR_BUFFER_OVERFLOW;
1231 }
1232 }
1233
1234 if ( RT_FAILURE(rc)
1235 && rc != VERR_CANCELLED)
1236 {
1237 if (dataHdr.pvMetaFmt)
1238 RTMemFree(dataHdr.pvMetaFmt);
1239 if (pvData)
1240 RTMemFree(pvData);
1241
1242 int rc2 = VbglR3DnDHGSendProgress(pCtx, DND_PROGRESS_ERROR, 100 /* Percent */, rc);
1243 if (RT_FAILURE(rc2))
1244 LogFlowFunc(("Unable to send progress error %Rrc to host: %Rrc\n", rc, rc2));
1245 }
1246 else if (RT_SUCCESS(rc))
1247 {
1248 *ppszFormat = (char *)dataHdr.pvMetaFmt;
1249 *pcbFormat = dataHdr.cbMetaFmt;
1250 *ppvData = pvData;
1251 *pcbData = cbData;
1252 }
1253
1254 LogFlowFuncLeaveRC(rc);
1255 return rc;
1256}
1257
1258#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1259/**
1260 * Guest -> Host
1261 * Utility function to receive the HOST_DND_GH_REQ_PENDING message from the host.
1262 *
1263 * @returns IPRT status code.
1264 * @param pCtx DnD context to use.
1265 * @param puScreenId For which screen on the host the request is for.
1266 */
1267static int vbglR3DnDGHRecvPending(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puScreenId)
1268{
1269 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1270 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
1271
1272 VBOXDNDGHREQPENDINGMSG Msg;
1273 RT_ZERO(Msg);
1274 if (pCtx->uProtocol < 3)
1275 {
1276 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_GH_REQ_PENDING, 1);
1277 Msg.u.v1.uScreenId.SetUInt32(0);
1278 }
1279 else
1280 {
1281 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_GH_REQ_PENDING, 2);
1282 /** @todo Context ID not used yet. */
1283 Msg.u.v3.uContext.SetUInt32(0);
1284 Msg.u.v3.uScreenId.SetUInt32(0);
1285 }
1286
1287 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1288 if (RT_SUCCESS(rc))
1289 {
1290 if (pCtx->uProtocol < 3)
1291 {
1292 rc = Msg.u.v1.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
1293 }
1294 else
1295 {
1296 /** @todo Context ID not used yet. */
1297 rc = Msg.u.v3.uContext.GetUInt32(puScreenId); AssertRC(rc);
1298 }
1299 }
1300
1301 return rc;
1302}
1303
1304/**
1305 * Guest -> Host
1306 * Utility function to receive the HOST_DND_GH_EVT_DROPPED message from the host.
1307 *
1308 * @returns IPRT status code.
1309 * @param pCtx DnD context to use.
1310 * @param pszFormat Requested data format from the host.
1311 * @param cbFormat Size of requested data format (in bytes).
1312 * @param pcbFormatRecv Actual size of requested data format (in bytes).
1313 * @param puAction Requested action from the host.
1314 */
1315static int vbglR3DnDGHRecvDropped(PVBGLR3GUESTDNDCMDCTX pCtx,
1316 char *pszFormat,
1317 uint32_t cbFormat,
1318 uint32_t *pcbFormatRecv,
1319 uint32_t *puAction)
1320{
1321 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1322 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
1323 AssertReturn(cbFormat, VERR_INVALID_PARAMETER);
1324 AssertPtrReturn(pcbFormatRecv, VERR_INVALID_POINTER);
1325 AssertPtrReturn(puAction, VERR_INVALID_POINTER);
1326
1327 VBOXDNDGHDROPPEDMSG Msg;
1328 RT_ZERO(Msg);
1329 if (pCtx->uProtocol < 3)
1330 {
1331 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_GH_EVT_DROPPED, 3);
1332 Msg.u.v1.pvFormat.SetPtr(pszFormat, cbFormat);
1333 Msg.u.v1.cbFormat.SetUInt32(0);
1334 Msg.u.v1.uAction.SetUInt32(0);
1335 }
1336 else
1337 {
1338 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, HOST_DND_GH_EVT_DROPPED, 4);
1339 Msg.u.v3.uContext.SetUInt32(0);
1340 Msg.u.v3.pvFormat.SetPtr(pszFormat, cbFormat);
1341 Msg.u.v3.cbFormat.SetUInt32(0);
1342 Msg.u.v3.uAction.SetUInt32(0);
1343 }
1344
1345 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1346 if (RT_SUCCESS(rc))
1347 {
1348 if (pCtx->uProtocol < 3)
1349 {
1350 rc = Msg.u.v1.cbFormat.GetUInt32(pcbFormatRecv); AssertRC(rc);
1351 rc = Msg.u.v1.uAction.GetUInt32(puAction); AssertRC(rc);
1352 }
1353 else
1354 {
1355 /** @todo Context ID not used yet. */
1356 rc = Msg.u.v3.cbFormat.GetUInt32(pcbFormatRecv); AssertRC(rc);
1357 rc = Msg.u.v3.uAction.GetUInt32(puAction); AssertRC(rc);
1358 }
1359
1360 AssertReturn(cbFormat >= *pcbFormatRecv, VERR_TOO_MUCH_DATA);
1361 }
1362
1363 return rc;
1364}
1365#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1366
1367
1368/*********************************************************************************************************************************
1369* Public functions *
1370*********************************************************************************************************************************/
1371
1372/**
1373 * Connects a DnD context to the DnD host service.
1374 *
1375 * @returns IPRT status code.
1376 * @param pCtx DnD context to connect.
1377 */
1378VBGLR3DECL(int) VbglR3DnDConnect(PVBGLR3GUESTDNDCMDCTX pCtx)
1379{
1380 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1381
1382 /* Initialize header */
1383 int rc = VbglR3HGCMConnect("VBoxDragAndDropSvc", &pCtx->uClientID);
1384 if (RT_FAILURE(rc))
1385 return rc;
1386
1387 /* Set the default protocol version to use. */
1388 pCtx->uProtocol = 3;
1389 Assert(pCtx->uClientID);
1390
1391 /*
1392 * Get the VM's session ID.
1393 * This is not fatal in case we're running with an ancient VBox version.
1394 */
1395 pCtx->uSessionID = 0;
1396 int rc2 = VbglR3GetSessionId(&pCtx->uSessionID);
1397 LogFlowFunc(("uSessionID=%RU64, rc=%Rrc\n", pCtx->uSessionID, rc2));
1398
1399 /*
1400 * Check if the host is >= VBox 5.0 which in case supports GUEST_DND_CONNECT.
1401 */
1402 bool fSupportsConnectReq = false;
1403 if (RT_SUCCESS(rc))
1404 {
1405 /* The guest property service might not be available. Not fatal. */
1406 uint32_t uGuestPropSvcClientID;
1407 rc2 = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
1408 if (RT_SUCCESS(rc2))
1409 {
1410 char *pszHostVersion;
1411 rc2 = VbglR3GuestPropReadValueAlloc(uGuestPropSvcClientID, "/VirtualBox/HostInfo/VBoxVer", &pszHostVersion);
1412 if (RT_SUCCESS(rc2))
1413 {
1414 fSupportsConnectReq = RTStrVersionCompare(pszHostVersion, "5.0") >= 0;
1415 LogFlowFunc(("pszHostVersion=%s, fSupportsConnectReq=%RTbool\n", pszHostVersion, fSupportsConnectReq));
1416 VbglR3GuestPropReadValueFree(pszHostVersion);
1417 }
1418
1419 VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
1420 }
1421
1422 if (RT_FAILURE(rc2))
1423 LogFlowFunc(("Retrieving host version failed with rc=%Rrc\n", rc2));
1424 }
1425
1426 if (fSupportsConnectReq)
1427 {
1428 /*
1429 * Try sending the connect message to tell the protocol version to use.
1430 * Note: This might fail when the Guest Additions run on an older VBox host (< VBox 5.0) which
1431 * does not implement this command.
1432 */
1433 VBOXDNDCONNECTMSG Msg;
1434 RT_ZERO(Msg);
1435 if (pCtx->uProtocol < 3)
1436 {
1437 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_CONNECT, 2);
1438 Msg.u.v2.uProtocol.SetUInt32(pCtx->uProtocol);
1439 Msg.u.v2.uFlags.SetUInt32(0); /* Unused at the moment. */
1440 }
1441 else
1442 {
1443 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_CONNECT, 3);
1444 /** @todo Context ID not used yet. */
1445 Msg.u.v3.uContext.SetUInt32(0);
1446 Msg.u.v3.uProtocol.SetUInt32(pCtx->uProtocol);
1447 Msg.u.v3.uFlags.SetUInt32(0); /* Unused at the moment. */
1448 }
1449
1450 rc2 = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1451 if (RT_FAILURE(rc2))
1452 fSupportsConnectReq = false;
1453
1454 LogFlowFunc(("Connection request ended with rc=%Rrc\n", rc2));
1455 }
1456
1457 /* GUEST_DND_CONNECT not supported; play safe here and just use protocol v1. */
1458 if (!fSupportsConnectReq)
1459 pCtx->uProtocol = 1; /* Fall back to protocol version 1 (< VBox 5.0). */
1460
1461 pCtx->cbMaxChunkSize = _64K; /** @todo Use a scratch buffer on the heap? */
1462
1463 LogFlowFunc(("uClient=%RU32, uProtocol=%RU32, rc=%Rrc\n", pCtx->uClientID, pCtx->uProtocol, rc));
1464 return rc;
1465}
1466
1467/**
1468 * Disconnects a given DnD context from the DnD host service.
1469 *
1470 * @returns IPRT status code.
1471 * @param pCtx DnD context to use.
1472 * The context is invalid afterwards on successful disconnection.
1473 */
1474VBGLR3DECL(int) VbglR3DnDDisconnect(PVBGLR3GUESTDNDCMDCTX pCtx)
1475{
1476 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1477 int rc = VbglR3HGCMDisconnect(pCtx->uClientID);
1478 if (RT_SUCCESS(rc))
1479 pCtx->uClientID = 0;
1480 return rc;
1481}
1482
1483/**
1484 * Receives the next upcoming event for a given DnD context.
1485 *
1486 * @returns IPRT status code.
1487 * @param pCtx DnD context to use.
1488 * @param pEvent Where to return the received DnD event on success.
1489 */
1490VBGLR3DECL(int) VbglR3DnDRecvNextMsg(PVBGLR3GUESTDNDCMDCTX pCtx, CPVBGLR3DNDHGCMEVENT pEvent)
1491{
1492 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1493 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1494
1495 const uint32_t cbFormatMax = pCtx->cbMaxChunkSize;
1496
1497 uint32_t uMsg = 0;
1498 uint32_t cParms = 0;
1499 int rc = vbglR3DnDGetNextMsgType(pCtx, &uMsg, &cParms, true /* fWait */);
1500 if (RT_SUCCESS(rc))
1501 {
1502 /* Check for VM session change. */
1503 uint64_t uSessionID;
1504 int rc2 = VbglR3GetSessionId(&uSessionID);
1505 if ( RT_SUCCESS(rc2)
1506 && (uSessionID != pCtx->uSessionID))
1507 {
1508 LogFlowFunc(("VM session ID changed to %RU64, doing reconnect\n", uSessionID));
1509
1510 /* Try a reconnect to the DnD service. */
1511 rc2 = VbglR3DnDDisconnect(pCtx);
1512 AssertRC(rc2);
1513 rc2 = VbglR3DnDConnect(pCtx);
1514 AssertRC(rc2);
1515
1516 /* At this point we continue processing the messsages with the new client ID. */
1517 }
1518 }
1519
1520 if (RT_SUCCESS(rc))
1521 {
1522 pEvent->uType = uMsg;
1523
1524 switch(uMsg)
1525 {
1526 case HOST_DND_HG_EVT_ENTER:
1527 case HOST_DND_HG_EVT_MOVE:
1528 case HOST_DND_HG_EVT_DROPPED:
1529 {
1530 pEvent->pszFormats = static_cast<char*>(RTMemAlloc(cbFormatMax));
1531 if (!pEvent->pszFormats)
1532 rc = VERR_NO_MEMORY;
1533
1534 if (RT_SUCCESS(rc))
1535 rc = vbglR3DnDHGRecvAction(pCtx,
1536 uMsg,
1537 &pEvent->uScreenId,
1538 &pEvent->u.a.uXpos,
1539 &pEvent->u.a.uYpos,
1540 &pEvent->u.a.uDefAction,
1541 &pEvent->u.a.uAllActions,
1542 pEvent->pszFormats,
1543 cbFormatMax,
1544 &pEvent->cbFormats);
1545 break;
1546 }
1547 case HOST_DND_HG_EVT_LEAVE:
1548 {
1549 rc = vbglR3DnDHGRecvLeave(pCtx);
1550 break;
1551 }
1552 case HOST_DND_HG_SND_DATA:
1553 /* Protocol v1 + v2: Also contains the header data.
1554 * Note: Fall through is intentional. */
1555 case HOST_DND_HG_SND_DATA_HDR:
1556 {
1557 rc = vbglR3DnDHGRecvDataMain(pCtx,
1558 /* Screen ID */
1559 &pEvent->uScreenId,
1560 /* Format */
1561 &pEvent->pszFormats,
1562 &pEvent->cbFormats,
1563 /* Data */
1564 &pEvent->u.b.pvData,
1565 &pEvent->u.b.cbData);
1566 break;
1567 }
1568 case HOST_DND_HG_SND_MORE_DATA:
1569 case HOST_DND_HG_SND_DIR:
1570 case HOST_DND_HG_SND_FILE_DATA:
1571 {
1572 /*
1573 * All messages in this case are handled internally
1574 * by vbglR3DnDHGRecvDataMain() and must be specified
1575 * by a preceding HOST_DND_HG_SND_DATA or HOST_DND_HG_SND_DATA_HDR
1576 * calls.
1577 */
1578 rc = VERR_WRONG_ORDER;
1579 break;
1580 }
1581 case HOST_DND_HG_EVT_CANCEL:
1582 {
1583 rc = vbglR3DnDHGRecvCancel(pCtx);
1584 break;
1585 }
1586#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1587 case HOST_DND_GH_REQ_PENDING:
1588 {
1589 rc = vbglR3DnDGHRecvPending(pCtx, &pEvent->uScreenId);
1590 break;
1591 }
1592 case HOST_DND_GH_EVT_DROPPED:
1593 {
1594 pEvent->pszFormats = static_cast<char*>(RTMemAlloc(cbFormatMax));
1595 if (!pEvent->pszFormats)
1596 rc = VERR_NO_MEMORY;
1597
1598 if (RT_SUCCESS(rc))
1599 rc = vbglR3DnDGHRecvDropped(pCtx,
1600 pEvent->pszFormats,
1601 cbFormatMax,
1602 &pEvent->cbFormats,
1603 &pEvent->u.a.uDefAction);
1604 break;
1605 }
1606#endif
1607 default:
1608 {
1609 rc = VERR_NOT_SUPPORTED;
1610 break;
1611 }
1612 }
1613 }
1614
1615 if (RT_FAILURE(rc))
1616 LogFlowFunc(("Returning error %Rrc\n", rc));
1617 return rc;
1618}
1619
1620/**
1621 * Host -> Guest
1622 * Acknowledges that a pending DnD operation from the host can be dropped
1623 * on the currently selected area on the guest.
1624 *
1625 * @returns IPRT status code.
1626 * @param pCtx DnD context to use.
1627 * @param uAction Action to acknowledge.
1628 */
1629VBGLR3DECL(int) VbglR3DnDHGSendAckOp(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uAction)
1630{
1631 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1632
1633 VBOXDNDHGACKOPMSG Msg;
1634 RT_ZERO(Msg);
1635 LogFlowFunc(("uProto=%RU32\n", pCtx->uProtocol));
1636 if (pCtx->uProtocol < 3)
1637 {
1638 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_HG_ACK_OP, 1);
1639 Msg.u.v1.uAction.SetUInt32(uAction);
1640 }
1641 else
1642 {
1643 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_HG_ACK_OP, 2);
1644 /** @todo Context ID not used yet. */
1645 Msg.u.v3.uContext.SetUInt32(0);
1646 Msg.u.v3.uAction.SetUInt32(uAction);
1647 }
1648
1649 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1650}
1651
1652/**
1653 * Host -> Guest
1654 * Requests the actual DnD data to be sent from the host.
1655 *
1656 * @returns IPRT status code.
1657 * @param pCtx DnD context to use.
1658 * @param pcszFormat Format to request the data from the host in.
1659 */
1660VBGLR3DECL(int) VbglR3DnDHGSendReqData(PVBGLR3GUESTDNDCMDCTX pCtx, const char* pcszFormat)
1661{
1662 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1663 AssertPtrReturn(pcszFormat, VERR_INVALID_POINTER);
1664 if (!RTStrIsValidEncoding(pcszFormat))
1665 return VERR_INVALID_PARAMETER;
1666
1667 const uint32_t cbFormat = (uint32_t)strlen(pcszFormat) + 1; /* Include termination */
1668
1669 VBOXDNDHGREQDATAMSG Msg;
1670 RT_ZERO(Msg);
1671 if (pCtx->uProtocol < 3)
1672 {
1673 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_HG_REQ_DATA, 1);
1674 Msg.u.v1.pvFormat.SetPtr((void*)pcszFormat, cbFormat);
1675 }
1676 else
1677 {
1678 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_HG_REQ_DATA, 3);
1679 /** @todo Context ID not used yet. */
1680 Msg.u.v3.uContext.SetUInt32(0);
1681 Msg.u.v3.pvFormat.SetPtr((void*)pcszFormat, cbFormat);
1682 Msg.u.v3.cbFormat.SetUInt32(cbFormat);
1683 }
1684
1685 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1686}
1687
1688/**
1689 * Host -> Guest
1690 * Reports back its progress back to the host.
1691 *
1692 * @returns IPRT status code.
1693 * @param pCtx DnD context to use.
1694 * @param uStatus DnD status to report.
1695 * @param uPercent Overall progress (in percent) to report.
1696 * @param rcErr Error code (IPRT-style) to report.
1697 */
1698VBGLR3DECL(int) VbglR3DnDHGSendProgress(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uStatus, uint8_t uPercent, int rcErr)
1699{
1700 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1701 AssertReturn(uStatus > DND_PROGRESS_UNKNOWN, VERR_INVALID_PARAMETER);
1702
1703 VBOXDNDHGEVTPROGRESSMSG Msg;
1704 RT_ZERO(Msg);
1705 if (pCtx->uProtocol < 3)
1706 {
1707 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_HG_EVT_PROGRESS, 3);
1708 Msg.u.v1.uStatus.SetUInt32(uStatus);
1709 Msg.u.v1.uPercent.SetUInt32(uPercent);
1710 Msg.u.v1.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1711 }
1712 else
1713 {
1714 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_HG_EVT_PROGRESS, 4);
1715 /** @todo Context ID not used yet. */
1716 Msg.u.v3.uContext.SetUInt32(0);
1717 Msg.u.v3.uStatus.SetUInt32(uStatus);
1718 Msg.u.v3.uPercent.SetUInt32(uPercent);
1719 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1720 }
1721
1722 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1723}
1724
1725#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1726/**
1727 * Guest -> Host
1728 * Acknowledges that there currently is a drag'n drop operation in progress on the guest,
1729 * which eventually could be dragged over to the host.
1730 *
1731 * @returns IPRT status code.
1732 * @param pCtx DnD context to use.
1733 * @param uDefAction Default action for the operation to report.
1734 * @param uAllActions All available actions for the operation to report.
1735 * @param pcszFormats Available formats for the operation to report.
1736 * @param cbFormats Size (in bytes) of formats to report.
1737 */
1738VBGLR3DECL(int) VbglR3DnDGHSendAckPending(PVBGLR3GUESTDNDCMDCTX pCtx,
1739 uint32_t uDefAction, uint32_t uAllActions, const char* pcszFormats, uint32_t cbFormats)
1740{
1741 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1742 AssertPtrReturn(pcszFormats, VERR_INVALID_POINTER);
1743 AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
1744
1745 if (!RTStrIsValidEncoding(pcszFormats))
1746 return VERR_INVALID_UTF8_ENCODING;
1747
1748 VBOXDNDGHACKPENDINGMSG Msg;
1749 RT_ZERO(Msg);
1750 if (pCtx->uProtocol < 3)
1751 {
1752 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_ACK_PENDING, 3);
1753 Msg.u.v1.uDefAction.SetUInt32(uDefAction);
1754 Msg.u.v1.uAllActions.SetUInt32(uAllActions);
1755 Msg.u.v1.pvFormats.SetPtr((void*)pcszFormats, cbFormats);
1756 }
1757 else
1758 {
1759 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_ACK_PENDING, 5);
1760 /** @todo Context ID not used yet. */
1761 Msg.u.v3.uContext.SetUInt32(0);
1762 Msg.u.v3.uDefAction.SetUInt32(uDefAction);
1763 Msg.u.v3.uAllActions.SetUInt32(uAllActions);
1764 Msg.u.v3.pvFormats.SetPtr((void*)pcszFormats, cbFormats);
1765 Msg.u.v3.cbFormats.SetUInt32(cbFormats);
1766 }
1767
1768 return VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1769}
1770
1771/**
1772 * Guest -> Host
1773 * Utility function to send DnD data from guest to the host.
1774 *
1775 * @returns IPRT status code.
1776 * @param pCtx DnD context to use.
1777 * @param pvData Data block to send.
1778 * @param cbData Size (in bytes) of data block to send.
1779 * @param pDataHdr Data header to use -- needed for accounting.
1780 */
1781static int vbglR3DnDGHSendDataInternal(PVBGLR3GUESTDNDCMDCTX pCtx,
1782 void *pvData, uint64_t cbData, PVBOXDNDSNDDATAHDR pDataHdr)
1783{
1784 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1785 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1786 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1787 /* cbAdditionalData is optional. */
1788 /* pDataHdr is optional in protocols < v3. */
1789
1790 int rc = VINF_SUCCESS;
1791
1792 /* For protocol v3 and up we need to send the data header first. */
1793 if (pCtx->uProtocol >= 3)
1794 {
1795 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
1796
1797 VBOXDNDGHSENDDATAHDRMSG Msg;
1798 RT_ZERO(Msg);
1799 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DATA_HDR, 12);
1800 Msg.uContext.SetUInt32(0); /** @todo Not used yet. */
1801 Msg.uFlags.SetUInt32(0); /** @todo Not used yet. */
1802 Msg.uScreenId.SetUInt32(0); /** @todo Not used for guest->host (yet). */
1803 Msg.cbTotal.SetUInt64(pDataHdr->cbTotal);
1804 Msg.cbMeta.SetUInt32(pDataHdr->cbMeta);
1805 Msg.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
1806 Msg.cbMetaFmt.SetUInt32(pDataHdr->cbMetaFmt);
1807 Msg.cObjects.SetUInt64(pDataHdr->cObjects);
1808 Msg.enmCompression.SetUInt32(0); /** @todo Not used yet. */
1809 Msg.enmChecksumType.SetUInt32(RTDIGESTTYPE_INVALID); /** @todo Not used yet. */
1810 Msg.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1811 Msg.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1812
1813 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1814
1815 LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU64, rc=%Rrc\n",
1816 pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects, rc));
1817 }
1818
1819 if (RT_SUCCESS(rc))
1820 {
1821 VBOXDNDGHSENDDATAMSG Msg;
1822 RT_ZERO(Msg);
1823 if (pCtx->uProtocol >= 3)
1824 {
1825 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DATA, 5);
1826 Msg.u.v3.uContext.SetUInt32(0); /** @todo Not used yet. */
1827 Msg.u.v3.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1828 Msg.u.v3.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1829 }
1830 else
1831 {
1832 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DATA, 2);
1833
1834 /* Total amount of bytes to send (meta data + all directory/file objects). */
1835 /* Note: Only supports uint32_t, so this is *not* a typo. */
1836 Msg.u.v1.cbTotalBytes.SetUInt32((uint32_t)pDataHdr->cbTotal);
1837 }
1838
1839 uint32_t cbCurChunk;
1840 const uint32_t cbMaxChunk = pCtx->cbMaxChunkSize;
1841 uint32_t cbSent = 0;
1842
1843 HGCMFunctionParameter *pParm = (pCtx->uProtocol >= 3)
1844 ? &Msg.u.v3.pvData
1845 : &Msg.u.v1.pvData;
1846 while (cbSent < cbData)
1847 {
1848 cbCurChunk = RT_MIN(cbData - cbSent, cbMaxChunk);
1849 pParm->SetPtr(static_cast<uint8_t *>(pvData) + cbSent, cbCurChunk);
1850 if (pCtx->uProtocol > 2)
1851 Msg.u.v3.cbData.SetUInt32(cbCurChunk);
1852
1853 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1854 if (RT_FAILURE(rc))
1855 break;
1856
1857 cbSent += cbCurChunk;
1858 }
1859
1860 LogFlowFunc(("cbMaxChunk=%RU32, cbData=%RU64, cbSent=%RU32, rc=%Rrc\n",
1861 cbMaxChunk, cbData, cbSent, rc));
1862
1863 if (RT_SUCCESS(rc))
1864 Assert(cbSent == cbData);
1865 }
1866
1867 LogFlowFuncLeaveRC(rc);
1868 return rc;
1869}
1870
1871/**
1872 * Guest -> Host
1873 * Utility function to send a guest directory to the host.
1874 *
1875 * @returns IPRT status code.
1876 * @param pCtx DnD context to use.
1877 * @param pObj URI object containing the directory to send.
1878 */
1879static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
1880{
1881 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1882 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1883 AssertReturn(pObj->GetType() == DnDURIObject::Directory, VERR_INVALID_PARAMETER);
1884
1885 RTCString strPath = pObj->GetDestPath();
1886 LogFlowFunc(("strDir=%s (%zu), fMode=0x%x\n",
1887 strPath.c_str(), strPath.length(), pObj->GetMode()));
1888
1889 if (strPath.length() > RTPATH_MAX)
1890 return VERR_INVALID_PARAMETER;
1891
1892 const uint32_t cbPath = (uint32_t)strPath.length() + 1; /* Include termination. */
1893
1894 VBOXDNDGHSENDDIRMSG Msg;
1895 RT_ZERO(Msg);
1896 if (pCtx->uProtocol < 3)
1897 {
1898 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DIR, 3);
1899 Msg.u.v1.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)cbPath);
1900 Msg.u.v1.cbName.SetUInt32((uint32_t)cbPath);
1901 Msg.u.v1.fMode.SetUInt32(pObj->GetMode());
1902 }
1903 else
1904 {
1905 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_DIR, 4);
1906 /** @todo Context ID not used yet. */
1907 Msg.u.v3.uContext.SetUInt32(0);
1908 Msg.u.v3.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)cbPath);
1909 Msg.u.v3.cbName.SetUInt32((uint32_t)cbPath);
1910 Msg.u.v3.fMode.SetUInt32(pObj->GetMode());
1911 }
1912
1913 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
1914
1915 LogFlowFuncLeaveRC(rc);
1916 return rc;
1917}
1918
1919/**
1920 * Guest -> Host
1921 * Utility function to send a file from the guest to the host.
1922 *
1923 * @returns IPRT status code.
1924 * @param pCtx DnD context to use.
1925 * @param pObj URI object containing the file to send.
1926 */
1927static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
1928{
1929 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1930 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1931 AssertReturn(pObj->GetType() == DnDURIObject::File, VERR_INVALID_PARAMETER);
1932 AssertReturn(pObj->IsOpen(), VERR_INVALID_STATE);
1933
1934 uint32_t cbBuf = _64K; /** @todo Make this configurable? */
1935 void *pvBuf = RTMemAlloc(cbBuf); /** @todo Make this buffer part of PVBGLR3GUESTDNDCMDCTX? */
1936 if (!pvBuf)
1937 return VERR_NO_MEMORY;
1938
1939 int rc;
1940
1941 RTCString strPath = pObj->GetDestPath();
1942
1943 LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", strPath.c_str(), strPath.length(),
1944 pObj->GetSize(), pObj->GetMode()));
1945 LogFlowFunc(("uProtocol=%RU32, uClientID=%RU32\n", pCtx->uProtocol, pCtx->uClientID));
1946
1947 if (pCtx->uProtocol >= 2) /* Protocol version 2 and up sends a file header first. */
1948 {
1949 VBOXDNDGHSENDFILEHDRMSG MsgHdr;
1950 RT_ZERO(MsgHdr);
1951 VBGL_HGCM_HDR_INIT(&MsgHdr.hdr, pCtx->uClientID, GUEST_DND_GH_SND_FILE_HDR, 6);
1952 MsgHdr.uContext.SetUInt32(0); /* Context ID; unused at the moment. */
1953 MsgHdr.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1));
1954 MsgHdr.cbName.SetUInt32((uint32_t)(strPath.length() + 1));
1955 MsgHdr.uFlags.SetUInt32(0); /* Flags; unused at the moment. */
1956 MsgHdr.fMode.SetUInt32(pObj->GetMode()); /* File mode */
1957 MsgHdr.cbTotal.SetUInt64(pObj->GetSize()); /* File size (in bytes). */
1958
1959 rc = VbglR3HGCMCall(&MsgHdr.hdr, sizeof(MsgHdr));
1960
1961 LogFlowFunc(("Sending file header resulted in %Rrc\n", rc));
1962 }
1963 else
1964 rc = VINF_SUCCESS;
1965
1966 if (RT_SUCCESS(rc))
1967 {
1968 /*
1969 * Send the actual file data, chunk by chunk.
1970 */
1971 VBOXDNDGHSENDFILEDATAMSG Msg;
1972 RT_ZERO(Msg);
1973 switch (pCtx->uProtocol)
1974 {
1975 case 3:
1976 {
1977 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_FILE_DATA, 5);
1978 Msg.u.v3.uContext.SetUInt32(0);
1979 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
1980 Msg.u.v3.cbChecksum.SetUInt32(0);
1981 break;
1982 }
1983
1984 case 2:
1985 {
1986 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_FILE_DATA, 3);
1987 Msg.u.v2.uContext.SetUInt32(0);
1988 break;
1989 }
1990
1991 default: /* Protocol v1 */
1992 {
1993 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_SND_FILE_DATA, 5);
1994 Msg.u.v1.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1));
1995 Msg.u.v1.cbName.SetUInt32((uint32_t)(strPath.length() + 1));
1996 Msg.u.v1.fMode.SetUInt32(pObj->GetMode());
1997 break;
1998 }
1999 }
2000
2001 uint64_t cbToReadTotal = pObj->GetSize();
2002 uint64_t cbWrittenTotal = 0;
2003 while (cbToReadTotal)
2004 {
2005 uint32_t cbToRead = RT_MIN(cbToReadTotal, cbBuf);
2006 uint32_t cbRead = 0;
2007 if (cbToRead)
2008 rc = pObj->Read(pvBuf, cbToRead, &cbRead);
2009
2010 LogFlowFunc(("cbToReadTotal=%RU64, cbToRead=%RU32, cbRead=%RU32, rc=%Rrc\n",
2011 cbToReadTotal, cbToRead, cbRead, rc));
2012
2013 if ( RT_SUCCESS(rc)
2014 && cbRead)
2015 {
2016 switch (pCtx->uProtocol)
2017 {
2018 case 3:
2019 {
2020 Msg.u.v3.pvData.SetPtr(pvBuf, cbRead);
2021 Msg.u.v3.cbData.SetUInt32(cbRead);
2022 /** @todo Calculate + set checksums. */
2023 break;
2024 }
2025
2026 case 2:
2027 {
2028 Msg.u.v2.pvData.SetPtr(pvBuf, cbRead);
2029 Msg.u.v2.cbData.SetUInt32(cbRead);
2030 break;
2031 }
2032
2033 default:
2034 {
2035 Msg.u.v1.pvData.SetPtr(pvBuf, cbRead);
2036 Msg.u.v1.cbData.SetUInt32(cbRead);
2037 break;
2038 }
2039 }
2040
2041 rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
2042 }
2043
2044 if (RT_FAILURE(rc))
2045 {
2046 LogFlowFunc(("Reading failed with rc=%Rrc\n", rc));
2047 break;
2048 }
2049
2050 Assert(cbRead <= cbToReadTotal);
2051 cbToReadTotal -= cbRead;
2052 cbWrittenTotal += cbRead;
2053
2054 LogFlowFunc(("%RU64/%RU64 -- %RU8%%\n", cbWrittenTotal, pObj->GetSize(), cbWrittenTotal * 100 / pObj->GetSize()));
2055 };
2056 }
2057
2058 RTMemFree(pvBuf);
2059
2060 LogFlowFuncLeaveRC(rc);
2061 return rc;
2062}
2063
2064/**
2065 * Guest -> Host
2066 * Utility function to send an URI object from guest to the host.
2067 *
2068 * @returns IPRT status code.
2069 * @param pCtx DnD context to use.
2070 * @param pObj URI object to send from guest to the host.
2071 */
2072static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
2073{
2074 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2075 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
2076
2077 int rc;
2078
2079 switch (pObj->GetType())
2080 {
2081 case DnDURIObject::Directory:
2082 rc = vbglR3DnDGHSendDir(pCtx, pObj);
2083 break;
2084
2085 case DnDURIObject::File:
2086 rc = vbglR3DnDGHSendFile(pCtx, pObj);
2087 break;
2088
2089 default:
2090 AssertMsgFailed(("Object type %ld not implemented\n", pObj->GetType()));
2091 rc = VERR_NOT_IMPLEMENTED;
2092 break;
2093 }
2094
2095 return rc;
2096}
2097
2098/**
2099 * Guest -> Host
2100 * Utility function to send raw data from guest to the host.
2101 *
2102 * @returns IPRT status code.
2103 * @param pCtx DnD context to use.
2104 * @param pvData Block to raw data to send.
2105 * @param cbData Size (in bytes) of raw data to send.
2106 */
2107static int vbglR3DnDGHSendRawData(PVBGLR3GUESTDNDCMDCTX pCtx, void *pvData, size_t cbData)
2108{
2109 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2110 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
2111 /* cbData can be 0. */
2112
2113 VBOXDNDDATAHDR dataHdr;
2114 RT_ZERO(dataHdr);
2115
2116 /* For raw data only the total size is required to be specified. */
2117 dataHdr.cbTotal = cbData;
2118
2119 return vbglR3DnDGHSendDataInternal(pCtx, pvData, cbData, &dataHdr);
2120}
2121
2122/**
2123 * Guest -> Host
2124 * Utility function to send URI data from guest to the host.
2125 *
2126 * @returns IPRT status code.
2127 * @param pCtx DnD context to use.
2128 * @param pvData Block to URI data to send.
2129 * @param cbData Size (in bytes) of URI data to send.
2130 */
2131static int vbglR3DnDGHSendURIData(PVBGLR3GUESTDNDCMDCTX pCtx, const void *pvData, size_t cbData)
2132{
2133 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2134 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
2135 AssertReturn(cbData, VERR_INVALID_PARAMETER);
2136
2137 RTCList<RTCString> lstPaths =
2138 RTCString((const char *)pvData, cbData).split("\r\n");
2139
2140 /** @todo Add symlink support (DNDURILIST_FLAGS_RESOLVE_SYMLINKS) here. */
2141 /** @todo Add lazy loading (DNDURILIST_FLAGS_LAZY) here. */
2142 uint32_t fFlags = DNDURILIST_FLAGS_KEEP_OPEN;
2143
2144 DnDURIList lstURI;
2145 int rc = lstURI.AppendURIPathsFromList(lstPaths, fFlags);
2146 if (RT_SUCCESS(rc))
2147 {
2148 /*
2149 * Send the (meta) data; in case of URIs it's the (non-recursive) file/directory
2150 * URI list the host needs to know upfront to set up the drag'n drop operation.
2151 */
2152 RTCString strRootDest = lstURI.RootToString();
2153 if (strRootDest.isNotEmpty())
2154 {
2155 void *pvURIList = (void *)strRootDest.c_str(); /* URI root list. */
2156 uint32_t cbURLIist = (uint32_t)strRootDest.length() + 1; /* Include string termination. */
2157
2158 /* The total size also contains the size of the meta data. */
2159 uint64_t cbTotal = cbURLIist;
2160 cbTotal += lstURI.TotalBytes();
2161
2162 /* We're going to send an URI list in text format. */
2163 const char szMetaFmt[] = "text/uri-list";
2164 const uint32_t cbMetaFmt = (uint32_t)strlen(szMetaFmt) + 1; /* Include termination. */
2165
2166 VBOXDNDDATAHDR dataHdr;
2167 dataHdr.uFlags = 0; /* Flags not used yet. */
2168 dataHdr.cbTotal = cbTotal;
2169 dataHdr.cbMeta = cbURLIist;
2170 dataHdr.pvMetaFmt = (void *)szMetaFmt;
2171 dataHdr.cbMetaFmt = cbMetaFmt;
2172 dataHdr.cObjects = lstURI.TotalCount();
2173
2174 rc = vbglR3DnDGHSendDataInternal(pCtx,
2175 pvURIList, cbURLIist, &dataHdr);
2176 }
2177 else
2178 rc = VERR_INVALID_PARAMETER;
2179 }
2180
2181 if (RT_SUCCESS(rc))
2182 {
2183 while (!lstURI.IsEmpty())
2184 {
2185 DnDURIObject *pNextObj = lstURI.First();
2186
2187 rc = vbglR3DnDGHSendURIObject(pCtx, pNextObj);
2188 if (RT_FAILURE(rc))
2189 break;
2190
2191 lstURI.RemoveFirst();
2192 }
2193 }
2194
2195 return rc;
2196}
2197
2198/**
2199 * Guest -> Host
2200 * Sends data, which either can be raw or URI data, from guest to the host. This function
2201 * initiates the actual data transfer from guest to the host.
2202 *
2203 * @returns IPRT status code.
2204 * @param pCtx DnD context to use.
2205 * @param pszFormat In which format the data will be sent.
2206 * @param pvData Data block to send.
2207 * @param cbData Size (in bytes) of data block to send.
2208 */
2209VBGLR3DECL(int) VbglR3DnDGHSendData(PVBGLR3GUESTDNDCMDCTX pCtx, const char *pszFormat, void *pvData, uint32_t cbData)
2210{
2211 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2212 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
2213 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
2214 AssertReturn(cbData, VERR_INVALID_PARAMETER);
2215
2216 LogFlowFunc(("pszFormat=%s, pvData=%p, cbData=%RU32\n", pszFormat, pvData, cbData));
2217
2218 int rc;
2219 if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat)))
2220 {
2221 /* Send file data. */
2222 rc = vbglR3DnDGHSendURIData(pCtx, pvData, cbData);
2223 }
2224 else
2225 rc = vbglR3DnDGHSendRawData(pCtx, pvData, cbData);
2226
2227 if (RT_FAILURE(rc))
2228 {
2229 int rc2 = VbglR3DnDGHSendError(pCtx, rc);
2230 if (RT_FAILURE(rc2))
2231 LogFlowFunc(("Unable to send error (%Rrc) to host, rc=%Rrc\n", rc, rc2));
2232 }
2233
2234 return rc;
2235}
2236
2237/**
2238 * Guest -> Host
2239 * Send an error back to the host.
2240 *
2241 * @returns IPRT status code.
2242 * @param pCtx DnD context to use.
2243 * @param rcErr Error (IPRT-style) to send.
2244 */
2245VBGLR3DECL(int) VbglR3DnDGHSendError(PVBGLR3GUESTDNDCMDCTX pCtx, int rcErr)
2246{
2247 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2248
2249 VBOXDNDGHEVTERRORMSG Msg;
2250 RT_ZERO(Msg);
2251 if (pCtx->uProtocol < 3)
2252 {
2253 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_EVT_ERROR, 1);
2254 Msg.u.v1.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
2255 }
2256 else
2257 {
2258 VBGL_HGCM_HDR_INIT(&Msg.hdr, pCtx->uClientID, GUEST_DND_GH_EVT_ERROR, 2);
2259 /** @todo Context ID not used yet. */
2260 Msg.u.v3.uContext.SetUInt32(0);
2261 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
2262 }
2263
2264 int rc = VbglR3HGCMCall(&Msg.hdr, sizeof(Msg));
2265
2266 /*
2267 * Never return an error if the host did not accept the error at the current
2268 * time. This can be due to the host not having any appropriate callbacks
2269 * set which would handle that error.
2270 *
2271 * bird: Looks like VERR_NOT_SUPPORTED is what the host will return if it
2272 * doesn't an appropriate callback. The code used to ignore ALL errors
2273 * the host would return, also relevant ones.
2274 */
2275 if (RT_FAILURE(rc))
2276 LogFlowFunc(("Sending error %Rrc failed with rc=%Rrc\n", rcErr, rc));
2277 if (rc == VERR_NOT_SUPPORTED)
2278 rc = VINF_SUCCESS;
2279
2280 return rc;
2281}
2282#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
2283
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