VirtualBox

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

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

DnD: Update.

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

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