VirtualBox

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

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

DnD: Bugfixes.

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