VirtualBox

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

Last change on this file since 62290 was 60979, checked in by vboxsync, 9 years ago

DnD/VbglR3: Report back proper error in VbglR3DnDConnect().

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 76.6 KB
Line 
1/* $Id: VBoxGuestR3LibDragAndDrop.cpp 60979 2016-05-13 16:59:51Z vboxsync $ */
2/** @file
3 * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, Drag & Drop.
4 */
5
6/*
7 * Copyright (C) 2011-2016 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
50using namespace DragAndDropSvc;
51
52#include "VBGLR3Internal.h"
53
54/*********************************************************************************************************************************
55* Forward declarations *
56*********************************************************************************************************************************/
57
58VBGLR3DECL(int) VbglR3DnDHGSendProgress(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uStatus, uint8_t uPercent, int rcErr);
59
60/*********************************************************************************************************************************
61* Private internal functions *
62*********************************************************************************************************************************/
63
64static int vbglR3DnDGetNextMsgType(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puMsg, uint32_t *pcParms, bool fWait)
65{
66 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
67 AssertPtrReturn(puMsg, VERR_INVALID_POINTER);
68 AssertPtrReturn(pcParms, VERR_INVALID_POINTER);
69
70 VBOXDNDNEXTMSGMSG Msg;
71 RT_ZERO(Msg);
72 Msg.hdr.result = VERR_WRONG_ORDER;
73 Msg.hdr.u32ClientID = pCtx->uClientID;
74 Msg.hdr.u32Function = GUEST_DND_GET_NEXT_HOST_MSG;
75 Msg.hdr.cParms = 3;
76
77 Msg.uMsg.SetUInt32(0);
78 Msg.cParms.SetUInt32(0);
79 Msg.fBlock.SetUInt32(fWait ? 1 : 0);
80
81 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
82 if (RT_SUCCESS(rc))
83 {
84 rc = Msg.hdr.result;
85 if (RT_SUCCESS(rc))
86 {
87 rc = Msg.uMsg.GetUInt32(puMsg); AssertRC(rc);
88 rc = Msg.cParms.GetUInt32(pcParms); AssertRC(rc);
89 }
90 }
91
92 return rc;
93}
94
95/** @todo r=andy Clean up the parameter list. */
96static int vbglR3DnDHGRecvAction(PVBGLR3GUESTDNDCMDCTX pCtx,
97 uint32_t uMsg,
98 uint32_t *puScreenId,
99 uint32_t *puX,
100 uint32_t *puY,
101 uint32_t *puDefAction,
102 uint32_t *puAllActions,
103 char *pszFormats,
104 uint32_t cbFormats,
105 uint32_t *pcbFormatsRecv)
106{
107 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
108 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
109 AssertPtrReturn(puX, VERR_INVALID_POINTER);
110 AssertPtrReturn(puY, VERR_INVALID_POINTER);
111 AssertPtrReturn(puDefAction, VERR_INVALID_POINTER);
112 AssertPtrReturn(puAllActions, VERR_INVALID_POINTER);
113 AssertPtrReturn(pszFormats, VERR_INVALID_POINTER);
114 AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
115 AssertPtrReturn(pcbFormatsRecv, VERR_INVALID_POINTER);
116
117 VBOXDNDHGACTIONMSG Msg;
118 RT_ZERO(Msg);
119 Msg.hdr.result = VERR_WRONG_ORDER;
120 Msg.hdr.u32ClientID = pCtx->uClientID;
121 Msg.hdr.u32Function = uMsg;
122
123 if (pCtx->uProtocol < 3)
124 {
125 Msg.hdr.cParms = 7;
126
127 Msg.u.v1.uScreenId.SetUInt32(0);
128 Msg.u.v1.uX.SetUInt32(0);
129 Msg.u.v1.uY.SetUInt32(0);
130 Msg.u.v1.uDefAction.SetUInt32(0);
131 Msg.u.v1.uAllActions.SetUInt32(0);
132 Msg.u.v1.pvFormats.SetPtr(pszFormats, cbFormats);
133 Msg.u.v1.cbFormats.SetUInt32(0);
134 }
135 else
136 {
137 Msg.hdr.cParms = 8;
138
139 Msg.u.v3.uContext.SetUInt32(0);
140 Msg.u.v3.uScreenId.SetUInt32(0);
141 Msg.u.v3.uX.SetUInt32(0);
142 Msg.u.v3.uY.SetUInt32(0);
143 Msg.u.v3.uDefAction.SetUInt32(0);
144 Msg.u.v3.uAllActions.SetUInt32(0);
145 Msg.u.v3.pvFormats.SetPtr(pszFormats, cbFormats);
146 Msg.u.v3.cbFormats.SetUInt32(0);
147 }
148
149 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
150 if (RT_SUCCESS(rc))
151 {
152 rc = Msg.hdr.result;
153 if (RT_SUCCESS(rc))
154 {
155 if (pCtx->uProtocol < 3)
156 {
157 rc = Msg.u.v1.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
158 rc = Msg.u.v1.uX.GetUInt32(puX); AssertRC(rc);
159 rc = Msg.u.v1.uY.GetUInt32(puY); AssertRC(rc);
160 rc = Msg.u.v1.uDefAction.GetUInt32(puDefAction); AssertRC(rc);
161 rc = Msg.u.v1.uAllActions.GetUInt32(puAllActions); AssertRC(rc);
162 rc = Msg.u.v1.cbFormats.GetUInt32(pcbFormatsRecv); AssertRC(rc);
163 }
164 else
165 {
166 /** @todo Context ID not used yet. */
167 rc = Msg.u.v3.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
168 rc = Msg.u.v3.uX.GetUInt32(puX); AssertRC(rc);
169 rc = Msg.u.v3.uY.GetUInt32(puY); AssertRC(rc);
170 rc = Msg.u.v3.uDefAction.GetUInt32(puDefAction); AssertRC(rc);
171 rc = Msg.u.v3.uAllActions.GetUInt32(puAllActions); AssertRC(rc);
172 rc = Msg.u.v3.cbFormats.GetUInt32(pcbFormatsRecv); AssertRC(rc);
173 }
174
175 AssertReturn(cbFormats >= *pcbFormatsRecv, VERR_TOO_MUCH_DATA);
176 }
177 }
178
179 return rc;
180}
181
182static int vbglR3DnDHGRecvLeave(PVBGLR3GUESTDNDCMDCTX pCtx)
183{
184 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
185
186 VBOXDNDHGLEAVEMSG Msg;
187 RT_ZERO(Msg);
188 Msg.hdr.u32ClientID = pCtx->uClientID;
189 Msg.hdr.u32Function = HOST_DND_HG_EVT_LEAVE;
190
191 if (pCtx->uProtocol < 3)
192 {
193 Msg.hdr.cParms = 0;
194 }
195 else
196 {
197 Msg.hdr.cParms = 1;
198
199 /** @todo Context ID not used yet. */
200 Msg.u.v3.uContext.SetUInt32(0);
201 }
202
203 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
204 if (RT_SUCCESS(rc))
205 rc = Msg.hdr.result;
206
207 return rc;
208}
209
210static int vbglR3DnDHGRecvCancel(PVBGLR3GUESTDNDCMDCTX pCtx)
211{
212 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
213
214 VBOXDNDHGCANCELMSG Msg;
215 RT_ZERO(Msg);
216 Msg.hdr.result = VERR_WRONG_ORDER;
217 Msg.hdr.u32ClientID = pCtx->uClientID;
218 Msg.hdr.u32Function = HOST_DND_HG_EVT_CANCEL;
219
220 if (pCtx->uProtocol < 3)
221 {
222 Msg.hdr.cParms = 0;
223 }
224 else
225 {
226 Msg.hdr.cParms = 1;
227
228 /** @todo Context ID not used yet. */
229 Msg.u.v3.uContext.SetUInt32(0);
230 }
231
232 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
233 if (RT_SUCCESS(rc))
234 rc = Msg.hdr.result;
235
236 return rc;
237}
238
239static int vbglR3DnDHGRecvDir(PVBGLR3GUESTDNDCMDCTX pCtx,
240 char *pszDirname,
241 uint32_t cbDirname,
242 uint32_t *pcbDirnameRecv,
243 uint32_t *pfMode)
244{
245 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
246 AssertPtrReturn(pszDirname, VERR_INVALID_POINTER);
247 AssertReturn(cbDirname, VERR_INVALID_PARAMETER);
248 AssertPtrReturn(pcbDirnameRecv, VERR_INVALID_POINTER);
249 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
250
251 VBOXDNDHGSENDDIRMSG Msg;
252 RT_ZERO(Msg);
253 Msg.hdr.result = VERR_WRONG_ORDER;
254 Msg.hdr.u32ClientID = pCtx->uClientID;
255 Msg.hdr.u32Function = HOST_DND_HG_SND_DIR;
256
257 if (pCtx->uProtocol < 3)
258 {
259 Msg.hdr.cParms = 3;
260
261 Msg.u.v1.pvName.SetPtr(pszDirname, cbDirname);
262 Msg.u.v1.cbName.SetUInt32(cbDirname);
263 Msg.u.v1.fMode.SetUInt32(0);
264 }
265 else
266 {
267 Msg.hdr.cParms = 4;
268
269 /** @todo Context ID not used yet. */
270 Msg.u.v3.uContext.SetUInt32(0);
271 Msg.u.v3.pvName.SetPtr(pszDirname, cbDirname);
272 Msg.u.v3.cbName.SetUInt32(cbDirname);
273 Msg.u.v3.fMode.SetUInt32(0);
274 }
275
276 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
277 if (RT_SUCCESS(rc))
278 {
279 rc = Msg.hdr.result;
280 if (RT_SUCCESS(Msg.hdr.result))
281 {
282 if (pCtx->uProtocol < 3)
283 {
284 rc = Msg.u.v1.cbName.GetUInt32(pcbDirnameRecv); AssertRC(rc);
285 rc = Msg.u.v1.fMode.GetUInt32(pfMode); AssertRC(rc);
286 }
287 else
288 {
289 /** @todo Context ID not used yet. */
290 rc = Msg.u.v3.cbName.GetUInt32(pcbDirnameRecv); AssertRC(rc);
291 rc = Msg.u.v3.fMode.GetUInt32(pfMode); AssertRC(rc);
292 }
293
294 AssertReturn(cbDirname >= *pcbDirnameRecv, VERR_TOO_MUCH_DATA);
295 }
296 }
297
298 return rc;
299}
300
301static int vbglR3DnDHGRecvFileData(PVBGLR3GUESTDNDCMDCTX pCtx,
302 char *pszFilename,
303 uint32_t cbFilename,
304 uint32_t *pcbFilenameRecv,
305 void *pvData,
306 uint32_t cbData,
307 uint32_t *pcbDataRecv,
308 uint32_t *pfMode)
309{
310 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
311 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
312 AssertReturn(cbFilename, VERR_INVALID_PARAMETER);
313 AssertPtrReturn(pcbFilenameRecv, VERR_INVALID_POINTER);
314 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
315 AssertReturn(cbData, VERR_INVALID_PARAMETER);
316 AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER);
317 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
318
319 VBOXDNDHGSENDFILEDATAMSG Msg;
320 RT_ZERO(Msg);
321 Msg.hdr.result = VERR_WRONG_ORDER;
322 Msg.hdr.u32ClientID = pCtx->uClientID;
323 Msg.hdr.u32Function = HOST_DND_HG_SND_FILE_DATA;
324
325 if (pCtx->uProtocol <= 1)
326 {
327 Msg.u.v1.pvName.SetPtr(pszFilename, cbFilename);
328 Msg.u.v1.cbName.SetUInt32(0);
329 Msg.u.v1.pvData.SetPtr(pvData, cbData);
330 Msg.u.v1.cbData.SetUInt32(0);
331 Msg.u.v1.fMode.SetUInt32(0);
332
333 Msg.hdr.cParms = 5;
334 }
335 else if (pCtx->uProtocol == 2)
336 {
337 Msg.u.v2.uContext.SetUInt32(0);
338 Msg.u.v2.pvData.SetPtr(pvData, cbData);
339 Msg.u.v2.cbData.SetUInt32(cbData);
340
341 Msg.hdr.cParms = 3;
342 }
343 else if (pCtx->uProtocol >= 3)
344 {
345 Msg.u.v3.uContext.SetUInt32(0);
346 Msg.u.v3.pvData.SetPtr(pvData, cbData);
347 Msg.u.v3.cbData.SetUInt32(0);
348 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
349 Msg.u.v3.cbChecksum.SetUInt32(0);
350
351 Msg.hdr.cParms = 5;
352 }
353 else
354 AssertMsgFailed(("Protocol %RU32 not implemented\n", pCtx->uProtocol));
355
356 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
357 if (RT_SUCCESS(rc))
358 {
359 rc = Msg.hdr.result;
360 if (RT_SUCCESS(rc))
361 {
362 if (pCtx->uProtocol <= 1)
363 {
364 rc = Msg.u.v1.cbName.GetUInt32(pcbFilenameRecv); AssertRC(rc);
365 rc = Msg.u.v1.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
366 rc = Msg.u.v1.fMode.GetUInt32(pfMode); AssertRC(rc);
367
368 AssertReturn(cbFilename >= *pcbFilenameRecv, VERR_TOO_MUCH_DATA);
369 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
370 }
371 else if (pCtx->uProtocol == 2)
372 {
373 /** @todo Context ID not used yet. */
374 rc = Msg.u.v2.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
375 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
376 }
377 else if (pCtx->uProtocol >= 3)
378 {
379 /** @todo Context ID not used yet. */
380 rc = Msg.u.v3.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
381 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
382 /** @todo Add checksum support. */
383 }
384 else
385 AssertMsgFailed(("Protocol %RU32 not implemented\n", pCtx->uProtocol));
386 }
387 }
388
389 return rc;
390}
391
392static int vbglR3DnDHGRecvFileHdr(PVBGLR3GUESTDNDCMDCTX pCtx,
393 char *pszFilename,
394 uint32_t cbFilename,
395 uint32_t *puFlags,
396 uint32_t *pfMode,
397 uint64_t *pcbTotal)
398{
399 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
400 AssertPtrReturn(pszFilename, VERR_INVALID_POINTER);
401 AssertReturn(cbFilename, VERR_INVALID_PARAMETER);
402 AssertPtrReturn(puFlags, VERR_INVALID_POINTER);
403 AssertPtrReturn(pfMode, VERR_INVALID_POINTER);
404 AssertReturn(pcbTotal, VERR_INVALID_POINTER);
405
406 VBOXDNDHGSENDFILEHDRMSG Msg;
407 RT_ZERO(Msg);
408 Msg.hdr.result = VERR_WRONG_ORDER;
409 Msg.hdr.u32ClientID = pCtx->uClientID;
410 Msg.hdr.u32Function = HOST_DND_HG_SND_FILE_HDR;
411
412 int rc;
413
414 if (pCtx->uProtocol <= 1)
415 {
416 rc = VERR_NOT_SUPPORTED;
417 }
418 else
419 {
420 Msg.hdr.cParms = 6;
421
422 Msg.uContext.SetUInt32(0); /** @todo Not used yet. */
423 Msg.pvName.SetPtr(pszFilename, cbFilename);
424 Msg.cbName.SetUInt32(cbFilename);
425 Msg.uFlags.SetUInt32(0);
426 Msg.fMode.SetUInt32(0);
427 Msg.cbTotal.SetUInt64(0);
428
429 rc = VINF_SUCCESS;
430 }
431
432 if (RT_SUCCESS(rc))
433 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
434 if (RT_SUCCESS(rc))
435 {
436 rc = Msg.hdr.result;
437 if (RT_SUCCESS(rc))
438 {
439 /** @todo Get context ID. */
440 rc = Msg.uFlags.GetUInt32(puFlags); AssertRC(rc);
441 rc = Msg.fMode.GetUInt32(pfMode); AssertRC(rc);
442 rc = Msg.cbTotal.GetUInt64(pcbTotal); AssertRC(rc);
443 }
444 }
445
446 return rc;
447}
448
449static int vbglR3DnDHGRecvURIData(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr, DnDDroppedFiles *pDroppedFiles)
450{
451 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
452 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
453 AssertPtrReturn(pDroppedFiles, VERR_INVALID_POINTER);
454
455 /* Only count the raw data minus the already received meta data. */
456 Assert(pDataHdr->cbTotal >= pDataHdr->cbMeta);
457 uint64_t cbToRecvBytes = pDataHdr->cbTotal - pDataHdr->cbMeta;
458 uint64_t cToRecvObjs = pDataHdr->cObjects;
459
460 LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64, (cbTotal=%RU64, cbMeta=%RU32)\n",
461 cbToRecvBytes, cToRecvObjs, pDataHdr->cbTotal, pDataHdr->cbMeta));
462
463 /*
464 * Only do accounting for protocol v3 and up.
465 * The older protocols did not have any data accounting available, so
466 * we simply tried to receive as much data as available and bail out.
467 */
468 const bool fDoAccounting = pCtx->uProtocol >= 3;
469
470 /* Anything to do at all? */
471 if (fDoAccounting)
472 {
473 if ( !cbToRecvBytes
474 && !cToRecvObjs)
475 {
476 return VINF_SUCCESS;
477 }
478 }
479
480 /*
481 * Allocate temporary chunk buffer.
482 */
483 uint32_t cbChunkMax = pCtx->cbMaxChunkSize;
484 void *pvChunk = RTMemAlloc(cbChunkMax);
485 if (!pvChunk)
486 return VERR_NO_MEMORY;
487 uint32_t cbChunkRead = 0;
488
489 uint64_t cbFileSize = 0; /* Total file size (in bytes). */
490 uint64_t cbFileWritten = 0; /* Written bytes. */
491
492 /*
493 * Create and query the (unique) drop target directory in the user's temporary directory.
494 */
495 int rc = pDroppedFiles->OpenTemp(0 /* fFlags */);
496 if (RT_FAILURE(rc))
497 {
498 RTMemFree(pvChunk);
499 return VERR_NO_MEMORY;
500 }
501
502 const char *pszDropDir = pDroppedFiles->GetDirAbs();
503 AssertPtr(pszDropDir);
504
505 /*
506 * Enter the main loop of retieving files + directories.
507 */
508 DnDURIObject objFile(DnDURIObject::File);
509
510 char szPathName[RTPATH_MAX] = { 0 };
511 uint32_t cbPathName = 0;
512 uint32_t fFlags = 0;
513 uint32_t fMode = 0;
514
515 /*
516 * Only wait for new incoming commands for protocol v3 and up.
517 * The older protocols did not have any data accounting available, so
518 * we simply tried to receive as much data as available and bail out.
519 */
520 const bool fWait = pCtx->uProtocol >= 3;
521
522 do
523 {
524 LogFlowFunc(("Wating for new message ...\n"));
525
526 uint32_t uNextMsg;
527 uint32_t cNextParms;
528 rc = vbglR3DnDGetNextMsgType(pCtx, &uNextMsg, &cNextParms, fWait);
529 if (RT_SUCCESS(rc))
530 {
531 LogFlowFunc(("uNextMsg=%RU32, cNextParms=%RU32\n", uNextMsg, cNextParms));
532
533 switch (uNextMsg)
534 {
535 case HOST_DND_HG_SND_DIR:
536 {
537 rc = vbglR3DnDHGRecvDir(pCtx,
538 szPathName,
539 sizeof(szPathName),
540 &cbPathName,
541 &fMode);
542 LogFlowFunc(("HOST_DND_HG_SND_DIR pszPathName=%s, cbPathName=%RU32, fMode=0x%x, rc=%Rrc\n",
543 szPathName, cbPathName, fMode, rc));
544
545 char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName);
546 if (pszPathAbs)
547 {
548#ifdef RT_OS_WINDOWS
549 uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL;
550#else
551 uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRWXU;
552#endif
553 rc = RTDirCreate(pszPathAbs, fCreationMode, 0);
554 if (RT_SUCCESS(rc))
555 rc = pDroppedFiles->AddDir(pszPathAbs);
556
557 if ( RT_SUCCESS(rc)
558 && fDoAccounting)
559 {
560 Assert(cToRecvObjs);
561 cToRecvObjs--;
562 }
563
564 RTStrFree(pszPathAbs);
565 }
566 else
567 rc = VERR_NO_MEMORY;
568 break;
569 }
570 case HOST_DND_HG_SND_FILE_HDR:
571 case HOST_DND_HG_SND_FILE_DATA:
572 {
573 if (uNextMsg == HOST_DND_HG_SND_FILE_HDR)
574 {
575 rc = vbglR3DnDHGRecvFileHdr(pCtx,
576 szPathName,
577 sizeof(szPathName),
578 &fFlags,
579 &fMode,
580 &cbFileSize);
581 LogFlowFunc(("HOST_DND_HG_SND_FILE_HDR: "
582 "szPathName=%s, fFlags=0x%x, fMode=0x%x, cbFileSize=%RU64, rc=%Rrc\n",
583 szPathName, fFlags, fMode, cbFileSize, rc));
584 }
585 else
586 {
587 rc = vbglR3DnDHGRecvFileData(pCtx,
588 szPathName,
589 sizeof(szPathName),
590 &cbPathName,
591 pvChunk,
592 cbChunkMax,
593 &cbChunkRead,
594 &fMode);
595
596 LogFlowFunc(("HOST_DND_HG_SND_FILE_DATA: "
597 "szPathName=%s, cbPathName=%RU32, cbChunkRead=%RU32, fMode=0x%x, rc=%Rrc\n",
598 szPathName, cbPathName, cbChunkRead, fMode, rc));
599 }
600
601 if ( RT_SUCCESS(rc)
602 && ( uNextMsg == HOST_DND_HG_SND_FILE_HDR
603 /* Protocol v1 always sends the file name, so opening the file every time. */
604 || pCtx->uProtocol <= 1)
605 )
606 {
607 char *pszPathAbs = RTPathJoinA(pszDropDir, szPathName);
608 if (pszPathAbs)
609 {
610 LogFlowFunc(("Opening pszPathName=%s, cbPathName=%RU32, fMode=0x%x, cbFileSize=%zu\n",
611 szPathName, cbPathName, fMode, cbFileSize));
612
613 uint64_t fOpen = RTFILE_O_WRITE | RTFILE_O_DENY_WRITE;
614 if (pCtx->uProtocol <= 1)
615 fOpen |= RTFILE_O_OPEN_CREATE | RTFILE_O_APPEND;
616 else
617 fOpen |= RTFILE_O_CREATE_REPLACE;
618
619 /* Is there already a file open, e.g. in transfer? */
620 if (!objFile.IsOpen())
621 {
622 RTCString strPathAbs(pszPathAbs);
623#ifdef RT_OS_WINDOWS
624 uint32_t fCreationMode = (fMode & RTFS_DOS_MASK) | RTFS_DOS_NT_NORMAL;
625#else
626 uint32_t fCreationMode = (fMode & RTFS_UNIX_MASK) | RTFS_UNIX_IRUSR | RTFS_UNIX_IWUSR;
627#endif
628 rc = objFile.OpenEx(strPathAbs, DnDURIObject::File, DnDURIObject::Target, fOpen, fCreationMode);
629 if (RT_SUCCESS(rc))
630 {
631 rc = pDroppedFiles->AddFile(strPathAbs.c_str());
632 if (RT_SUCCESS(rc))
633 {
634 cbFileWritten = 0;
635
636 if (pCtx->uProtocol >= 2) /* Set the expected file size. */
637 objFile.SetSize(cbFileSize);
638 }
639 }
640 }
641 else
642 {
643 AssertMsgFailed(("ObjType=%RU32, Proto=%RU32\n", objFile.GetType(), pCtx->uProtocol));
644 rc = VERR_WRONG_ORDER;
645 }
646
647 RTStrFree(pszPathAbs);
648 }
649 else
650 rc = VERR_NO_MEMORY;
651 }
652
653 if ( RT_SUCCESS(rc)
654 && uNextMsg == HOST_DND_HG_SND_FILE_DATA)
655 {
656 uint32_t cbChunkWritten;
657 rc = objFile.Write(pvChunk, cbChunkRead, &cbChunkWritten);
658 if (RT_SUCCESS(rc))
659 {
660 LogFlowFunc(("HOST_DND_HG_SND_FILE_DATA "
661 "cbChunkRead=%RU32, cbChunkWritten=%RU32, cbFileWritten=%RU64 cbFileSize=%RU64\n",
662 cbChunkRead, cbChunkWritten, cbFileWritten + cbChunkWritten, cbFileSize));
663
664 cbFileWritten += cbChunkWritten;
665
666 if (fDoAccounting)
667 {
668 Assert(cbChunkRead <= cbToRecvBytes);
669 cbToRecvBytes -= cbChunkRead;
670 }
671 }
672 }
673
674 bool fClose = false;
675 if (pCtx->uProtocol >= 2)
676 {
677 /* Data transfer complete? Close the file. */
678 fClose = objFile.IsComplete();
679 if ( fClose
680 && fDoAccounting)
681 {
682 Assert(cToRecvObjs);
683 cToRecvObjs--;
684 }
685
686 /* Only since protocol v2 we know the file size upfront. */
687 Assert(cbFileWritten <= cbFileSize);
688 }
689 else
690 fClose = true; /* Always close the file after each chunk. */
691
692 if (fClose)
693 {
694 LogFlowFunc(("Closing file\n"));
695 objFile.Close();
696 }
697
698 break;
699 }
700 case HOST_DND_HG_EVT_CANCEL:
701 {
702 rc = vbglR3DnDHGRecvCancel(pCtx);
703 if (RT_SUCCESS(rc))
704 rc = VERR_CANCELLED;
705 break;
706 }
707 default:
708 {
709 LogFlowFunc(("Message %RU32 not supported\n", uNextMsg));
710 rc = VERR_NOT_SUPPORTED;
711 break;
712 }
713 }
714 }
715
716 if (RT_FAILURE(rc))
717 break;
718
719 if (fDoAccounting)
720 {
721 LogFlowFunc(("cbToRecvBytes=%RU64, cToRecvObjs=%RU64\n", cbToRecvBytes, cToRecvObjs));
722 if ( !cbToRecvBytes
723 && !cToRecvObjs)
724 {
725 break;
726 }
727 }
728
729 } while (RT_SUCCESS(rc));
730
731 LogFlowFunc(("Loop ended with %Rrc\n", rc));
732
733 /* All URI data processed? */
734 if (rc == VERR_NO_DATA)
735 rc = VINF_SUCCESS;
736
737 /* Delete temp buffer again. */
738 if (pvChunk)
739 RTMemFree(pvChunk);
740
741 /* Cleanup on failure or if the user has canceled the operation or
742 * something else went wrong. */
743 if (RT_FAILURE(rc))
744 {
745 objFile.Close();
746 pDroppedFiles->Rollback();
747 }
748 else
749 {
750 /** @todo Compare the URI list with the dirs/files we really transferred. */
751 /** @todo Implement checksum verification, if any. */
752 }
753
754 /*
755 * Close the dropped files directory.
756 * Don't try to remove it here, however, as the files are being needed
757 * by the client's drag'n drop operation lateron.
758 */
759 int rc2 = pDroppedFiles->Reset(false /* fRemoveDropDir */);
760 if (RT_FAILURE(rc2)) /* Not fatal, don't report back to host. */
761 LogFlowFunc(("Closing dropped files directory failed with %Rrc\n", rc2));
762
763 LogFlowFuncLeaveRC(rc);
764 return rc;
765}
766
767static int vbglR3DnDHGRecvDataRaw(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr,
768 void *pvData, uint32_t cbData, uint32_t *pcbDataRecv)
769{
770 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
771 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
772 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
773 AssertReturn(cbData, VERR_INVALID_PARAMETER);
774 /* pcbDataRecv is optional. */
775
776 int rc;
777
778 LogFlowFunc(("pvDate=%p, cbData=%RU32\n", pvData, cbData));
779
780 VBOXDNDHGSENDDATAMSG Msg;
781 RT_ZERO(Msg);
782 Msg.hdr.result = VERR_WRONG_ORDER;
783 Msg.hdr.u32ClientID = pCtx->uClientID;
784 Msg.hdr.u32Function = HOST_DND_HG_SND_DATA;
785
786 do
787 {
788 uint32_t cbDataRecv;
789
790 if (pCtx->uProtocol < 3)
791 {
792 Msg.hdr.cParms = 5;
793
794 Msg.u.v1.uScreenId.SetUInt32(0);
795 Msg.u.v1.pvFormat.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
796 Msg.u.v1.cbFormat.SetUInt32(0);
797 Msg.u.v1.pvData.SetPtr(pvData, cbData);
798 Msg.u.v1.cbData.SetUInt32(0);
799
800 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
801 if (RT_SUCCESS(rc))
802 {
803 rc = Msg.hdr.result;
804 if ( RT_SUCCESS(rc)
805 || rc == VERR_BUFFER_OVERFLOW)
806 {
807 rc = Msg.u.v1.uScreenId.GetUInt32(&pDataHdr->uScreenId);
808 AssertRC(rc);
809
810 /*
811 * In case of VERR_BUFFER_OVERFLOW get the data sizes required
812 * for the format + data blocks.
813 */
814 uint32_t cbFormatRecv;
815 rc = Msg.u.v1.cbFormat.GetUInt32(&cbFormatRecv);
816 AssertRC(rc);
817 if (cbFormatRecv >= pDataHdr->cbMetaFmt)
818 {
819 rc = VERR_TOO_MUCH_DATA;
820 break;
821 }
822
823 rc = Msg.u.v1.cbData.GetUInt32(&cbDataRecv);
824 AssertRC(rc);
825 if (cbDataRecv >= pDataHdr->cbMeta)
826 {
827 rc = VERR_TOO_MUCH_DATA;
828 break;
829 }
830
831 pDataHdr->cbMetaFmt = cbFormatRecv;
832 }
833 }
834
835 if (RT_FAILURE(rc))
836 break;
837 }
838 else /* Protocol v3 and up. */
839 {
840 Msg.hdr.cParms = 5;
841
842 Msg.u.v3.uContext.SetUInt32(0);
843 Msg.u.v3.pvData.SetPtr(pvData, cbData);
844 Msg.u.v3.cbData.SetUInt32(0);
845 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
846 Msg.u.v3.cbChecksum.SetUInt32(0);
847
848 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
849 if (RT_SUCCESS(rc))
850 {
851 rc = Msg.hdr.result;
852 if (RT_SUCCESS(rc))
853 {
854 rc = Msg.u.v3.cbData.GetUInt32(&cbDataRecv);
855 AssertRC(rc);
856 }
857
858 /** @todo Use checksum for validating the received data. */
859 }
860
861 if (RT_FAILURE(rc))
862 break;
863 }
864
865 if (pcbDataRecv)
866 *pcbDataRecv = cbDataRecv;
867
868 } while (0);
869
870 LogFlowFuncLeaveRC(rc);
871 return rc;
872}
873
874static int vbglR3DnDHGRecvDataHdr(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr)
875{
876 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
877 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
878
879 Assert(pCtx->uProtocol >= 3); /* Only for protocol v3 and up. */
880
881 VBOXDNDHGSENDDATAHDRMSG Msg;
882 RT_ZERO(Msg);
883 Msg.hdr.result = VERR_WRONG_ORDER;
884 Msg.hdr.u32ClientID = pCtx->uClientID;
885 Msg.hdr.u32Function = HOST_DND_HG_SND_DATA_HDR;
886 Msg.hdr.cParms = 12;
887
888 Msg.uContext.SetUInt32(0);
889 Msg.uFlags.SetUInt32(0);
890 Msg.uScreenId.SetUInt32(0);
891 Msg.cbTotal.SetUInt64(0);
892 Msg.cbMeta.SetUInt32(0);
893 Msg.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
894 Msg.cbMetaFmt.SetUInt32(0);
895 Msg.cObjects.SetUInt64(0);
896 Msg.enmCompression.SetUInt32(0);
897 Msg.enmChecksumType.SetUInt32(0);
898 Msg.pvChecksum.SetPtr(pDataHdr->pvChecksum, pDataHdr->cbChecksum);
899 Msg.cbChecksum.SetUInt32(0);
900
901 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
902 if (RT_SUCCESS(rc))
903 {
904 rc = Msg.hdr.result;
905 if (RT_SUCCESS(rc))
906 {
907 /* Msg.uContext not needed here. */
908 Msg.uFlags.GetUInt32(&pDataHdr->uFlags);
909 Msg.uScreenId.GetUInt32(&pDataHdr->uScreenId);
910 Msg.cbTotal.GetUInt64(&pDataHdr->cbTotal);
911 Msg.cbMeta.GetUInt32(&pDataHdr->cbMeta);
912 Msg.cbMetaFmt.GetUInt32(&pDataHdr->cbMetaFmt);
913 Msg.cObjects.GetUInt64(&pDataHdr->cObjects);
914 Msg.enmCompression.GetUInt32(&pDataHdr->enmCompression);
915 Msg.enmChecksumType.GetUInt32((uint32_t *)&pDataHdr->enmChecksumType);
916 Msg.cbChecksum.GetUInt32(&pDataHdr->cbChecksum);
917 }
918 }
919
920 LogFlowFuncLeaveRC(rc);
921 return rc;
922}
923
924/** @todo Deprecated function; will be removed. */
925static int vbglR3DnDHGRecvMoreData(PVBGLR3GUESTDNDCMDCTX pCtx,
926 void *pvData, uint32_t cbData, uint32_t *pcbDataRecv)
927{
928 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
929 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
930 AssertReturn(cbData, VERR_INVALID_PARAMETER);
931 AssertPtrReturn(pcbDataRecv, VERR_INVALID_POINTER);
932
933 VBOXDNDHGSENDMOREDATAMSG Msg;
934 RT_ZERO(Msg);
935 Msg.hdr.result = VERR_WRONG_ORDER;
936 Msg.hdr.u32ClientID = pCtx->uClientID;
937 Msg.hdr.u32Function = HOST_DND_HG_SND_MORE_DATA;
938 Msg.hdr.cParms = 2;
939
940 Msg.pvData.SetPtr(pvData, cbData);
941 Msg.cbData.SetUInt32(0);
942
943 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
944 if (RT_SUCCESS(rc))
945 {
946 rc = Msg.hdr.result;
947 if ( RT_SUCCESS(rc)
948 || rc == VERR_BUFFER_OVERFLOW)
949 {
950 rc = Msg.cbData.GetUInt32(pcbDataRecv); AssertRC(rc);
951 AssertReturn(cbData >= *pcbDataRecv, VERR_TOO_MUCH_DATA);
952 }
953 }
954 return rc;
955}
956
957static int vbglR3DnDHGRecvDataLoop(PVBGLR3GUESTDNDCMDCTX pCtx, PVBOXDNDSNDDATAHDR pDataHdr,
958 void **ppvData, uint64_t *pcbData)
959{
960 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
961 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
962 AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
963 AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
964
965 int rc;
966 uint32_t cbDataRecv;
967
968 LogFlowFuncEnter();
969
970 if (pCtx->uProtocol < 3)
971 {
972 uint64_t cbDataTmp = pCtx->cbMaxChunkSize;
973 void *pvDataTmp = RTMemAlloc(cbDataTmp);
974
975 if (!cbDataTmp)
976 return VERR_NO_MEMORY;
977
978 /**
979 * Protocols < v3 contain the header information in every HOST_DND_HG_SND_DATA
980 * message, so do the actual retrieving immediately.
981 *
982 * Also, the initial implementation used VERR_BUFFER_OVERFLOW as a return code to
983 * indicate that there will be more data coming in after the initial data chunk. There
984 * was no way of telling the total data size upfront (in form of a header or some such),
985 * so also handle this case to not break backwards compatibility.
986 */
987 rc = vbglR3DnDHGRecvDataRaw(pCtx, pDataHdr, pvDataTmp, pCtx->cbMaxChunkSize, &cbDataRecv);
988
989 /* See comment above. */
990 while (rc == VERR_BUFFER_OVERFLOW)
991 {
992 uint32_t uNextMsg;
993 uint32_t cNextParms;
994 rc = vbglR3DnDGetNextMsgType(pCtx, &uNextMsg, &cNextParms, false /* fBlock */);
995 if (RT_SUCCESS(rc))
996 {
997 switch(uNextMsg)
998 {
999 case HOST_DND_HG_SND_MORE_DATA:
1000 {
1001 /** @todo r=andy Don't use reallocate here; can go wrong with *really* big URI lists.
1002 * Instead send as many URI entries as possible per chunk and add those entries
1003 * to our to-process list for immediata processing. Repeat the step after processing then. */
1004 LogFlowFunc(("HOST_DND_HG_SND_MORE_DATA cbDataTotal: %RU64 -> %RU64\n",
1005 cbDataTmp, cbDataTmp + pCtx->cbMaxChunkSize));
1006 void *pvDataNew = RTMemRealloc(pvDataTmp, cbDataTmp + pCtx->cbMaxChunkSize);
1007 if (!pvDataNew)
1008 {
1009 rc = VERR_NO_MEMORY;
1010 break;
1011 }
1012
1013 pvDataTmp = pvDataNew;
1014
1015 uint8_t *pvDataOff = (uint8_t *)pvDataTmp + cbDataTmp;
1016 rc = vbglR3DnDHGRecvMoreData(pCtx, pvDataOff, pCtx->cbMaxChunkSize, &cbDataRecv);
1017 if ( RT_SUCCESS(rc)
1018 || rc == VERR_BUFFER_OVERFLOW) /* Still can return VERR_BUFFER_OVERFLOW. */
1019 {
1020 cbDataTmp += cbDataRecv;
1021 }
1022 break;
1023 }
1024 case HOST_DND_HG_EVT_CANCEL:
1025 default:
1026 {
1027 rc = vbglR3DnDHGRecvCancel(pCtx);
1028 if (RT_SUCCESS(rc))
1029 rc = VERR_CANCELLED;
1030 break;
1031 }
1032 }
1033 }
1034 }
1035
1036 if (RT_SUCCESS(rc))
1037 {
1038 /* There was no way of telling the total data size upfront
1039 * (in form of a header or some such), so set the total data size here. */
1040 pDataHdr->cbTotal = cbDataTmp;
1041
1042 *ppvData = pvDataTmp;
1043 *pcbData = cbDataTmp;
1044 }
1045 else
1046 RTMemFree(pvDataTmp);
1047 }
1048 else /* Protocol v3 and up. */
1049 {
1050 rc = vbglR3DnDHGRecvDataHdr(pCtx, pDataHdr);
1051 if (RT_SUCCESS(rc))
1052 {
1053 LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32\n", pDataHdr->cbTotal, pDataHdr->cbMeta));
1054 if (pDataHdr->cbMeta)
1055 {
1056 uint64_t cbDataTmp = 0;
1057 void *pvDataTmp = RTMemAlloc(pDataHdr->cbMeta);
1058 if (!pvDataTmp)
1059 rc = VERR_NO_MEMORY;
1060
1061 if (RT_SUCCESS(rc))
1062 {
1063 uint8_t *pvDataOff = (uint8_t *)pvDataTmp;
1064 while (cbDataTmp < pDataHdr->cbMeta)
1065 {
1066 rc = vbglR3DnDHGRecvDataRaw(pCtx, pDataHdr,
1067 pvDataOff, RT_MIN(pDataHdr->cbMeta - cbDataTmp, pCtx->cbMaxChunkSize),
1068 &cbDataRecv);
1069 if (RT_SUCCESS(rc))
1070 {
1071 LogFlowFunc(("cbDataRecv=%RU32, cbDataTmp=%RU64\n", cbDataRecv, cbDataTmp));
1072 Assert(cbDataTmp + cbDataRecv <= pDataHdr->cbMeta);
1073 cbDataTmp += cbDataRecv;
1074 pvDataOff += cbDataRecv;
1075 }
1076 else
1077 break;
1078 }
1079
1080 if (RT_SUCCESS(rc))
1081 {
1082 Assert(cbDataTmp == pDataHdr->cbMeta);
1083
1084 LogFlowFunc(("Received %RU64 bytes of data\n", cbDataTmp));
1085
1086 *ppvData = pvDataTmp;
1087 *pcbData = cbDataTmp;
1088 }
1089 else
1090 RTMemFree(pvDataTmp);
1091 }
1092 }
1093 else
1094 {
1095 *ppvData = NULL;
1096 *pcbData = 0;
1097 }
1098 }
1099 }
1100
1101 LogFlowFuncLeaveRC(rc);
1102 return rc;
1103}
1104
1105/** @todo Replace the parameters (except pCtx) with PVBOXDNDSNDDATAHDR. Later. */
1106/** @todo Hand in the DnDURIList + DnDDroppedFiles objects so that this function
1107 * can fill it directly instead of passing huge blobs of data around. */
1108static int vbglR3DnDHGRecvDataMain(PVBGLR3GUESTDNDCMDCTX pCtx,
1109 uint32_t *puScreenId,
1110 char **ppszFormat,
1111 uint32_t *pcbFormat,
1112 void **ppvData,
1113 uint32_t *pcbData)
1114{
1115 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1116 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
1117 AssertPtrReturn(ppszFormat, VERR_INVALID_POINTER);
1118 AssertPtrReturn(pcbFormat, VERR_INVALID_POINTER);
1119 AssertPtrReturn(ppvData, VERR_INVALID_POINTER);
1120 AssertPtrReturn(pcbData, VERR_INVALID_POINTER);
1121
1122 VBOXDNDDATAHDR dataHdr; /** @todo See todo above. */
1123 RT_ZERO(dataHdr);
1124
1125 dataHdr.cbMetaFmt = _64K; /** @todo Make this configurable? */
1126 dataHdr.pvMetaFmt = RTMemAlloc(dataHdr.cbMetaFmt);
1127 if (!dataHdr.pvMetaFmt)
1128 return VERR_NO_MEMORY;
1129
1130 DnDURIList lstURI;
1131 DnDDroppedFiles droppedFiles;
1132
1133 void *pvData; /** @todo See todo above. */
1134 uint64_t cbData; /** @todo See todo above. */
1135
1136 int rc = vbglR3DnDHGRecvDataLoop(pCtx, &dataHdr, &pvData, &cbData);
1137 if (RT_SUCCESS(rc))
1138 {
1139 /**
1140 * Check if this is an URI event. If so, let VbglR3 do all the actual
1141 * data transfer + file/directory creation internally without letting
1142 * the caller know.
1143 *
1144 * This keeps the actual (guest OS-)dependent client (like VBoxClient /
1145 * VBoxTray) small by not having too much redundant code.
1146 */
1147 Assert(dataHdr.cbMetaFmt);
1148 AssertPtr(dataHdr.pvMetaFmt);
1149 if (DnDMIMEHasFileURLs((char *)dataHdr.pvMetaFmt, dataHdr.cbMetaFmt))
1150 {
1151 AssertPtr(pvData);
1152 Assert(cbData);
1153 rc = lstURI.RootFromURIData(pvData, cbData, 0 /* fFlags */);
1154 if (RT_SUCCESS(rc))
1155 rc = vbglR3DnDHGRecvURIData(pCtx, &dataHdr, &droppedFiles);
1156
1157 if (RT_SUCCESS(rc)) /** @todo Remove this block as soon as we hand in DnDURIList. */
1158 {
1159 if (pvData)
1160 {
1161 /* Reuse data buffer to fill in the transformed URI file list. */
1162 RTMemFree(pvData);
1163 pvData = NULL;
1164 }
1165
1166 RTCString strData = lstURI.RootToString(droppedFiles.GetDirAbs());
1167 Assert(!strData.isEmpty());
1168
1169 cbData = strData.length() + 1;
1170 LogFlowFunc(("URI list has %zu bytes\n", cbData));
1171
1172 pvData = RTMemAlloc(cbData);
1173 if (pvData)
1174 {
1175 memcpy(pvData, strData.c_str(), cbData);
1176 }
1177 else
1178 rc = VERR_NO_MEMORY;
1179 }
1180 }
1181 else /* Raw data. */
1182 {
1183 const uint32_t cbDataRaw = dataHdr.cbMetaFmt;
1184 if (cbData >= cbDataRaw)
1185 {
1186 if (cbDataRaw)
1187 memcpy(pvData, dataHdr.pvMetaFmt, cbDataRaw);
1188 cbData = cbDataRaw;
1189 }
1190 else
1191 rc = VERR_BUFFER_OVERFLOW;
1192 }
1193 }
1194
1195 if ( RT_FAILURE(rc)
1196 && rc != VERR_CANCELLED)
1197 {
1198 if (dataHdr.pvMetaFmt)
1199 RTMemFree(dataHdr.pvMetaFmt);
1200 if (pvData)
1201 RTMemFree(pvData);
1202
1203 int rc2 = VbglR3DnDHGSendProgress(pCtx, DND_PROGRESS_ERROR, 100 /* Percent */, rc);
1204 if (RT_FAILURE(rc2))
1205 LogFlowFunc(("Unable to send progress error %Rrc to host: %Rrc\n", rc, rc2));
1206 }
1207 else if (RT_SUCCESS(rc))
1208 {
1209 *ppszFormat = (char *)dataHdr.pvMetaFmt;
1210 *pcbFormat = dataHdr.cbMetaFmt;
1211 *ppvData = pvData;
1212 *pcbData = cbData;
1213 }
1214
1215 LogFlowFuncLeaveRC(rc);
1216 return rc;
1217}
1218
1219static int vbglR3DnDGHRecvPending(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t *puScreenId)
1220{
1221 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1222 AssertPtrReturn(puScreenId, VERR_INVALID_POINTER);
1223
1224 VBOXDNDGHREQPENDINGMSG Msg;
1225 RT_ZERO(Msg);
1226 Msg.hdr.result = VERR_WRONG_ORDER;
1227 Msg.hdr.u32ClientID = pCtx->uClientID;
1228 Msg.hdr.u32Function = HOST_DND_GH_REQ_PENDING;
1229
1230 if (pCtx->uProtocol < 3)
1231 {
1232 Msg.hdr.cParms = 1;
1233
1234 Msg.u.v1.uScreenId.SetUInt32(0);
1235 }
1236 else
1237 {
1238 Msg.hdr.cParms = 2;
1239
1240 /** @todo Context ID not used yet. */
1241 Msg.u.v3.uContext.SetUInt32(0);
1242 Msg.u.v3.uScreenId.SetUInt32(0);
1243 }
1244
1245 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1246 if (RT_SUCCESS(rc))
1247 {
1248 rc = Msg.hdr.result;
1249 if (RT_SUCCESS(rc))
1250 {
1251 if (pCtx->uProtocol < 3)
1252 {
1253 rc = Msg.u.v1.uScreenId.GetUInt32(puScreenId); AssertRC(rc);
1254 }
1255 else
1256 {
1257 /** @todo Context ID not used yet. */
1258 rc = Msg.u.v3.uContext.GetUInt32(puScreenId); AssertRC(rc);
1259 }
1260 }
1261 }
1262
1263 return rc;
1264}
1265
1266static int vbglR3DnDGHRecvDropped(PVBGLR3GUESTDNDCMDCTX pCtx,
1267 char *pszFormat,
1268 uint32_t cbFormat,
1269 uint32_t *pcbFormatRecv,
1270 uint32_t *puAction)
1271{
1272 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1273 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
1274 AssertReturn(cbFormat, VERR_INVALID_PARAMETER);
1275 AssertPtrReturn(pcbFormatRecv, VERR_INVALID_POINTER);
1276 AssertPtrReturn(puAction, VERR_INVALID_POINTER);
1277
1278 VBOXDNDGHDROPPEDMSG Msg;
1279 RT_ZERO(Msg);
1280 Msg.hdr.result = VERR_WRONG_ORDER;
1281 Msg.hdr.u32ClientID = pCtx->uClientID;
1282 Msg.hdr.u32Function = HOST_DND_GH_EVT_DROPPED;
1283
1284 if (pCtx->uProtocol < 3)
1285 {
1286 Msg.hdr.cParms = 3;
1287
1288 Msg.u.v1.pvFormat.SetPtr(pszFormat, cbFormat);
1289 Msg.u.v1.cbFormat.SetUInt32(0);
1290 Msg.u.v1.uAction.SetUInt32(0);
1291 }
1292 else
1293 {
1294 Msg.hdr.cParms = 4;
1295
1296 Msg.u.v3.uContext.SetUInt32(0);
1297 Msg.u.v3.pvFormat.SetPtr(pszFormat, cbFormat);
1298 Msg.u.v3.cbFormat.SetUInt32(0);
1299 Msg.u.v3.uAction.SetUInt32(0);
1300 }
1301
1302 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1303 if (RT_SUCCESS(rc))
1304 {
1305 rc = Msg.hdr.result;
1306 if (RT_SUCCESS(rc))
1307 {
1308 if (pCtx->uProtocol < 3)
1309 {
1310 rc = Msg.u.v1.cbFormat.GetUInt32(pcbFormatRecv); AssertRC(rc);
1311 rc = Msg.u.v1.uAction.GetUInt32(puAction); AssertRC(rc);
1312 }
1313 else
1314 {
1315 /** @todo Context ID not used yet. */
1316 rc = Msg.u.v3.cbFormat.GetUInt32(pcbFormatRecv); AssertRC(rc);
1317 rc = Msg.u.v3.uAction.GetUInt32(puAction); AssertRC(rc);
1318 }
1319
1320 AssertReturn(cbFormat >= *pcbFormatRecv, VERR_TOO_MUCH_DATA);
1321 }
1322 }
1323
1324 return rc;
1325}
1326
1327
1328/*********************************************************************************************************************************
1329* Public functions *
1330*********************************************************************************************************************************/
1331
1332VBGLR3DECL(int) VbglR3DnDConnect(PVBGLR3GUESTDNDCMDCTX pCtx)
1333{
1334 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1335
1336 /* Initialize header */
1337 VBoxGuestHGCMConnectInfo Info;
1338 RT_ZERO(Info.Loc.u);
1339 Info.result = VERR_WRONG_ORDER;
1340 Info.u32ClientID = UINT32_MAX; /* try make valgrind shut up. */
1341 Info.Loc.type = VMMDevHGCMLoc_LocalHost_Existing;
1342
1343 int rc = RTStrCopy(Info.Loc.u.host.achName, sizeof(Info.Loc.u.host.achName), "VBoxDragAndDropSvc");
1344 if (RT_FAILURE(rc))
1345 return rc;
1346
1347 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CONNECT, &Info, sizeof(Info));
1348 if (RT_SUCCESS(rc))
1349 {
1350 rc = Info.result;
1351 if (RT_SUCCESS(rc))
1352 {
1353 /* Set the default protocol version to use. */
1354 pCtx->uProtocol = 3;
1355
1356 Assert(Info.u32ClientID);
1357 pCtx->uClientID = Info.u32ClientID;
1358 }
1359 }
1360
1361 if (RT_FAILURE(rc))
1362 return rc;
1363
1364 /*
1365 * Get the VM's session ID.
1366 * This is not fatal in case we're running with an ancient VBox version.
1367 */
1368 pCtx->uSessionID = 0;
1369 int rc2 = VbglR3GetSessionId(&pCtx->uSessionID);
1370 LogFlowFunc(("uSessionID=%RU64, rc=%Rrc\n", pCtx->uSessionID, rc2));
1371
1372 /*
1373 * Check if the host is >= VBox 5.0 which in case supports GUEST_DND_CONNECT.
1374 */
1375 bool fSupportsConnectReq = false;
1376 if (RT_SUCCESS(rc))
1377 {
1378 /* The guest property service might not be available. Not fatal. */
1379 uint32_t uGuestPropSvcClientID;
1380 rc2 = VbglR3GuestPropConnect(&uGuestPropSvcClientID);
1381 if (RT_SUCCESS(rc2))
1382 {
1383 char *pszHostVersion;
1384 rc2 = VbglR3GuestPropReadValueAlloc(uGuestPropSvcClientID, "/VirtualBox/HostInfo/VBoxVer", &pszHostVersion);
1385 if (RT_SUCCESS(rc2))
1386 {
1387 fSupportsConnectReq = RTStrVersionCompare(pszHostVersion, "5.0") >= 0;
1388 LogFlowFunc(("pszHostVersion=%s, fSupportsConnectReq=%RTbool\n", pszHostVersion, fSupportsConnectReq));
1389 VbglR3GuestPropReadValueFree(pszHostVersion);
1390 }
1391
1392 VbglR3GuestPropDisconnect(uGuestPropSvcClientID);
1393 }
1394
1395 if (RT_FAILURE(rc2))
1396 LogFlowFunc(("Retrieving host version failed with rc=%Rrc\n", rc2));
1397 }
1398
1399 if (fSupportsConnectReq)
1400 {
1401 /*
1402 * Try sending the connect message to tell the protocol version to use.
1403 * Note: This might fail when the Guest Additions run on an older VBox host (< VBox 5.0) which
1404 * does not implement this command.
1405 */
1406 VBOXDNDCONNECTMSG Msg;
1407 RT_ZERO(Msg);
1408 Msg.hdr.result = VERR_WRONG_ORDER;
1409 Msg.hdr.u32ClientID = pCtx->uClientID;
1410 Msg.hdr.u32Function = GUEST_DND_CONNECT;
1411
1412 if (pCtx->uProtocol < 3)
1413 {
1414 Msg.hdr.cParms = 2;
1415
1416 Msg.u.v2.uProtocol.SetUInt32(pCtx->uProtocol);
1417 Msg.u.v2.uFlags.SetUInt32(0); /* Unused at the moment. */
1418 }
1419 else
1420 {
1421 Msg.hdr.cParms = 3;
1422
1423 /** @todo Context ID not used yet. */
1424 Msg.u.v3.uContext.SetUInt32(0);
1425 Msg.u.v3.uProtocol.SetUInt32(pCtx->uProtocol);
1426 Msg.u.v3.uFlags.SetUInt32(0); /* Unused at the moment. */
1427 }
1428
1429 rc2 = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1430 if (RT_SUCCESS(rc2))
1431 rc2 = Msg.hdr.result; /* Not fatal. */
1432
1433 if (RT_FAILURE(rc2))
1434 fSupportsConnectReq = false;
1435
1436 LogFlowFunc(("Connection request ended with rc=%Rrc\n", rc2));
1437 }
1438
1439 /* GUEST_DND_CONNECT not supported; play safe here and just use protocol v1. */
1440 if (!fSupportsConnectReq)
1441 pCtx->uProtocol = 1; /* Fall back to protocol version 1 (< VBox 5.0). */
1442
1443 pCtx->cbMaxChunkSize = _64K; /** @todo Use a scratch buffer on the heap? */
1444
1445 LogFlowFunc(("uClient=%RU32, uProtocol=%RU32, rc=%Rrc\n", pCtx->uClientID, pCtx->uProtocol, rc));
1446 return rc;
1447}
1448
1449VBGLR3DECL(int) VbglR3DnDDisconnect(PVBGLR3GUESTDNDCMDCTX pCtx)
1450{
1451 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1452
1453 VBoxGuestHGCMDisconnectInfo Info;
1454 Info.result = VERR_WRONG_ORDER;
1455 Info.u32ClientID = pCtx->uClientID;
1456
1457 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_DISCONNECT, &Info, sizeof(Info));
1458 if (RT_SUCCESS(rc))
1459 rc = Info.result;
1460
1461 return rc;
1462}
1463
1464VBGLR3DECL(int) VbglR3DnDRecvNextMsg(PVBGLR3GUESTDNDCMDCTX pCtx, CPVBGLR3DNDHGCMEVENT pEvent)
1465{
1466 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1467 AssertPtrReturn(pEvent, VERR_INVALID_POINTER);
1468
1469 uint32_t uMsg = 0;
1470 uint32_t uNumParms = 0;
1471
1472 const uint32_t cbDataMax = pCtx->cbMaxChunkSize;
1473 const uint32_t cbFormatMax = pCtx->cbMaxChunkSize;
1474
1475 int rc = vbglR3DnDGetNextMsgType(pCtx, &uMsg, &uNumParms, true /* fWait */);
1476 if (RT_SUCCESS(rc))
1477 {
1478 /* Check for VM session change. */
1479 uint64_t uSessionID;
1480 int rc2 = VbglR3GetSessionId(&uSessionID);
1481 if ( RT_SUCCESS(rc2)
1482 && (uSessionID != pCtx->uSessionID))
1483 {
1484 LogFlowFunc(("VM session ID changed to %RU64, doing reconnect\n", uSessionID));
1485
1486 /* Try a reconnect to the DnD service. */
1487 rc2 = VbglR3DnDDisconnect(pCtx);
1488 AssertRC(rc2);
1489 rc2 = VbglR3DnDConnect(pCtx);
1490 AssertRC(rc2);
1491
1492 /* At this point we continue processing the messsages with the new client ID. */
1493 }
1494 }
1495
1496 if (RT_SUCCESS(rc))
1497 {
1498 pEvent->uType = uMsg;
1499
1500 switch(uMsg)
1501 {
1502 case HOST_DND_HG_EVT_ENTER:
1503 case HOST_DND_HG_EVT_MOVE:
1504 case HOST_DND_HG_EVT_DROPPED:
1505 {
1506 pEvent->pszFormats = static_cast<char*>(RTMemAlloc(cbFormatMax));
1507 if (!pEvent->pszFormats)
1508 rc = VERR_NO_MEMORY;
1509
1510 if (RT_SUCCESS(rc))
1511 rc = vbglR3DnDHGRecvAction(pCtx,
1512 uMsg,
1513 &pEvent->uScreenId,
1514 &pEvent->u.a.uXpos,
1515 &pEvent->u.a.uYpos,
1516 &pEvent->u.a.uDefAction,
1517 &pEvent->u.a.uAllActions,
1518 pEvent->pszFormats,
1519 cbFormatMax,
1520 &pEvent->cbFormats);
1521 break;
1522 }
1523 case HOST_DND_HG_EVT_LEAVE:
1524 {
1525 rc = vbglR3DnDHGRecvLeave(pCtx);
1526 break;
1527 }
1528 case HOST_DND_HG_SND_DATA:
1529 /* Protocol v1 + v2: Also contains the header data.
1530 * Note: Fall through is intentional. */
1531 case HOST_DND_HG_SND_DATA_HDR:
1532 {
1533 rc = vbglR3DnDHGRecvDataMain(pCtx,
1534 /* Screen ID */
1535 &pEvent->uScreenId,
1536 /* Format */
1537 &pEvent->pszFormats,
1538 &pEvent->cbFormats,
1539 /* Data */
1540 &pEvent->u.b.pvData,
1541 &pEvent->u.b.cbData);
1542 break;
1543 }
1544 case HOST_DND_HG_SND_MORE_DATA:
1545 case HOST_DND_HG_SND_DIR:
1546 case HOST_DND_HG_SND_FILE_DATA:
1547 {
1548 /*
1549 * All messages in this case are handled internally
1550 * by vbglR3DnDHGRecvDataMain() and must be specified
1551 * by a preceding HOST_DND_HG_SND_DATA or HOST_DND_HG_SND_DATA_HDR
1552 * calls.
1553 */
1554 rc = VERR_WRONG_ORDER;
1555 break;
1556 }
1557 case HOST_DND_HG_EVT_CANCEL:
1558 {
1559 rc = vbglR3DnDHGRecvCancel(pCtx);
1560 break;
1561 }
1562#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1563 case HOST_DND_GH_REQ_PENDING:
1564 {
1565 rc = vbglR3DnDGHRecvPending(pCtx, &pEvent->uScreenId);
1566 break;
1567 }
1568 case HOST_DND_GH_EVT_DROPPED:
1569 {
1570 pEvent->pszFormats = static_cast<char*>(RTMemAlloc(cbFormatMax));
1571 if (!pEvent->pszFormats)
1572 rc = VERR_NO_MEMORY;
1573
1574 if (RT_SUCCESS(rc))
1575 rc = vbglR3DnDGHRecvDropped(pCtx,
1576 pEvent->pszFormats,
1577 cbFormatMax,
1578 &pEvent->cbFormats,
1579 &pEvent->u.a.uDefAction);
1580 break;
1581 }
1582#endif
1583 default:
1584 {
1585 rc = VERR_NOT_SUPPORTED;
1586 break;
1587 }
1588 }
1589 }
1590
1591 if (RT_FAILURE(rc))
1592 LogFlowFunc(("Returning error %Rrc\n", rc));
1593 return rc;
1594}
1595
1596VBGLR3DECL(int) VbglR3DnDHGSendAckOp(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uAction)
1597{
1598 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1599
1600 VBOXDNDHGACKOPMSG Msg;
1601 RT_ZERO(Msg);
1602 Msg.hdr.result = VERR_WRONG_ORDER;
1603 Msg.hdr.u32ClientID = pCtx->uClientID;
1604 Msg.hdr.u32Function = GUEST_DND_HG_ACK_OP;
1605
1606 LogFlowFunc(("uProto=%RU32\n", pCtx->uProtocol));
1607
1608 if (pCtx->uProtocol < 3)
1609 {
1610 Msg.hdr.cParms = 1;
1611
1612 Msg.u.v1.uAction.SetUInt32(uAction);
1613 }
1614 else
1615 {
1616 Msg.hdr.cParms = 2;
1617
1618 /** @todo Context ID not used yet. */
1619 Msg.u.v3.uContext.SetUInt32(0);
1620 Msg.u.v3.uAction.SetUInt32(uAction);
1621 }
1622
1623 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1624 if (RT_SUCCESS(rc))
1625 rc = Msg.hdr.result;
1626
1627 return rc;
1628}
1629
1630VBGLR3DECL(int) VbglR3DnDHGSendReqData(PVBGLR3GUESTDNDCMDCTX pCtx, const char* pcszFormat)
1631{
1632 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1633 AssertPtrReturn(pcszFormat, VERR_INVALID_POINTER);
1634
1635 VBOXDNDHGREQDATAMSG Msg;
1636 RT_ZERO(Msg);
1637 Msg.hdr.result = VERR_WRONG_ORDER;
1638 Msg.hdr.u32ClientID = pCtx->uClientID;
1639 Msg.hdr.u32Function = GUEST_DND_HG_REQ_DATA;
1640
1641 if (!RTStrIsValidEncoding(pcszFormat))
1642 return VERR_INVALID_PARAMETER;
1643
1644 const uint32_t cbFormat = (uint32_t)strlen(pcszFormat) + 1; /* Include termination */
1645
1646 if (pCtx->uProtocol < 3)
1647 {
1648 Msg.hdr.cParms = 1;
1649
1650 Msg.u.v1.pvFormat.SetPtr((void*)pcszFormat, cbFormat);
1651 }
1652 else
1653 {
1654 Msg.hdr.cParms = 3;
1655
1656 /** @todo Context ID not used yet. */
1657 Msg.u.v3.uContext.SetUInt32(0);
1658 Msg.u.v3.pvFormat.SetPtr((void*)pcszFormat, cbFormat);
1659 Msg.u.v3.cbFormat.SetUInt32(cbFormat);
1660 }
1661
1662 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1663 if (RT_SUCCESS(rc))
1664 rc = Msg.hdr.result;
1665
1666 return rc;
1667}
1668
1669VBGLR3DECL(int) VbglR3DnDHGSendProgress(PVBGLR3GUESTDNDCMDCTX pCtx, uint32_t uStatus, uint8_t uPercent, int rcErr)
1670{
1671 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1672 AssertReturn(uStatus > DND_PROGRESS_UNKNOWN, VERR_INVALID_PARAMETER);
1673
1674 VBOXDNDHGEVTPROGRESSMSG Msg;
1675 RT_ZERO(Msg);
1676 Msg.hdr.result = VERR_WRONG_ORDER;
1677 Msg.hdr.u32ClientID = pCtx->uClientID;
1678 Msg.hdr.u32Function = uStatus;
1679
1680 if (pCtx->uProtocol < 3)
1681 {
1682 Msg.hdr.cParms = 3;
1683
1684 Msg.u.v1.uStatus.SetUInt32(uStatus);
1685 Msg.u.v1.uPercent.SetUInt32(uPercent);
1686 Msg.u.v1.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1687 }
1688 else
1689 {
1690 Msg.hdr.cParms = 4;
1691
1692 /** @todo Context ID not used yet. */
1693 Msg.u.v3.uContext.SetUInt32(0);
1694 Msg.u.v3.uStatus.SetUInt32(uStatus);
1695 Msg.u.v3.uPercent.SetUInt32(uPercent);
1696 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
1697 }
1698
1699 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1700 if (RT_SUCCESS(rc))
1701 rc = Msg.hdr.result;
1702
1703 return rc;
1704}
1705
1706#ifdef VBOX_WITH_DRAG_AND_DROP_GH
1707VBGLR3DECL(int) VbglR3DnDGHSendAckPending(PVBGLR3GUESTDNDCMDCTX pCtx,
1708 uint32_t uDefAction, uint32_t uAllActions, const char* pcszFormats, uint32_t cbFormats)
1709{
1710 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1711 AssertPtrReturn(pcszFormats, VERR_INVALID_POINTER);
1712 AssertReturn(cbFormats, VERR_INVALID_PARAMETER);
1713
1714 if (!RTStrIsValidEncoding(pcszFormats))
1715 return VERR_INVALID_UTF8_ENCODING;
1716
1717 VBOXDNDGHACKPENDINGMSG Msg;
1718 RT_ZERO(Msg);
1719 Msg.hdr.result = VERR_WRONG_ORDER;
1720 Msg.hdr.u32ClientID = pCtx->uClientID;
1721 Msg.hdr.u32Function = GUEST_DND_GH_ACK_PENDING;
1722
1723 if (pCtx->uProtocol < 3)
1724 {
1725 Msg.hdr.cParms = 3;
1726
1727 Msg.u.v1.uDefAction.SetUInt32(uDefAction);
1728 Msg.u.v1.uAllActions.SetUInt32(uAllActions);
1729 Msg.u.v1.pvFormats.SetPtr((void*)pcszFormats, cbFormats);
1730 }
1731 else
1732 {
1733 Msg.hdr.cParms = 5;
1734
1735 /** @todo Context ID not used yet. */
1736 Msg.u.v3.uContext.SetUInt32(0);
1737 Msg.u.v3.uDefAction.SetUInt32(uDefAction);
1738 Msg.u.v3.uAllActions.SetUInt32(uAllActions);
1739 Msg.u.v3.pvFormats.SetPtr((void*)pcszFormats, cbFormats);
1740 Msg.u.v3.cbFormats.SetUInt32(cbFormats);
1741 }
1742
1743 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1744 if (RT_SUCCESS(rc))
1745 rc = Msg.hdr.result;
1746
1747 return rc;
1748}
1749
1750static int vbglR3DnDGHSendDataInternal(PVBGLR3GUESTDNDCMDCTX pCtx,
1751 void *pvData, uint64_t cbData, PVBOXDNDSNDDATAHDR pDataHdr)
1752{
1753 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1754 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
1755 AssertReturn(cbData, VERR_INVALID_PARAMETER);
1756 /* cbAdditionalData is optional. */
1757 /* pDataHdr is optional in protocols < v3. */
1758
1759 int rc = VINF_SUCCESS;
1760
1761 /* For protocol v3 and up we need to send the data header first. */
1762 if (pCtx->uProtocol >= 3)
1763 {
1764 AssertPtrReturn(pDataHdr, VERR_INVALID_POINTER);
1765
1766 VBOXDNDGHSENDDATAHDRMSG Msg;
1767 RT_ZERO(Msg);
1768 Msg.hdr.result = VERR_WRONG_ORDER;
1769 Msg.hdr.u32ClientID = pCtx->uClientID;
1770 Msg.hdr.u32Function = GUEST_DND_GH_SND_DATA_HDR;
1771 Msg.hdr.cParms = 12;
1772
1773 Msg.uContext.SetUInt32(0); /** @todo Not used yet. */
1774 Msg.uFlags.SetUInt32(0); /** @todo Not used yet. */
1775 Msg.uScreenId.SetUInt32(0); /** @todo Not used for guest->host (yet). */
1776 Msg.cbTotal.SetUInt64(pDataHdr->cbTotal);
1777 Msg.cbMeta.SetUInt32(pDataHdr->cbMeta);
1778 Msg.pvMetaFmt.SetPtr(pDataHdr->pvMetaFmt, pDataHdr->cbMetaFmt);
1779 Msg.cbMetaFmt.SetUInt32(pDataHdr->cbMetaFmt);
1780 Msg.cObjects.SetUInt64(pDataHdr->cObjects);
1781 Msg.enmCompression.SetUInt32(0); /** @todo Not used yet. */
1782 Msg.enmChecksumType.SetUInt32(RTDIGESTTYPE_INVALID); /** @todo Not used yet. */
1783 Msg.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1784 Msg.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1785
1786 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1787 if (RT_SUCCESS(rc))
1788 rc = Msg.hdr.result;
1789
1790 LogFlowFunc(("cbTotal=%RU64, cbMeta=%RU32, cObjects=%RU64, rc=%Rrc\n",
1791 pDataHdr->cbTotal, pDataHdr->cbMeta, pDataHdr->cObjects, rc));
1792 }
1793
1794 if (RT_SUCCESS(rc))
1795 {
1796 VBOXDNDGHSENDDATAMSG Msg;
1797 RT_ZERO(Msg);
1798 Msg.hdr.result = VERR_WRONG_ORDER;
1799 Msg.hdr.u32ClientID = pCtx->uClientID;
1800 Msg.hdr.u32Function = GUEST_DND_GH_SND_DATA;
1801
1802 if (pCtx->uProtocol >= 3)
1803 {
1804 Msg.hdr.cParms = 5;
1805
1806 Msg.u.v3.uContext.SetUInt32(0); /** @todo Not used yet. */
1807 Msg.u.v3.pvChecksum.SetPtr(NULL, 0); /** @todo Not used yet. */
1808 Msg.u.v3.cbChecksum.SetUInt32(0); /** @todo Not used yet. */
1809 }
1810 else
1811 {
1812 Msg.hdr.cParms = 2;
1813
1814 /* Total amount of bytes to send (meta data + all directory/file objects). */
1815 /* Note: Only supports uint32_t, so this is *not* a typo. */
1816 Msg.u.v1.cbTotalBytes.SetUInt32((uint32_t)pDataHdr->cbTotal);
1817 }
1818
1819 uint32_t cbCurChunk;
1820 const uint32_t cbMaxChunk = pCtx->cbMaxChunkSize;
1821 uint32_t cbSent = 0;
1822
1823 HGCMFunctionParameter *pParm = (pCtx->uProtocol >= 3)
1824 ? &Msg.u.v3.pvData
1825 : &Msg.u.v1.pvData;
1826 while (cbSent < cbData)
1827 {
1828 cbCurChunk = RT_MIN(cbData - cbSent, cbMaxChunk);
1829 pParm->SetPtr(static_cast<uint8_t *>(pvData) + cbSent, cbCurChunk);
1830 if (pCtx->uProtocol > 2)
1831 Msg.u.v3.cbData.SetUInt32(cbCurChunk);
1832
1833 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1834 if (RT_SUCCESS(rc))
1835 rc = Msg.hdr.result;
1836
1837 if (RT_FAILURE(rc))
1838 break;
1839
1840 cbSent += cbCurChunk;
1841 }
1842
1843 LogFlowFunc(("cbMaxChunk=%RU32, cbData=%RU64, cbSent=%RU32, rc=%Rrc\n",
1844 cbMaxChunk, cbData, cbSent, rc));
1845
1846 if (RT_SUCCESS(rc))
1847 Assert(cbSent == cbData);
1848 }
1849
1850 LogFlowFuncLeaveRC(rc);
1851 return rc;
1852}
1853
1854static int vbglR3DnDGHSendDir(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
1855{
1856 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1857 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1858 AssertReturn(pObj->GetType() == DnDURIObject::Directory, VERR_INVALID_PARAMETER);
1859
1860 RTCString strPath = pObj->GetDestPath();
1861 LogFlowFunc(("strDir=%s (%zu), fMode=0x%x\n",
1862 strPath.c_str(), strPath.length(), pObj->GetMode()));
1863
1864 if (strPath.length() > RTPATH_MAX)
1865 return VERR_INVALID_PARAMETER;
1866
1867 const uint32_t cbPath = (uint32_t)strPath.length() + 1; /* Include termination. */
1868
1869 VBOXDNDGHSENDDIRMSG Msg;
1870 RT_ZERO(Msg);
1871 Msg.hdr.result = VERR_WRONG_ORDER;
1872 Msg.hdr.u32ClientID = pCtx->uClientID;
1873 Msg.hdr.u32Function = GUEST_DND_GH_SND_DIR;
1874
1875 if (pCtx->uProtocol < 3)
1876 {
1877 Msg.hdr.cParms = 3;
1878
1879 Msg.u.v1.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)cbPath);
1880 Msg.u.v1.cbName.SetUInt32((uint32_t)cbPath);
1881 Msg.u.v1.fMode.SetUInt32(pObj->GetMode());
1882 }
1883 else
1884 {
1885 Msg.hdr.cParms = 4;
1886
1887 /** @todo Context ID not used yet. */
1888 Msg.u.v3.uContext.SetUInt32(0);
1889 Msg.u.v3.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)cbPath);
1890 Msg.u.v3.cbName.SetUInt32((uint32_t)cbPath);
1891 Msg.u.v3.fMode.SetUInt32(pObj->GetMode());
1892 }
1893
1894 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
1895 if (RT_SUCCESS(rc))
1896 rc = Msg.hdr.result;
1897
1898 LogFlowFuncLeaveRC(rc);
1899 return rc;
1900}
1901
1902static int vbglR3DnDGHSendFile(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
1903{
1904 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1905 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
1906 AssertReturn(pObj->GetType() == DnDURIObject::File, VERR_INVALID_PARAMETER);
1907 AssertReturn(pObj->IsOpen(), VERR_INVALID_STATE);
1908
1909 uint32_t cbBuf = _64K; /** @todo Make this configurable? */
1910 void *pvBuf = RTMemAlloc(cbBuf); /** @todo Make this buffer part of PVBGLR3GUESTDNDCMDCTX? */
1911 if (!pvBuf)
1912 return VERR_NO_MEMORY;
1913
1914 int rc;
1915
1916 RTCString strPath = pObj->GetDestPath();
1917
1918 LogFlowFunc(("strFile=%s (%zu), cbSize=%RU64, fMode=0x%x\n", strPath.c_str(), strPath.length(),
1919 pObj->GetSize(), pObj->GetMode()));
1920 LogFlowFunc(("uProtocol=%RU32, uClientID=%RU32\n", pCtx->uProtocol, pCtx->uClientID));
1921
1922 if (pCtx->uProtocol >= 2) /* Protocol version 2 and up sends a file header first. */
1923 {
1924 VBOXDNDGHSENDFILEHDRMSG MsgHdr;
1925 RT_ZERO(MsgHdr);
1926 MsgHdr.hdr.result = VERR_WRONG_ORDER;
1927 MsgHdr.hdr.u32ClientID = pCtx->uClientID;
1928 MsgHdr.hdr.u32Function = GUEST_DND_GH_SND_FILE_HDR;
1929 MsgHdr.hdr.cParms = 6;
1930
1931 MsgHdr.uContext.SetUInt32(0); /* Context ID; unused at the moment. */
1932 MsgHdr.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1));
1933 MsgHdr.cbName.SetUInt32((uint32_t)(strPath.length() + 1));
1934 MsgHdr.uFlags.SetUInt32(0); /* Flags; unused at the moment. */
1935 MsgHdr.fMode.SetUInt32(pObj->GetMode()); /* File mode */
1936 MsgHdr.cbTotal.SetUInt64(pObj->GetSize()); /* File size (in bytes). */
1937
1938 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(MsgHdr)), &MsgHdr, sizeof(MsgHdr));
1939 if (RT_SUCCESS(rc))
1940 rc = MsgHdr.hdr.result;
1941
1942 LogFlowFunc(("Sending file header resulted in %Rrc\n", rc));
1943 }
1944 else
1945 rc = VINF_SUCCESS;
1946
1947 if (RT_SUCCESS(rc))
1948 {
1949 /*
1950 * Send the actual file data, chunk by chunk.
1951 */
1952 VBOXDNDGHSENDFILEDATAMSG Msg;
1953 RT_ZERO(Msg);
1954 Msg.hdr.result = VERR_WRONG_ORDER;
1955 Msg.hdr.u32ClientID = pCtx->uClientID;
1956 Msg.hdr.u32Function = GUEST_DND_GH_SND_FILE_DATA;
1957
1958 switch (pCtx->uProtocol)
1959 {
1960 case 3:
1961 {
1962 Msg.hdr.cParms = 5;
1963
1964 Msg.u.v3.uContext.SetUInt32(0);
1965 Msg.u.v3.pvChecksum.SetPtr(NULL, 0);
1966 Msg.u.v3.cbChecksum.SetUInt32(0);
1967 break;
1968 }
1969
1970 case 2:
1971 {
1972 Msg.hdr.cParms = 3;
1973
1974 Msg.u.v2.uContext.SetUInt32(0);
1975 break;
1976 }
1977
1978 default: /* Protocol v1 */
1979 {
1980 Msg.hdr.cParms = 5;
1981
1982 Msg.u.v1.pvName.SetPtr((void *)strPath.c_str(), (uint32_t)(strPath.length() + 1));
1983 Msg.u.v1.cbName.SetUInt32((uint32_t)(strPath.length() + 1));
1984 Msg.u.v1.fMode.SetUInt32(pObj->GetMode());
1985 break;
1986 }
1987 }
1988
1989 uint64_t cbToReadTotal = pObj->GetSize();
1990 uint64_t cbWrittenTotal = 0;
1991 while (cbToReadTotal)
1992 {
1993 uint32_t cbToRead = RT_MIN(cbToReadTotal, cbBuf);
1994 uint32_t cbRead = 0;
1995 if (cbToRead)
1996 rc = pObj->Read(pvBuf, cbToRead, &cbRead);
1997
1998 LogFlowFunc(("cbToReadTotal=%RU64, cbToRead=%RU32, cbRead=%RU32, rc=%Rrc\n",
1999 cbToReadTotal, cbToRead, cbRead, rc));
2000
2001 if ( RT_SUCCESS(rc)
2002 && cbRead)
2003 {
2004 switch (pCtx->uProtocol)
2005 {
2006 case 3:
2007 {
2008 Msg.u.v3.pvData.SetPtr(pvBuf, cbRead);
2009 Msg.u.v3.cbData.SetUInt32(cbRead);
2010 /** @todo Calculate + set checksums. */
2011 break;
2012 }
2013
2014 case 2:
2015 {
2016 Msg.u.v2.pvData.SetPtr(pvBuf, cbRead);
2017 Msg.u.v2.cbData.SetUInt32(cbRead);
2018 break;
2019 }
2020
2021 default:
2022 {
2023 Msg.u.v1.pvData.SetPtr(pvBuf, cbRead);
2024 Msg.u.v1.cbData.SetUInt32(cbRead);
2025 break;
2026 }
2027 }
2028
2029 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
2030 if (RT_SUCCESS(rc))
2031 rc = Msg.hdr.result;
2032 }
2033
2034 if (RT_FAILURE(rc))
2035 {
2036 LogFlowFunc(("Reading failed with rc=%Rrc\n", rc));
2037 break;
2038 }
2039
2040 Assert(cbRead <= cbToReadTotal);
2041 cbToReadTotal -= cbRead;
2042 cbWrittenTotal += cbRead;
2043
2044 LogFlowFunc(("%RU64/%RU64 -- %RU8%%\n", cbWrittenTotal, pObj->GetSize(), cbWrittenTotal * 100 / pObj->GetSize()));
2045 };
2046 }
2047
2048 RTMemFree(pvBuf);
2049
2050 LogFlowFuncLeaveRC(rc);
2051 return rc;
2052}
2053
2054static int vbglR3DnDGHSendURIObject(PVBGLR3GUESTDNDCMDCTX pCtx, DnDURIObject *pObj)
2055{
2056 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2057 AssertPtrReturn(pObj, VERR_INVALID_POINTER);
2058
2059 int rc;
2060
2061 switch (pObj->GetType())
2062 {
2063 case DnDURIObject::Directory:
2064 rc = vbglR3DnDGHSendDir(pCtx, pObj);
2065 break;
2066
2067 case DnDURIObject::File:
2068 rc = vbglR3DnDGHSendFile(pCtx, pObj);
2069 break;
2070
2071 default:
2072 AssertMsgFailed(("Object type %ld not implemented\n", pObj->GetType()));
2073 rc = VERR_NOT_IMPLEMENTED;
2074 break;
2075 }
2076
2077 return rc;
2078}
2079
2080static int vbglR3DnDGHSendRawData(PVBGLR3GUESTDNDCMDCTX pCtx, void *pvData, size_t cbData)
2081{
2082 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2083 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
2084 /* cbData can be 0. */
2085
2086 VBOXDNDDATAHDR dataHdr;
2087 RT_ZERO(dataHdr);
2088
2089 /* For raw data only the total size is required to be specified. */
2090 dataHdr.cbTotal = cbData;
2091
2092 return vbglR3DnDGHSendDataInternal(pCtx, pvData, cbData, &dataHdr);
2093}
2094
2095static int vbglR3DnDGHSendURIData(PVBGLR3GUESTDNDCMDCTX pCtx, const void *pvData, size_t cbData)
2096{
2097 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2098 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
2099 AssertReturn(cbData, VERR_INVALID_PARAMETER);
2100
2101 RTCList<RTCString> lstPaths =
2102 RTCString((const char *)pvData, cbData).split("\r\n");
2103
2104 /** @todo Add symlink support (DNDURILIST_FLAGS_RESOLVE_SYMLINKS) here. */
2105 /** @todo Add lazy loading (DNDURILIST_FLAGS_LAZY) here. */
2106 uint32_t fFlags = DNDURILIST_FLAGS_KEEP_OPEN;
2107
2108 DnDURIList lstURI;
2109 int rc = lstURI.AppendURIPathsFromList(lstPaths, fFlags);
2110 if (RT_SUCCESS(rc))
2111 {
2112 /*
2113 * Send the (meta) data; in case of URIs it's the (non-recursive) file/directory
2114 * URI list the host needs to know upfront to set up the drag'n drop operation.
2115 */
2116 RTCString strRootDest = lstURI.RootToString();
2117 if (strRootDest.isNotEmpty())
2118 {
2119 void *pvURIList = (void *)strRootDest.c_str(); /* URI root list. */
2120 uint32_t cbURLIist = (uint32_t)strRootDest.length() + 1; /* Include string termination. */
2121
2122 /* The total size also contains the size of the meta data. */
2123 uint64_t cbTotal = cbURLIist;
2124 cbTotal += lstURI.TotalBytes();
2125
2126 /* We're going to send an URI list in text format. */
2127 const char szMetaFmt[] = "text/uri-list";
2128 const uint32_t cbMetaFmt = (uint32_t)strlen(szMetaFmt) + 1; /* Include termination. */
2129
2130 VBOXDNDDATAHDR dataHdr;
2131 dataHdr.uFlags = 0; /* Flags not used yet. */
2132 dataHdr.cbTotal = cbTotal;
2133 dataHdr.cbMeta = cbURLIist;
2134 dataHdr.pvMetaFmt = (void *)szMetaFmt;
2135 dataHdr.cbMetaFmt = cbMetaFmt;
2136 dataHdr.cObjects = lstURI.TotalCount();
2137
2138 rc = vbglR3DnDGHSendDataInternal(pCtx,
2139 pvURIList, cbURLIist, &dataHdr);
2140 }
2141 else
2142 rc = VERR_INVALID_PARAMETER;
2143 }
2144
2145 if (RT_SUCCESS(rc))
2146 {
2147 while (!lstURI.IsEmpty())
2148 {
2149 DnDURIObject *pNextObj = lstURI.First();
2150
2151 rc = vbglR3DnDGHSendURIObject(pCtx, pNextObj);
2152 if (RT_FAILURE(rc))
2153 break;
2154
2155 lstURI.RemoveFirst();
2156 }
2157 }
2158
2159 return rc;
2160}
2161
2162VBGLR3DECL(int) VbglR3DnDGHSendData(PVBGLR3GUESTDNDCMDCTX pCtx, const char *pszFormat, void *pvData, uint32_t cbData)
2163{
2164 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2165 AssertPtrReturn(pszFormat, VERR_INVALID_POINTER);
2166 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
2167 AssertReturn(cbData, VERR_INVALID_PARAMETER);
2168
2169 LogFlowFunc(("pszFormat=%s, pvData=%p, cbData=%RU32\n", pszFormat, pvData, cbData));
2170
2171 int rc;
2172 if (DnDMIMEHasFileURLs(pszFormat, strlen(pszFormat)))
2173 {
2174 /* Send file data. */
2175 rc = vbglR3DnDGHSendURIData(pCtx, pvData, cbData);
2176 }
2177 else
2178 rc = vbglR3DnDGHSendRawData(pCtx, pvData, cbData);
2179
2180 if (RT_FAILURE(rc))
2181 {
2182 int rc2 = VbglR3DnDGHSendError(pCtx, rc);
2183 if (RT_FAILURE(rc2))
2184 LogFlowFunc(("Unable to send error (%Rrc) to host, rc=%Rrc\n", rc, rc2));
2185 }
2186
2187 return rc;
2188}
2189
2190VBGLR3DECL(int) VbglR3DnDGHSendError(PVBGLR3GUESTDNDCMDCTX pCtx, int rcErr)
2191{
2192 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
2193
2194 VBOXDNDGHEVTERRORMSG Msg;
2195 RT_ZERO(Msg);
2196 Msg.hdr.result = VERR_WRONG_ORDER;
2197 Msg.hdr.u32ClientID = pCtx->uClientID;
2198 Msg.hdr.u32Function = GUEST_DND_GH_EVT_ERROR;
2199
2200 if (pCtx->uProtocol < 3)
2201 {
2202 Msg.hdr.cParms = 1;
2203
2204 Msg.u.v1.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
2205 }
2206 else
2207 {
2208 Msg.hdr.cParms = 2;
2209
2210 /** @todo Context ID not used yet. */
2211 Msg.u.v3.uContext.SetUInt32(0);
2212 Msg.u.v3.rc.SetUInt32((uint32_t)rcErr); /* uint32_t vs. int. */
2213 }
2214
2215 int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg));
2216 if (RT_SUCCESS(rc))
2217 {
2218 if (RT_FAILURE(Msg.hdr.result))
2219 {
2220 LogFlowFunc(("Sending error %Rrc failed with rc=%Rrc\n", rcErr, Msg.hdr.result));
2221
2222 /* Never return an error if the host did not accept the error at
2223 * the current time. This can be due to the host not having any appropriate
2224 * callbacks set which would handle that error. */
2225 rc = VINF_SUCCESS;
2226 }
2227 }
2228
2229 return rc;
2230}
2231#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
2232
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