VirtualBox

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

Last change on this file since 58277 was 58257, checked in by vboxsync, 9 years ago

DnD: Added context IDs for all HGCM messages.

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