VirtualBox

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

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

*: scm --update-copyright-year

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