VirtualBox

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

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

DnD: Fixed host->guest crashes on OS X hosts.

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