VirtualBox

source: vbox/trunk/src/VBox/Main/src-client/ConsoleVRDPServer.cpp@ 42719

Last change on this file since 42719 was 42248, checked in by vboxsync, 12 years ago

API change for SetVideoModeHint to be able to disable guest screens and to set the origin of guest screens

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 105.7 KB
Line 
1/* $Id: ConsoleVRDPServer.cpp 42248 2012-07-20 08:39:45Z vboxsync $ */
2/** @file
3 * VBox Console VRDP Helper class
4 */
5
6/*
7 * Copyright (C) 2006-2010 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#include "ConsoleVRDPServer.h"
19#include "ConsoleImpl.h"
20#include "DisplayImpl.h"
21#include "KeyboardImpl.h"
22#include "MouseImpl.h"
23#include "AudioSnifferInterface.h"
24#ifdef VBOX_WITH_EXTPACK
25# include "ExtPackManagerImpl.h"
26#endif
27#include "VMMDev.h"
28#ifdef VBOX_WITH_USB_CARDREADER
29# include "UsbCardReader.h"
30#endif
31
32#include "Global.h"
33#include "AutoCaller.h"
34#include "Logging.h"
35
36#include <iprt/asm.h>
37#include <iprt/alloca.h>
38#include <iprt/ldr.h>
39#include <iprt/param.h>
40#include <iprt/path.h>
41#include <iprt/cpp/utils.h>
42
43#include <VBox/err.h>
44#include <VBox/RemoteDesktop/VRDEOrders.h>
45#include <VBox/com/listeners.h>
46#include <VBox/HostServices/VBoxCrOpenGLSvc.h>
47
48class VRDPConsoleListener
49{
50public:
51 VRDPConsoleListener()
52 {
53 }
54
55 HRESULT init(ConsoleVRDPServer *server)
56 {
57 m_server = server;
58 return S_OK;
59 }
60
61 void uninit()
62 {
63 }
64
65 STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent * aEvent)
66 {
67 switch (aType)
68 {
69 case VBoxEventType_OnMousePointerShapeChanged:
70 {
71 ComPtr<IMousePointerShapeChangedEvent> mpscev = aEvent;
72 Assert(mpscev);
73 BOOL visible, alpha;
74 ULONG xHot, yHot, width, height;
75 com::SafeArray <BYTE> shape;
76
77 mpscev->COMGETTER(Visible)(&visible);
78 mpscev->COMGETTER(Alpha)(&alpha);
79 mpscev->COMGETTER(Xhot)(&xHot);
80 mpscev->COMGETTER(Yhot)(&yHot);
81 mpscev->COMGETTER(Width)(&width);
82 mpscev->COMGETTER(Height)(&height);
83 mpscev->COMGETTER(Shape)(ComSafeArrayAsOutParam(shape));
84
85 OnMousePointerShapeChange(visible, alpha, xHot, yHot, width, height, ComSafeArrayAsInParam(shape));
86 break;
87 }
88 case VBoxEventType_OnMouseCapabilityChanged:
89 {
90 ComPtr<IMouseCapabilityChangedEvent> mccev = aEvent;
91 Assert(mccev);
92 if (m_server)
93 {
94 BOOL fAbsoluteMouse;
95 mccev->COMGETTER(SupportsAbsolute)(&fAbsoluteMouse);
96 m_server->NotifyAbsoluteMouse(!!fAbsoluteMouse);
97 }
98 break;
99 }
100 case VBoxEventType_OnKeyboardLedsChanged:
101 {
102 ComPtr<IKeyboardLedsChangedEvent> klcev = aEvent;
103 Assert(klcev);
104
105 if (m_server)
106 {
107 BOOL fNumLock, fCapsLock, fScrollLock;
108 klcev->COMGETTER(NumLock)(&fNumLock);
109 klcev->COMGETTER(CapsLock)(&fCapsLock);
110 klcev->COMGETTER(ScrollLock)(&fScrollLock);
111 m_server->NotifyKeyboardLedsChange(fNumLock, fCapsLock, fScrollLock);
112 }
113 break;
114 }
115
116 default:
117 AssertFailed();
118 }
119
120 return S_OK;
121 }
122
123private:
124 STDMETHOD(OnMousePointerShapeChange)(BOOL visible, BOOL alpha, ULONG xHot, ULONG yHot,
125 ULONG width, ULONG height, ComSafeArrayIn(BYTE,shape));
126 ConsoleVRDPServer *m_server;
127};
128
129typedef ListenerImpl<VRDPConsoleListener, ConsoleVRDPServer*> VRDPConsoleListenerImpl;
130
131VBOX_LISTENER_DECLARE(VRDPConsoleListenerImpl)
132
133#ifdef DEBUG_sunlover
134#define LOGDUMPPTR Log
135void dumpPointer(const uint8_t *pu8Shape, uint32_t width, uint32_t height, bool fXorMaskRGB32)
136{
137 unsigned i;
138
139 const uint8_t *pu8And = pu8Shape;
140
141 for (i = 0; i < height; i++)
142 {
143 unsigned j;
144 LOGDUMPPTR(("%p: ", pu8And));
145 for (j = 0; j < (width + 7) / 8; j++)
146 {
147 unsigned k;
148 for (k = 0; k < 8; k++)
149 {
150 LOGDUMPPTR(("%d", ((*pu8And) & (1 << (7 - k)))? 1: 0));
151 }
152
153 pu8And++;
154 }
155 LOGDUMPPTR(("\n"));
156 }
157
158 if (fXorMaskRGB32)
159 {
160 uint32_t *pu32Xor = (uint32_t*)(pu8Shape + ((((width + 7) / 8) * height + 3) & ~3));
161
162 for (i = 0; i < height; i++)
163 {
164 unsigned j;
165 LOGDUMPPTR(("%p: ", pu32Xor));
166 for (j = 0; j < width; j++)
167 {
168 LOGDUMPPTR(("%08X", *pu32Xor++));
169 }
170 LOGDUMPPTR(("\n"));
171 }
172 }
173 else
174 {
175 /* RDP 24 bit RGB mask. */
176 uint8_t *pu8Xor = (uint8_t*)(pu8Shape + ((((width + 7) / 8) * height + 3) & ~3));
177 for (i = 0; i < height; i++)
178 {
179 unsigned j;
180 LOGDUMPPTR(("%p: ", pu8Xor));
181 for (j = 0; j < width; j++)
182 {
183 LOGDUMPPTR(("%02X%02X%02X", pu8Xor[2], pu8Xor[1], pu8Xor[0]));
184 pu8Xor += 3;
185 }
186 LOGDUMPPTR(("\n"));
187 }
188 }
189}
190#else
191#define dumpPointer(a, b, c, d) do {} while (0)
192#endif /* DEBUG_sunlover */
193
194static void findTopLeftBorder(const uint8_t *pu8AndMask, const uint8_t *pu8XorMask, uint32_t width, uint32_t height, uint32_t *pxSkip, uint32_t *pySkip)
195{
196 /*
197 * Find the top border of the AND mask. First assign to special value.
198 */
199 uint32_t ySkipAnd = ~0;
200
201 const uint8_t *pu8And = pu8AndMask;
202 const uint32_t cbAndRow = (width + 7) / 8;
203 const uint8_t maskLastByte = (uint8_t)( 0xFF << (cbAndRow * 8 - width) );
204
205 Assert(cbAndRow > 0);
206
207 unsigned y;
208 unsigned x;
209
210 for (y = 0; y < height && ySkipAnd == ~(uint32_t)0; y++, pu8And += cbAndRow)
211 {
212 /* For each complete byte in the row. */
213 for (x = 0; x < cbAndRow - 1; x++)
214 {
215 if (pu8And[x] != 0xFF)
216 {
217 ySkipAnd = y;
218 break;
219 }
220 }
221
222 if (ySkipAnd == ~(uint32_t)0)
223 {
224 /* Last byte. */
225 if ((pu8And[cbAndRow - 1] & maskLastByte) != maskLastByte)
226 {
227 ySkipAnd = y;
228 }
229 }
230 }
231
232 if (ySkipAnd == ~(uint32_t)0)
233 {
234 ySkipAnd = 0;
235 }
236
237 /*
238 * Find the left border of the AND mask.
239 */
240 uint32_t xSkipAnd = ~0;
241
242 /* For all bit columns. */
243 for (x = 0; x < width && xSkipAnd == ~(uint32_t)0; x++)
244 {
245 pu8And = pu8AndMask + x/8; /* Currently checking byte. */
246 uint8_t mask = 1 << (7 - x%8); /* Currently checking bit in the byte. */
247
248 for (y = ySkipAnd; y < height; y++, pu8And += cbAndRow)
249 {
250 if ((*pu8And & mask) == 0)
251 {
252 xSkipAnd = x;
253 break;
254 }
255 }
256 }
257
258 if (xSkipAnd == ~(uint32_t)0)
259 {
260 xSkipAnd = 0;
261 }
262
263 /*
264 * Find the XOR mask top border.
265 */
266 uint32_t ySkipXor = ~0;
267
268 uint32_t *pu32XorStart = (uint32_t *)pu8XorMask;
269
270 uint32_t *pu32Xor = pu32XorStart;
271
272 for (y = 0; y < height && ySkipXor == ~(uint32_t)0; y++, pu32Xor += width)
273 {
274 for (x = 0; x < width; x++)
275 {
276 if (pu32Xor[x] != 0)
277 {
278 ySkipXor = y;
279 break;
280 }
281 }
282 }
283
284 if (ySkipXor == ~(uint32_t)0)
285 {
286 ySkipXor = 0;
287 }
288
289 /*
290 * Find the left border of the XOR mask.
291 */
292 uint32_t xSkipXor = ~(uint32_t)0;
293
294 /* For all columns. */
295 for (x = 0; x < width && xSkipXor == ~(uint32_t)0; x++)
296 {
297 pu32Xor = pu32XorStart + x; /* Currently checking dword. */
298
299 for (y = ySkipXor; y < height; y++, pu32Xor += width)
300 {
301 if (*pu32Xor != 0)
302 {
303 xSkipXor = x;
304 break;
305 }
306 }
307 }
308
309 if (xSkipXor == ~(uint32_t)0)
310 {
311 xSkipXor = 0;
312 }
313
314 *pxSkip = RT_MIN(xSkipAnd, xSkipXor);
315 *pySkip = RT_MIN(ySkipAnd, ySkipXor);
316}
317
318/* Generate an AND mask for alpha pointers here, because
319 * guest driver does not do that correctly for Vista pointers.
320 * Similar fix, changing the alpha threshold, could be applied
321 * for the guest driver, but then additions reinstall would be
322 * necessary, which we try to avoid.
323 */
324static void mousePointerGenerateANDMask(uint8_t *pu8DstAndMask, int cbDstAndMask, const uint8_t *pu8SrcAlpha, int w, int h)
325{
326 memset(pu8DstAndMask, 0xFF, cbDstAndMask);
327
328 int y;
329 for (y = 0; y < h; y++)
330 {
331 uint8_t bitmask = 0x80;
332
333 int x;
334 for (x = 0; x < w; x++, bitmask >>= 1)
335 {
336 if (bitmask == 0)
337 {
338 bitmask = 0x80;
339 }
340
341 /* Whether alpha channel value is not transparent enough for the pixel to be seen. */
342 if (pu8SrcAlpha[x * 4 + 3] > 0x7f)
343 {
344 pu8DstAndMask[x / 8] &= ~bitmask;
345 }
346 }
347
348 /* Point to next source and dest scans. */
349 pu8SrcAlpha += w * 4;
350 pu8DstAndMask += (w + 7) / 8;
351 }
352}
353
354STDMETHODIMP VRDPConsoleListener::OnMousePointerShapeChange(BOOL visible,
355 BOOL alpha,
356 ULONG xHot,
357 ULONG yHot,
358 ULONG width,
359 ULONG height,
360 ComSafeArrayIn(BYTE,inShape))
361{
362 LogSunlover(("VRDPConsoleListener::OnMousePointerShapeChange: %d, %d, %lux%lu, @%lu,%lu\n", visible, alpha, width, height, xHot, yHot));
363
364 if (m_server)
365 {
366 com::SafeArray <BYTE> aShape(ComSafeArrayInArg(inShape));
367 if (aShape.size() == 0)
368 {
369 if (!visible)
370 {
371 m_server->MousePointerHide();
372 }
373 }
374 else if (width != 0 && height != 0)
375 {
376 uint8_t* shape = aShape.raw();
377
378 dumpPointer(shape, width, height, true);
379
380 if (m_server->MousePointer(alpha, xHot, yHot, width, height, shape) == VINF_SUCCESS)
381 {
382 return S_OK;
383 }
384
385 /* Pointer consists of 1 bpp AND and 24 BPP XOR masks.
386 * 'shape' AND mask followed by XOR mask.
387 * XOR mask contains 32 bit (lsb)BGR0(msb) values.
388 *
389 * We convert this to RDP color format which consist of
390 * one bpp AND mask and 24 BPP (BGR) color XOR image.
391 *
392 * RDP clients expect 8 aligned width and height of
393 * pointer (preferably 32x32).
394 *
395 * They even contain bugs which do not appear for
396 * 32x32 pointers but would appear for a 41x32 one.
397 *
398 * So set pointer size to 32x32. This can be done safely
399 * because most pointers are 32x32.
400 */
401
402 int cbDstAndMask = (((width + 7) / 8) * height + 3) & ~3;
403
404 uint8_t *pu8AndMask = shape;
405 uint8_t *pu8XorMask = shape + cbDstAndMask;
406
407 if (alpha)
408 {
409 pu8AndMask = (uint8_t*)alloca(cbDstAndMask);
410
411 mousePointerGenerateANDMask(pu8AndMask, cbDstAndMask, pu8XorMask, width, height);
412 }
413
414 /* Windows guest alpha pointers are wider than 32 pixels.
415 * Try to find out the top-left border of the pointer and
416 * then copy only meaningful bits. All complete top rows
417 * and all complete left columns where (AND == 1 && XOR == 0)
418 * are skipped. Hot spot is adjusted.
419 */
420 uint32_t ySkip = 0; /* How many rows to skip at the top. */
421 uint32_t xSkip = 0; /* How many columns to skip at the left. */
422
423 findTopLeftBorder(pu8AndMask, pu8XorMask, width, height, &xSkip, &ySkip);
424
425 /* Must not skip the hot spot. */
426 xSkip = RT_MIN(xSkip, xHot);
427 ySkip = RT_MIN(ySkip, yHot);
428
429 /*
430 * Compute size and allocate memory for the pointer.
431 */
432 const uint32_t dstwidth = 32;
433 const uint32_t dstheight = 32;
434
435 VRDECOLORPOINTER *pointer = NULL;
436
437 uint32_t dstmaskwidth = (dstwidth + 7) / 8;
438
439 uint32_t rdpmaskwidth = dstmaskwidth;
440 uint32_t rdpmasklen = dstheight * rdpmaskwidth;
441
442 uint32_t rdpdatawidth = dstwidth * 3;
443 uint32_t rdpdatalen = dstheight * rdpdatawidth;
444
445 pointer = (VRDECOLORPOINTER *)RTMemTmpAlloc(sizeof(VRDECOLORPOINTER) + rdpmasklen + rdpdatalen);
446
447 if (pointer)
448 {
449 uint8_t *maskarray = (uint8_t*)pointer + sizeof(VRDECOLORPOINTER);
450 uint8_t *dataarray = maskarray + rdpmasklen;
451
452 memset(maskarray, 0xFF, rdpmasklen);
453 memset(dataarray, 0x00, rdpdatalen);
454
455 uint32_t srcmaskwidth = (width + 7) / 8;
456 uint32_t srcdatawidth = width * 4;
457
458 /* Copy AND mask. */
459 uint8_t *src = pu8AndMask + ySkip * srcmaskwidth;
460 uint8_t *dst = maskarray + (dstheight - 1) * rdpmaskwidth;
461
462 uint32_t minheight = RT_MIN(height - ySkip, dstheight);
463 uint32_t minwidth = RT_MIN(width - xSkip, dstwidth);
464
465 unsigned x, y;
466
467 for (y = 0; y < minheight; y++)
468 {
469 for (x = 0; x < minwidth; x++)
470 {
471 uint32_t byteIndex = (x + xSkip) / 8;
472 uint32_t bitIndex = (x + xSkip) % 8;
473
474 bool bit = (src[byteIndex] & (1 << (7 - bitIndex))) != 0;
475
476 if (!bit)
477 {
478 byteIndex = x / 8;
479 bitIndex = x % 8;
480
481 dst[byteIndex] &= ~(1 << (7 - bitIndex));
482 }
483 }
484
485 src += srcmaskwidth;
486 dst -= rdpmaskwidth;
487 }
488
489 /* Point src to XOR mask */
490 src = pu8XorMask + ySkip * srcdatawidth;
491 dst = dataarray + (dstheight - 1) * rdpdatawidth;
492
493 for (y = 0; y < minheight ; y++)
494 {
495 for (x = 0; x < minwidth; x++)
496 {
497 memcpy(dst + x * 3, &src[4 * (x + xSkip)], 3);
498 }
499
500 src += srcdatawidth;
501 dst -= rdpdatawidth;
502 }
503
504 pointer->u16HotX = (uint16_t)(xHot - xSkip);
505 pointer->u16HotY = (uint16_t)(yHot - ySkip);
506
507 pointer->u16Width = (uint16_t)dstwidth;
508 pointer->u16Height = (uint16_t)dstheight;
509
510 pointer->u16MaskLen = (uint16_t)rdpmasklen;
511 pointer->u16DataLen = (uint16_t)rdpdatalen;
512
513 dumpPointer((uint8_t*)pointer + sizeof(*pointer), dstwidth, dstheight, false);
514
515 m_server->MousePointerUpdate(pointer);
516
517 RTMemTmpFree(pointer);
518 }
519 }
520 }
521
522 return S_OK;
523}
524
525
526// ConsoleVRDPServer
527////////////////////////////////////////////////////////////////////////////////
528
529RTLDRMOD ConsoleVRDPServer::mVRDPLibrary = NIL_RTLDRMOD;
530
531PFNVRDECREATESERVER ConsoleVRDPServer::mpfnVRDECreateServer = NULL;
532
533VRDEENTRYPOINTS_4 ConsoleVRDPServer::mEntryPoints; /* A copy of the server entry points. */
534VRDEENTRYPOINTS_4 *ConsoleVRDPServer::mpEntryPoints = NULL;
535
536VRDECALLBACKS_4 ConsoleVRDPServer::mCallbacks =
537{
538 { VRDE_INTERFACE_VERSION_4, sizeof(VRDECALLBACKS_4) },
539 ConsoleVRDPServer::VRDPCallbackQueryProperty,
540 ConsoleVRDPServer::VRDPCallbackClientLogon,
541 ConsoleVRDPServer::VRDPCallbackClientConnect,
542 ConsoleVRDPServer::VRDPCallbackClientDisconnect,
543 ConsoleVRDPServer::VRDPCallbackIntercept,
544 ConsoleVRDPServer::VRDPCallbackUSB,
545 ConsoleVRDPServer::VRDPCallbackClipboard,
546 ConsoleVRDPServer::VRDPCallbackFramebufferQuery,
547 ConsoleVRDPServer::VRDPCallbackFramebufferLock,
548 ConsoleVRDPServer::VRDPCallbackFramebufferUnlock,
549 ConsoleVRDPServer::VRDPCallbackInput,
550 ConsoleVRDPServer::VRDPCallbackVideoModeHint,
551 ConsoleVRDPServer::VRDECallbackAudioIn
552};
553
554DECLCALLBACK(int) ConsoleVRDPServer::VRDPCallbackQueryProperty(void *pvCallback, uint32_t index, void *pvBuffer, uint32_t cbBuffer, uint32_t *pcbOut)
555{
556 ConsoleVRDPServer *server = static_cast<ConsoleVRDPServer*>(pvCallback);
557
558 int rc = VERR_NOT_SUPPORTED;
559
560 switch (index)
561 {
562 case VRDE_QP_NETWORK_PORT:
563 {
564 /* This is obsolete, the VRDE server uses VRDE_QP_NETWORK_PORT_RANGE instead. */
565 ULONG port = 0;
566
567 if (cbBuffer >= sizeof(uint32_t))
568 {
569 *(uint32_t *)pvBuffer = (uint32_t)port;
570 rc = VINF_SUCCESS;
571 }
572 else
573 {
574 rc = VINF_BUFFER_OVERFLOW;
575 }
576
577 *pcbOut = sizeof(uint32_t);
578 } break;
579
580 case VRDE_QP_NETWORK_ADDRESS:
581 {
582 com::Bstr bstr;
583 server->mConsole->getVRDEServer()->GetVRDEProperty(Bstr("TCP/Address").raw(), bstr.asOutParam());
584
585 /* The server expects UTF8. */
586 com::Utf8Str address = bstr;
587
588 size_t cbAddress = address.length() + 1;
589
590 if (cbAddress >= 0x10000)
591 {
592 /* More than 64K seems to be an invalid address. */
593 rc = VERR_TOO_MUCH_DATA;
594 break;
595 }
596
597 if ((size_t)cbBuffer >= cbAddress)
598 {
599 memcpy(pvBuffer, address.c_str(), cbAddress);
600 rc = VINF_SUCCESS;
601 }
602 else
603 {
604 rc = VINF_BUFFER_OVERFLOW;
605 }
606
607 *pcbOut = (uint32_t)cbAddress;
608 } break;
609
610 case VRDE_QP_NUMBER_MONITORS:
611 {
612 ULONG cMonitors = 1;
613
614 server->mConsole->machine()->COMGETTER(MonitorCount)(&cMonitors);
615
616 if (cbBuffer >= sizeof(uint32_t))
617 {
618 *(uint32_t *)pvBuffer = (uint32_t)cMonitors;
619 rc = VINF_SUCCESS;
620 }
621 else
622 {
623 rc = VINF_BUFFER_OVERFLOW;
624 }
625
626 *pcbOut = sizeof(uint32_t);
627 } break;
628
629 case VRDE_QP_NETWORK_PORT_RANGE:
630 {
631 com::Bstr bstr;
632 HRESULT hrc = server->mConsole->getVRDEServer()->GetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.asOutParam());
633
634 if (hrc != S_OK)
635 {
636 bstr = "";
637 }
638
639 if (bstr == "0")
640 {
641 bstr = "3389";
642 }
643
644 /* The server expects UTF8. */
645 com::Utf8Str portRange = bstr;
646
647 size_t cbPortRange = portRange.length() + 1;
648
649 if (cbPortRange >= 0x10000)
650 {
651 /* More than 64K seems to be an invalid port range string. */
652 rc = VERR_TOO_MUCH_DATA;
653 break;
654 }
655
656 if ((size_t)cbBuffer >= cbPortRange)
657 {
658 memcpy(pvBuffer, portRange.c_str(), cbPortRange);
659 rc = VINF_SUCCESS;
660 }
661 else
662 {
663 rc = VINF_BUFFER_OVERFLOW;
664 }
665
666 *pcbOut = (uint32_t)cbPortRange;
667 } break;
668
669#ifdef VBOX_WITH_VRDP_VIDEO_CHANNEL
670 case VRDE_QP_VIDEO_CHANNEL:
671 {
672 com::Bstr bstr;
673 HRESULT hrc = server->mConsole->getVRDEServer()->GetVRDEProperty(Bstr("VideoChannel/Enabled").raw(), bstr.asOutParam());
674
675 if (hrc != S_OK)
676 {
677 bstr = "";
678 }
679
680 com::Utf8Str value = bstr;
681
682 BOOL fVideoEnabled = RTStrICmp(value.c_str(), "true") == 0
683 || RTStrICmp(value.c_str(), "1") == 0;
684
685 if (cbBuffer >= sizeof(uint32_t))
686 {
687 *(uint32_t *)pvBuffer = (uint32_t)fVideoEnabled;
688 rc = VINF_SUCCESS;
689 }
690 else
691 {
692 rc = VINF_BUFFER_OVERFLOW;
693 }
694
695 *pcbOut = sizeof(uint32_t);
696 } break;
697
698 case VRDE_QP_VIDEO_CHANNEL_QUALITY:
699 {
700 com::Bstr bstr;
701 HRESULT hrc = server->mConsole->getVRDEServer()->GetVRDEProperty(Bstr("VideoChannel/Quality").raw(), bstr.asOutParam());
702
703 if (hrc != S_OK)
704 {
705 bstr = "";
706 }
707
708 com::Utf8Str value = bstr;
709
710 ULONG ulQuality = RTStrToUInt32(value.c_str()); /* This returns 0 on invalid string which is ok. */
711
712 if (cbBuffer >= sizeof(uint32_t))
713 {
714 *(uint32_t *)pvBuffer = (uint32_t)ulQuality;
715 rc = VINF_SUCCESS;
716 }
717 else
718 {
719 rc = VINF_BUFFER_OVERFLOW;
720 }
721
722 *pcbOut = sizeof(uint32_t);
723 } break;
724
725 case VRDE_QP_VIDEO_CHANNEL_SUNFLSH:
726 {
727 ULONG ulSunFlsh = 1;
728
729 com::Bstr bstr;
730 HRESULT hrc = server->mConsole->machine()->GetExtraData(Bstr("VRDP/SunFlsh").raw(),
731 bstr.asOutParam());
732 if (hrc == S_OK && !bstr.isEmpty())
733 {
734 com::Utf8Str sunFlsh = bstr;
735 if (!sunFlsh.isEmpty())
736 {
737 ulSunFlsh = sunFlsh.toUInt32();
738 }
739 }
740
741 if (cbBuffer >= sizeof(uint32_t))
742 {
743 *(uint32_t *)pvBuffer = (uint32_t)ulSunFlsh;
744 rc = VINF_SUCCESS;
745 }
746 else
747 {
748 rc = VINF_BUFFER_OVERFLOW;
749 }
750
751 *pcbOut = sizeof(uint32_t);
752 } break;
753#endif /* VBOX_WITH_VRDP_VIDEO_CHANNEL */
754
755 case VRDE_QP_FEATURE:
756 {
757 if (cbBuffer < sizeof(VRDEFEATURE))
758 {
759 rc = VERR_INVALID_PARAMETER;
760 break;
761 }
762
763 size_t cbInfo = cbBuffer - RT_OFFSETOF(VRDEFEATURE, achInfo);
764
765 VRDEFEATURE *pFeature = (VRDEFEATURE *)pvBuffer;
766
767 size_t cchInfo = 0;
768 rc = RTStrNLenEx(pFeature->achInfo, cbInfo, &cchInfo);
769
770 if (RT_FAILURE(rc))
771 {
772 rc = VERR_INVALID_PARAMETER;
773 break;
774 }
775
776 Log(("VRDE_QP_FEATURE [%s]\n", pFeature->achInfo));
777
778 com::Bstr bstrValue;
779
780 if ( RTStrICmp(pFeature->achInfo, "Client/DisableDisplay") == 0
781 || RTStrICmp(pFeature->achInfo, "Client/DisableInput") == 0
782 || RTStrICmp(pFeature->achInfo, "Client/DisableAudio") == 0
783 || RTStrICmp(pFeature->achInfo, "Client/DisableUSB") == 0
784 || RTStrICmp(pFeature->achInfo, "Client/DisableClipboard") == 0
785 )
786 {
787 /* @todo these features should be per client. */
788 NOREF(pFeature->u32ClientId);
789
790 /* These features are mapped to "VRDE/Feature/NAME" extra data. */
791 com::Utf8Str extraData("VRDE/Feature/");
792 extraData += pFeature->achInfo;
793
794 HRESULT hrc = server->mConsole->machine()->GetExtraData(com::Bstr(extraData).raw(),
795 bstrValue.asOutParam());
796 if (FAILED(hrc) || bstrValue.isEmpty())
797 {
798 /* Also try the old "VRDP/Feature/NAME" */
799 extraData = "VRDP/Feature/";
800 extraData += pFeature->achInfo;
801
802 hrc = server->mConsole->machine()->GetExtraData(com::Bstr(extraData).raw(),
803 bstrValue.asOutParam());
804 if (FAILED(hrc))
805 {
806 rc = VERR_NOT_SUPPORTED;
807 }
808 }
809 }
810 else if (RTStrNCmp(pFeature->achInfo, "Property/", 9) == 0)
811 {
812 /* Generic properties. */
813 const char *pszPropertyName = &pFeature->achInfo[9];
814 HRESULT hrc = server->mConsole->getVRDEServer()->GetVRDEProperty(Bstr(pszPropertyName).raw(), bstrValue.asOutParam());
815 if (FAILED(hrc))
816 {
817 rc = VERR_NOT_SUPPORTED;
818 }
819 }
820 else
821 {
822 rc = VERR_NOT_SUPPORTED;
823 }
824
825 /* Copy the value string to the callers buffer. */
826 if (rc == VINF_SUCCESS)
827 {
828 com::Utf8Str value = bstrValue;
829
830 size_t cb = value.length() + 1;
831
832 if ((size_t)cbInfo >= cb)
833 {
834 memcpy(pFeature->achInfo, value.c_str(), cb);
835 }
836 else
837 {
838 rc = VINF_BUFFER_OVERFLOW;
839 }
840
841 *pcbOut = (uint32_t)cb;
842 }
843 } break;
844
845 case VRDE_SP_NETWORK_BIND_PORT:
846 {
847 if (cbBuffer != sizeof(uint32_t))
848 {
849 rc = VERR_INVALID_PARAMETER;
850 break;
851 }
852
853 ULONG port = *(uint32_t *)pvBuffer;
854
855 server->mVRDPBindPort = port;
856
857 rc = VINF_SUCCESS;
858
859 if (pcbOut)
860 {
861 *pcbOut = sizeof(uint32_t);
862 }
863
864 server->mConsole->onVRDEServerInfoChange();
865 } break;
866
867 case VRDE_SP_CLIENT_STATUS:
868 {
869 if (cbBuffer < sizeof(VRDECLIENTSTATUS))
870 {
871 rc = VERR_INVALID_PARAMETER;
872 break;
873 }
874
875 size_t cbStatus = cbBuffer - RT_UOFFSETOF(VRDECLIENTSTATUS, achStatus);
876
877 VRDECLIENTSTATUS *pStatus = (VRDECLIENTSTATUS *)pvBuffer;
878
879 if (cbBuffer < RT_UOFFSETOF(VRDECLIENTSTATUS, achStatus) + pStatus->cbStatus)
880 {
881 rc = VERR_INVALID_PARAMETER;
882 break;
883 }
884
885 size_t cchStatus = 0;
886 rc = RTStrNLenEx(pStatus->achStatus, cbStatus, &cchStatus);
887
888 if (RT_FAILURE(rc))
889 {
890 rc = VERR_INVALID_PARAMETER;
891 break;
892 }
893
894 Log(("VRDE_SP_CLIENT_STATUS [%s]\n", pStatus->achStatus));
895
896 server->mConsole->VRDPClientStatusChange(pStatus->u32ClientId, pStatus->achStatus);
897
898 rc = VINF_SUCCESS;
899
900 if (pcbOut)
901 {
902 *pcbOut = cbBuffer;
903 }
904
905 server->mConsole->onVRDEServerInfoChange();
906 } break;
907
908 default:
909 break;
910 }
911
912 return rc;
913}
914
915DECLCALLBACK(int) ConsoleVRDPServer::VRDPCallbackClientLogon(void *pvCallback, uint32_t u32ClientId, const char *pszUser, const char *pszPassword, const char *pszDomain)
916{
917 ConsoleVRDPServer *server = static_cast<ConsoleVRDPServer*>(pvCallback);
918
919 return server->mConsole->VRDPClientLogon(u32ClientId, pszUser, pszPassword, pszDomain);
920}
921
922DECLCALLBACK(void) ConsoleVRDPServer::VRDPCallbackClientConnect(void *pvCallback, uint32_t u32ClientId)
923{
924 ConsoleVRDPServer *server = static_cast<ConsoleVRDPServer*>(pvCallback);
925
926 server->mConsole->VRDPClientConnect(u32ClientId);
927}
928
929DECLCALLBACK(void) ConsoleVRDPServer::VRDPCallbackClientDisconnect(void *pvCallback, uint32_t u32ClientId, uint32_t fu32Intercepted)
930{
931 ConsoleVRDPServer *server = static_cast<ConsoleVRDPServer*>(pvCallback);
932
933 server->mConsole->VRDPClientDisconnect(u32ClientId, fu32Intercepted);
934
935 if (ASMAtomicReadU32(&server->mu32AudioInputClientId) == u32ClientId)
936 {
937 Log(("AUDIOIN: disconnected client %u\n", u32ClientId));
938 ASMAtomicWriteU32(&server->mu32AudioInputClientId, 0);
939
940 PPDMIAUDIOSNIFFERPORT pPort = server->mConsole->getAudioSniffer()->getAudioSnifferPort();
941 if (pPort)
942 {
943 pPort->pfnAudioInputIntercept(pPort, false);
944 }
945 else
946 {
947 AssertFailed();
948 }
949 }
950}
951
952DECLCALLBACK(int) ConsoleVRDPServer::VRDPCallbackIntercept(void *pvCallback, uint32_t u32ClientId, uint32_t fu32Intercept, void **ppvIntercept)
953{
954 ConsoleVRDPServer *server = static_cast<ConsoleVRDPServer*>(pvCallback);
955
956 LogFlowFunc(("%x\n", fu32Intercept));
957
958 int rc = VERR_NOT_SUPPORTED;
959
960 switch (fu32Intercept)
961 {
962 case VRDE_CLIENT_INTERCEPT_AUDIO:
963 {
964 server->mConsole->VRDPInterceptAudio(u32ClientId);
965 if (ppvIntercept)
966 {
967 *ppvIntercept = server;
968 }
969 rc = VINF_SUCCESS;
970 } break;
971
972 case VRDE_CLIENT_INTERCEPT_USB:
973 {
974 server->mConsole->VRDPInterceptUSB(u32ClientId, ppvIntercept);
975 rc = VINF_SUCCESS;
976 } break;
977
978 case VRDE_CLIENT_INTERCEPT_CLIPBOARD:
979 {
980 server->mConsole->VRDPInterceptClipboard(u32ClientId);
981 if (ppvIntercept)
982 {
983 *ppvIntercept = server;
984 }
985 rc = VINF_SUCCESS;
986 } break;
987
988 case VRDE_CLIENT_INTERCEPT_AUDIO_INPUT:
989 {
990 /* This request is processed internally by the ConsoleVRDPServer.
991 * Only one client is allowed to intercept audio input.
992 */
993 if (ASMAtomicCmpXchgU32(&server->mu32AudioInputClientId, u32ClientId, 0) == true)
994 {
995 Log(("AUDIOIN: connected client %u\n", u32ClientId));
996
997 PPDMIAUDIOSNIFFERPORT pPort = server->mConsole->getAudioSniffer()->getAudioSnifferPort();
998 if (pPort)
999 {
1000 pPort->pfnAudioInputIntercept(pPort, true);
1001 if (ppvIntercept)
1002 {
1003 *ppvIntercept = server;
1004 }
1005 }
1006 else
1007 {
1008 AssertFailed();
1009 ASMAtomicWriteU32(&server->mu32AudioInputClientId, 0);
1010 rc = VERR_NOT_SUPPORTED;
1011 }
1012 }
1013 else
1014 {
1015 Log(("AUDIOIN: ignored client %u, active client %u\n", u32ClientId, server->mu32AudioInputClientId));
1016 rc = VERR_NOT_SUPPORTED;
1017 }
1018 } break;
1019
1020 default:
1021 break;
1022 }
1023
1024 return rc;
1025}
1026
1027DECLCALLBACK(int) ConsoleVRDPServer::VRDPCallbackUSB(void *pvCallback, void *pvIntercept, uint32_t u32ClientId, uint8_t u8Code, const void *pvRet, uint32_t cbRet)
1028{
1029#ifdef VBOX_WITH_USB
1030 return USBClientResponseCallback(pvIntercept, u32ClientId, u8Code, pvRet, cbRet);
1031#else
1032 return VERR_NOT_SUPPORTED;
1033#endif
1034}
1035
1036DECLCALLBACK(int) ConsoleVRDPServer::VRDPCallbackClipboard(void *pvCallback, void *pvIntercept, uint32_t u32ClientId, uint32_t u32Function, uint32_t u32Format, const void *pvData, uint32_t cbData)
1037{
1038 return ClipboardCallback(pvIntercept, u32ClientId, u32Function, u32Format, pvData, cbData);
1039}
1040
1041DECLCALLBACK(bool) ConsoleVRDPServer::VRDPCallbackFramebufferQuery(void *pvCallback, unsigned uScreenId, VRDEFRAMEBUFFERINFO *pInfo)
1042{
1043 ConsoleVRDPServer *server = static_cast<ConsoleVRDPServer*>(pvCallback);
1044
1045 bool fAvailable = false;
1046
1047 IFramebuffer *pfb = NULL;
1048 LONG xOrigin = 0;
1049 LONG yOrigin = 0;
1050
1051 server->mConsole->getDisplay()->GetFramebuffer(uScreenId, &pfb, &xOrigin, &yOrigin);
1052
1053 if (pfb)
1054 {
1055 pfb->Lock ();
1056
1057 /* Query framebuffer parameters. */
1058 ULONG lineSize = 0;
1059 pfb->COMGETTER(BytesPerLine)(&lineSize);
1060
1061 ULONG bitsPerPixel = 0;
1062 pfb->COMGETTER(BitsPerPixel)(&bitsPerPixel);
1063
1064 BYTE *address = NULL;
1065 pfb->COMGETTER(Address)(&address);
1066
1067 ULONG height = 0;
1068 pfb->COMGETTER(Height)(&height);
1069
1070 ULONG width = 0;
1071 pfb->COMGETTER(Width)(&width);
1072
1073 /* Now fill the information as requested by the caller. */
1074 pInfo->pu8Bits = address;
1075 pInfo->xOrigin = xOrigin;
1076 pInfo->yOrigin = yOrigin;
1077 pInfo->cWidth = width;
1078 pInfo->cHeight = height;
1079 pInfo->cBitsPerPixel = bitsPerPixel;
1080 pInfo->cbLine = lineSize;
1081
1082 pfb->Unlock();
1083
1084 fAvailable = true;
1085 }
1086
1087 if (server->maFramebuffers[uScreenId])
1088 {
1089 server->maFramebuffers[uScreenId]->Release();
1090 }
1091 server->maFramebuffers[uScreenId] = pfb;
1092
1093 return fAvailable;
1094}
1095
1096DECLCALLBACK(void) ConsoleVRDPServer::VRDPCallbackFramebufferLock(void *pvCallback, unsigned uScreenId)
1097{
1098 ConsoleVRDPServer *server = static_cast<ConsoleVRDPServer*>(pvCallback);
1099
1100 if (server->maFramebuffers[uScreenId])
1101 {
1102 server->maFramebuffers[uScreenId]->Lock();
1103 }
1104}
1105
1106DECLCALLBACK(void) ConsoleVRDPServer::VRDPCallbackFramebufferUnlock(void *pvCallback, unsigned uScreenId)
1107{
1108 ConsoleVRDPServer *server = static_cast<ConsoleVRDPServer*>(pvCallback);
1109
1110 if (server->maFramebuffers[uScreenId])
1111 {
1112 server->maFramebuffers[uScreenId]->Unlock();
1113 }
1114}
1115
1116static void fixKbdLockStatus(VRDPInputSynch *pInputSynch, IKeyboard *pKeyboard)
1117{
1118 if ( pInputSynch->cGuestNumLockAdaptions
1119 && (pInputSynch->fGuestNumLock != pInputSynch->fClientNumLock))
1120 {
1121 pInputSynch->cGuestNumLockAdaptions--;
1122 pKeyboard->PutScancode(0x45);
1123 pKeyboard->PutScancode(0x45 | 0x80);
1124 }
1125 if ( pInputSynch->cGuestCapsLockAdaptions
1126 && (pInputSynch->fGuestCapsLock != pInputSynch->fClientCapsLock))
1127 {
1128 pInputSynch->cGuestCapsLockAdaptions--;
1129 pKeyboard->PutScancode(0x3a);
1130 pKeyboard->PutScancode(0x3a | 0x80);
1131 }
1132}
1133
1134DECLCALLBACK(void) ConsoleVRDPServer::VRDPCallbackInput(void *pvCallback, int type, const void *pvInput, unsigned cbInput)
1135{
1136 ConsoleVRDPServer *server = static_cast<ConsoleVRDPServer*>(pvCallback);
1137 Console *pConsole = server->mConsole;
1138
1139 switch (type)
1140 {
1141 case VRDE_INPUT_SCANCODE:
1142 {
1143 if (cbInput == sizeof(VRDEINPUTSCANCODE))
1144 {
1145 IKeyboard *pKeyboard = pConsole->getKeyboard();
1146
1147 const VRDEINPUTSCANCODE *pInputScancode = (VRDEINPUTSCANCODE *)pvInput;
1148
1149 /* Track lock keys. */
1150 if (pInputScancode->uScancode == 0x45)
1151 {
1152 server->m_InputSynch.fClientNumLock = !server->m_InputSynch.fClientNumLock;
1153 }
1154 else if (pInputScancode->uScancode == 0x3a)
1155 {
1156 server->m_InputSynch.fClientCapsLock = !server->m_InputSynch.fClientCapsLock;
1157 }
1158 else if (pInputScancode->uScancode == 0x46)
1159 {
1160 server->m_InputSynch.fClientScrollLock = !server->m_InputSynch.fClientScrollLock;
1161 }
1162 else if ((pInputScancode->uScancode & 0x80) == 0)
1163 {
1164 /* Key pressed. */
1165 fixKbdLockStatus(&server->m_InputSynch, pKeyboard);
1166 }
1167
1168 pKeyboard->PutScancode((LONG)pInputScancode->uScancode);
1169 }
1170 } break;
1171
1172 case VRDE_INPUT_POINT:
1173 {
1174 if (cbInput == sizeof(VRDEINPUTPOINT))
1175 {
1176 const VRDEINPUTPOINT *pInputPoint = (VRDEINPUTPOINT *)pvInput;
1177
1178 int mouseButtons = 0;
1179 int iWheel = 0;
1180
1181 if (pInputPoint->uButtons & VRDE_INPUT_POINT_BUTTON1)
1182 {
1183 mouseButtons |= MouseButtonState_LeftButton;
1184 }
1185 if (pInputPoint->uButtons & VRDE_INPUT_POINT_BUTTON2)
1186 {
1187 mouseButtons |= MouseButtonState_RightButton;
1188 }
1189 if (pInputPoint->uButtons & VRDE_INPUT_POINT_BUTTON3)
1190 {
1191 mouseButtons |= MouseButtonState_MiddleButton;
1192 }
1193 if (pInputPoint->uButtons & VRDE_INPUT_POINT_WHEEL_UP)
1194 {
1195 mouseButtons |= MouseButtonState_WheelUp;
1196 iWheel = -1;
1197 }
1198 if (pInputPoint->uButtons & VRDE_INPUT_POINT_WHEEL_DOWN)
1199 {
1200 mouseButtons |= MouseButtonState_WheelDown;
1201 iWheel = 1;
1202 }
1203
1204 if (server->m_fGuestWantsAbsolute)
1205 {
1206 pConsole->getMouse()->PutMouseEventAbsolute(pInputPoint->x + 1, pInputPoint->y + 1, iWheel, 0 /* Horizontal wheel */, mouseButtons);
1207 } else
1208 {
1209 pConsole->getMouse()->PutMouseEvent(pInputPoint->x - server->m_mousex,
1210 pInputPoint->y - server->m_mousey,
1211 iWheel, 0 /* Horizontal wheel */, mouseButtons);
1212 server->m_mousex = pInputPoint->x;
1213 server->m_mousey = pInputPoint->y;
1214 }
1215 }
1216 } break;
1217
1218 case VRDE_INPUT_CAD:
1219 {
1220 pConsole->getKeyboard()->PutCAD();
1221 } break;
1222
1223 case VRDE_INPUT_RESET:
1224 {
1225 pConsole->Reset();
1226 } break;
1227
1228 case VRDE_INPUT_SYNCH:
1229 {
1230 if (cbInput == sizeof(VRDEINPUTSYNCH))
1231 {
1232 IKeyboard *pKeyboard = pConsole->getKeyboard();
1233
1234 const VRDEINPUTSYNCH *pInputSynch = (VRDEINPUTSYNCH *)pvInput;
1235
1236 server->m_InputSynch.fClientNumLock = (pInputSynch->uLockStatus & VRDE_INPUT_SYNCH_NUMLOCK) != 0;
1237 server->m_InputSynch.fClientCapsLock = (pInputSynch->uLockStatus & VRDE_INPUT_SYNCH_CAPITAL) != 0;
1238 server->m_InputSynch.fClientScrollLock = (pInputSynch->uLockStatus & VRDE_INPUT_SYNCH_SCROLL) != 0;
1239
1240 /* The client initiated synchronization. Always make the guest to reflect the client state.
1241 * Than means, when the guest changes the state itself, it is forced to return to the client
1242 * state.
1243 */
1244 if (server->m_InputSynch.fClientNumLock != server->m_InputSynch.fGuestNumLock)
1245 {
1246 server->m_InputSynch.cGuestNumLockAdaptions = 2;
1247 }
1248
1249 if (server->m_InputSynch.fClientCapsLock != server->m_InputSynch.fGuestCapsLock)
1250 {
1251 server->m_InputSynch.cGuestCapsLockAdaptions = 2;
1252 }
1253
1254 fixKbdLockStatus(&server->m_InputSynch, pKeyboard);
1255 }
1256 } break;
1257
1258 default:
1259 break;
1260 }
1261}
1262
1263DECLCALLBACK(void) ConsoleVRDPServer::VRDPCallbackVideoModeHint(void *pvCallback, unsigned cWidth, unsigned cHeight, unsigned cBitsPerPixel, unsigned uScreenId)
1264{
1265 ConsoleVRDPServer *server = static_cast<ConsoleVRDPServer*>(pvCallback);
1266
1267 server->mConsole->getDisplay()->SetVideoModeHint(uScreenId, TRUE /*=enabled*/,
1268 FALSE /*=changeOrigin*/, 0/*=OriginX*/, 0/*=OriginY*/,
1269 cWidth, cHeight, cBitsPerPixel);
1270}
1271
1272DECLCALLBACK(void) ConsoleVRDPServer::VRDECallbackAudioIn(void *pvCallback,
1273 void *pvCtx,
1274 uint32_t u32ClientId,
1275 uint32_t u32Event,
1276 const void *pvData,
1277 uint32_t cbData)
1278{
1279 ConsoleVRDPServer *server = static_cast<ConsoleVRDPServer*>(pvCallback);
1280
1281 PPDMIAUDIOSNIFFERPORT pPort = server->mConsole->getAudioSniffer()->getAudioSnifferPort();
1282
1283 switch (u32Event)
1284 {
1285 case VRDE_AUDIOIN_BEGIN:
1286 {
1287 const VRDEAUDIOINBEGIN *pParms = (const VRDEAUDIOINBEGIN *)pvData;
1288
1289 pPort->pfnAudioInputEventBegin (pPort, pvCtx,
1290 VRDE_AUDIO_FMT_SAMPLE_FREQ(pParms->fmt),
1291 VRDE_AUDIO_FMT_CHANNELS(pParms->fmt),
1292 VRDE_AUDIO_FMT_BITS_PER_SAMPLE(pParms->fmt),
1293 VRDE_AUDIO_FMT_SIGNED(pParms->fmt)
1294 );
1295 } break;
1296
1297 case VRDE_AUDIOIN_DATA:
1298 {
1299 pPort->pfnAudioInputEventData (pPort, pvCtx, pvData, cbData);
1300 } break;
1301
1302 case VRDE_AUDIOIN_END:
1303 {
1304 pPort->pfnAudioInputEventEnd (pPort, pvCtx);
1305 } break;
1306
1307 default:
1308 return;
1309 }
1310}
1311
1312
1313ConsoleVRDPServer::ConsoleVRDPServer(Console *console)
1314{
1315 mConsole = console;
1316
1317 int rc = RTCritSectInit(&mCritSect);
1318 AssertRC(rc);
1319
1320 mcClipboardRefs = 0;
1321 mpfnClipboardCallback = NULL;
1322
1323#ifdef VBOX_WITH_USB
1324 mUSBBackends.pHead = NULL;
1325 mUSBBackends.pTail = NULL;
1326
1327 mUSBBackends.thread = NIL_RTTHREAD;
1328 mUSBBackends.fThreadRunning = false;
1329 mUSBBackends.event = 0;
1330#endif
1331
1332 mhServer = 0;
1333 mServerInterfaceVersion = 0;
1334
1335 m_fGuestWantsAbsolute = false;
1336 m_mousex = 0;
1337 m_mousey = 0;
1338
1339 m_InputSynch.cGuestNumLockAdaptions = 2;
1340 m_InputSynch.cGuestCapsLockAdaptions = 2;
1341
1342 m_InputSynch.fGuestNumLock = false;
1343 m_InputSynch.fGuestCapsLock = false;
1344 m_InputSynch.fGuestScrollLock = false;
1345
1346 m_InputSynch.fClientNumLock = false;
1347 m_InputSynch.fClientCapsLock = false;
1348 m_InputSynch.fClientScrollLock = false;
1349
1350 memset(maFramebuffers, 0, sizeof(maFramebuffers));
1351
1352 {
1353 ComPtr<IEventSource> es;
1354 console->COMGETTER(EventSource)(es.asOutParam());
1355 ComObjPtr<VRDPConsoleListenerImpl> aConsoleListener;
1356 aConsoleListener.createObject();
1357 aConsoleListener->init(new VRDPConsoleListener(), this);
1358 mConsoleListener = aConsoleListener;
1359 com::SafeArray <VBoxEventType_T> eventTypes;
1360 eventTypes.push_back(VBoxEventType_OnMousePointerShapeChanged);
1361 eventTypes.push_back(VBoxEventType_OnMouseCapabilityChanged);
1362 eventTypes.push_back(VBoxEventType_OnKeyboardLedsChanged);
1363 es->RegisterListener(mConsoleListener, ComSafeArrayAsInParam(eventTypes), true);
1364 }
1365
1366 mVRDPBindPort = -1;
1367
1368 mAuthLibrary = 0;
1369
1370 mu32AudioInputClientId = 0;
1371
1372 /*
1373 * Optional interfaces.
1374 */
1375 m_fInterfaceImage = false;
1376 memset(&m_interfaceImage, 0, sizeof (m_interfaceImage));
1377 memset(&m_interfaceCallbacksImage, 0, sizeof (m_interfaceCallbacksImage));
1378 RT_ZERO(m_interfaceMousePtr);
1379 RT_ZERO(m_interfaceSCard);
1380 RT_ZERO(m_interfaceCallbacksSCard);
1381}
1382
1383ConsoleVRDPServer::~ConsoleVRDPServer()
1384{
1385 Stop();
1386
1387 if (mConsoleListener)
1388 {
1389 ComPtr<IEventSource> es;
1390 mConsole->COMGETTER(EventSource)(es.asOutParam());
1391 es->UnregisterListener(mConsoleListener);
1392 mConsoleListener.setNull();
1393 }
1394
1395 unsigned i;
1396 for (i = 0; i < RT_ELEMENTS(maFramebuffers); i++)
1397 {
1398 if (maFramebuffers[i])
1399 {
1400 maFramebuffers[i]->Release();
1401 maFramebuffers[i] = NULL;
1402 }
1403 }
1404
1405 if (RTCritSectIsInitialized(&mCritSect))
1406 {
1407 RTCritSectDelete(&mCritSect);
1408 memset(&mCritSect, 0, sizeof(mCritSect));
1409 }
1410}
1411
1412int ConsoleVRDPServer::Launch(void)
1413{
1414 LogFlowThisFunc(("\n"));
1415
1416 IVRDEServer *server = mConsole->getVRDEServer();
1417 AssertReturn(server, VERR_INTERNAL_ERROR_2);
1418
1419 /*
1420 * Check if VRDE is enabled.
1421 */
1422 BOOL fEnabled;
1423 HRESULT hrc = server->COMGETTER(Enabled)(&fEnabled);
1424 AssertComRCReturn(hrc, Global::vboxStatusCodeFromCOM(hrc));
1425 if (!fEnabled)
1426 return VINF_SUCCESS;
1427
1428 /*
1429 * Check that a VRDE extension pack name is set and resolve it into a
1430 * library path.
1431 */
1432 Bstr bstrExtPack;
1433 hrc = server->COMGETTER(VRDEExtPack)(bstrExtPack.asOutParam());
1434 if (FAILED(hrc))
1435 return Global::vboxStatusCodeFromCOM(hrc);
1436 if (bstrExtPack.isEmpty())
1437 return VINF_NOT_SUPPORTED;
1438
1439 Utf8Str strExtPack(bstrExtPack);
1440 Utf8Str strVrdeLibrary;
1441 int vrc = VINF_SUCCESS;
1442 if (strExtPack.equals(VBOXVRDP_KLUDGE_EXTPACK_NAME))
1443 strVrdeLibrary = "VBoxVRDP";
1444 else
1445 {
1446#ifdef VBOX_WITH_EXTPACK
1447 ExtPackManager *pExtPackMgr = mConsole->getExtPackManager();
1448 vrc = pExtPackMgr->getVrdeLibraryPathForExtPack(&strExtPack, &strVrdeLibrary);
1449#else
1450 vrc = VERR_FILE_NOT_FOUND;
1451#endif
1452 }
1453 if (RT_SUCCESS(vrc))
1454 {
1455 /*
1456 * Load the VRDE library and start the server, if it is enabled.
1457 */
1458 vrc = loadVRDPLibrary(strVrdeLibrary.c_str());
1459 if (RT_SUCCESS(vrc))
1460 {
1461 VRDEENTRYPOINTS_4 *pEntryPoints4;
1462 vrc = mpfnVRDECreateServer(&mCallbacks.header, this, (VRDEINTERFACEHDR **)&pEntryPoints4, &mhServer);
1463
1464 if (RT_SUCCESS(vrc))
1465 {
1466 mServerInterfaceVersion = 4;
1467 mEntryPoints = *pEntryPoints4;
1468 mpEntryPoints = &mEntryPoints;
1469 }
1470 else if (vrc == VERR_VERSION_MISMATCH)
1471 {
1472 /* An older version of VRDE is installed, try version 3. */
1473 VRDEENTRYPOINTS_3 *pEntryPoints3;
1474
1475 static VRDECALLBACKS_3 sCallbacks3 =
1476 {
1477 { VRDE_INTERFACE_VERSION_3, sizeof(VRDECALLBACKS_3) },
1478 ConsoleVRDPServer::VRDPCallbackQueryProperty,
1479 ConsoleVRDPServer::VRDPCallbackClientLogon,
1480 ConsoleVRDPServer::VRDPCallbackClientConnect,
1481 ConsoleVRDPServer::VRDPCallbackClientDisconnect,
1482 ConsoleVRDPServer::VRDPCallbackIntercept,
1483 ConsoleVRDPServer::VRDPCallbackUSB,
1484 ConsoleVRDPServer::VRDPCallbackClipboard,
1485 ConsoleVRDPServer::VRDPCallbackFramebufferQuery,
1486 ConsoleVRDPServer::VRDPCallbackFramebufferLock,
1487 ConsoleVRDPServer::VRDPCallbackFramebufferUnlock,
1488 ConsoleVRDPServer::VRDPCallbackInput,
1489 ConsoleVRDPServer::VRDPCallbackVideoModeHint,
1490 ConsoleVRDPServer::VRDECallbackAudioIn
1491 };
1492
1493 vrc = mpfnVRDECreateServer(&sCallbacks3.header, this, (VRDEINTERFACEHDR **)&pEntryPoints3, &mhServer);
1494 if (RT_SUCCESS(vrc))
1495 {
1496 mServerInterfaceVersion = 3;
1497 mEntryPoints.header = pEntryPoints3->header;
1498 mEntryPoints.VRDEDestroy = pEntryPoints3->VRDEDestroy;
1499 mEntryPoints.VRDEEnableConnections = pEntryPoints3->VRDEEnableConnections;
1500 mEntryPoints.VRDEDisconnect = pEntryPoints3->VRDEDisconnect;
1501 mEntryPoints.VRDEResize = pEntryPoints3->VRDEResize;
1502 mEntryPoints.VRDEUpdate = pEntryPoints3->VRDEUpdate;
1503 mEntryPoints.VRDEColorPointer = pEntryPoints3->VRDEColorPointer;
1504 mEntryPoints.VRDEHidePointer = pEntryPoints3->VRDEHidePointer;
1505 mEntryPoints.VRDEAudioSamples = pEntryPoints3->VRDEAudioSamples;
1506 mEntryPoints.VRDEAudioVolume = pEntryPoints3->VRDEAudioVolume;
1507 mEntryPoints.VRDEUSBRequest = pEntryPoints3->VRDEUSBRequest;
1508 mEntryPoints.VRDEClipboard = pEntryPoints3->VRDEClipboard;
1509 mEntryPoints.VRDEQueryInfo = pEntryPoints3->VRDEQueryInfo;
1510 mEntryPoints.VRDERedirect = pEntryPoints3->VRDERedirect;
1511 mEntryPoints.VRDEAudioInOpen = pEntryPoints3->VRDEAudioInOpen;
1512 mEntryPoints.VRDEAudioInClose = pEntryPoints3->VRDEAudioInClose;
1513 mEntryPoints.VRDEGetInterface = NULL;
1514 mpEntryPoints = &mEntryPoints;
1515 }
1516 else if (vrc == VERR_VERSION_MISMATCH)
1517 {
1518 /* An older version of VRDE is installed, try version 1. */
1519 VRDEENTRYPOINTS_1 *pEntryPoints1;
1520
1521 static VRDECALLBACKS_1 sCallbacks1 =
1522 {
1523 { VRDE_INTERFACE_VERSION_1, sizeof(VRDECALLBACKS_1) },
1524 ConsoleVRDPServer::VRDPCallbackQueryProperty,
1525 ConsoleVRDPServer::VRDPCallbackClientLogon,
1526 ConsoleVRDPServer::VRDPCallbackClientConnect,
1527 ConsoleVRDPServer::VRDPCallbackClientDisconnect,
1528 ConsoleVRDPServer::VRDPCallbackIntercept,
1529 ConsoleVRDPServer::VRDPCallbackUSB,
1530 ConsoleVRDPServer::VRDPCallbackClipboard,
1531 ConsoleVRDPServer::VRDPCallbackFramebufferQuery,
1532 ConsoleVRDPServer::VRDPCallbackFramebufferLock,
1533 ConsoleVRDPServer::VRDPCallbackFramebufferUnlock,
1534 ConsoleVRDPServer::VRDPCallbackInput,
1535 ConsoleVRDPServer::VRDPCallbackVideoModeHint
1536 };
1537
1538 vrc = mpfnVRDECreateServer(&sCallbacks1.header, this, (VRDEINTERFACEHDR **)&pEntryPoints1, &mhServer);
1539 if (RT_SUCCESS(vrc))
1540 {
1541 mServerInterfaceVersion = 1;
1542 mEntryPoints.header = pEntryPoints1->header;
1543 mEntryPoints.VRDEDestroy = pEntryPoints1->VRDEDestroy;
1544 mEntryPoints.VRDEEnableConnections = pEntryPoints1->VRDEEnableConnections;
1545 mEntryPoints.VRDEDisconnect = pEntryPoints1->VRDEDisconnect;
1546 mEntryPoints.VRDEResize = pEntryPoints1->VRDEResize;
1547 mEntryPoints.VRDEUpdate = pEntryPoints1->VRDEUpdate;
1548 mEntryPoints.VRDEColorPointer = pEntryPoints1->VRDEColorPointer;
1549 mEntryPoints.VRDEHidePointer = pEntryPoints1->VRDEHidePointer;
1550 mEntryPoints.VRDEAudioSamples = pEntryPoints1->VRDEAudioSamples;
1551 mEntryPoints.VRDEAudioVolume = pEntryPoints1->VRDEAudioVolume;
1552 mEntryPoints.VRDEUSBRequest = pEntryPoints1->VRDEUSBRequest;
1553 mEntryPoints.VRDEClipboard = pEntryPoints1->VRDEClipboard;
1554 mEntryPoints.VRDEQueryInfo = pEntryPoints1->VRDEQueryInfo;
1555 mEntryPoints.VRDERedirect = NULL;
1556 mEntryPoints.VRDEAudioInOpen = NULL;
1557 mEntryPoints.VRDEAudioInClose = NULL;
1558 mEntryPoints.VRDEGetInterface = NULL;
1559 mpEntryPoints = &mEntryPoints;
1560 }
1561 }
1562 }
1563
1564 if (RT_SUCCESS(vrc))
1565 {
1566 LogRel(("VRDE: loaded version %d of the server.\n", mServerInterfaceVersion));
1567
1568 if (mServerInterfaceVersion >= 4)
1569 {
1570 /* The server supports optional interfaces. */
1571 Assert(mpEntryPoints->VRDEGetInterface != NULL);
1572
1573 /* Image interface. */
1574 m_interfaceImage.header.u64Version = 1;
1575 m_interfaceImage.header.u64Size = sizeof(m_interfaceImage);
1576
1577 m_interfaceCallbacksImage.header.u64Version = 1;
1578 m_interfaceCallbacksImage.header.u64Size = sizeof(m_interfaceCallbacksImage);
1579 m_interfaceCallbacksImage.VRDEImageCbNotify = VRDEImageCbNotify;
1580
1581 vrc = mpEntryPoints->VRDEGetInterface(mhServer,
1582 VRDE_IMAGE_INTERFACE_NAME,
1583 &m_interfaceImage.header,
1584 &m_interfaceCallbacksImage.header,
1585 this);
1586 if (RT_SUCCESS(vrc))
1587 {
1588 LogRel(("VRDE: [%s]\n", VRDE_IMAGE_INTERFACE_NAME));
1589 m_fInterfaceImage = true;
1590 }
1591
1592 /* Mouse pointer interface. */
1593 m_interfaceMousePtr.header.u64Version = 1;
1594 m_interfaceMousePtr.header.u64Size = sizeof(m_interfaceMousePtr);
1595
1596 vrc = mpEntryPoints->VRDEGetInterface(mhServer,
1597 VRDE_MOUSEPTR_INTERFACE_NAME,
1598 &m_interfaceMousePtr.header,
1599 NULL,
1600 this);
1601 if (RT_SUCCESS(vrc))
1602 {
1603 LogRel(("VRDE: [%s]\n", VRDE_MOUSEPTR_INTERFACE_NAME));
1604 }
1605 else
1606 {
1607 RT_ZERO(m_interfaceMousePtr);
1608 }
1609
1610 /* Smartcard interface. */
1611 m_interfaceSCard.header.u64Version = 1;
1612 m_interfaceSCard.header.u64Size = sizeof(m_interfaceSCard);
1613
1614 m_interfaceCallbacksSCard.header.u64Version = 1;
1615 m_interfaceCallbacksSCard.header.u64Size = sizeof(m_interfaceCallbacksSCard);
1616 m_interfaceCallbacksSCard.VRDESCardCbNotify = VRDESCardCbNotify;
1617 m_interfaceCallbacksSCard.VRDESCardCbResponse = VRDESCardCbResponse;
1618
1619 vrc = mpEntryPoints->VRDEGetInterface(mhServer,
1620 VRDE_SCARD_INTERFACE_NAME,
1621 &m_interfaceSCard.header,
1622 &m_interfaceCallbacksSCard.header,
1623 this);
1624 if (RT_SUCCESS(vrc))
1625 {
1626 LogRel(("VRDE: [%s]\n", VRDE_SCARD_INTERFACE_NAME));
1627 }
1628 else
1629 {
1630 RT_ZERO(m_interfaceSCard);
1631 }
1632
1633 /* Since these interfaces are optional, it is always a success here. */
1634 vrc = VINF_SUCCESS;
1635 }
1636#ifdef VBOX_WITH_USB
1637 remoteUSBThreadStart();
1638#endif
1639 }
1640 else
1641 {
1642 if (vrc != VERR_NET_ADDRESS_IN_USE)
1643 LogRel(("VRDE: Could not start the server rc = %Rrc\n", vrc));
1644 /* Don't unload the lib, because it prevents us trying again or
1645 because there may be other users? */
1646 }
1647 }
1648 }
1649
1650 return vrc;
1651}
1652
1653typedef struct H3DORInstance
1654{
1655 ConsoleVRDPServer *pThis;
1656 HVRDEIMAGE hImageBitmap;
1657 int32_t x;
1658 int32_t y;
1659 uint32_t w;
1660 uint32_t h;
1661 bool fCreated;
1662} H3DORInstance;
1663
1664/* static */ DECLCALLBACK(void) ConsoleVRDPServer::H3DORBegin(const void *pvContext, void **ppvInstance,
1665 const char *pszFormat)
1666{
1667 LogFlowFunc(("ctx %p\n", pvContext));
1668
1669 H3DORInstance *p = (H3DORInstance *)RTMemAlloc(sizeof (H3DORInstance));
1670
1671 if (p)
1672 {
1673 p->pThis = (ConsoleVRDPServer *)pvContext;
1674 p->hImageBitmap = NULL;
1675 p->x = 0;
1676 p->y = 0;
1677 p->w = 0;
1678 p->h = 0;
1679 p->fCreated = false;
1680
1681 /* Host 3D service passes the actual format of data in this redirect instance.
1682 * That is what will be in the H3DORFrame's parameters pvData and cbData.
1683 */
1684 if (RTStrICmp(pszFormat, H3DOR_FMT_RGBA_TOPDOWN) == 0)
1685 {
1686 /* Accept it. */
1687 }
1688 else
1689 {
1690 RTMemFree(p);
1691 p = NULL;
1692 }
1693 }
1694
1695 /* Caller check this for NULL. */
1696 *ppvInstance = p;
1697}
1698
1699/* static */ DECLCALLBACK(void) ConsoleVRDPServer::H3DORGeometry(void *pvInstance,
1700 int32_t x, int32_t y, uint32_t w, uint32_t h)
1701{
1702 LogFlowFunc(("ins %p %d,%d %dx%d\n", pvInstance, x, y, w, h));
1703
1704 H3DORInstance *p = (H3DORInstance *)pvInstance;
1705 Assert(p);
1706 Assert(p->pThis);
1707
1708 /* @todo find out what to do if size changes to 0x0 from non zero */
1709 if (w == 0 || h == 0)
1710 {
1711 /* Do nothing. */
1712 return;
1713 }
1714
1715 RTRECT rect;
1716 rect.xLeft = x;
1717 rect.yTop = y;
1718 rect.xRight = x + w;
1719 rect.yBottom = y + h;
1720
1721 if (p->hImageBitmap)
1722 {
1723 /* An image handle has been already created,
1724 * check if it has the same size as the reported geometry.
1725 */
1726 if ( p->x == x
1727 && p->y == y
1728 && p->w == w
1729 && p->h == h)
1730 {
1731 LogFlowFunc(("geometry not changed\n"));
1732 /* Do nothing. Continue using the existing handle. */
1733 }
1734 else
1735 {
1736 int rc = p->pThis->m_interfaceImage.VRDEImageGeometrySet(p->hImageBitmap, &rect);
1737 if (RT_SUCCESS(rc))
1738 {
1739 p->x = x;
1740 p->y = y;
1741 p->w = w;
1742 p->h = h;
1743 }
1744 else
1745 {
1746 /* The handle must be recreated. Delete existing handle here. */
1747 p->pThis->m_interfaceImage.VRDEImageHandleClose(p->hImageBitmap);
1748 p->hImageBitmap = NULL;
1749 }
1750 }
1751 }
1752
1753 if (!p->hImageBitmap)
1754 {
1755 /* Create a new bitmap handle. */
1756 uint32_t u32ScreenId = 0; /* @todo clip to corresponding screens.
1757 * Clipping can be done here or in VRDP server.
1758 * If VRDP does clipping, then uScreenId parameter
1759 * is not necessary and coords must be global.
1760 * (have to check which coords are used in opengl service).
1761 * Since all VRDE API uses a ScreenId,
1762 * the clipping must be done here in ConsoleVRDPServer
1763 */
1764 uint32_t fu32CompletionFlags = 0;
1765 int rc = p->pThis->m_interfaceImage.VRDEImageHandleCreate(p->pThis->mhServer,
1766 &p->hImageBitmap,
1767 p,
1768 u32ScreenId,
1769 VRDE_IMAGE_F_CREATE_CONTENT_3D
1770 | VRDE_IMAGE_F_CREATE_WINDOW,
1771 &rect,
1772 VRDE_IMAGE_FMT_ID_BITMAP_BGRA8,
1773 NULL,
1774 0,
1775 &fu32CompletionFlags);
1776 if (RT_SUCCESS(rc))
1777 {
1778 p->x = x;
1779 p->y = y;
1780 p->w = w;
1781 p->h = h;
1782
1783 if ((fu32CompletionFlags & VRDE_IMAGE_F_COMPLETE_ASYNC) == 0)
1784 {
1785 p->fCreated = true;
1786 }
1787 }
1788 else
1789 {
1790 p->hImageBitmap = NULL;
1791 p->w = 0;
1792 p->h = 0;
1793 }
1794 }
1795}
1796
1797/* static */ DECLCALLBACK(void) ConsoleVRDPServer::H3DORVisibleRegion(void *pvInstance,
1798 uint32_t cRects, RTRECT *paRects)
1799{
1800 LogFlowFunc(("ins %p %d\n", pvInstance, cRects));
1801
1802 H3DORInstance *p = (H3DORInstance *)pvInstance;
1803 Assert(p);
1804 Assert(p->pThis);
1805
1806 if (cRects == 0)
1807 {
1808 /* Complete image is visible. */
1809 RTRECT rect;
1810 rect.xLeft = p->x;
1811 rect.yTop = p->y;
1812 rect.xRight = p->x + p->w;
1813 rect.yBottom = p->y + p->h;
1814 p->pThis->m_interfaceImage.VRDEImageRegionSet (p->hImageBitmap,
1815 1,
1816 &rect);
1817 }
1818 else
1819 {
1820 p->pThis->m_interfaceImage.VRDEImageRegionSet (p->hImageBitmap,
1821 cRects,
1822 paRects);
1823 }
1824}
1825
1826/* static */ DECLCALLBACK(void) ConsoleVRDPServer::H3DORFrame(void *pvInstance,
1827 void *pvData, uint32_t cbData)
1828{
1829 LogFlowFunc(("ins %p %p %d\n", pvInstance, pvData, cbData));
1830
1831 H3DORInstance *p = (H3DORInstance *)pvInstance;
1832 Assert(p);
1833 Assert(p->pThis);
1834
1835 /* Currently only a topdown BGR0 bitmap format is supported. */
1836 VRDEIMAGEBITMAP image;
1837
1838 image.cWidth = p->w;
1839 image.cHeight = p->h;
1840 image.pvData = pvData;
1841 image.cbData = cbData;
1842 image.pvScanLine0 = (uint8_t *)pvData + (p->h - 1) * p->w * 4;
1843 image.iScanDelta = -4 * p->w;
1844
1845 p->pThis->m_interfaceImage.VRDEImageUpdate (p->hImageBitmap,
1846 p->x,
1847 p->y,
1848 p->w,
1849 p->h,
1850 &image,
1851 sizeof(VRDEIMAGEBITMAP));
1852}
1853
1854/* static */ DECLCALLBACK(void) ConsoleVRDPServer::H3DOREnd(void *pvInstance)
1855{
1856 LogFlowFunc(("ins %p\n", pvInstance));
1857
1858 H3DORInstance *p = (H3DORInstance *)pvInstance;
1859 Assert(p);
1860 Assert(p->pThis);
1861
1862 p->pThis->m_interfaceImage.VRDEImageHandleClose(p->hImageBitmap);
1863
1864 RTMemFree(p);
1865}
1866
1867/* static */ DECLCALLBACK(int) ConsoleVRDPServer::H3DORContextProperty(const void *pvContext, uint32_t index,
1868 void *pvBuffer, uint32_t cbBuffer, uint32_t *pcbOut)
1869{
1870 int rc = VINF_SUCCESS;
1871
1872 if (index == H3DOR_PROP_FORMATS)
1873 {
1874 /* Return a comma separated list of supported formats. */
1875 static const char *pszSupportedFormats = H3DOR_FMT_RGBA_TOPDOWN;
1876 uint32_t cbOut = (uint32_t)strlen(pszSupportedFormats) + 1;
1877 if (cbOut <= cbBuffer)
1878 {
1879 memcpy(pvBuffer, pszSupportedFormats, cbOut);
1880 }
1881 else
1882 {
1883 rc = VERR_BUFFER_OVERFLOW;
1884 }
1885 *pcbOut = cbOut;
1886 }
1887 else
1888 {
1889 rc = VERR_NOT_SUPPORTED;
1890 }
1891
1892 return rc;
1893}
1894
1895void ConsoleVRDPServer::remote3DRedirect(void)
1896{
1897 if (!m_fInterfaceImage)
1898 {
1899 /* No redirect without corresponding interface. */
1900 return;
1901 }
1902
1903 /* Check if 3D redirection has been enabled. */
1904 com::Bstr bstr;
1905 HRESULT hrc = mConsole->getVRDEServer()->GetVRDEProperty(Bstr("H3DRedirect/Enabled").raw(), bstr.asOutParam());
1906
1907 if (hrc != S_OK)
1908 {
1909 bstr = "";
1910 }
1911
1912 com::Utf8Str value = bstr;
1913
1914 bool fEnabled = RTStrICmp(value.c_str(), "true") == 0
1915 || RTStrICmp(value.c_str(), "1") == 0;
1916
1917 if (!fEnabled)
1918 {
1919 return;
1920 }
1921
1922 /* Tell the host 3D service to redirect output using the ConsoleVRDPServer callbacks. */
1923 H3DOUTPUTREDIRECT outputRedirect =
1924 {
1925 this,
1926 H3DORBegin,
1927 H3DORGeometry,
1928 H3DORVisibleRegion,
1929 H3DORFrame,
1930 H3DOREnd,
1931 H3DORContextProperty
1932 };
1933
1934 VBOXHGCMSVCPARM parm;
1935
1936 parm.type = VBOX_HGCM_SVC_PARM_PTR;
1937 parm.u.pointer.addr = &outputRedirect;
1938 parm.u.pointer.size = sizeof(outputRedirect);
1939
1940 VMMDev *pVMMDev = mConsole->getVMMDev();
1941
1942 if (!pVMMDev)
1943 {
1944 AssertMsgFailed(("remote3DRedirect no vmmdev\n"));
1945 return;
1946 }
1947
1948 int rc = pVMMDev->hgcmHostCall("VBoxSharedCrOpenGL",
1949 SHCRGL_HOST_FN_SET_OUTPUT_REDIRECT,
1950 SHCRGL_CPARMS_SET_OUTPUT_REDIRECT,
1951 &parm);
1952
1953 if (!RT_SUCCESS(rc))
1954 {
1955 AssertMsgFailed(("SHCRGL_HOST_FN_SET_CONSOLE failed with %Rrc\n", rc));
1956 return;
1957 }
1958
1959 LogRel(("VRDE: Enabled 3D redirect.\n"));
1960
1961 return;
1962}
1963
1964/* static */ DECLCALLBACK(int) ConsoleVRDPServer::VRDEImageCbNotify (void *pvContext,
1965 void *pvUser,
1966 HVRDEIMAGE hVideo,
1967 uint32_t u32Id,
1968 void *pvData,
1969 uint32_t cbData)
1970{
1971 LogFlowFunc(("pvContext %p, pvUser %p, hVideo %p, u32Id %u, pvData %p, cbData %d\n",
1972 pvContext, pvUser, hVideo, u32Id, pvData, cbData));
1973
1974 ConsoleVRDPServer *pServer = static_cast<ConsoleVRDPServer*>(pvContext);
1975 H3DORInstance *p = (H3DORInstance *)pvUser;
1976 Assert(p);
1977 Assert(p->pThis);
1978 Assert(p->pThis == pServer);
1979
1980 if (u32Id == VRDE_IMAGE_NOTIFY_HANDLE_CREATE)
1981 {
1982 if (cbData != sizeof(uint32_t))
1983 {
1984 AssertFailed();
1985 return VERR_INVALID_PARAMETER;
1986 }
1987
1988 uint32_t u32StreamId = *(uint32_t *)pvData;
1989 LogFlowFunc(("VRDE_IMAGE_NOTIFY_HANDLE_CREATE u32StreamId %d\n",
1990 u32StreamId));
1991
1992 if (u32StreamId != 0)
1993 {
1994 p->fCreated = true; // @todo not needed?
1995 }
1996 else
1997 {
1998 /* The stream has not been created. */
1999 }
2000 }
2001
2002 return VINF_SUCCESS;
2003}
2004
2005/* static */ DECLCALLBACK(int) ConsoleVRDPServer::VRDESCardCbNotify(void *pvContext,
2006 uint32_t u32Id,
2007 void *pvData,
2008 uint32_t cbData)
2009{
2010#ifdef VBOX_WITH_USB_CARDREADER
2011 ConsoleVRDPServer *pThis = static_cast<ConsoleVRDPServer*>(pvContext);
2012 UsbCardReader *pReader = pThis->mConsole->getUsbCardReader();
2013 return pReader->VRDENotify(u32Id, pvData, cbData);
2014#else
2015 NOREF(pvContext);
2016 NOREF(u32Id);
2017 NOREF(pvData);
2018 NOREF(cbData);
2019 return VERR_NOT_SUPPORTED;
2020#endif
2021}
2022
2023/* static */ DECLCALLBACK(int) ConsoleVRDPServer::VRDESCardCbResponse(void *pvContext,
2024 int rcRequest,
2025 void *pvUser,
2026 uint32_t u32Function,
2027 void *pvData,
2028 uint32_t cbData)
2029{
2030#ifdef VBOX_WITH_USB_CARDREADER
2031 ConsoleVRDPServer *pThis = static_cast<ConsoleVRDPServer*>(pvContext);
2032 UsbCardReader *pReader = pThis->mConsole->getUsbCardReader();
2033 return pReader->VRDEResponse(rcRequest, pvUser, u32Function, pvData, cbData);
2034#else
2035 NOREF(pvContext);
2036 NOREF(rcRequest);
2037 NOREF(pvUser);
2038 NOREF(u32Function);
2039 NOREF(pvData);
2040 NOREF(cbData);
2041 return VERR_NOT_SUPPORTED;
2042#endif
2043}
2044
2045int ConsoleVRDPServer::SCardRequest(void *pvUser, uint32_t u32Function, const void *pvData, uint32_t cbData)
2046{
2047 int rc = VINF_SUCCESS;
2048
2049 if (mhServer && mpEntryPoints && m_interfaceSCard.VRDESCardRequest)
2050 {
2051 rc = m_interfaceSCard.VRDESCardRequest(mhServer, pvUser, u32Function, pvData, cbData);
2052 }
2053 else
2054 {
2055 rc = VERR_NOT_SUPPORTED;
2056 }
2057
2058 return rc;
2059}
2060
2061void ConsoleVRDPServer::EnableConnections(void)
2062{
2063 if (mpEntryPoints && mhServer)
2064 {
2065 mpEntryPoints->VRDEEnableConnections(mhServer, true);
2066
2067 /* Redirect 3D output if it is enabled. */
2068 remote3DRedirect();
2069 }
2070}
2071
2072void ConsoleVRDPServer::DisconnectClient(uint32_t u32ClientId, bool fReconnect)
2073{
2074 if (mpEntryPoints && mhServer)
2075 {
2076 mpEntryPoints->VRDEDisconnect(mhServer, u32ClientId, fReconnect);
2077 }
2078}
2079
2080int ConsoleVRDPServer::MousePointer(BOOL alpha,
2081 ULONG xHot,
2082 ULONG yHot,
2083 ULONG width,
2084 ULONG height,
2085 const uint8_t *pu8Shape)
2086{
2087 int rc = VINF_SUCCESS;
2088
2089 if (mhServer && mpEntryPoints && m_interfaceMousePtr.VRDEMousePtr)
2090 {
2091 size_t cbMask = (((width + 7) / 8) * height + 3) & ~3;
2092 size_t cbData = width * height * 4;
2093
2094 size_t cbDstMask = alpha? 0: cbMask;
2095
2096 size_t cbPointer = sizeof(VRDEMOUSEPTRDATA) + cbDstMask + cbData;
2097 uint8_t *pu8Pointer = (uint8_t *)RTMemAlloc(cbPointer);
2098 if (pu8Pointer != NULL)
2099 {
2100 VRDEMOUSEPTRDATA *pPointer = (VRDEMOUSEPTRDATA *)pu8Pointer;
2101
2102 pPointer->u16HotX = (uint16_t)xHot;
2103 pPointer->u16HotY = (uint16_t)yHot;
2104 pPointer->u16Width = (uint16_t)width;
2105 pPointer->u16Height = (uint16_t)height;
2106 pPointer->u16MaskLen = (uint16_t)cbDstMask;
2107 pPointer->u32DataLen = (uint32_t)cbData;
2108
2109 /* AND mask. */
2110 uint8_t *pu8Mask = pu8Pointer + sizeof(VRDEMOUSEPTRDATA);
2111 if (cbDstMask)
2112 {
2113 memcpy(pu8Mask, pu8Shape, cbDstMask);
2114 }
2115
2116 /* XOR mask */
2117 uint8_t *pu8Data = pu8Mask + pPointer->u16MaskLen;
2118 memcpy(pu8Data, pu8Shape + cbMask, cbData);
2119
2120 m_interfaceMousePtr.VRDEMousePtr(mhServer, pPointer);
2121
2122 RTMemFree(pu8Pointer);
2123 }
2124 else
2125 {
2126 rc = VERR_NO_MEMORY;
2127 }
2128 }
2129 else
2130 {
2131 rc = VERR_NOT_SUPPORTED;
2132 }
2133
2134 return rc;
2135}
2136
2137void ConsoleVRDPServer::MousePointerUpdate(const VRDECOLORPOINTER *pPointer)
2138{
2139 if (mpEntryPoints && mhServer)
2140 {
2141 mpEntryPoints->VRDEColorPointer(mhServer, pPointer);
2142 }
2143}
2144
2145void ConsoleVRDPServer::MousePointerHide(void)
2146{
2147 if (mpEntryPoints && mhServer)
2148 {
2149 mpEntryPoints->VRDEHidePointer(mhServer);
2150 }
2151}
2152
2153void ConsoleVRDPServer::Stop(void)
2154{
2155 Assert(VALID_PTR(this)); /** @todo r=bird: there are(/was) some odd cases where this buster was invalid on
2156 * linux. Just remove this when it's 100% sure that problem has been fixed. */
2157 if (mhServer)
2158 {
2159 HVRDESERVER hServer = mhServer;
2160
2161 /* Reset the handle to avoid further calls to the server. */
2162 mhServer = 0;
2163
2164 if (mpEntryPoints && hServer)
2165 {
2166 mpEntryPoints->VRDEDestroy(hServer);
2167 }
2168 }
2169
2170#ifdef VBOX_WITH_USB
2171 remoteUSBThreadStop();
2172#endif /* VBOX_WITH_USB */
2173
2174 mpfnAuthEntry = NULL;
2175 mpfnAuthEntry2 = NULL;
2176 mpfnAuthEntry3 = NULL;
2177
2178 if (mAuthLibrary)
2179 {
2180 RTLdrClose(mAuthLibrary);
2181 mAuthLibrary = 0;
2182 }
2183}
2184
2185/* Worker thread for Remote USB. The thread polls the clients for
2186 * the list of attached USB devices.
2187 * The thread is also responsible for attaching/detaching devices
2188 * to/from the VM.
2189 *
2190 * It is expected that attaching/detaching is not a frequent operation.
2191 *
2192 * The thread is always running when the VRDP server is active.
2193 *
2194 * The thread scans backends and requests the device list every 2 seconds.
2195 *
2196 * When device list is available, the thread calls the Console to process it.
2197 *
2198 */
2199#define VRDP_DEVICE_LIST_PERIOD_MS (2000)
2200
2201#ifdef VBOX_WITH_USB
2202static DECLCALLBACK(int) threadRemoteUSB(RTTHREAD self, void *pvUser)
2203{
2204 ConsoleVRDPServer *pOwner = (ConsoleVRDPServer *)pvUser;
2205
2206 LogFlow(("Console::threadRemoteUSB: start. owner = %p.\n", pOwner));
2207
2208 pOwner->notifyRemoteUSBThreadRunning(self);
2209
2210 while (pOwner->isRemoteUSBThreadRunning())
2211 {
2212 RemoteUSBBackend *pRemoteUSBBackend = NULL;
2213
2214 while ((pRemoteUSBBackend = pOwner->usbBackendGetNext(pRemoteUSBBackend)) != NULL)
2215 {
2216 pRemoteUSBBackend->PollRemoteDevices();
2217 }
2218
2219 pOwner->waitRemoteUSBThreadEvent(VRDP_DEVICE_LIST_PERIOD_MS);
2220
2221 LogFlow(("Console::threadRemoteUSB: iteration. owner = %p.\n", pOwner));
2222 }
2223
2224 return VINF_SUCCESS;
2225}
2226
2227void ConsoleVRDPServer::notifyRemoteUSBThreadRunning(RTTHREAD thread)
2228{
2229 mUSBBackends.thread = thread;
2230 mUSBBackends.fThreadRunning = true;
2231 int rc = RTThreadUserSignal(thread);
2232 AssertRC(rc);
2233}
2234
2235bool ConsoleVRDPServer::isRemoteUSBThreadRunning(void)
2236{
2237 return mUSBBackends.fThreadRunning;
2238}
2239
2240void ConsoleVRDPServer::waitRemoteUSBThreadEvent(RTMSINTERVAL cMillies)
2241{
2242 int rc = RTSemEventWait(mUSBBackends.event, cMillies);
2243 Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT);
2244 NOREF(rc);
2245}
2246
2247void ConsoleVRDPServer::remoteUSBThreadStart(void)
2248{
2249 int rc = RTSemEventCreate(&mUSBBackends.event);
2250
2251 if (RT_FAILURE(rc))
2252 {
2253 AssertFailed();
2254 mUSBBackends.event = 0;
2255 }
2256
2257 if (RT_SUCCESS(rc))
2258 {
2259 rc = RTThreadCreate(&mUSBBackends.thread, threadRemoteUSB, this, 65536,
2260 RTTHREADTYPE_VRDP_IO, RTTHREADFLAGS_WAITABLE, "remote usb");
2261 }
2262
2263 if (RT_FAILURE(rc))
2264 {
2265 LogRel(("Warning: could not start the remote USB thread, rc = %Rrc!!!\n", rc));
2266 mUSBBackends.thread = NIL_RTTHREAD;
2267 }
2268 else
2269 {
2270 /* Wait until the thread is ready. */
2271 rc = RTThreadUserWait(mUSBBackends.thread, 60000);
2272 AssertRC(rc);
2273 Assert (mUSBBackends.fThreadRunning || RT_FAILURE(rc));
2274 }
2275}
2276
2277void ConsoleVRDPServer::remoteUSBThreadStop(void)
2278{
2279 mUSBBackends.fThreadRunning = false;
2280
2281 if (mUSBBackends.thread != NIL_RTTHREAD)
2282 {
2283 Assert (mUSBBackends.event != 0);
2284
2285 RTSemEventSignal(mUSBBackends.event);
2286
2287 int rc = RTThreadWait(mUSBBackends.thread, 60000, NULL);
2288 AssertRC(rc);
2289
2290 mUSBBackends.thread = NIL_RTTHREAD;
2291 }
2292
2293 if (mUSBBackends.event)
2294 {
2295 RTSemEventDestroy(mUSBBackends.event);
2296 mUSBBackends.event = 0;
2297 }
2298}
2299#endif /* VBOX_WITH_USB */
2300
2301AuthResult ConsoleVRDPServer::Authenticate(const Guid &uuid, AuthGuestJudgement guestJudgement,
2302 const char *pszUser, const char *pszPassword, const char *pszDomain,
2303 uint32_t u32ClientId)
2304{
2305 AUTHUUID rawuuid;
2306
2307 memcpy(rawuuid, uuid.raw(), sizeof(rawuuid));
2308
2309 LogFlow(("ConsoleVRDPServer::Authenticate: uuid = %RTuuid, guestJudgement = %d, pszUser = %s, pszPassword = %s, pszDomain = %s, u32ClientId = %d\n",
2310 rawuuid, guestJudgement, pszUser, pszPassword, pszDomain, u32ClientId));
2311
2312 /*
2313 * Called only from VRDP input thread. So thread safety is not required.
2314 */
2315
2316 if (!mAuthLibrary)
2317 {
2318 /* Load the external authentication library. */
2319 Bstr authLibrary;
2320 mConsole->getVRDEServer()->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
2321
2322 Utf8Str filename = authLibrary;
2323
2324 LogRel(("AUTH: ConsoleVRDPServer::Authenticate: loading external authentication library '%ls'\n", authLibrary.raw()));
2325
2326 int rc;
2327 if (RTPathHavePath(filename.c_str()))
2328 rc = RTLdrLoad(filename.c_str(), &mAuthLibrary);
2329 else
2330 {
2331 rc = RTLdrLoadAppPriv(filename.c_str(), &mAuthLibrary);
2332 if (RT_FAILURE(rc))
2333 {
2334 /* Backward compatibility with old default 'VRDPAuth' name.
2335 * Try to load new default 'VBoxAuth' instead.
2336 */
2337 if (filename == "VRDPAuth")
2338 {
2339 LogRel(("AUTH: ConsoleVRDPServer::Authenticate: loading external authentication library VBoxAuth\n"));
2340 rc = RTLdrLoadAppPriv("VBoxAuth", &mAuthLibrary);
2341 }
2342 }
2343 }
2344
2345 if (RT_FAILURE(rc))
2346 LogRel(("AUTH: Failed to load external authentication library. Error code: %Rrc\n", rc));
2347
2348 if (RT_SUCCESS(rc))
2349 {
2350 typedef struct AuthEntryInfoStruct
2351 {
2352 const char *pszName;
2353 void **ppvAddress;
2354
2355 } AuthEntryInfo;
2356 AuthEntryInfo entries[] =
2357 {
2358 { AUTHENTRY3_NAME, (void **)&mpfnAuthEntry3 },
2359 { AUTHENTRY2_NAME, (void **)&mpfnAuthEntry2 },
2360 { AUTHENTRY_NAME, (void **)&mpfnAuthEntry },
2361 { NULL, NULL }
2362 };
2363
2364 /* Get the entry point. */
2365 AuthEntryInfo *pEntryInfo = &entries[0];
2366 while (pEntryInfo->pszName)
2367 {
2368 *pEntryInfo->ppvAddress = NULL;
2369
2370 int rc2 = RTLdrGetSymbol(mAuthLibrary, pEntryInfo->pszName, pEntryInfo->ppvAddress);
2371 if (RT_SUCCESS(rc2))
2372 {
2373 /* Found an entry point. */
2374 LogRel(("AUTH: Using entry point '%s'.\n", pEntryInfo->pszName));
2375 rc = VINF_SUCCESS;
2376 break;
2377 }
2378
2379 if (rc2 != VERR_SYMBOL_NOT_FOUND)
2380 {
2381 LogRel(("AUTH: Could not resolve import '%s'. Error code: %Rrc\n", pEntryInfo->pszName, rc2));
2382 }
2383 rc = rc2;
2384
2385 pEntryInfo++;
2386 }
2387 }
2388
2389 if (RT_FAILURE(rc))
2390 {
2391 mConsole->setError(E_FAIL,
2392 mConsole->tr("Could not load the external authentication library '%s' (%Rrc)"),
2393 filename.c_str(),
2394 rc);
2395
2396 mpfnAuthEntry = NULL;
2397 mpfnAuthEntry2 = NULL;
2398 mpfnAuthEntry3 = NULL;
2399
2400 if (mAuthLibrary)
2401 {
2402 RTLdrClose(mAuthLibrary);
2403 mAuthLibrary = 0;
2404 }
2405
2406 return AuthResultAccessDenied;
2407 }
2408 }
2409
2410 Assert(mAuthLibrary && (mpfnAuthEntry || mpfnAuthEntry2 || mpfnAuthEntry3));
2411
2412 AuthResult result = AuthResultAccessDenied;
2413 if (mpfnAuthEntry3)
2414 {
2415 result = mpfnAuthEntry3("vrde", &rawuuid, guestJudgement, pszUser, pszPassword, pszDomain, true, u32ClientId);
2416 }
2417 else if (mpfnAuthEntry2)
2418 {
2419 result = mpfnAuthEntry2(&rawuuid, guestJudgement, pszUser, pszPassword, pszDomain, true, u32ClientId);
2420 }
2421 else if (mpfnAuthEntry)
2422 {
2423 result = mpfnAuthEntry(&rawuuid, guestJudgement, pszUser, pszPassword, pszDomain);
2424 }
2425
2426 switch (result)
2427 {
2428 case AuthResultAccessDenied:
2429 LogRel(("AUTH: external authentication module returned 'access denied'\n"));
2430 break;
2431 case AuthResultAccessGranted:
2432 LogRel(("AUTH: external authentication module returned 'access granted'\n"));
2433 break;
2434 case AuthResultDelegateToGuest:
2435 LogRel(("AUTH: external authentication module returned 'delegate request to guest'\n"));
2436 break;
2437 default:
2438 LogRel(("AUTH: external authentication module returned incorrect return code %d\n", result));
2439 result = AuthResultAccessDenied;
2440 }
2441
2442 LogFlow(("ConsoleVRDPServer::Authenticate: result = %d\n", result));
2443
2444 return result;
2445}
2446
2447void ConsoleVRDPServer::AuthDisconnect(const Guid &uuid, uint32_t u32ClientId)
2448{
2449 AUTHUUID rawuuid;
2450
2451 memcpy(rawuuid, uuid.raw(), sizeof(rawuuid));
2452
2453 LogFlow(("ConsoleVRDPServer::AuthDisconnect: uuid = %RTuuid, u32ClientId = %d\n",
2454 rawuuid, u32ClientId));
2455
2456 Assert(mAuthLibrary && (mpfnAuthEntry || mpfnAuthEntry2 || mpfnAuthEntry3));
2457
2458 if (mpfnAuthEntry3)
2459 mpfnAuthEntry3("vrde", &rawuuid, AuthGuestNotAsked, NULL, NULL, NULL, false, u32ClientId);
2460 else if (mpfnAuthEntry2)
2461 mpfnAuthEntry2(&rawuuid, AuthGuestNotAsked, NULL, NULL, NULL, false, u32ClientId);
2462}
2463
2464int ConsoleVRDPServer::lockConsoleVRDPServer(void)
2465{
2466 int rc = RTCritSectEnter(&mCritSect);
2467 AssertRC(rc);
2468 return rc;
2469}
2470
2471void ConsoleVRDPServer::unlockConsoleVRDPServer(void)
2472{
2473 RTCritSectLeave(&mCritSect);
2474}
2475
2476DECLCALLBACK(int) ConsoleVRDPServer::ClipboardCallback(void *pvCallback,
2477 uint32_t u32ClientId,
2478 uint32_t u32Function,
2479 uint32_t u32Format,
2480 const void *pvData,
2481 uint32_t cbData)
2482{
2483 LogFlowFunc(("pvCallback = %p, u32ClientId = %d, u32Function = %d, u32Format = 0x%08X, pvData = %p, cbData = %d\n",
2484 pvCallback, u32ClientId, u32Function, u32Format, pvData, cbData));
2485
2486 int rc = VINF_SUCCESS;
2487
2488 ConsoleVRDPServer *pServer = static_cast <ConsoleVRDPServer *>(pvCallback);
2489
2490 NOREF(u32ClientId);
2491
2492 switch (u32Function)
2493 {
2494 case VRDE_CLIPBOARD_FUNCTION_FORMAT_ANNOUNCE:
2495 {
2496 if (pServer->mpfnClipboardCallback)
2497 {
2498 pServer->mpfnClipboardCallback(VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE,
2499 u32Format,
2500 (void *)pvData,
2501 cbData);
2502 }
2503 } break;
2504
2505 case VRDE_CLIPBOARD_FUNCTION_DATA_READ:
2506 {
2507 if (pServer->mpfnClipboardCallback)
2508 {
2509 pServer->mpfnClipboardCallback(VBOX_CLIPBOARD_EXT_FN_DATA_READ,
2510 u32Format,
2511 (void *)pvData,
2512 cbData);
2513 }
2514 } break;
2515
2516 default:
2517 rc = VERR_NOT_SUPPORTED;
2518 }
2519
2520 return rc;
2521}
2522
2523DECLCALLBACK(int) ConsoleVRDPServer::ClipboardServiceExtension(void *pvExtension,
2524 uint32_t u32Function,
2525 void *pvParms,
2526 uint32_t cbParms)
2527{
2528 LogFlowFunc(("pvExtension = %p, u32Function = %d, pvParms = %p, cbParms = %d\n",
2529 pvExtension, u32Function, pvParms, cbParms));
2530
2531 int rc = VINF_SUCCESS;
2532
2533 ConsoleVRDPServer *pServer = static_cast <ConsoleVRDPServer *>(pvExtension);
2534
2535 VBOXCLIPBOARDEXTPARMS *pParms = (VBOXCLIPBOARDEXTPARMS *)pvParms;
2536
2537 switch (u32Function)
2538 {
2539 case VBOX_CLIPBOARD_EXT_FN_SET_CALLBACK:
2540 {
2541 pServer->mpfnClipboardCallback = pParms->u.pfnCallback;
2542 } break;
2543
2544 case VBOX_CLIPBOARD_EXT_FN_FORMAT_ANNOUNCE:
2545 {
2546 /* The guest announces clipboard formats. This must be delivered to all clients. */
2547 if (mpEntryPoints && pServer->mhServer)
2548 {
2549 mpEntryPoints->VRDEClipboard(pServer->mhServer,
2550 VRDE_CLIPBOARD_FUNCTION_FORMAT_ANNOUNCE,
2551 pParms->u32Format,
2552 NULL,
2553 0,
2554 NULL);
2555 }
2556 } break;
2557
2558 case VBOX_CLIPBOARD_EXT_FN_DATA_READ:
2559 {
2560 /* The clipboard service expects that the pvData buffer will be filled
2561 * with clipboard data. The server returns the data from the client that
2562 * announced the requested format most recently.
2563 */
2564 if (mpEntryPoints && pServer->mhServer)
2565 {
2566 mpEntryPoints->VRDEClipboard(pServer->mhServer,
2567 VRDE_CLIPBOARD_FUNCTION_DATA_READ,
2568 pParms->u32Format,
2569 pParms->u.pvData,
2570 pParms->cbData,
2571 &pParms->cbData);
2572 }
2573 } break;
2574
2575 case VBOX_CLIPBOARD_EXT_FN_DATA_WRITE:
2576 {
2577 if (mpEntryPoints && pServer->mhServer)
2578 {
2579 mpEntryPoints->VRDEClipboard(pServer->mhServer,
2580 VRDE_CLIPBOARD_FUNCTION_DATA_WRITE,
2581 pParms->u32Format,
2582 pParms->u.pvData,
2583 pParms->cbData,
2584 NULL);
2585 }
2586 } break;
2587
2588 default:
2589 rc = VERR_NOT_SUPPORTED;
2590 }
2591
2592 return rc;
2593}
2594
2595void ConsoleVRDPServer::ClipboardCreate(uint32_t u32ClientId)
2596{
2597 int rc = lockConsoleVRDPServer();
2598
2599 if (RT_SUCCESS(rc))
2600 {
2601 if (mcClipboardRefs == 0)
2602 {
2603 rc = HGCMHostRegisterServiceExtension(&mhClipboard, "VBoxSharedClipboard", ClipboardServiceExtension, this);
2604
2605 if (RT_SUCCESS(rc))
2606 {
2607 mcClipboardRefs++;
2608 }
2609 }
2610
2611 unlockConsoleVRDPServer();
2612 }
2613}
2614
2615void ConsoleVRDPServer::ClipboardDelete(uint32_t u32ClientId)
2616{
2617 int rc = lockConsoleVRDPServer();
2618
2619 if (RT_SUCCESS(rc))
2620 {
2621 mcClipboardRefs--;
2622
2623 if (mcClipboardRefs == 0)
2624 {
2625 HGCMHostUnregisterServiceExtension(mhClipboard);
2626 }
2627
2628 unlockConsoleVRDPServer();
2629 }
2630}
2631
2632/* That is called on INPUT thread of the VRDP server.
2633 * The ConsoleVRDPServer keeps a list of created backend instances.
2634 */
2635void ConsoleVRDPServer::USBBackendCreate(uint32_t u32ClientId, void **ppvIntercept)
2636{
2637#ifdef VBOX_WITH_USB
2638 LogFlow(("ConsoleVRDPServer::USBBackendCreate: u32ClientId = %d\n", u32ClientId));
2639
2640 /* Create a new instance of the USB backend for the new client. */
2641 RemoteUSBBackend *pRemoteUSBBackend = new RemoteUSBBackend(mConsole, this, u32ClientId);
2642
2643 if (pRemoteUSBBackend)
2644 {
2645 pRemoteUSBBackend->AddRef(); /* 'Release' called in USBBackendDelete. */
2646
2647 /* Append the new instance in the list. */
2648 int rc = lockConsoleVRDPServer();
2649
2650 if (RT_SUCCESS(rc))
2651 {
2652 pRemoteUSBBackend->pNext = mUSBBackends.pHead;
2653 if (mUSBBackends.pHead)
2654 {
2655 mUSBBackends.pHead->pPrev = pRemoteUSBBackend;
2656 }
2657 else
2658 {
2659 mUSBBackends.pTail = pRemoteUSBBackend;
2660 }
2661
2662 mUSBBackends.pHead = pRemoteUSBBackend;
2663
2664 unlockConsoleVRDPServer();
2665
2666 if (ppvIntercept)
2667 {
2668 *ppvIntercept = pRemoteUSBBackend;
2669 }
2670 }
2671
2672 if (RT_FAILURE(rc))
2673 {
2674 pRemoteUSBBackend->Release();
2675 }
2676 }
2677#endif /* VBOX_WITH_USB */
2678}
2679
2680void ConsoleVRDPServer::USBBackendDelete(uint32_t u32ClientId)
2681{
2682#ifdef VBOX_WITH_USB
2683 LogFlow(("ConsoleVRDPServer::USBBackendDelete: u32ClientId = %d\n", u32ClientId));
2684
2685 RemoteUSBBackend *pRemoteUSBBackend = NULL;
2686
2687 /* Find the instance. */
2688 int rc = lockConsoleVRDPServer();
2689
2690 if (RT_SUCCESS(rc))
2691 {
2692 pRemoteUSBBackend = usbBackendFind(u32ClientId);
2693
2694 if (pRemoteUSBBackend)
2695 {
2696 /* Notify that it will be deleted. */
2697 pRemoteUSBBackend->NotifyDelete();
2698 }
2699
2700 unlockConsoleVRDPServer();
2701 }
2702
2703 if (pRemoteUSBBackend)
2704 {
2705 /* Here the instance has been excluded from the list and can be dereferenced. */
2706 pRemoteUSBBackend->Release();
2707 }
2708#endif
2709}
2710
2711void *ConsoleVRDPServer::USBBackendRequestPointer(uint32_t u32ClientId, const Guid *pGuid)
2712{
2713#ifdef VBOX_WITH_USB
2714 RemoteUSBBackend *pRemoteUSBBackend = NULL;
2715
2716 /* Find the instance. */
2717 int rc = lockConsoleVRDPServer();
2718
2719 if (RT_SUCCESS(rc))
2720 {
2721 pRemoteUSBBackend = usbBackendFind(u32ClientId);
2722
2723 if (pRemoteUSBBackend)
2724 {
2725 /* Inform the backend instance that it is referenced by the Guid. */
2726 bool fAdded = pRemoteUSBBackend->addUUID(pGuid);
2727
2728 if (fAdded)
2729 {
2730 /* Reference the instance because its pointer is being taken. */
2731 pRemoteUSBBackend->AddRef(); /* 'Release' is called in USBBackendReleasePointer. */
2732 }
2733 else
2734 {
2735 pRemoteUSBBackend = NULL;
2736 }
2737 }
2738
2739 unlockConsoleVRDPServer();
2740 }
2741
2742 if (pRemoteUSBBackend)
2743 {
2744 return pRemoteUSBBackend->GetBackendCallbackPointer();
2745 }
2746
2747#endif
2748 return NULL;
2749}
2750
2751void ConsoleVRDPServer::USBBackendReleasePointer(const Guid *pGuid)
2752{
2753#ifdef VBOX_WITH_USB
2754 RemoteUSBBackend *pRemoteUSBBackend = NULL;
2755
2756 /* Find the instance. */
2757 int rc = lockConsoleVRDPServer();
2758
2759 if (RT_SUCCESS(rc))
2760 {
2761 pRemoteUSBBackend = usbBackendFindByUUID(pGuid);
2762
2763 if (pRemoteUSBBackend)
2764 {
2765 pRemoteUSBBackend->removeUUID(pGuid);
2766 }
2767
2768 unlockConsoleVRDPServer();
2769
2770 if (pRemoteUSBBackend)
2771 {
2772 pRemoteUSBBackend->Release();
2773 }
2774 }
2775#endif
2776}
2777
2778RemoteUSBBackend *ConsoleVRDPServer::usbBackendGetNext(RemoteUSBBackend *pRemoteUSBBackend)
2779{
2780 LogFlow(("ConsoleVRDPServer::usbBackendGetNext: pBackend = %p\n", pRemoteUSBBackend));
2781
2782 RemoteUSBBackend *pNextRemoteUSBBackend = NULL;
2783#ifdef VBOX_WITH_USB
2784
2785 int rc = lockConsoleVRDPServer();
2786
2787 if (RT_SUCCESS(rc))
2788 {
2789 if (pRemoteUSBBackend == NULL)
2790 {
2791 /* The first backend in the list is requested. */
2792 pNextRemoteUSBBackend = mUSBBackends.pHead;
2793 }
2794 else
2795 {
2796 /* Get pointer to the next backend. */
2797 pNextRemoteUSBBackend = (RemoteUSBBackend *)pRemoteUSBBackend->pNext;
2798 }
2799
2800 if (pNextRemoteUSBBackend)
2801 {
2802 pNextRemoteUSBBackend->AddRef();
2803 }
2804
2805 unlockConsoleVRDPServer();
2806
2807 if (pRemoteUSBBackend)
2808 {
2809 pRemoteUSBBackend->Release();
2810 }
2811 }
2812#endif
2813
2814 return pNextRemoteUSBBackend;
2815}
2816
2817#ifdef VBOX_WITH_USB
2818/* Internal method. Called under the ConsoleVRDPServerLock. */
2819RemoteUSBBackend *ConsoleVRDPServer::usbBackendFind(uint32_t u32ClientId)
2820{
2821 RemoteUSBBackend *pRemoteUSBBackend = mUSBBackends.pHead;
2822
2823 while (pRemoteUSBBackend)
2824 {
2825 if (pRemoteUSBBackend->ClientId() == u32ClientId)
2826 {
2827 break;
2828 }
2829
2830 pRemoteUSBBackend = (RemoteUSBBackend *)pRemoteUSBBackend->pNext;
2831 }
2832
2833 return pRemoteUSBBackend;
2834}
2835
2836/* Internal method. Called under the ConsoleVRDPServerLock. */
2837RemoteUSBBackend *ConsoleVRDPServer::usbBackendFindByUUID(const Guid *pGuid)
2838{
2839 RemoteUSBBackend *pRemoteUSBBackend = mUSBBackends.pHead;
2840
2841 while (pRemoteUSBBackend)
2842 {
2843 if (pRemoteUSBBackend->findUUID(pGuid))
2844 {
2845 break;
2846 }
2847
2848 pRemoteUSBBackend = (RemoteUSBBackend *)pRemoteUSBBackend->pNext;
2849 }
2850
2851 return pRemoteUSBBackend;
2852}
2853#endif
2854
2855/* Internal method. Called by the backend destructor. */
2856void ConsoleVRDPServer::usbBackendRemoveFromList(RemoteUSBBackend *pRemoteUSBBackend)
2857{
2858#ifdef VBOX_WITH_USB
2859 int rc = lockConsoleVRDPServer();
2860 AssertRC(rc);
2861
2862 /* Exclude the found instance from the list. */
2863 if (pRemoteUSBBackend->pNext)
2864 {
2865 pRemoteUSBBackend->pNext->pPrev = pRemoteUSBBackend->pPrev;
2866 }
2867 else
2868 {
2869 mUSBBackends.pTail = (RemoteUSBBackend *)pRemoteUSBBackend->pPrev;
2870 }
2871
2872 if (pRemoteUSBBackend->pPrev)
2873 {
2874 pRemoteUSBBackend->pPrev->pNext = pRemoteUSBBackend->pNext;
2875 }
2876 else
2877 {
2878 mUSBBackends.pHead = (RemoteUSBBackend *)pRemoteUSBBackend->pNext;
2879 }
2880
2881 pRemoteUSBBackend->pNext = pRemoteUSBBackend->pPrev = NULL;
2882
2883 unlockConsoleVRDPServer();
2884#endif
2885}
2886
2887
2888void ConsoleVRDPServer::SendUpdate(unsigned uScreenId, void *pvUpdate, uint32_t cbUpdate) const
2889{
2890 if (mpEntryPoints && mhServer)
2891 {
2892 mpEntryPoints->VRDEUpdate(mhServer, uScreenId, pvUpdate, cbUpdate);
2893 }
2894}
2895
2896void ConsoleVRDPServer::SendResize(void) const
2897{
2898 if (mpEntryPoints && mhServer)
2899 {
2900 mpEntryPoints->VRDEResize(mhServer);
2901 }
2902}
2903
2904void ConsoleVRDPServer::SendUpdateBitmap(unsigned uScreenId, uint32_t x, uint32_t y, uint32_t w, uint32_t h) const
2905{
2906 VRDEORDERHDR update;
2907 update.x = x;
2908 update.y = y;
2909 update.w = w;
2910 update.h = h;
2911 if (mpEntryPoints && mhServer)
2912 {
2913 mpEntryPoints->VRDEUpdate(mhServer, uScreenId, &update, sizeof(update));
2914 }
2915}
2916
2917void ConsoleVRDPServer::SendAudioSamples(void *pvSamples, uint32_t cSamples, VRDEAUDIOFORMAT format) const
2918{
2919 if (mpEntryPoints && mhServer)
2920 {
2921 mpEntryPoints->VRDEAudioSamples(mhServer, pvSamples, cSamples, format);
2922 }
2923}
2924
2925void ConsoleVRDPServer::SendAudioVolume(uint16_t left, uint16_t right) const
2926{
2927 if (mpEntryPoints && mhServer)
2928 {
2929 mpEntryPoints->VRDEAudioVolume(mhServer, left, right);
2930 }
2931}
2932
2933void ConsoleVRDPServer::SendUSBRequest(uint32_t u32ClientId, void *pvParms, uint32_t cbParms) const
2934{
2935 if (mpEntryPoints && mhServer)
2936 {
2937 mpEntryPoints->VRDEUSBRequest(mhServer, u32ClientId, pvParms, cbParms);
2938 }
2939}
2940
2941/* @todo rc not needed? */
2942int ConsoleVRDPServer::SendAudioInputBegin(void **ppvUserCtx,
2943 void *pvContext,
2944 uint32_t cSamples,
2945 uint32_t iSampleHz,
2946 uint32_t cChannels,
2947 uint32_t cBits)
2948{
2949 if (mpEntryPoints && mhServer && mpEntryPoints->VRDEAudioInOpen)
2950 {
2951 uint32_t u32ClientId = ASMAtomicReadU32(&mu32AudioInputClientId);
2952 if (u32ClientId != 0) /* 0 would mean broadcast to all clients. */
2953 {
2954 VRDEAUDIOFORMAT audioFormat = VRDE_AUDIO_FMT_MAKE(iSampleHz, cChannels, cBits, 0);
2955 mpEntryPoints->VRDEAudioInOpen (mhServer,
2956 pvContext,
2957 u32ClientId,
2958 audioFormat,
2959 cSamples);
2960 *ppvUserCtx = NULL; /* This is the ConsoleVRDPServer context.
2961 * Currently not used because only one client is allowed to
2962 * do audio input and the client id is saved by the ConsoleVRDPServer.
2963 */
2964
2965 return VINF_SUCCESS;
2966 }
2967 }
2968 return VERR_NOT_SUPPORTED;
2969}
2970
2971void ConsoleVRDPServer::SendAudioInputEnd(void *pvUserCtx)
2972{
2973 if (mpEntryPoints && mhServer && mpEntryPoints->VRDEAudioInClose)
2974 {
2975 uint32_t u32ClientId = ASMAtomicReadU32(&mu32AudioInputClientId);
2976 if (u32ClientId != 0) /* 0 would mean broadcast to all clients. */
2977 {
2978 mpEntryPoints->VRDEAudioInClose(mhServer, u32ClientId);
2979 }
2980 }
2981}
2982
2983#ifdef VBOX_WITH_USB_VIDEO
2984int ConsoleVRDPServer::GetVideoFrameDimensions(uint16_t *pu16Heigh, uint16_t *pu16Width)
2985{
2986 *pu16Heigh = 640;
2987 *pu16Width = 480;
2988 return VINF_SUCCESS;
2989}
2990
2991int ConsoleVRDPServer::SendVideoSreamOn(bool fFetch)
2992{
2993 /* Here we inform server that guest is starting/stopping
2994 * the stream
2995 */
2996 return VINF_SUCCESS;
2997}
2998#endif
2999
3000
3001
3002void ConsoleVRDPServer::QueryInfo(uint32_t index, void *pvBuffer, uint32_t cbBuffer, uint32_t *pcbOut) const
3003{
3004 if (index == VRDE_QI_PORT)
3005 {
3006 uint32_t cbOut = sizeof(int32_t);
3007
3008 if (cbBuffer >= cbOut)
3009 {
3010 *pcbOut = cbOut;
3011 *(int32_t *)pvBuffer = (int32_t)mVRDPBindPort;
3012 }
3013 }
3014 else if (mpEntryPoints && mhServer)
3015 {
3016 mpEntryPoints->VRDEQueryInfo(mhServer, index, pvBuffer, cbBuffer, pcbOut);
3017 }
3018}
3019
3020/* static */ int ConsoleVRDPServer::loadVRDPLibrary(const char *pszLibraryName)
3021{
3022 int rc = VINF_SUCCESS;
3023
3024 if (mVRDPLibrary == NIL_RTLDRMOD)
3025 {
3026 RTERRINFOSTATIC ErrInfo;
3027 RTErrInfoInitStatic(&ErrInfo);
3028
3029 if (RTPathHavePath(pszLibraryName))
3030 rc = SUPR3HardenedLdrLoadPlugIn(pszLibraryName, &mVRDPLibrary, &ErrInfo.Core);
3031 else
3032 rc = SUPR3HardenedLdrLoadAppPriv(pszLibraryName, &mVRDPLibrary, RTLDRLOAD_FLAGS_LOCAL, &ErrInfo.Core);
3033 if (RT_SUCCESS(rc))
3034 {
3035 struct SymbolEntry
3036 {
3037 const char *name;
3038 void **ppfn;
3039 };
3040
3041 #define DEFSYMENTRY(a) { #a, (void**)&mpfn##a }
3042
3043 static const struct SymbolEntry s_aSymbols[] =
3044 {
3045 DEFSYMENTRY(VRDECreateServer)
3046 };
3047
3048 #undef DEFSYMENTRY
3049
3050 for (unsigned i = 0; i < RT_ELEMENTS(s_aSymbols); i++)
3051 {
3052 rc = RTLdrGetSymbol(mVRDPLibrary, s_aSymbols[i].name, s_aSymbols[i].ppfn);
3053
3054 if (RT_FAILURE(rc))
3055 {
3056 LogRel(("VRDE: Error resolving symbol '%s', rc %Rrc.\n", s_aSymbols[i].name, rc));
3057 break;
3058 }
3059 }
3060 }
3061 else
3062 {
3063 if (RTErrInfoIsSet(&ErrInfo.Core))
3064 LogRel(("VRDE: Error loading the library '%s': %s (%Rrc)\n", pszLibraryName, ErrInfo.Core.pszMsg, rc));
3065 else
3066 LogRel(("VRDE: Error loading the library '%s' rc = %Rrc.\n", pszLibraryName, rc));
3067
3068 mVRDPLibrary = NIL_RTLDRMOD;
3069 }
3070 }
3071
3072 if (RT_FAILURE(rc))
3073 {
3074 if (mVRDPLibrary != NIL_RTLDRMOD)
3075 {
3076 RTLdrClose(mVRDPLibrary);
3077 mVRDPLibrary = NIL_RTLDRMOD;
3078 }
3079 }
3080
3081 return rc;
3082}
3083
3084/*
3085 * IVRDEServerInfo implementation.
3086 */
3087// constructor / destructor
3088/////////////////////////////////////////////////////////////////////////////
3089
3090VRDEServerInfo::VRDEServerInfo()
3091 : mParent(NULL)
3092{
3093}
3094
3095VRDEServerInfo::~VRDEServerInfo()
3096{
3097}
3098
3099
3100HRESULT VRDEServerInfo::FinalConstruct()
3101{
3102 return BaseFinalConstruct();
3103}
3104
3105void VRDEServerInfo::FinalRelease()
3106{
3107 uninit();
3108 BaseFinalRelease();
3109}
3110
3111// public methods only for internal purposes
3112/////////////////////////////////////////////////////////////////////////////
3113
3114/**
3115 * Initializes the guest object.
3116 */
3117HRESULT VRDEServerInfo::init(Console *aParent)
3118{
3119 LogFlowThisFunc(("aParent=%p\n", aParent));
3120
3121 ComAssertRet(aParent, E_INVALIDARG);
3122
3123 /* Enclose the state transition NotReady->InInit->Ready */
3124 AutoInitSpan autoInitSpan(this);
3125 AssertReturn(autoInitSpan.isOk(), E_FAIL);
3126
3127 unconst(mParent) = aParent;
3128
3129 /* Confirm a successful initialization */
3130 autoInitSpan.setSucceeded();
3131
3132 return S_OK;
3133}
3134
3135/**
3136 * Uninitializes the instance and sets the ready flag to FALSE.
3137 * Called either from FinalRelease() or by the parent when it gets destroyed.
3138 */
3139void VRDEServerInfo::uninit()
3140{
3141 LogFlowThisFunc(("\n"));
3142
3143 /* Enclose the state transition Ready->InUninit->NotReady */
3144 AutoUninitSpan autoUninitSpan(this);
3145 if (autoUninitSpan.uninitDone())
3146 return;
3147
3148 unconst(mParent) = NULL;
3149}
3150
3151// IVRDEServerInfo properties
3152/////////////////////////////////////////////////////////////////////////////
3153
3154#define IMPL_GETTER_BOOL(_aType, _aName, _aIndex) \
3155 STDMETHODIMP VRDEServerInfo::COMGETTER(_aName)(_aType *a##_aName) \
3156 { \
3157 if (!a##_aName) \
3158 return E_POINTER; \
3159 \
3160 AutoCaller autoCaller(this); \
3161 if (FAILED(autoCaller.rc())) return autoCaller.rc(); \
3162 \
3163 /* todo: Not sure if a AutoReadLock would be sufficient. */ \
3164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); \
3165 \
3166 uint32_t value; \
3167 uint32_t cbOut = 0; \
3168 \
3169 mParent->consoleVRDPServer()->QueryInfo \
3170 (_aIndex, &value, sizeof(value), &cbOut); \
3171 \
3172 *a##_aName = cbOut? !!value: FALSE; \
3173 \
3174 return S_OK; \
3175 } \
3176 extern void IMPL_GETTER_BOOL_DUMMY(void)
3177
3178#define IMPL_GETTER_SCALAR(_aType, _aName, _aIndex, _aValueMask) \
3179 STDMETHODIMP VRDEServerInfo::COMGETTER(_aName)(_aType *a##_aName) \
3180 { \
3181 if (!a##_aName) \
3182 return E_POINTER; \
3183 \
3184 AutoCaller autoCaller(this); \
3185 if (FAILED(autoCaller.rc())) return autoCaller.rc(); \
3186 \
3187 /* todo: Not sure if a AutoReadLock would be sufficient. */ \
3188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); \
3189 \
3190 _aType value; \
3191 uint32_t cbOut = 0; \
3192 \
3193 mParent->consoleVRDPServer()->QueryInfo \
3194 (_aIndex, &value, sizeof(value), &cbOut); \
3195 \
3196 if (_aValueMask) value &= (_aValueMask); \
3197 *a##_aName = cbOut? value: 0; \
3198 \
3199 return S_OK; \
3200 } \
3201 extern void IMPL_GETTER_SCALAR_DUMMY(void)
3202
3203#define IMPL_GETTER_BSTR(_aType, _aName, _aIndex) \
3204 STDMETHODIMP VRDEServerInfo::COMGETTER(_aName)(_aType *a##_aName) \
3205 { \
3206 if (!a##_aName) \
3207 return E_POINTER; \
3208 \
3209 AutoCaller autoCaller(this); \
3210 if (FAILED(autoCaller.rc())) return autoCaller.rc(); \
3211 \
3212 /* todo: Not sure if a AutoReadLock would be sufficient. */ \
3213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); \
3214 \
3215 uint32_t cbOut = 0; \
3216 \
3217 mParent->consoleVRDPServer()->QueryInfo \
3218 (_aIndex, NULL, 0, &cbOut); \
3219 \
3220 if (cbOut == 0) \
3221 { \
3222 Bstr str(""); \
3223 str.cloneTo(a##_aName); \
3224 return S_OK; \
3225 } \
3226 \
3227 char *pchBuffer = (char *)RTMemTmpAlloc(cbOut); \
3228 \
3229 if (!pchBuffer) \
3230 { \
3231 Log(("VRDEServerInfo::" \
3232 #_aName \
3233 ": Failed to allocate memory %d bytes\n", cbOut)); \
3234 return E_OUTOFMEMORY; \
3235 } \
3236 \
3237 mParent->consoleVRDPServer()->QueryInfo \
3238 (_aIndex, pchBuffer, cbOut, &cbOut); \
3239 \
3240 Bstr str(pchBuffer); \
3241 \
3242 str.cloneTo(a##_aName); \
3243 \
3244 RTMemTmpFree(pchBuffer); \
3245 \
3246 return S_OK; \
3247 } \
3248 extern void IMPL_GETTER_BSTR_DUMMY(void)
3249
3250IMPL_GETTER_BOOL (BOOL, Active, VRDE_QI_ACTIVE);
3251IMPL_GETTER_SCALAR (LONG, Port, VRDE_QI_PORT, 0);
3252IMPL_GETTER_SCALAR (ULONG, NumberOfClients, VRDE_QI_NUMBER_OF_CLIENTS, 0);
3253IMPL_GETTER_SCALAR (LONG64, BeginTime, VRDE_QI_BEGIN_TIME, 0);
3254IMPL_GETTER_SCALAR (LONG64, EndTime, VRDE_QI_END_TIME, 0);
3255IMPL_GETTER_SCALAR (LONG64, BytesSent, VRDE_QI_BYTES_SENT, INT64_MAX);
3256IMPL_GETTER_SCALAR (LONG64, BytesSentTotal, VRDE_QI_BYTES_SENT_TOTAL, INT64_MAX);
3257IMPL_GETTER_SCALAR (LONG64, BytesReceived, VRDE_QI_BYTES_RECEIVED, INT64_MAX);
3258IMPL_GETTER_SCALAR (LONG64, BytesReceivedTotal, VRDE_QI_BYTES_RECEIVED_TOTAL, INT64_MAX);
3259IMPL_GETTER_BSTR (BSTR, User, VRDE_QI_USER);
3260IMPL_GETTER_BSTR (BSTR, Domain, VRDE_QI_DOMAIN);
3261IMPL_GETTER_BSTR (BSTR, ClientName, VRDE_QI_CLIENT_NAME);
3262IMPL_GETTER_BSTR (BSTR, ClientIP, VRDE_QI_CLIENT_IP);
3263IMPL_GETTER_SCALAR (ULONG, ClientVersion, VRDE_QI_CLIENT_VERSION, 0);
3264IMPL_GETTER_SCALAR (ULONG, EncryptionStyle, VRDE_QI_ENCRYPTION_STYLE, 0);
3265
3266#undef IMPL_GETTER_BSTR
3267#undef IMPL_GETTER_SCALAR
3268#undef IMPL_GETTER_BOOL
3269/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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