VirtualBox

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

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

build fix

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