VirtualBox

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

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

DnD: Added context IDs for all HGCM messages.

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