VirtualBox

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

Last change on this file since 68462 was 68462, checked in by vboxsync, 8 years ago

VBoxGuestLib: Use common VbglR3HGCMDisconnect to disconnect.

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