VirtualBox

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

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

Main: More uninitialized variable warnings.

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