VirtualBox

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

Last change on this file since 74140 was 74140, checked in by vboxsync, 7 years ago

DnD/VbglR3: Don't check for file sizes in vbglR3DnDHGRecvURIData(), as the to-be-transferred files might be 0-byte files.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette