VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibDragAndDrop.cpp@ 58212

Last change on this file since 58212 was 58212, checked in by vboxsync, 9 years ago

DnD: Updates.

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