VirtualBox

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

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

scm: fixes in previous cleanup run.

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