VirtualBox

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

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

Build fix.

  • 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 56655 2015-06-26 08:47:05Z 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_vecFmtSup, aFormats);
280
281 /* If there is no valid supported format, ignore this request. */
282 if (strFormats.isEmpty())
283 return S_OK;
284
285 HRESULT hr = S_OK;
286
287 /* Adjust the coordinates in a multi-monitor setup. */
288 int rc = GuestDnDInst()->adjustScreenCoordinates(aScreenId, &aX, &aY);
289 if (RT_SUCCESS(rc))
290 {
291 GuestDnDMsg Msg;
292 Msg.setType(DragAndDropSvc::HOST_DND_HG_EVT_ENTER);
293 Msg.setNextUInt32(aScreenId);
294 Msg.setNextUInt32(aX);
295 Msg.setNextUInt32(aY);
296 Msg.setNextUInt32(uDefAction);
297 Msg.setNextUInt32(uAllowedActions);
298 Msg.setNextPointer((void*)strFormats.c_str(), strFormats.length() + 1);
299 Msg.setNextUInt32(strFormats.length() + 1);
300
301 rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
302 if (RT_SUCCESS(rc))
303 {
304 GuestDnDResponse *pResp = GuestDnDInst()->response();
305 if (pResp && RT_SUCCESS(pResp->waitForGuestResponse()))
306 resAction = GuestDnD::toMainAction(pResp->defAction());
307 }
308 }
309
310 if (aResultAction)
311 *aResultAction = resAction;
312
313 LogFlowFunc(("hr=%Rhrc, resAction=%ld\n", hr, resAction));
314 return hr;
315#endif /* VBOX_WITH_DRAG_AND_DROP */
316}
317
318HRESULT GuestDnDTarget::move(ULONG aScreenId, ULONG aX, ULONG aY,
319 DnDAction_T aDefaultAction,
320 const std::vector<DnDAction_T> &aAllowedActions,
321 const std::vector<com::Utf8Str> &aFormats,
322 DnDAction_T *aResultAction)
323{
324#if !defined(VBOX_WITH_DRAG_AND_DROP)
325 ReturnComNotImplemented();
326#else /* VBOX_WITH_DRAG_AND_DROP */
327
328 /* Input validation. */
329
330 AutoCaller autoCaller(this);
331 if (FAILED(autoCaller.rc())) return autoCaller.rc();
332
333 /* Default action is ignoring. */
334 DnDAction_T resAction = DnDAction_Ignore;
335
336 /* Check & convert the drag & drop actions. */
337 uint32_t uDefAction = 0;
338 uint32_t uAllowedActions = 0;
339 GuestDnD::toHGCMActions(aDefaultAction, &uDefAction,
340 aAllowedActions, &uAllowedActions);
341 /* If there is no usable action, ignore this request. */
342 if (isDnDIgnoreAction(uDefAction))
343 return S_OK;
344
345 /* Make a flat data string out of the supported format list. */
346 RTCString strFormats = GuestDnD::toFormatString(m_vecFmtSup, aFormats);
347 /* If there is no valid supported format, ignore this request. */
348 if (strFormats.isEmpty())
349 return S_OK;
350
351 HRESULT hr = S_OK;
352
353 int rc = GuestDnDInst()->adjustScreenCoordinates(aScreenId, &aX, &aY);
354 if (RT_SUCCESS(rc))
355 {
356 GuestDnDMsg Msg;
357 Msg.setType(DragAndDropSvc::HOST_DND_HG_EVT_MOVE);
358 Msg.setNextUInt32(aScreenId);
359 Msg.setNextUInt32(aX);
360 Msg.setNextUInt32(aY);
361 Msg.setNextUInt32(uDefAction);
362 Msg.setNextUInt32(uAllowedActions);
363 Msg.setNextPointer((void*)strFormats.c_str(), strFormats.length() + 1);
364 Msg.setNextUInt32(strFormats.length() + 1);
365
366 rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
367 if (RT_SUCCESS(rc))
368 {
369 GuestDnDResponse *pResp = GuestDnDInst()->response();
370 if (pResp && RT_SUCCESS(pResp->waitForGuestResponse()))
371 resAction = GuestDnD::toMainAction(pResp->defAction());
372 }
373 }
374
375 if (aResultAction)
376 *aResultAction = resAction;
377
378 LogFlowFunc(("hr=%Rhrc, *pResultAction=%ld\n", hr, resAction));
379 return hr;
380#endif /* VBOX_WITH_DRAG_AND_DROP */
381}
382
383HRESULT GuestDnDTarget::leave(ULONG uScreenId)
384{
385#if !defined(VBOX_WITH_DRAG_AND_DROP)
386 ReturnComNotImplemented();
387#else /* VBOX_WITH_DRAG_AND_DROP */
388
389 AutoCaller autoCaller(this);
390 if (FAILED(autoCaller.rc())) return autoCaller.rc();
391
392 HRESULT hr = S_OK;
393 int rc = GuestDnDInst()->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_LEAVE,
394 0 /* cParms */, NULL /* paParms */);
395 if (RT_SUCCESS(rc))
396 {
397 GuestDnDResponse *pResp = GuestDnDInst()->response();
398 if (pResp)
399 pResp->waitForGuestResponse();
400 }
401
402 LogFlowFunc(("hr=%Rhrc\n", hr));
403 return hr;
404#endif /* VBOX_WITH_DRAG_AND_DROP */
405}
406
407HRESULT GuestDnDTarget::drop(ULONG aScreenId, ULONG aX, ULONG aY,
408 DnDAction_T aDefaultAction,
409 const std::vector<DnDAction_T> &aAllowedActions,
410 const std::vector<com::Utf8Str> &aFormats,
411 com::Utf8Str &aFormat, DnDAction_T *aResultAction)
412{
413#if !defined(VBOX_WITH_DRAG_AND_DROP)
414 ReturnComNotImplemented();
415#else /* VBOX_WITH_DRAG_AND_DROP */
416
417 /* Input validation. */
418
419 /* Everything else is optional. */
420
421 AutoCaller autoCaller(this);
422 if (FAILED(autoCaller.rc())) return autoCaller.rc();
423
424 /* Default action is ignoring. */
425 DnDAction_T resAction = DnDAction_Ignore;
426
427 /* Check & convert the drag & drop actions. */
428 uint32_t uDefAction = 0;
429 uint32_t uAllowedActions = 0;
430 GuestDnD::toHGCMActions(aDefaultAction, &uDefAction,
431 aAllowedActions, &uAllowedActions);
432 /* If there is no usable action, ignore this request. */
433 if (isDnDIgnoreAction(uDefAction))
434 return S_OK;
435
436 /* Make a flat data string out of the supported format list. */
437 Utf8Str strFormats = GuestDnD::toFormatString(m_vecFmtSup, aFormats);
438 /* If there is no valid supported format, ignore this request. */
439 if (strFormats.isEmpty())
440 return S_OK;
441
442 HRESULT hr = S_OK;
443
444 /* Adjust the coordinates in a multi-monitor setup. */
445 int rc = GuestDnDInst()->adjustScreenCoordinates(aScreenId, &aX, &aY);
446 if (RT_SUCCESS(rc))
447 {
448 GuestDnDMsg Msg;
449 Msg.setType(DragAndDropSvc::HOST_DND_HG_EVT_DROPPED);
450 Msg.setNextUInt32(aScreenId);
451 Msg.setNextUInt32(aX);
452 Msg.setNextUInt32(aY);
453 Msg.setNextUInt32(uDefAction);
454 Msg.setNextUInt32(uAllowedActions);
455 Msg.setNextPointer((void*)strFormats.c_str(), strFormats.length() + 1);
456 Msg.setNextUInt32(strFormats.length() + 1);
457
458 rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
459 if (RT_SUCCESS(rc))
460 {
461 GuestDnDResponse *pResp = GuestDnDInst()->response();
462 if (pResp && RT_SUCCESS(pResp->waitForGuestResponse()))
463 {
464 resAction = GuestDnD::toMainAction(pResp->defAction());
465 aFormat = pResp->fmtReq();
466
467 LogFlowFunc(("resFormat=%s, resAction=%RU32\n",
468 pResp->fmtReq().c_str(), pResp->defAction()));
469 }
470 }
471 }
472
473 if (aResultAction)
474 *aResultAction = resAction;
475
476 return hr;
477#endif /* VBOX_WITH_DRAG_AND_DROP */
478}
479
480/* static */
481DECLCALLBACK(int) GuestDnDTarget::i_sendDataThread(RTTHREAD Thread, void *pvUser)
482{
483 LogFlowFunc(("pvUser=%p\n", pvUser));
484
485 SendDataTask *pTask = (SendDataTask *)pvUser;
486 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
487
488 const ComObjPtr<GuestDnDTarget> pTarget(pTask->getTarget());
489 Assert(!pTarget.isNull());
490
491 int rc;
492
493 AutoCaller autoCaller(pTarget);
494 if (SUCCEEDED(autoCaller.rc()))
495 {
496 rc = pTarget->i_sendData(pTask->getCtx(), RT_INDEFINITE_WAIT /* msTimeout */);
497 /* Nothing to do here anymore. */
498 }
499 else
500 rc = VERR_COM_INVALID_OBJECT_STATE;
501
502 ASMAtomicWriteBool(&pTarget->mDataBase.mfTransferIsPending, false);
503
504 if (pTask)
505 delete pTask;
506
507 LogFlowFunc(("pTarget=%p returning rc=%Rrc\n", (GuestDnDTarget *)pTarget, rc));
508 return rc;
509}
510
511/**
512 * Initiates a data transfer from the host to the guest. The source is the host whereas the target is the
513 * guest in this case.
514 *
515 * @return HRESULT
516 * @param aScreenId
517 * @param aFormat
518 * @param aData
519 * @param aProgress
520 */
521HRESULT GuestDnDTarget::sendData(ULONG aScreenId, const com::Utf8Str &aFormat, const std::vector<BYTE> &aData,
522 ComPtr<IProgress> &aProgress)
523{
524#if !defined(VBOX_WITH_DRAG_AND_DROP)
525 ReturnComNotImplemented();
526#else /* VBOX_WITH_DRAG_AND_DROP */
527
528 AutoCaller autoCaller(this);
529 if (FAILED(autoCaller.rc())) return autoCaller.rc();
530
531 /* Input validation. */
532 if (RT_UNLIKELY((aFormat.c_str()) == NULL || *(aFormat.c_str()) == '\0'))
533 return setError(E_INVALIDARG, tr("No data format specified"));
534 if (RT_UNLIKELY(!aData.size()))
535 return setError(E_INVALIDARG, tr("No data to send specified"));
536
537 /* Note: At the moment we only support one transfer at a time. */
538 if (ASMAtomicReadBool(&mDataBase.mfTransferIsPending))
539 return setError(E_INVALIDARG, tr("Another send operation already is in progress"));
540
541 ASMAtomicWriteBool(&mDataBase.mfTransferIsPending, true);
542
543 /* Dito. */
544 GuestDnDResponse *pResp = GuestDnDInst()->response();
545 AssertPtr(pResp);
546
547 HRESULT hr = pResp->resetProgress(m_pGuest);
548 if (FAILED(hr))
549 return hr;
550
551 try
552 {
553 PSENDDATACTX pSendCtx = new SENDDATACTX;
554 RT_BZERO(pSendCtx, sizeof(SENDDATACTX));
555
556 pSendCtx->mpTarget = this;
557 pSendCtx->mpResp = pResp;
558 pSendCtx->mScreenID = aScreenId;
559 pSendCtx->mFmtReq = aFormat;
560 pSendCtx->mData.vecData = aData;
561
562 SendDataTask *pTask = new SendDataTask(this, pSendCtx);
563 AssertReturn(pTask->isOk(), pTask->getRC());
564
565 LogFlowFunc(("Starting thread ...\n"));
566
567 int rc = RTThreadCreate(NULL, GuestDnDTarget::i_sendDataThread,
568 (void *)pTask, 0, RTTHREADTYPE_MAIN_WORKER, 0, "dndTgtSndData");
569 if (RT_SUCCESS(rc))
570 {
571 hr = pResp->queryProgressTo(aProgress.asOutParam());
572 ComAssertComRC(hr);
573
574 /* Note: pTask is now owned by the worker thread. */
575 }
576 else
577 hr = setError(VBOX_E_IPRT_ERROR, tr("Starting thread failed (%Rrc)"), rc);
578
579 if (RT_FAILURE(rc))
580 delete pSendCtx;
581 }
582 catch(std::bad_alloc &)
583 {
584 hr = setError(E_OUTOFMEMORY);
585 }
586
587 /* Note: mDataBase.mfTransferIsPending will be set to false again by i_sendDataThread. */
588
589 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
590 return hr;
591#endif /* VBOX_WITH_DRAG_AND_DROP */
592}
593
594int GuestDnDTarget::i_cancelOperation(void)
595{
596 /** @todo Check for pending cancel requests. */
597
598#if 0 /** @todo Later. */
599 /* Cancel any outstanding waits for guest responses first. */
600 if (pResp)
601 pResp->notifyAboutGuestResponse();
602#endif
603
604 LogFlowFunc(("Cancelling operation, telling guest ...\n"));
605 return GuestDnDInst()->hostCall(DragAndDropSvc::HOST_DND_HG_EVT_CANCEL, 0 /* cParms */, NULL /*paParms*/);
606}
607
608/* static */
609Utf8Str GuestDnDTarget::i_guestErrorToString(int guestRc)
610{
611 Utf8Str strError;
612
613 switch (guestRc)
614 {
615 case VERR_ACCESS_DENIED:
616 strError += Utf8StrFmt(tr("For one or more guest files or directories selected for transferring to the host your guest "
617 "user does not have the appropriate access rights for. Please make sure that all selected "
618 "elements can be accessed and that your guest user has the appropriate rights"));
619 break;
620
621 case VERR_NOT_FOUND:
622 /* Should not happen due to file locking on the guest, but anyway ... */
623 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were not"
624 "found on the guest anymore. This can be the case if the guest files were moved and/or"
625 "altered while the drag and drop operation was in progress"));
626 break;
627
628 case VERR_SHARING_VIOLATION:
629 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were locked. "
630 "Please make sure that all selected elements can be accessed and that your guest user has "
631 "the appropriate rights"));
632 break;
633
634 case VERR_TIMEOUT:
635 strError += Utf8StrFmt(tr("The guest was not able to process the drag and drop data within time"));
636 break;
637
638 default:
639 strError += Utf8StrFmt(tr("Drag and drop error from guest (%Rrc)"), guestRc);
640 break;
641 }
642
643 return strError;
644}
645
646/* static */
647Utf8Str GuestDnDTarget::i_hostErrorToString(int hostRc)
648{
649 Utf8Str strError;
650
651 switch (hostRc)
652 {
653 case VERR_ACCESS_DENIED:
654 strError += Utf8StrFmt(tr("For one or more host files or directories selected for transferring to the guest your host "
655 "user does not have the appropriate access rights for. Please make sure that all selected "
656 "elements can be accessed and that your host user has the appropriate rights."));
657 break;
658
659 case VERR_NOT_FOUND:
660 /* Should not happen due to file locking on the host, but anyway ... */
661 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the host were not"
662 "found on the host anymore. This can be the case if the host files were moved and/or"
663 "altered while the drag and drop operation was in progress."));
664 break;
665
666 case VERR_SHARING_VIOLATION:
667 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the guest were locked. "
668 "Please make sure that all selected elements can be accessed and that your host user has "
669 "the appropriate rights."));
670 break;
671
672 default:
673 strError += Utf8StrFmt(tr("Drag and drop error from host (%Rrc)"), hostRc);
674 break;
675 }
676
677 return strError;
678}
679
680int GuestDnDTarget::i_sendData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
681{
682 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
683
684 GuestDnD *pInst = GuestDnDInst();
685 if (!pInst)
686 return VERR_INVALID_POINTER;
687
688 int rc;
689
690 ASMAtomicWriteBool(&pCtx->mIsActive, true);
691
692 /* Clear all remaining outgoing messages. */
693 mDataBase.mListOutgoing.clear();
694
695 const char *pszFormat = pCtx->mFmtReq.c_str();
696 uint32_t cbFormat = pCtx->mFmtReq.length() + 1;
697
698 /* Do we need to build up a file tree? */
699 bool fHasURIList = DnDMIMEHasFileURLs(pszFormat, cbFormat);
700 if (fHasURIList)
701 {
702 rc = i_sendURIData(pCtx, msTimeout);
703 }
704 else
705 {
706 rc = i_sendRawData(pCtx, msTimeout);
707 }
708
709 ASMAtomicWriteBool(&pCtx->mIsActive, false);
710
711#undef DATA_IS_VALID_BREAK
712
713 LogFlowFuncLeaveRC(rc);
714 return rc;
715}
716
717int GuestDnDTarget::i_sendDirectory(PSENDDATACTX pCtx, GuestDnDMsg *pMsg, DnDURIObject &aDirectory)
718{
719 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
720
721 RTCString strPath = aDirectory.GetDestPath();
722 if (strPath.isEmpty())
723 return VERR_INVALID_PARAMETER;
724 if (strPath.length() >= RTPATH_MAX) /* Note: Maximum is RTPATH_MAX on guest side. */
725 return VERR_BUFFER_OVERFLOW;
726
727 LogFlowFunc(("Sending directory \"%s\" using protocol v%RU32 ...\n", strPath.c_str(), mDataBase.mProtocolVersion));
728
729 pMsg->setType(DragAndDropSvc::HOST_DND_HG_SND_DIR);
730 pMsg->setNextString(strPath.c_str()); /* path */
731 pMsg->setNextUInt32((uint32_t)(strPath.length() + 1)); /* path length - note: Maximum is RTPATH_MAX on guest side. */
732 pMsg->setNextUInt32(aDirectory.GetMode()); /* mode */
733
734 return VINF_SUCCESS;
735}
736
737int GuestDnDTarget::i_sendFile(PSENDDATACTX pCtx, GuestDnDMsg *pMsg, DnDURIObject &aFile)
738{
739 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
740
741 RTCString strPathSrc = aFile.GetSourcePath();
742 if (strPathSrc.isEmpty())
743 return VERR_INVALID_PARAMETER;
744
745 int rc = VINF_SUCCESS;
746
747 LogFlowFunc(("Sending \"%s\" (%RU32 bytes buffer) using protocol v%RU32 ...\n",
748 strPathSrc.c_str(), mData.mcbBlockSize, mDataBase.mProtocolVersion));
749
750 bool fOpen = aFile.IsOpen();
751 if (!fOpen)
752 {
753 LogFlowFunc(("Opening \"%s\" ...\n", strPathSrc.c_str()));
754 rc = aFile.OpenEx(strPathSrc, DnDURIObject::File, DnDURIObject::Source,
755 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, 0 /* fFlags */);
756 if (RT_FAILURE(rc))
757 LogRel(("DnD: Error opening host file \"%s\", rc=%Rrc\n", strPathSrc.c_str(), rc));
758 }
759
760 bool fSendFileData = false;
761 if (RT_SUCCESS(rc))
762 {
763 if (mDataBase.mProtocolVersion >= 2)
764 {
765 if (!fOpen)
766 {
767 /*
768 * Since protocol v2 the file header and the actual file contents are
769 * separate messages, so send the file header first.
770 * The just registered callback will be called by the guest afterwards.
771 */
772 pMsg->setType(DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR);
773 pMsg->setNextUInt32(0); /* context ID */
774 rc = pMsg->setNextString(aFile.GetDestPath().c_str()); /* pvName */
775 AssertRC(rc);
776 pMsg->setNextUInt32((uint32_t)(aFile.GetDestPath().length() + 1)); /* cbName */
777 pMsg->setNextUInt32(0); /* uFlags */
778 pMsg->setNextUInt32(aFile.GetMode()); /* fMode */
779 pMsg->setNextUInt64(aFile.GetSize()); /* uSize */
780
781 LogFlowFunc(("Sending file header ...\n"));
782 LogRel2(("DnD: Transferring host file to guest: %s (%RU64 bytes, mode 0x%x)\n",
783 strPathSrc.c_str(), aFile.GetSize(), aFile.GetMode()));
784
785 /** @todo Set progress object title to current file being transferred? */
786 }
787 else
788 {
789 /* File header was sent, so only send the actual file data. */
790 fSendFileData = true;
791 }
792 }
793 else /* Protocol v1. */
794 {
795 /* Always send the file data, every time. */
796 fSendFileData = true;
797 }
798 }
799
800 if ( RT_SUCCESS(rc)
801 && fSendFileData)
802 {
803 rc = i_sendFileData(pCtx, pMsg, aFile);
804 }
805
806 LogFlowFuncLeaveRC(rc);
807 return rc;
808}
809
810int GuestDnDTarget::i_sendFileData(PSENDDATACTX pCtx, GuestDnDMsg *pMsg, DnDURIObject &aFile)
811{
812 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
813 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
814
815 GuestDnDResponse *pResp = pCtx->mpResp;
816 AssertPtr(pResp);
817
818 /** @todo Don't allow concurrent reads per context! */
819
820 /* Something to transfer? */
821 if ( pCtx->mURI.lstURI.IsEmpty()
822 || !pCtx->mIsActive)
823 {
824 return VERR_WRONG_ORDER;
825 }
826
827 /*
828 * Start sending stuff.
829 */
830
831 /* Set the message type. */
832 pMsg->setType(DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA);
833
834 /* Protocol version 1 sends the file path *every* time with a new file chunk.
835 * In protocol version 2 we only do this once with HOST_DND_HG_SND_FILE_HDR. */
836 if (mDataBase.mProtocolVersion <= 1)
837 {
838 pMsg->setNextString(aFile.GetDestPath().c_str()); /* pvName */
839 pMsg->setNextUInt32((uint32_t)(aFile.GetDestPath().length() + 1)); /* cbName */
840 }
841 else
842 {
843 /* Protocol version 2 also sends the context ID. Currently unused. */
844 pMsg->setNextUInt32(0); /* context ID */
845 }
846
847 uint32_t cbRead = 0;
848
849 int rc = aFile.Read(pCtx->mURI.pvScratchBuf, pCtx->mURI.cbScratchBuf, &cbRead);
850 if (RT_SUCCESS(rc))
851 {
852 pCtx->mData.cbProcessed += cbRead;
853
854 if (mDataBase.mProtocolVersion <= 1)
855 {
856 pMsg->setNextPointer(pCtx->mURI.pvScratchBuf, cbRead); /* pvData */
857 pMsg->setNextUInt32(cbRead); /* cbData */
858 pMsg->setNextUInt32(aFile.GetMode()); /* fMode */
859 }
860 else
861 {
862 pMsg->setNextPointer(pCtx->mURI.pvScratchBuf, cbRead); /* pvData */
863 pMsg->setNextUInt32(cbRead); /* cbData */
864 }
865
866 if (aFile.IsComplete()) /* Done reading? */
867 {
868 LogRel2(("DnD: File transfer to guest complete: %s\n", aFile.GetSourcePath().c_str()));
869 LogFlowFunc(("File \"%s\" complete\n", aFile.GetSourcePath().c_str()));
870 rc = VINF_EOF;
871 }
872 }
873
874 LogFlowFuncLeaveRC(rc);
875 return rc;
876}
877
878/* static */
879DECLCALLBACK(int) GuestDnDTarget::i_sendURIDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
880{
881 PSENDDATACTX pCtx = (PSENDDATACTX)pvUser;
882 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
883
884 GuestDnDTarget *pThis = pCtx->mpTarget;
885 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
886
887 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
888
889 int rc = VINF_SUCCESS; /* Will be reported back to guest. */
890
891 int rcCallback = VINF_SUCCESS; /* rc for the callback. */
892 bool fNotify = false;
893
894 switch (uMsg)
895 {
896 case DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG:
897 {
898 DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSG pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSG>(pvParms);
899 AssertPtr(pCBData);
900 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGGETNEXTHOSTMSG) == cbParms, VERR_INVALID_PARAMETER);
901 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
902
903 try
904 {
905 GuestDnDMsg *pMsg = new GuestDnDMsg();
906
907 rc = pThis->i_sendURIDataLoop(pCtx, pMsg);
908 if (RT_SUCCESS(rc))
909 {
910 rc = pThis->msgQueueAdd(pMsg);
911 if (RT_SUCCESS(rc)) /* Return message type & required parameter count to the guest. */
912 {
913 LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG -> %RU32 (%RU32 params)\n", pMsg->getType(), pMsg->getCount()));
914 pCBData->uMsg = pMsg->getType();
915 pCBData->cParms = pMsg->getCount();
916 }
917 }
918
919 if (RT_FAILURE(rc))
920 delete pMsg;
921 }
922 catch(std::bad_alloc & /*e*/)
923 {
924 rc = VERR_NO_MEMORY;
925 }
926 break;
927 }
928 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
929 {
930 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
931 AssertPtr(pCBData);
932 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
933 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
934
935 pCtx->mpResp->reset();
936
937 if (RT_SUCCESS(pCBData->rc))
938 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
939
940 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc,
941 GuestDnDTarget::i_guestErrorToString(pCBData->rc));
942 if (RT_SUCCESS(rc))
943 rcCallback = VERR_GSTDND_GUEST_ERROR;
944 break;
945 }
946 case DragAndDropSvc::HOST_DND_HG_SND_DIR:
947 case DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR:
948 case DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA:
949 {
950 DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSGDATA pCBData
951 = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBHGGETNEXTHOSTMSGDATA>(pvParms);
952 AssertPtr(pCBData);
953 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBHGGETNEXTHOSTMSGDATA) == cbParms, VERR_INVALID_PARAMETER);
954 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
955
956 LogFlowFunc(("pCBData->uMsg=%RU32, paParms=%p, cParms=%RU32\n", pCBData->uMsg, pCBData->paParms, pCBData->cParms));
957
958 GuestDnDMsg *pMsg = pThis->msgQueueGetNext();
959 if (pMsg)
960 {
961 /*
962 * Sanity checks.
963 */
964 if ( pCBData->uMsg != uMsg
965 || pCBData->paParms == NULL
966 || pCBData->cParms != pMsg->getCount())
967 {
968 /* Start over. */
969 pThis->msgQueueClear();
970
971 rc = VERR_INVALID_PARAMETER;
972 }
973
974 if (RT_SUCCESS(rc))
975 {
976 LogFlowFunc(("Returning uMsg=%RU32\n", uMsg));
977 rc = HGCM::Message::copyParms(pMsg->getCount(), pMsg->getParms(), pCBData->paParms);
978 if (RT_SUCCESS(rc))
979 {
980 pCBData->cParms = pMsg->getCount();
981 pThis->msgQueueRemoveNext();
982 }
983 else
984 LogFlowFunc(("Copying parameters failed with rc=%Rrc\n", rc));
985 }
986 }
987 else
988 rc = VERR_NO_DATA;
989
990 LogFlowFunc(("Processing next message ended with rc=%Rrc\n", rc));
991 break;
992 }
993 default:
994 rc = VERR_NOT_SUPPORTED;
995 break;
996 }
997
998 if ( RT_FAILURE(rc)
999 || RT_FAILURE(rcCallback))
1000 {
1001 fNotify = true;
1002 if (RT_SUCCESS(rcCallback))
1003 rcCallback = rc;
1004 }
1005
1006 if (RT_FAILURE(rc))
1007 {
1008 switch (rc)
1009 {
1010 case VERR_NO_DATA:
1011 LogRel2(("DnD: Transfer to guest complete\n"));
1012 break;
1013
1014 case VERR_CANCELLED:
1015 LogRel2(("DnD: Transfer to guest canceled\n"));
1016 break;
1017
1018 default:
1019 LogRel(("DnD: Error %Rrc occurred, aborting transfer to guest\n", rc));
1020 break;
1021 }
1022
1023 /* Unregister this callback. */
1024 AssertPtr(pCtx->mpResp);
1025 int rc2 = pCtx->mpResp->setCallback(uMsg, NULL /* PFNGUESTDNDCALLBACK */);
1026 AssertRC(rc2);
1027 }
1028
1029 LogFlowFunc(("fNotify=%RTbool, rcCallback=%Rrc, rc=%Rrc\n", fNotify, rcCallback, rc));
1030
1031 if (fNotify)
1032 {
1033 int rc2 = pCtx->mCallback.Notify(rcCallback);
1034 AssertRC(rc2);
1035 }
1036
1037 LogFlowFuncLeaveRC(rc);
1038 return rc; /* Tell the guest. */
1039}
1040
1041int GuestDnDTarget::i_sendURIData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
1042{
1043 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1044 AssertPtr(pCtx->mpResp);
1045
1046#define URI_DATA_IS_VALID_BREAK(x) \
1047 if (!x) \
1048 { \
1049 LogFlowFunc(("Invalid URI data value for \"" #x "\"\n")); \
1050 rc = VERR_INVALID_PARAMETER; \
1051 break; \
1052 }
1053
1054 void *pvBuf = RTMemAlloc(mData.mcbBlockSize);
1055 if (!pvBuf)
1056 return VERR_NO_MEMORY;
1057
1058 int rc;
1059
1060#define REGISTER_CALLBACK(x) \
1061 rc = pCtx->mpResp->setCallback(x, i_sendURIDataCallback, pCtx); \
1062 if (RT_FAILURE(rc)) \
1063 return rc;
1064
1065#define UNREGISTER_CALLBACK(x) \
1066 { \
1067 int rc2 = pCtx->mpResp->setCallback(x, NULL); \
1068 AssertRC(rc2); \
1069 }
1070
1071 rc = pCtx->mCallback.Reset();
1072 if (RT_FAILURE(rc))
1073 return rc;
1074
1075 /*
1076 * Register callbacks.
1077 */
1078 /* Guest callbacks. */
1079 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG);
1080 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
1081 /* Host callbacks. */
1082 REGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_DIR);
1083 if (mDataBase.mProtocolVersion >= 2)
1084 REGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR);
1085 REGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA);
1086
1087 do
1088 {
1089 /*
1090 * Set our scratch buffer.
1091 */
1092 pCtx->mURI.pvScratchBuf = pvBuf;
1093 pCtx->mURI.cbScratchBuf = mData.mcbBlockSize;
1094
1095 /*
1096 * Extract URI list from byte data.
1097 */
1098 DnDURIList &lstURI = pCtx->mURI.lstURI; /* Use the URI list from the context. */
1099
1100 const char *pszList = (const char *)&pCtx->mData.vecData.front();
1101 URI_DATA_IS_VALID_BREAK(pszList);
1102
1103 uint32_t cbList = pCtx->mData.vecData.size();
1104 URI_DATA_IS_VALID_BREAK(cbList);
1105
1106 RTCList<RTCString> lstURIOrg = RTCString(pszList, cbList).split("\r\n");
1107 URI_DATA_IS_VALID_BREAK(!lstURIOrg.isEmpty());
1108
1109 rc = lstURI.AppendURIPathsFromList(lstURIOrg, 0 /* fFlags */);
1110 if (RT_SUCCESS(rc))
1111 LogFlowFunc(("URI root objects: %zu, total bytes (raw data to transfer): %zu\n",
1112 lstURI.RootCount(), lstURI.TotalBytes()));
1113 else
1114 break;
1115
1116 pCtx->mData.cbProcessed = 0;
1117 pCtx->mData.cbToProcess = lstURI.TotalBytes();
1118
1119 /*
1120 * The first message always is the meta info for the data. The meta
1121 * info *only* contains the root elements of an URI list.
1122 *
1123 * After the meta data we generate the messages required to send the data itself.
1124 */
1125 Assert(!lstURI.IsEmpty());
1126 RTCString strData = lstURI.RootToString().c_str();
1127 size_t cbData = strData.length() + 1; /* Include terminating zero. */
1128
1129 GuestDnDMsg MsgSndData;
1130 MsgSndData.setType(DragAndDropSvc::HOST_DND_HG_SND_DATA);
1131 MsgSndData.setNextUInt32(pCtx->mScreenID);
1132 MsgSndData.setNextPointer((void *)pCtx->mFmtReq.c_str(), (uint32_t)pCtx->mFmtReq.length() + 1);
1133 MsgSndData.setNextUInt32((uint32_t)pCtx->mFmtReq.length() + 1);
1134 MsgSndData.setNextPointer((void*)strData.c_str(), (uint32_t)cbData);
1135 MsgSndData.setNextUInt32((uint32_t)cbData);
1136
1137 rc = GuestDnDInst()->hostCall(MsgSndData.getType(), MsgSndData.getCount(), MsgSndData.getParms());
1138 if (RT_SUCCESS(rc))
1139 {
1140 rc = waitForEvent(msTimeout, pCtx->mCallback, pCtx->mpResp);
1141 if (RT_FAILURE(rc))
1142 {
1143 if (rc == VERR_CANCELLED)
1144 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_CANCELLED, VINF_SUCCESS);
1145 else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
1146 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, rc,
1147 GuestDnDTarget::i_hostErrorToString(rc));
1148 }
1149 else
1150 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_COMPLETE, VINF_SUCCESS);
1151 }
1152
1153 } while (0);
1154
1155 /*
1156 * Unregister callbacks.
1157 */
1158 /* Guest callbacks. */
1159 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GET_NEXT_HOST_MSG);
1160 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
1161 /* Host callbacks. */
1162 UNREGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_DIR);
1163 if (mDataBase.mProtocolVersion >= 2)
1164 UNREGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_HDR);
1165 UNREGISTER_CALLBACK(DragAndDropSvc::HOST_DND_HG_SND_FILE_DATA);
1166
1167#undef REGISTER_CALLBACK
1168#undef UNREGISTER_CALLBACK
1169
1170 /*
1171 * Now that we've cleaned up tell the guest side to cancel.
1172 * This does not imply we're waiting for the guest to react, as the
1173 * host side never must depend on anything from the guest.
1174 */
1175 if (rc == VERR_CANCELLED)
1176 {
1177 int rc2 = sendCancel();
1178 AssertRC(rc2);
1179 }
1180
1181 /* Destroy temporary scratch buffer. */
1182 if (pvBuf)
1183 RTMemFree(pvBuf);
1184
1185#undef URI_DATA_IS_VALID_BREAK
1186
1187 LogFlowFuncLeaveRC(rc);
1188 return rc;
1189}
1190
1191int GuestDnDTarget::i_sendURIDataLoop(PSENDDATACTX pCtx, GuestDnDMsg *pMsg)
1192{
1193 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1194
1195 DnDURIList &lstURI = pCtx->mURI.lstURI;
1196
1197 int rc;
1198
1199 uint64_t cbTotal = pCtx->mData.cbToProcess;
1200 uint8_t uPercent = pCtx->mData.cbProcessed * 100 / (cbTotal ? cbTotal : 1);
1201
1202 LogFlowFunc(("%RU64 / %RU64 -- %RU8%%\n", pCtx->mData.cbProcessed, cbTotal, uPercent));
1203
1204 bool fComplete = (uPercent >= 100) || lstURI.IsEmpty();
1205
1206 if (pCtx->mpResp)
1207 {
1208 int rc2 = pCtx->mpResp->setProgress(uPercent,
1209 fComplete
1210 ? DragAndDropSvc::DND_PROGRESS_COMPLETE
1211 : DragAndDropSvc::DND_PROGRESS_RUNNING);
1212 AssertRC(rc2);
1213 }
1214
1215 if (fComplete)
1216 {
1217 LogFlowFunc(("Last URI item processed, bailing out\n"));
1218 return VERR_NO_DATA;
1219 }
1220
1221 Assert(!lstURI.IsEmpty());
1222 DnDURIObject &curObj = lstURI.First();
1223
1224 uint32_t fMode = curObj.GetMode();
1225 LogFlowFunc(("Processing srcPath=%s, dstPath=%s, fMode=0x%x, cbSize=%RU32, fIsDir=%RTbool, fIsFile=%RTbool\n",
1226 curObj.GetSourcePath().c_str(), curObj.GetDestPath().c_str(),
1227 fMode, curObj.GetSize(),
1228 RTFS_IS_DIRECTORY(fMode), RTFS_IS_FILE(fMode)));
1229
1230 if (RTFS_IS_DIRECTORY(fMode))
1231 {
1232 rc = i_sendDirectory(pCtx, pMsg, curObj);
1233 }
1234 else if (RTFS_IS_FILE(fMode))
1235 {
1236 rc = i_sendFile(pCtx, pMsg, curObj);
1237 }
1238 else
1239 {
1240 AssertMsgFailed(("fMode=0x%x is not supported for srcPath=%s, dstPath=%s\n",
1241 fMode, curObj.GetSourcePath().c_str(), curObj.GetDestPath().c_str()));
1242 rc = VERR_NOT_SUPPORTED;
1243 }
1244
1245 bool fRemove = false; /* Remove current entry? */
1246 if ( curObj.IsComplete()
1247 || RT_FAILURE(rc))
1248 {
1249 fRemove = true;
1250 }
1251
1252 if (fRemove)
1253 {
1254 LogFlowFunc(("Removing \"%s\" from list, rc=%Rrc\n", curObj.GetSourcePath().c_str(), rc));
1255 lstURI.RemoveFirst();
1256 }
1257
1258 LogFlowFuncLeaveRC(rc);
1259 return rc;
1260}
1261
1262int GuestDnDTarget::i_sendRawData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
1263{
1264 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1265 NOREF(msTimeout);
1266
1267 GuestDnD *pInst = GuestDnDInst();
1268 AssertPtr(pInst);
1269
1270 /* At the moment we only allow up to 64K raw data. */
1271 size_t cbDataTotal = pCtx->mData.vecData.size();
1272 if ( !cbDataTotal
1273 || cbDataTotal > _64K)
1274 {
1275 return VERR_INVALID_PARAMETER;
1276 }
1277
1278 /* Just copy over the raw data. */
1279 GuestDnDMsg Msg;
1280 Msg.setType(DragAndDropSvc::HOST_DND_HG_SND_DATA);
1281 Msg.setNextUInt32(pCtx->mScreenID);
1282 Msg.setNextPointer((void *)pCtx->mFmtReq.c_str(), (uint32_t)pCtx->mFmtReq.length() + 1);
1283 Msg.setNextUInt32((uint32_t)pCtx->mFmtReq.length() + 1);
1284 Msg.setNextPointer((void*)&pCtx->mData.vecData.front(), (uint32_t)cbDataTotal);
1285 Msg.setNextUInt32(cbDataTotal);
1286
1287 LogFlowFunc(("%zu total bytes of raw data to transfer\n", cbDataTotal));
1288
1289 return pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
1290}
1291
1292HRESULT GuestDnDTarget::cancel(BOOL *aVeto)
1293{
1294#if !defined(VBOX_WITH_DRAG_AND_DROP)
1295 ReturnComNotImplemented();
1296#else /* VBOX_WITH_DRAG_AND_DROP */
1297
1298 int rc = i_cancelOperation();
1299
1300 if (aVeto)
1301 *aVeto = FALSE; /** @todo */
1302
1303 return RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
1304#endif /* VBOX_WITH_DRAG_AND_DROP */
1305}
1306
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