VirtualBox

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

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

DnD/Main: Resolved worker thread @todos.

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