VirtualBox

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

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

(C) 2016

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 52.7 KB
Line 
1/* $Id: GuestDnDTargetImpl.cpp 62485 2016-07-22 18:36:43Z 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 threadSnd;
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 hr = pTask->createThread(&threadSnd);
656
657 }
658 catch(std::bad_alloc &)
659 {
660 hr = setError(E_OUTOFMEMORY);
661 }
662 catch(...)
663 {
664 LogRel2(("DnD: Could not create thread for SendDataTask \n"));
665 hr = E_FAIL;
666 }
667
668 if (SUCCEEDED(hr))
669 {
670 rc = RTThreadUserWait(threadSnd, 30 * 1000 /* 30s timeout */);
671 if (RT_SUCCESS(rc))
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("Waiting for sending thread failed (%Rrc)"), rc);
682 }
683 else
684 hr = setError(VBOX_E_IPRT_ERROR, tr("Starting thread for GuestDnDTarget::i_sendDataThread (%Rrc)"), rc);
685
686 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
687 return hr;
688#endif /* VBOX_WITH_DRAG_AND_DROP */
689}
690
691int GuestDnDTarget::i_cancelOperation(void)
692{
693 /** @todo Check for pending cancel requests. */
694
695#if 0 /** @todo Later. */
696 /* Cancel any outstanding waits for guest responses first. */
697 if (pResp)
698 pResp->notifyAboutGuestResponse();
699#endif
700
701 LogFlowFunc(("Cancelling operation, telling guest ...\n"));
702 return GuestDnDInst()->hostCall(HOST_DND_HG_EVT_CANCEL, 0 /* cParms */, NULL /*paParms*/);
703}
704
705/* static */
706Utf8Str GuestDnDTarget::i_guestErrorToString(int guestRc)
707{
708 Utf8Str strError;
709
710 switch (guestRc)
711 {
712 case VERR_ACCESS_DENIED:
713 strError += Utf8StrFmt(tr("For one or more guest files or directories selected for transferring to the host your guest "
714 "user does not have the appropriate access rights for. Please make sure that all selected "
715 "elements can be accessed and that your guest user has the appropriate rights"));
716 break;
717
718 case VERR_NOT_FOUND:
719 /* Should not happen due to file locking on the guest, but anyway ... */
720 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were not"
721 "found on the guest anymore. This can be the case if the guest files were moved and/or"
722 "altered while the drag and drop operation was in progress"));
723 break;
724
725 case VERR_SHARING_VIOLATION:
726 strError += Utf8StrFmt(tr("One or more guest files or directories selected for transferring to the host were locked. "
727 "Please make sure that all selected elements can be accessed and that your guest user has "
728 "the appropriate rights"));
729 break;
730
731 case VERR_TIMEOUT:
732 strError += Utf8StrFmt(tr("The guest was not able to process the drag and drop data within time"));
733 break;
734
735 default:
736 strError += Utf8StrFmt(tr("Drag and drop error from guest (%Rrc)"), guestRc);
737 break;
738 }
739
740 return strError;
741}
742
743/* static */
744Utf8Str GuestDnDTarget::i_hostErrorToString(int hostRc)
745{
746 Utf8Str strError;
747
748 switch (hostRc)
749 {
750 case VERR_ACCESS_DENIED:
751 strError += Utf8StrFmt(tr("For one or more host files or directories selected for transferring to the guest your host "
752 "user does not have the appropriate access rights for. Please make sure that all selected "
753 "elements can be accessed and that your host user has the appropriate rights."));
754 break;
755
756 case VERR_NOT_FOUND:
757 /* Should not happen due to file locking on the host, but anyway ... */
758 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the host were not"
759 "found on the host anymore. This can be the case if the host files were moved and/or"
760 "altered while the drag and drop operation was in progress."));
761 break;
762
763 case VERR_SHARING_VIOLATION:
764 strError += Utf8StrFmt(tr("One or more host files or directories selected for transferring to the guest were locked. "
765 "Please make sure that all selected elements can be accessed and that your host user has "
766 "the appropriate rights."));
767 break;
768
769 default:
770 strError += Utf8StrFmt(tr("Drag and drop error from host (%Rrc)"), hostRc);
771 break;
772 }
773
774 return strError;
775}
776
777int GuestDnDTarget::i_sendData(PSENDDATACTX pCtx, RTMSINTERVAL msTimeout)
778{
779 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
780
781 int rc;
782
783 ASMAtomicWriteBool(&pCtx->mIsActive, true);
784
785 /* Clear all remaining outgoing messages. */
786 mDataBase.m_lstMsgOut.clear();
787
788 /**
789 * Do we need to build up a file tree?
790 * Note: The decision whether we need to build up a file tree and sending
791 * actual file data only depends on the actual formats offered by this target.
792 * If the guest does not want an URI list ("text/uri-list") but text ("TEXT" and
793 * friends) instead, still send the data over to the guest -- the file as such still
794 * is needed on the guest in this case, as the guest then just wants a simple path
795 * instead of an URI list (pointing to a file on the guest itself).
796 *
797 ** @todo Support more than one format; add a format<->function handler concept. Later. */
798 bool fHasURIList = std::find(m_lstFmtOffered.begin(),
799 m_lstFmtOffered.end(), "text/uri-list") != m_lstFmtOffered.end();
800 if (fHasURIList)
801 {
802 rc = i_sendURIData(pCtx, msTimeout);
803 }
804 else
805 {
806 rc = i_sendRawData(pCtx, msTimeout);
807 }
808
809 ASMAtomicWriteBool(&pCtx->mIsActive, false);
810
811 LogFlowFuncLeaveRC(rc);
812 return rc;
813}
814
815int GuestDnDTarget::i_sendDataBody(PSENDDATACTX pCtx, GuestDnDData *pData)
816{
817 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
818 AssertPtrReturn(pData, VERR_INVALID_POINTER);
819
820 /** @todo Add support for multiple HOST_DND_HG_SND_DATA messages in case of more than 64K data! */
821 if (pData->getMeta().getSize() > _64K)
822 return VERR_NOT_IMPLEMENTED;
823
824 GuestDnDMsg Msg;
825
826 LogFlowFunc(("cbFmt=%RU32, cbMeta=%RU32, cbChksum=%RU32\n",
827 pData->getFmtSize(), pData->getMeta().getSize(), pData->getChkSumSize()));
828
829 Msg.setType(HOST_DND_HG_SND_DATA);
830 if (mDataBase.m_uProtocolVersion < 3)
831 {
832 Msg.setNextUInt32(pCtx->mScreenID); /* uScreenId */
833 Msg.setNextPointer(pData->getFmtMutable(), pData->getFmtSize()); /* pvFormat */
834 Msg.setNextUInt32(pData->getFmtSize()); /* cbFormat */
835 Msg.setNextPointer(pData->getMeta().getDataMutable(), pData->getMeta().getSize()); /* pvData */
836 /* Fill in the current data block size to send.
837 * Note: Only supports uint32_t. */
838 Msg.setNextUInt32((uint32_t)pData->getMeta().getSize()); /* cbData */
839 }
840 else
841 {
842 Msg.setNextUInt32(0); /** @todo ContextID not used yet. */
843 Msg.setNextPointer(pData->getMeta().getDataMutable(), pData->getMeta().getSize()); /* pvData */
844 Msg.setNextUInt32(pData->getMeta().getSize()); /* cbData */
845 Msg.setNextPointer(pData->getChkSumMutable(), pData->getChkSumSize()); /** @todo pvChecksum; not used yet. */
846 Msg.setNextUInt32(pData->getChkSumSize()); /** @todo cbChecksum; not used yet. */
847 }
848
849 int rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
850 if (RT_SUCCESS(rc))
851 rc = updateProgress(pData, pCtx->mpResp, pData->getMeta().getSize());
852
853 LogFlowFuncLeaveRC(rc);
854 return rc;
855}
856
857int GuestDnDTarget::i_sendDataHeader(PSENDDATACTX pCtx, GuestDnDData *pData, GuestDnDURIData *pURIData /* = NULL */)
858{
859 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
860 AssertPtrReturn(pData, VERR_INVALID_POINTER);
861 /* pURIData is optional. */
862
863 GuestDnDMsg Msg;
864
865 Msg.setType(HOST_DND_HG_SND_DATA_HDR);
866
867 Msg.setNextUInt32(0); /** @todo uContext; not used yet. */
868 Msg.setNextUInt32(0); /** @todo uFlags; not used yet. */
869 Msg.setNextUInt32(pCtx->mScreenID); /* uScreen */
870 Msg.setNextUInt64(pData->getTotal()); /* cbTotal */
871 Msg.setNextUInt32(pData->getMeta().getSize()); /* cbMeta*/
872 Msg.setNextPointer(pData->getFmtMutable(), pData->getFmtSize()); /* pvMetaFmt */
873 Msg.setNextUInt32(pData->getFmtSize()); /* cbMetaFmt */
874 Msg.setNextUInt64(pURIData ? pURIData->getObjToProcess() : 0); /* cObjects */
875 Msg.setNextUInt32(0); /** @todo enmCompression; not used yet. */
876 Msg.setNextUInt32(0); /** @todo enmChecksumType; not used yet. */
877 Msg.setNextPointer(NULL, 0); /** @todo pvChecksum; not used yet. */
878 Msg.setNextUInt32(0); /** @todo cbChecksum; not used yet. */
879
880 int rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
881
882 LogFlowFuncLeaveRC(rc);
883 return rc;
884}
885
886int GuestDnDTarget::i_sendDirectory(PSENDDATACTX pCtx, GuestDnDURIObjCtx *pObjCtx, GuestDnDMsg *pMsg)
887{
888 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
889 AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
890 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
891
892 DnDURIObject *pObj = pObjCtx->getObj();
893 AssertPtr(pObj);
894
895 RTCString strPath = pObj->GetDestPath();
896 if (strPath.isEmpty())
897 return VERR_INVALID_PARAMETER;
898 if (strPath.length() >= RTPATH_MAX) /* Note: Maximum is RTPATH_MAX on guest side. */
899 return VERR_BUFFER_OVERFLOW;
900
901 LogRel2(("DnD: Transferring host directory to guest: %s\n", strPath.c_str()));
902
903 pMsg->setType(HOST_DND_HG_SND_DIR);
904 if (mDataBase.m_uProtocolVersion >= 3)
905 pMsg->setNextUInt32(0); /** @todo ContextID not used yet. */
906 pMsg->setNextString(strPath.c_str()); /* path */
907 pMsg->setNextUInt32((uint32_t)(strPath.length() + 1)); /* path length (maximum is RTPATH_MAX on guest side). */
908 pMsg->setNextUInt32(pObj->GetMode()); /* mode */
909
910 return VINF_SUCCESS;
911}
912
913int GuestDnDTarget::i_sendFile(PSENDDATACTX pCtx, GuestDnDURIObjCtx *pObjCtx, GuestDnDMsg *pMsg)
914{
915 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
916 AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
917 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
918
919 DnDURIObject *pObj = pObjCtx->getObj();
920 AssertPtr(pObj);
921
922 RTCString strPathSrc = pObj->GetSourcePath();
923 if (strPathSrc.isEmpty())
924 return VERR_INVALID_PARAMETER;
925
926 int rc = VINF_SUCCESS;
927
928 LogFlowFunc(("Sending file with %RU32 bytes buffer, using protocol v%RU32 ...\n",
929 mData.mcbBlockSize, mDataBase.m_uProtocolVersion));
930 LogFlowFunc(("strPathSrc=%s, fIsOpen=%RTbool, cbSize=%RU64\n", strPathSrc.c_str(), pObj->IsOpen(), pObj->GetSize()));
931
932 if (!pObj->IsOpen())
933 {
934 LogRel2(("DnD: Opening host file for transferring to guest: %s\n", strPathSrc.c_str()));
935 rc = pObj->OpenEx(strPathSrc, DnDURIObject::File, DnDURIObject::Source,
936 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE, 0 /* fFlags */);
937 if (RT_FAILURE(rc))
938 LogRel(("DnD: Error opening host file '%s', rc=%Rrc\n", strPathSrc.c_str(), rc));
939 }
940
941 bool fSendData = false;
942 if (RT_SUCCESS(rc))
943 {
944 if (mDataBase.m_uProtocolVersion >= 2)
945 {
946 uint32_t fState = pObjCtx->getState();
947 if (!(fState & DND_OBJCTX_STATE_HAS_HDR))
948 {
949 /*
950 * Since protocol v2 the file header and the actual file contents are
951 * separate messages, so send the file header first.
952 * The just registered callback will be called by the guest afterwards.
953 */
954 pMsg->setType(HOST_DND_HG_SND_FILE_HDR);
955 pMsg->setNextUInt32(0); /** @todo ContextID not used yet. */
956 pMsg->setNextString(pObj->GetDestPath().c_str()); /* pvName */
957 pMsg->setNextUInt32((uint32_t)(pObj->GetDestPath().length() + 1)); /* cbName */
958 pMsg->setNextUInt32(0); /* uFlags */
959 pMsg->setNextUInt32(pObj->GetMode()); /* fMode */
960 pMsg->setNextUInt64(pObj->GetSize()); /* uSize */
961
962 LogFlowFunc(("Sending file header ...\n"));
963 LogRel2(("DnD: Transferring host file to guest: %s (%RU64 bytes, mode 0x%x)\n",
964 strPathSrc.c_str(), pObj->GetSize(), pObj->GetMode()));
965
966 /** @todo Set progress object title to current file being transferred? */
967
968 pObjCtx->setState(fState | DND_OBJCTX_STATE_HAS_HDR);
969 }
970 else
971 {
972 /* File header was sent, so only send the actual file data. */
973 fSendData = true;
974 }
975 }
976 else /* Protocol v1. */
977 {
978 /* Always send the file data, every time. */
979 fSendData = true;
980 }
981 }
982
983 if ( RT_SUCCESS(rc)
984 && fSendData)
985 {
986 rc = i_sendFileData(pCtx, pObjCtx, pMsg);
987 }
988
989 LogFlowFuncLeaveRC(rc);
990 return rc;
991}
992
993int GuestDnDTarget::i_sendFileData(PSENDDATACTX pCtx, GuestDnDURIObjCtx *pObjCtx, GuestDnDMsg *pMsg)
994{
995 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
996 AssertPtrReturn(pObjCtx, VERR_INVALID_POINTER);
997 AssertPtrReturn(pMsg, VERR_INVALID_POINTER);
998
999 DnDURIObject *pObj = pObjCtx->getObj();
1000 AssertPtr(pObj);
1001
1002 GuestDnDResponse *pResp = pCtx->mpResp;
1003 AssertPtr(pResp);
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 GuestDnD *pInst = GuestDnDInst();
1495 AssertPtr(pInst);
1496
1497 GuestDnDData *pData = &pCtx->mData;
1498
1499 /** @todo At the moment we only allow sending up to 64K raw data.
1500 * For protocol v1+v2: Fix this by using HOST_DND_HG_SND_MORE_DATA.
1501 * For protocol v3 : Send another HOST_DND_HG_SND_DATA message. */
1502 if (!pData->getMeta().getSize())
1503 return VINF_SUCCESS;
1504
1505 int rc = VINF_SUCCESS;
1506
1507 /*
1508 * Send the data header first.
1509 */
1510 if (mDataBase.m_uProtocolVersion >= 3)
1511 rc = i_sendDataHeader(pCtx, pData, NULL /* URI list */);
1512
1513 /*
1514 * Send the (meta) data body.
1515 */
1516 if (RT_SUCCESS(rc))
1517 rc = i_sendDataBody(pCtx, pData);
1518
1519 int rc2;
1520 if (RT_FAILURE(rc))
1521 rc2 = pCtx->mpResp->setProgress(100, DND_PROGRESS_ERROR, rc,
1522 GuestDnDTarget::i_hostErrorToString(rc));
1523 else
1524 rc2 = pCtx->mpResp->setProgress(100, DND_PROGRESS_COMPLETE, rc);
1525 AssertRC(rc2);
1526
1527 LogFlowFuncLeaveRC(rc);
1528 return rc;
1529}
1530
1531HRESULT GuestDnDTarget::cancel(BOOL *aVeto)
1532{
1533#if !defined(VBOX_WITH_DRAG_AND_DROP)
1534 ReturnComNotImplemented();
1535#else /* VBOX_WITH_DRAG_AND_DROP */
1536
1537 int rc = i_cancelOperation();
1538
1539 if (aVeto)
1540 *aVeto = FALSE; /** @todo */
1541
1542 HRESULT hr = RT_SUCCESS(rc) ? S_OK : VBOX_E_IPRT_ERROR;
1543
1544 LogFlowFunc(("hr=%Rhrc\n", hr));
1545 return hr;
1546#endif /* VBOX_WITH_DRAG_AND_DROP */
1547}
1548
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