VirtualBox

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

Last change on this file since 70772 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use