VirtualBox

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

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

Main: warnings

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