VirtualBox

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

Last change on this file since 74492 was 74492, checked in by vboxsync, 6 years ago

DnD/Main: Made sure that the active flag of PRECVDATACTX / PSENDDATACTX is set correctly.

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