VirtualBox

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

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

No unique_ptr (yet).

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