VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIDnDHandler.cpp

Last change on this file was 106061, checked in by vboxsync, 7 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.5 KB
Line 
1/* $Id: UIDnDHandler.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UIDnDHandler class implementation.
4 */
5
6/*
7 * Copyright (C) 2011-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QApplication>
30#include <QDrag>
31#include <QKeyEvent>
32#include <QStringList>
33#include <QTimer>
34#include <QUrl>
35#include <QWidget>
36
37/* VirtualBox interface declarations: */
38#include <VBox/com/VirtualBox.h>
39
40/* GUI includes: */
41#include "UIDnDHandler.h"
42#ifdef VBOX_WITH_DRAG_AND_DROP_GH
43#include "CDnDSource.h"
44#ifdef RT_OS_WINDOWS
45# include "UIDnDDataObject_win.h"
46# include "UIDnDDropSource_win.h"
47#endif
48#include "UIDnDMIMEData.h"
49#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
50#include "UIMachine.h"
51#include "UIMessageCenter.h"
52#include "UISession.h"
53
54/* COM includes: */
55#include "CConsole.h"
56#include "CGuest.h"
57#include "CGuestDnDSource.h"
58#include "CGuestDnDTarget.h"
59#include "CSession.h"
60
61#ifdef LOG_GROUP
62 #undef LOG_GROUP
63#endif
64#define LOG_GROUP LOG_GROUP_GUEST_DND
65#include <VBox/log.h>
66#include <iprt/err.h>
67
68#if 0
69# ifdef DEBUG
70# include <QTextStream>
71# include <QFile>
72/** Enable this to log debug output of a Qt debug build to a file defined by DEBUG_DND_QT_LOGFILE. */
73# define DEBUG_DND_QT
74# ifdef RT_OS_WINDOWS
75# define DEBUG_DND_QT_LOGFILE "c:\\temp\\VBoxQt.log"
76# else
77# define DEBUG_DND_QT_LOGFILE "/var/tmp/vboxqt.log"
78# endif
79# endif /* DEBUG */
80#endif
81
82
83UIDnDHandler::UIDnDHandler(UIMachine *pMachine, QWidget *pParent)
84 : m_pMachine(pMachine)
85 , m_pParent(pParent)
86 , m_fDataRetrieved(false)
87#ifdef RT_OS_WINDOWS
88 , m_dwIntegrityLevel(0)
89#else
90 , m_pMIMEData(NULL)
91#endif
92{
93 AssertPtr(m_pMachine);
94 if (m_pMachine)
95 {
96 CGuest comGuest = m_pMachine->uisession()->guest();
97 if (comGuest.isNotNull())
98 {
99 m_dndSource = static_cast<CDnDSource>(comGuest.GetDnDSource());
100 m_dndTarget = static_cast<CDnDTarget>(comGuest.GetDnDTarget());
101 }
102 }
103}
104
105UIDnDHandler::~UIDnDHandler(void)
106{
107}
108
109/*
110 * Frontend -> Target.
111 */
112
113Qt::DropAction UIDnDHandler::dragEnter(ulong screenID, int x, int y,
114 Qt::DropAction proposedAction, Qt::DropActions possibleActions,
115 const QMimeData *pMimeData)
116{
117 LogFlowFunc(("screenID=%RU32, x=%d, y=%d, action=%ld\n", screenID, x, y, toVBoxDnDAction(proposedAction)));
118
119 /* Ask the guest for starting a DnD event. */
120 KDnDAction result = m_dndTarget.Enter(screenID,
121 x,
122 y,
123 toVBoxDnDAction(proposedAction),
124 toVBoxDnDActions(possibleActions),
125 pMimeData->formats().toVector());
126 if (m_dndTarget.isOk())
127 {
128 return toQtDnDAction(result);
129 }
130
131 msgCenter().cannotDropDataToGuest(m_dndTarget, m_pParent);
132 return Qt::IgnoreAction;
133}
134
135Qt::DropAction UIDnDHandler::dragMove(ulong screenID, int x, int y,
136 Qt::DropAction proposedAction, Qt::DropActions possibleActions,
137 const QMimeData *pMimeData)
138{
139 LogFlowFunc(("screenID=%RU32, x=%d, y=%d, action=%ld\n", screenID, x, y, toVBoxDnDAction(proposedAction)));
140
141 if (!m_dndTarget.isOk()) /* Don't try any further if we got an error before. */
142 return Qt::IgnoreAction;
143
144 /* Notify the guest that the mouse has been moved while doing
145 * a drag'n drop operation. */
146 KDnDAction result = m_dndTarget.Move(screenID,
147 x,
148 y,
149 toVBoxDnDAction(proposedAction),
150 toVBoxDnDActions(possibleActions),
151 pMimeData->formats().toVector());
152 if (m_dndTarget.isOk())
153 return toQtDnDAction(result);
154
155 msgCenter().cannotDropDataToGuest(m_dndTarget, m_pParent);
156 return Qt::IgnoreAction;
157}
158
159Qt::DropAction UIDnDHandler::dragDrop(ulong screenID, int x, int y,
160 Qt::DropAction proposedAction, Qt::DropActions possibleActions,
161 const QMimeData *pMimeData)
162{
163 LogFlowFunc(("screenID=%RU32, x=%d, y=%d, action=%ld\n", screenID, x, y, toVBoxDnDAction(proposedAction)));
164
165 if (!m_dndTarget.isOk()) /* Don't try any further if we got an error before. */
166 return Qt::IgnoreAction;
167
168 /* The format the guest requests. */
169 QString strFormat;
170 /* Ask the guest for dropping data. */
171 KDnDAction enmResult = m_dndTarget.Drop(screenID,
172 x,
173 y,
174 toVBoxDnDAction(proposedAction),
175 toVBoxDnDActions(possibleActions),
176 pMimeData->formats().toVector(), strFormat);
177 if (!m_dndTarget.isOk())
178 {
179 msgCenter().cannotDropDataToGuest(m_dndTarget, m_pParent);
180 }
181 else if (enmResult != KDnDAction_Ignore) /* Has the guest accepted the drop event? */
182 {
183 LogRel2(("DnD: Guest requested format '%s'\n", strFormat.toUtf8().constData()));
184 LogRel2(("DnD: The host offered %d formats:\n", pMimeData->formats().size()));
185
186#if 0
187 QStringList::const_iterator itFmt = pMimeData->formats().constBegin();
188 while (itFmt != pMimeData->formats().constEnd())
189 {
190 LogRel2(("DnD:\t'%s'\n", (*itFmt).toUtf8().constData()));
191 itFmt++;
192 }
193#endif
194 QByteArray arrBytes;
195
196 /*
197 * Does the host support the format requested by the guest?
198 * Lookup the format in the MIME data object.
199 */
200 AssertPtr(pMimeData);
201 if (pMimeData->formats().indexOf(strFormat) >= 0)
202 {
203 arrBytes = pMimeData->data(strFormat);
204 Assert(!arrBytes.isEmpty());
205 }
206 /*
207 * The host does not support the format requested by the guest.
208 * This can happen if the host wants to send plan text, for example, but
209 * the guest requested something else, e.g. an URI list.
210 *
211 * In that case dictate the guest to use a fixed format from the host,
212 * so instead returning the requested URI list, return the original
213 * data format from the host. The guest has to try to deal with that then.
214 **/
215 else
216 {
217 if (pMimeData->hasText())
218 {
219 LogRel2(("DnD: Converting data to text ...\n"));
220 arrBytes = pMimeData->text().toUtf8();
221 strFormat = "text/plain;charset=utf-8";
222 }
223 else
224 {
225 LogRel(("DnD: Host formats did not offer a matching format for the guest, skipping\n"));
226 enmResult = KDnDAction_Ignore;
227 }
228 }
229
230 if (arrBytes.size()) /* Anything to send? */
231 {
232 /* Convert data to a vector. */
233 QVector<uint8_t> vecData(arrBytes.size()); /** @todo Can this throw or anything? */
234 AssertReleaseMsg(vecData.size() == arrBytes.size(), ("Drag and drop format buffer size does not match"));
235 memcpy(vecData.data(), arrBytes.constData(), arrBytes.size());
236
237 /* Send data to the guest. */
238 LogRel2(("DnD: Host is sending %d bytes of data as '%s'\n", vecData.size(), strFormat.toUtf8().constData()));
239 CProgress progress = m_dndTarget.SendData(screenID, strFormat, vecData);
240
241 if (m_dndTarget.isOk())
242 {
243 msgCenter().showModalProgressDialog(progress,
244 tr("Dropping data ..."), ":/progress_dnd_hg_90px.png",
245 m_pParent);
246
247 LogFlowFunc(("Transfer fCompleted=%RTbool, fCanceled=%RTbool, hr=%Rhrc\n",
248 progress.GetCompleted(), progress.GetCanceled(), progress.GetResultCode()));
249
250 BOOL fCanceled = progress.GetCanceled();
251 if ( !fCanceled
252 && ( !progress.isOk()
253 || progress.GetResultCode() != 0))
254 {
255 msgCenter().cannotDropDataToGuest(progress, m_pParent);
256 enmResult = KDnDAction_Ignore;
257 }
258 }
259 else
260 {
261 msgCenter().cannotDropDataToGuest(m_dndTarget, m_pParent);
262 enmResult = KDnDAction_Ignore;
263 }
264 }
265 else /* Error. */
266 enmResult = KDnDAction_Ignore;
267 }
268
269 return toQtDnDAction(enmResult);
270}
271
272void UIDnDHandler::dragLeave(ulong screenID)
273{
274 LogFlowFunc(("screenID=%RU32\n", screenID));
275
276 if (!m_dndTarget.isOk()) /* Don't try any further if we got an error before. */
277 return;
278
279 m_dndTarget.Leave(screenID);
280 if (m_dndTarget.isOk())
281 return;
282
283 msgCenter().cannotDropDataToGuest(m_dndTarget, m_pParent);
284 return;
285}
286
287#ifdef DEBUG_DND_QT
288QTextStream *g_pStrmLogQt = NULL; /* Output stream for Qt debug logging. */
289
290/* static */
291void UIDnDHandler::debugOutputQt(QtMsgType type, const QMessageLogContext &context, const QString &strMessage)
292{
293 QString strMsg;
294 switch (type)
295 {
296 case QtWarningMsg:
297 strMsg += "[W]";
298 break;
299 case QtCriticalMsg:
300 strMsg += "[C]";
301 break;
302 case QtFatalMsg:
303 strMsg += "[F]";
304 break;
305 case QtDebugMsg:
306 default:
307 strMsg += "[D]";
308 break;
309 }
310
311 if (g_pStrmLogQt)
312 (*g_pStrmLogQt) << strMsg << " " << strMessage << endl;
313}
314#endif /* DEBUG_DND_QT */
315
316/*
317 * Source -> Frontend.
318 */
319
320int UIDnDHandler::dragStartInternal(const QStringList &lstFormats,
321 Qt::DropAction defAction, Qt::DropActions actions)
322{
323 RT_NOREF(defAction);
324
325 int rc = VINF_SUCCESS;
326
327#ifdef VBOX_WITH_DRAG_AND_DROP_GH
328
329 LogFlowFunc(("defAction=0x%x\n", defAction));
330 LogFlowFunc(("Number of formats: %d\n", lstFormats.size()));
331# ifdef DEBUG
332 for (int i = 0; i < lstFormats.size(); i++)
333 LogFlowFunc(("\tFormat %d: %s\n", i, lstFormats.at(i).toUtf8().constData()));
334# endif
335
336# ifdef DEBUG_DND_QT
337 QFile *pFileDebugQt = new QFile(DEBUG_DND_QT_LOGFILE);
338 if (pFileDebugQt->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
339 {
340 g_pStrmLogQt = new QTextStream(pFileDebugQt);
341
342 qInstallMessageHandler(UIDnDHandler::debugOutputQt);
343 qDebug("========================================================================");
344 }
345# endif
346
347# ifdef RT_OS_WINDOWS
348 UIDnDDataObject *pDataObject = new UIDnDDataObject(this, lstFormats);
349 if (!pDataObject)
350 return VERR_NO_MEMORY;
351 UIDnDDropSource *pDropSource = new UIDnDDropSource(m_pParent, pDataObject);
352 if (!pDropSource)
353 return VERR_NO_MEMORY;
354
355 DWORD dwOKEffects = DROPEFFECT_NONE;
356 if (actions)
357 {
358 if (actions & Qt::CopyAction)
359 dwOKEffects |= DROPEFFECT_COPY;
360 if (actions & Qt::MoveAction)
361 dwOKEffects |= DROPEFFECT_MOVE;
362 if (actions & Qt::LinkAction)
363 dwOKEffects |= DROPEFFECT_LINK;
364 }
365
366 DWORD dwEffect;
367 LogRel2(("DnD: Starting drag and drop operation\n", dwOKEffects));
368 LogRel3(("DnD: DoDragDrop dwOKEffects=0x%x\n", dwOKEffects));
369 HRESULT hr = ::DoDragDrop(pDataObject, pDropSource, dwOKEffects, &dwEffect);
370 LogRel3(("DnD: DoDragDrop ended with hr=%Rhrc, dwEffect=%RI32\n", hr, dwEffect));
371
372 if (pDropSource)
373 pDropSource->Release();
374 if (pDataObject)
375 pDataObject->Release();
376
377# else /* !RT_OS_WINDOWS */
378
379 QDrag *pDrag = new QDrag(m_pParent);
380 if (!pDrag)
381 return VERR_NO_MEMORY;
382
383 /* Note: pMData is transferred to the QDrag object, so no need for deletion. */
384 m_pMIMEData = new UIDnDMIMEData(this, lstFormats, defAction, actions);
385 if (!m_pMIMEData)
386 {
387 delete pDrag;
388 return VERR_NO_MEMORY;
389 }
390
391 /* Inform the MIME data object of any changes in the current action. */
392 connect(pDrag, &QDrag::actionChanged,
393 m_pMIMEData, &UIDnDMIMEData::sltDropActionChanged);
394
395 /* Invoke this handler as data needs to be retrieved by our derived QMimeData class. */
396 connect(m_pMIMEData, &UIDnDMIMEData::sigGetData,
397 this, &UIDnDHandler::sltGetData);
398
399 /*
400 * Set MIME data object and start the (modal) drag'n drop operation on the host.
401 * This does not block Qt's event loop, however (on Windows it would).
402 */
403 pDrag->setMimeData(m_pMIMEData);
404 LogFlowFunc(("Executing modal drag'n drop operation ...\n"));
405
406 Qt::DropAction dropAction;
407# ifdef RT_OS_DARWIN
408# ifdef VBOX_WITH_DRAG_AND_DROP_PROMISES
409 dropAction = pDrag->exec(actions, defAction);
410# else
411 /* Without having VBOX_WITH_DRAG_AND_DROP_PROMISES enabled drag and drop
412 * will not work on OS X! It also requires some handcrafted patches within Qt
413 * (which also needs VBOX_WITH_DRAG_AND_DROP_PROMISES set there). */
414 dropAction = Qt::IgnoreAction;
415 rc = VERR_NOT_SUPPORTED;
416# endif
417# else /* !RT_OS_DARWIN */
418 dropAction = pDrag->exec(actions, defAction);
419# endif /* RT_OS_DARWIN */
420 LogRel3(("DnD: Ended with dropAction=%ld\n", UIDnDHandler::toVBoxDnDAction(dropAction)));
421
422 /* Note: The UIDnDMimeData object will not be not accessible here anymore,
423 * since QDrag had its ownership and deleted it after the (blocking)
424 * QDrag::exec() call. */
425
426 /* pDrag will be cleaned up by Qt automatically. */
427
428# endif /* !RT_OS_WINDOWS */
429
430 reset();
431
432#ifdef DEBUG_DND_QT
433 if (g_pStrmLogQt)
434 {
435 delete g_pStrmLogQt;
436 g_pStrmLogQt = NULL;
437 }
438
439 if (pFileDebugQt)
440 {
441 pFileDebugQt->close();
442 delete pFileDebugQt;
443 }
444#endif /* DEBUG_DND_QT */
445
446#else /* !VBOX_WITH_DRAG_AND_DROP_GH */
447 rc = VERR_NOT_SUPPORTED;
448#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
449
450 LogFlowFuncLeaveRC(rc);
451 return rc;
452}
453
454/**
455 * Checks whether a G->H drag'n drop operation is pending.
456 *
457 * @returns VBox status code.
458 * @retval VERR_NO_DATA if no operation is pending or an error has occurred.
459 * @retval VERR_NOT_SUPPORTED if G->H operations are not supported.
460 * @param screenID Screen ID to check pending status.
461 */
462int UIDnDHandler::dragCheckPending(ulong screenID)
463{
464 int rc;
465#ifdef VBOX_WITH_DRAG_AND_DROP_GH
466 LogFlowFunc(("screenID=%RU32\n", screenID));
467
468 /**
469 * How this works: Source is asking the target if there is any DnD
470 * operation pending, when the mouse leaves the guest window. On
471 * return there is some info about a running DnD operation
472 * (or defaultAction is KDnDAction_Ignore if not). With
473 * this information we create a Qt QDrag object with our own QMimeType
474 * implementation and call exec.
475 *
476 * Note: This function *blocks* until the actual drag'n drop operation
477 * has been finished (successfully or not)!
478 */
479
480 /* Clear our current data set. */
481 m_dataSource.lstFormats.clear();
482 m_dataSource.vecActions.clear();
483
484 /* Ask the guest if there is a drag and drop operation pending (on the guest). */
485 QVector<QString> vecFormats;
486 m_dataSource.defaultAction = m_dndSource.DragIsPending(screenID, vecFormats, m_dataSource.vecActions);
487 if (!m_dndSource.isOk())
488 {
489 msgCenter().cannotDropDataToHost(m_dndSource, m_pParent);
490 return VERR_NO_DATA;
491 }
492
493 LogRelMax3(10, ("DnD: Default action is: 0x%x\n", m_dataSource.defaultAction));
494 LogRelMax3(10, ("DnD: Number of supported guest actions: %d\n", m_dataSource.vecActions.size()));
495 for (int i = 0; i < m_dataSource.vecActions.size(); i++)
496 LogRelMax3(10, ("DnD: \tAction %d: 0x%x\n", i, m_dataSource.vecActions.at(i)));
497
498 LogRelMax3(10, ("DnD: Number of supported guest formats: %d\n", vecFormats.size()));
499 for (int i = 0; i < vecFormats.size(); i++)
500 {
501 const QString &strFmtGuest = vecFormats.at(i);
502 LogRelMax3(10, ("DnD: \tFormat %d: %s\n", i, strFmtGuest.toUtf8().constData()));
503 }
504
505 LogFlowFunc(("defaultAction=0x%x, vecFormatsSize=%d, vecActionsSize=%d\n",
506 m_dataSource.defaultAction, vecFormats.size(), m_dataSource.vecActions.size()));
507
508 if ( m_dataSource.defaultAction != KDnDAction_Ignore
509 && vecFormats.size())
510 {
511 for (int i = 0; i < vecFormats.size(); i++)
512 {
513 const QString &strFormat = vecFormats.at(i);
514 m_dataSource.lstFormats << strFormat;
515 }
516
517 rc = VINF_SUCCESS; /* There's a valid pending drag and drop operation on the guest. */
518 }
519 else /* No format data from the guest arrived yet. */
520 rc = VERR_NO_DATA;
521
522#else /* !VBOX_WITH_DRAG_AND_DROP_GH */
523 RT_NOREF(screenID);
524 rc = VERR_NOT_SUPPORTED;
525#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
526
527 LogFlowFuncLeaveRC(rc);
528 return rc;
529}
530
531int UIDnDHandler::dragStart(ulong screenID)
532{
533 int rc;
534#ifdef VBOX_WITH_DRAG_AND_DROP_GH
535
536 RT_NOREF(screenID);
537
538 LogFlowFuncEnter();
539
540 /* Sanity checks. */
541 if ( !m_dataSource.lstFormats.size()
542 || m_dataSource.defaultAction == KDnDAction_Ignore
543 || !m_dataSource.vecActions.size())
544 {
545 return VERR_INVALID_PARAMETER;
546 }
547
548 rc = dragStartInternal(m_dataSource.lstFormats,
549 toQtDnDAction(m_dataSource.defaultAction), toQtDnDActions(m_dataSource.vecActions));
550
551#else /* !VBOX_WITH_DRAG_AND_DROP_GH */
552 RT_NOREF(screenID);
553 rc = VERR_NOT_SUPPORTED;
554#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
555
556 LogFlowFuncLeaveRC(rc);
557 return rc;
558}
559
560int UIDnDHandler::dragStop(ulong screenID)
561{
562 RT_NOREF(screenID);
563 int rc;
564#ifdef VBOX_WITH_DRAG_AND_DROP_GH
565 reset();
566 rc = VINF_SUCCESS;
567#else /* !VBOX_WITH_DRAG_AND_DROP_GH */
568 rc = VERR_NOT_SUPPORTED;
569#endif /* VBOX_WITH_DRAG_AND_DROP_GH */
570 LogFlowFuncLeaveRC(rc);
571 return rc;
572}
573
574/**
575 * Initializes the drag'n drop UI handler.
576 *
577 * @returns VBox status code.
578 */
579int UIDnDHandler::init(void)
580{
581 int vrc;
582#ifdef RT_OS_WINDOWS
583
584# define CASE_INTEGRITY_LEVEL(a_Level) \
585 case a_Level: \
586 LogRel(("DnD: User Interface Privilege Isolation (UIPI) is running with %s\n", #a_Level)); \
587 break;
588
589 /*
590 * Assign and log the current process integrity interity level, so that we have a better chance of diagnosing issues
591 * when it comes to drag'n drop and UIPI (User Interface Privilege Isolation) -- a lower integrity level process
592 * cannot drag'n drop stuff to a higher integrity level one.
593 */
594 vrc = getProcessIntegrityLevel(&m_dwIntegrityLevel);
595 if (RT_SUCCESS(vrc))
596 {
597 switch (m_dwIntegrityLevel)
598 {
599 CASE_INTEGRITY_LEVEL(SECURITY_MANDATORY_UNTRUSTED_RID);
600 CASE_INTEGRITY_LEVEL(SECURITY_MANDATORY_LOW_RID);
601 CASE_INTEGRITY_LEVEL(SECURITY_MANDATORY_MEDIUM_RID);
602 CASE_INTEGRITY_LEVEL(SECURITY_MANDATORY_HIGH_RID);
603 CASE_INTEGRITY_LEVEL(SECURITY_MANDATORY_SYSTEM_RID);
604 CASE_INTEGRITY_LEVEL(SECURITY_MANDATORY_PROTECTED_PROCESS_RID);
605 default:
606 break;
607 }
608
609 if (m_dwIntegrityLevel > SECURITY_MANDATORY_MEDIUM_RID)
610 LogRel(("DnD: Warning: The VM process' integrity level is higher than most regular processes on the system. "
611 "This means that drag'n drop most likely will not work with other applications!\n"));
612 }
613 else
614 LogRel(("DnD: Unable to retrieve process integrity level (%Rrc) -- please report this bug!\n", vrc));
615# undef CASE_INTEGRITY_LEVEL
616
617#else /* ! RT_OS_WINDOWS */
618 vrc = VINF_SUCCESS;
619#endif /* RT_OS_WINDOWS */
620
621 return vrc;
622}
623
624void UIDnDHandler::reset(void)
625{
626 LogFlowFuncEnter();
627
628 m_fDataRetrieved = false;
629
630#ifdef RT_OS_WINDOWS
631 m_dwIntegrityLevel = 0;
632#endif
633}
634
635#ifdef RT_OS_WINDOWS
636/**
637 * Returns the process' current integrity level.
638 *
639 * @returns VBox status code.
640 * @param pdwIntegrityLevel Where to return the detected process integrity level on success.
641 */
642/* static */
643int UIDnDHandler::getProcessIntegrityLevel(DWORD *pdwIntegrityLevel)
644{
645 AssertPtrReturn(pdwIntegrityLevel, VERR_INVALID_POINTER);
646
647 int vrc = VINF_SUCCESS;
648
649# define PRINT_AND_ASSIGN_LAST_ERROR(a_Msg) \
650 { \
651 dwLastErr = GetLastError(); \
652 vrc = RTErrConvertFromWin32(dwLastErr); \
653 LogRel(("DnD: %s: %Rrc (%#x)\n", a_Msg, vrc, dwLastErr)); \
654 }
655
656 DWORD dwLastErr = 0;
657
658 DWORD cb;
659 HANDLE hToken;
660 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
661 {
662 PRINT_AND_ASSIGN_LAST_ERROR("OpenProcessToken failed");
663 return vrc;
664 }
665
666 if ( !GetTokenInformation(hToken, TokenIntegrityLevel, NULL, 0, &cb)
667 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
668 {
669 PSID_AND_ATTRIBUTES pSidAndAttr = (PSID_AND_ATTRIBUTES)RTMemAlloc(cb);
670 if (GetTokenInformation(hToken, TokenIntegrityLevel, pSidAndAttr, cb, &cb))
671 {
672 *pdwIntegrityLevel = *GetSidSubAuthority(pSidAndAttr->Sid, *GetSidSubAuthorityCount(pSidAndAttr->Sid) - 1U);
673 }
674 else
675 PRINT_AND_ASSIGN_LAST_ERROR("GetTokenInformation(2) failed");
676 RTMemFree(pSidAndAttr);
677 }
678 else if ( GetLastError() == ERROR_INVALID_PARAMETER
679 || GetLastError() == ERROR_NOT_SUPPORTED)
680 {
681 /* Should never show, as we at least require Windows 7 nowadays on Windows hosts. */
682 PRINT_AND_ASSIGN_LAST_ERROR("Querying process integrity level not supported");
683 }
684 else
685 PRINT_AND_ASSIGN_LAST_ERROR("GetTokenInformation(1) failed");
686
687# undef PRINT_AND_ASSIGN_LAST_ERROR
688
689 CloseHandle(hToken);
690 return vrc;
691}
692#endif /* RT_OS_WINDOWS */
693
694int UIDnDHandler::retrieveData( Qt::DropAction dropAction,
695 const QString &strMIMEType,
696 QVector<uint8_t> &vecData)
697{
698 /** @todo r=andy Locking required? */
699
700 if (!strMIMEType.compare("application/x-qt-mime-type-name", Qt::CaseInsensitive))
701 return VINF_SUCCESS;
702
703 int rc;
704 if (!m_fDataRetrieved)
705 {
706 /*
707 * Retrieve the actual data from the guest.
708 */
709 rc = retrieveDataInternal(dropAction, strMIMEType, m_vecData);
710 if (RT_FAILURE(rc))
711 LogRel3(("DnD: Receiving data failed: %Rrc\n", rc));
712 else
713 m_fDataRetrieved = true;
714 }
715 else /* Data already received, supply cached version. */
716 rc = VINF_SUCCESS;
717
718 if (RT_SUCCESS(rc))
719 vecData = m_vecData;
720
721 return rc;
722}
723
724int UIDnDHandler::retrieveData( Qt::DropAction dropAction,
725 const QString &strMIMEType,
726 QMetaType::Type vaType,
727 QVariant &vaData)
728{
729 QVector<uint8_t> vecData;
730 int rc = retrieveData(dropAction, strMIMEType, vecData);
731 if (RT_SUCCESS(rc))
732 {
733 /* If no/an invalid variant is set, try to guess the variant type.
734 * This can happen on OS X. */
735 if (vaType == QMetaType::UnknownType)
736 vaType = UIDnDMIMEData::getMetaType(strMIMEType);
737
738 rc = UIDnDMIMEData::getDataAsVariant(vecData, strMIMEType, vaType, vaData);
739 }
740
741 LogFlowFuncLeaveRC(rc);
742 return rc;
743}
744
745int UIDnDHandler::retrieveDataInternal( Qt::DropAction dropAction,
746 const QString &strMIMEType,
747 QVector<uint8_t> &vecData)
748{
749 LogRel3(("DnD: Retrieving data from guest as '%s' (%d)\n", qPrintable(strMIMEType), dropAction));
750
751 int rc = VINF_SUCCESS;
752
753 /* Indicate to the guest that we have dropped the data on the host.
754 * The guest then will initiate the actual "drop" operation into our proxy on the guest. */
755 Assert(!m_dndSource.isNull());
756 CProgress progress = m_dndSource.Drop(strMIMEType,
757 UIDnDHandler::toVBoxDnDAction(dropAction));
758 LogFlowFunc(("Source: isOk=%RTbool\n", m_dndSource.isOk()));
759 if (m_dndSource.isOk())
760 {
761 /* Send a mouse event with released mouse buttons into the guest that triggers
762 * the "drop" event in our proxy window on the guest. */
763 m_pMachine->putMouseEvent(0, 0, 0, 0, 0);
764
765 msgCenter().showModalProgressDialog(progress,
766 tr("Retrieving data ..."), ":/progress_dnd_gh_90px.png",
767 m_pParent);
768
769 LogFlowFunc(("Progress: fCanceled=%RTbool, fCompleted=%RTbool, isOk=%RTbool, hrc=%Rhrc\n",
770 progress.GetCanceled(), progress.GetCompleted(), progress.isOk(), progress.GetResultCode()));
771
772 if (!progress.GetCanceled())
773 {
774 rc = ( progress.isOk()
775 && progress.GetResultCode() == 0)
776 ? VINF_SUCCESS : VERR_GENERAL_FAILURE; /** @todo Fudge; do a GetResultCode() to rc translation. */
777
778 if (RT_SUCCESS(rc))
779 {
780 /* After we successfully retrieved data from the source we query it from Main. */
781 vecData = m_dndSource.ReceiveData(); /** @todo QVector.size() is "int" only!? */
782 if (m_dndSource.isOk())
783 {
784 if (vecData.isEmpty())
785 rc = VERR_NO_DATA;
786 }
787 else
788 {
789 msgCenter().cannotDropDataToHost(m_dndSource, m_pParent);
790 rc = VERR_GENERAL_FAILURE; /** @todo Fudge; do a GetResultCode() to rc translation. */
791 }
792 }
793 else
794 msgCenter().cannotDropDataToHost(progress, m_pParent);
795 }
796 else /* Don't pop up a message. */
797 rc = VERR_CANCELLED;
798 }
799 else
800 {
801 msgCenter().cannotDropDataToHost(m_dndSource, m_pParent);
802 rc = VERR_GENERAL_FAILURE; /** @todo Fudge; do a GetResultCode() to rc translation. */
803 }
804
805 LogFlowFuncLeaveRC(rc);
806 return rc;
807}
808
809int UIDnDHandler::sltGetData( Qt::DropAction dropAction,
810 const QString &strMIMEType,
811 QMetaType::Type vaType,
812 QVariant &vaData)
813{
814 int rc = retrieveData(dropAction, strMIMEType, vaType, vaData);
815 LogFlowFuncLeaveRC(rc);
816 return rc;
817}
818
819/*
820 * Drag and Drop helper methods
821 */
822
823/**
824 * Static helper function to convert a Qt drop action to an internal DnD drop action.
825 *
826 * @returns Converted internal drop action.
827 * @param action Qt drop action to convert.
828 */
829/* static */
830KDnDAction UIDnDHandler::toVBoxDnDAction(Qt::DropAction action)
831{
832 if (action == Qt::CopyAction)
833 return KDnDAction_Copy;
834 if (action == Qt::MoveAction)
835 return KDnDAction_Move;
836 if (action == Qt::LinkAction)
837 return KDnDAction_Link;
838
839 return KDnDAction_Ignore;
840}
841
842/**
843 * Static helper function to convert Qt drop actions to internal DnD drop actions.
844 *
845 * @returns Vector of converted internal drop actions.
846 * @param actions Qt drop actions to convert.
847 */
848/* static */
849QVector<KDnDAction> UIDnDHandler::toVBoxDnDActions(Qt::DropActions actions)
850{
851 QVector<KDnDAction> vbActions;
852 if (actions.testFlag(Qt::IgnoreAction))
853 vbActions << KDnDAction_Ignore;
854 if (actions.testFlag(Qt::CopyAction))
855 vbActions << KDnDAction_Copy;
856 if (actions.testFlag(Qt::MoveAction))
857 vbActions << KDnDAction_Move;
858 if (actions.testFlag(Qt::LinkAction))
859 vbActions << KDnDAction_Link;
860
861 return vbActions;
862}
863
864/**
865 * Static helper function to convert an internal drop action to a Qt drop action.
866 *
867 * @returns Converted Qt drop action.
868 * @param actions Internal drop action to convert.
869 */
870/* static */
871Qt::DropAction UIDnDHandler::toQtDnDAction(KDnDAction action)
872{
873 Qt::DropAction dropAct = Qt::IgnoreAction;
874 if (action == KDnDAction_Copy)
875 dropAct = Qt::CopyAction;
876 if (action == KDnDAction_Move)
877 dropAct = Qt::MoveAction;
878 if (action == KDnDAction_Link)
879 dropAct = Qt::LinkAction;
880
881 LogFlowFunc(("dropAct=0x%x\n", dropAct));
882 return dropAct;
883}
884
885/**
886 * Static helper function to convert a vector of internal drop actions to Qt drop actions.
887 *
888 * @returns Converted Qt drop actions.
889 * @param vecActions Internal drop actions to convert.
890 */
891/* static */
892Qt::DropActions UIDnDHandler::toQtDnDActions(const QVector<KDnDAction> &vecActions)
893{
894 Qt::DropActions dropActs = Qt::IgnoreAction;
895 for (int i = 0; i < vecActions.size(); i++)
896 {
897 switch (vecActions.at(i))
898 {
899 case KDnDAction_Ignore:
900 dropActs |= Qt::IgnoreAction;
901 break;
902 case KDnDAction_Copy:
903 dropActs |= Qt::CopyAction;
904 break;
905 case KDnDAction_Move:
906 dropActs |= Qt::MoveAction;
907 break;
908 case KDnDAction_Link:
909 dropActs |= Qt::LinkAction;
910 break;
911 default:
912 break;
913 }
914 }
915
916 LogFlowFunc(("dropActions=0x%x\n", int(dropActs)));
917 return dropActs;
918}
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