VirtualBox

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

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

DnD: Changed DnDURIList's allocation scheme for stored DnDURIObject objects.

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