VirtualBox

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

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

DnD: Protocol overhaul with versioning added which now can communicate with Main.

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