VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestDnDTargetImpl.cpp@ 56616

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

DnD: GuestDnDTargetImpl.cpp: Use destination paths for data block.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.7 KB
Line 
1/* $Id: GuestDnDTargetImpl.cpp 56616 2015-06-24 09:58:45Z vboxsync $ */
2/** @file
3 * VBox Console COM Class implementation - Guest drag'n drop target.
4 */
5
6/*
7 * Copyright (C) 2014-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include "GuestImpl.h"
23#include "GuestDnDTargetImpl.h"
24#include "ConsoleImpl.h"
25
26#include "Global.h"
27#include "AutoCaller.h"
28
29#include <algorithm> /* For std::find(). */
30
31#include <iprt/asm.h>
32#include <iprt/file.h>
33#include <iprt/dir.h>
34#include <iprt/path.h>
35#include <iprt/uri.h>
36#include <iprt/cpp/utils.h> /* For unconst(). */
37
38#include <VBox/com/array.h>
39
40#include <VBox/GuestHost/DragAndDrop.h>
41#include <VBox/HostServices/Service.h>
42
43#ifdef LOG_GROUP
44 #undef LOG_GROUP
45#endif
46#define LOG_GROUP LOG_GROUP_GUEST_DND
47#include <VBox/log.h>
48
49
50/**
51 * Base class for a target task.
52 */
53class GuestDnDTargetTask
54{
55public:
56
57 GuestDnDTargetTask(GuestDnDTarget *pTarget)
58 : mTarget(pTarget),
59 mRC(VINF_SUCCESS) { }
60
61 virtual ~GuestDnDTargetTask(void) { }
62
63 int getRC(void) const { return mRC; }
64 bool isOk(void) const { return RT_SUCCESS(mRC); }
65 const ComObjPtr<GuestDnDTarget> &getTarget(void) const { return mTarget; }
66
67protected:
68
69 const ComObjPtr<GuestDnDTarget> mTarget;
70 int mRC;
71};
72
73/**
74 * Task structure for sending data to a target using
75 * a worker thread.
76 */
77class SendDataTask : public GuestDnDTargetTask
78{
79public:
80
81 SendDataTask(GuestDnDTarget *pTarget, PSENDDATACTX pCtx)
82 : GuestDnDTargetTask(pTarget),
83 mpCtx(pCtx) { }
84
85 virtual ~SendDataTask(void)
86 {
87 if (mpCtx)
88 {
89 delete mpCtx;
90 mpCtx = NULL;
91 }
92 }
93
94
95 PSENDDATACTX getCtx(void) { return mpCtx; }
96
97protected:
98
99 /** Pointer to send data context. */
100 PSENDDATACTX mpCtx;
101};
102
103// constructor / destructor
104/////////////////////////////////////////////////////////////////////////////
105
106DEFINE_EMPTY_CTOR_DTOR(GuestDnDTarget)
107
108HRESULT GuestDnDTarget::FinalConstruct(void)
109{
110 /* Set the maximum block size our guests can handle to 64K. This always has
111 * been hardcoded until now. */
112 /* Note: Never ever rely on information from the guest; the host dictates what and
113 * how to do something, so try to negogiate a sensible value here later. */
114 mData.mcbBlockSize = _64K; /** @todo Make this configurable. */
115
116 LogFlowThisFunc(("\n"));
117 return BaseFinalConstruct();
118}
119
120void GuestDnDTarget::FinalRelease(void)
121{
122 LogFlowThisFuncEnter();
123 uninit();
124 BaseFinalRelease();
125 LogFlowThisFuncLeave();
126}
127
128// public initializer/uninitializer for internal purposes only
129/////////////////////////////////////////////////////////////////////////////
130
131int GuestDnDTarget::init(const ComObjPtr<Guest>& pGuest)
132{
133 LogFlowThisFuncEnter();
134
135 /* Enclose the state transition NotReady->InInit->Ready. */
136 AutoInitSpan autoInitSpan(this);
137 AssertReturn(autoInitSpan.isOk(), E_FAIL);
138
139 unconst(m_pGuest) = pGuest;
140
141 /* Confirm a successful initialization when it's the case. */
142 autoInitSpan.setSucceeded();
143
144 return VINF_SUCCESS;
145}
146
147/**
148 * Uninitializes the instance.
149 * Called from FinalRelease().
150 */
151void GuestDnDTarget::uninit(void)
152{
153 LogFlowThisFunc(("\n"));
154
155 /* Enclose the state transition Ready->InUninit->NotReady. */
156 AutoUninitSpan autoUninitSpan(this);
157 if (autoUninitSpan.uninitDone())
158 return;
159}
160
161// implementation of wrapped IDnDBase methods.
162/////////////////////////////////////////////////////////////////////////////
163
164HRESULT GuestDnDTarget::isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
165{
166#if !defined(VBOX_WITH_DRAG_AND_DROP)
167 ReturnComNotImplemented();
168#else /* VBOX_WITH_DRAG_AND_DROP */
169
170 AutoCaller autoCaller(this);
171 if (FAILED(autoCaller.rc())) return autoCaller.rc();
172
173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
174
175 return GuestDnDBase::i_isFormatSupported(aFormat, aSupported);
176#endif /* VBOX_WITH_DRAG_AND_DROP */
177}
178
179HRESULT GuestDnDTarget::getFormats(std::vector<com::Utf8Str> &aFormats)
180{
181#if !defined(VBOX_WITH_DRAG_AND_DROP)
182 ReturnComNotImplemented();
183#else /* VBOX_WITH_DRAG_AND_DROP */
184
185 AutoCaller autoCaller(this);
186 if (FAILED(autoCaller.rc())) return autoCaller.rc();
187
188 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
189
190 return GuestDnDBase::i_getFormats(aFormats);
191#endif /* VBOX_WITH_DRAG_AND_DROP */
192}
193
194HRESULT GuestDnDTarget::addFormats(const std::vector<com::Utf8Str> &aFormats)
195{
196#if !defined(VBOX_WITH_DRAG_AND_DROP)
197 ReturnComNotImplemented();
198#else /* VBOX_WITH_DRAG_AND_DROP */
199
200 AutoCaller autoCaller(this);
201 if (FAILED(autoCaller.rc())) return autoCaller.rc();
202
203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
204
205 return GuestDnDBase::i_addFormats(aFormats);
206#endif /* VBOX_WITH_DRAG_AND_DROP */
207}
208
209HRESULT GuestDnDTarget::removeFormats(const std::vector<com::Utf8Str> &aFormats)
210{
211#if !defined(VBOX_WITH_DRAG_AND_DROP)
212 ReturnComNotImplemented();
213#else /* VBOX_WITH_DRAG_AND_DROP */
214
215 AutoCaller autoCaller(this);
216 if (FAILED(autoCaller.rc())) return autoCaller.rc();
217
218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
219
220 return GuestDnDBase::i_removeFormats(aFormats);
221#endif /* VBOX_WITH_DRAG_AND_DROP */
222}
223
224HRESULT GuestDnDTarget::getProtocolVersion(ULONG *aProtocolVersion)
225{
226#if !defined(VBOX_WITH_DRAG_AND_DROP)
227 ReturnComNotImplemented();
228#else /* VBOX_WITH_DRAG_AND_DROP */
229
230 AutoCaller autoCaller(this);
231 if (FAILED(autoCaller.rc())) return autoCaller.rc();
232
233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
234
235 return GuestDnDBase::i_getProtocolVersion(aProtocolVersion);
236#endif /* VBOX_WITH_DRAG_AND_DROP */
237}
238
239// implementation of wrapped IDnDTarget methods.
240/////////////////////////////////////////////////////////////////////////////
241
242HRESULT GuestDnDTarget::enter(ULONG aScreenId, ULONG aX, ULONG aY,
243 DnDAction_T aDefaultAction,
244 const std::vector<DnDAction_T> &aAllowedActions,
245 const std::vector<com::Utf8Str> &aFormats,
246 DnDAction_T *aResultAction)
247{
248#if !defined(VBOX_WITH_DRAG_AND_DROP)
249 ReturnComNotImplemented();
250#else /* VBOX_WITH_DRAG_AND_DROP */
251
252 /* Input validation. */
253 if (aDefaultAction == DnDAction_Ignore)
254 return setError(E_INVALIDARG, tr("No default action specified"));
255 if (!aAllowedActions.size())
256 return setError(E_INVALIDARG, tr("Number of allowed actions is empty"));
257 if (!aFormats.size())
258 return setError(E_INVALIDARG, tr("Number of supported formats is empty"));
259
260 AutoCaller autoCaller(this);
261 if (FAILED(autoCaller.rc())) return autoCaller.rc();
262
263 /* Determine guest DnD protocol to use. */
264 GuestDnDBase::getProtocolVersion(&mDataBase.mProtocolVersion);
265
266 /* Default action is ignoring. */
267 DnDAction_T resAction = DnDAction_Ignore;
268
269 /* Check & convert the drag & drop actions */
270 uint32_t uDefAction = 0;
271 uint32_t uAllowedActions = 0;
272 GuestDnD::toHGCMActions(aDefaultAction, &uDefAction,
273 aAllowedActions, &uAllowedActions);
274 /* If there is no usable action, ignore this request. */
275 if (isDnDIgnoreAction(uDefAction))
276 return S_OK;
277
278 /* Make a flat data string out of the supported format list. */
279 Utf8Str strFormats = GuestDnD::toFormatString(m_strFormats, aFormats);
280 /* If there is no valid supported format, ignore this request. */
281 if (strFormats.isEmpty())
282 return S_OK;
283
284 HRESULT hr = S_OK;
285
286 /* Adjust the coordinates in a multi-monitor setup. */
287 int rc = GuestDnDInst()->adjustScreenCoordinates(aScreenId, &aX, &aY);
288 if (RT_SUCCESS(rc))
289 {
290 GuestDnDMsg Msg;
291 Msg.setType(DragAndDropSvc::HOST_DND_HG_EVT_ENTER);
292 Msg.setNextUInt32(aScreenId);
293 Msg.setNextUInt32(aX);
294 Msg.setNextUInt32(aY);
295 Msg.setNextUInt32(uDefAction);
296 Msg.setNextUInt32(uAllowedActions);
297 Msg.setNextPointer((void*)strFormats.c_str(), strFormats.length() + 1);
298 Msg.setNextUInt32(strFormats.length() + 1);
299
300 rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
301 if (RT_SUCCESS(rc))
302 {
303 GuestDnDResponse *pResp = GuestDnDInst()->response();
304 if (pResp && RT_SUCCESS(pResp->waitForGuestResponse()))
305 resAction = GuestDnD::toMainAction(pResp->defAction());
306 }
307 }
308
309 if (aResultAction)
310 *aResultAction = resAction;
311
312 LogFlowFunc(("hr=%Rhrc, resAction=%ld\n", hr, resAction));
313 return hr;
314#endif /* VBOX_WITH_DRAG_AND_DROP */
315}
316
317HRESULT GuestDnDTarget::move(ULONG aScreenId, ULONG aX, ULONG aY,
318 DnDAction_T aDefaultAction,
319 const std::vector<DnDAction_T> &aAllowedActions,
320 const std::vector<com::Utf8Str> &aFormats,
321 DnDAction_T *aResultAction)
322{
323#if !defined(VBOX_WITH_DRAG_AND_DROP)
324 ReturnComNotImplemented();
325#else /* VBOX_WITH_DRAG_AND_DROP */
326
327 /* Input validation. */
328
329 AutoCaller autoCaller(this);
330 if (FAILED(autoCaller.rc())) return autoCaller.rc();
331
332 /* Default action is ignoring. */
333 DnDAction_T resAction = DnDAction_Ignore;
334
335 /* Check & convert the drag & drop actions. */
336 uint32_t uDefAction = 0;
337 uint32_t uAllowedActions = 0;
338 GuestDnD::toHGCMActions(aDefaultAction, &uDefAction,
339 aAllowedActions, &uAllowedActions);
340 /* If there is no usable action, ignore this request. */
341 if (isDnDIgnoreAction(uDefAction))
342 return S_OK;
343
344 /* Make a flat data string out of the supported format list. */
345 RTCString strFormats = GuestDnD::toFormatString(m_strFormats, aFormats);
346 /* If there is no valid supported format, ignore this request. */
347 if (strFormats.isEmpty())
348 return S_OK;
349
350 HRESULT hr = S_OK;
351
352 int rc = GuestDnDInst()->adjustScreenCoordinates(aScreenId, &aX, &aY);
353 if (RT_SUCCESS(rc))
354 {
355 GuestDnDMsg Msg;
356 Msg.setType(DragAndDropSvc::HOST_DND_HG_EVT_MOVE);
357 Msg.setNextUInt32(aScreenId);
358 Msg.setNextUInt32(aX);
359 Msg.setNextUInt32(aY);
360 Msg.setNextUInt32(uDefAction);
361 Msg.setNextUInt32(uAllowedActions);
362 Msg.setNextPointer((void*)strFormats.c_str(), strFormats.length() + 1);
363 Msg.setNextUInt32(strFormats.length() + 1);
364
365 rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
366 if (RT_SUCCESS(rc))
367 {
368 GuestDnDResponse *pResp = GuestDnDInst()->response();
369 if (pResp && RT_SUCCESS(pResp->waitForGuestResponse()))
370 resAction = GuestDnD::toMainAction(pResp->defAction());
371 }
372 }
373
374 if (aResultAction)
375 *aResultAction = resAction;
376
377 LogFlowFunc(("hr=%Rhrc, *pResultAction=%ld\n", hr, resAction));
378 return hr;
379#endif /* VBOX_WITH_DRAG_AND_DROP */
380}
381
382HRESULT GuestDnDTarget::leave(ULONG uScreenId)
383{
384#if !defined(VBOX_WITH_DRAG_AND_DROP)
385 ReturnComNotImplemented();
386#else /* VBOX_WITH_DRAG_AND_DROP */
387
388 AutoCaller autoCaller(this);
389 if (FAILED(autoCaller.rc())) return autoCaller.rc();
390
391 HRESULT hr = S_OK;
392 int rc = GuestDnDInst()->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_LEAVE,
393 0 /* cParms */, NULL /* paParms */);
394 if (RT_SUCCESS(rc))
395 {
396 GuestDnDResponse *pResp = GuestDnDInst()->response();
397 if (pResp)
398 pResp->waitForGuestResponse();
399 }
400
401 LogFlowFunc(("hr=%Rhrc\n", hr));
402 return hr;
403#endif /* VBOX_WITH_DRAG_AND_DROP */
404}
405
406HRESULT GuestDnDTarget::drop(ULONG aScreenId, ULONG aX, ULONG aY,
407 DnDAction_T aDefaultAction,
408 const std::vector<DnDAction_T> &aAllowedActions,
409 const std::vector<com::Utf8Str> &aFormats,
410 com::Utf8Str &aFormat, DnDAction_T *aResultAction)
411{
412#if !defined(VBOX_WITH_DRAG_AND_DROP)
413 ReturnComNotImplemented();
414#else /* VBOX_WITH_DRAG_AND_DROP */
415
416 /* Input validation. */
417
418 /* Everything else is optional. */
419
420 AutoCaller autoCaller(this);
421 if (FAILED(autoCaller.rc())) return autoCaller.rc();
422
423 /* Default action is ignoring. */
424 DnDAction_T resAction = DnDAction_Ignore;
425
426 /* Check & convert the drag & drop actions. */
427 uint32_t uDefAction = 0;
428 uint32_t uAllowedActions = 0;
429 GuestDnD::toHGCMActions(aDefaultAction, &uDefAction,
430 aAllowedActions, &uAllowedActions);
431 /* If there is no usable action, ignore this request. */
432 if (isDnDIgnoreAction(uDefAction))
433 return S_OK;
434
435 /* Make a flat data string out of the supported format list. */
436 Utf8Str strFormats = GuestDnD::toFormatString(m_strFormats, aFormats);
437 /* If there is no valid supported format, ignore this request. */
438 if (strFormats.isEmpty())
439 return S_OK;
440
441 HRESULT hr = S_OK;
442
443 /* Adjust the coordinates in a multi-monitor setup. */
444 int rc = GuestDnDInst()->adjustScreenCoordinates(aScreenId, &aX, &aY);
445 if (RT_SUCCESS(rc))
446 {
447 GuestDnDMsg Msg;
448 Msg.setType(DragAndDropSvc::HOST_DND_HG_EVT_DROPPED);
449 Msg.setNextUInt32(aScreenId);
450 Msg.setNextUInt32(aX);
451 Msg.setNextUInt32(aY);
452 Msg.setNextUInt32(uDefAction);
453 Msg.setNextUInt32(uAllowedActions);
454 Msg.setNextPointer((void*)strFormats.c_str(), strFormats.length() + 1);
455 Msg.setNextUInt32(strFormats.length() + 1);
456
457 rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
458 if (RT_SUCCESS(rc))
459 {
460 GuestDnDResponse *pResp = GuestDnDInst()->response();
461 if (pResp && RT_SUCCESS(pResp->waitForGuestResponse()))
462 {
463 resAction = GuestDnD::toMainAction(pResp->defAction());
464 aFormat = pResp->format();
465
466 LogFlowFunc(("resFormat=%s, resAction=%RU32\n",
467 pResp->format().c_str(), pResp->defAction()));
468 }
469 }
470 }
471
472 if (aResultAction)
473 *aResultAction = resAction;
474
475 return hr;
476#endif /* VBOX_WITH_DRAG_AND_DROP */
477}
478
479/* static */
480DECLCALLBACK(int) GuestDnDTarget::i_sendDataThread(RTTHREAD Thread, void *pvUser)
481{
482 LogFlowFunc(("pvUser=%p\n", pvUser));
483
484 SendDataTask *pTask = (SendDataTask *)pvUser;
485 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
486
487 const ComObjPtr<GuestDnDTarget> pTarget(pTask->getTarget());
488 Assert(!pTarget.isNull());
489
490 int rc;
491
492 AutoCaller autoCaller(pTarget);
493 if (SUCCEEDED(autoCaller.rc()))
494 {
495 rc = pTarget->i_sendData(pTask->getCtx(), RT_INDEFINITE_WAIT /* msTimeout */);
496 /* Nothing to do here anymore. */
497 }
498 else
499 rc = VERR_COM_INVALID_OBJECT_STATE;
500
501 ASMAtomicWriteBool(&pTarget->mDataBase.mfTransferIsPending, false);
502
503 if (pTask)
504 delete pTask;
505
506 LogFlowFunc(("pTarget=%p returning rc=%Rrc\n", (GuestDnDTarget *)pTarget, rc));
507 return rc;
508}
509
510/**
511 * Initiates a data transfer from the host to the guest. The source is the host whereas the target is the
512 * guest in this case.
513 *
514 * @return HRESULT
515 * @param aScreenId
516 * @param aFormat
517 * @param aData
518 * @param aProgress
519 */
520HRESULT GuestDnDTarget::sendData(ULONG aScreenId, const com::Utf8Str &aFormat, const std::vector<BYTE> &aData,
521 ComPtr<IProgress> &aProgress)
522{
523#if !defined(VBOX_WITH_DRAG_AND_DROP)
524 ReturnComNotImplemented();
525#else /* VBOX_WITH_DRAG_AND_DROP */
526
527 AutoCaller autoCaller(this);
528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
529
530 /* Input validation. */
531 if (RT_UNLIKELY((aFormat.c_str()) == NULL || *(aFormat.c_str()) == '\0'))
532 return setError(E_INVALIDARG, tr("No data format specified"));
533 if (RT_UNLIKELY(!aData.size()))
534 return setError(E_INVALIDARG, tr("No data to send specified"));
535
536 /* Note: At the moment we only support one transfer at a time. */
537 if (ASMAtomicReadBool(&mDataBase.mfTransferIsPending))
538 return setError(E_INVALIDARG, tr("Another send operation already is in progress"));
539
540 ASMAtomicWriteBool(&mDataBase.mfTransferIsPending, true);
541
542 /* Dito. */
543 GuestDnDResponse *pResp = GuestDnDInst()->response();
544 AssertPtr(pResp);
545
546 HRESULT hr = pResp->resetProgress(m_pGuest);
547 if (FAILED(hr))
548 return hr;
549
550 try
551 {
552 PSENDDATACTX pSendCtx = new SENDDATACTX;
553 RT_BZERO(pSendCtx, sizeof(SENDDATACTX));
554
555 pSendCtx->mpTarget = this;
556 pSendCtx->mpResp = pResp;
557 pSendCtx->mScreenID = aScreenId;
558 pSendCtx->mFormat = aFormat;
559 pSendCtx->mData.vecData = aData;
560
561 SendDataTask *pTask = new SendDataTask(this, pSendCtx);
562 AssertReturn(pTask->isOk(), pTask->getRC());
563
564 LogFlowFunc(("Starting thread ...\n"));
565
566 int rc = RTThreadCreate(NULL, GuestDnDTarget::i_sendDataThread,
567 (void *)pTask, 0, RTTHREADTYPE_MAIN_WORKER, 0, "dndTgtSndData");
568 if (RT_SUCCESS(rc))
569 {
570 hr = pResp->queryProgressTo(aProgress.asOutParam());
571 ComAssertComRC(hr);
572
573 /* Note: pTask is now owned by the worker thread. */
574 }
575 else
576 hr = setError(VBOX_E_IPRT_ERROR, tr("Starting thread failed (%Rrc)"), rc);
577
578 if (RT_FAILURE(rc))
579 delete pSendCtx;
580 }
581 catch(std::bad_alloc &)
582 {
583 hr = setError(E_OUTOFMEMORY);
584 }
585
586 /* Note: mDataBase.mfTransferIsPending will be set to false again by i_sendDataThread. */
587
588 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
589 return hr;
590#endif /* VBOX_WITH_DRAG_AND_DROP */
591}
592
593int GuestDnDTarget::i_cancelOperation(void)
594{
595 /** @todo Check for pending cancel requests. */
596
597#if 0 /** @todo Later. */
598 /* Cancel any outstanding waits for guest responses first. */
599 if (pResp)
600 pResp->notifyAboutGuestResponse();
601#endif
602
603 LogFlowFunc(("Cancelling operation, telling guest ...\n"));
604 return GuestDnDInst()->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_CANCEL, 0 /* cParms */, NULL /*paParms*/);
605}
606
607/* static */
608Utf8Str GuestDnDTarget::i_guestErrorToString(int guestRc)
609{
610 Utf8Str strError;
611
612 switch (guestRc)
613 {
614 case VERR_ACCESS_DENIED:
615 strError += Utf8StrFmt(tr("For one or more guest files or directories selected for transferring to the host your guest "
616 "user does not have the appropriate access rights for. Please make sure that all selected "
617 "elements can be accessed and that your guest user has the appropriate rights"));
618 break;
619
620 case VERR_NOT_FOUND:
621 /* Should not happen due to file locking on the guest, but anyway ... */
622 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were not"
623 "found on the guest anymore. This can be the case if the guest files were moved and/or"
624 "altered while the drag and drop operation was in progress"));
625 break;
626
627 case VERR_SHARING_VIOLATION:
628 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were locked. "
629 "Please make sure that all selected elements can be accessed and that your guest user has "
630 "the appropriate rights"));
631 break;
632
633 case VERR_TIMEOUT:
634 strError += Utf8StrFmt(tr("The guest was not able to process the drag and drop data within time"));
635 break;
636
637 default:
638 strError += Utf8StrFmt(tr("Drag and drop error from guest (%Rrc)"), guestRc);
639 break;
640 }
641
642 return strError;
643}
644
645/* static */
646Utf8Str GuestDnDTarget::i_hostErrorToString(int hostRc)
647{
648 Utf8Str strError;
649
650 switch (hostRc)
651 {
652 case VERR_ACCESS_DENIED:
653 strError += Utf8StrFmt(tr("For one or more host files or directories selected for transferring to the guest your host "
654 "user does not have the appropriate access rights for. Please make sure that all selected "
655 "elements can be accessed and that your host user has the appropriate rights."));
656 break;
657
658 case VERR_NOT_FOUND:
659 /* Should not happen due to file locking on the host, but anyway ... */
660 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the host were not"
661 "found on the host anymore. This can be the case if the host files were moved and/or"
662 "altered while the drag and drop operation was in progress."));
663 break;
664
665 case VERR_SHARING_VIOLATION:
666 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the guest were locked. "
667 "Please make sure that all selected elements can be accessed and that your host user has "
668 "the appropriate rights."));
669 break;
670
671 default:
672 strError += Utf8StrFmt(tr("Drag and drop error from host (%Rrc)"), hostRc);
673 break;
674 }
675
676 return strError;
677}
678
679int GuestDnDTarget::i_sendData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
680{
681 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
682
683 GuestDnD *pInst = GuestDnDInst();
684 if (!pInst)
685 return VERR_INVALID_POINTER;
686
687 int rc;
688
689 ASMAtomicWriteBool(&pCtx->mIsActive, true);
690
691 /* Clear all remaining outgoing messages. */
692 mDataBase.mListOutgoing.clear();
693
694 const char *pszFormat = pCtx->mFormat.c_str();
695 uint32_t cbFormat = pCtx->mFormat.length() + 1;
696
697 /* Do we need to build up a file tree? */
698 bool fHasURIList = DnDMIMEHasFileURLs(pszFormat, cbFormat);
699 if (fHasURIList)
700 {
701 rc = i_sendURIData(pCtx, msTimeout);
702 }
703 else
704 {
705 rc = i_sendRawData(pCtx, msTimeout);
706 }
707
708 ASMAtomicWriteBool(&pCtx->mIsActive, false);
709
710#undef DATA_IS_VALID_BREAK
711
712 LogFlowFuncLeaveRC(rc);
713 return rc;
714}
715
716int GuestDnDTarget::i_sendDirectory(PSENDDATACTX pCtx, GuestDnDMsg *pMsg, DnDURIObject &aDirectory)
717{
718 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
719
720 RTCString strPath = aDirectory.GetDestPath();
721 if (strPath.isEmpty())
722 return VERR_INVALID_PARAMETER;
723 if (strPath.length() >= RTPATH_MAX) /* Note: Maximum is RTPATH_MAX on guest side. */
724 return VERR_BUFFER_OVERFLOW;
725
726 LogFlowFunc(("Sending directory \"%s\" using protocol v%RU32 ...\n", strPath.c_str(), mDataBase.mProtocolVersion));
727
728 pMsg->setType(DragAndDropSvc::HOST_DND_HG_SND_DIR);
729 pMsg->setNextString(strPath.c_str()); /* path */
730 pMsg->setNextUInt32((uint32_t)(strPath.length() + 1)); /* path length - note: Maximum is RTPATH_MAX on guest side. */
731 pMsg->setNextUInt32(aDirectory.GetMode()); /* mode */
732
733 return VINF_SUCCESS;
734}
735
736int GuestDnDTarget::i_sendFile(PSENDDATACTX pCtx, GuestDnDMsg *pMsg, DnDURIObject &aFile)
737{
738 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
739
740 RTCString strPathSrc = aFile.GetSourcePath();
741 if (strPathSrc.isEmpty())
742 return VERR_INVALID_PARAMETER;
743
744 int rc = VINF_SUCCESS;
745
746 LogFlowFunc(("Sending \"%s\" (%RU32 bytes buffer) using protocol v%RU32 ...\n",
747 strPathSrc.c_str(), mData.mcbBlockSize, mDataBase.mProtocolVersion));
748
749 bool fOpen = aFile.IsOpen();
750 if (!fOpen)
751 {
752 LogFlowFunc(("Opening \"%s\" ...\n", strPathSrc.c_str()));
753 rc = aFile.OpenEx(strPathSrc, DnDURIObject::File, DnDURIObject::Source,
754 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, 0 /* fFlags */);
755 if (RT_FAILURE(rc))
756 LogRel(("DnD: Error opening host file \"%s\", rc=%Rrc\n", strPathSrc.c_str(), rc));
757 }
758
759 bool fSendFileData = false;
760 if (RT_SUCCESS(rc))
761 {
762 if (mDataBase.mProtocolVersion >= 2)
763 {
764 if (!fOpen)
765 {
766 /*
767 * Since protocol v2 the file header and the actual file contents are
768 * separate messages, so send the file header first.
769 * The just registered callback will be called by the guest afterwards.
770 */
771 pMsg->setType(DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR);
772 pMsg->setNextUInt32(0); /* context ID */
773 rc = pMsg->setNextString(aFile.GetDestPath().c_str()); /* pvName */
774 AssertRC(rc);
775 pMsg->setNextUInt32((uint32_t)(aFile.GetDestPath().length() + 1)); /* cbName */
776 pMsg->setNextUInt32(0); /* uFlags */
777 pMsg->setNextUInt32(aFile.GetMode()); /* fMode */
778 pMsg->setNextUInt64(aFile.GetSize()); /* uSize */
779
780 LogFlowFunc(("Sending file header ...\n"));
781 LogRel2(("DnD: Transferring host file to guest: %s (%RU64 bytes, mode 0x%x)\n",
782 strPathSrc.c_str(), aFile.GetSize(), aFile.GetMode()));
783
784 /** @todo Set progress object title to current file being transferred? */
785 }
786 else
787 {
788 /* File header was sent, so only send the actual file data. */
789 fSendFileData = true;
790 }
791 }
792 else /* Protocol v1. */
793 {
794 /* Always send the file data, every time. */
795 fSendFileData = true;
796 }
797 }
798
799 if ( RT_SUCCESS(rc)
800 && fSendFileData)
801 {
802 rc = i_sendFileData(pCtx, pMsg, aFile);
803 }
804
805 LogFlowFuncLeaveRC(rc);
806 return rc;
807}
808
809int GuestDnDTarget::i_sendFileData(PSENDDATACTX pCtx, GuestDnDMsg *pMsg, DnDURIObject &aFile)
810{
811 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
812 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
813
814 GuestDnDResponse *pResp = pCtx->mpResp;
815 AssertPtr(pResp);
816
817 /** @todo Don't allow concurrent reads per context! */
818
819 /* Something to transfer? */
820 if ( pCtx->mURI.lstURI.IsEmpty()
821 || !pCtx->mIsActive)
822 {
823 return VERR_WRONG_ORDER;
824 }
825
826 /*
827 * Start sending stuff.
828 */
829
830 /* Set the message type. */
831 pMsg->setType(DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA);
832
833 /* Protocol version 1 sends the file path *every* time with a new file chunk.
834 * In protocol version 2 we only do this once with HOST_DND_HG_SND_FILE_HDR. */
835 if (mDataBase.mProtocolVersion <= 1)
836 {
837 pMsg->setNextString(aFile.GetDestPath().c_str()); /* pvName */
838 pMsg->setNextUInt32((uint32_t)(aFile.GetDestPath().length() + 1)); /* cbName */
839 }
840 else
841 {
842 /* Protocol version 2 also sends the context ID. Currently unused. */
843 pMsg->setNextUInt32(0); /* context ID */
844 }
845
846 uint32_t cbRead = 0;
847
848 int rc = aFile.Read(pCtx->mURI.pvScratchBuf, pCtx->mURI.cbScratchBuf, &cbRead);
849 if (RT_SUCCESS(rc))
850 {
851 pCtx->mData.cbProcessed += cbRead;
852
853 if (mDataBase.mProtocolVersion <= 1)
854 {
855 pMsg->setNextPointer(pCtx->mURI.pvScratchBuf, cbRead); /* pvData */
856 pMsg->setNextUInt32(cbRead); /* cbData */
857 pMsg->setNextUInt32(aFile.GetMode()); /* fMode */
858 }
859 else
860 {
861 pMsg->setNextPointer(pCtx->mURI.pvScratchBuf, cbRead); /* pvData */
862 pMsg->setNextUInt32(cbRead); /* cbData */
863 }
864
865 if (aFile.IsComplete()) /* Done reading? */
866 {
867 LogRel2(("DnD: File transfer to guest complete: %s\n", aFile.GetSourcePath().c_str()));
868 LogFlowFunc(("File \"%s\" complete\n", aFile.GetSourcePath().c_str()));
869 rc = VINF_EOF;
870 }
871 }
872
873 LogFlowFuncLeaveRC(rc);
874 return rc;
875}
876
877/* static */
878DECLCALLBACK(int) GuestDnDTarget::i_sendURIDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
879{
880 PSENDDATACTX pCtx = (PSENDDATACTX)pvUser;
881 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
882
883 GuestDnDTarget *pThis = pCtx->mpTarget;
884 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
885
886 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
887
888 int rc = VINF_SUCCESS; /* Will be reported back to guest. */
889
890 int rcCallback = VINF_SUCCESS; /* rc for the callback. */
891 bool fNotify = false;
892
893 switch (uMsg)
894 {
895 case DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG:
896 {
897 DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSG pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSG>(pvParms);
898 AssertPtr(pCBData);
899 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGGETNEXTHOSTMSG) == cbParms, VERR_INVALID_PARAMETER);
900 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
901
902 try
903 {
904 GuestDnDMsg *pMsg = new GuestDnDMsg();
905
906 rc = pThis->i_sendURIDataLoop(pCtx, pMsg);
907 if (RT_SUCCESS(rc))
908 {
909 rc = pThis->msgQueueAdd(pMsg);
910 if (RT_SUCCESS(rc)) /* Return message type & required parameter count to the guest. */
911 {
912 LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG -> %RU32 (%RU32 params)\n", pMsg->getType(), pMsg->getCount()));
913 pCBData->uMsg = pMsg->getType();
914 pCBData->cParms = pMsg->getCount();
915 }
916 }
917
918 if (RT_FAILURE(rc))
919 delete pMsg;
920 }
921 catch(std::bad_alloc & /*e*/)
922 {
923 rc = VERR_NO_MEMORY;
924 }
925 break;
926 }
927 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
928 {
929 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
930 AssertPtr(pCBData);
931 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
932 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
933
934 pCtx->mpResp->reset();
935
936 if (RT_SUCCESS(pCBData->rc))
937 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
938
939 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc,
940 GuestDnDTarget::i_guestErrorToString(pCBData->rc));
941 if (RT_SUCCESS(rc))
942 rcCallback = VERR_GSTDND_GUEST_ERROR;
943 break;
944 }
945 case DragAndDropSvc::HOST_DND_HG_SND_DIR:
946 case DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR:
947 case DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA:
948 {
949 DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSGDATA pCBData
950 = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSGDATA>(pvParms);
951 AssertPtr(pCBData);
952 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGGETNEXTHOSTMSGDATA) == cbParms, VERR_INVALID_PARAMETER);
953 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
954
955 LogFlowFunc(("pCBData->uMsg=%RU32, paParms=%p, cParms=%RU32\n", pCBData->uMsg, pCBData->paParms, pCBData->cParms));
956
957 GuestDnDMsg *pMsg = pThis->msgQueueGetNext();
958 if (pMsg)
959 {
960 /*
961 * Sanity checks.
962 */
963 if ( pCBData->uMsg != uMsg
964 || pCBData->paParms == NULL
965 || pCBData->cParms != pMsg->getCount())
966 {
967 /* Start over. */
968 pThis->msgQueueClear();
969
970 rc = VERR_INVALID_PARAMETER;
971 }
972
973 if (RT_SUCCESS(rc))
974 {
975 LogFlowFunc(("Returning uMsg=%RU32\n", uMsg));
976 rc = HGCM::Message::copyParms(pMsg->getCount(), pMsg->getParms(), pCBData->paParms);
977 if (RT_SUCCESS(rc))
978 {
979 pCBData->cParms = pMsg->getCount();
980 pThis->msgQueueRemoveNext();
981 }
982 else
983 LogFlowFunc(("Copying parameters failed with rc=%Rrc\n", rc));
984 }
985 }
986 else
987 rc = VERR_NO_DATA;
988
989 LogFlowFunc(("Processing next message ended with rc=%Rrc\n", rc));
990 break;
991 }
992 default:
993 rc = VERR_NOT_SUPPORTED;
994 break;
995 }
996
997 if ( RT_FAILURE(rc)
998 || RT_FAILURE(rcCallback))
999 {
1000 fNotify = true;
1001 if (RT_SUCCESS(rcCallback))
1002 rcCallback = rc;
1003 }
1004
1005 if (RT_FAILURE(rc))
1006 {
1007 switch (rc)
1008 {
1009 case VERR_NO_DATA:
1010 LogRel2(("DnD: Transfer to guest complete\n"));
1011 break;
1012
1013 case VERR_CANCELLED:
1014 LogRel2(("DnD: Transfer to guest canceled\n"));
1015 break;
1016
1017 default:
1018 LogRel(("DnD: Error %Rrc occurred, aborting transfer to guest\n", rc));
1019 break;
1020 }
1021
1022 /* Unregister this callback. */
1023 AssertPtr(pCtx->mpResp);
1024 int rc2 = pCtx->mpResp->setCallback(uMsg, NULL /* PFNGUESTDNDCALLBACK */);
1025 AssertRC(rc2);
1026 }
1027
1028 LogFlowFunc(("fNotify=%RTbool, rcCallback=%Rrc, rc=%Rrc\n", fNotify, rcCallback, rc));
1029
1030 if (fNotify)
1031 {
1032 int rc2 = pCtx->mCallback.Notify(rcCallback);
1033 AssertRC(rc2);
1034 }
1035
1036 LogFlowFuncLeaveRC(rc);
1037 return rc; /* Tell the guest. */
1038}
1039
1040int GuestDnDTarget::i_sendURIData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
1041{
1042 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1043 AssertPtr(pCtx->mpResp);
1044
1045#define URI_DATA_IS_VALID_BREAK(x) \
1046 if (!x) \
1047 { \
1048 LogFlowFunc(("Invalid URI data value for \"" #x "\"\n")); \
1049 rc = VERR_INVALID_PARAMETER; \
1050 break; \
1051 }
1052
1053 void *pvBuf = RTMemAlloc(mData.mcbBlockSize);
1054 if (!pvBuf)
1055 return VERR_NO_MEMORY;
1056
1057 int rc;
1058
1059#define REGISTER_CALLBACK(x) \
1060 rc = pCtx->mpResp->setCallback(x, i_sendURIDataCallback, pCtx); \
1061 if (RT_FAILURE(rc)) \
1062 return rc;
1063
1064#define UNREGISTER_CALLBACK(x) \
1065 { \
1066 int rc2 = pCtx->mpResp->setCallback(x, NULL); \
1067 AssertRC(rc2); \
1068 }
1069
1070 rc = pCtx->mCallback.Reset();
1071 if (RT_FAILURE(rc))
1072 return rc;
1073
1074 /*
1075 * Register callbacks.
1076 */
1077 /* Guest callbacks. */
1078 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG);
1079 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
1080 /* Host callbacks. */
1081 REGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_DIR);
1082 if (mDataBase.mProtocolVersion >= 2)
1083 REGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR);
1084 REGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA);
1085
1086 do
1087 {
1088 /*
1089 * Set our scratch buffer.
1090 */
1091 pCtx->mURI.pvScratchBuf = pvBuf;
1092 pCtx->mURI.cbScratchBuf = mData.mcbBlockSize;
1093
1094 /*
1095 * Extract URI list from byte data.
1096 */
1097 DnDURIList &lstURI = pCtx->mURI.lstURI; /* Use the URI list from the context. */
1098
1099 const char *pszList = (const char *)&pCtx->mData.vecData.front();
1100 URI_DATA_IS_VALID_BREAK(pszList);
1101
1102 uint32_t cbList = pCtx->mData.vecData.size();
1103 URI_DATA_IS_VALID_BREAK(cbList);
1104
1105 RTCList<RTCString> lstURIOrg = RTCString(pszList, cbList).split("\r\n");
1106 URI_DATA_IS_VALID_BREAK(!lstURIOrg.isEmpty());
1107
1108 rc = lstURI.AppendURIPathsFromList(lstURIOrg, 0 /* fFlags */);
1109 if (RT_SUCCESS(rc))
1110 LogFlowFunc(("URI root objects: %zu, total bytes (raw data to transfer): %zu\n",
1111 lstURI.RootCount(), lstURI.TotalBytes()));
1112 else
1113 break;
1114
1115 pCtx->mData.cbProcessed = 0;
1116 pCtx->mData.cbToProcess = lstURI.TotalBytes();
1117
1118 /*
1119 * The first message always is the meta info for the data. The meta
1120 * info *only* contains the root elements of an URI list.
1121 *
1122 * After the meta data we generate the messages required to send the data itself.
1123 */
1124 Assert(!lstURI.IsEmpty());
1125 RTCString strData = lstURI.RootToString().c_str();
1126 size_t cbData = strData.length() + 1; /* Include terminating zero. */
1127
1128 GuestDnDMsg MsgSndData;
1129 MsgSndData.setType(DragAndDropSvc::HOST_DND_HG_SND_DATA);
1130 MsgSndData.setNextUInt32(pCtx->mScreenID);
1131 MsgSndData.setNextPointer((void *)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
1132 MsgSndData.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
1133 MsgSndData.setNextPointer((void*)strData.c_str(), (uint32_t)cbData);
1134 MsgSndData.setNextUInt32((uint32_t)cbData);
1135
1136 rc = GuestDnDInst()->hostCall(MsgSndData.getType(), MsgSndData.getCount(), MsgSndData.getParms());
1137 if (RT_SUCCESS(rc))
1138 {
1139 rc = waitForEvent(msTimeout, pCtx->mCallback, pCtx->mpResp);
1140 if (RT_FAILURE(rc))
1141 {
1142 if (rc == VERR_CANCELLED)
1143 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED, VINF_SUCCESS);
1144 else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
1145 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, rc,
1146 GuestDnDTarget::i_hostErrorToString(rc));
1147 }
1148 else
1149 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_COMPLETE, VINF_SUCCESS);
1150 }
1151
1152 } while (0);
1153
1154 /*
1155 * Unregister callbacks.
1156 */
1157 /* Guest callbacks. */
1158 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG);
1159 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
1160 /* Host callbacks. */
1161 UNREGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_DIR);
1162 if (mDataBase.mProtocolVersion >= 2)
1163 UNREGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR);
1164 UNREGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA);
1165
1166#undef REGISTER_CALLBACK
1167#undef UNREGISTER_CALLBACK
1168
1169 /*
1170 * Now that we've cleaned up tell the guest side to cancel.
1171 * This does not imply we're waiting for the guest to react, as the
1172 * host side never must depend on anything from the guest.
1173 */
1174 if (rc == VERR_CANCELLED)
1175 {
1176 int rc2 = sendCancel();
1177 AssertRC(rc2);
1178 }
1179
1180 /* Destroy temporary scratch buffer. */
1181 if (pvBuf)
1182 RTMemFree(pvBuf);
1183
1184#undef URI_DATA_IS_VALID_BREAK
1185
1186 LogFlowFuncLeaveRC(rc);
1187 return rc;
1188}
1189
1190int GuestDnDTarget::i_sendURIDataLoop(PSENDDATACTX pCtx, GuestDnDMsg *pMsg)
1191{
1192 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1193
1194 DnDURIList &lstURI = pCtx->mURI.lstURI;
1195
1196 int rc;
1197
1198 uint64_t cbTotal = pCtx->mData.cbToProcess;
1199 uint8_t uPercent = pCtx->mData.cbProcessed * 100 / (cbTotal ? cbTotal : 1);
1200
1201 LogFlowFunc(("%RU64 / %RU64 -- %RU8%%\n", pCtx->mData.cbProcessed, cbTotal, uPercent));
1202
1203 bool fComplete = (uPercent >= 100) || lstURI.IsEmpty();
1204
1205 if (pCtx->mpResp)
1206 {
1207 int rc2 = pCtx->mpResp->setProgress(uPercent,
1208 fComplete
1209 ? DragAndDropSvc::DND_PROGRESS_COMPLETE
1210 : DragAndDropSvc::DND_PROGRESS_RUNNING);
1211 AssertRC(rc2);
1212 }
1213
1214 if (fComplete)
1215 {
1216 LogFlowFunc(("Last URI item processed, bailing out\n"));
1217 return VERR_NO_DATA;
1218 }
1219
1220 Assert(!lstURI.IsEmpty());
1221 DnDURIObject &curObj = lstURI.First();
1222
1223 uint32_t fMode = curObj.GetMode();
1224 LogFlowFunc(("Processing srcPath=%s, dstPath=%s, fMode=0x%x, cbSize=%RU32, fIsDir=%RTbool, fIsFile=%RTbool\n",
1225 curObj.GetSourcePath().c_str(), curObj.GetDestPath().c_str(),
1226 fMode, curObj.GetSize(),
1227 RTFS_IS_DIRECTORY(fMode), RTFS_IS_FILE(fMode)));
1228
1229 if (RTFS_IS_DIRECTORY(fMode))
1230 {
1231 rc = i_sendDirectory(pCtx, pMsg, curObj);
1232 }
1233 else if (RTFS_IS_FILE(fMode))
1234 {
1235 rc = i_sendFile(pCtx, pMsg, curObj);
1236 }
1237 else
1238 {
1239 AssertMsgFailed(("fMode=0x%x is not supported for srcPath=%s, dstPath=%s\n",
1240 fMode, curObj.GetSourcePath().c_str(), curObj.GetDestPath().c_str()));
1241 rc = VERR_NOT_SUPPORTED;
1242 }
1243
1244 bool fRemove = false; /* Remove current entry? */
1245 if ( curObj.IsComplete()
1246 || RT_FAILURE(rc))
1247 {
1248 fRemove = true;
1249 }
1250
1251 if (fRemove)
1252 {
1253 LogFlowFunc(("Removing \"%s\" from list, rc=%Rrc\n", curObj.GetSourcePath().c_str(), rc));
1254 lstURI.RemoveFirst();
1255 }
1256
1257 LogFlowFuncLeaveRC(rc);
1258 return rc;
1259}
1260
1261int GuestDnDTarget::i_sendRawData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
1262{
1263 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1264 NOREF(msTimeout);
1265
1266 GuestDnD *pInst = GuestDnDInst();
1267 AssertPtr(pInst);
1268
1269 /* At the moment we only allow up to 64K raw data. */
1270 size_t cbDataTotal = pCtx->mData.vecData.size();
1271 if ( !cbDataTotal
1272 || cbDataTotal > _64K)
1273 {
1274 return VERR_INVALID_PARAMETER;
1275 }
1276
1277 /* Just copy over the raw data. */
1278 GuestDnDMsg Msg;
1279 Msg.setType(DragAndDropSvc::HOST_DND_HG_SND_DATA);
1280 Msg.setNextUInt32(pCtx->mScreenID);
1281 Msg.setNextPointer((void *)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
1282 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
1283 Msg.setNextPointer((void*)&pCtx->mData.vecData.front(), (uint32_t)cbDataTotal);
1284 Msg.setNextUInt32(cbDataTotal);
1285
1286 LogFlowFunc(("%zu total bytes of raw data to transfer\n", cbDataTotal));
1287
1288 return pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
1289}
1290
1291HRESULT GuestDnDTarget::cancel(BOOL *aVeto)
1292{
1293#if !defined(VBOX_WITH_DRAG_AND_DROP)
1294 ReturnComNotImplemented();
1295#else /* VBOX_WITH_DRAG_AND_DROP */
1296
1297 int rc = i_cancelOperation();
1298
1299 if (aVeto)
1300 *aVeto = FALSE; /** @todo */
1301
1302 return RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
1303#endif /* VBOX_WITH_DRAG_AND_DROP */
1304}
1305
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