VirtualBox

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

Last change on this file since 56390 was 55640, checked in by vboxsync, 10 years ago

DnD:

  • Overhauled "dropped files" directory + general file handling: Keep the directories/files open when doing the actual transfers (only protocol >= 2)
  • Unified "dropped files" directory creation/rollback handling for guest/host parts.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 56.5 KB
Line 
1/* $Id: VBoxGuestR3LibDragAndDrop.cpp 55640 2015-05-04 12:38:57Z 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
50#include "VBGLR3Internal.h"
51
52/* Here all the communication with the host over HGCM is handled platform
53 * neutral. Also the receiving of URIs content (directory trees and files) is
54 * done here. So the platform code of the guests, should not take care of that.
55 *
56 * Todo:
57 * - Sending dirs/files in the G->H case
58 * - Maybe the EOL converting of text MIME types (not fully sure, eventually
59 * better done on the host side)
60 */
61
62/******************************************************************************
63 * Private internal functions *
64 ******************************************************************************/
65
66static int vbglR3DnDQueryNextHostMessageType(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 DragAndDropSvc::VBOXDNDNEXTMSGMSG Msg;
73 RT_ZERO(Msg);
74 Msg.hdr.result = VERR_WRONG_ORDER;
75 Msg.hdr.u32ClientID = pCtx->uClientID;
76 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG;
77 Msg.hdr.cParms = 3;
78
79 Msg.msg.SetUInt32(0);
80 Msg.num_parms.SetUInt32(0);
81 Msg.block.SetUInt32(fWait ? 1 : 0);
82
83 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
84 if (RT_SUCCESS(rc))
85 {
86 rc = Msg.hdr.result;
87 if (RT_SUCCESS(rc))
88 {
89 rc = Msg.msg.GetUInt32(puMsg); AssertRC(rc);
90 rc = Msg.num_parms.GetUInt32(pcParms); AssertRC(rc);
91 }
92 }
93
94 return rc;
95}
96
97static int vbglR3DnDHGProcessActionMessage(PVBGLR3GUESTDNDCMDCTX pCtx,
98 uint32_t uMsg,
99 uint32_t *puScreenId,
100 uint32_t *puX,
101 uint32_t *puY,
102 uint32_t *puDefAction,
103 uint32_t *puAllActions,
104 char *pszFormats,
105 uint32_t cbFormats,
106 uint32_t *pcbFormatsRecv)
107{
108 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
109 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
110 AssertPtrReturn(puX, VERR_INVALID_POINTER);
111 AssertPtrReturn(puY, VERR_INVALID_POINTER);
112 AssertPtrReturn(puDefAction, VERR_INVALID_POINTER);
113 AssertPtrReturn(puAllActions, VERR_INVALID_POINTER);
114 AssertPtrReturn(pszFormats, VERR_INVALID_POINTER);
115 AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
116 AssertPtrReturn(pcbFormatsRecv, VERR_INVALID_POINTER);
117
118 DragAndDropSvc::VBOXDNDHGACTIONMSG Msg;
119 RT_ZERO(Msg);
120 Msg.hdr.u32ClientID = pCtx->uClientID;
121 Msg.hdr.u32Function = uMsg;
122 Msg.hdr.cParms = 7;
123
124 Msg.uScreenId.SetUInt32(0);
125 Msg.uX.SetUInt32(0);
126 Msg.uY.SetUInt32(0);
127 Msg.uDefAction.SetUInt32(0);
128 Msg.uAllActions.SetUInt32(0);
129 Msg.pvFormats.SetPtr(pszFormats, cbFormats);
130 Msg.cFormats.SetUInt32(0);
131
132 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
133 if (RT_SUCCESS(rc))
134 {
135 rc = Msg.hdr.result;
136 if (RT_SUCCESS(rc))
137 {
138 rc = Msg.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
139 rc = Msg.uX.GetUInt32(puX); AssertRC(rc);
140 rc = Msg.uY.GetUInt32(puY); AssertRC(rc);
141 rc = Msg.uDefAction.GetUInt32(puDefAction); AssertRC(rc);
142 rc = Msg.uAllActions.GetUInt32(puAllActions); AssertRC(rc);
143 rc = Msg.cFormats.GetUInt32(pcbFormatsRecv); AssertRC(rc);
144
145 AssertReturn(cbFormats >= *pcbFormatsRecv, VERR_TOO_MUCH_DATA);
146 }
147 }
148
149 return rc;
150}
151
152static int vbglR3DnDHGProcessLeaveMessage(PVBGLR3GUESTDNDCMDCTX pCtx)
153{
154 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
155
156 DragAndDropSvc::VBOXDNDHGLEAVEMSG Msg;
157 RT_ZERO(Msg);
158 Msg.hdr.u32ClientID = pCtx->uClientID;
159 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_EVT_LEAVE;
160 Msg.hdr.cParms = 0;
161
162 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
163 if (RT_SUCCESS(rc))
164 rc = Msg.hdr.result;
165
166 return rc;
167}
168
169static int vbglR3DnDHGProcessCancelMessage(PVBGLR3GUESTDNDCMDCTX pCtx)
170{
171 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
172
173 DragAndDropSvc::VBOXDNDHGCANCELMSG Msg;
174 RT_ZERO(Msg);
175 Msg.hdr.u32ClientID = pCtx->uClientID;
176 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_EVT_CANCEL;
177 Msg.hdr.cParms = 0;
178
179 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
180 if (RT_SUCCESS(rc))
181 rc = Msg.hdr.result;
182
183 return rc;
184}
185
186static int vbglR3DnDHGProcessSendDirMessage(PVBGLR3GUESTDNDCMDCTX pCtx,
187 char *pszDirname,
188 uint32_t cbDirname,
189 uint32_t *pcbDirnameRecv,
190 uint32_t *pfMode)
191{
192 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
193 AssertPtrReturn(pszDirname, VERR_INVALID_POINTER);
194 AssertReturn(cbDirname, VERR_INVALID_PARAMETER);
195 AssertPtrReturn(pcbDirnameRecv, VERR_INVALID_POINTER);
196 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
197
198 DragAndDropSvc::VBOXDNDHGSENDDIRMSG Msg;
199 RT_ZERO(Msg);
200 Msg.hdr.u32ClientID = pCtx->uClientID;
201 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_SND_DIR;
202 Msg.hdr.cParms = 3;
203
204 Msg.pvName.SetPtr(pszDirname, cbDirname);
205 Msg.cbName.SetUInt32(0);
206 Msg.fMode.SetUInt32(0);
207
208 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
209 if (RT_SUCCESS(rc))
210 {
211 rc = Msg.hdr.result;
212 if (RT_SUCCESS(Msg.hdr.result))
213 {
214 rc = Msg.cbName.GetUInt32(pcbDirnameRecv); AssertRC(rc);
215 rc = Msg.fMode.GetUInt32(pfMode); AssertRC(rc);
216
217 AssertReturn(cbDirname >= *pcbDirnameRecv, VERR_TOO_MUCH_DATA);
218 }
219 }
220
221 return rc;
222}
223
224static int vbglR3DnDHGProcessSendFileMessage(PVBGLR3GUESTDNDCMDCTX pCtx,
225 char *pszFilename,
226 uint32_t cbFilename,
227 uint32_t *pcbFilenameRecv,
228 void *pvData,
229 uint32_t cbData,
230 uint32_t *pcbDataRecv,
231 uint32_t *pfMode)
232{
233 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
234 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
235 AssertReturn(cbFilename, VERR_INVALID_PARAMETER);
236 AssertPtrReturn(pcbFilenameRecv, VERR_INVALID_POINTER);
237 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
238 AssertReturn(cbData, VERR_INVALID_PARAMETER);
239 AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER);
240 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
241
242 DragAndDropSvc::VBOXDNDHGSENDFILEDATAMSG Msg;
243 RT_ZERO(Msg);
244 Msg.hdr.u32ClientID = pCtx->uClientID;
245 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA;
246
247 if (pCtx->uProtocol <= 1)
248 {
249 Msg.u.v1.pvName.SetPtr(pszFilename, cbFilename);
250 Msg.u.v1.cbName.SetUInt32(cbFilename);
251 Msg.u.v1.pvData.SetPtr(pvData, cbData);
252 Msg.u.v1.cbData.SetUInt32(cbData);
253 Msg.u.v1.fMode.SetUInt32(0);
254
255 Msg.hdr.cParms = 5;
256 }
257 else
258 {
259 Msg.u.v2.uContext.SetUInt32(0); /** @todo Not used yet. */
260 Msg.u.v2.pvData.SetPtr(pvData, cbData);
261 Msg.u.v2.cbData.SetUInt32(cbData);
262
263 Msg.hdr.cParms = 3;
264 }
265
266 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
267 if (RT_SUCCESS(rc))
268 {
269 rc = Msg.hdr.result;
270 if (RT_SUCCESS(rc))
271 {
272 if (pCtx->uProtocol <= 1)
273 {
274 rc = Msg.u.v1.cbName.GetUInt32(pcbFilenameRecv); AssertRC(rc);
275 rc = Msg.u.v1.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
276 rc = Msg.u.v1.fMode.GetUInt32(pfMode); AssertRC(rc);
277
278 AssertReturn(cbFilename >= *pcbFilenameRecv, VERR_TOO_MUCH_DATA);
279 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
280 }
281 else
282 {
283 rc = Msg.u.v2.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
284 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
285 }
286 }
287 }
288
289 LogFlowFuncLeaveRC(rc);
290 return rc;
291}
292
293static int vbglR3DnDHGProcessSendFileHdrMessage(PVBGLR3GUESTDNDCMDCTX pCtx,
294 char *pszFilename,
295 uint32_t cbFilename,
296 uint32_t *puFlags,
297 uint32_t *pfMode,
298 uint64_t *pcbTotal)
299{
300 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
301 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
302 AssertReturn(cbFilename, VERR_INVALID_PARAMETER);
303 AssertPtrReturn(puFlags, VERR_INVALID_POINTER);
304 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
305 AssertReturn(pcbTotal, VERR_INVALID_POINTER);
306
307 DragAndDropSvc::VBOXDNDHGSENDFILEHDRMSG Msg;
308 RT_ZERO(Msg);
309 Msg.hdr.u32ClientID = pCtx->uClientID;
310 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR;
311
312 int rc;
313
314 if (pCtx->uProtocol <= 1)
315 {
316 rc = VERR_NOT_SUPPORTED;
317 }
318 else
319 {
320 Msg.uContext.SetUInt32(0); /** @todo Not used yet. */
321 Msg.pvName.SetPtr(pszFilename, cbFilename);
322 Msg.cbName.SetUInt32(cbFilename);
323 Msg.uFlags.SetUInt32(0);
324 Msg.fMode.SetUInt32(0);
325 Msg.cbTotal.SetUInt64(0);
326
327 Msg.hdr.cParms = 6;
328
329 rc = VINF_SUCCESS;
330 }
331
332 if (RT_SUCCESS(rc))
333 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
334 if (RT_SUCCESS(rc))
335 {
336 rc = Msg.hdr.result;
337 if (RT_SUCCESS(rc))
338 {
339 /** @todo Get context ID. */
340 rc = Msg.uFlags.GetUInt32(puFlags); AssertRC(rc);
341 rc = Msg.fMode.GetUInt32(pfMode); AssertRC(rc);
342 rc = Msg.cbTotal.GetUInt64(pcbTotal); AssertRC(rc);
343 }
344 }
345
346 return rc;
347}
348
349static int vbglR3DnDHGProcessURIMessages(PVBGLR3GUESTDNDCMDCTX pCtx,
350 uint32_t *puScreenId,
351 char *pszFormat,
352 uint32_t cbFormat,
353 uint32_t *pcbFormatRecv,
354 void **ppvData,
355 uint32_t cbData,
356 size_t *pcbDataRecv)
357{
358 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
359 AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
360 AssertPtrReturn(cbData, VERR_INVALID_PARAMETER);
361 AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER);
362
363 void *pvData = *ppvData;
364 uint32_t cbDataRecv = 0;
365 uint64_t cbDataToRead = *pcbDataRecv;
366 uint64_t cbDataWritten = 0;
367
368 int rc = VINF_SUCCESS;
369
370 /* Allocate temp buffer. */
371 uint32_t cbTmpData = _64K; /** @todo Make this configurable? */
372 void *pvTmpData = RTMemAlloc(cbTmpData);
373 if (!pvTmpData)
374 return VERR_NO_MEMORY;
375
376 /* Create and query the (unique) drop target directory in the user's temporary directory. */
377 DNDDIRDROPPEDFILES dirDroppedFiles;
378 const char *pszDropDir;
379 rc = DnDDirDroppedFilesCreateAndOpenTemp(&dirDroppedFiles);
380 if (RT_SUCCESS(rc))
381 pszDropDir = DnDDirDroppedFilesGetDirAbs(&dirDroppedFiles);
382
383 DnDURIList lstURI;
384 DnDURIObject objFile(DnDURIObject::File);
385
386 char szPathName[RTPATH_MAX] = { 0 };
387 uint32_t cbPathName = 0;
388 uint32_t fFlags = 0;
389 uint32_t fMode = 0;
390
391 while (RT_SUCCESS(rc))
392 {
393 uint32_t uNextMsg;
394 uint32_t cNextParms;
395 rc = vbglR3DnDQueryNextHostMessageType(pCtx, &uNextMsg, &cNextParms, false /* fWait */);
396 if (RT_SUCCESS(rc))
397 {
398 LogFlowFunc(("uNextMsg=%RU32, cNextParms=%RU32\n", uNextMsg, cNextParms));
399
400 switch (uNextMsg)
401 {
402 case DragAndDropSvc::HOST_DND_HG_SND_DIR:
403 {
404 rc = vbglR3DnDHGProcessSendDirMessage(pCtx,
405 szPathName,
406 sizeof(szPathName),
407 &cbPathName,
408 &fMode);
409 LogFlowFunc(("HOST_DND_HG_SND_DIR pszPathName=%s, cbPathName=%RU32, fMode=0x%x, rc=%Rrc\n",
410 szPathName, cbPathName, fMode, rc));
411
412 char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName);
413 if (pszPathAbs)
414 {
415 rc = RTDirCreate(pszPathAbs, (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRWXU, 0);
416 if (RT_SUCCESS(rc))
417 rc = DnDDirDroppedAddDir(&dirDroppedFiles, pszPathAbs);
418
419 RTStrFree(pszPathAbs);
420 }
421 else
422 rc = VERR_NO_MEMORY;
423 break;
424 }
425 case DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR:
426 case DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA:
427 {
428 if (uNextMsg == DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR)
429 rc = vbglR3DnDHGProcessSendFileHdrMessage(pCtx,
430 szPathName,
431 sizeof(szPathName),
432 &fFlags,
433 &fMode,
434 &cbDataToRead);
435 else
436 rc = vbglR3DnDHGProcessSendFileMessage(pCtx,
437 szPathName,
438 sizeof(szPathName),
439 &cbPathName,
440 pvTmpData,
441 cbTmpData,
442 &cbDataRecv,
443 &fMode);
444 if ( RT_SUCCESS(rc)
445 && ( uNextMsg == DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR
446 /* Protocol v1 always sends the file name, so try opening every time. */
447 || pCtx->uProtocol <= 1)
448 )
449 {
450 char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName);
451 if (pszPathAbs)
452 {
453 LogFlowFunc(("Opening pszPathName=%s, cbPathName=%RU32, fMode=0x%x, cbSize=%RU64\n",
454 szPathName, cbPathName, fMode, cbDataToRead));
455
456 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_ALL;
457 if (pCtx->uProtocol <= 1)
458 fOpen |= RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND;
459 else
460 fOpen |= RTFILE_O_CREATE_REPLACE;
461
462 /* Is there already a file open, e.g. in transfer? */
463 if (!objFile.IsOpen())
464 {
465
466 RTCString strPathAbs(pszPathAbs);
467 rc = objFile.OpenEx(strPathAbs, DnDURIObject::File, DnDURIObject::Target, fOpen,
468 (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR);
469 if (RT_SUCCESS(rc))
470 {
471 rc = DnDDirDroppedAddFile(&dirDroppedFiles, strPathAbs.c_str());
472 if (RT_SUCCESS(rc))
473 {
474 cbDataWritten = 0;
475
476 if (pCtx->uProtocol >= 2) /* Set the expected file size. */
477 objFile.SetSize(cbDataToRead);
478 }
479 }
480 }
481 else
482 rc = VERR_WRONG_ORDER;
483
484 RTStrFree(pszPathAbs);
485 }
486 else
487 rc = VERR_NO_MEMORY;
488 }
489
490 if ( RT_SUCCESS(rc)
491 && uNextMsg == DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA)
492 {
493 bool fClose = false;
494
495 uint32_t cbWritten;
496 rc = objFile.Write(pvTmpData, cbDataRecv, &cbWritten);
497 if (RT_SUCCESS(rc))
498 {
499 if (pCtx->uProtocol >= 2)
500 {
501 /* Data transfer complete? Close the file. */
502 fClose = objFile.IsComplete();
503 }
504 else
505 fClose = true; /* Always close the file after each chunk. */
506
507 cbDataWritten += cbWritten;
508 Assert(cbDataWritten <= cbDataToRead);
509 }
510
511 if (fClose)
512 {
513 LogFlowFunc(("Closing file\n"));
514 objFile.Close();
515 }
516 }
517 break;
518 }
519 case DragAndDropSvc::HOST_DND_HG_EVT_CANCEL:
520 {
521 rc = vbglR3DnDHGProcessCancelMessage(pCtx);
522 if (RT_SUCCESS(rc))
523 rc = VERR_CANCELLED;
524 break;
525 }
526 default:
527 LogFlowFunc(("Message %RU32 not supported\n", uNextMsg));
528 rc = VERR_NOT_SUPPORTED;
529 break;
530 }
531
532#if 0 /* Not used yet. */
533 if (pCtx->uProtocol >= XXX)
534 {
535 /*
536 * Send the progress back to the host.
537 */
538 uint32_t uStatus;
539 int guestRc;
540 uint8_t uPercent;
541 switch (rc)
542 {
543 case VINF_SUCCESS:
544 {
545 if (!cbData)
546 cbData = 1;
547 uPercent = cbDataWritten * 100 / (cbDataToRead ? cbDataToRead : 1);
548 uStatus = uPercent >= 100 ?
549 DragAndDropSvc::DND_PROGRESS_COMPLETE : DragAndDropSvc::DND_PROGRESS_RUNNING;
550 guestRc = VINF_SUCCESS;
551 break;
552 }
553
554 case VERR_CANCELLED:
555 {
556 uStatus = DragAndDropSvc::DND_PROGRESS_CANCELLED;
557 uPercent = 100;
558 guestRc = VINF_SUCCESS;
559 break;
560 }
561
562 default:
563 {
564 uStatus = DragAndDropSvc::DND_PROGRESS_ERROR;
565 uPercent = 100;
566 guestRc = rc;
567 break;
568 }
569 }
570
571 int rc2 = VbglR3DnDHGSetProgress(pCtx, uStatus, uPercent, guestRc);
572 LogFlowFunc(("cbDataWritten=%RU64 / cbDataToRead=%RU64 => %RU8%% (uStatus=%ld, %Rrc), rc=%Rrc\n", cbDataWritten, cbDataToRead,
573 uPercent, uStatus, guestRc, rc2));
574 if (RT_SUCCESS(rc))
575 rc = rc2;
576
577 /* All data transferred? */
578 if ( RT_SUCCESS(rc)
579 && uPercent == 100)
580 {
581 rc = VINF_EOF;
582 break;
583 }
584 }
585#endif
586 }
587
588 if (RT_FAILURE(rc))
589 break;
590
591 } /* while */
592
593 LogFlowFunc(("Loop ended with %Rrc\n", rc));
594
595 /* All URI data processed? */
596 if (rc == VERR_NO_DATA)
597 rc = VINF_SUCCESS;
598
599 if (pvTmpData)
600 RTMemFree(pvTmpData);
601
602 /* Cleanup on failure or if the user has canceled the operation or
603 * something else went wrong. */
604 if (RT_FAILURE(rc))
605 {
606 int rc2 = DnDDirDroppedFilesRollback(&dirDroppedFiles);
607 AssertRC(rc2); /* Not fatal, don't report back to host. */
608 }
609 else
610 {
611 /*
612 * Patch the old drop data with the new drop directory, so the drop target can find the files.
613 */
614 rc = lstURI.RootFromURIData(pvData, cbDataToRead, 0 /* fFlags */);
615 if (RT_SUCCESS(rc))
616 {
617 /* Cleanup the old data and write the new data back to the event. */
618 RTMemFree(pvData);
619
620 RTCString strData = lstURI.RootToString(pszDropDir);
621 LogFlowFunc(("cbDataToRead: %zu -> %zu\n", cbDataToRead, strData.length() + 1));
622
623 pvData = RTStrDupN(strData.c_str(), strData.length());
624 cbDataToRead = strData.length() + 1;
625 }
626
627 if (RT_SUCCESS(rc))
628 {
629 *ppvData = pvData;
630 *pcbDataRecv = cbDataToRead;
631 }
632
633 /** @todo Compare the URI list with the dirs/files we really transferred. */
634 }
635
636 /*
637 * Close the dropped files directory.
638 * Don't try to remove it here, however, as the files are being needed
639 * by the client's drag'n drop operation lateron.
640 */
641 int rc2 = DnDDirDroppedFilesClose(&dirDroppedFiles, false);
642 if (RT_FAILURE(rc2)) /* Not fatal, don't report back to host. */
643 LogFlowFunc(("Closing dropped files directory failed with %Rrc\n", rc2));
644
645 LogFlowFuncLeaveRC(rc);
646 return rc;
647}
648
649static int vbglR3DnDHGProcessDataMessageInternal(PVBGLR3GUESTDNDCMDCTX pCtx,
650 uint32_t *puScreenId,
651 char *pszFormat,
652 uint32_t cbFormat,
653 uint32_t *pcbFormatRecv,
654 void *pvData,
655 uint32_t cbData,
656 uint32_t *pcbDataTotal)
657{
658 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
659 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
660 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
661 AssertReturn(cbFormat, VERR_INVALID_PARAMETER);
662 AssertPtrReturn(pcbFormatRecv, VERR_INVALID_POINTER);
663 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
664 AssertReturn(cbData, VERR_INVALID_PARAMETER);
665 AssertPtrReturn(pcbDataTotal, VERR_INVALID_POINTER);
666
667 DragAndDropSvc::VBOXDNDHGSENDDATAMSG Msg;
668 RT_ZERO(Msg);
669 Msg.hdr.u32ClientID = pCtx->uClientID;
670 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_SND_DATA;
671 Msg.hdr.cParms = 5;
672
673 Msg.uScreenId.SetUInt32(0);
674 Msg.pvFormat.SetPtr(pszFormat, cbFormat);
675 Msg.cFormat.SetUInt32(0);
676 Msg.pvData.SetPtr(pvData, cbData);
677 Msg.cbData.SetUInt32(0);
678
679 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
680 if (RT_SUCCESS(rc))
681 {
682 rc = Msg.hdr.result;
683 if ( RT_SUCCESS(rc)
684 || rc == VERR_BUFFER_OVERFLOW)
685 {
686 rc = Msg.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
687
688 /*
689 * In case of VERR_BUFFER_OVERFLOW get the data sizes required
690 * for the format + data blocks.
691 */
692 rc = Msg.cFormat.GetUInt32(pcbFormatRecv); AssertRC(rc);
693 rc = Msg.cbData.GetUInt32(pcbDataTotal); AssertRC(rc);
694
695 AssertReturn(cbFormat >= *pcbFormatRecv, VERR_TOO_MUCH_DATA);
696 AssertReturn(cbData >= *pcbDataTotal, VERR_TOO_MUCH_DATA);
697 }
698 }
699
700 return rc;
701}
702
703static int vbglR3DnDHGProcessMoreDataMessageInternal(PVBGLR3GUESTDNDCMDCTX pCtx,
704 void *pvData,
705 uint32_t cbData,
706 uint32_t *pcbDataTotal)
707{
708 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
709 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
710 AssertReturn(cbData, VERR_INVALID_PARAMETER);
711 AssertPtrReturn(pcbDataTotal, VERR_INVALID_POINTER);
712
713 DragAndDropSvc::VBOXDNDHGSENDMOREDATAMSG Msg;
714 RT_ZERO(Msg);
715 Msg.hdr.u32ClientID = pCtx->uClientID;
716 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_SND_MORE_DATA;
717 Msg.hdr.cParms = 2;
718
719 Msg.pvData.SetPtr(pvData, cbData);
720 Msg.cbData.SetUInt32(0);
721
722 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
723 if (RT_SUCCESS(rc))
724 {
725 rc = Msg.hdr.result;
726 if ( RT_SUCCESS(rc)
727 || rc == VERR_BUFFER_OVERFLOW)
728 {
729 rc = Msg.cbData.GetUInt32(pcbDataTotal); AssertRC(rc);
730 AssertReturn(cbData >= *pcbDataTotal, VERR_TOO_MUCH_DATA);
731 }
732 }
733 return rc;
734}
735
736static int vbglR3DnDHGProcessSendDataMessageLoop(PVBGLR3GUESTDNDCMDCTX pCtx,
737 uint32_t *puScreenId,
738 char *pszFormat,
739 uint32_t cbFormat,
740 uint32_t *pcbFormatRecv,
741 void **ppvData,
742 uint32_t cbData,
743 size_t *pcbDataRecv)
744{
745 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
746 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
747 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
748 AssertPtrReturn(pcbFormatRecv, VERR_INVALID_POINTER);
749 AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
750 /* pcbDataRecv is optional. */
751
752 uint32_t cbDataReq = 0;
753 int rc = vbglR3DnDHGProcessDataMessageInternal(pCtx,
754 puScreenId,
755 pszFormat,
756 cbFormat,
757 pcbFormatRecv,
758 *ppvData,
759 cbData,
760 &cbDataReq);
761 uint32_t cbDataTotal = cbDataReq;
762 void *pvData = *ppvData;
763
764 LogFlowFunc(("HOST_DND_HG_SND_DATA cbDataReq=%RU32, rc=%Rrc\n", cbDataTotal, rc));
765
766 while (rc == VERR_BUFFER_OVERFLOW)
767 {
768 uint32_t uNextMsg;
769 uint32_t cNextParms;
770 rc = vbglR3DnDQueryNextHostMessageType(pCtx, &uNextMsg, &cNextParms, false);
771 if (RT_SUCCESS(rc))
772 {
773 switch(uNextMsg)
774 {
775 case DragAndDropSvc::HOST_DND_HG_SND_MORE_DATA:
776 {
777 /** @todo r=andy Don't use reallocate here; can go wrong with *really* big URI lists.
778 * Instead send as many URI entries as possible per chunk and add those entries
779 * to our to-process list for immediata processing. Repeat the step after processing then. */
780 LogFlowFunc(("HOST_DND_HG_SND_MORE_DATA cbDataTotal: %RU32 -> %RU32\n", cbDataReq, cbDataReq + cbData));
781 pvData = RTMemRealloc(*ppvData, cbDataTotal + cbData);
782 if (!pvData)
783 {
784 rc = VERR_NO_MEMORY;
785 break;
786 }
787 rc = vbglR3DnDHGProcessMoreDataMessageInternal(pCtx,
788 &((char *)pvData)[cbDataTotal],
789 cbData,
790 &cbDataReq);
791 cbDataTotal += cbDataReq;
792 break;
793 }
794 case DragAndDropSvc::HOST_DND_HG_EVT_CANCEL:
795 default:
796 {
797 rc = vbglR3DnDHGProcessCancelMessage(pCtx);
798 if (RT_SUCCESS(rc))
799 rc = VERR_CANCELLED;
800 break;
801 }
802 }
803 }
804 }
805
806 if (RT_SUCCESS(rc))
807 {
808 *ppvData = pvData;
809 if (pcbDataRecv)
810 *pcbDataRecv = cbDataTotal;
811 }
812
813 return rc;
814}
815
816static int vbglR3DnDHGProcessSendDataMessage(PVBGLR3GUESTDNDCMDCTX pCtx,
817 uint32_t *puScreenId,
818 char *pszFormat,
819 uint32_t cbFormat,
820 uint32_t *pcbFormatRecv,
821 void **ppvData,
822 uint32_t cbData,
823 size_t *pcbDataRecv)
824{
825 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
826 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
827 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
828 AssertPtrReturn(pcbFormatRecv, VERR_INVALID_POINTER);
829 AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
830
831 int rc = vbglR3DnDHGProcessSendDataMessageLoop(pCtx,
832 puScreenId,
833 pszFormat,
834 cbFormat,
835 pcbFormatRecv,
836 ppvData,
837 cbData,
838 pcbDataRecv);
839 if (RT_SUCCESS(rc))
840 {
841 /* Check if this is an URI event. If so, let VbglR3 do all the actual
842 * data transfer + file/directory creation internally without letting
843 * the caller know.
844 *
845 * This keeps the actual (guest OS-)dependent client (like VBoxClient /
846 * VBoxTray) small by not having too much redundant code. */
847 AssertPtr(pcbFormatRecv);
848 if (DnDMIMEHasFileURLs(pszFormat, *pcbFormatRecv))
849 {
850 rc = vbglR3DnDHGProcessURIMessages(pCtx,
851 puScreenId,
852 pszFormat,
853 cbFormat,
854 pcbFormatRecv,
855 ppvData,
856 cbData,
857 pcbDataRecv);
858 }
859 else
860 rc = VERR_NOT_SUPPORTED;
861
862 if (RT_FAILURE(rc))
863 {
864 if (RT_FAILURE(rc))
865 {
866 int rc2 = VbglR3DnDHGSetProgress(pCtx, DragAndDropSvc::DND_PROGRESS_ERROR, 100 /* Percent */, rc);
867 AssertRC(rc2);
868 }
869 }
870 }
871
872 return rc;
873}
874
875static int vbglR3DnDGHProcessRequestPendingMessage(PVBGLR3GUESTDNDCMDCTX pCtx,
876 uint32_t *puScreenId)
877{
878 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
879 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
880
881 DragAndDropSvc::VBOXDNDGHREQPENDINGMSG Msg;
882 RT_ZERO(Msg);
883 Msg.hdr.u32ClientID = pCtx->uClientID;
884 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_GH_REQ_PENDING;
885 Msg.hdr.cParms = 1;
886
887 Msg.uScreenId.SetUInt32(0);
888
889 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
890 if (RT_SUCCESS(rc))
891 {
892 rc = Msg.hdr.result;
893 if (RT_SUCCESS(rc))
894 {
895 rc = Msg.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
896 }
897 }
898
899 return rc;
900}
901
902static int vbglR3DnDGHProcessDroppedMessage(PVBGLR3GUESTDNDCMDCTX pCtx,
903 char *pszFormat,
904 uint32_t cbFormat,
905 uint32_t *pcbFormatRecv,
906 uint32_t *puAction)
907{
908 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
909 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
910 AssertReturn(cbFormat, VERR_INVALID_PARAMETER);
911 AssertPtrReturn(pcbFormatRecv, VERR_INVALID_POINTER);
912 AssertPtrReturn(puAction, VERR_INVALID_POINTER);
913
914 DragAndDropSvc::VBOXDNDGHDROPPEDMSG Msg;
915 RT_ZERO(Msg);
916 Msg.hdr.u32ClientID = pCtx->uClientID;
917 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_GH_EVT_DROPPED;
918 Msg.hdr.cParms = 3;
919
920 Msg.pvFormat.SetPtr(pszFormat, cbFormat);
921 Msg.cFormat.SetUInt32(0);
922 Msg.uAction.SetUInt32(0);
923
924 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
925 if (RT_SUCCESS(rc))
926 {
927 rc = Msg.hdr.result;
928 if (RT_SUCCESS(rc))
929 {
930 rc = Msg.cFormat.GetUInt32(pcbFormatRecv); AssertRC(rc);
931 rc = Msg.uAction.GetUInt32(puAction); AssertRC(rc);
932
933 AssertReturn(cbFormat >= *pcbFormatRecv, VERR_TOO_MUCH_DATA);
934 }
935 }
936
937 return rc;
938}
939
940/******************************************************************************
941 * Public functions *
942 ******************************************************************************/
943
944VBGLR3DECL(int) VbglR3DnDConnect(PVBGLR3GUESTDNDCMDCTX pCtx)
945{
946 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
947
948 /* Initialize header */
949 VBoxGuestHGCMConnectInfo Info;
950 RT_ZERO(Info.Loc.u);
951 Info.result = VERR_WRONG_ORDER;
952 Info.u32ClientID = UINT32_MAX; /* try make valgrind shut up. */
953 Info.Loc.type = VMMDevHGCMLoc_LocalHost_Existing;
954
955 int rc = RTStrCopy(Info.Loc.u.host.achName, sizeof(Info.Loc.u.host.achName), "VBoxDragAndDropSvc");
956 if (RT_FAILURE(rc))
957 return rc;
958
959 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CONNECT, &Info, sizeof(Info));
960 if (RT_SUCCESS(rc))
961 {
962 rc = Info.result;
963 if (rc == VERR_HGCM_SERVICE_NOT_FOUND)
964 rc = VINF_PERMISSION_DENIED;
965
966 /* Set the protocol version to use. */
967 pCtx->uProtocol = 2;
968
969 Assert(Info.u32ClientID);
970 pCtx->uClientID = Info.u32ClientID;
971 }
972
973 if (RT_SUCCESS(rc))
974 {
975 /*
976 * Try sending the connect message to tell the protocol version to use.
977 * Note: This might fail when the Guest Additions run on an older VBox host (< VBox 5.0) which
978 * does not implement this command.
979 */
980 DragAndDropSvc::VBOXDNDCONNECTPMSG Msg;
981 RT_ZERO(Msg);
982 Msg.hdr.result = VERR_WRONG_ORDER;
983 Msg.hdr.u32ClientID = pCtx->uClientID;
984 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_CONNECT;
985 Msg.hdr.cParms = 2;
986
987 Msg.uProtocol.SetUInt32(pCtx->uProtocol);
988 Msg.uFlags.SetUInt32(0); /* Unused at the moment. */
989
990 int rc2 = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
991 if (RT_SUCCESS(rc2))
992 rc2 = Msg.hdr.result; /* Not fatal. */
993
994 LogFlowFunc(("Connection request ended with rc=%Rrc\n", rc2));
995 }
996
997 LogFlowFunc(("uClient=%RU32, uProtocol=%RU32, rc=%Rrc\n", pCtx->uClientID, pCtx->uProtocol, rc));
998 return rc;
999}
1000
1001VBGLR3DECL(int) VbglR3DnDDisconnect(PVBGLR3GUESTDNDCMDCTX pCtx)
1002{
1003 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1004
1005 VBoxGuestHGCMDisconnectInfo Info;
1006 Info.result = VERR_WRONG_ORDER;
1007 Info.u32ClientID = pCtx->uClientID;
1008
1009 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_DISCONNECT, &Info, sizeof(Info));
1010 if (RT_SUCCESS(rc))
1011 rc = Info.result;
1012
1013 return rc;
1014}
1015
1016VBGLR3DECL(int) VbglR3DnDProcessNextMessage(PVBGLR3GUESTDNDCMDCTX pCtx, CPVBGLR3DNDHGCMEVENT pEvent)
1017{
1018 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1019 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1020
1021 uint32_t uMsg = 0;
1022 uint32_t uNumParms = 0;
1023 const uint32_t ccbFormats = _64K;
1024 const uint32_t ccbData = _64K;
1025 int rc = vbglR3DnDQueryNextHostMessageType(pCtx, &uMsg, &uNumParms,
1026 true /* fWait */);
1027 if (RT_SUCCESS(rc))
1028 {
1029 pEvent->uType = uMsg;
1030
1031 switch(uMsg)
1032 {
1033 case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
1034 case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
1035 case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
1036 {
1037 pEvent->pszFormats = static_cast<char*>(RTMemAlloc(ccbFormats));
1038 if (!pEvent->pszFormats)
1039 rc = VERR_NO_MEMORY;
1040
1041 if (RT_SUCCESS(rc))
1042 rc = vbglR3DnDHGProcessActionMessage(pCtx,
1043 uMsg,
1044 &pEvent->uScreenId,
1045 &pEvent->u.a.uXpos,
1046 &pEvent->u.a.uYpos,
1047 &pEvent->u.a.uDefAction,
1048 &pEvent->u.a.uAllActions,
1049 pEvent->pszFormats,
1050 ccbFormats,
1051 &pEvent->cbFormats);
1052 break;
1053 }
1054 case DragAndDropSvc::HOST_DND_HG_EVT_LEAVE:
1055 {
1056 rc = vbglR3DnDHGProcessLeaveMessage(pCtx);
1057 break;
1058 }
1059 case DragAndDropSvc::HOST_DND_HG_SND_DATA:
1060 {
1061 pEvent->pszFormats = static_cast<char*>(RTMemAlloc(ccbFormats));
1062 if (!pEvent->pszFormats)
1063 rc = VERR_NO_MEMORY;
1064
1065 if (RT_SUCCESS(rc))
1066 {
1067 pEvent->u.b.pvData = RTMemAlloc(ccbData);
1068 if (!pEvent->u.b.pvData)
1069 {
1070 RTMemFree(pEvent->pszFormats);
1071 pEvent->pszFormats = NULL;
1072
1073 rc = VERR_NO_MEMORY;
1074 }
1075 }
1076
1077 if (RT_SUCCESS(rc))
1078 rc = vbglR3DnDHGProcessSendDataMessage(pCtx,
1079 &pEvent->uScreenId,
1080 pEvent->pszFormats,
1081 ccbFormats,
1082 &pEvent->cbFormats,
1083 &pEvent->u.b.pvData,
1084 ccbData,
1085 &pEvent->u.b.cbData);
1086 break;
1087 }
1088 case DragAndDropSvc::HOST_DND_HG_SND_MORE_DATA:
1089 case DragAndDropSvc::HOST_DND_HG_SND_DIR:
1090 case DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA:
1091 {
1092 /*
1093 * All messages in this case are handled internally
1094 * by vbglR3DnDHGProcessSendDataMessage() and must
1095 * be specified by a preceding HOST_DND_HG_SND_DATA call.
1096 */
1097 rc = VERR_WRONG_ORDER;
1098 break;
1099 }
1100 case DragAndDropSvc::HOST_DND_HG_EVT_CANCEL:
1101 {
1102 rc = vbglR3DnDHGProcessCancelMessage(pCtx);
1103 break;
1104 }
1105#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1106 case DragAndDropSvc::HOST_DND_GH_REQ_PENDING:
1107 {
1108 rc = vbglR3DnDGHProcessRequestPendingMessage(pCtx, &pEvent->uScreenId);
1109 break;
1110 }
1111 case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
1112 {
1113 pEvent->pszFormats = static_cast<char*>(RTMemAlloc(ccbFormats));
1114 if (!pEvent->pszFormats)
1115 rc = VERR_NO_MEMORY;
1116
1117 if (RT_SUCCESS(rc))
1118 rc = vbglR3DnDGHProcessDroppedMessage(pCtx,
1119 pEvent->pszFormats,
1120 ccbFormats,
1121 &pEvent->cbFormats,
1122 &pEvent->u.a.uDefAction);
1123 break;
1124 }
1125#endif
1126 default:
1127 {
1128 rc = VERR_NOT_SUPPORTED;
1129 break;
1130 }
1131 }
1132 }
1133
1134 return rc;
1135}
1136
1137VBGLR3DECL(int) VbglR3DnDHGAcknowledgeOperation(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uAction)
1138{
1139 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1140
1141 DragAndDropSvc::VBOXDNDHGACKOPMSG Msg;
1142 RT_ZERO(Msg);
1143 Msg.hdr.result = VERR_WRONG_ORDER;
1144 Msg.hdr.u32ClientID = pCtx->uClientID;
1145 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_HG_ACK_OP;
1146 Msg.hdr.cParms = 1;
1147
1148 Msg.uAction.SetUInt32(uAction);
1149
1150 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1151 if (RT_SUCCESS(rc))
1152 rc = Msg.hdr.result;
1153
1154 return rc;
1155}
1156
1157VBGLR3DECL(int) VbglR3DnDHGRequestData(PVBGLR3GUESTDNDCMDCTX pCtx, const char* pcszFormat)
1158{
1159 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1160 AssertPtrReturn(pcszFormat, VERR_INVALID_POINTER);
1161
1162 DragAndDropSvc::VBOXDNDHGREQDATAMSG Msg;
1163 RT_ZERO(Msg);
1164 Msg.hdr.result = VERR_WRONG_ORDER;
1165 Msg.hdr.u32ClientID = pCtx->uClientID;
1166 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_HG_REQ_DATA;
1167 Msg.hdr.cParms = 1;
1168
1169 Msg.pFormat.SetPtr((void*)pcszFormat, (uint32_t)strlen(pcszFormat) + 1);
1170
1171 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1172 if (RT_SUCCESS(rc))
1173 rc = Msg.hdr.result;
1174
1175 return rc;
1176}
1177
1178VBGLR3DECL(int) VbglR3DnDHGSetProgress(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uStatus, uint8_t uPercent, int rcErr)
1179{
1180 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1181
1182 DragAndDropSvc::VBOXDNDHGEVTPROGRESSMSG Msg;
1183 RT_ZERO(Msg);
1184 Msg.hdr.result = VERR_WRONG_ORDER;
1185 Msg.hdr.u32ClientID = pCtx->uClientID;
1186 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_HG_EVT_PROGRESS;
1187 Msg.hdr.cParms = 3;
1188
1189 Msg.uStatus.SetUInt32(uStatus);
1190 Msg.uPercent.SetUInt32(uPercent);
1191 Msg.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1192
1193 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1194 if (RT_SUCCESS(rc))
1195 rc = Msg.hdr.result;
1196
1197 return rc;
1198}
1199
1200#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1201VBGLR3DECL(int) VbglR3DnDGHAcknowledgePending(PVBGLR3GUESTDNDCMDCTX pCtx,
1202 uint32_t uDefAction, uint32_t uAllActions, const char* pcszFormats)
1203{
1204 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1205 AssertPtrReturn(pcszFormats, VERR_INVALID_POINTER);
1206
1207 DragAndDropSvc::VBOXDNDGHACKPENDINGMSG Msg;
1208 RT_ZERO(Msg);
1209 Msg.hdr.result = VERR_WRONG_ORDER;
1210 Msg.hdr.u32ClientID = pCtx->uClientID;
1211 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_ACK_PENDING;
1212 Msg.hdr.cParms = 3;
1213
1214 Msg.uDefAction.SetUInt32(uDefAction);
1215 Msg.uAllActions.SetUInt32(uAllActions);
1216 Msg.pFormat.SetPtr((void*)pcszFormats, (uint32_t)strlen(pcszFormats) + 1);
1217
1218 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1219 if (RT_SUCCESS(rc))
1220 rc = Msg.hdr.result;
1221
1222 return rc;
1223}
1224
1225static int vbglR3DnDGHSendDataInternal(PVBGLR3GUESTDNDCMDCTX pCtx,
1226 void *pvData, uint32_t cbData, uint32_t cbAdditionalData)
1227{
1228 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1229 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1230 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1231 /* cbAdditionalData is optional. */
1232
1233 DragAndDropSvc::VBOXDNDGHSENDDATAMSG Msg;
1234 RT_ZERO(Msg);
1235 Msg.hdr.result = VERR_WRONG_ORDER;
1236 Msg.hdr.u32ClientID = pCtx->uClientID;
1237 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_SND_DATA;
1238 Msg.hdr.cParms = 2;
1239
1240 /* Total amount of bytes to send (including this data block). */
1241 Msg.cbTotalBytes.SetUInt32(cbData + cbAdditionalData);
1242
1243 int rc = VINF_SUCCESS;
1244
1245 uint32_t cbCurChunk;
1246 uint32_t cbMaxChunk = _64K; /** @todo Transfer max. 64K chunks per message. Configurable? */
1247 uint32_t cbSent = 0;
1248
1249 while (cbSent < cbData)
1250 {
1251 cbCurChunk = RT_MIN(cbData - cbSent, cbMaxChunk);
1252 Msg.pvData.SetPtr(static_cast<uint8_t *>(pvData) + cbSent, cbCurChunk);
1253
1254 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1255 if (RT_SUCCESS(rc))
1256 rc = Msg.hdr.result;
1257
1258 if (RT_FAILURE(rc))
1259 break;
1260
1261 cbSent += cbCurChunk;
1262 }
1263
1264 if (RT_SUCCESS(rc))
1265 Assert(cbSent == cbData);
1266
1267 LogFlowFunc(("Returning rc=%Rrc, cbData=%RU32, cbAddtionalData=%RU32, cbSent=%RU32\n",
1268 rc, cbData, cbAdditionalData, cbSent));
1269 return rc;
1270}
1271
1272static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject &obj)
1273{
1274 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1275 AssertReturn(obj.GetType() == DnDURIObject::Directory, VERR_INVALID_PARAMETER);
1276
1277 DragAndDropSvc::VBOXDNDGHSENDDIRMSG Msg;
1278 RT_ZERO(Msg);
1279 Msg.hdr.result = VERR_WRONG_ORDER;
1280 Msg.hdr.u32ClientID = pCtx->uClientID;
1281 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_SND_DIR;
1282 Msg.hdr.cParms = 3;
1283
1284 RTCString strPath = obj.GetDestPath();
1285 LogFlowFunc(("strDir=%s (%zu), fMode=0x%x\n",
1286 strPath.c_str(), strPath.length(), obj.GetMode()));
1287
1288 Msg.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1));
1289 Msg.cbName.SetUInt32((uint32_t)(strPath.length() + 1));
1290 Msg.fMode.SetUInt32(obj.GetMode());
1291
1292 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1293 if (RT_SUCCESS(rc))
1294 rc = Msg.hdr.result;
1295
1296 LogFlowFuncLeaveRC(rc);
1297 return rc;
1298}
1299
1300static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject &obj)
1301{
1302 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1303 AssertReturn(obj.GetType() == DnDURIObject::File, VERR_INVALID_PARAMETER);
1304
1305 uint32_t cbBuf = _64K; /** @todo Make this configurable? */
1306 void *pvBuf = RTMemAlloc(cbBuf); /** @todo Make this buffer part of PVBGLR3GUESTDNDCMDCTX? */
1307 if (!pvBuf)
1308 return VERR_NO_MEMORY;
1309
1310 RTCString strPath = obj.GetDestPath();
1311
1312 int rc = obj.Open(DnDURIObject::Source, RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
1313 if (RT_FAILURE(rc))
1314 {
1315 LogFunc(("Opening file \"%s\" failed with rc=%Rrc\n", obj.GetSourcePath().c_str(), rc));
1316 return rc;
1317 }
1318
1319 LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", strPath.c_str(), strPath.length(), obj.GetSize(), obj.GetMode()));
1320 LogFlowFunc(("uProtocol=%RU32, uClientID=%RU32\n", pCtx->uProtocol, pCtx->uClientID));
1321
1322 if (pCtx->uProtocol >= 2) /* Protocol version 2 and up sends a file header first. */
1323 {
1324 DragAndDropSvc::VBOXDNDGHSENDFILEHDRMSG MsgHdr;
1325 RT_ZERO(MsgHdr);
1326 MsgHdr.hdr.result = VERR_WRONG_ORDER;
1327 MsgHdr.hdr.u32ClientID = pCtx->uClientID;
1328 MsgHdr.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR;
1329 MsgHdr.hdr.cParms = 6;
1330
1331 MsgHdr.uContext.SetUInt32(0); /* Context ID; unused at the moment. */
1332 MsgHdr.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1));
1333 MsgHdr.cbName.SetUInt32((uint32_t)(strPath.length() + 1));
1334 MsgHdr.uFlags.SetUInt32(0); /* Flags; unused at the moment. */
1335 MsgHdr.fMode.SetUInt32(obj.GetMode());
1336 MsgHdr.cbTotal.SetUInt64(obj.GetSize());
1337
1338 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(MsgHdr)), &MsgHdr, sizeof(MsgHdr));
1339 if (RT_SUCCESS(rc))
1340 rc = MsgHdr.hdr.result;
1341
1342 LogFlowFunc(("Sending file header resulted in %Rrc\n", rc));
1343 }
1344 else
1345 rc = VINF_SUCCESS;
1346
1347 if (RT_SUCCESS(rc))
1348 {
1349 /*
1350 * Send the actual file data, chunk by chunk.
1351 */
1352 DragAndDropSvc::VBOXDNDGHSENDFILEDATAMSG Msg;
1353 RT_ZERO(Msg);
1354 Msg.hdr.result = VERR_WRONG_ORDER;
1355 Msg.hdr.u32ClientID = pCtx->uClientID;
1356 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA;
1357
1358 if (pCtx->uProtocol <= 1)
1359 {
1360 Msg.hdr.cParms = 5;
1361
1362 Msg.u.v1.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1));
1363 Msg.u.v1.cbName.SetUInt32((uint32_t)(strPath.length() + 1));
1364 Msg.u.v1.fMode.SetUInt32(obj.GetMode());
1365 }
1366 else
1367 {
1368 /* Only send context ID, file chunk + chunk size. */
1369 Msg.hdr.cParms = 3;
1370
1371 Msg.u.v2.uContext.SetUInt32(0); /** @todo Set context ID. */
1372 }
1373
1374 uint64_t cbToReadTotal = obj.GetSize();
1375 uint64_t cbWrittenTotal = 0;
1376 while (cbToReadTotal)
1377 {
1378 uint32_t cbToRead = RT_MIN(cbToReadTotal, cbBuf);
1379 uint32_t cbRead = 0;
1380 if (cbToRead)
1381 rc = obj.Read(pvBuf, cbToRead, &cbRead);
1382
1383 LogFlowFunc(("cbToReadTotal=%RU64, cbToRead=%RU32, cbRead=%RU32, rc=%Rrc\n",
1384 cbToReadTotal, cbToRead, cbRead, rc));
1385
1386 if ( RT_SUCCESS(rc)
1387 && cbRead)
1388 {
1389 if (pCtx->uProtocol <= 1)
1390 {
1391 Msg.u.v1.pvData.SetPtr(pvBuf, cbRead);
1392 Msg.u.v1.cbData.SetUInt32(cbRead);
1393 }
1394 else
1395 {
1396 Msg.u.v2.pvData.SetPtr(pvBuf, cbRead);
1397 Msg.u.v2.cbData.SetUInt32(cbRead);
1398 }
1399
1400 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1401 if (RT_SUCCESS(rc))
1402 rc = Msg.hdr.result;
1403 }
1404
1405 if (RT_FAILURE(rc))
1406 {
1407 LogFlowFunc(("Reading failed with rc=%Rrc\n", rc));
1408 break;
1409 }
1410
1411 Assert(cbRead <= cbToReadTotal);
1412 cbToReadTotal -= cbRead;
1413 cbWrittenTotal += cbRead;
1414
1415 LogFlowFunc(("%RU64/%RU64 -- %RU8%%\n", cbWrittenTotal, obj.GetSize(), cbWrittenTotal * 100 / obj.GetSize()));
1416 };
1417 }
1418
1419 obj.Close();
1420
1421 RTMemFree(pvBuf);
1422
1423 LogFlowFuncLeaveRC(rc);
1424 return rc;
1425}
1426
1427static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject &obj)
1428{
1429 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1430
1431 int rc;
1432
1433 switch (obj.GetType())
1434 {
1435 case DnDURIObject::Directory:
1436 rc = vbglR3DnDGHSendDir(pCtx, obj);
1437 break;
1438
1439 case DnDURIObject::File:
1440 rc = vbglR3DnDGHSendFile(pCtx, obj);
1441 break;
1442
1443 default:
1444 AssertMsgFailed(("URI type %ld not implemented\n", obj.GetType()));
1445 rc = VERR_NOT_IMPLEMENTED;
1446 break;
1447 }
1448
1449 return rc;
1450}
1451
1452static int vbglR3DnDGHProcessURIMessages(PVBGLR3GUESTDNDCMDCTX pCtx,
1453 const void *pvData, uint32_t cbData)
1454{
1455 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1456 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1457 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1458
1459 RTCList<RTCString> lstPaths =
1460 RTCString((const char *)pvData, cbData).split("\r\n");
1461
1462 DnDURIList lstURI;
1463 int rc = lstURI.AppendURIPathsFromList(lstPaths, 0 /* fFlags */);
1464 if (RT_SUCCESS(rc))
1465 {
1466 /* Send metadata; in this case it's the (non-recursive) file/directory
1467 * URI list the host needs to know to initialize the drag'n drop operation. */
1468 RTCString strRootDest = lstURI.RootToString();
1469 Assert(strRootDest.isNotEmpty());
1470
1471 void *pvToSend = (void *)strRootDest.c_str();
1472 uint32_t cbToSend = (uint32_t)strRootDest.length() + 1;
1473
1474 rc = vbglR3DnDGHSendDataInternal(pCtx, pvToSend, cbToSend,
1475 /* Include total bytes of all file paths,
1476 * file sizes etc. */
1477 lstURI.TotalBytes());
1478 }
1479
1480 if (RT_SUCCESS(rc))
1481 {
1482 while (!lstURI.IsEmpty())
1483 {
1484 DnDURIObject &nextObj = lstURI.First();
1485
1486 rc = vbglR3DnDGHSendURIObject(pCtx, nextObj);
1487 if (RT_FAILURE(rc))
1488 break;
1489
1490 lstURI.RemoveFirst();
1491 }
1492 }
1493
1494 return rc;
1495}
1496
1497VBGLR3DECL(int) VbglR3DnDGHSendData(PVBGLR3GUESTDNDCMDCTX pCtx, const char *pszFormat, void *pvData, uint32_t cbData)
1498{
1499 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1500 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
1501 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1502 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1503
1504 LogFlowFunc(("pszFormat=%s, pvData=%p, cbData=%RU32\n", pszFormat, pvData, cbData));
1505
1506 int rc;
1507 if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat)))
1508 {
1509 rc = vbglR3DnDGHProcessURIMessages(pCtx, pvData, cbData);
1510 }
1511 else
1512 {
1513 rc = vbglR3DnDGHSendDataInternal(pCtx, pvData, cbData, 0 /* cbAdditionalData */);
1514 }
1515
1516 if (RT_FAILURE(rc))
1517 {
1518 int rc2 = VbglR3DnDGHSendError(pCtx, rc);
1519 if (RT_FAILURE(rc2))
1520 LogFlowFunc(("Unable to send error (%Rrc) to host, rc=%Rrc\n", rc, rc2));
1521 }
1522
1523 return rc;
1524}
1525
1526VBGLR3DECL(int) VbglR3DnDGHSendError(PVBGLR3GUESTDNDCMDCTX pCtx, int rcErr)
1527{
1528 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1529
1530 DragAndDropSvc::VBOXDNDGHEVTERRORMSG Msg;
1531 RT_ZERO(Msg);
1532 Msg.hdr.result = VERR_WRONG_ORDER;
1533 Msg.hdr.u32ClientID = pCtx->uClientID;
1534 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_EVT_ERROR;
1535 Msg.hdr.cParms = 1;
1536
1537 Msg.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1538
1539 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1540 if (RT_SUCCESS(rc))
1541 rc = Msg.hdr.result;
1542
1543 LogFlowFunc(("Sending error %Rrc returned with rc=%Rrc\n", rcErr, rc));
1544 return rc;
1545}
1546#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1547
Note: See TracBrowser for help on using the repository browser.

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