VirtualBox

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

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

DnD: Make source/target blocksizes configurable.

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