VirtualBox

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

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

DnD: Fixed hang when reporting host errors such as inaccessible (locked) files.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 42.5 KB
Line 
1/* $Id: GuestDnDTargetImpl.cpp 55963 2015-05-20 11:18:14Z 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 default:
634 strError += Utf8StrFmt(tr("Drag and drop error from guest (%Rrc)"), guestRc);
635 break;
636 }
637
638 return strError;
639}
640
641/* static */
642Utf8Str GuestDnDTarget::i_hostErrorToString(int hostRc)
643{
644 Utf8Str strError;
645
646 switch (hostRc)
647 {
648 case VERR_ACCESS_DENIED:
649 strError += Utf8StrFmt(tr("For one or more host files or directories selected for transferring to the guest your host "
650 "user does not have the appropriate access rights for. Please make sure that all selected "
651 "elements can be accessed and that your host user has the appropriate rights."));
652 break;
653
654 case VERR_NOT_FOUND:
655 /* Should not happen due to file locking on the host, but anyway ... */
656 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the host were not"
657 "found on the host anymore. This can be the case if the host files were moved and/or"
658 "altered while the drag and drop operation was in progress."));
659 break;
660
661 case VERR_SHARING_VIOLATION:
662 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the guest were locked. "
663 "Please make sure that all selected elements can be accessed and that your host user has "
664 "the appropriate rights."));
665 break;
666
667 default:
668 strError += Utf8StrFmt(tr("Drag and drop error from host (%Rrc)"), hostRc);
669 break;
670 }
671
672 return strError;
673}
674
675int GuestDnDTarget::i_sendData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
676{
677 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
678
679 GuestDnD *pInst = GuestDnDInst();
680 if (!pInst)
681 return VERR_INVALID_POINTER;
682
683 int rc;
684
685 ASMAtomicWriteBool(&pCtx->mIsActive, true);
686
687 /* Clear all remaining outgoing messages. */
688 mDataBase.mListOutgoing.clear();
689
690 const char *pszFormat = pCtx->mFormat.c_str();
691 uint32_t cbFormat = pCtx->mFormat.length() + 1;
692
693 /* Do we need to build up a file tree? */
694 bool fHasURIList = DnDMIMEHasFileURLs(pszFormat, cbFormat);
695 if (fHasURIList)
696 {
697 rc = i_sendURIData(pCtx, msTimeout);
698 }
699 else
700 {
701 rc = i_sendRawData(pCtx, msTimeout);
702 }
703
704 ASMAtomicWriteBool(&pCtx->mIsActive, false);
705
706#undef DATA_IS_VALID_BREAK
707
708 LogFlowFuncLeaveRC(rc);
709 return rc;
710}
711
712int GuestDnDTarget::i_sendDirectory(PSENDDATACTX pCtx, GuestDnDMsg *pMsg, DnDURIObject &aDirectory)
713{
714 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
715
716 RTCString strPath = aDirectory.GetDestPath();
717 if (strPath.isEmpty())
718 return VERR_INVALID_PARAMETER;
719 if (strPath.length() >= RTPATH_MAX) /* Note: Maximum is RTPATH_MAX on guest side. */
720 return VERR_BUFFER_OVERFLOW;
721
722 LogFlowFunc(("Sending directory \"%s\" using protocol v%RU32 ...\n", strPath.c_str(), mDataBase.mProtocolVersion));
723
724 pMsg->setType(DragAndDropSvc::HOST_DND_HG_SND_DIR);
725 pMsg->setNextString(strPath.c_str()); /* path */
726 pMsg->setNextUInt32((uint32_t)(strPath.length() + 1)); /* path length - note: Maximum is RTPATH_MAX on guest side. */
727 pMsg->setNextUInt32(aDirectory.GetMode()); /* mode */
728
729 return VINF_SUCCESS;
730}
731
732int GuestDnDTarget::i_sendFile(PSENDDATACTX pCtx, GuestDnDMsg *pMsg, DnDURIObject &aFile)
733{
734 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
735
736 RTCString strPathSrc = aFile.GetSourcePath();
737 if (strPathSrc.isEmpty())
738 return VERR_INVALID_PARAMETER;
739
740 int rc = VINF_SUCCESS;
741
742 LogFlowFunc(("Sending \"%s\" (%RU32 bytes buffer) using protocol v%RU32 ...\n",
743 strPathSrc.c_str(), mData.mcbBlockSize, mDataBase.mProtocolVersion));
744
745 bool fOpen = aFile.IsOpen();
746 if (!fOpen)
747 {
748 LogFlowFunc(("Opening \"%s\" ...\n", strPathSrc.c_str()));
749 rc = aFile.OpenEx(strPathSrc, DnDURIObject::File, DnDURIObject::Source,
750 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, 0 /* fFlags */);
751 if (RT_FAILURE(rc))
752 LogRel(("DnD: Error opening host file \"%s\", rc=%Rrc\n", strPathSrc.c_str(), rc));
753 }
754
755 bool fSendFileData = false;
756 if (RT_SUCCESS(rc))
757 {
758 if (mDataBase.mProtocolVersion >= 2)
759 {
760 if (!fOpen)
761 {
762 /*
763 * Since protocol v2 the file header and the actual file contents are
764 * separate messages, so send the file header first.
765 * The just registered callback will be called by the guest afterwards.
766 */
767 pMsg->setType(DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR);
768 pMsg->setNextUInt32(0); /* context ID */
769 rc = pMsg->setNextString(aFile.GetDestPath().c_str()); /* pvName */
770 AssertRC(rc);
771 pMsg->setNextUInt32((uint32_t)(aFile.GetDestPath().length() + 1)); /* cbName */
772 pMsg->setNextUInt32(0); /* uFlags */
773 pMsg->setNextUInt32(aFile.GetMode()); /* fMode */
774 pMsg->setNextUInt64(aFile.GetSize()); /* uSize */
775
776 LogFlowFunc(("Sending file header ...\n"));
777 LogRel2(("DnD: Transferring host file to guest: %s (%RU64 bytes, mode 0x%x)\n",
778 strPathSrc.c_str(), aFile.GetSize(), aFile.GetMode()));
779
780 /** @todo Set progress object title to current file being transferred? */
781 }
782 else
783 {
784 /* File header was sent, so only send the actual file data. */
785 fSendFileData = true;
786 }
787 }
788 else /* Protocol v1. */
789 {
790 /* Always send the file data, every time. */
791 fSendFileData = true;
792 }
793 }
794
795 if ( RT_SUCCESS(rc)
796 && fSendFileData)
797 {
798 rc = i_sendFileData(pCtx, pMsg, aFile);
799 }
800
801 LogFlowFuncLeaveRC(rc);
802 return rc;
803}
804
805int GuestDnDTarget::i_sendFileData(PSENDDATACTX pCtx, GuestDnDMsg *pMsg, DnDURIObject &aFile)
806{
807 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
808 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
809
810 GuestDnDResponse *pResp = pCtx->mpResp;
811 AssertPtr(pResp);
812
813 /** @todo Don't allow concurrent reads per context! */
814
815 /* Something to transfer? */
816 if ( pCtx->mURI.lstURI.IsEmpty()
817 || !pCtx->mIsActive)
818 {
819 return VERR_WRONG_ORDER;
820 }
821
822 /*
823 * Start sending stuff.
824 */
825
826 /* Set the message type. */
827 pMsg->setType(DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA);
828
829 /* Protocol version 1 sends the file path *every* time with a new file chunk.
830 * In protocol version 2 we only do this once with HOST_DND_HG_SND_FILE_HDR. */
831 if (mDataBase.mProtocolVersion <= 1)
832 {
833 pMsg->setNextString(aFile.GetSourcePath().c_str()); /* pvName */
834 pMsg->setNextUInt32((uint32_t)(aFile.GetSourcePath().length() + 1)); /* cbName */
835 }
836 else
837 {
838 /* Protocol version 2 also sends the context ID. Currently unused. */
839 pMsg->setNextUInt32(0); /* context ID */
840 }
841
842 uint32_t cbRead = 0;
843
844 int rc = aFile.Read(pCtx->mURI.pvScratchBuf, pCtx->mURI.cbScratchBuf, &cbRead);
845 if (RT_SUCCESS(rc))
846 {
847 pCtx->mData.cbProcessed += cbRead;
848
849 if (mDataBase.mProtocolVersion <= 1)
850 {
851 pMsg->setNextPointer(pCtx->mURI.pvScratchBuf, cbRead); /* pvData */
852 pMsg->setNextUInt32(cbRead); /* cbData */
853 pMsg->setNextUInt32(aFile.GetMode()); /* fMode */
854 }
855 else
856 {
857 pMsg->setNextPointer(pCtx->mURI.pvScratchBuf, cbRead); /* pvData */
858 pMsg->setNextUInt32(cbRead); /* cbData */
859 }
860
861 if (aFile.IsComplete()) /* Done reading? */
862 {
863 LogRel2(("DnD: File transfer to guest complete: %s\n", aFile.GetSourcePath().c_str()));
864 LogFlowFunc(("File \"%s\" complete\n", aFile.GetSourcePath().c_str()));
865 rc = VINF_EOF;
866 }
867 }
868
869 LogFlowFuncLeaveRC(rc);
870 return rc;
871}
872
873/* static */
874DECLCALLBACK(int) GuestDnDTarget::i_sendURIDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
875{
876 PSENDDATACTX pCtx = (PSENDDATACTX)pvUser;
877 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
878
879 GuestDnDTarget *pThis = pCtx->mpTarget;
880 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
881
882 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
883
884 int rc = VINF_SUCCESS; /* Will be reported back to guest. */
885
886 int rcCallback = VINF_SUCCESS; /* rc for the callback. */
887 bool fNotify = false;
888
889 switch (uMsg)
890 {
891 case DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG:
892 {
893 DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSG pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSG>(pvParms);
894 AssertPtr(pCBData);
895 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGGETNEXTHOSTMSG) == cbParms, VERR_INVALID_PARAMETER);
896 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
897
898 try
899 {
900 GuestDnDMsg *pMsg = new GuestDnDMsg();
901
902 rc = pThis->i_sendURIDataLoop(pCtx, pMsg);
903 if (RT_SUCCESS(rc))
904 {
905 rc = pThis->msgQueueAdd(pMsg);
906 if (RT_SUCCESS(rc)) /* Return message type & required parameter count to the guest. */
907 {
908 LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG -> %RU32 (%RU32 params)\n", pMsg->getType(), pMsg->getCount()));
909 pCBData->uMsg = pMsg->getType();
910 pCBData->cParms = pMsg->getCount();
911 }
912 }
913
914 if (RT_FAILURE(rc))
915 delete pMsg;
916 }
917 catch(std::bad_alloc & /*e*/)
918 {
919 rc = VERR_NO_MEMORY;
920 }
921 break;
922 }
923 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
924 {
925 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
926 AssertPtr(pCBData);
927 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
928 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
929
930 pCtx->mpResp->reset();
931
932 if (RT_SUCCESS(pCBData->rc))
933 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
934
935 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc,
936 GuestDnDTarget::i_guestErrorToString(pCBData->rc));
937 if (RT_SUCCESS(rc))
938 rcCallback = VERR_GSTDND_GUEST_ERROR;
939 break;
940 }
941 case DragAndDropSvc::HOST_DND_HG_SND_DIR:
942 case DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR:
943 case DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA:
944 {
945 DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSGDATA pCBData
946 = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSGDATA>(pvParms);
947 AssertPtr(pCBData);
948 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGGETNEXTHOSTMSGDATA) == cbParms, VERR_INVALID_PARAMETER);
949 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
950
951 LogFlowFunc(("pCBData->uMsg=%RU32, paParms=%p, cParms=%RU32\n", pCBData->uMsg, pCBData->paParms, pCBData->cParms));
952
953 GuestDnDMsg *pMsg = pThis->msgQueueGetNext();
954 if (pMsg)
955 {
956 /*
957 * Sanity checks.
958 */
959 if ( pCBData->uMsg != uMsg
960 || pCBData->paParms == NULL
961 || pCBData->cParms != pMsg->getCount())
962 {
963 /* Start over. */
964 pThis->msgQueueClear();
965
966 rc = VERR_INVALID_PARAMETER;
967 }
968
969 if (RT_SUCCESS(rc))
970 {
971 LogFlowFunc(("Returning uMsg=%RU32\n", uMsg));
972 rc = HGCM::Message::copyParms(pMsg->getCount(), pMsg->getParms(), pCBData->paParms);
973 if (RT_SUCCESS(rc))
974 {
975 pCBData->cParms = pMsg->getCount();
976 pThis->msgQueueRemoveNext();
977 }
978 else
979 LogFlowFunc(("Copying parameters failed with rc=%Rrc\n", rc));
980 }
981 }
982 else
983 rc = VERR_NO_DATA;
984
985 LogFlowFunc(("Processing next message ended with rc=%Rrc\n", rc));
986 break;
987 }
988 default:
989 rc = VERR_NOT_SUPPORTED;
990 break;
991 }
992
993 if ( RT_FAILURE(rc)
994 || RT_FAILURE(rcCallback))
995 {
996 fNotify = true;
997 if (RT_SUCCESS(rcCallback))
998 rcCallback = rc;
999 }
1000
1001 if (RT_FAILURE(rc))
1002 {
1003 switch (rc)
1004 {
1005 case VERR_NO_DATA:
1006 LogRel2(("DnD: Transfer to guest complete\n"));
1007 break;
1008
1009 case VERR_CANCELLED:
1010 LogRel2(("DnD: Transfer to guest canceled\n"));
1011 break;
1012
1013 default:
1014 LogRel(("DnD: Error %Rrc occurred, aborting transfer to guest\n", rc));
1015 break;
1016 }
1017
1018 /* Unregister this callback. */
1019 AssertPtr(pCtx->mpResp);
1020 int rc2 = pCtx->mpResp->setCallback(uMsg, NULL /* PFNGUESTDNDCALLBACK */);
1021 AssertRC(rc2);
1022 }
1023
1024 LogFlowFunc(("fNotify=%RTbool, rcCallback=%Rrc, rc=%Rrc\n", fNotify, rcCallback, rc));
1025
1026 if (fNotify)
1027 {
1028 int rc2 = pCtx->mCallback.Notify(rcCallback);
1029 AssertRC(rc2);
1030 }
1031
1032 LogFlowFuncLeaveRC(rc);
1033 return rc; /* Tell the guest. */
1034}
1035
1036int GuestDnDTarget::i_sendURIData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
1037{
1038 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1039 AssertPtr(pCtx->mpResp);
1040
1041#define URI_DATA_IS_VALID_BREAK(x) \
1042 if (!x) \
1043 { \
1044 LogFlowFunc(("Invalid URI data value for \"" #x "\"\n")); \
1045 rc = VERR_INVALID_PARAMETER; \
1046 break; \
1047 }
1048
1049 void *pvBuf = RTMemAlloc(mData.mcbBlockSize);
1050 if (!pvBuf)
1051 return VERR_NO_MEMORY;
1052
1053 int rc;
1054
1055#define REGISTER_CALLBACK(x) \
1056 rc = pCtx->mpResp->setCallback(x, i_sendURIDataCallback, pCtx); \
1057 if (RT_FAILURE(rc)) \
1058 return rc;
1059
1060#define UNREGISTER_CALLBACK(x) \
1061 { \
1062 int rc2 = pCtx->mpResp->setCallback(x, NULL); \
1063 AssertRC(rc2); \
1064 }
1065
1066 rc = pCtx->mCallback.Reset();
1067 if (RT_FAILURE(rc))
1068 return rc;
1069
1070 /*
1071 * Register callbacks.
1072 */
1073 /* Guest callbacks. */
1074 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG);
1075 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
1076 /* Host callbacks. */
1077 REGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_DIR);
1078 if (mDataBase.mProtocolVersion >= 2)
1079 REGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR);
1080 REGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA);
1081
1082 do
1083 {
1084 /*
1085 * Set our scratch buffer.
1086 */
1087 pCtx->mURI.pvScratchBuf = pvBuf;
1088 pCtx->mURI.cbScratchBuf = mData.mcbBlockSize;
1089
1090 /*
1091 * Extract URI list from byte data.
1092 */
1093 DnDURIList &lstURI = pCtx->mURI.lstURI; /* Use the URI list from the context. */
1094
1095 const char *pszList = (const char *)&pCtx->mData.vecData.front();
1096 URI_DATA_IS_VALID_BREAK(pszList);
1097
1098 uint32_t cbList = pCtx->mData.vecData.size();
1099 URI_DATA_IS_VALID_BREAK(cbList);
1100
1101 RTCList<RTCString> lstURIOrg = RTCString(pszList, cbList).split("\r\n");
1102 URI_DATA_IS_VALID_BREAK(!lstURIOrg.isEmpty());
1103
1104 rc = lstURI.AppendURIPathsFromList(lstURIOrg, 0 /* fFlags */);
1105 if (RT_SUCCESS(rc))
1106 LogFlowFunc(("URI root objects: %zu, total bytes (raw data to transfer): %zu\n",
1107 lstURI.RootCount(), lstURI.TotalBytes()));
1108 else
1109 break;
1110
1111 pCtx->mData.cbProcessed = 0;
1112 pCtx->mData.cbToProcess = lstURI.TotalBytes();
1113
1114 /*
1115 * The first message always is the meta info for the data. The meta
1116 * info *only* contains the root elements of an URI list.
1117 *
1118 * After the meta data we generate the messages required to send the data itself.
1119 */
1120 Assert(!lstURI.IsEmpty());
1121 RTCString strData = lstURI.RootToString().c_str();
1122 size_t cbData = strData.length() + 1; /* Include terminating zero. */
1123
1124 GuestDnDMsg MsgSndData;
1125 MsgSndData.setType(DragAndDropSvc::HOST_DND_HG_SND_DATA);
1126 MsgSndData.setNextUInt32(pCtx->mScreenID);
1127 MsgSndData.setNextPointer((void *)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
1128 MsgSndData.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
1129 MsgSndData.setNextPointer((void*)strData.c_str(), (uint32_t)cbData);
1130 MsgSndData.setNextUInt32((uint32_t)cbData);
1131
1132 rc = GuestDnDInst()->hostCall(MsgSndData.getType(), MsgSndData.getCount(), MsgSndData.getParms());
1133 if (RT_SUCCESS(rc))
1134 {
1135 rc = waitForEvent(msTimeout, pCtx->mCallback, pCtx->mpResp);
1136 if (RT_FAILURE(rc))
1137 {
1138 if (rc == VERR_CANCELLED)
1139 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED, VINF_SUCCESS);
1140 else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
1141 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, rc,
1142 GuestDnDTarget::i_hostErrorToString(rc));
1143 }
1144 else
1145 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_COMPLETE, VINF_SUCCESS);
1146 }
1147
1148 } while (0);
1149
1150 /*
1151 * Unregister callbacks.
1152 */
1153 /* Guest callbacks. */
1154 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG);
1155 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
1156 /* Host callbacks. */
1157 UNREGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_DIR);
1158 if (mDataBase.mProtocolVersion >= 2)
1159 UNREGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR);
1160 UNREGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA);
1161
1162#undef REGISTER_CALLBACK
1163#undef UNREGISTER_CALLBACK
1164
1165 /*
1166 * Now that we've cleaned up tell the guest side to cancel.
1167 * This does not imply we're waiting for the guest to react, as the
1168 * host side never must depend on anything from the guest.
1169 */
1170 if (rc == VERR_CANCELLED)
1171 {
1172 int rc2 = sendCancel();
1173 AssertRC(rc2);
1174 }
1175
1176 /* Destroy temporary scratch buffer. */
1177 if (pvBuf)
1178 RTMemFree(pvBuf);
1179
1180#undef URI_DATA_IS_VALID_BREAK
1181
1182 LogFlowFuncLeaveRC(rc);
1183 return rc;
1184}
1185
1186int GuestDnDTarget::i_sendURIDataLoop(PSENDDATACTX pCtx, GuestDnDMsg *pMsg)
1187{
1188 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1189
1190 DnDURIList &lstURI = pCtx->mURI.lstURI;
1191
1192 int rc;
1193
1194 uint64_t cbTotal = pCtx->mData.cbToProcess;
1195 uint8_t uPercent = pCtx->mData.cbProcessed * 100 / (cbTotal ? cbTotal : 1);
1196
1197 LogFlowFunc(("%RU64 / %RU64 -- %RU8%%\n", pCtx->mData.cbProcessed, cbTotal, uPercent));
1198
1199 bool fComplete = (uPercent >= 100) || lstURI.IsEmpty();
1200
1201 if (pCtx->mpResp)
1202 {
1203 int rc2 = pCtx->mpResp->setProgress(uPercent,
1204 fComplete
1205 ? DragAndDropSvc::DND_PROGRESS_COMPLETE
1206 : DragAndDropSvc::DND_PROGRESS_RUNNING);
1207 AssertRC(rc2);
1208 }
1209
1210 if (fComplete)
1211 {
1212 LogFlowFunc(("Last URI item processed, bailing out\n"));
1213 return VERR_NO_DATA;
1214 }
1215
1216 Assert(!lstURI.IsEmpty());
1217 DnDURIObject &curObj = lstURI.First();
1218
1219 uint32_t fMode = curObj.GetMode();
1220 LogFlowFunc(("Processing srcPath=%s, dstPath=%s, fMode=0x%x, cbSize=%RU32, fIsDir=%RTbool, fIsFile=%RTbool\n",
1221 curObj.GetSourcePath().c_str(), curObj.GetDestPath().c_str(),
1222 fMode, curObj.GetSize(),
1223 RTFS_IS_DIRECTORY(fMode), RTFS_IS_FILE(fMode)));
1224
1225 if (RTFS_IS_DIRECTORY(fMode))
1226 {
1227 rc = i_sendDirectory(pCtx, pMsg, curObj);
1228 }
1229 else if (RTFS_IS_FILE(fMode))
1230 {
1231 rc = i_sendFile(pCtx, pMsg, curObj);
1232 }
1233 else
1234 {
1235 AssertMsgFailed(("fMode=0x%x is not supported for srcPath=%s, dstPath=%s\n",
1236 fMode, curObj.GetSourcePath().c_str(), curObj.GetDestPath().c_str()));
1237 rc = VERR_NOT_SUPPORTED;
1238 }
1239
1240 bool fRemove = false; /* Remove current entry? */
1241 if ( curObj.IsComplete()
1242 || RT_FAILURE(rc))
1243 {
1244 fRemove = true;
1245 }
1246
1247 if (fRemove)
1248 {
1249 LogFlowFunc(("Removing \"%s\" from list, rc=%Rrc\n", curObj.GetSourcePath().c_str(), rc));
1250 lstURI.RemoveFirst();
1251 }
1252
1253 LogFlowFuncLeaveRC(rc);
1254 return rc;
1255}
1256
1257int GuestDnDTarget::i_sendRawData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
1258{
1259 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1260 NOREF(msTimeout);
1261
1262 GuestDnD *pInst = GuestDnDInst();
1263 AssertPtr(pInst);
1264
1265 /* At the moment we only allow up to 64K raw data. */
1266 size_t cbDataTotal = pCtx->mData.vecData.size();
1267 if ( !cbDataTotal
1268 || cbDataTotal > _64K)
1269 {
1270 return VERR_INVALID_PARAMETER;
1271 }
1272
1273 /* Just copy over the raw data. */
1274 GuestDnDMsg Msg;
1275 Msg.setType(DragAndDropSvc::HOST_DND_HG_SND_DATA);
1276 Msg.setNextUInt32(pCtx->mScreenID);
1277 Msg.setNextPointer((void *)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
1278 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
1279 Msg.setNextPointer((void*)&pCtx->mData.vecData.front(), (uint32_t)cbDataTotal);
1280 Msg.setNextUInt32(cbDataTotal);
1281
1282 LogFlowFunc(("%zu total bytes of raw data to transfer\n", cbDataTotal));
1283
1284 return pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
1285}
1286
1287HRESULT GuestDnDTarget::cancel(BOOL *aVeto)
1288{
1289#if !defined(VBOX_WITH_DRAG_AND_DROP)
1290 ReturnComNotImplemented();
1291#else /* VBOX_WITH_DRAG_AND_DROP */
1292
1293 int rc = i_cancelOperation();
1294
1295 if (aVeto)
1296 *aVeto = FALSE; /** @todo */
1297
1298 return RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
1299#endif /* VBOX_WITH_DRAG_AND_DROP */
1300}
1301
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