VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/GuestDnDSourceImpl.cpp@ 55520

Last change on this file since 55520 was 55520, checked in by vboxsync, 10 years ago

DnD: Don't allow simultaneous drop operations for now.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 34.6 KB
Line 
1/* $Id: GuestDnDSourceImpl.cpp 55520 2015-04-29 12:51:40Z vboxsync $ */
2/** @file
3 * VBox Console COM Class implementation - Guest drag and drop source.
4 */
5
6/*
7 * Copyright (C) 2014-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include "GuestImpl.h"
23#include "GuestDnDSourceImpl.h"
24#include "GuestDnDPrivate.h"
25
26#include "Global.h"
27#include "AutoCaller.h"
28
29#include <iprt/dir.h>
30#include <iprt/file.h>
31#include <iprt/path.h>
32
33#include <iprt/cpp/utils.h> /* For unconst(). */
34
35#include <VBox/com/array.h>
36#include <VBox/GuestHost/DragAndDrop.h>
37#include <VBox/HostServices/DragAndDropSvc.h>
38
39#ifdef LOG_GROUP
40 #undef LOG_GROUP
41#endif
42#define LOG_GROUP LOG_GROUP_GUEST_DND
43#include <VBox/log.h>
44
45/**
46 * Base class for a source task.
47 */
48class GuestDnDSourceTask
49{
50public:
51
52 GuestDnDSourceTask(GuestDnDSource *pSource)
53 : mSource(pSource),
54 mRC(VINF_SUCCESS) { }
55
56 virtual ~GuestDnDSourceTask(void) { }
57
58 int getRC(void) const { return mRC; }
59 bool isOk(void) const { return RT_SUCCESS(mRC); }
60 const ComObjPtr<GuestDnDSource> &getSource(void) const { return mSource; }
61
62protected:
63
64 const ComObjPtr<GuestDnDSource> mSource;
65 int mRC;
66};
67
68/**
69 * Task structure for receiving data from a source using
70 * a worker thread.
71 */
72class RecvDataTask : public GuestDnDSourceTask
73{
74public:
75
76 RecvDataTask(GuestDnDSource *pSource, PRECVDATACTX pCtx)
77 : GuestDnDSourceTask(pSource)
78 , mpCtx(pCtx) { }
79
80 virtual ~RecvDataTask(void) { }
81
82 PRECVDATACTX getCtx(void) { return mpCtx; }
83
84protected:
85
86 /** Pointer to receive data context. */
87 PRECVDATACTX mpCtx;
88};
89
90// constructor / destructor
91/////////////////////////////////////////////////////////////////////////////
92
93DEFINE_EMPTY_CTOR_DTOR(GuestDnDSource)
94
95HRESULT GuestDnDSource::FinalConstruct(void)
96{
97 /* Set the maximum block size this source can handle to 64K. This always has
98 * been hardcoded until now. */
99 /* Note: Never ever rely on information from the guest; the host dictates what and
100 * how to do something, so try to negogiate a sensible value here later. */
101 mData.mcbBlockSize = _64K; /** @todo Make this configurable. */
102 mData.mfDropIsPending = false;
103
104 LogFlowThisFunc(("\n"));
105 return BaseFinalConstruct();
106}
107
108void GuestDnDSource::FinalRelease(void)
109{
110 LogFlowThisFuncEnter();
111 uninit();
112 BaseFinalRelease();
113 LogFlowThisFuncLeave();
114}
115
116// public initializer/uninitializer for internal purposes only
117/////////////////////////////////////////////////////////////////////////////
118
119int GuestDnDSource::init(const ComObjPtr<Guest>& pGuest)
120{
121 LogFlowThisFuncEnter();
122
123 /* Enclose the state transition NotReady->InInit->Ready. */
124 AutoInitSpan autoInitSpan(this);
125 AssertReturn(autoInitSpan.isOk(), E_FAIL);
126
127 unconst(m_pGuest) = pGuest;
128
129 /* Confirm a successful initialization when it's the case. */
130 autoInitSpan.setSucceeded();
131
132 return VINF_SUCCESS;
133}
134
135/**
136 * Uninitializes the instance.
137 * Called from FinalRelease().
138 */
139void GuestDnDSource::uninit(void)
140{
141 LogFlowThisFunc(("\n"));
142
143 /* Enclose the state transition Ready->InUninit->NotReady. */
144 AutoUninitSpan autoUninitSpan(this);
145 if (autoUninitSpan.uninitDone())
146 return;
147}
148
149// implementation of wrapped IDnDBase methods.
150/////////////////////////////////////////////////////////////////////////////
151
152HRESULT GuestDnDSource::isFormatSupported(const com::Utf8Str &aFormat, BOOL *aSupported)
153{
154#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
155 ReturnComNotImplemented();
156#else /* VBOX_WITH_DRAG_AND_DROP */
157
158 AutoCaller autoCaller(this);
159 if (FAILED(autoCaller.rc())) return autoCaller.rc();
160
161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
162
163 return GuestDnDBase::i_isFormatSupported(aFormat, aSupported);
164#endif /* VBOX_WITH_DRAG_AND_DROP */
165}
166
167HRESULT GuestDnDSource::getFormats(std::vector<com::Utf8Str> &aFormats)
168{
169#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
170 ReturnComNotImplemented();
171#else /* VBOX_WITH_DRAG_AND_DROP */
172
173 AutoCaller autoCaller(this);
174 if (FAILED(autoCaller.rc())) return autoCaller.rc();
175
176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
177
178 return GuestDnDBase::i_getFormats(aFormats);
179#endif /* VBOX_WITH_DRAG_AND_DROP */
180}
181
182HRESULT GuestDnDSource::addFormats(const std::vector<com::Utf8Str> &aFormats)
183{
184#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
185 ReturnComNotImplemented();
186#else /* VBOX_WITH_DRAG_AND_DROP */
187
188 AutoCaller autoCaller(this);
189 if (FAILED(autoCaller.rc())) return autoCaller.rc();
190
191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
192
193 return GuestDnDBase::i_addFormats(aFormats);
194#endif /* VBOX_WITH_DRAG_AND_DROP */
195}
196
197HRESULT GuestDnDSource::removeFormats(const std::vector<com::Utf8Str> &aFormats)
198{
199#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
200 ReturnComNotImplemented();
201#else /* VBOX_WITH_DRAG_AND_DROP */
202
203 AutoCaller autoCaller(this);
204 if (FAILED(autoCaller.rc())) return autoCaller.rc();
205
206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
207
208 return GuestDnDBase::i_removeFormats(aFormats);
209#endif /* VBOX_WITH_DRAG_AND_DROP */
210}
211
212HRESULT GuestDnDSource::getProtocolVersion(ULONG *aProtocolVersion)
213{
214#if !defined(VBOX_WITH_DRAG_AND_DROP)
215 ReturnComNotImplemented();
216#else /* VBOX_WITH_DRAG_AND_DROP */
217
218 AutoCaller autoCaller(this);
219 if (FAILED(autoCaller.rc())) return autoCaller.rc();
220
221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
222
223 return GuestDnDBase::i_getProtocolVersion(aProtocolVersion);
224#endif /* VBOX_WITH_DRAG_AND_DROP */
225}
226
227// implementation of wrapped IDnDTarget methods.
228/////////////////////////////////////////////////////////////////////////////
229
230HRESULT GuestDnDSource::dragIsPending(ULONG uScreenId,
231 std::vector<com::Utf8Str> &aFormats,
232 std::vector<DnDAction_T> &aAllowedActions,
233 DnDAction_T *aDefaultAction)
234{
235#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
236 ReturnComNotImplemented();
237#else /* VBOX_WITH_DRAG_AND_DROP */
238
239 AutoCaller autoCaller(this);
240 if (FAILED(autoCaller.rc())) return autoCaller.rc();
241
242 /* Determine guest DnD protocol to use. */
243 GuestDnDBase::getProtocolVersion(&mDataBase.mProtocolVersion);
244
245 /* Default is ignoring the action. */
246 DnDAction_T defaultAction = DnDAction_Ignore;
247
248 HRESULT hr = S_OK;
249
250 GuestDnDMsg Msg;
251 Msg.setType(DragAndDropSvc::HOST_DND_GH_REQ_PENDING);
252 Msg.setNextUInt32(uScreenId);
253
254 int rc = GuestDnDInst()->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
255 if (RT_SUCCESS(rc))
256 {
257 bool fFetchResult = true;
258 GuestDnDResponse *pResp = GuestDnDInst()->response();
259 if (pResp)
260 {
261 if (pResp->waitForGuestResponse() == VERR_TIMEOUT)
262 fFetchResult = false;
263
264 if (isDnDIgnoreAction(pResp->defAction()))
265 fFetchResult = false;
266
267 /* Fetch the default action to use. */
268 if (fFetchResult)
269 {
270 defaultAction = GuestDnD::toMainAction(pResp->defAction());
271
272 GuestDnD::toFormatVector(m_strFormats, pResp->format(), aFormats);
273 GuestDnD::toMainActions(pResp->allActions(), aAllowedActions);
274 }
275 }
276
277 if (aDefaultAction)
278 *aDefaultAction = defaultAction;
279 }
280
281 if (RT_FAILURE(rc))
282 hr = setError(VBOX_E_IPRT_ERROR,
283 tr("Error retrieving drag'n drop pending status (%Rrc)\n"), rc);
284
285 LogFlowFunc(("hr=%Rhrc, defaultAction=0x%x\n", hr, defaultAction));
286 return hr;
287#endif /* VBOX_WITH_DRAG_AND_DROP */
288}
289
290HRESULT GuestDnDSource::drop(const com::Utf8Str &aFormat,
291 DnDAction_T aAction, ComPtr<IProgress> &aProgress)
292{
293#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
294 ReturnComNotImplemented();
295#else /* VBOX_WITH_DRAG_AND_DROP */
296
297 /* Input validation. */
298 if (RT_UNLIKELY((aFormat.c_str()) == NULL || *(aFormat.c_str()) == '\0'))
299 return setError(E_INVALIDARG, tr("No drop format specified"));
300
301 AutoCaller autoCaller(this);
302 if (FAILED(autoCaller.rc())) return autoCaller.rc();
303
304 uint32_t uAction = GuestDnD::toHGCMAction(aAction);
305 /* If there is no usable action, ignore this request. */
306 if (isDnDIgnoreAction(uAction))
307 return S_OK;
308
309 if (ASMAtomicReadBool(&mData.mfDropIsPending))
310 return setError(E_INVALIDARG, tr("Another drop operation already is in progress"));
311
312 ASMAtomicWriteBool(&mData.mfDropIsPending, true);
313
314 HRESULT hr = S_OK;
315
316 /* Note: At the moment we only support one response at a time. */
317 GuestDnDResponse *pResp = GuestDnDInst()->response();
318 if (pResp)
319 {
320 pResp->resetProgress(m_pGuest);
321
322 int rc;
323
324 try
325 {
326 mData.mRecvCtx.mpSource = this;
327 mData.mRecvCtx.mpResp = pResp;
328 mData.mRecvCtx.mFormat = aFormat;
329
330 RecvDataTask *pTask = new RecvDataTask(this, &mData.mRecvCtx);
331 AssertReturn(pTask->isOk(), pTask->getRC());
332
333 rc = RTThreadCreate(NULL, GuestDnDSource::i_receiveDataThread,
334 (void *)pTask, 0, RTTHREADTYPE_MAIN_WORKER, 0, "dndSrcRcvData");
335 if (RT_SUCCESS(rc))
336 {
337 hr = pResp->queryProgressTo(aProgress.asOutParam());
338 ComAssertComRC(hr);
339
340 /* Note: pTask is now owned by the worker thread. */
341 }
342 }
343 catch(std::bad_alloc &)
344 {
345 rc = VERR_NO_MEMORY;
346 }
347
348 /*if (RT_FAILURE(vrc)) @todo SetError(...) */
349 }
350 /** @todo SetError(...) */
351
352 ASMAtomicWriteBool(&mData.mfDropIsPending, false);
353
354 LogFlowFunc(("Returning hr=%Rhrc\n", hr));
355 return hr;
356#endif /* VBOX_WITH_DRAG_AND_DROP */
357}
358
359HRESULT GuestDnDSource::receiveData(std::vector<BYTE> &aData)
360{
361#if !defined(VBOX_WITH_DRAG_AND_DROP) || !defined(VBOX_WITH_DRAG_AND_DROP_GH)
362 ReturnComNotImplemented();
363#else /* VBOX_WITH_DRAG_AND_DROP */
364
365 /* Input validation. */
366
367 AutoCaller autoCaller(this);
368 if (FAILED(autoCaller.rc())) return autoCaller.rc();
369
370 if (ASMAtomicReadBool(&mData.mfDropIsPending))
371 return setError(E_INVALIDARG, tr("Current drop operation still running"));
372
373 PRECVDATACTX pCtx = &mData.mRecvCtx;
374
375 if (pCtx->mData.vecData.empty())
376 {
377 aData.resize(0);
378 return S_OK;
379 }
380
381 HRESULT hr = S_OK;
382 size_t cbData;
383
384 try
385 {
386 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length());
387 if (fHasURIList)
388 {
389 Utf8Str strURIs = pCtx->mURI.lstURI.RootToString(pCtx->mURI.strDropDir);
390 cbData = strURIs.length();
391
392 LogFlowFunc(("Found %zu root URIs (%zu bytes)\n", pCtx->mURI.lstURI.RootCount(), cbData));
393
394 aData.resize(cbData + 1 /* Include termination */);
395 memcpy(&aData.front(), strURIs.c_str(), cbData);
396 }
397 else
398 {
399 cbData = pCtx->mData.vecData.size();
400
401 /* Copy the data into a safe array of bytes. */
402 aData.resize(cbData);
403 memcpy(&aData.front(), &pCtx->mData.vecData[0], cbData);
404 }
405 }
406 catch (std::bad_alloc &)
407 {
408 hr = E_OUTOFMEMORY;
409 }
410
411 LogFlowFunc(("Returning cbData=%zu, hr=%Rhrc\n", cbData, hr));
412 return hr;
413#endif /* VBOX_WITH_DRAG_AND_DROP */
414}
415
416// implementation of internal methods.
417/////////////////////////////////////////////////////////////////////////////
418
419#ifdef VBOX_WITH_DRAG_AND_DROP_GH
420int GuestDnDSource::i_onReceiveData(PRECVDATACTX pCtx, const void *pvData, uint32_t cbData, uint64_t cbTotalSize)
421{
422 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
423 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
424 AssertReturn(cbData, VERR_INVALID_PARAMETER);
425 AssertReturn(cbTotalSize, VERR_INVALID_PARAMETER);
426
427 LogFlowFunc(("cbData=%RU32, cbTotalSize=%RU64\n", cbData, cbTotalSize));
428
429 int rc = VINF_SUCCESS;
430
431 try
432 {
433 if ( cbData > cbTotalSize
434 || cbData > mData.mcbBlockSize)
435 {
436 LogFlowFunc(("Data sizes invalid: cbData=%RU32, cbTotalSize=%RU64\n", cbData, cbTotalSize));
437 rc = VERR_INVALID_PARAMETER;
438 }
439 else if (cbData < pCtx->mData.vecData.size())
440 {
441 AssertMsgFailed(("New size (%RU64) is smaller than current size (%zu)\n", cbTotalSize, pCtx->mData.vecData.size()));
442 rc = VERR_INVALID_PARAMETER;
443 }
444
445 if (RT_SUCCESS(rc))
446 {
447 pCtx->mData.vecData.insert(pCtx->mData.vecData.begin(), (BYTE *)pvData, (BYTE *)pvData + cbData);
448
449 LogFlowFunc(("vecDataSize=%zu, cbData=%RU32, cbTotalSize=%RU64\n", pCtx->mData.vecData.size(), cbData, cbTotalSize));
450
451 /* Data transfer complete? */
452 Assert(cbData <= pCtx->mData.vecData.size());
453 if (cbData == pCtx->mData.vecData.size())
454 {
455 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length());
456 LogFlowFunc(("fHasURIList=%RTbool, cbTotalSize=%RU32\n", fHasURIList, cbTotalSize));
457 if (fHasURIList)
458 {
459 /* Try parsing the data as URI list. */
460 rc = pCtx->mURI.lstURI.RootFromURIData(&pCtx->mData.vecData[0], pCtx->mData.vecData.size(), 0 /* uFlags */);
461 if (RT_SUCCESS(rc))
462 {
463 pCtx->mData.cbProcessed = 0;
464
465 /* Assign new total size which also includes all paths + file
466 * data to receive from the guest. */
467 pCtx->mData.cbToProcess = cbTotalSize;
468 }
469 }
470 }
471
472 if (RT_SUCCESS(rc))
473 rc = i_updateProcess(pCtx, cbData);
474 }
475 }
476 catch (std::bad_alloc &)
477 {
478 rc = VERR_NO_MEMORY;
479 }
480
481 LogFlowFuncLeaveRC(rc);
482 return rc;
483}
484
485int GuestDnDSource::i_onReceiveDir(PRECVDATACTX pCtx, const char *pszPath, uint32_t cbPath, uint32_t fMode)
486{
487 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
488 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
489 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
490
491 LogFlowFunc(("pszPath=%s, cbPath=%zu, fMode=0x%x\n", pszPath, cbPath, fMode));
492
493 int rc;
494 char *pszDir = RTPathJoinA(pCtx->mURI.strDropDir.c_str(), pszPath);
495 if (pszDir)
496 {
497 rc = RTDirCreateFullPath(pszDir, fMode);
498 if (RT_FAILURE(rc))
499 LogRel2(("DnD: Error creating guest directory \"%s\" on the host, rc=%Rrc\n", pszDir, rc));
500
501 RTStrFree(pszDir);
502 }
503 else
504 rc = VERR_NO_MEMORY;
505
506 if (RT_SUCCESS(rc))
507 rc = i_updateProcess(pCtx, cbPath);
508
509 LogFlowFuncLeaveRC(rc);
510 return rc;
511}
512
513int GuestDnDSource::i_onReceiveFileHdr(PRECVDATACTX pCtx, const char *pszPath, uint32_t cbPath,
514 uint64_t cbSize, uint32_t fMode, uint32_t fFlags)
515{
516 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
517 AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
518 AssertReturn(cbPath, VERR_INVALID_PARAMETER);
519 AssertReturn(fMode, VERR_INVALID_PARAMETER);
520 /* fFlags are optional. */
521
522 LogFlowFunc(("pszPath=%s, cbPath=%RU32, cbSize=%RU64, fMode=0x%x, fFlags=0x%x\n", pszPath, cbPath, cbSize, fMode, fFlags));
523
524 int rc = VINF_SUCCESS;
525
526 do
527 {
528 if (!pCtx->mURI.objURI.IsComplete())
529 {
530 LogFlowFunc(("Warning: Object \"%s\" not complete yet\n", pCtx->mURI.objURI.GetDestPath().c_str()));
531 rc = VERR_INVALID_PARAMETER;
532 break;
533 }
534
535 if (pCtx->mURI.objURI.IsOpen()) /* File already opened? */
536 {
537 LogFlowFunc(("Warning: Current opened object is \"%s\"\n", pCtx->mURI.objURI.GetDestPath().c_str()));
538 rc = VERR_WRONG_ORDER;
539 break;
540 }
541
542 char pszPathAbs[RTPATH_MAX];
543 rc = RTPathJoin(pszPathAbs, sizeof(pszPathAbs), pCtx->mURI.strDropDir.c_str(), pszPath);
544 if (RT_FAILURE(rc))
545 {
546 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
547 break;
548 }
549
550 rc = DnDPathSanitize(pszPathAbs, sizeof(pszPathAbs));
551 if (RT_FAILURE(rc))
552 {
553 LogFlowFunc(("Warning: Rebasing current file failed with rc=%Rrc\n", rc));
554 break;
555 }
556
557 LogFunc(("Rebased to: %s\n", pszPathAbs));
558
559 /** @todo Add sparse file support based on fFlags? (Use Open(..., fFlags | SPARSE). */
560 /** @todo Add fMode to opening flags. */
561 rc = pCtx->mURI.objURI.OpenEx(pszPathAbs, DnDURIObject::File, DnDURIObject::Target,
562 RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE);
563 if (RT_SUCCESS(rc))
564 {
565 /** @todo Unescpae path before printing. */
566 LogRel2(("DnD: Transferring file to host: %s\n", pCtx->mURI.objURI.GetDestPath().c_str()));
567
568 /* Note: Protocol v1 does not send any file sizes, so always 0. */
569 if (mDataBase.mProtocolVersion >= 2)
570 rc = pCtx->mURI.objURI.SetSize(cbSize);
571 }
572 else
573 {
574 LogRel2(("DnD: Error opening/creating guest file \"%s\" on host, rc=%Rrc\n",
575 pCtx->mURI.objURI.GetDestPath().c_str(), rc));
576 break;
577 }
578
579 } while (0);
580
581 LogFlowFuncLeaveRC(rc);
582 return rc;
583}
584
585int GuestDnDSource::i_onReceiveFileData(PRECVDATACTX pCtx, const void *pvData, uint32_t cbData)
586{
587 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
588 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
589 AssertReturn(cbData, VERR_INVALID_PARAMETER);
590
591 int rc = VINF_SUCCESS;
592
593 do
594 {
595 if (pCtx->mURI.objURI.IsComplete())
596 {
597 LogFlowFunc(("Warning: Object \"%s\" already completed\n", pCtx->mURI.objURI.GetDestPath().c_str()));
598 rc = VERR_WRONG_ORDER;
599 break;
600 }
601
602 if (!pCtx->mURI.objURI.IsOpen()) /* File opened on host? */
603 {
604 LogFlowFunc(("Warning: Object \"%s\" not opened\n", pCtx->mURI.objURI.GetDestPath().c_str()));
605 rc = VERR_WRONG_ORDER;
606 break;
607 }
608
609 uint32_t cbWritten;
610 rc = pCtx->mURI.objURI.Write(pvData, cbData, &cbWritten);
611 if (RT_SUCCESS(rc))
612 {
613 Assert(cbWritten <= cbData);
614 if (cbWritten < cbData)
615 {
616 /** @todo What to do when the host's disk is full? */
617 rc = VERR_DISK_FULL;
618 }
619
620 if (RT_SUCCESS(rc))
621 rc = i_updateProcess(pCtx, cbWritten);
622 }
623
624 if (RT_SUCCESS(rc))
625 {
626 if (pCtx->mURI.objURI.IsComplete())
627 {
628 /** @todo Sanitize path. */
629 LogRel2(("DnD: File transfer to host complete: %s\n", pCtx->mURI.objURI.GetDestPath().c_str()));
630 rc = VINF_EOF;
631
632 /* Prepare URI object for next use. */
633 pCtx->mURI.objURI.Reset();
634 }
635 }
636 else
637 LogRel(("DnD: Error: Can't write guest file to host to \"%s\": %Rrc\n", pCtx->mURI.objURI.GetDestPath().c_str(), rc));
638
639 } while (0);
640
641 LogFlowFuncLeaveRC(rc);
642 return rc;
643}
644#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
645
646int GuestDnDSource::i_receiveData(PRECVDATACTX pCtx)
647{
648 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
649
650 GuestDnD *pInst = GuestDnDInst();
651 if (!pInst)
652 return VERR_INVALID_POINTER;
653
654 GuestDnDResponse *pResp = pCtx->mpResp;
655 AssertPtr(pCtx->mpResp);
656
657 ASMAtomicWriteBool(&pCtx->mIsActive, true);
658
659 int rc = pCtx->mCallback.Reset();
660 if (RT_FAILURE(rc))
661 return rc;
662
663 pCtx->mData.vecData.clear();
664 pCtx->mData.cbToProcess = 0;
665 pCtx->mData.cbProcessed = 0;
666
667 do
668 {
669 /* Reset any old data. */
670 pResp->reset();
671 pResp->resetProgress(m_pGuest);
672
673 /* Set the format we are going to retrieve to have it around
674 * when retrieving the data later. */
675 pResp->setFormat(pCtx->mFormat);
676
677 bool fHasURIList = DnDMIMENeedsDropDir(pCtx->mFormat.c_str(), pCtx->mFormat.length());
678 LogFlowFunc(("strFormat=%s, uAction=0x%x, fHasURIList=%RTbool\n", pCtx->mFormat.c_str(), pCtx->mAction, fHasURIList));
679
680 if (fHasURIList)
681 {
682 rc = i_receiveURIData(pCtx);
683 }
684 else
685 {
686 rc = i_receiveRawData(pCtx);
687 }
688
689 } while (0);
690
691 ASMAtomicWriteBool(&pCtx->mIsActive, false);
692
693 LogFlowFuncLeaveRC(rc);
694 return rc;
695}
696
697/* static */
698DECLCALLBACK(int) GuestDnDSource::i_receiveDataThread(RTTHREAD Thread, void *pvUser)
699{
700 LogFlowFunc(("pvUser=%p\n", pvUser));
701
702 RecvDataTask *pTask = (RecvDataTask *)pvUser;
703 AssertPtrReturn(pTask, VERR_INVALID_POINTER);
704
705 const ComObjPtr<GuestDnDSource> pSource(pTask->getSource());
706 Assert(!pSource.isNull());
707
708 int rc;
709
710 AutoCaller autoCaller(pSource);
711 if (SUCCEEDED(autoCaller.rc()))
712 {
713 rc = pSource->i_receiveData(pTask->getCtx());
714 }
715 else
716 rc = VERR_COM_INVALID_OBJECT_STATE;
717
718 LogFlowFunc(("pSource=%p returning rc=%Rrc\n", (GuestDnDSource *)pSource, rc));
719
720 if (pTask)
721 delete pTask;
722 return rc;
723}
724
725int GuestDnDSource::i_receiveRawData(PRECVDATACTX pCtx)
726{
727 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
728
729 int rc;
730
731 GuestDnDResponse *pResp = pCtx->mpResp;
732 AssertPtr(pCtx->mpResp);
733
734 GuestDnD *pInst = GuestDnDInst();
735 if (!pInst)
736 return VERR_INVALID_POINTER;
737
738#define REGISTER_CALLBACK(x) \
739 rc = pResp->setCallback(x, i_receiveRawDataCallback, pCtx); \
740 if (RT_FAILURE(rc)) \
741 return rc;
742
743#define UNREGISTER_CALLBACK(x) \
744 rc = pCtx->mpResp->setCallback(x, NULL); \
745 AssertRC(rc);
746
747 /*
748 * Register callbacks.
749 */
750 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
751 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
752
753 do
754 {
755 /*
756 * Receive the raw data.
757 */
758 GuestDnDMsg Msg;
759 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED);
760 Msg.setNextPointer((void*)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
761 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
762 Msg.setNextUInt32(pCtx->mAction);
763
764 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
765 * the host and therefore now waiting for the actual raw data. */
766 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
767 if (RT_SUCCESS(rc))
768 {
769 /*
770 * Wait until our callback i_receiveRawDataCallback triggered the
771 * wait event.
772 */
773 LogFlowFunc(("Waiting for raw data callback ...\n"));
774 rc = pCtx->mCallback.Wait(RT_INDEFINITE_WAIT);
775 LogFlowFunc(("Raw callback done, rc=%Rrc\n", rc));
776 }
777
778 } while (0);
779
780 /*
781 * Unregister callbacks.
782 */
783 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
784 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
785
786#undef REGISTER_CALLBACK
787#undef UNREGISTER_CALLBACK
788
789 LogFlowFuncLeaveRC(rc);
790 return rc;
791}
792
793int GuestDnDSource::i_receiveURIData(PRECVDATACTX pCtx)
794{
795 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
796
797 int rc;
798
799 GuestDnDResponse *pResp = pCtx->mpResp;
800 AssertPtr(pCtx->mpResp);
801
802 GuestDnD *pInst = GuestDnDInst();
803 if (!pInst)
804 return VERR_INVALID_POINTER;
805
806#define REGISTER_CALLBACK(x) \
807 rc = pResp->setCallback(x, i_receiveURIDataCallback, pCtx); \
808 if (RT_FAILURE(rc)) \
809 return rc;
810
811#define UNREGISTER_CALLBACK(x) \
812 rc = pResp->setCallback(x, NULL); \
813 AssertRC(rc);
814
815 /*
816 * Register callbacks.
817 */
818 /* Guest callbacks. */
819 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
820 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
821 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR);
822 if (mDataBase.mProtocolVersion >= 2)
823 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR);
824 REGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA);
825
826 do
827 {
828 char szDropDir[RTPATH_MAX];
829 rc = DnDDirCreateDroppedFiles(szDropDir, sizeof(szDropDir));
830 LogFlowFunc(("rc=%Rrc, szDropDir=%s\n", rc, szDropDir));
831 if (RT_FAILURE(rc))
832 break;
833
834 pCtx->mURI.strDropDir = szDropDir; /** @todo Keep directory handle open? */
835
836 /*
837 * Receive the URI list.
838 */
839 GuestDnDMsg Msg;
840 Msg.setType(DragAndDropSvc::HOST_DND_GH_EVT_DROPPED);
841 Msg.setNextPointer((void*)pCtx->mFormat.c_str(), (uint32_t)pCtx->mFormat.length() + 1);
842 Msg.setNextUInt32((uint32_t)pCtx->mFormat.length() + 1);
843 Msg.setNextUInt32(pCtx->mAction);
844
845 /* Make the initial call to the guest by telling that we initiated the "dropped" event on
846 * the host and therefore now waiting for the actual URI actual data. */
847 rc = pInst->hostCall(Msg.getType(), Msg.getCount(), Msg.getParms());
848 if (RT_SUCCESS(rc))
849 {
850 /*
851 * Wait until our callback i_receiveURIDataCallback triggered the
852 * wait event.
853 */
854 LogFlowFunc(("Waiting for URI callback ...\n"));
855 rc = pCtx->mCallback.Wait(RT_INDEFINITE_WAIT);
856 LogFlowFunc(("URI callback done, rc=%Rrc\n", rc));
857 }
858
859 } while (0);
860
861 /*
862 * Unregister callbacks.
863 */
864 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_EVT_ERROR);
865 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DATA);
866 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_DIR);
867 if (mDataBase.mProtocolVersion >= 2)
868 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR);
869 UNREGISTER_CALLBACK(DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA);
870
871#undef REGISTER_CALLBACK
872#undef UNREGISTER_CALLBACK
873
874 if (RT_FAILURE(rc))
875 {
876 LogFlowFunc(("Rolling back ...\n"));
877
878 /* Rollback by removing any stuff created. */
879 for (size_t i = 0; i < pCtx->mURI.lstFiles.size(); ++i)
880 RTFileDelete(pCtx->mURI.lstFiles.at(i).c_str());
881 for (size_t i = 0; i < pCtx->mURI.lstDirs.size(); ++i)
882 RTDirRemove(pCtx->mURI.lstDirs.at(i).c_str());
883 }
884
885 /* Try removing (hopefully) empty drop directory in any case. */
886 if (pCtx->mURI.strDropDir.isNotEmpty())
887 RTDirRemove(pCtx->mURI.strDropDir.c_str());
888
889 LogFlowFuncLeaveRC(rc);
890 return rc;
891}
892
893/* static */
894DECLCALLBACK(int) GuestDnDSource::i_receiveRawDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
895{
896 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
897 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
898
899 GuestDnDSource *pThis = pCtx->mpSource;
900 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
901
902 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
903
904 int rc = VINF_SUCCESS;
905 bool fNotify = false;
906
907 switch (uMsg)
908 {
909#ifdef VBOX_WITH_DRAG_AND_DROP_GH
910 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
911 {
912 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
913 AssertPtr(pCBData);
914 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
915 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
916
917 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize);
918 break;
919 }
920 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
921 {
922 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
923 AssertPtr(pCBData);
924 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
925 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
926
927 pCtx->mpResp->reset();
928 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
929 if (RT_SUCCESS(rc))
930 rc = pCBData->rc;
931 break;
932 }
933#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
934 default:
935 rc = VERR_NOT_SUPPORTED;
936 break;
937 }
938
939 if (RT_FAILURE(rc))
940 fNotify = true;
941
942 if (fNotify)
943 {
944 int rc2 = pCtx->mCallback.Notify(rc);
945 AssertRC(rc2);
946 }
947
948 LogFlowFuncLeaveRC(rc);
949 return rc; /* Tell the guest. */
950}
951
952/* static */
953DECLCALLBACK(int) GuestDnDSource::i_receiveURIDataCallback(uint32_t uMsg, void *pvParms, size_t cbParms, void *pvUser)
954{
955 PRECVDATACTX pCtx = (PRECVDATACTX)pvUser;
956 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
957
958 GuestDnDSource *pThis = pCtx->mpSource;
959 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
960
961 LogFlowFunc(("pThis=%p, uMsg=%RU32\n", pThis, uMsg));
962
963 int rc = VINF_SUCCESS;
964 bool fNotify = false;
965
966 switch (uMsg)
967 {
968#ifdef VBOX_WITH_DRAG_AND_DROP_GH
969 case DragAndDropSvc::GUEST_DND_GH_SND_DATA:
970 {
971 DragAndDropSvc::PVBOXDNDCBSNDDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDATADATA>(pvParms);
972 AssertPtr(pCBData);
973 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDATADATA) == cbParms, VERR_INVALID_PARAMETER);
974 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
975
976 rc = pThis->i_onReceiveData(pCtx, pCBData->pvData, pCBData->cbData, pCBData->cbTotalSize);
977 break;
978 }
979 case DragAndDropSvc::GUEST_DND_GH_SND_DIR:
980 {
981 DragAndDropSvc::PVBOXDNDCBSNDDIRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDDIRDATA>(pvParms);
982 AssertPtr(pCBData);
983 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDDIRDATA) == cbParms, VERR_INVALID_PARAMETER);
984 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_DIR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
985
986 rc = pThis->i_onReceiveDir(pCtx, pCBData->pszPath, pCBData->cbPath, pCBData->fMode);
987 break;
988 }
989 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_HDR:
990 {
991 DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEHDRDATA>(pvParms);
992 AssertPtr(pCBData);
993 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEHDRDATA) == cbParms, VERR_INVALID_PARAMETER);
994 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_HDR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
995
996 rc = pThis->i_onReceiveFileHdr(pCtx, pCBData->pszFilePath, pCBData->cbFilePath,
997 pCBData->cbSize, pCBData->fMode, pCBData->fFlags);
998 break;
999 }
1000 case DragAndDropSvc::GUEST_DND_GH_SND_FILE_DATA:
1001 {
1002 DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBSNDFILEDATADATA>(pvParms);
1003 AssertPtr(pCBData);
1004 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBSNDFILEDATADATA) == cbParms, VERR_INVALID_PARAMETER);
1005 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_SND_FILE_DATA == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1006
1007 if (pThis->mDataBase.mProtocolVersion <= 1)
1008 {
1009 /**
1010 * Notes for protocol v1 (< VBox 5.0):
1011 * - Every time this command is being sent it includes the file header,
1012 * so just process both calls here.
1013 * - There was no information whatsoever about the total file size; the old code only
1014 * appended data to the desired file. So just pass 0 as cbSize.
1015 */
1016 rc = pThis->i_onReceiveFileHdr(pCtx,
1017 pCBData->u.v1.pszFilePath, pCBData->u.v1.cbFilePath,
1018 0 /* cbSize */, pCBData->u.v1.fMode, 0 /* fFlags */);
1019 if (RT_SUCCESS(rc))
1020 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
1021 }
1022 else /* Protocol v2 and up. */
1023 rc = pThis->i_onReceiveFileData(pCtx, pCBData->pvData, pCBData->cbData);
1024 break;
1025 }
1026 case DragAndDropSvc::GUEST_DND_GH_EVT_ERROR:
1027 {
1028 DragAndDropSvc::PVBOXDNDCBEVTERRORDATA pCBData = reinterpret_cast<DragAndDropSvc::PVBOXDNDCBEVTERRORDATA>(pvParms);
1029 AssertPtr(pCBData);
1030 AssertReturn(sizeof(DragAndDropSvc::VBOXDNDCBEVTERRORDATA) == cbParms, VERR_INVALID_PARAMETER);
1031 AssertReturn(DragAndDropSvc::CB_MAGIC_DND_GH_EVT_ERROR == pCBData->hdr.u32Magic, VERR_INVALID_PARAMETER);
1032
1033 pCtx->mpResp->reset();
1034 rc = pCtx->mpResp->setProgress(100, DragAndDropSvc::DND_PROGRESS_ERROR, pCBData->rc);
1035 if (RT_SUCCESS(rc))
1036 rc = pCBData->rc;
1037 break;
1038 }
1039#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
1040 default:
1041 rc = VERR_NOT_SUPPORTED;
1042 break;
1043 }
1044
1045 if (RT_FAILURE(rc))
1046 fNotify = true;
1047
1048 /* All URI data processed? */
1049 if (pCtx->mData.cbProcessed >= pCtx->mData.cbToProcess)
1050 {
1051 Assert(pCtx->mData.cbProcessed == pCtx->mData.cbToProcess);
1052 fNotify = true;
1053 }
1054
1055 LogFlowFunc(("cbProcessed=%RU64, cbToProcess=%RU64, fNotify=%RTbool\n",
1056 pCtx->mData.cbProcessed, pCtx->mData.cbToProcess, fNotify));
1057
1058 if (fNotify)
1059 {
1060 int rc2 = pCtx->mCallback.Notify(rc);
1061 AssertRC(rc2);
1062 }
1063
1064 LogFlowFuncLeaveRC(rc);
1065 return rc; /* Tell the guest. */
1066}
1067
1068int GuestDnDSource::i_updateProcess(PRECVDATACTX pCtx, uint32_t cbDataAdd)
1069{
1070 AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
1071
1072 pCtx->mData.cbProcessed += cbDataAdd;
1073 Assert(pCtx->mData.cbProcessed <= pCtx->mData.cbToProcess);
1074
1075 int64_t cbTotal = pCtx->mData.cbToProcess;
1076 uint8_t uPercent = pCtx->mData.cbProcessed * 100 / (cbTotal ? cbTotal : 1);
1077
1078 int rc = pCtx->mpResp->setProgress(uPercent,
1079 uPercent >= 100
1080 ? DragAndDropSvc::DND_PROGRESS_COMPLETE
1081 : DragAndDropSvc::DND_PROGRESS_RUNNING);
1082 return rc;
1083}
1084
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette