VirtualBox

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

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

GuestDnDTargetImpl.cpp,GuestDnDSourceImpl.cpp: @todo r=bird: Pointed out race condition in thread task.

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