VirtualBox

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

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

DnD: Renamed DNDDIRDROPPEDFILES to DnDDroppedFiles and restructured it for being a class.

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

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