VirtualBox

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

Last change on this file since 62372 was 62372, checked in by vboxsync, 8 years ago

backed out r108916

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.7 KB
Line 
1/* $Id: GuestDnDTargetImpl.cpp 62372 2016-07-20 17:25:41Z 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#include "ThreadTask.h"
29
30#include <algorithm> /* For std::find(). */
31
32#include <iprt/asm.h>
33#include <iprt/file.h>
34#include <iprt/dir.h>
35#include <iprt/path.h>
36#include <iprt/uri.h>
37#include <iprt/cpp/utils.h> /* For unconst(). */
38
39#include <VBox/com/array.h>
40
41#include <VBox/GuestHost/DragAndDrop.h>
42#include <VBox/HostServices/Service.h>
43
44#ifdef LOG_GROUP
45 #undef LOG_GROUP
46#endif
47#define LOG_GROUP LOG_GROUP_GUEST_DND
48#include <VBox/log.h>
49
50
51/**
52 * Base class for a target task.
53 */
54class GuestDnDTargetTask : public ThreadTask
55{
56public:
57
58 GuestDnDTargetTask(GuestDnDTarget *pTarget)
59 : ThreadTask("GenericGuestDnDTargetTask")
60 , mTarget(pTarget)
61 , mRC(VINF_SUCCESS) { }
62
63 virtual ~GuestDnDTargetTask(void) { }
64
65 int getRC(void) const { return mRC; }
66 bool isOk(void) const { return RT_SUCCESS(mRC); }
67 const ComObjPtr<GuestDnDTarget> &getTarget(void) const { return mTarget; }
68
69protected:
70
71 const ComObjPtr<GuestDnDTarget> mTarget;
72 int mRC;
73};
74
75/**
76 * Task structure for sending data to a target using
77 * a worker thread.
78 */
79class SendDataTask : public GuestDnDTargetTask
80{
81public:
82
83 SendDataTask(GuestDnDTarget *pTarget, PSENDDATACTX pCtx)
84 : GuestDnDTargetTask(pTarget),
85 mpCtx(pCtx)
86 {
87 m_strTaskName = "dndTgtSndData";
88 }
89
90 void handler()
91 {
92 int vrc = GuestDnDTarget::i_sendDataThread(*m_pThread, this);
93 }
94
95 virtual ~SendDataTask(void)
96 {
97 if (mpCtx)
98 {
99 delete mpCtx;
100 mpCtx = NULL;
101 }
102 }
103
104
105 PSENDDATACTX getCtx(void) { return mpCtx; }
106
107protected:
108
109 /** Pointer to send data context. */
110 PSENDDATACTX mpCtx;
111};
112
113// constructor / destructor
114/////////////////////////////////////////////////////////////////////////////
115
116DEFINE_EMPTY_CTOR_DTOR(GuestDnDTarget)
117
118HRESULT GuestDnDTarget::FinalConstruct(void)
119{
120 /* Set the maximum block size our guests can handle to 64K. This always has
121 * been hardcoded until now. */
122 /* Note: Never ever rely on information from the guest; the host dictates what and
123 * how to do something, so try to negogiate a sensible value here later. */
124 mData.mcbBlockSize = _64K; /** @todo Make this configurable. */
125
126 LogFlowThisFunc(("\n"));
127 return BaseFinalConstruct();
128}
129
130void GuestDnDTarget::FinalRelease(void)
131{
132 LogFlowThisFuncEnter();
133 uninit();
134 BaseFinalRelease();
135 LogFlowThisFuncLeave();
136}
137
138// public initializer/uninitializer for internal purposes only
139/////////////////////////////////////////////////////////////////////////////
140
141int GuestDnDTarget::init(const ComObjPtr<Guest>& pGuest)
142{
143 LogFlowThisFuncEnter();
144
145 /* Enclose the state transition NotReady->InInit->Ready. */
146 AutoInitSpan autoInitSpan(this);
147 AssertReturn(autoInitSpan.isOk(), E_FAIL);
148
149 unconst(m_pGuest) = pGuest;
150
151 /* Confirm a successful initialization when it's the case. */
152 autoInitSpan.setSucceeded();
153
154 return VINF_SUCCESS;
155}
156
157/**
158 * Uninitializes the instance.
159 * Called from FinalRelease().
160 */
161void GuestDnDTarget::uninit(void)
162{
163 LogFlowThisFunc(("\n"));
164
165 /* Enclose the state transition Ready->InUninit->NotReady. */
166 AutoUninitSpan autoUninitSpan(this);
167 if (autoUninitSpan.uninitDone())
168 return;
169}
170
171// implementation of wrapped IDnDBase methods.
172/////////////////////////////////////////////////////////////////////////////
173
174HRESULT GuestDnDTarget::isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
175{
176#if !defined(VBOX_WITH_DRAG_AND_DROP)
177 ReturnComNotImplemented();
178#else /* VBOX_WITH_DRAG_AND_DROP */
179
180 AutoCaller autoCaller(this);
181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
182
183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
184
185 return GuestDnDBase::i_isFormatSupported(aFormat, aSupported);
186#endif /* VBOX_WITH_DRAG_AND_DROP */
187}
188
189HRESULT GuestDnDTarget::getFormats(GuestDnDMIMEList &aFormats)
190{
191#if !defined(VBOX_WITH_DRAG_AND_DROP)
192 ReturnComNotImplemented();
193#else /* VBOX_WITH_DRAG_AND_DROP */
194
195 AutoCaller autoCaller(this);
196 if (FAILED(autoCaller.rc())) return autoCaller.rc();
197
198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
199
200 return GuestDnDBase::i_getFormats(aFormats);
201#endif /* VBOX_WITH_DRAG_AND_DROP */
202}
203
204HRESULT GuestDnDTarget::addFormats(const GuestDnDMIMEList &aFormats)
205{
206#if !defined(VBOX_WITH_DRAG_AND_DROP)
207 ReturnComNotImplemented();
208#else /* VBOX_WITH_DRAG_AND_DROP */
209
210 AutoCaller autoCaller(this);
211 if (FAILED(autoCaller.rc())) return autoCaller.rc();
212
213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
214
215 return GuestDnDBase::i_addFormats(aFormats);
216#endif /* VBOX_WITH_DRAG_AND_DROP */
217}
218
219HRESULT GuestDnDTarget::removeFormats(const GuestDnDMIMEList &aFormats)
220{
221#if !defined(VBOX_WITH_DRAG_AND_DROP)
222 ReturnComNotImplemented();
223#else /* VBOX_WITH_DRAG_AND_DROP */
224
225 AutoCaller autoCaller(this);
226 if (FAILED(autoCaller.rc())) return autoCaller.rc();
227
228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
229
230 return GuestDnDBase::i_removeFormats(aFormats);
231#endif /* VBOX_WITH_DRAG_AND_DROP */
232}
233
234HRESULT GuestDnDTarget::getProtocolVersion(ULONG *aProtocolVersion)
235{
236#if !defined(VBOX_WITH_DRAG_AND_DROP)
237 ReturnComNotImplemented();
238#else /* VBOX_WITH_DRAG_AND_DROP */
239
240 AutoCaller autoCaller(this);
241 if (FAILED(autoCaller.rc())) return autoCaller.rc();
242
243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
244
245 return GuestDnDBase::i_getProtocolVersion(aProtocolVersion);
246#endif /* VBOX_WITH_DRAG_AND_DROP */
247}
248
249// implementation of wrapped IDnDTarget methods.
250/////////////////////////////////////////////////////////////////////////////
251
252HRESULT GuestDnDTarget::enter(ULONG aScreenId, ULONG aX, ULONG aY,
253 DnDAction_T aDefaultAction,
254 const std::vector<DnDAction_T> &aAllowedActions,
255 const GuestDnDMIMEList &aFormats,
256 DnDAction_T *aResultAction)
257{
258#if !defined(VBOX_WITH_DRAG_AND_DROP)
259 ReturnComNotImplemented();
260#else /* VBOX_WITH_DRAG_AND_DROP */
261
262 /* Input validation. */
263 if (aDefaultAction == DnDAction_Ignore)
264 return setError(E_INVALIDARG, tr("No default action specified"));
265 if (!aAllowedActions.size())
266 return setError(E_INVALIDARG, tr("Number of allowed actions is empty"));
267 if (!aFormats.size())
268 return setError(E_INVALIDARG, tr("Number of supported formats is empty"));
269
270 AutoCaller autoCaller(this);
271 if (FAILED(autoCaller.rc())) return autoCaller.rc();
272
273 /* Determine guest DnD protocol to use. */
274 GuestDnDBase::getProtocolVersion(&mDataBase.m_uProtocolVersion);
275
276 /* Default action is ignoring. */
277 DnDAction_T resAction = DnDAction_Ignore;
278
279 /* Check & convert the drag & drop actions */
280 uint32_t uDefAction = 0;
281 uint32_t uAllowedActions = 0;
282 GuestDnD::toHGCMActions(aDefaultAction, &uDefAction,
283 aAllowedActions, &uAllowedActions);
284 /* If there is no usable action, ignore this request. */
285 if (isDnDIgnoreAction(uDefAction))
286 return S_OK;
287
288 /*
289 * Make a flat data string out of the supported format list.
290 * In the GuestDnDTarget case the source formats are from the host,
291 * as GuestDnDTarget acts as a source for the guest.
292 */
293 Utf8Str strFormats = GuestDnD::toFormatString(GuestDnD::toFilteredFormatList(m_lstFmtSupported, aFormats));
294 if (strFormats.isEmpty())
295 return setError(E_INVALIDARG, tr("No or not supported format(s) specified"));
296 const uint32_t cbFormats = (uint32_t)strFormats.length() + 1; /* Include terminating zero. */
297
298 LogRel2(("DnD: Offered formats to guest:\n"));
299 RTCList<RTCString> lstFormats = strFormats.split("\r\n");
300 for (size_t i = 0; i < lstFormats.size(); i++)
301 LogRel2(("DnD: \t%s\n", lstFormats[i].c_str()));
302
303 /* Save the formats offered to the guest. This is needed to later
304 * decide what to do with the data when sending stuff to the guest. */
305 m_lstFmtOffered = aFormats;
306 Assert(m_lstFmtOffered.size());
307
308 HRESULT hr = S_OK;
309
310 /* Adjust the coordinates in a multi-monitor setup. */
311 int rc = GuestDnDInst()->adjustScreenCoordinates(aScreenId, &aX, &aY);
312 if (RT_SUCCESS(rc))
313 {
314 GuestDnDMsg Msg;
315 Msg.setType(HOST_DND_HG_EVT_ENTER);
316 if (mDataBase.m_uProtocolVersion >= 3)
317 Msg.setNextUInt32(0); /** @todo ContextID not used yet. */
318 Msg.setNextUInt32(aScreenId);
319 Msg.setNextUInt32(aX);
320 Msg.setNextUInt32(aY);
321 Msg.setNextUInt32(uDefAction);
322 Msg.setNextUInt32(uAllowedActions);
323 Msg.setNextPointer((void *)strFormats.c_str(), cbFormats);
324 Msg.setNextUInt32(cbFormats);
325
326 rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
327 if (RT_SUCCESS(rc))
328 {
329 GuestDnDResponse *pResp = GuestDnDInst()->response();
330 if (pResp && RT_SUCCESS(pResp->waitForGuestResponse()))
331 resAction = GuestDnD::toMainAction(pResp->defAction());
332 }
333 }
334
335 if (RT_FAILURE(rc))
336 hr = VBOX_E_IPRT_ERROR;
337
338 if (SUCCEEDED(hr))
339 {
340 if (aResultAction)
341 *aResultAction = resAction;
342 }
343
344 LogFlowFunc(("hr=%Rhrc, resAction=%ld\n", hr, resAction));
345 return hr;
346#endif /* VBOX_WITH_DRAG_AND_DROP */
347}
348
349HRESULT GuestDnDTarget::move(ULONG aScreenId, ULONG aX, ULONG aY,
350 DnDAction_T aDefaultAction,
351 const std::vector<DnDAction_T> &aAllowedActions,
352 const GuestDnDMIMEList &aFormats,
353 DnDAction_T *aResultAction)
354{
355#if !defined(VBOX_WITH_DRAG_AND_DROP)
356 ReturnComNotImplemented();
357#else /* VBOX_WITH_DRAG_AND_DROP */
358
359 /* Input validation. */
360
361 AutoCaller autoCaller(this);
362 if (FAILED(autoCaller.rc())) return autoCaller.rc();
363
364 /* Default action is ignoring. */
365 DnDAction_T resAction = DnDAction_Ignore;
366
367 /* Check & convert the drag & drop actions. */
368 uint32_t uDefAction = 0;
369 uint32_t uAllowedActions = 0;
370 GuestDnD::toHGCMActions(aDefaultAction, &uDefAction,
371 aAllowedActions, &uAllowedActions);
372 /* If there is no usable action, ignore this request. */
373 if (isDnDIgnoreAction(uDefAction))
374 return S_OK;
375
376 /*
377 * Make a flat data string out of the supported format list.
378 * In the GuestDnDTarget case the source formats are from the host,
379 * as GuestDnDTarget acts as a source for the guest.
380 */
381 Utf8Str strFormats = GuestDnD::toFormatString(GuestDnD::toFilteredFormatList(m_lstFmtSupported, aFormats));
382 if (strFormats.isEmpty())
383 return setError(E_INVALIDARG, tr("No or not supported format(s) specified"));
384 const uint32_t cbFormats = (uint32_t)strFormats.length() + 1; /* Include terminating zero. */
385
386 HRESULT hr = S_OK;
387
388 int rc = GuestDnDInst()->adjustScreenCoordinates(aScreenId, &aX, &aY);
389 if (RT_SUCCESS(rc))
390 {
391 GuestDnDMsg Msg;
392 Msg.setType(HOST_DND_HG_EVT_MOVE);
393 if (mDataBase.m_uProtocolVersion >= 3)
394 Msg.setNextUInt32(0); /** @todo ContextID not used yet. */
395 Msg.setNextUInt32(aScreenId);
396 Msg.setNextUInt32(aX);
397 Msg.setNextUInt32(aY);
398 Msg.setNextUInt32(uDefAction);
399 Msg.setNextUInt32(uAllowedActions);
400 Msg.setNextPointer((void *)strFormats.c_str(), cbFormats);
401 Msg.setNextUInt32(cbFormats);
402
403 rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
404 if (RT_SUCCESS(rc))
405 {
406 GuestDnDResponse *pResp = GuestDnDInst()->response();
407 if (pResp && RT_SUCCESS(pResp->waitForGuestResponse()))
408 resAction = GuestDnD::toMainAction(pResp->defAction());
409 }
410 }
411
412 if (RT_FAILURE(rc))
413 hr = VBOX_E_IPRT_ERROR;
414
415 if (SUCCEEDED(hr))
416 {
417 if (aResultAction)
418 *aResultAction = resAction;
419 }
420
421 LogFlowFunc(("hr=%Rhrc, *pResultAction=%ld\n", hr, resAction));
422 return hr;
423#endif /* VBOX_WITH_DRAG_AND_DROP */
424}
425
426HRESULT GuestDnDTarget::leave(ULONG uScreenId)
427{
428#if !defined(VBOX_WITH_DRAG_AND_DROP)
429 ReturnComNotImplemented();
430#else /* VBOX_WITH_DRAG_AND_DROP */
431
432 AutoCaller autoCaller(this);
433 if (FAILED(autoCaller.rc())) return autoCaller.rc();
434
435 HRESULT hr = S_OK;
436 int rc = GuestDnDInst()->hostCall(HOST_DND_HG_EVT_LEAVE,
437 0 /* cParms */, NULL /* paParms */);
438 if (RT_SUCCESS(rc))
439 {
440 GuestDnDResponse *pResp = GuestDnDInst()->response();
441 if (pResp)
442 pResp->waitForGuestResponse();
443 }
444
445 if (RT_FAILURE(rc))
446 hr = VBOX_E_IPRT_ERROR;
447
448 LogFlowFunc(("hr=%Rhrc\n", hr));
449 return hr;
450#endif /* VBOX_WITH_DRAG_AND_DROP */
451}
452
453HRESULT GuestDnDTarget::drop(ULONG aScreenId, ULONG aX, ULONG aY,
454 DnDAction_T aDefaultAction,
455 const std::vector<DnDAction_T> &aAllowedActions,
456 const GuestDnDMIMEList &aFormats,
457 com::Utf8Str &aFormat,
458 DnDAction_T *aResultAction)
459{
460#if !defined(VBOX_WITH_DRAG_AND_DROP)
461 ReturnComNotImplemented();
462#else /* VBOX_WITH_DRAG_AND_DROP */
463
464 if (aDefaultAction == DnDAction_Ignore)
465 return setError(E_INVALIDARG, tr("Invalid default action specified"));
466 if (!aAllowedActions.size())
467 return setError(E_INVALIDARG, tr("Invalid allowed actions specified"));
468 if (!aFormats.size())
469 return setError(E_INVALIDARG, tr("No drop format(s) specified"));
470 /* aResultAction is optional. */
471
472 AutoCaller autoCaller(this);
473 if (FAILED(autoCaller.rc())) return autoCaller.rc();
474
475 /* Default action is ignoring. */
476 DnDAction_T resAction = DnDAction_Ignore;
477
478 /* Check & convert the drag & drop actions to HGCM codes. */
479 uint32_t uDefAction = DND_IGNORE_ACTION;
480 uint32_t uAllowedActions = 0;
481 GuestDnD::toHGCMActions(aDefaultAction, &uDefAction,
482 aAllowedActions, &uAllowedActions);
483 /* If there is no usable action, ignore this request. */
484 if (isDnDIgnoreAction(uDefAction))
485 {
486 aFormat = "";
487 if (aResultAction)
488 *aResultAction = DnDAction_Ignore;
489 return S_OK;
490 }
491
492 /*
493 * Make a flat data string out of the supported format list.
494 * In the GuestDnDTarget case the source formats are from the host,
495 * as GuestDnDTarget acts as a source for the guest.
496 */
497 Utf8Str strFormats = GuestDnD::toFormatString(GuestDnD::toFilteredFormatList(m_lstFmtSupported, aFormats));
498 if (strFormats.isEmpty())
499 return setError(E_INVALIDARG, tr("No or not supported format(s) specified"));
500 const uint32_t cbFormats = (uint32_t)strFormats.length() + 1; /* Include terminating zero. */
501
502 /* Adjust the coordinates in a multi-monitor setup. */
503 HRESULT hr = GuestDnDInst()->adjustScreenCoordinates(aScreenId, &aX, &aY);
504 if (SUCCEEDED(hr))
505 {
506 GuestDnDMsg Msg;
507 Msg.setType(HOST_DND_HG_EVT_DROPPED);
508 if (mDataBase.m_uProtocolVersion >= 3)
509 Msg.setNextUInt32(0); /** @todo ContextID not used yet. */
510 Msg.setNextUInt32(aScreenId);
511 Msg.setNextUInt32(aX);
512 Msg.setNextUInt32(aY);
513 Msg.setNextUInt32(uDefAction);
514 Msg.setNextUInt32(uAllowedActions);
515 Msg.setNextPointer((void*)strFormats.c_str(), cbFormats);
516 Msg.setNextUInt32(cbFormats);
517
518 int rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
519 if (RT_SUCCESS(rc))
520 {
521 GuestDnDResponse *pResp = GuestDnDInst()->response();
522 AssertPtr(pResp);
523
524 rc = pResp->waitForGuestResponse();
525 if (RT_SUCCESS(rc))
526 {
527 resAction = GuestDnD::toMainAction(pResp->defAction());
528
529 GuestDnDMIMEList lstFormats = pResp->formats();
530 if (lstFormats.size() == 1) /* Exactly one format to use specified? */
531 {
532 aFormat = lstFormats.at(0);
533 LogFlowFunc(("resFormat=%s, resAction=%RU32\n", aFormat.c_str(), pResp->defAction()));
534 }
535 else
536 hr = setError(VBOX_E_IPRT_ERROR, tr("Guest returned invalid drop formats (%zu formats)"), lstFormats.size());
537 }
538 else
539 hr = setError(VBOX_E_IPRT_ERROR, tr("Waiting for response of dropped event failed (%Rrc)"), rc);
540 }
541 else
542 hr = setError(VBOX_E_IPRT_ERROR, tr("Sending dropped event to guest failed (%Rrc)"), rc);
543 }
544 else
545 hr = setError(hr, tr("Retrieving drop coordinates failed"));
546
547 if (SUCCEEDED(hr))
548 {
549 if (aResultAction)
550 *aResultAction = resAction;
551 }
552
553 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
554 return hr;
555#endif /* VBOX_WITH_DRAG_AND_DROP */
556}
557
558/* static */
559DECLCALLBACK(int) GuestDnDTarget::i_sendDataThread(RTTHREAD Thread, void *pvUser)
560{
561 LogFlowFunc(("pvUser=%p\n", pvUser));
562
563 SendDataTask *pTask = (SendDataTask *)pvUser;
564 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
565
566 const ComObjPtr<GuestDnDTarget> pThis(pTask->getTarget());
567 Assert(!pThis.isNull());
568
569 AutoCaller autoCaller(pThis);
570 if (FAILED(autoCaller.rc())) return VERR_COM_INVALID_OBJECT_STATE;
571
572 int rc = RTThreadUserSignal(Thread);
573 AssertRC(rc);
574
575 rc = pThis->i_sendData(pTask->getCtx(), RT_INDEFINITE_WAIT /* msTimeout */);
576
577 AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
578
579 Assert(pThis->mDataBase.m_cTransfersPending);
580 pThis->mDataBase.m_cTransfersPending--;
581
582 LogFlowFunc(("pTarget=%p returning rc=%Rrc\n", (GuestDnDTarget *)pThis, rc));
583 return rc;
584}
585
586/**
587 * Initiates a data transfer from the host to the guest. The source is the host whereas the target is the
588 * guest in this case.
589 *
590 * @return HRESULT
591 * @param aScreenId
592 * @param aFormat
593 * @param aData
594 * @param aProgress
595 */
596HRESULT GuestDnDTarget::sendData(ULONG aScreenId, const com::Utf8Str &aFormat, const std::vector<BYTE> &aData,
597 ComPtr<IProgress> &aProgress)
598{
599#if !defined(VBOX_WITH_DRAG_AND_DROP)
600 ReturnComNotImplemented();
601#else /* VBOX_WITH_DRAG_AND_DROP */
602
603 AutoCaller autoCaller(this);
604 if (FAILED(autoCaller.rc())) return autoCaller.rc();
605
606 /* Input validation. */
607 if (RT_UNLIKELY((aFormat.c_str()) == NULL || *(aFormat.c_str()) == '\0'))
608 return setError(E_INVALIDARG, tr("No data format specified"));
609 if (RT_UNLIKELY(!aData.size()))
610 return setError(E_INVALIDARG, tr("No data to send specified"));
611
612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
613
614 /* At the moment we only support one transfer at a time. */
615 if (mDataBase.m_cTransfersPending)
616 return setError(E_INVALIDARG, tr("Another drop operation already is in progress"));
617
618 /* Dito. */
619 GuestDnDResponse *pResp = GuestDnDInst()->response();
620 AssertPtr(pResp);
621
622 HRESULT hr = pResp->resetProgress(m_pGuest);
623 if (FAILED(hr))
624 return hr;
625
626 SendDataTask *pTask = NULL;
627 PSENDDATACTX pSendCtx = NULL;
628 RTTHREAD threadSnd;
629 int rc = S_OK;
630
631 try
632 {
633 //pSendCtx is passed into SendDataTask where one is deleted in destructor
634 pSendCtx = new SENDDATACTX;
635 RT_BZERO(pSendCtx, sizeof(SENDDATACTX));
636
637 pSendCtx->mpTarget = this;
638 pSendCtx->mpResp = pResp;
639 pSendCtx->mScreenID = aScreenId;
640 pSendCtx->mFmtReq = aFormat;
641 pSendCtx->mData.getMeta().add(aData);
642
643 /* pTask is responsible for deletion of pSendCtx after creating */
644 pTask = new SendDataTask(this, pSendCtx);
645 if (!pTask->isOk())
646 {
647 delete pTask;
648 LogRel2(("DnD: Could not create SendDataTask object \n"));
649 throw hr = E_FAIL;
650 }
651
652 //this function delete pTask in case of exceptions, so there is no need in the call of delete operator
653 //pSendCtx is deleted in the pTask destructor
654 hr = pTask->createThread(&threadSnd);
655
656 }
657 catch(std::bad_alloc &)
658 {
659 hr = setError(E_OUTOFMEMORY);
660 }
661 catch(...)
662 {
663 LogRel2(("DnD: Could not create thread for SendDataTask \n"));
664 hr = E_FAIL;
665 }
666
667 if (SUCCEEDED(hr))
668 {
669 rc = RTThreadUserWait(threadSnd, 30 * 1000 /* 30s timeout */);
670 if (RT_SUCCESS(rc))
671 {
672 mDataBase.m_cTransfersPending++;
673
674 hr = pResp->queryProgressTo(aProgress.asOutParam());
675 ComAssertComRC(hr);
676
677 /* Note: pTask is now owned by the worker thread. */
678 }
679 else
680 hr = setError(VBOX_E_IPRT_ERROR, tr("Waiting for sending thread failed (%Rrc)"), rc);
681 }
682 else
683 hr = setError(VBOX_E_IPRT_ERROR, tr("Starting thread for GuestDnDTarget::i_sendDataThread (%Rrc)"), rc);
684
685 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
686 return hr;
687#endif /* VBOX_WITH_DRAG_AND_DROP */
688}
689
690int GuestDnDTarget::i_cancelOperation(void)
691{
692 /** @todo Check for pending cancel requests. */
693
694#if 0 /** @todo Later. */
695 /* Cancel any outstanding waits for guest responses first. */
696 if (pResp)
697 pResp->notifyAboutGuestResponse();
698#endif
699
700 LogFlowFunc(("Cancelling operation, telling guest ...\n"));
701 return GuestDnDInst()->hostCall(HOST_DND_HG_EVT_CANCEL, 0 /* cParms */, NULL /*paParms*/);
702}
703
704/* static */
705Utf8Str GuestDnDTarget::i_guestErrorToString(int guestRc)
706{
707 Utf8Str strError;
708
709 switch (guestRc)
710 {
711 case VERR_ACCESS_DENIED:
712 strError += Utf8StrFmt(tr("For one or more guest files or directories selected for transferring to the host your guest "
713 "user does not have the appropriate access rights for. Please make sure that all selected "
714 "elements can be accessed and that your guest user has the appropriate rights"));
715 break;
716
717 case VERR_NOT_FOUND:
718 /* Should not happen due to file locking on the guest, but anyway ... */
719 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were not"
720 "found on the guest anymore. This can be the case if the guest files were moved and/or"
721 "altered while the drag and drop operation was in progress"));
722 break;
723
724 case VERR_SHARING_VIOLATION:
725 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were locked. "
726 "Please make sure that all selected elements can be accessed and that your guest user has "
727 "the appropriate rights"));
728 break;
729
730 case VERR_TIMEOUT:
731 strError += Utf8StrFmt(tr("The guest was not able to process the drag and drop data within time"));
732 break;
733
734 default:
735 strError += Utf8StrFmt(tr("Drag and drop error from guest (%Rrc)"), guestRc);
736 break;
737 }
738
739 return strError;
740}
741
742/* static */
743Utf8Str GuestDnDTarget::i_hostErrorToString(int hostRc)
744{
745 Utf8Str strError;
746
747 switch (hostRc)
748 {
749 case VERR_ACCESS_DENIED:
750 strError += Utf8StrFmt(tr("For one or more host files or directories selected for transferring to the guest your host "
751 "user does not have the appropriate access rights for. Please make sure that all selected "
752 "elements can be accessed and that your host user has the appropriate rights."));
753 break;
754
755 case VERR_NOT_FOUND:
756 /* Should not happen due to file locking on the host, but anyway ... */
757 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the host were not"
758 "found on the host anymore. This can be the case if the host files were moved and/or"
759 "altered while the drag and drop operation was in progress."));
760 break;
761
762 case VERR_SHARING_VIOLATION:
763 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the guest were locked. "
764 "Please make sure that all selected elements can be accessed and that your host user has "
765 "the appropriate rights."));
766 break;
767
768 default:
769 strError += Utf8StrFmt(tr("Drag and drop error from host (%Rrc)"), hostRc);
770 break;
771 }
772
773 return strError;
774}
775
776int GuestDnDTarget::i_sendData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
777{
778 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
779
780 int rc;
781
782 ASMAtomicWriteBool(&pCtx->mIsActive, true);
783
784 /* Clear all remaining outgoing messages. */
785 mDataBase.m_lstMsgOut.clear();
786
787 /**
788 * Do we need to build up a file tree?
789 * Note: The decision whether we need to build up a file tree and sending
790 * actual file data only depends on the actual formats offered by this target.
791 * If the guest does not want an URI list ("text/uri-list") but text ("TEXT" and
792 * friends) instead, still send the data over to the guest -- the file as such still
793 * is needed on the guest in this case, as the guest then just wants a simple path
794 * instead of an URI list (pointing to a file on the guest itself).
795 *
796 ** @todo Support more than one format; add a format<->function handler concept. Later. */
797 bool fHasURIList = std::find(m_lstFmtOffered.begin(),
798 m_lstFmtOffered.end(), "text/uri-list") != m_lstFmtOffered.end();
799 if (fHasURIList)
800 {
801 rc = i_sendURIData(pCtx, msTimeout);
802 }
803 else
804 {
805 rc = i_sendRawData(pCtx, msTimeout);
806 }
807
808 ASMAtomicWriteBool(&pCtx->mIsActive, false);
809
810 LogFlowFuncLeaveRC(rc);
811 return rc;
812}
813
814int GuestDnDTarget::i_sendDataBody(PSENDDATACTX pCtx, GuestDnDData *pData)
815{
816 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
817 AssertPtrReturn(pData, VERR_INVALID_POINTER);
818
819 /** @todo Add support for multiple HOST_DND_HG_SND_DATA messages in case of more than 64K data! */
820 if (pData->getMeta().getSize() > _64K)
821 return VERR_NOT_IMPLEMENTED;
822
823 GuestDnDMsg Msg;
824
825 LogFlowFunc(("cbFmt=%RU32, cbMeta=%RU32, cbChksum=%RU32\n",
826 pData->getFmtSize(), pData->getMeta().getSize(), pData->getChkSumSize()));
827
828 Msg.setType(HOST_DND_HG_SND_DATA);
829 if (mDataBase.m_uProtocolVersion < 3)
830 {
831 Msg.setNextUInt32(pCtx->mScreenID); /* uScreenId */
832 Msg.setNextPointer(pData->getFmtMutable(), pData->getFmtSize()); /* pvFormat */
833 Msg.setNextUInt32(pData->getFmtSize()); /* cbFormat */
834 Msg.setNextPointer(pData->getMeta().getDataMutable(), pData->getMeta().getSize()); /* pvData */
835 /* Fill in the current data block size to send.
836 * Note: Only supports uint32_t. */
837 Msg.setNextUInt32((uint32_t)pData->getMeta().getSize()); /* cbData */
838 }
839 else
840 {
841 Msg.setNextUInt32(0); /** @todo ContextID not used yet. */
842 Msg.setNextPointer(pData->getMeta().getDataMutable(), pData->getMeta().getSize()); /* pvData */
843 Msg.setNextUInt32(pData->getMeta().getSize()); /* cbData */
844 Msg.setNextPointer(pData->getChkSumMutable(), pData->getChkSumSize()); /** @todo pvChecksum; not used yet. */
845 Msg.setNextUInt32(pData->getChkSumSize()); /** @todo cbChecksum; not used yet. */
846 }
847
848 int rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
849 if (RT_SUCCESS(rc))
850 rc = updateProgress(pData, pCtx->mpResp, pData->getMeta().getSize());
851
852 LogFlowFuncLeaveRC(rc);
853 return rc;
854}
855
856int GuestDnDTarget::i_sendDataHeader(PSENDDATACTX pCtx, GuestDnDData *pData, GuestDnDURIData *pURIData /* = NULL */)
857{
858 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
859 AssertPtrReturn(pData, VERR_INVALID_POINTER);
860 /* pURIData is optional. */
861
862 GuestDnDMsg Msg;
863
864 Msg.setType(HOST_DND_HG_SND_DATA_HDR);
865
866 Msg.setNextUInt32(0); /** @todo uContext; not used yet. */
867 Msg.setNextUInt32(0); /** @todo uFlags; not used yet. */
868 Msg.setNextUInt32(pCtx->mScreenID); /* uScreen */
869 Msg.setNextUInt64(pData->getTotal()); /* cbTotal */
870 Msg.setNextUInt32(pData->getMeta().getSize()); /* cbMeta*/
871 Msg.setNextPointer(pData->getFmtMutable(), pData->getFmtSize()); /* pvMetaFmt */
872 Msg.setNextUInt32(pData->getFmtSize()); /* cbMetaFmt */
873 Msg.setNextUInt64(pURIData ? pURIData->getObjToProcess() : 0); /* cObjects */
874 Msg.setNextUInt32(0); /** @todo enmCompression; not used yet. */
875 Msg.setNextUInt32(0); /** @todo enmChecksumType; not used yet. */
876 Msg.setNextPointer(NULL, 0); /** @todo pvChecksum; not used yet. */
877 Msg.setNextUInt32(0); /** @todo cbChecksum; not used yet. */
878
879 int rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
880
881 LogFlowFuncLeaveRC(rc);
882 return rc;
883}
884
885int GuestDnDTarget::i_sendDirectory(PSENDDATACTX pCtx, GuestDnDURIObjCtx *pObjCtx, GuestDnDMsg *pMsg)
886{
887 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
888 AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
889 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
890
891 DnDURIObject *pObj = pObjCtx->getObj();
892 AssertPtr(pObj);
893
894 RTCString strPath = pObj->GetDestPath();
895 if (strPath.isEmpty())
896 return VERR_INVALID_PARAMETER;
897 if (strPath.length() >= RTPATH_MAX) /* Note: Maximum is RTPATH_MAX on guest side. */
898 return VERR_BUFFER_OVERFLOW;
899
900 LogRel2(("DnD: Transferring host directory to guest: %s\n", strPath.c_str()));
901
902 pMsg->setType(HOST_DND_HG_SND_DIR);
903 if (mDataBase.m_uProtocolVersion >= 3)
904 pMsg->setNextUInt32(0); /** @todo ContextID not used yet. */
905 pMsg->setNextString(strPath.c_str()); /* path */
906 pMsg->setNextUInt32((uint32_t)(strPath.length() + 1)); /* path length (maximum is RTPATH_MAX on guest side). */
907 pMsg->setNextUInt32(pObj->GetMode()); /* mode */
908
909 return VINF_SUCCESS;
910}
911
912int GuestDnDTarget::i_sendFile(PSENDDATACTX pCtx, GuestDnDURIObjCtx *pObjCtx, GuestDnDMsg *pMsg)
913{
914 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
915 AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
916 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
917
918 DnDURIObject *pObj = pObjCtx->getObj();
919 AssertPtr(pObj);
920
921 RTCString strPathSrc = pObj->GetSourcePath();
922 if (strPathSrc.isEmpty())
923 return VERR_INVALID_PARAMETER;
924
925 int rc = VINF_SUCCESS;
926
927 LogFlowFunc(("Sending file with %RU32 bytes buffer, using protocol v%RU32 ...\n",
928 mData.mcbBlockSize, mDataBase.m_uProtocolVersion));
929 LogFlowFunc(("strPathSrc=%s, fIsOpen=%RTbool, cbSize=%RU64\n", strPathSrc.c_str(), pObj->IsOpen(), pObj->GetSize()));
930
931 if (!pObj->IsOpen())
932 {
933 LogRel2(("DnD: Opening host file for transferring to guest: %s\n", strPathSrc.c_str()));
934 rc = pObj->OpenEx(strPathSrc, DnDURIObject::File, DnDURIObject::Source,
935 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, 0 /* fFlags */);
936 if (RT_FAILURE(rc))
937 LogRel(("DnD: Error opening host file '%s', rc=%Rrc\n", strPathSrc.c_str(), rc));
938 }
939
940 bool fSendData = false;
941 if (RT_SUCCESS(rc))
942 {
943 if (mDataBase.m_uProtocolVersion >= 2)
944 {
945 uint32_t fState = pObjCtx->getState();
946 if (!(fState & DND_OBJCTX_STATE_HAS_HDR))
947 {
948 /*
949 * Since protocol v2 the file header and the actual file contents are
950 * separate messages, so send the file header first.
951 * The just registered callback will be called by the guest afterwards.
952 */
953 pMsg->setType(HOST_DND_HG_SND_FILE_HDR);
954 pMsg->setNextUInt32(0); /** @todo ContextID not used yet. */
955 pMsg->setNextString(pObj->GetDestPath().c_str()); /* pvName */
956 pMsg->setNextUInt32((uint32_t)(pObj->GetDestPath().length() + 1)); /* cbName */
957 pMsg->setNextUInt32(0); /* uFlags */
958 pMsg->setNextUInt32(pObj->GetMode()); /* fMode */
959 pMsg->setNextUInt64(pObj->GetSize()); /* uSize */
960
961 LogFlowFunc(("Sending file header ...\n"));
962 LogRel2(("DnD: Transferring host file to guest: %s (%RU64 bytes, mode 0x%x)\n",
963 strPathSrc.c_str(), pObj->GetSize(), pObj->GetMode()));
964
965 /** @todo Set progress object title to current file being transferred? */
966
967 pObjCtx->setState(fState | DND_OBJCTX_STATE_HAS_HDR);
968 }
969 else
970 {
971 /* File header was sent, so only send the actual file data. */
972 fSendData = true;
973 }
974 }
975 else /* Protocol v1. */
976 {
977 /* Always send the file data, every time. */
978 fSendData = true;
979 }
980 }
981
982 if ( RT_SUCCESS(rc)
983 && fSendData)
984 {
985 rc = i_sendFileData(pCtx, pObjCtx, pMsg);
986 }
987
988 LogFlowFuncLeaveRC(rc);
989 return rc;
990}
991
992int GuestDnDTarget::i_sendFileData(PSENDDATACTX pCtx, GuestDnDURIObjCtx *pObjCtx, GuestDnDMsg *pMsg)
993{
994 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
995 AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
996 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
997
998 DnDURIObject *pObj = pObjCtx->getObj();
999 AssertPtr(pObj);
1000
1001 GuestDnDResponse *pResp = pCtx->mpResp;
1002 AssertPtr(pResp);
1003
1004 /** @todo Don't allow concurrent reads per context! */
1005
1006 /*
1007 * Start sending stuff.
1008 */
1009
1010 /* Set the message type. */
1011 pMsg->setType(HOST_DND_HG_SND_FILE_DATA);
1012
1013 /* Protocol version 1 sends the file path *every* time with a new file chunk.
1014 * In protocol version 2 we only do this once with HOST_DND_HG_SND_FILE_HDR. */
1015 if (mDataBase.m_uProtocolVersion <= 1)
1016 {
1017 pMsg->setNextString(pObj->GetDestPath().c_str()); /* pvName */
1018 pMsg->setNextUInt32((uint32_t)(pObj->GetDestPath().length() + 1)); /* cbName */
1019 }
1020 else if (mDataBase.m_uProtocolVersion >= 2)
1021 {
1022 pMsg->setNextUInt32(0); /** @todo ContextID not used yet. */
1023 }
1024
1025 uint32_t cbRead = 0;
1026
1027 int rc = pObj->Read(pCtx->mURI.getBufferMutable(), pCtx->mURI.getBufferSize(), &cbRead);
1028 if (RT_SUCCESS(rc))
1029 {
1030 pCtx->mData.addProcessed(cbRead);
1031 LogFlowFunc(("cbBufSize=%zu, cbRead=%RU32\n", pCtx->mURI.getBufferSize(), cbRead));
1032
1033 if (mDataBase.m_uProtocolVersion <= 1)
1034 {
1035 pMsg->setNextPointer(pCtx->mURI.getBufferMutable(), cbRead); /* pvData */
1036 pMsg->setNextUInt32(cbRead); /* cbData */
1037 pMsg->setNextUInt32(pObj->GetMode()); /* fMode */
1038 }
1039 else /* Protocol v2 and up. */
1040 {
1041 pMsg->setNextPointer(pCtx->mURI.getBufferMutable(), cbRead); /* pvData */
1042 pMsg->setNextUInt32(cbRead); /* cbData */
1043
1044 if (mDataBase.m_uProtocolVersion >= 3)
1045 {
1046 /** @todo Calculate checksum. */
1047 pMsg->setNextPointer(NULL, 0); /* pvChecksum */
1048 pMsg->setNextUInt32(0); /* cbChecksum */
1049 }
1050 }
1051
1052 if (pObj->IsComplete()) /* Done reading? */
1053 {
1054 LogRel2(("DnD: File transfer to guest complete: %s\n", pObj->GetSourcePath().c_str()));
1055 LogFlowFunc(("File '%s' complete\n", pObj->GetSourcePath().c_str()));
1056
1057 /* DnDURIObject::Read() returns VINF_EOF when finished reading the entire fire,
1058 * but we don't want this here -- so just override this with VINF_SUCCESS. */
1059 rc = VINF_SUCCESS;
1060 }
1061 }
1062
1063 LogFlowFuncLeaveRC(rc);
1064 return rc;
1065}
1066
1067/* static */
1068DECLCALLBACK(int) GuestDnDTarget::i_sendURIDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
1069{
1070 PSENDDATACTX pCtx = (PSENDDATACTX)pvUser;
1071 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1072
1073 GuestDnDTarget *pThis = pCtx->mpTarget;
1074 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1075
1076 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
1077
1078 int rc = VINF_SUCCESS;
1079 int rcGuest = VINF_SUCCESS; /* Contains error code from guest in case of VERR_GSTDND_GUEST_ERROR. */
1080 bool fNotify = false;
1081
1082 switch (uMsg)
1083 {
1084 case GUEST_DND_CONNECT:
1085 /* Nothing to do here (yet). */
1086 break;
1087
1088 case GUEST_DND_DISCONNECT:
1089 rc = VERR_CANCELLED;
1090 break;
1091
1092 case GUEST_DND_GET_NEXT_HOST_MSG:
1093 {
1094 PVBOXDNDCBHGGETNEXTHOSTMSG pCBData = reinterpret_cast<PVBOXDNDCBHGGETNEXTHOSTMSG>(pvParms);
1095 AssertPtr(pCBData);
1096 AssertReturn(sizeof(VBOXDNDCBHGGETNEXTHOSTMSG) == cbParms, VERR_INVALID_PARAMETER);
1097 AssertReturn(CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
1098
1099 try
1100 {
1101 GuestDnDMsg *pMsg = new GuestDnDMsg();
1102
1103 rc = pThis->i_sendURIDataLoop(pCtx, pMsg);
1104 if (rc == VINF_EOF) /* Transfer complete? */
1105 {
1106 LogFlowFunc(("Last URI item processed, bailing out\n"));
1107 }
1108 else if (RT_SUCCESS(rc))
1109 {
1110 rc = pThis->msgQueueAdd(pMsg);
1111 if (RT_SUCCESS(rc)) /* Return message type & required parameter count to the guest. */
1112 {
1113 LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG -> %RU32 (%RU32 params)\n", pMsg->getType(), pMsg->getCount()));
1114 pCBData->uMsg = pMsg->getType();
1115 pCBData->cParms = pMsg->getCount();
1116 }
1117 }
1118
1119 if ( RT_FAILURE(rc)
1120 || rc == VINF_EOF) /* Transfer complete? */
1121 {
1122 delete pMsg;
1123 pMsg = NULL;
1124 }
1125 }
1126 catch(std::bad_alloc & /*e*/)
1127 {
1128 rc = VERR_NO_MEMORY;
1129 }
1130 break;
1131 }
1132 case GUEST_DND_GH_EVT_ERROR:
1133 {
1134 PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<PVBOXDNDCBEVTERRORDATA>(pvParms);
1135 AssertPtr(pCBData);
1136 AssertReturn(sizeof(VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1137 AssertReturn(CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
1138
1139 pCtx->mpResp->reset();
1140
1141 if (RT_SUCCESS(pCBData->rc))
1142 {
1143 AssertMsgFailed(("Guest has sent an error event but did not specify an actual error code\n"));
1144 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
1145 }
1146
1147 rc = pCtx->mpResp->setProgress(100, DND_PROGRESS_ERROR, pCBData->rc,
1148 GuestDnDTarget::i_guestErrorToString(pCBData->rc));
1149 if (RT_SUCCESS(rc))
1150 {
1151 rc = VERR_GSTDND_GUEST_ERROR;
1152 rcGuest = pCBData->rc;
1153 }
1154 break;
1155 }
1156 case HOST_DND_HG_SND_DIR:
1157 case HOST_DND_HG_SND_FILE_HDR:
1158 case HOST_DND_HG_SND_FILE_DATA:
1159 {
1160 PVBOXDNDCBHGGETNEXTHOSTMSGDATA pCBData
1161 = reinterpret_cast<PVBOXDNDCBHGGETNEXTHOSTMSGDATA>(pvParms);
1162 AssertPtr(pCBData);
1163 AssertReturn(sizeof(VBOXDNDCBHGGETNEXTHOSTMSGDATA) == cbParms, VERR_INVALID_PARAMETER);
1164
1165 LogFlowFunc(("pCBData->uMsg=%RU32, paParms=%p, cParms=%RU32\n", pCBData->uMsg, pCBData->paParms, pCBData->cParms));
1166
1167 GuestDnDMsg *pMsg = pThis->msgQueueGetNext();
1168 if (pMsg)
1169 {
1170 /*
1171 * Sanity checks.
1172 */
1173 if ( pCBData->uMsg != uMsg
1174 || pCBData->paParms == NULL
1175 || pCBData->cParms != pMsg->getCount())
1176 {
1177 LogFlowFunc(("Current message does not match:\n"));
1178 LogFlowFunc(("\tCallback: uMsg=%RU32, cParms=%RU32, paParms=%p\n",
1179 pCBData->uMsg, pCBData->cParms, pCBData->paParms));
1180 LogFlowFunc(("\t Next: uMsg=%RU32, cParms=%RU32\n", pMsg->getType(), pMsg->getCount()));
1181
1182 /* Start over. */
1183 pThis->msgQueueClear();
1184
1185 rc = VERR_INVALID_PARAMETER;
1186 }
1187
1188 if (RT_SUCCESS(rc))
1189 {
1190 LogFlowFunc(("Returning uMsg=%RU32\n", uMsg));
1191 rc = HGCM::Message::copyParms(pCBData->paParms, pCBData->cParms, pMsg->getParms(), pMsg->getCount());
1192 if (RT_SUCCESS(rc))
1193 {
1194 pCBData->cParms = pMsg->getCount();
1195 pThis->msgQueueRemoveNext();
1196 }
1197 else
1198 LogFlowFunc(("Copying parameters failed with rc=%Rrc\n", rc));
1199 }
1200 }
1201 else
1202 rc = VERR_NO_DATA;
1203
1204 LogFlowFunc(("Processing next message ended with rc=%Rrc\n", rc));
1205 break;
1206 }
1207 default:
1208 rc = VERR_NOT_SUPPORTED;
1209 break;
1210 }
1211
1212 int rcToGuest = VINF_SUCCESS; /* Status which will be sent back to the guest. */
1213
1214 /*
1215 * Resolve errors.
1216 */
1217 switch (rc)
1218 {
1219 case VINF_SUCCESS:
1220 break;
1221
1222 case VINF_EOF:
1223 {
1224 LogRel2(("DnD: Transfer to guest complete\n"));
1225
1226 /* Complete operation on host side. */
1227 fNotify = true;
1228
1229 /* The guest expects VERR_NO_DATA if the transfer is complete. */
1230 rcToGuest = VERR_NO_DATA;
1231 break;
1232 }
1233
1234 case VERR_GSTDND_GUEST_ERROR:
1235 {
1236 LogRel(("DnD: Guest reported error %Rrc, aborting transfer to guest\n", rcGuest));
1237 break;
1238 }
1239
1240 case VERR_CANCELLED:
1241 {
1242 LogRel2(("DnD: Transfer to guest canceled\n"));
1243 rcToGuest = VERR_CANCELLED; /* Also cancel on guest side. */
1244 break;
1245 }
1246
1247 default:
1248 {
1249 LogRel(("DnD: Host error %Rrc occurred, aborting transfer to guest\n", rc));
1250 rcToGuest = VERR_CANCELLED; /* Also cancel on guest side. */
1251 break;
1252 }
1253 }
1254
1255 if (RT_FAILURE(rc))
1256 {
1257 /* Unregister this callback. */
1258 AssertPtr(pCtx->mpResp);
1259 int rc2 = pCtx->mpResp->setCallback(uMsg, NULL /* PFNGUESTDNDCALLBACK */);
1260 AssertRC(rc2);
1261
1262 /* Let the waiter(s) know. */
1263 fNotify = true;
1264 }
1265
1266 LogFlowFunc(("fNotify=%RTbool, rc=%Rrc, rcToGuest=%Rrc\n", fNotify, rc, rcToGuest));
1267
1268 if (fNotify)
1269 {
1270 int rc2 = pCtx->mCBEvent.Notify(rc); /** @todo Also pass guest error back? */
1271 AssertRC(rc2);
1272 }
1273
1274 LogFlowFuncLeaveRC(rc);
1275 return rcToGuest; /* Tell the guest. */
1276}
1277
1278int GuestDnDTarget::i_sendURIData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
1279{
1280 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1281 AssertPtr(pCtx->mpResp);
1282
1283#define REGISTER_CALLBACK(x) \
1284 do { \
1285 rc = pCtx->mpResp->setCallback(x, i_sendURIDataCallback, pCtx); \
1286 if (RT_FAILURE(rc)) \
1287 return rc; \
1288 } while (0)
1289
1290#define UNREGISTER_CALLBACK(x) \
1291 do { \
1292 int rc2 = pCtx->mpResp->setCallback(x, NULL); \
1293 AssertRC(rc2); \
1294 } while (0)
1295
1296 int rc = pCtx->mURI.init(mData.mcbBlockSize);
1297 if (RT_FAILURE(rc))
1298 return rc;
1299
1300 rc = pCtx->mCBEvent.Reset();
1301 if (RT_FAILURE(rc))
1302 return rc;
1303
1304 /*
1305 * Register callbacks.
1306 */
1307 /* Guest callbacks. */
1308 REGISTER_CALLBACK(GUEST_DND_CONNECT);
1309 REGISTER_CALLBACK(GUEST_DND_DISCONNECT);
1310 REGISTER_CALLBACK(GUEST_DND_GET_NEXT_HOST_MSG);
1311 REGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
1312 /* Host callbacks. */
1313 REGISTER_CALLBACK(HOST_DND_HG_SND_DIR);
1314 if (mDataBase.m_uProtocolVersion >= 2)
1315 REGISTER_CALLBACK(HOST_DND_HG_SND_FILE_HDR);
1316 REGISTER_CALLBACK(HOST_DND_HG_SND_FILE_DATA);
1317
1318 do
1319 {
1320 /*
1321 * Extract URI list from current meta data.
1322 */
1323 GuestDnDData *pData = &pCtx->mData;
1324 GuestDnDURIData *pURI = &pCtx->mURI;
1325
1326 rc = pURI->fromLocalMetaData(pData->getMeta());
1327 if (RT_FAILURE(rc))
1328 break;
1329
1330 LogFlowFunc(("URI root objects: %zu, total bytes (raw data to transfer): %zu\n",
1331 pURI->getURIList().RootCount(), pURI->getURIList().TotalBytes()));
1332
1333 /*
1334 * Set the new meta data with the URI list in it.
1335 */
1336 rc = pData->getMeta().fromURIList(pURI->getURIList());
1337 if (RT_FAILURE(rc))
1338 break;
1339
1340 /*
1341 * Set the estimated data sizes we are going to send.
1342 * The total size also contains the meta data size.
1343 */
1344 const uint32_t cbMeta = pData->getMeta().getSize();
1345 pData->setEstimatedSize(pURI->getURIList().TotalBytes() + cbMeta /* cbTotal */,
1346 cbMeta /* cbMeta */);
1347
1348 /*
1349 * Set the meta format.
1350 */
1351 void *pvFmt = (void *)pCtx->mFmtReq.c_str();
1352 uint32_t cbFmt = (uint32_t)pCtx->mFmtReq.length() + 1; /* Include terminating zero. */
1353
1354 pData->setFmt(pvFmt, cbFmt);
1355
1356 /*
1357 * The first message always is the data header. The meta data itself then follows
1358 * and *only* contains the root elements of an URI list.
1359 *
1360 * After the meta data we generate the messages required to send the
1361 * file/directory data itself.
1362 *
1363 * Note: Protocol < v3 use the first data message to tell what's being sent.
1364 */
1365 GuestDnDMsg Msg;
1366
1367 /*
1368 * Send the data header first.
1369 */
1370 if (mDataBase.m_uProtocolVersion >= 3)
1371 rc = i_sendDataHeader(pCtx, pData, &pCtx->mURI);
1372
1373 /*
1374 * Send the (meta) data body.
1375 */
1376 if (RT_SUCCESS(rc))
1377 rc = i_sendDataBody(pCtx, pData);
1378
1379 if (RT_SUCCESS(rc))
1380 {
1381 rc = waitForEvent(&pCtx->mCBEvent, pCtx->mpResp, msTimeout);
1382 if (RT_SUCCESS(rc))
1383 pCtx->mpResp->setProgress(100, DND_PROGRESS_COMPLETE, VINF_SUCCESS);
1384 }
1385
1386 } while (0);
1387
1388 /*
1389 * Unregister callbacks.
1390 */
1391 /* Guest callbacks. */
1392 UNREGISTER_CALLBACK(GUEST_DND_CONNECT);
1393 UNREGISTER_CALLBACK(GUEST_DND_DISCONNECT);
1394 UNREGISTER_CALLBACK(GUEST_DND_GET_NEXT_HOST_MSG);
1395 UNREGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
1396 /* Host callbacks. */
1397 UNREGISTER_CALLBACK(HOST_DND_HG_SND_DIR);
1398 if (mDataBase.m_uProtocolVersion >= 2)
1399 UNREGISTER_CALLBACK(HOST_DND_HG_SND_FILE_HDR);
1400 UNREGISTER_CALLBACK(HOST_DND_HG_SND_FILE_DATA);
1401
1402#undef REGISTER_CALLBACK
1403#undef UNREGISTER_CALLBACK
1404
1405 if (RT_FAILURE(rc))
1406 {
1407 if (rc == VERR_CANCELLED)
1408 pCtx->mpResp->setProgress(100, DND_PROGRESS_CANCELLED, VINF_SUCCESS);
1409 else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
1410 pCtx->mpResp->setProgress(100, DND_PROGRESS_ERROR, rc,
1411 GuestDnDTarget::i_hostErrorToString(rc));
1412 }
1413
1414 /*
1415 * Now that we've cleaned up tell the guest side to cancel.
1416 * This does not imply we're waiting for the guest to react, as the
1417 * host side never must depend on anything from the guest.
1418 */
1419 if (rc == VERR_CANCELLED)
1420 {
1421 int rc2 = sendCancel();
1422 AssertRC(rc2);
1423 }
1424
1425 LogFlowFuncLeaveRC(rc);
1426 return rc;
1427}
1428
1429int GuestDnDTarget::i_sendURIDataLoop(PSENDDATACTX pCtx, GuestDnDMsg *pMsg)
1430{
1431 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1432 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
1433
1434 int rc = updateProgress(&pCtx->mData, pCtx->mpResp);
1435 AssertRC(rc);
1436
1437 if ( pCtx->mData.isComplete()
1438 && pCtx->mURI.isComplete())
1439 {
1440 return VINF_EOF;
1441 }
1442
1443 GuestDnDURIObjCtx &objCtx = pCtx->mURI.getObjCurrent();
1444 if (!objCtx.isValid())
1445 return VERR_WRONG_ORDER;
1446
1447 DnDURIObject *pCurObj = objCtx.getObj();
1448 AssertPtr(pCurObj);
1449
1450 uint32_t fMode = pCurObj->GetMode();
1451 LogRel3(("DnD: Processing: srcPath=%s, dstPath=%s, fMode=0x%x, cbSize=%RU32, fIsDir=%RTbool, fIsFile=%RTbool\n",
1452 pCurObj->GetSourcePath().c_str(), pCurObj->GetDestPath().c_str(),
1453 fMode, pCurObj->GetSize(),
1454 RTFS_IS_DIRECTORY(fMode), RTFS_IS_FILE(fMode)));
1455
1456 if (RTFS_IS_DIRECTORY(fMode))
1457 {
1458 rc = i_sendDirectory(pCtx, &objCtx, pMsg);
1459 }
1460 else if (RTFS_IS_FILE(fMode))
1461 {
1462 rc = i_sendFile(pCtx, &objCtx, pMsg);
1463 }
1464 else
1465 {
1466 AssertMsgFailed(("fMode=0x%x is not supported for srcPath=%s, dstPath=%s\n",
1467 fMode, pCurObj->GetSourcePath().c_str(), pCurObj->GetDestPath().c_str()));
1468 rc = VERR_NOT_SUPPORTED;
1469 }
1470
1471 bool fRemove = false; /* Remove current entry? */
1472 if ( pCurObj->IsComplete()
1473 || RT_FAILURE(rc))
1474 {
1475 fRemove = true;
1476 }
1477
1478 if (fRemove)
1479 {
1480 LogFlowFunc(("Removing \"%s\" from list, rc=%Rrc\n", pCurObj->GetSourcePath().c_str(), rc));
1481 pCtx->mURI.removeObjCurrent();
1482 }
1483
1484 LogFlowFuncLeaveRC(rc);
1485 return rc;
1486}
1487
1488int GuestDnDTarget::i_sendRawData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
1489{
1490 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1491 NOREF(msTimeout);
1492
1493 GuestDnD *pInst = GuestDnDInst();
1494 AssertPtr(pInst);
1495
1496 GuestDnDData *pData = &pCtx->mData;
1497
1498 /** @todo At the moment we only allow sending up to 64K raw data.
1499 * For protocol v1+v2: Fix this by using HOST_DND_HG_SND_MORE_DATA.
1500 * For protocol v3 : Send another HOST_DND_HG_SND_DATA message. */
1501 if (!pData->getMeta().getSize())
1502 return VINF_SUCCESS;
1503
1504 int rc = VINF_SUCCESS;
1505
1506 /*
1507 * Send the data header first.
1508 */
1509 if (mDataBase.m_uProtocolVersion >= 3)
1510 rc = i_sendDataHeader(pCtx, pData, NULL /* URI list */);
1511
1512 /*
1513 * Send the (meta) data body.
1514 */
1515 if (RT_SUCCESS(rc))
1516 rc = i_sendDataBody(pCtx, pData);
1517
1518 int rc2;
1519 if (RT_FAILURE(rc))
1520 rc2 = pCtx->mpResp->setProgress(100, DND_PROGRESS_ERROR, rc,
1521 GuestDnDTarget::i_hostErrorToString(rc));
1522 else
1523 rc2 = pCtx->mpResp->setProgress(100, DND_PROGRESS_COMPLETE, rc);
1524 AssertRC(rc2);
1525
1526 LogFlowFuncLeaveRC(rc);
1527 return rc;
1528}
1529
1530HRESULT GuestDnDTarget::cancel(BOOL *aVeto)
1531{
1532#if !defined(VBOX_WITH_DRAG_AND_DROP)
1533 ReturnComNotImplemented();
1534#else /* VBOX_WITH_DRAG_AND_DROP */
1535
1536 int rc = i_cancelOperation();
1537
1538 if (aVeto)
1539 *aVeto = FALSE; /** @todo */
1540
1541 HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
1542
1543 LogFlowFunc(("hr=%Rhrc\n", hr));
1544 return hr;
1545#endif /* VBOX_WITH_DRAG_AND_DROP */
1546}
1547
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