VirtualBox

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

Last change on this file since 49950 was 49930, checked in by vboxsync, 11 years ago

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 39.5 KB
Line 
1/* $Id: VBoxGuestR3LibDragAndDrop.cpp 49930 2013-12-16 13:02:19Z vboxsync $ */
2/** @file
3 * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Drag & Drop.
4 */
5
6/*
7 * Copyright (C) 2011-2013 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#include "VBGLR3Internal.h"
41#include "VBox/HostServices/DragAndDropSvc.h"
42
43/* Here all the communication with the host over HGCM is handled platform
44 * neutral. Also the receiving of URIs content (directory trees and files) is
45 * done here. So the platform code of the guests, should not take care of that.
46 *
47 * Todo:
48 * - Sending dirs/files in the G->H case
49 * - Maybe the EOL converting of text mime-types (not fully sure, eventually
50 * better done on the host side)
51 */
52
53static int vbglR3DnDPathSanitize(char *pszPath, size_t cbPath);
54
55/******************************************************************************
56 * Private internal functions *
57 ******************************************************************************/
58
59static int vbglR3DnDCreateDropDir(char* pszDropDir, size_t cbSize)
60{
61 AssertPtrReturn(pszDropDir, VERR_INVALID_POINTER);
62 AssertReturn(cbSize, VERR_INVALID_PARAMETER);
63
64 /** @todo On Windows we also could use the registry to override
65 * this path, on Posix a dotfile and/or a guest property
66 * can be used. */
67
68 /* Get the users temp directory. Don't use the user's root directory (or
69 * something inside it) because we don't know for how long/if the data will
70 * be kept after the guest OS used it. */
71 int rc = RTPathTemp(pszDropDir, cbSize);
72 if (RT_FAILURE(rc))
73 return rc;
74
75 /* Append our base drop directory. */
76 rc = RTPathAppend(pszDropDir, cbSize, "VirtualBox Dropped Files");
77 if (RT_FAILURE(rc))
78 return rc;
79
80 /* Create it when necessary. */
81 if (!RTDirExists(pszDropDir))
82 {
83 rc = RTDirCreateFullPath(pszDropDir, RTFS_UNIX_IRWXU);
84 if (RT_FAILURE(rc))
85 return rc;
86 }
87
88 /* The actually drop directory consist of the current time stamp and a
89 * unique number when necessary. */
90 char pszTime[64];
91 RTTIMESPEC time;
92 if (!RTTimeSpecToString(RTTimeNow(&time), pszTime, sizeof(pszTime)))
93 return VERR_BUFFER_OVERFLOW;
94 rc = vbglR3DnDPathSanitize(pszTime, sizeof(pszTime));
95 if (RT_FAILURE(rc))
96 return rc;
97
98 rc = RTPathAppend(pszDropDir, cbSize, pszTime);
99 if (RT_FAILURE(rc))
100 return rc;
101
102 /* Create it (only accessible by the current user) */
103 return RTDirCreateUniqueNumbered(pszDropDir, cbSize, RTFS_UNIX_IRWXU, 3, '-');
104}
105
106static int vbglR3DnDQueryNextHostMessageType(uint32_t uClientId, uint32_t *puMsg, uint32_t *pcParms, bool fWait)
107{
108 AssertPtrReturn(puMsg, VERR_INVALID_POINTER);
109 AssertPtrReturn(pcParms, VERR_INVALID_POINTER);
110
111 DragAndDropSvc::VBOXDNDNEXTMSGMSG Msg;
112 RT_ZERO(Msg);
113 Msg.hdr.result = VERR_WRONG_ORDER;
114 Msg.hdr.u32ClientID = uClientId;
115 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG;
116 Msg.hdr.cParms = 3;
117
118 Msg.msg.SetUInt32(0);
119 Msg.num_parms.SetUInt32(0);
120 Msg.block.SetUInt32(fWait);
121
122 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
123 if (RT_SUCCESS(rc))
124 {
125 rc = Msg.hdr.result;
126 if (RT_SUCCESS(rc))
127 {
128 /* Fetch results */
129 rc = Msg.msg.GetUInt32(puMsg); AssertRC(rc);
130 rc = Msg.num_parms.GetUInt32(pcParms); AssertRC(rc);
131 }
132 }
133
134 return rc;
135}
136
137static int vbglR3DnDHGProcessActionMessage(uint32_t uClientId,
138 uint32_t uMsg,
139 uint32_t *puScreenId,
140 uint32_t *puX,
141 uint32_t *puY,
142 uint32_t *puDefAction,
143 uint32_t *puAllActions,
144 char *pszFormats,
145 uint32_t cbFormats,
146 uint32_t *pcbFormatsRecv)
147{
148 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
149 AssertPtrReturn(puX, VERR_INVALID_POINTER);
150 AssertPtrReturn(puY, VERR_INVALID_POINTER);
151 AssertPtrReturn(puDefAction, VERR_INVALID_POINTER);
152 AssertPtrReturn(puAllActions, VERR_INVALID_POINTER);
153 AssertPtrReturn(pszFormats, VERR_INVALID_POINTER);
154 AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
155 AssertPtrReturn(pcbFormatsRecv, VERR_INVALID_POINTER);
156
157 DragAndDropSvc::VBOXDNDHGACTIONMSG Msg;
158 RT_ZERO(Msg);
159 Msg.hdr.u32ClientID = uClientId;
160 Msg.hdr.u32Function = uMsg;
161 Msg.hdr.cParms = 7;
162
163 Msg.uScreenId.SetUInt32(0);
164 Msg.uX.SetUInt32(0);
165 Msg.uY.SetUInt32(0);
166 Msg.uDefAction.SetUInt32(0);
167 Msg.uAllActions.SetUInt32(0);
168 Msg.pvFormats.SetPtr(pszFormats, cbFormats);
169 Msg.cFormats.SetUInt32(0);
170
171 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
172 if (RT_SUCCESS(rc))
173 {
174 rc = Msg.hdr.result;
175 if (RT_SUCCESS(rc))
176 {
177 /* Fetch results */
178 rc = Msg.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
179 rc = Msg.uX.GetUInt32(puX); AssertRC(rc);
180 rc = Msg.uY.GetUInt32(puY); AssertRC(rc);
181 rc = Msg.uDefAction.GetUInt32(puDefAction); AssertRC(rc);
182 rc = Msg.uAllActions.GetUInt32(puAllActions); AssertRC(rc);
183 rc = Msg.cFormats.GetUInt32(pcbFormatsRecv); AssertRC(rc);
184 /* A little bit paranoia */
185 AssertReturn(cbFormats >= *pcbFormatsRecv, VERR_TOO_MUCH_DATA);
186 }
187 }
188
189 return rc;
190}
191
192static int vbglR3DnDHGProcessLeaveMessage(uint32_t uClientId)
193{
194 DragAndDropSvc::VBOXDNDHGLEAVEMSG Msg;
195 RT_ZERO(Msg);
196 Msg.hdr.u32ClientID = uClientId;
197 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_EVT_LEAVE;
198 Msg.hdr.cParms = 0;
199
200 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
201 if (RT_SUCCESS(rc))
202 rc = Msg.hdr.result;
203
204 return rc;
205}
206
207static int vbglR3DnDHGProcessCancelMessage(uint32_t uClientId)
208{
209 DragAndDropSvc::VBOXDNDHGCANCELMSG Msg;
210 RT_ZERO(Msg);
211 Msg.hdr.u32ClientID = uClientId;
212 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_EVT_CANCEL;
213 Msg.hdr.cParms = 0;
214
215 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
216 if (RT_SUCCESS(rc))
217 rc = Msg.hdr.result;
218
219 return rc;
220}
221
222static int vbglR3DnDHGProcessSendDirMessage(uint32_t uClientId,
223 char *pszDirname,
224 uint32_t cbDirname,
225 uint32_t *pcbDirnameRecv,
226 uint32_t *pfMode)
227{
228 AssertPtrReturn(pszDirname, VERR_INVALID_POINTER);
229 AssertReturn(cbDirname, VERR_INVALID_PARAMETER);
230 AssertPtrReturn(pcbDirnameRecv, VERR_INVALID_POINTER);
231 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
232
233 DragAndDropSvc::VBOXDNDHGSENDDIRMSG Msg;
234 RT_ZERO(Msg);
235 Msg.hdr.u32ClientID = uClientId;
236 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_SND_DIR;
237 Msg.hdr.cParms = 3;
238
239 Msg.pvName.SetPtr(pszDirname, cbDirname);
240 Msg.cName.SetUInt32(0);
241 Msg.fMode.SetUInt32(0);
242
243 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
244 if (RT_SUCCESS(rc))
245 {
246 rc = Msg.hdr.result;
247 if (RT_SUCCESS(Msg.hdr.result))
248 {
249 /* Fetch results */
250 rc = Msg.cName.GetUInt32(pcbDirnameRecv); AssertRC(rc);
251 rc = Msg.fMode.GetUInt32(pfMode); AssertRC(rc);
252 /* A little bit paranoia */
253 AssertReturn(cbDirname >= *pcbDirnameRecv, VERR_TOO_MUCH_DATA);
254 }
255 }
256
257 return rc;
258}
259
260static int vbglR3DnDHGProcessSendFileMessage(uint32_t uClientId,
261 char *pszFilename,
262 uint32_t cbFilename,
263 uint32_t *pcbFilenameRecv,
264 void *pvData,
265 uint32_t cbData,
266 uint32_t *pcbDataRecv,
267 uint32_t *pfMode)
268{
269 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
270 AssertReturn(cbFilename, VERR_INVALID_PARAMETER);
271 AssertPtrReturn(pcbFilenameRecv, VERR_INVALID_POINTER);
272 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
273 AssertReturn(cbData, VERR_INVALID_PARAMETER);
274 AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER);
275 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
276
277 DragAndDropSvc::VBOXDNDHGSENDFILEMSG Msg;
278 RT_ZERO(Msg);
279 Msg.hdr.u32ClientID = uClientId;
280 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_SND_FILE;
281 Msg.hdr.cParms = 5;
282
283 Msg.pvName.SetPtr(pszFilename, cbFilename);
284 Msg.cName.SetUInt32(0);
285 Msg.pvData.SetPtr(pvData, cbData);
286 Msg.cData.SetUInt32(0);
287 Msg.fMode.SetUInt32(0);
288
289 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
290 if (RT_SUCCESS(rc))
291 {
292 rc = Msg.hdr.result;
293 if (RT_SUCCESS(rc))
294 {
295 /* Fetch results */
296 rc = Msg.cName.GetUInt32(pcbFilenameRecv); AssertRC(rc);
297 rc = Msg.cData.GetUInt32(pcbDataRecv); AssertRC(rc);
298 rc = Msg.fMode.GetUInt32(pfMode); AssertRC(rc);
299 /* A little bit paranoia */
300 AssertReturn(cbFilename >= *pcbFilenameRecv, VERR_TOO_MUCH_DATA);
301 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
302 }
303 }
304
305 return rc;
306}
307
308static int vbglR3DnDHGProcessURIMessages(uint32_t uClientId,
309 uint32_t *puScreenId,
310 char *pszFormat,
311 uint32_t cbFormat,
312 uint32_t *pcbFormatRecv,
313 void **ppvData,
314 uint32_t cbData,
315 size_t *pcbDataRecv)
316{
317 AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
318 AssertPtrReturn(cbData, VERR_INVALID_PARAMETER);
319 AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER);
320
321 /* Make a string list out of the uri data. */
322 RTCList<RTCString> uriList = RTCString(static_cast<char*>(*ppvData), *pcbDataRecv - 1).split("\r\n");
323 if (uriList.isEmpty())
324 return VINF_SUCCESS;
325
326 uint32_t cbTmpData = _1M * 10; /** @todo r=andy 10MB, uh, really?? */
327 void *pvTmpData = RTMemAlloc(cbTmpData);
328 if (!pvTmpData)
329 return VERR_NO_MEMORY;
330
331 /* Create and query the drop target directory. */
332 char pszDropDir[RTPATH_MAX];
333 int rc = vbglR3DnDCreateDropDir(pszDropDir, sizeof(pszDropDir));
334 if (RT_FAILURE(rc))
335 {
336 RTMemFree(pvTmpData);
337 return rc;
338 }
339
340 /* Patch the old drop data with the new drop directory, so the drop target
341 * can find the files. */
342 RTCList<RTCString> guestUriList;
343 for (size_t i = 0; i < uriList.size(); ++i)
344 {
345 const RTCString &strUri = uriList.at(i);
346 /* Query the path component of a file URI. If this hasn't a
347 * file scheme, null is returned. */
348 char *pszFilePath = RTUriFilePath(strUri.c_str(), URI_FILE_FORMAT_AUTO);
349 if (pszFilePath)
350 {
351 rc = vbglR3DnDPathSanitize(pszFilePath, strlen(pszFilePath));
352 if (RT_FAILURE(rc))
353 break;
354
355 /** @todo Use RTPathJoin? */
356 RTCString strFullPath = RTCString().printf("%s%c%s", pszDropDir, RTPATH_SLASH, pszFilePath);
357 char *pszNewUri = RTUriFileCreate(strFullPath.c_str());
358 if (pszNewUri)
359 {
360 guestUriList.append(pszNewUri);
361 RTStrFree(pszNewUri);
362 }
363 }
364 else
365 guestUriList.append(strUri);
366 }
367
368 if (RT_SUCCESS(rc))
369 {
370 /* Cleanup the old data and write the new data back to the event. */
371 RTMemFree(*ppvData);
372 RTCString newData = RTCString::join(guestUriList, "\r\n") + "\r\n";
373
374 *ppvData = RTStrDupN(newData.c_str(), newData.length());
375 *pcbDataRecv = newData.length() + 1;
376 }
377
378 /* Lists for holding created files & directories in the case of a
379 * rollback. */
380 RTCList<RTCString> guestDirList;
381 RTCList<RTCString> guestFileList;
382 char pszPathname[RTPATH_MAX];
383 uint32_t cbPathname = 0;
384 bool fLoop = RT_SUCCESS(rc); /* No error occurred yet? */
385 while (fLoop)
386 {
387 uint32_t uNextMsg;
388 uint32_t cNextParms;
389 rc = vbglR3DnDQueryNextHostMessageType(uClientId, &uNextMsg, &cNextParms, false /* fWait */);
390 if (RT_SUCCESS(rc))
391 {
392 switch (uNextMsg)
393 {
394 case DragAndDropSvc::HOST_DND_HG_SND_DIR:
395 {
396 uint32_t fMode = 0;
397 rc = vbglR3DnDHGProcessSendDirMessage(uClientId,
398 pszPathname,
399 sizeof(pszPathname),
400 &cbPathname,
401 &fMode);
402 if (RT_SUCCESS(rc))
403 rc = vbglR3DnDPathSanitize(pszPathname, sizeof(pszPathname));
404 if (RT_SUCCESS(rc))
405 {
406 char *pszNewDir = RTPathJoinA(pszDropDir, pszPathname);
407 if (pszNewDir)
408 {
409 rc = RTDirCreate(pszNewDir, (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRWXU, 0);
410 if (!guestDirList.contains(pszNewDir))
411 guestDirList.append(pszNewDir);
412
413 RTStrFree(pszNewDir);
414 }
415 else
416 rc = VERR_NO_MEMORY;
417 }
418 break;
419 }
420 case DragAndDropSvc::HOST_DND_HG_SND_FILE:
421 {
422 uint32_t cbDataRecv;
423 uint32_t fMode = 0;
424 rc = vbglR3DnDHGProcessSendFileMessage(uClientId,
425 pszPathname,
426 sizeof(pszPathname),
427 &cbPathname,
428 pvTmpData,
429 cbTmpData,
430 &cbDataRecv,
431 &fMode);
432 if (RT_SUCCESS(rc))
433 rc = vbglR3DnDPathSanitize(pszPathname, sizeof(pszPathname));
434 if (RT_SUCCESS(rc))
435 {
436 char *pszNewFile = RTPathJoinA(pszDropDir, pszPathname);
437 if (pszNewFile)
438 {
439 RTFILE hFile;
440 /** @todo r=andy Keep the file open and locked during the actual file transfer. Otherwise this will
441 * create all sorts of funny races because we don't know if the guest has
442 * modified the file in between the file data send calls. */
443 rc = RTFileOpen(&hFile, pszNewFile,
444 RTFILE_O_WRITE | RTFILE_O_APPEND | RTFILE_O_DENY_ALL | RTFILE_O_OPEN_CREATE);
445 if (RT_SUCCESS(rc))
446 {
447 rc = RTFileSeek(hFile, 0, RTFILE_SEEK_END, NULL);
448 if (RT_SUCCESS(rc))
449 {
450 rc = RTFileWrite(hFile, pvTmpData, cbDataRecv, 0);
451 /* Valid UNIX mode? */
452 if ( RT_SUCCESS(rc)
453 && (fMode & RTFS_UNIX_MASK))
454 rc = RTFileSetMode(hFile, (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR);
455 }
456 RTFileClose(hFile);
457 if (!guestFileList.contains(pszNewFile))
458 guestFileList.append(pszNewFile);
459 }
460
461 RTStrFree(pszNewFile);
462 }
463 else
464 rc = VERR_NO_MEMORY;
465 }
466 break;
467 }
468 case DragAndDropSvc::HOST_DND_HG_EVT_CANCEL:
469 {
470 rc = vbglR3DnDHGProcessCancelMessage(uClientId);
471 if (RT_SUCCESS(rc))
472 rc = VERR_CANCELLED;
473 /* Break out of the loop. */
474 }
475 default:
476 fLoop = false;
477 break;
478 }
479 }
480 else
481 {
482 if (rc == VERR_NO_DATA)
483 rc = VINF_SUCCESS;
484 break;
485 }
486 } /* while */
487
488 RTMemFree(pvTmpData);
489
490 /* Cleanup on failure or if the user has canceled. */
491 if (RT_FAILURE(rc))
492 {
493 /* Remove any stuff created. */
494 for (size_t i = 0; i < guestFileList.size(); ++i)
495 RTFileDelete(guestFileList.at(i).c_str());
496 for (size_t i = 0; i < guestDirList.size(); ++i)
497 RTDirRemove(guestDirList.at(i).c_str());
498 RTDirRemove(pszDropDir);
499 }
500
501 return rc;
502}
503
504static int vbglR3DnDHGProcessDataMessageInternal(uint32_t uClientId,
505 uint32_t *puScreenId,
506 char *pszFormat,
507 uint32_t cbFormat,
508 uint32_t *pcbFormatRecv,
509 void *pvData,
510 uint32_t cbData,
511 uint32_t *pcbDataRecv)
512{
513 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
514 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
515 AssertReturn(cbFormat, VERR_INVALID_PARAMETER);
516 AssertPtrReturn(pcbFormatRecv, VERR_INVALID_POINTER);
517 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
518 AssertReturn(cbData, VERR_INVALID_PARAMETER);
519 AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER);
520
521 DragAndDropSvc::VBOXDNDHGSENDDATAMSG Msg;
522 RT_ZERO(Msg);
523 Msg.hdr.u32ClientID = uClientId;
524 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_SND_DATA;
525 Msg.hdr.cParms = 5;
526
527 Msg.uScreenId.SetUInt32(0);
528 Msg.pvFormat.SetPtr(pszFormat, cbFormat);
529 Msg.cFormat.SetUInt32(0);
530 Msg.pvData.SetPtr(pvData, cbData);
531 Msg.cData.SetUInt32(0);
532
533 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
534 if (RT_SUCCESS(rc))
535 {
536 rc = Msg.hdr.result;
537 if (RT_SUCCESS(rc)
538 || rc == VERR_BUFFER_OVERFLOW)
539 {
540 /* Fetch results */
541 rc = Msg.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
542 rc = Msg.cFormat.GetUInt32(pcbFormatRecv); AssertRC(rc);
543 rc = Msg.cData.GetUInt32(pcbDataRecv); AssertRC(rc);
544 /* A little bit paranoia */
545 AssertReturn(cbFormat >= *pcbFormatRecv, VERR_TOO_MUCH_DATA);
546 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
547 }
548 }
549
550 return rc;
551}
552
553static int vbglR3DnDHGProcessMoreDataMessageInternal(uint32_t uClientId,
554 void *pvData,
555 uint32_t cbData,
556 uint32_t *pcbDataRecv)
557{
558 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
559 AssertReturn(cbData, VERR_INVALID_PARAMETER);
560 AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER);
561
562 DragAndDropSvc::VBOXDNDHGSENDMOREDATAMSG Msg;
563 RT_ZERO(Msg);
564 Msg.hdr.u32ClientID = uClientId;
565 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_HG_SND_MORE_DATA;
566 Msg.hdr.cParms = 2;
567
568 Msg.pvData.SetPtr(pvData, cbData);
569 Msg.cData.SetUInt32(0);
570
571 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
572 if (RT_SUCCESS(rc))
573 {
574 rc = Msg.hdr.result;
575 if ( RT_SUCCESS(rc)
576 || rc == VERR_BUFFER_OVERFLOW)
577 {
578 /* Fetch results */
579 rc = Msg.cData.GetUInt32(pcbDataRecv); AssertRC(rc);
580 /* A little bit paranoia */
581 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
582 }
583 }
584 return rc;
585}
586
587static int vbglR3DnDHGProcessSendDataMessageLoop(uint32_t uClientId,
588 uint32_t *puScreenId,
589 char *pszFormat,
590 uint32_t cbFormat,
591 uint32_t *pcbFormatRecv,
592 void **ppvData,
593 uint32_t cbData,
594 size_t *pcbDataRecv)
595{
596 uint32_t cbDataRecv = 0;
597 int rc = vbglR3DnDHGProcessDataMessageInternal(uClientId,
598 puScreenId,
599 pszFormat,
600 cbFormat,
601 pcbFormatRecv,
602 *ppvData,
603 cbData,
604 &cbDataRecv);
605 uint32_t cbAllDataRecv = cbDataRecv;
606 while (rc == VERR_BUFFER_OVERFLOW)
607 {
608 uint32_t uNextMsg;
609 uint32_t cNextParms;
610 rc = vbglR3DnDQueryNextHostMessageType(uClientId, &uNextMsg, &cNextParms, false);
611 if (RT_SUCCESS(rc))
612 {
613 switch(uNextMsg)
614 {
615 case DragAndDropSvc::HOST_DND_HG_SND_MORE_DATA:
616 {
617 *ppvData = RTMemRealloc(*ppvData, cbAllDataRecv + cbData);
618 if (!*ppvData)
619 {
620 rc = VERR_NO_MEMORY;
621 break;
622 }
623 rc = vbglR3DnDHGProcessMoreDataMessageInternal(uClientId,
624 &((char*)*ppvData)[cbAllDataRecv],
625 cbData,
626 &cbDataRecv);
627 cbAllDataRecv += cbDataRecv;
628 break;
629 }
630 case DragAndDropSvc::HOST_DND_HG_EVT_CANCEL:
631 default:
632 {
633 rc = vbglR3DnDHGProcessCancelMessage(uClientId);
634 if (RT_SUCCESS(rc))
635 rc = VERR_CANCELLED;
636 break;
637 }
638 }
639 }
640 }
641 if (RT_SUCCESS(rc))
642 *pcbDataRecv = cbAllDataRecv;
643
644 return rc;
645}
646
647static int vbglR3DnDHGProcessSendDataMessage(uint32_t uClientId,
648 uint32_t *puScreenId,
649 char *pszFormat,
650 uint32_t cbFormat,
651 uint32_t *pcbFormatRecv,
652 void **ppvData,
653 uint32_t cbData,
654 size_t *pcbDataRecv)
655{
656 int rc = vbglR3DnDHGProcessSendDataMessageLoop(uClientId,
657 puScreenId,
658 pszFormat,
659 cbFormat,
660 pcbFormatRecv,
661 ppvData,
662 cbData,
663 pcbDataRecv);
664 if (RT_SUCCESS(rc))
665 {
666 /* Check if this is a uri-event. If so, let VbglR3 do all the actual
667 * data transfer + file /directory creation internally without letting
668 * the caller know. */
669 if (RTStrNICmp(pszFormat, "text/uri-list", *pcbFormatRecv) == 0)
670 rc = vbglR3DnDHGProcessURIMessages(uClientId,
671 puScreenId,
672 pszFormat,
673 cbFormat,
674 pcbFormatRecv,
675 ppvData,
676 cbData,
677 pcbDataRecv);
678 }
679
680 return rc;
681}
682
683static int vbglR3DnDGHProcessRequestPendingMessage(uint32_t uClientId,
684 uint32_t *puScreenId)
685{
686 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
687
688 DragAndDropSvc::VBOXDNDGHREQPENDINGMSG Msg;
689 RT_ZERO(Msg);
690 Msg.hdr.u32ClientID = uClientId;
691 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_GH_REQ_PENDING;
692 Msg.hdr.cParms = 1;
693
694 Msg.uScreenId.SetUInt32(0);
695
696 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
697 if (RT_SUCCESS(rc))
698 {
699 rc = Msg.hdr.result;
700 if (RT_SUCCESS(rc))
701 {
702 /* Fetch results */
703 rc = Msg.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
704 }
705 }
706
707 return rc;
708}
709
710static int vbglR3DnDGHProcessDroppedMessage(uint32_t uClientId,
711 char *pszFormat,
712 uint32_t cbFormat,
713 uint32_t *pcbFormatRecv,
714 uint32_t *puAction)
715{
716 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
717 AssertReturn(cbFormat, VERR_INVALID_PARAMETER);
718 AssertPtrReturn(pcbFormatRecv, VERR_INVALID_POINTER);
719 AssertPtrReturn(puAction, VERR_INVALID_POINTER);
720
721 DragAndDropSvc::VBOXDNDGHDROPPEDMSG Msg;
722 RT_ZERO(Msg);
723 Msg.hdr.u32ClientID = uClientId;
724 Msg.hdr.u32Function = DragAndDropSvc::HOST_DND_GH_EVT_DROPPED;
725 Msg.hdr.cParms = 3;
726
727 Msg.pvFormat.SetPtr(pszFormat, cbFormat);
728 Msg.cFormat.SetUInt32(0);
729 Msg.uAction.SetUInt32(0);
730
731 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
732 if (RT_SUCCESS(rc))
733 {
734 rc = Msg.hdr.result;
735 if (RT_SUCCESS(rc))
736 {
737 /* Fetch results */
738 rc = Msg.cFormat.GetUInt32(pcbFormatRecv); AssertRC(rc);
739 rc = Msg.uAction.GetUInt32(puAction); AssertRC(rc);
740 /* A little bit paranoia */
741 AssertReturn(cbFormat >= *pcbFormatRecv, VERR_TOO_MUCH_DATA);
742 }
743 }
744
745 return rc;
746}
747
748static int vbglR3DnDPathSanitize(char *pszPath, size_t cbPath)
749{
750 int rc = VINF_SUCCESS;
751#ifdef RT_OS_WINDOWS
752 /* Filter out characters not allowed on Windows platforms, put in by
753 RTTimeSpecToString(). */
754 /** @todo Use something like RTPathSanitize() when available. Later. */
755 RTUNICP aCpSet[] =
756 { ' ', ' ', '(', ')', '-', '.', '0', '9', 'A', 'Z', 'a', 'z', '_', '_',
757 0xa0, 0xd7af, '\0' };
758 ssize_t cReplaced = RTStrPurgeComplementSet(pszPath, aCpSet, '_' /* Replacement */);
759 if (cReplaced < 0)
760 rc = VERR_INVALID_UTF8_ENCODING;
761#endif
762 return rc;
763}
764
765/******************************************************************************
766 * Public functions *
767 ******************************************************************************/
768
769VBGLR3DECL(int) VbglR3DnDConnect(uint32_t *pu32ClientId)
770{
771 AssertPtrReturn(pu32ClientId, VERR_INVALID_POINTER);
772
773 /* Initialize header */
774 VBoxGuestHGCMConnectInfo Info;
775 RT_ZERO(Info.Loc.u);
776 Info.result = VERR_WRONG_ORDER;
777 Info.u32ClientID = UINT32_MAX; /* try make valgrind shut up. */
778 /* Initialize parameter */
779 Info.Loc.type = VMMDevHGCMLoc_LocalHost_Existing;
780 int rc = RTStrCopy(Info.Loc.u.host.achName, sizeof(Info.Loc.u.host.achName), "VBoxDragAndDropSvc");
781 if (RT_FAILURE(rc)) return rc;
782 /* Do request */
783 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CONNECT, &Info, sizeof(Info));
784 if (RT_SUCCESS(rc))
785 {
786 rc = Info.result;
787 if (RT_SUCCESS(rc))
788 *pu32ClientId = Info.u32ClientID;
789 }
790 return rc;
791}
792
793VBGLR3DECL(int) VbglR3DnDDisconnect(uint32_t u32ClientId)
794{
795 VBoxGuestHGCMDisconnectInfo Info;
796 Info.result = VERR_WRONG_ORDER;
797 Info.u32ClientID = u32ClientId;
798
799 /* Do request */
800 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_DISCONNECT, &Info, sizeof(Info));
801 if (RT_SUCCESS(rc))
802 rc = Info.result;
803
804 return rc;
805}
806
807VBGLR3DECL(int) VbglR3DnDProcessNextMessage(uint32_t u32ClientId, CPVBGLR3DNDHGCMEVENT pEvent)
808{
809 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
810
811 uint32_t uMsg = 0;
812 uint32_t uNumParms = 0;
813 const uint32_t ccbFormats = _64K;
814 const uint32_t ccbData = _64K;
815 int rc = vbglR3DnDQueryNextHostMessageType(u32ClientId, &uMsg, &uNumParms,
816 true /* fWait */);
817 if (RT_SUCCESS(rc))
818 {
819 switch(uMsg)
820 {
821 case DragAndDropSvc::HOST_DND_HG_EVT_ENTER:
822 case DragAndDropSvc::HOST_DND_HG_EVT_MOVE:
823 case DragAndDropSvc::HOST_DND_HG_EVT_DROPPED:
824 {
825 pEvent->uType = uMsg;
826 pEvent->pszFormats = static_cast<char*>(RTMemAlloc(ccbFormats));
827 if (!pEvent->pszFormats)
828 rc = VERR_NO_MEMORY;
829
830 if (RT_SUCCESS(rc))
831 rc = vbglR3DnDHGProcessActionMessage(u32ClientId,
832 uMsg,
833 &pEvent->uScreenId,
834 &pEvent->u.a.uXpos,
835 &pEvent->u.a.uYpos,
836 &pEvent->u.a.uDefAction,
837 &pEvent->u.a.uAllActions,
838 pEvent->pszFormats,
839 ccbFormats,
840 &pEvent->cbFormats);
841 break;
842 }
843 case DragAndDropSvc::HOST_DND_HG_EVT_LEAVE:
844 {
845 pEvent->uType = uMsg;
846 rc = vbglR3DnDHGProcessLeaveMessage(u32ClientId);
847 break;
848 }
849 case DragAndDropSvc::HOST_DND_HG_SND_DATA:
850 {
851 pEvent->uType = uMsg;
852 pEvent->pszFormats = static_cast<char*>(RTMemAlloc(ccbFormats));
853 if (!pEvent->pszFormats)
854 rc = VERR_NO_MEMORY;
855
856 if (RT_SUCCESS(rc))
857 {
858 pEvent->u.b.pvData = RTMemAlloc(ccbData);
859 if (!pEvent->u.b.pvData)
860 {
861 RTMemFree(pEvent->pszFormats);
862 pEvent->pszFormats = NULL;
863 rc = VERR_NO_MEMORY;
864 }
865 }
866
867 if (RT_SUCCESS(rc))
868 rc = vbglR3DnDHGProcessSendDataMessage(u32ClientId,
869 &pEvent->uScreenId,
870 pEvent->pszFormats,
871 ccbFormats,
872 &pEvent->cbFormats,
873 &pEvent->u.b.pvData,
874 ccbData,
875 &pEvent->u.b.cbData);
876 break;
877 }
878 case DragAndDropSvc::HOST_DND_HG_EVT_CANCEL:
879 {
880 pEvent->uType = uMsg;
881 rc = vbglR3DnDHGProcessCancelMessage(u32ClientId);
882 break;
883 }
884#ifdef VBOX_WITH_DRAG_AND_DROP_GH
885 case DragAndDropSvc::HOST_DND_GH_REQ_PENDING:
886 {
887 pEvent->uType = uMsg;
888 rc = vbglR3DnDGHProcessRequestPendingMessage(u32ClientId,
889 &pEvent->uScreenId);
890 break;
891 }
892 case DragAndDropSvc::HOST_DND_GH_EVT_DROPPED:
893 {
894 pEvent->uType = uMsg;
895 pEvent->pszFormats = static_cast<char*>(RTMemAlloc(ccbFormats));
896 if (!pEvent->pszFormats)
897 rc = VERR_NO_MEMORY;
898
899 if (RT_SUCCESS(rc))
900 rc = vbglR3DnDGHProcessDroppedMessage(u32ClientId,
901 pEvent->pszFormats,
902 ccbFormats,
903 &pEvent->cbFormats,
904 &pEvent->u.a.uDefAction);
905 break;
906 }
907#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
908 default:
909 AssertMsgFailedReturn(("Message %u isn't expected in this context", uMsg),
910 VERR_INVALID_PARAMETER);
911 break;
912 }
913 }
914
915 return rc;
916}
917
918VBGLR3DECL(int) VbglR3DnDHGAcknowledgeOperation(uint32_t u32ClientId, uint32_t uAction)
919{
920 DragAndDropSvc::VBOXDNDHGACKOPMSG Msg;
921 RT_ZERO(Msg);
922 Msg.hdr.result = VERR_WRONG_ORDER;
923 Msg.hdr.u32ClientID = u32ClientId;
924 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_HG_ACK_OP;
925 Msg.hdr.cParms = 1;
926 /* Initialize parameter */
927 Msg.uAction.SetUInt32(uAction);
928 /* Do request */
929 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
930 if (RT_SUCCESS(rc))
931 rc = Msg.hdr.result;
932
933 return rc;
934}
935
936VBGLR3DECL(int) VbglR3DnDHGRequestData(uint32_t u32ClientId, const char* pcszFormat)
937{
938 AssertPtrReturn(pcszFormat, VERR_INVALID_PARAMETER);
939
940 DragAndDropSvc::VBOXDNDHGREQDATAMSG Msg;
941 RT_ZERO(Msg);
942 Msg.hdr.result = VERR_WRONG_ORDER;
943 Msg.hdr.u32ClientID = u32ClientId;
944 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_HG_REQ_DATA;
945 Msg.hdr.cParms = 1;
946 /* Do request */
947 Msg.pFormat.SetPtr((void*)pcszFormat, (uint32_t)strlen(pcszFormat) + 1);
948 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
949 if (RT_SUCCESS(rc))
950 rc = Msg.hdr.result;
951
952 return rc;
953}
954
955VBGLR3DECL(int) VbglR3DnDGHAcknowledgePending(uint32_t u32ClientId,
956 uint32_t uDefAction, uint32_t uAllActions,
957 const char* pcszFormat)
958{
959 AssertPtrReturn(pcszFormat, VERR_INVALID_POINTER);
960
961 DragAndDropSvc::VBOXDNDGHACKPENDINGMSG Msg;
962 RT_ZERO(Msg);
963 Msg.hdr.result = VERR_WRONG_ORDER;
964 Msg.hdr.u32ClientID = u32ClientId;
965 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_ACK_PENDING;
966 Msg.hdr.cParms = 3;
967 /* Initialize parameter */
968 Msg.uDefAction.SetUInt32(uDefAction);
969 Msg.uAllActions.SetUInt32(uAllActions);
970 Msg.pFormat.SetPtr((void*)pcszFormat, static_cast<uint32_t>(strlen(pcszFormat) + 1));
971 /* Do request */
972 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
973 if (RT_SUCCESS(rc))
974 rc = Msg.hdr.result;
975
976 return rc;
977}
978
979VBGLR3DECL(int) VbglR3DnDGHSendData(uint32_t u32ClientId, void *pvData, uint32_t cbData)
980{
981 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
982 AssertReturn(cbData, VERR_INVALID_PARAMETER);
983
984 /* Todo: URI support. Currently only data is send over to the host. For URI
985 * support basically the same as in the H->G case (see
986 * HostServices/DragAndDrop/dndmanager.h/cpp) has to be done:
987 * 1. Parse the urilist
988 * 2. Recursively send "create dir" and "transfer file" msg to the host
989 * 3. Patch the urilist by removing all base dirnames
990 * 4. On the host all needs to received and the urilist patched afterwards
991 * to point to the new location
992 */
993
994 DragAndDropSvc::VBOXDNDGHSENDDATAMSG Msg;
995 RT_ZERO(Msg);
996 Msg.hdr.result = VERR_WRONG_ORDER;
997 Msg.hdr.u32ClientID = u32ClientId;
998 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_SND_DATA;
999 Msg.hdr.cParms = 2;
1000 Msg.uSize.SetUInt32(cbData);
1001 int rc = VINF_SUCCESS;
1002 uint32_t cbMax = _1M;
1003 uint32_t cbSend = 0;
1004 while(cbSend < cbData)
1005 {
1006 /* Initialize parameter */
1007 uint32_t cbToSend = RT_MIN(cbData - cbSend, cbMax);
1008 Msg.pData.SetPtr(static_cast<uint8_t*>(pvData) + cbSend, cbToSend);
1009 /* Do request */
1010 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1011 if (RT_SUCCESS(rc))
1012 {
1013 rc = Msg.hdr.result;
1014 /* Did the host cancel the event? */
1015 if (rc == VERR_CANCELLED)
1016 break;
1017 }
1018 else
1019 break;
1020 cbSend += cbToSend;
1021// RTThreadSleep(500);
1022 }
1023
1024 return rc;
1025}
1026
1027VBGLR3DECL(int) VbglR3DnDGHErrorEvent(uint32_t u32ClientId, int rcOp)
1028{
1029 DragAndDropSvc::VBOXDNDGHEVTERRORMSG Msg;
1030 RT_ZERO(Msg);
1031 Msg.hdr.result = VERR_WRONG_ORDER;
1032 Msg.hdr.u32ClientID = u32ClientId;
1033 Msg.hdr.u32Function = DragAndDropSvc::GUEST_DND_GH_EVT_ERROR;
1034 Msg.hdr.cParms = 1;
1035 /* Initialize parameter */
1036 Msg.uRC.SetUInt32(rcOp);
1037 /* Do request */
1038 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1039 if (RT_SUCCESS(rc))
1040 rc = Msg.hdr.result;
1041
1042 return rc;
1043}
Note: See TracBrowser for help on using the repository browser.

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