VirtualBox

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

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

DnD/Main: remove useless RTThreadUserSignal call

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.3 KB
Line 
1/* $Id: GuestDnDTargetImpl.cpp 64976 2016-12-21 12:47:45Z vboxsync $ */
2/** @file
3 * VBox Console COM Class implementation - Guest drag'n drop target.
4 */
5
6/*
7 * Copyright (C) 2014-2016 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 GuestDnDTarget::i_sendDataThreadTask(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 RT_NOREF(uScreenId);
429#if !defined(VBOX_WITH_DRAG_AND_DROP)
430 ReturnComNotImplemented();
431#else /* VBOX_WITH_DRAG_AND_DROP */
432
433 AutoCaller autoCaller(this);
434 if (FAILED(autoCaller.rc())) return autoCaller.rc();
435
436 HRESULT hr = S_OK;
437 int rc = GuestDnDInst()->hostCall(HOST_DND_HG_EVT_LEAVE,
438 0 /* cParms */, NULL /* paParms */);
439 if (RT_SUCCESS(rc))
440 {
441 GuestDnDResponse *pResp = GuestDnDInst()->response();
442 if (pResp)
443 pResp->waitForGuestResponse();
444 }
445
446 if (RT_FAILURE(rc))
447 hr = VBOX_E_IPRT_ERROR;
448
449 LogFlowFunc(("hr=%Rhrc\n", hr));
450 return hr;
451#endif /* VBOX_WITH_DRAG_AND_DROP */
452}
453
454HRESULT GuestDnDTarget::drop(ULONG aScreenId, ULONG aX, ULONG aY,
455 DnDAction_T aDefaultAction,
456 const std::vector<DnDAction_T> &aAllowedActions,
457 const GuestDnDMIMEList &aFormats,
458 com::Utf8Str &aFormat,
459 DnDAction_T *aResultAction)
460{
461#if !defined(VBOX_WITH_DRAG_AND_DROP)
462 ReturnComNotImplemented();
463#else /* VBOX_WITH_DRAG_AND_DROP */
464
465 if (aDefaultAction == DnDAction_Ignore)
466 return setError(E_INVALIDARG, tr("Invalid default action specified"));
467 if (!aAllowedActions.size())
468 return setError(E_INVALIDARG, tr("Invalid allowed actions specified"));
469 if (!aFormats.size())
470 return setError(E_INVALIDARG, tr("No drop format(s) specified"));
471 /* aResultAction is optional. */
472
473 AutoCaller autoCaller(this);
474 if (FAILED(autoCaller.rc())) return autoCaller.rc();
475
476 /* Default action is ignoring. */
477 DnDAction_T resAction = DnDAction_Ignore;
478
479 /* Check & convert the drag & drop actions to HGCM codes. */
480 uint32_t uDefAction = DND_IGNORE_ACTION;
481 uint32_t uAllowedActions = 0;
482 GuestDnD::toHGCMActions(aDefaultAction, &uDefAction,
483 aAllowedActions, &uAllowedActions);
484 /* If there is no usable action, ignore this request. */
485 if (isDnDIgnoreAction(uDefAction))
486 {
487 aFormat = "";
488 if (aResultAction)
489 *aResultAction = DnDAction_Ignore;
490 return S_OK;
491 }
492
493 /*
494 * Make a flat data string out of the supported format list.
495 * In the GuestDnDTarget case the source formats are from the host,
496 * as GuestDnDTarget acts as a source for the guest.
497 */
498 Utf8Str strFormats = GuestDnD::toFormatString(GuestDnD::toFilteredFormatList(m_lstFmtSupported, aFormats));
499 if (strFormats.isEmpty())
500 return setError(E_INVALIDARG, tr("No or not supported format(s) specified"));
501 const uint32_t cbFormats = (uint32_t)strFormats.length() + 1; /* Include terminating zero. */
502
503 /* Adjust the coordinates in a multi-monitor setup. */
504 HRESULT hr = GuestDnDInst()->adjustScreenCoordinates(aScreenId, &aX, &aY);
505 if (SUCCEEDED(hr))
506 {
507 GuestDnDMsg Msg;
508 Msg.setType(HOST_DND_HG_EVT_DROPPED);
509 if (mDataBase.m_uProtocolVersion >= 3)
510 Msg.setNextUInt32(0); /** @todo ContextID not used yet. */
511 Msg.setNextUInt32(aScreenId);
512 Msg.setNextUInt32(aX);
513 Msg.setNextUInt32(aY);
514 Msg.setNextUInt32(uDefAction);
515 Msg.setNextUInt32(uAllowedActions);
516 Msg.setNextPointer((void*)strFormats.c_str(), cbFormats);
517 Msg.setNextUInt32(cbFormats);
518
519 int rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
520 if (RT_SUCCESS(rc))
521 {
522 GuestDnDResponse *pResp = GuestDnDInst()->response();
523 AssertPtr(pResp);
524
525 rc = pResp->waitForGuestResponse();
526 if (RT_SUCCESS(rc))
527 {
528 resAction = GuestDnD::toMainAction(pResp->defAction());
529
530 GuestDnDMIMEList lstFormats = pResp->formats();
531 if (lstFormats.size() == 1) /* Exactly one format to use specified? */
532 {
533 aFormat = lstFormats.at(0);
534 LogFlowFunc(("resFormat=%s, resAction=%RU32\n", aFormat.c_str(), pResp->defAction()));
535 }
536 else
537 hr = setError(VBOX_E_IPRT_ERROR, tr("Guest returned invalid drop formats (%zu formats)"), lstFormats.size());
538 }
539 else
540 hr = setError(VBOX_E_IPRT_ERROR, tr("Waiting for response of dropped event failed (%Rrc)"), rc);
541 }
542 else
543 hr = setError(VBOX_E_IPRT_ERROR, tr("Sending dropped event to guest failed (%Rrc)"), rc);
544 }
545 else
546 hr = setError(hr, tr("Retrieving drop coordinates failed"));
547
548 if (SUCCEEDED(hr))
549 {
550 if (aResultAction)
551 *aResultAction = resAction;
552 }
553
554 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
555 return hr;
556#endif /* VBOX_WITH_DRAG_AND_DROP */
557}
558
559/* static */
560void GuestDnDTarget::i_sendDataThreadTask(SendDataTask *pTask)
561{
562 LogFlowFunc(("pTask=%p\n", pTask));
563
564 AssertPtrReturnVoid(pTask);
565
566 const ComObjPtr<GuestDnDTarget> pThis(pTask->getTarget());
567 Assert(!pThis.isNull());
568
569 AutoCaller autoCaller(pThis);
570 if (FAILED(autoCaller.rc()))
571 return;
572
573 vrc = pThis->i_sendData(pTask->getCtx(), RT_INDEFINITE_WAIT /* msTimeout */);
574/** @todo
575 *
576 * r=bird: What happens with @a vrc?
577 *
578 */
579
580 AutoWriteLock alock(pThis COMMA_LOCKVAL_SRC_POS);
581
582 Assert(pThis->mDataBase.m_cTransfersPending);
583 if (pThis->mDataBase.m_cTransfersPending)
584 pThis->mDataBase.m_cTransfersPending--;
585
586 LogFlowFunc(("pTarget=%p vrc=%Rrc (ignored)\n", (GuestDnDTarget *)pThis, vrc));
587}
588
589/**
590 * Initiates a data transfer from the host to the guest. The source is the host whereas the target is the
591 * guest in this case.
592 *
593 * @return HRESULT
594 * @param aScreenId
595 * @param aFormat
596 * @param aData
597 * @param aProgress
598 */
599HRESULT GuestDnDTarget::sendData(ULONG aScreenId, const com::Utf8Str &aFormat, const std::vector<BYTE> &aData,
600 ComPtr<IProgress> &aProgress)
601{
602#if !defined(VBOX_WITH_DRAG_AND_DROP)
603 ReturnComNotImplemented();
604#else /* VBOX_WITH_DRAG_AND_DROP */
605
606 AutoCaller autoCaller(this);
607 if (FAILED(autoCaller.rc())) return autoCaller.rc();
608
609 /* Input validation. */
610 if (RT_UNLIKELY((aFormat.c_str()) == NULL || *(aFormat.c_str()) == '\0'))
611 return setError(E_INVALIDARG, tr("No data format specified"));
612 if (RT_UNLIKELY(!aData.size()))
613 return setError(E_INVALIDARG, tr("No data to send specified"));
614
615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
616
617 /* At the moment we only support one transfer at a time. */
618 if (mDataBase.m_cTransfersPending)
619 return setError(E_INVALIDARG, tr("Another drop operation already is in progress"));
620
621 /* Ditto. */
622 GuestDnDResponse *pResp = GuestDnDInst()->response();
623 AssertPtr(pResp);
624
625 HRESULT hr = pResp->resetProgress(m_pGuest);
626 if (FAILED(hr))
627 return hr;
628
629 SendDataTask *pTask = NULL;
630 PSENDDATACTX pSendCtx = NULL;
631
632 try
633 {
634 //pSendCtx is passed into SendDataTask where one is deleted in destructor
635 pSendCtx = new SENDDATACTX;
636 RT_BZERO(pSendCtx, sizeof(SENDDATACTX));
637
638 pSendCtx->mpTarget = this;
639 pSendCtx->mpResp = pResp;
640 pSendCtx->mScreenID = aScreenId;
641 pSendCtx->mFmtReq = aFormat;
642 pSendCtx->mData.getMeta().add(aData);
643
644 /* pTask is responsible for deletion of pSendCtx after creating */
645 pTask = new SendDataTask(this, pSendCtx);
646 if (!pTask->isOk())
647 {
648 delete pTask;
649 LogRel2(("DnD: Could not create SendDataTask object \n"));
650 throw hr = E_FAIL;
651 }
652
653 /* This function delete pTask in case of exceptions,
654 * so there is no need in the call of delete operator. */
655 hr = pTask->createThreadWithType(RTTHREADTYPE_MAIN_WORKER);
656
657 }
658 catch (std::bad_alloc &)
659 {
660 hr = setError(E_OUTOFMEMORY);
661 }
662 catch (...)
663 {
664 LogRel2(("DnD: Could not create thread for data sending task\n"));
665 hr = E_FAIL;
666 }
667
668 if (SUCCEEDED(hr))
669 {
670 mDataBase.m_cTransfersPending++;
671
672 hr = pResp->queryProgressTo(aProgress.asOutParam());
673 ComAssertComRC(hr);
674
675 /* Note: pTask is now owned by the worker thread. */
676 }
677 else
678 hr = setError(VBOX_E_IPRT_ERROR, tr("Starting thread for GuestDnDTarget::i_sendDataThread (%Rhrc)"), hr);
679
680 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
681 return hr;
682#endif /* VBOX_WITH_DRAG_AND_DROP */
683}
684
685int GuestDnDTarget::i_cancelOperation(void)
686{
687 /** @todo Check for pending cancel requests. */
688
689#if 0 /** @todo Later. */
690 /* Cancel any outstanding waits for guest responses first. */
691 if (pResp)
692 pResp->notifyAboutGuestResponse();
693#endif
694
695 LogFlowFunc(("Cancelling operation, telling guest ...\n"));
696 return GuestDnDInst()->hostCall(HOST_DND_HG_EVT_CANCEL, 0 /* cParms */, NULL /*paParms*/);
697}
698
699/* static */
700Utf8Str GuestDnDTarget::i_guestErrorToString(int guestRc)
701{
702 Utf8Str strError;
703
704 switch (guestRc)
705 {
706 case VERR_ACCESS_DENIED:
707 strError += Utf8StrFmt(tr("For one or more guest files or directories selected for transferring to the host your guest "
708 "user does not have the appropriate access rights for. Please make sure that all selected "
709 "elements can be accessed and that your guest user has the appropriate rights"));
710 break;
711
712 case VERR_NOT_FOUND:
713 /* Should not happen due to file locking on the guest, but anyway ... */
714 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were not"
715 "found on the guest anymore. This can be the case if the guest files were moved and/or"
716 "altered while the drag and drop operation was in progress"));
717 break;
718
719 case VERR_SHARING_VIOLATION:
720 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were locked. "
721 "Please make sure that all selected elements can be accessed and that your guest user has "
722 "the appropriate rights"));
723 break;
724
725 case VERR_TIMEOUT:
726 strError += Utf8StrFmt(tr("The guest was not able to process the drag and drop data within time"));
727 break;
728
729 default:
730 strError += Utf8StrFmt(tr("Drag and drop error from guest (%Rrc)"), guestRc);
731 break;
732 }
733
734 return strError;
735}
736
737/* static */
738Utf8Str GuestDnDTarget::i_hostErrorToString(int hostRc)
739{
740 Utf8Str strError;
741
742 switch (hostRc)
743 {
744 case VERR_ACCESS_DENIED:
745 strError += Utf8StrFmt(tr("For one or more host files or directories selected for transferring to the guest your host "
746 "user does not have the appropriate access rights for. Please make sure that all selected "
747 "elements can be accessed and that your host user has the appropriate rights."));
748 break;
749
750 case VERR_NOT_FOUND:
751 /* Should not happen due to file locking on the host, but anyway ... */
752 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the host were not"
753 "found on the host anymore. This can be the case if the host files were moved and/or"
754 "altered while the drag and drop operation was in progress."));
755 break;
756
757 case VERR_SHARING_VIOLATION:
758 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the guest were locked. "
759 "Please make sure that all selected elements can be accessed and that your host user has "
760 "the appropriate rights."));
761 break;
762
763 default:
764 strError += Utf8StrFmt(tr("Drag and drop error from host (%Rrc)"), hostRc);
765 break;
766 }
767
768 return strError;
769}
770
771/**
772 * @returns VBox status code that the caller ignores. Not sure if that's
773 * intentional or not.
774 */
775int GuestDnDTarget::i_sendData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
776{
777 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
778
779 int rc;
780
781 ASMAtomicWriteBool(&pCtx->mIsActive, true);
782
783 /* Clear all remaining outgoing messages. */
784 mDataBase.m_lstMsgOut.clear();
785
786 /**
787 * Do we need to build up a file tree?
788 * Note: The decision whether we need to build up a file tree and sending
789 * actual file data only depends on the actual formats offered by this target.
790 * If the guest does not want an URI list ("text/uri-list") but text ("TEXT" and
791 * friends) instead, still send the data over to the guest -- the file as such still
792 * is needed on the guest in this case, as the guest then just wants a simple path
793 * instead of an URI list (pointing to a file on the guest itself).
794 *
795 ** @todo Support more than one format; add a format<->function handler concept. Later. */
796 bool fHasURIList = std::find(m_lstFmtOffered.begin(),
797 m_lstFmtOffered.end(), "text/uri-list") != m_lstFmtOffered.end();
798 if (fHasURIList)
799 {
800 rc = i_sendURIData(pCtx, msTimeout);
801 }
802 else
803 {
804 rc = i_sendRawData(pCtx, msTimeout);
805 }
806
807 ASMAtomicWriteBool(&pCtx->mIsActive, false);
808
809 LogFlowFuncLeaveRC(rc);
810 return rc;
811}
812
813int GuestDnDTarget::i_sendDataBody(PSENDDATACTX pCtx, GuestDnDData *pData)
814{
815 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
816 AssertPtrReturn(pData, VERR_INVALID_POINTER);
817
818 /** @todo Add support for multiple HOST_DND_HG_SND_DATA messages in case of more than 64K data! */
819 if (pData->getMeta().getSize() > _64K)
820 return VERR_NOT_IMPLEMENTED;
821
822 GuestDnDMsg Msg;
823
824 LogFlowFunc(("cbFmt=%RU32, cbMeta=%RU32, cbChksum=%RU32\n",
825 pData->getFmtSize(), pData->getMeta().getSize(), pData->getChkSumSize()));
826
827 Msg.setType(HOST_DND_HG_SND_DATA);
828 if (mDataBase.m_uProtocolVersion < 3)
829 {
830 Msg.setNextUInt32(pCtx->mScreenID); /* uScreenId */
831 Msg.setNextPointer(pData->getFmtMutable(), pData->getFmtSize()); /* pvFormat */
832 Msg.setNextUInt32(pData->getFmtSize()); /* cbFormat */
833 Msg.setNextPointer(pData->getMeta().getDataMutable(), pData->getMeta().getSize()); /* pvData */
834 /* Fill in the current data block size to send.
835 * Note: Only supports uint32_t. */
836 Msg.setNextUInt32((uint32_t)pData->getMeta().getSize()); /* cbData */
837 }
838 else
839 {
840 Msg.setNextUInt32(0); /** @todo ContextID not used yet. */
841 Msg.setNextPointer(pData->getMeta().getDataMutable(), pData->getMeta().getSize()); /* pvData */
842 Msg.setNextUInt32(pData->getMeta().getSize()); /* cbData */
843 Msg.setNextPointer(pData->getChkSumMutable(), pData->getChkSumSize()); /** @todo pvChecksum; not used yet. */
844 Msg.setNextUInt32(pData->getChkSumSize()); /** @todo cbChecksum; not used yet. */
845 }
846
847 int rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
848 if (RT_SUCCESS(rc))
849 rc = updateProgress(pData, pCtx->mpResp, pData->getMeta().getSize());
850
851 LogFlowFuncLeaveRC(rc);
852 return rc;
853}
854
855int GuestDnDTarget::i_sendDataHeader(PSENDDATACTX pCtx, GuestDnDData *pData, GuestDnDURIData *pURIData /* = NULL */)
856{
857 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
858 AssertPtrReturn(pData, VERR_INVALID_POINTER);
859 /* pURIData is optional. */
860
861 GuestDnDMsg Msg;
862
863 Msg.setType(HOST_DND_HG_SND_DATA_HDR);
864
865 Msg.setNextUInt32(0); /** @todo uContext; not used yet. */
866 Msg.setNextUInt32(0); /** @todo uFlags; not used yet. */
867 Msg.setNextUInt32(pCtx->mScreenID); /* uScreen */
868 Msg.setNextUInt64(pData->getTotal()); /* cbTotal */
869 Msg.setNextUInt32(pData->getMeta().getSize()); /* cbMeta*/
870 Msg.setNextPointer(pData->getFmtMutable(), pData->getFmtSize()); /* pvMetaFmt */
871 Msg.setNextUInt32(pData->getFmtSize()); /* cbMetaFmt */
872 Msg.setNextUInt64(pURIData ? pURIData->getObjToProcess() : 0); /* cObjects */
873 Msg.setNextUInt32(0); /** @todo enmCompression; not used yet. */
874 Msg.setNextUInt32(0); /** @todo enmChecksumType; not used yet. */
875 Msg.setNextPointer(NULL, 0); /** @todo pvChecksum; not used yet. */
876 Msg.setNextUInt32(0); /** @todo cbChecksum; not used yet. */
877
878 int rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
879
880 LogFlowFuncLeaveRC(rc);
881 return rc;
882}
883
884int GuestDnDTarget::i_sendDirectory(PSENDDATACTX pCtx, GuestDnDURIObjCtx *pObjCtx, GuestDnDMsg *pMsg)
885{
886 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
887 AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
888 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
889
890 DnDURIObject *pObj = pObjCtx->getObj();
891 AssertPtr(pObj);
892
893 RTCString strPath = pObj->GetDestPath();
894 if (strPath.isEmpty())
895 return VERR_INVALID_PARAMETER;
896 if (strPath.length() >= RTPATH_MAX) /* Note: Maximum is RTPATH_MAX on guest side. */
897 return VERR_BUFFER_OVERFLOW;
898
899 LogRel2(("DnD: Transferring host directory to guest: %s\n", strPath.c_str()));
900
901 pMsg->setType(HOST_DND_HG_SND_DIR);
902 if (mDataBase.m_uProtocolVersion >= 3)
903 pMsg->setNextUInt32(0); /** @todo ContextID not used yet. */
904 pMsg->setNextString(strPath.c_str()); /* path */
905 pMsg->setNextUInt32((uint32_t)(strPath.length() + 1)); /* path length (maximum is RTPATH_MAX on guest side). */
906 pMsg->setNextUInt32(pObj->GetMode()); /* mode */
907
908 return VINF_SUCCESS;
909}
910
911int GuestDnDTarget::i_sendFile(PSENDDATACTX pCtx, GuestDnDURIObjCtx *pObjCtx, GuestDnDMsg *pMsg)
912{
913 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
914 AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
915 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
916
917 DnDURIObject *pObj = pObjCtx->getObj();
918 AssertPtr(pObj);
919
920 RTCString strPathSrc = pObj->GetSourcePath();
921 if (strPathSrc.isEmpty())
922 return VERR_INVALID_PARAMETER;
923
924 int rc = VINF_SUCCESS;
925
926 LogFlowFunc(("Sending file with %RU32 bytes buffer, using protocol v%RU32 ...\n",
927 mData.mcbBlockSize, mDataBase.m_uProtocolVersion));
928 LogFlowFunc(("strPathSrc=%s, fIsOpen=%RTbool, cbSize=%RU64\n", strPathSrc.c_str(), pObj->IsOpen(), pObj->GetSize()));
929
930 if (!pObj->IsOpen())
931 {
932 LogRel2(("DnD: Opening host file for transferring to guest: %s\n", strPathSrc.c_str()));
933 rc = pObj->OpenEx(strPathSrc, DnDURIObject::File, DnDURIObject::Source,
934 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, 0 /* fFlags */);
935 if (RT_FAILURE(rc))
936 LogRel(("DnD: Error opening host file '%s', rc=%Rrc\n", strPathSrc.c_str(), rc));
937 }
938
939 bool fSendData = false;
940 if (RT_SUCCESS(rc))
941 {
942 if (mDataBase.m_uProtocolVersion >= 2)
943 {
944 uint32_t fState = pObjCtx->getState();
945 if (!(fState & DND_OBJCTX_STATE_HAS_HDR))
946 {
947 /*
948 * Since protocol v2 the file header and the actual file contents are
949 * separate messages, so send the file header first.
950 * The just registered callback will be called by the guest afterwards.
951 */
952 pMsg->setType(HOST_DND_HG_SND_FILE_HDR);
953 pMsg->setNextUInt32(0); /** @todo ContextID not used yet. */
954 pMsg->setNextString(pObj->GetDestPath().c_str()); /* pvName */
955 pMsg->setNextUInt32((uint32_t)(pObj->GetDestPath().length() + 1)); /* cbName */
956 pMsg->setNextUInt32(0); /* uFlags */
957 pMsg->setNextUInt32(pObj->GetMode()); /* fMode */
958 pMsg->setNextUInt64(pObj->GetSize()); /* uSize */
959
960 LogFlowFunc(("Sending file header ...\n"));
961 LogRel2(("DnD: Transferring host file to guest: %s (%RU64 bytes, mode 0x%x)\n",
962 strPathSrc.c_str(), pObj->GetSize(), pObj->GetMode()));
963
964 /** @todo Set progress object title to current file being transferred? */
965
966 pObjCtx->setState(fState | DND_OBJCTX_STATE_HAS_HDR);
967 }
968 else
969 {
970 /* File header was sent, so only send the actual file data. */
971 fSendData = true;
972 }
973 }
974 else /* Protocol v1. */
975 {
976 /* Always send the file data, every time. */
977 fSendData = true;
978 }
979 }
980
981 if ( RT_SUCCESS(rc)
982 && fSendData)
983 {
984 rc = i_sendFileData(pCtx, pObjCtx, pMsg);
985 }
986
987 LogFlowFuncLeaveRC(rc);
988 return rc;
989}
990
991int GuestDnDTarget::i_sendFileData(PSENDDATACTX pCtx, GuestDnDURIObjCtx *pObjCtx, GuestDnDMsg *pMsg)
992{
993 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
994 AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
995 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
996
997 DnDURIObject *pObj = pObjCtx->getObj();
998 AssertPtr(pObj);
999
1000 AssertPtr(pCtx->mpResp);
1001
1002 /** @todo Don't allow concurrent reads per context! */
1003
1004 /*
1005 * Start sending stuff.
1006 */
1007
1008 /* Set the message type. */
1009 pMsg->setType(HOST_DND_HG_SND_FILE_DATA);
1010
1011 /* Protocol version 1 sends the file path *every* time with a new file chunk.
1012 * In protocol version 2 we only do this once with HOST_DND_HG_SND_FILE_HDR. */
1013 if (mDataBase.m_uProtocolVersion <= 1)
1014 {
1015 pMsg->setNextString(pObj->GetDestPath().c_str()); /* pvName */
1016 pMsg->setNextUInt32((uint32_t)(pObj->GetDestPath().length() + 1)); /* cbName */
1017 }
1018 else if (mDataBase.m_uProtocolVersion >= 2)
1019 {
1020 pMsg->setNextUInt32(0); /** @todo ContextID not used yet. */
1021 }
1022
1023 uint32_t cbRead = 0;
1024
1025 int rc = pObj->Read(pCtx->mURI.getBufferMutable(), pCtx->mURI.getBufferSize(), &cbRead);
1026 if (RT_SUCCESS(rc))
1027 {
1028 pCtx->mData.addProcessed(cbRead);
1029 LogFlowFunc(("cbBufSize=%zu, cbRead=%RU32\n", pCtx->mURI.getBufferSize(), cbRead));
1030
1031 if (mDataBase.m_uProtocolVersion <= 1)
1032 {
1033 pMsg->setNextPointer(pCtx->mURI.getBufferMutable(), cbRead); /* pvData */
1034 pMsg->setNextUInt32(cbRead); /* cbData */
1035 pMsg->setNextUInt32(pObj->GetMode()); /* fMode */
1036 }
1037 else /* Protocol v2 and up. */
1038 {
1039 pMsg->setNextPointer(pCtx->mURI.getBufferMutable(), cbRead); /* pvData */
1040 pMsg->setNextUInt32(cbRead); /* cbData */
1041
1042 if (mDataBase.m_uProtocolVersion >= 3)
1043 {
1044 /** @todo Calculate checksum. */
1045 pMsg->setNextPointer(NULL, 0); /* pvChecksum */
1046 pMsg->setNextUInt32(0); /* cbChecksum */
1047 }
1048 }
1049
1050 if (pObj->IsComplete()) /* Done reading? */
1051 {
1052 LogRel2(("DnD: File transfer to guest complete: %s\n", pObj->GetSourcePath().c_str()));
1053 LogFlowFunc(("File '%s' complete\n", pObj->GetSourcePath().c_str()));
1054
1055 /* DnDURIObject::Read() returns VINF_EOF when finished reading the entire fire,
1056 * but we don't want this here -- so just override this with VINF_SUCCESS. */
1057 rc = VINF_SUCCESS;
1058 }
1059 }
1060
1061 LogFlowFuncLeaveRC(rc);
1062 return rc;
1063}
1064
1065/* static */
1066DECLCALLBACK(int) GuestDnDTarget::i_sendURIDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
1067{
1068 PSENDDATACTX pCtx = (PSENDDATACTX)pvUser;
1069 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1070
1071 GuestDnDTarget *pThis = pCtx->mpTarget;
1072 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
1073
1074 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
1075
1076 int rc = VINF_SUCCESS;
1077 int rcGuest = VINF_SUCCESS; /* Contains error code from guest in case of VERR_GSTDND_GUEST_ERROR. */
1078 bool fNotify = false;
1079
1080 switch (uMsg)
1081 {
1082 case GUEST_DND_CONNECT:
1083 /* Nothing to do here (yet). */
1084 break;
1085
1086 case GUEST_DND_DISCONNECT:
1087 rc = VERR_CANCELLED;
1088 break;
1089
1090 case GUEST_DND_GET_NEXT_HOST_MSG:
1091 {
1092 PVBOXDNDCBHGGETNEXTHOSTMSG pCBData = reinterpret_cast<PVBOXDNDCBHGGETNEXTHOSTMSG>(pvParms);
1093 AssertPtr(pCBData);
1094 AssertReturn(sizeof(VBOXDNDCBHGGETNEXTHOSTMSG) == cbParms, VERR_INVALID_PARAMETER);
1095 AssertReturn(CB_MAGIC_DND_HG_GET_NEXT_HOST_MSG == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
1096
1097 try
1098 {
1099 GuestDnDMsg *pMsg = new GuestDnDMsg();
1100
1101 rc = pThis->i_sendURIDataLoop(pCtx, pMsg);
1102 if (rc == VINF_EOF) /* Transfer complete? */
1103 {
1104 LogFlowFunc(("Last URI item processed, bailing out\n"));
1105 }
1106 else if (RT_SUCCESS(rc))
1107 {
1108 rc = pThis->msgQueueAdd(pMsg);
1109 if (RT_SUCCESS(rc)) /* Return message type & required parameter count to the guest. */
1110 {
1111 LogFlowFunc(("GUEST_DND_GET_NEXT_HOST_MSG -> %RU32 (%RU32 params)\n", pMsg->getType(), pMsg->getCount()));
1112 pCBData->uMsg = pMsg->getType();
1113 pCBData->cParms = pMsg->getCount();
1114 }
1115 }
1116
1117 if ( RT_FAILURE(rc)
1118 || rc == VINF_EOF) /* Transfer complete? */
1119 {
1120 delete pMsg;
1121 pMsg = NULL;
1122 }
1123 }
1124 catch(std::bad_alloc & /*e*/)
1125 {
1126 rc = VERR_NO_MEMORY;
1127 }
1128 break;
1129 }
1130 case GUEST_DND_GH_EVT_ERROR:
1131 {
1132 PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<PVBOXDNDCBEVTERRORDATA>(pvParms);
1133 AssertPtr(pCBData);
1134 AssertReturn(sizeof(VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1135 AssertReturn(CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.uMagic, VERR_INVALID_PARAMETER);
1136
1137 pCtx->mpResp->reset();
1138
1139 if (RT_SUCCESS(pCBData->rc))
1140 {
1141 AssertMsgFailed(("Guest has sent an error event but did not specify an actual error code\n"));
1142 pCBData->rc = VERR_GENERAL_FAILURE; /* Make sure some error is set. */
1143 }
1144
1145 rc = pCtx->mpResp->setProgress(100, DND_PROGRESS_ERROR, pCBData->rc,
1146 GuestDnDTarget::i_guestErrorToString(pCBData->rc));
1147 if (RT_SUCCESS(rc))
1148 {
1149 rc = VERR_GSTDND_GUEST_ERROR;
1150 rcGuest = pCBData->rc;
1151 }
1152 break;
1153 }
1154 case HOST_DND_HG_SND_DIR:
1155 case HOST_DND_HG_SND_FILE_HDR:
1156 case HOST_DND_HG_SND_FILE_DATA:
1157 {
1158 PVBOXDNDCBHGGETNEXTHOSTMSGDATA pCBData
1159 = reinterpret_cast<PVBOXDNDCBHGGETNEXTHOSTMSGDATA>(pvParms);
1160 AssertPtr(pCBData);
1161 AssertReturn(sizeof(VBOXDNDCBHGGETNEXTHOSTMSGDATA) == cbParms, VERR_INVALID_PARAMETER);
1162
1163 LogFlowFunc(("pCBData->uMsg=%RU32, paParms=%p, cParms=%RU32\n", pCBData->uMsg, pCBData->paParms, pCBData->cParms));
1164
1165 GuestDnDMsg *pMsg = pThis->msgQueueGetNext();
1166 if (pMsg)
1167 {
1168 /*
1169 * Sanity checks.
1170 */
1171 if ( pCBData->uMsg != uMsg
1172 || pCBData->paParms == NULL
1173 || pCBData->cParms != pMsg->getCount())
1174 {
1175 LogFlowFunc(("Current message does not match:\n"));
1176 LogFlowFunc(("\tCallback: uMsg=%RU32, cParms=%RU32, paParms=%p\n",
1177 pCBData->uMsg, pCBData->cParms, pCBData->paParms));
1178 LogFlowFunc(("\t Next: uMsg=%RU32, cParms=%RU32\n", pMsg->getType(), pMsg->getCount()));
1179
1180 /* Start over. */
1181 pThis->msgQueueClear();
1182
1183 rc = VERR_INVALID_PARAMETER;
1184 }
1185
1186 if (RT_SUCCESS(rc))
1187 {
1188 LogFlowFunc(("Returning uMsg=%RU32\n", uMsg));
1189 rc = HGCM::Message::copyParms(pCBData->paParms, pCBData->cParms, pMsg->getParms(), pMsg->getCount());
1190 if (RT_SUCCESS(rc))
1191 {
1192 pCBData->cParms = pMsg->getCount();
1193 pThis->msgQueueRemoveNext();
1194 }
1195 else
1196 LogFlowFunc(("Copying parameters failed with rc=%Rrc\n", rc));
1197 }
1198 }
1199 else
1200 rc = VERR_NO_DATA;
1201
1202 LogFlowFunc(("Processing next message ended with rc=%Rrc\n", rc));
1203 break;
1204 }
1205 default:
1206 rc = VERR_NOT_SUPPORTED;
1207 break;
1208 }
1209
1210 int rcToGuest = VINF_SUCCESS; /* Status which will be sent back to the guest. */
1211
1212 /*
1213 * Resolve errors.
1214 */
1215 switch (rc)
1216 {
1217 case VINF_SUCCESS:
1218 break;
1219
1220 case VINF_EOF:
1221 {
1222 LogRel2(("DnD: Transfer to guest complete\n"));
1223
1224 /* Complete operation on host side. */
1225 fNotify = true;
1226
1227 /* The guest expects VERR_NO_DATA if the transfer is complete. */
1228 rcToGuest = VERR_NO_DATA;
1229 break;
1230 }
1231
1232 case VERR_GSTDND_GUEST_ERROR:
1233 {
1234 LogRel(("DnD: Guest reported error %Rrc, aborting transfer to guest\n", rcGuest));
1235 break;
1236 }
1237
1238 case VERR_CANCELLED:
1239 {
1240 LogRel2(("DnD: Transfer to guest canceled\n"));
1241 rcToGuest = VERR_CANCELLED; /* Also cancel on guest side. */
1242 break;
1243 }
1244
1245 default:
1246 {
1247 LogRel(("DnD: Host error %Rrc occurred, aborting transfer to guest\n", rc));
1248 rcToGuest = VERR_CANCELLED; /* Also cancel on guest side. */
1249 break;
1250 }
1251 }
1252
1253 if (RT_FAILURE(rc))
1254 {
1255 /* Unregister this callback. */
1256 AssertPtr(pCtx->mpResp);
1257 int rc2 = pCtx->mpResp->setCallback(uMsg, NULL /* PFNGUESTDNDCALLBACK */);
1258 AssertRC(rc2);
1259
1260 /* Let the waiter(s) know. */
1261 fNotify = true;
1262 }
1263
1264 LogFlowFunc(("fNotify=%RTbool, rc=%Rrc, rcToGuest=%Rrc\n", fNotify, rc, rcToGuest));
1265
1266 if (fNotify)
1267 {
1268 int rc2 = pCtx->mCBEvent.Notify(rc); /** @todo Also pass guest error back? */
1269 AssertRC(rc2);
1270 }
1271
1272 LogFlowFuncLeaveRC(rc);
1273 return rcToGuest; /* Tell the guest. */
1274}
1275
1276int GuestDnDTarget::i_sendURIData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
1277{
1278 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1279 AssertPtr(pCtx->mpResp);
1280
1281#define REGISTER_CALLBACK(x) \
1282 do { \
1283 rc = pCtx->mpResp->setCallback(x, i_sendURIDataCallback, pCtx); \
1284 if (RT_FAILURE(rc)) \
1285 return rc; \
1286 } while (0)
1287
1288#define UNREGISTER_CALLBACK(x) \
1289 do { \
1290 int rc2 = pCtx->mpResp->setCallback(x, NULL); \
1291 AssertRC(rc2); \
1292 } while (0)
1293
1294 int rc = pCtx->mURI.init(mData.mcbBlockSize);
1295 if (RT_FAILURE(rc))
1296 return rc;
1297
1298 rc = pCtx->mCBEvent.Reset();
1299 if (RT_FAILURE(rc))
1300 return rc;
1301
1302 /*
1303 * Register callbacks.
1304 */
1305 /* Guest callbacks. */
1306 REGISTER_CALLBACK(GUEST_DND_CONNECT);
1307 REGISTER_CALLBACK(GUEST_DND_DISCONNECT);
1308 REGISTER_CALLBACK(GUEST_DND_GET_NEXT_HOST_MSG);
1309 REGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
1310 /* Host callbacks. */
1311 REGISTER_CALLBACK(HOST_DND_HG_SND_DIR);
1312 if (mDataBase.m_uProtocolVersion >= 2)
1313 REGISTER_CALLBACK(HOST_DND_HG_SND_FILE_HDR);
1314 REGISTER_CALLBACK(HOST_DND_HG_SND_FILE_DATA);
1315
1316 do
1317 {
1318 /*
1319 * Extract URI list from current meta data.
1320 */
1321 GuestDnDData *pData = &pCtx->mData;
1322 GuestDnDURIData *pURI = &pCtx->mURI;
1323
1324 rc = pURI->fromLocalMetaData(pData->getMeta());
1325 if (RT_FAILURE(rc))
1326 break;
1327
1328 LogFlowFunc(("URI root objects: %zu, total bytes (raw data to transfer): %zu\n",
1329 pURI->getURIList().RootCount(), pURI->getURIList().TotalBytes()));
1330
1331 /*
1332 * Set the new meta data with the URI list in it.
1333 */
1334 rc = pData->getMeta().fromURIList(pURI->getURIList());
1335 if (RT_FAILURE(rc))
1336 break;
1337
1338 /*
1339 * Set the estimated data sizes we are going to send.
1340 * The total size also contains the meta data size.
1341 */
1342 const uint32_t cbMeta = pData->getMeta().getSize();
1343 pData->setEstimatedSize(pURI->getURIList().TotalBytes() + cbMeta /* cbTotal */,
1344 cbMeta /* cbMeta */);
1345
1346 /*
1347 * Set the meta format.
1348 */
1349 void *pvFmt = (void *)pCtx->mFmtReq.c_str();
1350 uint32_t cbFmt = (uint32_t)pCtx->mFmtReq.length() + 1; /* Include terminating zero. */
1351
1352 pData->setFmt(pvFmt, cbFmt);
1353
1354 /*
1355 * The first message always is the data header. The meta data itself then follows
1356 * and *only* contains the root elements of an URI list.
1357 *
1358 * After the meta data we generate the messages required to send the
1359 * file/directory data itself.
1360 *
1361 * Note: Protocol < v3 use the first data message to tell what's being sent.
1362 */
1363 GuestDnDMsg Msg;
1364
1365 /*
1366 * Send the data header first.
1367 */
1368 if (mDataBase.m_uProtocolVersion >= 3)
1369 rc = i_sendDataHeader(pCtx, pData, &pCtx->mURI);
1370
1371 /*
1372 * Send the (meta) data body.
1373 */
1374 if (RT_SUCCESS(rc))
1375 rc = i_sendDataBody(pCtx, pData);
1376
1377 if (RT_SUCCESS(rc))
1378 {
1379 rc = waitForEvent(&pCtx->mCBEvent, pCtx->mpResp, msTimeout);
1380 if (RT_SUCCESS(rc))
1381 pCtx->mpResp->setProgress(100, DND_PROGRESS_COMPLETE, VINF_SUCCESS);
1382 }
1383
1384 } while (0);
1385
1386 /*
1387 * Unregister callbacks.
1388 */
1389 /* Guest callbacks. */
1390 UNREGISTER_CALLBACK(GUEST_DND_CONNECT);
1391 UNREGISTER_CALLBACK(GUEST_DND_DISCONNECT);
1392 UNREGISTER_CALLBACK(GUEST_DND_GET_NEXT_HOST_MSG);
1393 UNREGISTER_CALLBACK(GUEST_DND_GH_EVT_ERROR);
1394 /* Host callbacks. */
1395 UNREGISTER_CALLBACK(HOST_DND_HG_SND_DIR);
1396 if (mDataBase.m_uProtocolVersion >= 2)
1397 UNREGISTER_CALLBACK(HOST_DND_HG_SND_FILE_HDR);
1398 UNREGISTER_CALLBACK(HOST_DND_HG_SND_FILE_DATA);
1399
1400#undef REGISTER_CALLBACK
1401#undef UNREGISTER_CALLBACK
1402
1403 if (RT_FAILURE(rc))
1404 {
1405 if (rc == VERR_CANCELLED)
1406 pCtx->mpResp->setProgress(100, DND_PROGRESS_CANCELLED, VINF_SUCCESS);
1407 else if (rc != VERR_GSTDND_GUEST_ERROR) /* Guest-side error are already handled in the callback. */
1408 pCtx->mpResp->setProgress(100, DND_PROGRESS_ERROR, rc,
1409 GuestDnDTarget::i_hostErrorToString(rc));
1410 }
1411
1412 /*
1413 * Now that we've cleaned up tell the guest side to cancel.
1414 * This does not imply we're waiting for the guest to react, as the
1415 * host side never must depend on anything from the guest.
1416 */
1417 if (rc == VERR_CANCELLED)
1418 {
1419 int rc2 = sendCancel();
1420 AssertRC(rc2);
1421 }
1422
1423 LogFlowFuncLeaveRC(rc);
1424 return rc;
1425}
1426
1427int GuestDnDTarget::i_sendURIDataLoop(PSENDDATACTX pCtx, GuestDnDMsg *pMsg)
1428{
1429 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1430 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
1431
1432 int rc = updateProgress(&pCtx->mData, pCtx->mpResp);
1433 AssertRC(rc);
1434
1435 if ( pCtx->mData.isComplete()
1436 && pCtx->mURI.isComplete())
1437 {
1438 return VINF_EOF;
1439 }
1440
1441 GuestDnDURIObjCtx &objCtx = pCtx->mURI.getObjCurrent();
1442 if (!objCtx.isValid())
1443 return VERR_WRONG_ORDER;
1444
1445 DnDURIObject *pCurObj = objCtx.getObj();
1446 AssertPtr(pCurObj);
1447
1448 uint32_t fMode = pCurObj->GetMode();
1449 LogRel3(("DnD: Processing: srcPath=%s, dstPath=%s, fMode=0x%x, cbSize=%RU32, fIsDir=%RTbool, fIsFile=%RTbool\n",
1450 pCurObj->GetSourcePath().c_str(), pCurObj->GetDestPath().c_str(),
1451 fMode, pCurObj->GetSize(),
1452 RTFS_IS_DIRECTORY(fMode), RTFS_IS_FILE(fMode)));
1453
1454 if (RTFS_IS_DIRECTORY(fMode))
1455 {
1456 rc = i_sendDirectory(pCtx, &objCtx, pMsg);
1457 }
1458 else if (RTFS_IS_FILE(fMode))
1459 {
1460 rc = i_sendFile(pCtx, &objCtx, pMsg);
1461 }
1462 else
1463 {
1464 AssertMsgFailed(("fMode=0x%x is not supported for srcPath=%s, dstPath=%s\n",
1465 fMode, pCurObj->GetSourcePath().c_str(), pCurObj->GetDestPath().c_str()));
1466 rc = VERR_NOT_SUPPORTED;
1467 }
1468
1469 bool fRemove = false; /* Remove current entry? */
1470 if ( pCurObj->IsComplete()
1471 || RT_FAILURE(rc))
1472 {
1473 fRemove = true;
1474 }
1475
1476 if (fRemove)
1477 {
1478 LogFlowFunc(("Removing \"%s\" from list, rc=%Rrc\n", pCurObj->GetSourcePath().c_str(), rc));
1479 pCtx->mURI.removeObjCurrent();
1480 }
1481
1482 LogFlowFuncLeaveRC(rc);
1483 return rc;
1484}
1485
1486int GuestDnDTarget::i_sendRawData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
1487{
1488 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1489 NOREF(msTimeout);
1490
1491 GuestDnDData *pData = &pCtx->mData;
1492
1493 /** @todo At the moment we only allow sending up to 64K raw data.
1494 * For protocol v1+v2: Fix this by using HOST_DND_HG_SND_MORE_DATA.
1495 * For protocol v3 : Send another HOST_DND_HG_SND_DATA message. */
1496 if (!pData->getMeta().getSize())
1497 return VINF_SUCCESS;
1498
1499 int rc = VINF_SUCCESS;
1500
1501 /*
1502 * Send the data header first.
1503 */
1504 if (mDataBase.m_uProtocolVersion >= 3)
1505 rc = i_sendDataHeader(pCtx, pData, NULL /* URI list */);
1506
1507 /*
1508 * Send the (meta) data body.
1509 */
1510 if (RT_SUCCESS(rc))
1511 rc = i_sendDataBody(pCtx, pData);
1512
1513 int rc2;
1514 if (RT_FAILURE(rc))
1515 rc2 = pCtx->mpResp->setProgress(100, DND_PROGRESS_ERROR, rc,
1516 GuestDnDTarget::i_hostErrorToString(rc));
1517 else
1518 rc2 = pCtx->mpResp->setProgress(100, DND_PROGRESS_COMPLETE, rc);
1519 AssertRC(rc2);
1520
1521 LogFlowFuncLeaveRC(rc);
1522 return rc;
1523}
1524
1525HRESULT GuestDnDTarget::cancel(BOOL *aVeto)
1526{
1527#if !defined(VBOX_WITH_DRAG_AND_DROP)
1528 ReturnComNotImplemented();
1529#else /* VBOX_WITH_DRAG_AND_DROP */
1530
1531 int rc = i_cancelOperation();
1532
1533 if (aVeto)
1534 *aVeto = FALSE; /** @todo */
1535
1536 HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
1537
1538 LogFlowFunc(("hr=%Rhrc\n", hr));
1539 return hr;
1540#endif /* VBOX_WITH_DRAG_AND_DROP */
1541}
1542
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