summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-server/VRDEServerImpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-server/VRDEServerImpl.cpp')
-rw-r--r--src/VBox/Main/src-server/VRDEServerImpl.cpp975
1 files changed, 975 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/VRDEServerImpl.cpp b/src/VBox/Main/src-server/VRDEServerImpl.cpp
new file mode 100644
index 00000000..9c21f7f0
--- /dev/null
+++ b/src/VBox/Main/src-server/VRDEServerImpl.cpp
@@ -0,0 +1,975 @@
+/* $Id: VRDEServerImpl.cpp $ */
+/** @file
+ * VirtualBox COM class implementation
+ */
+
+/*
+ * Copyright (C) 2006-2022 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN_VRDESERVER
+#include "VRDEServerImpl.h"
+#include "MachineImpl.h"
+#include "VirtualBoxImpl.h"
+#ifdef VBOX_WITH_EXTPACK
+# include "ExtPackManagerImpl.h"
+#endif
+
+#include <iprt/cpp/utils.h>
+#include <iprt/ctype.h>
+#include <iprt/ldr.h>
+#include <iprt/path.h>
+
+#include <VBox/err.h>
+#include <VBox/sup.h>
+#include <VBox/com/array.h>
+
+#include <VBox/RemoteDesktop/VRDE.h>
+
+#include "AutoStateDep.h"
+#include "AutoCaller.h"
+#include "Global.h"
+#include "LoggingNew.h"
+
+// defines
+/////////////////////////////////////////////////////////////////////////////
+#define VRDP_DEFAULT_PORT_STR "3389"
+
+// constructor / destructor
+/////////////////////////////////////////////////////////////////////////////
+
+VRDEServer::VRDEServer()
+ : mParent(NULL)
+{
+}
+
+VRDEServer::~VRDEServer()
+{
+}
+
+HRESULT VRDEServer::FinalConstruct()
+{
+ return BaseFinalConstruct();
+}
+
+void VRDEServer::FinalRelease()
+{
+ uninit();
+ BaseFinalRelease();
+}
+
+// public initializer/uninitializer for internal purposes only
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Initializes the VRDP server object.
+ *
+ * @param aParent Handle of the parent object.
+ */
+HRESULT VRDEServer::init(Machine *aParent)
+{
+ LogFlowThisFunc(("aParent=%p\n", aParent));
+
+ ComAssertRet(aParent, E_INVALIDARG);
+
+ /* Enclose the state transition NotReady->InInit->Ready */
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ unconst(mParent) = aParent;
+ /* mPeer is left null */
+
+ mData.allocate();
+
+ mData->fEnabled = false;
+
+ /* Confirm a successful initialization */
+ autoInitSpan.setSucceeded();
+
+ return S_OK;
+}
+
+/**
+ * Initializes the object given another object
+ * (a kind of copy constructor). This object shares data with
+ * the object passed as an argument.
+ *
+ * @note This object must be destroyed before the original object
+ * it shares data with is destroyed.
+ *
+ * @note Locks @a aThat object for reading.
+ */
+HRESULT VRDEServer::init(Machine *aParent, VRDEServer *aThat)
+{
+ LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
+
+ ComAssertRet(aParent && aThat, E_INVALIDARG);
+
+ /* Enclose the state transition NotReady->InInit->Ready */
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ unconst(mParent) = aParent;
+ unconst(mPeer) = aThat;
+
+ AutoCaller thatCaller(aThat);
+ AssertComRCReturnRC(thatCaller.rc());
+
+ AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
+ mData.share(aThat->mData);
+
+ /* Confirm a successful initialization */
+ autoInitSpan.setSucceeded();
+
+ return S_OK;
+}
+
+/**
+ * Initializes the guest object given another guest object
+ * (a kind of copy constructor). This object makes a private copy of data
+ * of the original object passed as an argument.
+ *
+ * @note Locks @a aThat object for reading.
+ */
+HRESULT VRDEServer::initCopy(Machine *aParent, VRDEServer *aThat)
+{
+ LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat));
+
+ ComAssertRet(aParent && aThat, E_INVALIDARG);
+
+ /* Enclose the state transition NotReady->InInit->Ready */
+ AutoInitSpan autoInitSpan(this);
+ AssertReturn(autoInitSpan.isOk(), E_FAIL);
+
+ unconst(mParent) = aParent;
+ /* mPeer is left null */
+
+ AutoCaller thatCaller(aThat);
+ AssertComRCReturnRC(thatCaller.rc());
+
+ AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS);
+ mData.attachCopy(aThat->mData);
+
+ /* Confirm a successful initialization */
+ autoInitSpan.setSucceeded();
+
+ return S_OK;
+}
+
+/**
+ * Uninitializes the instance and sets the ready flag to FALSE.
+ * Called either from FinalRelease() or by the parent when it gets destroyed.
+ */
+void VRDEServer::uninit()
+{
+ LogFlowThisFunc(("\n"));
+
+ /* Enclose the state transition Ready->InUninit->NotReady */
+ AutoUninitSpan autoUninitSpan(this);
+ if (autoUninitSpan.uninitDone())
+ return;
+
+ mData.free();
+
+ unconst(mPeer) = NULL;
+ unconst(mParent) = NULL;
+}
+
+/**
+ * Loads settings from the given machine node.
+ * May be called once right after this object creation.
+ *
+ * @param data Configuration settings.
+ *
+ * @note Locks this object for writing.
+ */
+HRESULT VRDEServer::i_loadSettings(const settings::VRDESettings &data)
+{
+ using namespace settings;
+
+ AutoCaller autoCaller(this);
+ AssertComRCReturnRC(autoCaller.rc());
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ mData.assignCopy(&data);
+
+ return S_OK;
+}
+
+/**
+ * Saves settings to the given machine node.
+ *
+ * @param data Configuration settings.
+ *
+ * @note Locks this object for reading.
+ */
+HRESULT VRDEServer::i_saveSettings(settings::VRDESettings &data)
+{
+ AutoCaller autoCaller(this);
+ AssertComRCReturnRC(autoCaller.rc());
+
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ data = *mData.data();
+
+ return S_OK;
+}
+
+// IVRDEServer properties
+/////////////////////////////////////////////////////////////////////////////
+
+HRESULT VRDEServer::getEnabled(BOOL *aEnabled)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aEnabled = mData->fEnabled;
+
+ return S_OK;
+}
+
+HRESULT VRDEServer::setEnabled(BOOL aEnabled)
+{
+ /* the machine can also be in saved state for this property to change */
+ AutoMutableOrSavedOrRunningStateDependency adep(mParent);
+ if (FAILED(adep.rc())) return adep.rc();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ HRESULT rc = S_OK;
+
+ if (mData->fEnabled != RT_BOOL(aEnabled))
+ {
+ mData.backup();
+ mData->fEnabled = RT_BOOL(aEnabled);
+
+ /* leave the lock before informing callbacks */
+ alock.release();
+
+ AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
+ mParent->i_setModified(Machine::IsModified_VRDEServer);
+ mlock.release();
+
+ /* Avoid deadlock when i_onVRDEServerChange eventually calls SetExtraData. */
+ adep.release();
+
+ rc = mParent->i_onVRDEServerChange(/* aRestart */ TRUE);
+ if (FAILED(rc))
+ {
+ /* Failed to enable/disable the server. Revert the internal state. */
+ adep.add();
+ if (SUCCEEDED(adep.rc()))
+ {
+ alock.acquire();
+ mData->fEnabled = !RT_BOOL(aEnabled);
+ alock.release();
+ mlock.acquire();
+ mParent->i_setModified(Machine::IsModified_VRDEServer);
+ }
+ }
+ }
+
+ return rc;
+}
+
+static int i_portParseNumber(uint16_t *pu16Port, const char *pszStart, const char *pszEnd)
+{
+ /* Gets a string of digits, converts to 16 bit port number.
+ * Note: pszStart <= pszEnd is expected, the string contains
+ * only digits and pszEnd points to the char after last
+ * digit.
+ */
+ size_t cch = (size_t)(pszEnd - pszStart);
+ if (cch > 0 && cch <= 5) /* Port is up to 5 decimal digits. */
+ {
+ unsigned uPort = 0;
+ while (pszStart != pszEnd)
+ {
+ uPort = uPort * 10 + (unsigned)(*pszStart - '0');
+ pszStart++;
+ }
+
+ if (uPort != 0 && uPort < 0x10000)
+ {
+ if (pu16Port)
+ *pu16Port = (uint16_t)uPort;
+ return VINF_SUCCESS;
+ }
+ }
+
+ return VERR_INVALID_PARAMETER;
+}
+
+static int i_vrdpServerVerifyPortsString(const com::Utf8Str &aPortRange)
+{
+ const char *pszPortRange = aPortRange.c_str();
+
+ if (!pszPortRange || *pszPortRange == 0) /* Reject empty string. */
+ return VERR_INVALID_PARAMETER;
+
+ /* The string should be like "1000-1010,1020,2000-2003" */
+ while (*pszPortRange)
+ {
+ const char *pszStart = pszPortRange;
+ const char *pszDash = NULL;
+ const char *pszEnd = pszStart;
+
+ while (*pszEnd && *pszEnd != ',')
+ {
+ if (*pszEnd == '-')
+ {
+ if (pszDash != NULL)
+ return VERR_INVALID_PARAMETER; /* More than one '-'. */
+
+ pszDash = pszEnd;
+ }
+ else if (!RT_C_IS_DIGIT(*pszEnd))
+ return VERR_INVALID_PARAMETER;
+
+ pszEnd++;
+ }
+
+ /* Update the next range pointer. */
+ pszPortRange = pszEnd;
+ if (*pszPortRange == ',')
+ {
+ pszPortRange++;
+ }
+
+ /* A probably valid range. Verify and parse it. */
+ int rc;
+ if (pszDash)
+ {
+ rc = i_portParseNumber(NULL, pszStart, pszDash);
+ if (RT_SUCCESS(rc))
+ rc = i_portParseNumber(NULL, pszDash + 1, pszEnd);
+ }
+ else
+ rc = i_portParseNumber(NULL, pszStart, pszEnd);
+
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+HRESULT VRDEServer::setVRDEProperty(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
+{
+ LogFlowThisFunc(("\n"));
+
+ /* the machine can also be in saved state for this property to change */
+ AutoMutableOrSavedOrRunningStateDependency adep(mParent);
+ if (FAILED(adep.rc())) return adep.rc();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ /* Special processing for some "standard" properties. */
+ if (aKey == "TCP/Ports")
+ {
+ /* Verify the string. "0" means the default port. */
+ Utf8Str strPorts = aValue == "0"?
+ VRDP_DEFAULT_PORT_STR:
+ aValue;
+ int vrc = i_vrdpServerVerifyPortsString(strPorts);
+ if (RT_FAILURE(vrc))
+ return E_INVALIDARG;
+
+ if (strPorts != mData->mapProperties["TCP/Ports"])
+ {
+ /* Port value is not verified here because it is up to VRDP transport to
+ * use it. Specifying a wrong port number will cause a running server to
+ * stop. There is no fool proof here.
+ */
+ mData.backup();
+ mData->mapProperties["TCP/Ports"] = strPorts;
+
+ /* leave the lock before informing callbacks */
+ alock.release();
+
+ AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
+ mParent->i_setModified(Machine::IsModified_VRDEServer);
+ mlock.release();
+
+ /* Avoid deadlock when i_onVRDEServerChange eventually calls SetExtraData. */
+ adep.release();
+
+ mParent->i_onVRDEServerChange(/* aRestart */ TRUE);
+ }
+ }
+ else
+ {
+ /* Generic properties processing.
+ * Look up the old value first; if nothing's changed then do nothing.
+ */
+ Utf8Str strOldValue;
+
+ settings::StringsMap::const_iterator it = mData->mapProperties.find(aKey);
+ if (it != mData->mapProperties.end())
+ strOldValue = it->second;
+
+ if (strOldValue != aValue)
+ {
+ if (aValue.isEmpty())
+ mData->mapProperties.erase(aKey);
+ else
+ mData->mapProperties[aKey] = aValue;
+
+ /* leave the lock before informing callbacks */
+ alock.release();
+
+ AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS);
+ mParent->i_setModified(Machine::IsModified_VRDEServer);
+ mlock.release();
+
+ /* Avoid deadlock when i_onVRDEServerChange eventually calls SetExtraData. */
+ adep.release();
+
+ mParent->i_onVRDEServerChange(/* aRestart */ TRUE);
+ }
+ }
+
+ return S_OK;
+}
+
+HRESULT VRDEServer::getVRDEProperty(const com::Utf8Str &aKey, com::Utf8Str &aValue)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ settings::StringsMap::const_iterator it = mData->mapProperties.find(aKey);
+ if (it != mData->mapProperties.end())
+ aValue = it->second; // source is a Utf8Str
+ else if (aKey == "TCP/Ports")
+ aValue = VRDP_DEFAULT_PORT_STR;
+
+ return S_OK;
+}
+
+/*
+ * Work around clang being unhappy about PFNVRDESUPPORTEDPROPERTIES
+ * ("exception specifications are not allowed beyond a single level of
+ * indirection"). The original comment for 13.0 check said: "assuming
+ * this issue will be fixed eventually". Well, 13.0 is now out, and
+ * it was not.
+ */
+#define CLANG_EXCEPTION_SPEC_HACK (RT_CLANG_PREREQ(11, 0) /* && !RT_CLANG_PREREQ(13, 0) */)
+
+#if CLANG_EXCEPTION_SPEC_HACK
+static int loadVRDELibrary(const char *pszLibraryName, RTLDRMOD *phmod, void *ppfn)
+#else
+static int loadVRDELibrary(const char *pszLibraryName, RTLDRMOD *phmod, PFNVRDESUPPORTEDPROPERTIES *ppfn)
+#endif
+{
+ int rc = VINF_SUCCESS;
+
+ RTLDRMOD hmod = NIL_RTLDRMOD;
+
+ RTERRINFOSTATIC ErrInfo;
+ RTErrInfoInitStatic(&ErrInfo);
+ if (RTPathHavePath(pszLibraryName))
+ rc = SUPR3HardenedLdrLoadPlugIn(pszLibraryName, &hmod, &ErrInfo.Core);
+ else
+ rc = SUPR3HardenedLdrLoadAppPriv(pszLibraryName, &hmod, RTLDRLOAD_FLAGS_LOCAL, &ErrInfo.Core);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hmod, "VRDESupportedProperties", (void **)ppfn);
+
+ if (RT_FAILURE(rc) && rc != VERR_SYMBOL_NOT_FOUND)
+ LogRel(("VRDE: Error resolving symbol '%s', rc %Rrc.\n", "VRDESupportedProperties", rc));
+ }
+ else
+ {
+ if (RTErrInfoIsSet(&ErrInfo.Core))
+ LogRel(("VRDE: Error loading the library '%s': %s (%Rrc)\n", pszLibraryName, ErrInfo.Core.pszMsg, rc));
+ else
+ LogRel(("VRDE: Error loading the library '%s' rc = %Rrc.\n", pszLibraryName, rc));
+
+ hmod = NIL_RTLDRMOD;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ *phmod = hmod;
+ }
+ else
+ {
+ if (hmod != NIL_RTLDRMOD)
+ {
+ RTLdrClose(hmod);
+ hmod = NIL_RTLDRMOD;
+ }
+ }
+
+ return rc;
+}
+
+HRESULT VRDEServer::getVRDEProperties(std::vector<com::Utf8Str> &aProperties)
+{
+ size_t cProperties = 0;
+ aProperties.resize(0);
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if (!mData->fEnabled)
+ {
+ return S_OK;
+ }
+ alock.release();
+
+ /*
+ * Check that a VRDE extension pack name is set and resolve it into a
+ * library path.
+ */
+ Bstr bstrExtPack;
+ HRESULT hrc = COMGETTER(VRDEExtPack)(bstrExtPack.asOutParam());
+ Log(("VRDEPROP: get extpack hrc 0x%08X, isEmpty %d\n", hrc, bstrExtPack.isEmpty()));
+ if (FAILED(hrc))
+ return hrc;
+ if (bstrExtPack.isEmpty())
+ return E_FAIL;
+
+ Utf8Str strExtPack(bstrExtPack);
+ Utf8Str strVrdeLibrary;
+ int vrc = VINF_SUCCESS;
+ if (strExtPack.equals(VBOXVRDP_KLUDGE_EXTPACK_NAME))
+ strVrdeLibrary = "VBoxVRDP";
+ else
+ {
+#ifdef VBOX_WITH_EXTPACK
+ VirtualBox *pVirtualBox = mParent->i_getVirtualBox();
+ ExtPackManager *pExtPackMgr = pVirtualBox->i_getExtPackManager();
+ vrc = pExtPackMgr->i_getVrdeLibraryPathForExtPack(&strExtPack, &strVrdeLibrary);
+#else
+ vrc = VERR_FILE_NOT_FOUND;
+#endif
+ }
+ Log(("VRDEPROP: library get rc %Rrc\n", vrc));
+
+ if (RT_SUCCESS(vrc))
+ {
+ /*
+ * Load the VRDE library and start the server, if it is enabled.
+ */
+ PFNVRDESUPPORTEDPROPERTIES pfn = NULL;
+ RTLDRMOD hmod = NIL_RTLDRMOD;
+#if CLANG_EXCEPTION_SPEC_HACK
+ vrc = loadVRDELibrary(strVrdeLibrary.c_str(), &hmod, (void **)&pfn);
+#else
+ vrc = loadVRDELibrary(strVrdeLibrary.c_str(), &hmod, &pfn);
+#endif
+ Log(("VRDEPROP: load library [%s] rc %Rrc\n", strVrdeLibrary.c_str(), vrc));
+ if (RT_SUCCESS(vrc))
+ {
+ const char * const *papszNames = pfn();
+
+ if (papszNames)
+ {
+ size_t i;
+ for (i = 0; papszNames[i] != NULL; ++i)
+ {
+ cProperties++;
+ }
+ }
+ Log(("VRDEPROP: %d properties\n", cProperties));
+
+ if (cProperties > 0)
+ {
+ aProperties.resize(cProperties);
+ for (size_t i = 0; i < cProperties && papszNames[i] != NULL; ++i)
+ {
+ aProperties[i] = papszNames[i];
+ }
+ }
+
+ /* Do not forget to unload the library. */
+ RTLdrClose(hmod);
+ hmod = NIL_RTLDRMOD;
+ }
+ }
+
+ if (RT_FAILURE(vrc))
+ {
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+
+HRESULT VRDEServer::getAuthType(AuthType_T *aType)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aType = mData->authType;
+
+ return S_OK;
+}
+
+HRESULT VRDEServer::setAuthType(AuthType_T aType)
+{
+ /* the machine can also be in saved state for this property to change */
+ AutoMutableOrSavedOrRunningStateDependency adep(mParent);
+ if (FAILED(adep.rc())) return adep.rc();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (mData->authType != aType)
+ {
+ mData.backup();
+ mData->authType = aType;
+
+ /* leave the lock before informing callbacks */
+ alock.release();
+
+ AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
+ mParent->i_setModified(Machine::IsModified_VRDEServer);
+ mlock.release();
+
+ mParent->i_onVRDEServerChange(/* aRestart */ TRUE);
+ }
+
+ return S_OK;
+}
+
+HRESULT VRDEServer::getAuthTimeout(ULONG *aTimeout)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aTimeout = mData->ulAuthTimeout;
+
+ return S_OK;
+}
+
+
+HRESULT VRDEServer::setAuthTimeout(ULONG aTimeout)
+{
+ /* the machine can also be in saved state for this property to change */
+ AutoMutableOrSavedOrRunningStateDependency adep(mParent);
+ if (FAILED(adep.rc())) return adep.rc();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (aTimeout != mData->ulAuthTimeout)
+ {
+ mData.backup();
+ mData->ulAuthTimeout = aTimeout;
+
+ /* leave the lock before informing callbacks */
+ alock.release();
+
+ AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
+ mParent->i_setModified(Machine::IsModified_VRDEServer);
+ mlock.release();
+
+ /* sunlover 20060131: This setter does not require the notification
+ * really */
+#if 0
+ mParent->onVRDEServerChange();
+#endif
+ }
+
+ return S_OK;
+}
+
+HRESULT VRDEServer::getAuthLibrary(com::Utf8Str &aLibrary)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ aLibrary = mData->strAuthLibrary;
+ alock.release();
+
+ if (aLibrary.isEmpty())
+ {
+ /* Get the global setting. */
+ ComPtr<ISystemProperties> systemProperties;
+ HRESULT hrc = mParent->i_getVirtualBox()->COMGETTER(SystemProperties)(systemProperties.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ Bstr strlib;
+ hrc = systemProperties->COMGETTER(VRDEAuthLibrary)(strlib.asOutParam());
+ if (SUCCEEDED(hrc))
+ aLibrary = Utf8Str(strlib).c_str();
+ }
+
+ if (FAILED(hrc))
+ return setError(hrc, tr("failed to query the library setting\n"));
+ }
+
+ return S_OK;
+}
+
+
+HRESULT VRDEServer::setAuthLibrary(const com::Utf8Str &aLibrary)
+{
+ /* the machine can also be in saved state for this property to change */
+ AutoMutableOrSavedOrRunningStateDependency adep(mParent);
+ if (FAILED(adep.rc())) return adep.rc();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (mData->strAuthLibrary != aLibrary)
+ {
+ mData.backup();
+ mData->strAuthLibrary = aLibrary;
+
+ /* leave the lock before informing callbacks */
+ alock.release();
+
+ AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS);
+ mParent->i_setModified(Machine::IsModified_VRDEServer);
+ mlock.release();
+
+ mParent->i_onVRDEServerChange(/* aRestart */ TRUE);
+ }
+
+ return S_OK;
+}
+
+
+HRESULT VRDEServer::getAllowMultiConnection(BOOL *aAllowMultiConnection)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aAllowMultiConnection = mData->fAllowMultiConnection;
+
+ return S_OK;
+}
+
+
+HRESULT VRDEServer::setAllowMultiConnection(BOOL aAllowMultiConnection)
+{
+ /* the machine can also be in saved state for this property to change */
+ AutoMutableOrSavedOrRunningStateDependency adep(mParent);
+ if (FAILED(adep.rc())) return adep.rc();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (mData->fAllowMultiConnection != RT_BOOL(aAllowMultiConnection))
+ {
+ mData.backup();
+ mData->fAllowMultiConnection = RT_BOOL(aAllowMultiConnection);
+
+ /* leave the lock before informing callbacks */
+ alock.release();
+
+ AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
+ mParent->i_setModified(Machine::IsModified_VRDEServer);
+ mlock.release();
+
+ mParent->i_onVRDEServerChange(/* aRestart */ TRUE); /// @todo does it need a restart?
+ }
+
+ return S_OK;
+}
+
+HRESULT VRDEServer::getReuseSingleConnection(BOOL *aReuseSingleConnection)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ *aReuseSingleConnection = mData->fReuseSingleConnection;
+
+ return S_OK;
+}
+
+
+HRESULT VRDEServer::setReuseSingleConnection(BOOL aReuseSingleConnection)
+{
+ AutoMutableOrSavedOrRunningStateDependency adep(mParent);
+ if (FAILED(adep.rc())) return adep.rc();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ if (mData->fReuseSingleConnection != RT_BOOL(aReuseSingleConnection))
+ {
+ mData.backup();
+ mData->fReuseSingleConnection = RT_BOOL(aReuseSingleConnection);
+
+ /* leave the lock before informing callbacks */
+ alock.release();
+
+ AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS); // mParent is const, needs no locking
+ mParent->i_setModified(Machine::IsModified_VRDEServer);
+ mlock.release();
+
+ mParent->i_onVRDEServerChange(/* aRestart */ TRUE); /// @todo needs a restart?
+ }
+
+ return S_OK;
+}
+
+HRESULT VRDEServer::getVRDEExtPack(com::Utf8Str &aExtPack)
+{
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ Utf8Str strExtPack = mData->strVrdeExtPack;
+ alock.release();
+ HRESULT hrc = S_OK;
+
+ if (strExtPack.isNotEmpty())
+ {
+ if (strExtPack.equals(VBOXVRDP_KLUDGE_EXTPACK_NAME))
+ hrc = S_OK;
+ else
+ {
+#ifdef VBOX_WITH_EXTPACK
+ ExtPackManager *pExtPackMgr = mParent->i_getVirtualBox()->i_getExtPackManager();
+ hrc = pExtPackMgr->i_checkVrdeExtPack(&strExtPack);
+#else
+ hrc = setError(E_FAIL, tr("Extension pack '%s' does not exist"), strExtPack.c_str());
+#endif
+ }
+ if (SUCCEEDED(hrc))
+ aExtPack = strExtPack;
+ }
+ else
+ {
+ /* Get the global setting. */
+ ComPtr<ISystemProperties> systemProperties;
+ hrc = mParent->i_getVirtualBox()->COMGETTER(SystemProperties)(systemProperties.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ Bstr bstr;
+ hrc = systemProperties->COMGETTER(DefaultVRDEExtPack)(bstr.asOutParam());
+ if (SUCCEEDED(hrc))
+ aExtPack = bstr;
+ }
+ }
+ return hrc;
+}
+
+// public methods only for internal purposes
+/////////////////////////////////////////////////////////////////////////////
+HRESULT VRDEServer::setVRDEExtPack(const com::Utf8Str &aExtPack)
+{
+ HRESULT hrc = S_OK;
+ /* the machine can also be in saved state for this property to change */
+ AutoMutableOrSavedOrRunningStateDependency adep(mParent);
+ hrc = adep.rc();
+ if (SUCCEEDED(hrc))
+ {
+ /*
+ * If not empty, check the specific extension pack.
+ */
+ if (!aExtPack.isEmpty())
+ {
+ if (aExtPack.equals(VBOXVRDP_KLUDGE_EXTPACK_NAME))
+ hrc = S_OK;
+ else
+ {
+#ifdef VBOX_WITH_EXTPACK
+ ExtPackManager *pExtPackMgr = mParent->i_getVirtualBox()->i_getExtPackManager();
+ hrc = pExtPackMgr->i_checkVrdeExtPack(&aExtPack);
+#else
+ hrc = setError(E_FAIL, tr("Extension pack '%s' does not exist"), aExtPack.c_str());
+#endif
+ }
+ }
+ if (SUCCEEDED(hrc))
+ {
+ /*
+ * Update the setting if there is an actual change, post an
+ * change event to trigger a VRDE server restart.
+ */
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ if (aExtPack != mData->strVrdeExtPack)
+ {
+ mData.backup();
+ mData->strVrdeExtPack = aExtPack;
+
+ /* leave the lock before informing callbacks */
+ alock.release();
+
+ AutoWriteLock mlock(mParent COMMA_LOCKVAL_SRC_POS);
+ mParent->i_setModified(Machine::IsModified_VRDEServer);
+ mlock.release();
+
+ mParent->i_onVRDEServerChange(/* aRestart */ TRUE);
+ }
+ }
+ }
+
+ return hrc;
+}
+
+// public methods only for internal purposes
+/////////////////////////////////////////////////////////////////////////////
+
+/**
+ * @note Locks this object for writing.
+ */
+void VRDEServer::i_rollback()
+{
+ /* sanity */
+ AutoCaller autoCaller(this);
+ AssertComRCReturnVoid(autoCaller.rc());
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ mData.rollback();
+}
+
+/**
+ * @note Locks this object for writing, together with the peer object (also
+ * for writing) if there is one.
+ */
+void VRDEServer::i_commit()
+{
+ /* sanity */
+ AutoCaller autoCaller(this);
+ AssertComRCReturnVoid(autoCaller.rc());
+
+ /* sanity too */
+ AutoCaller peerCaller(mPeer);
+ AssertComRCReturnVoid(peerCaller.rc());
+
+ /* lock both for writing since we modify both (mPeer is "master" so locked
+ * first) */
+ AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
+
+ if (mData.isBackedUp())
+ {
+ mData.commit();
+ if (mPeer)
+ {
+ /* attach new data to the peer and reshare it */
+ mPeer->mData.attach(mData);
+ }
+ }
+}
+
+/**
+ * @note Locks this object for writing, together with the peer object
+ * represented by @a aThat (locked for reading).
+ */
+void VRDEServer::i_copyFrom(VRDEServer *aThat)
+{
+ AssertReturnVoid(aThat != NULL);
+
+ /* sanity */
+ AutoCaller autoCaller(this);
+ AssertComRCReturnVoid(autoCaller.rc());
+
+ /* sanity too */
+ AutoCaller thatCaller(aThat);
+ AssertComRCReturnVoid(thatCaller.rc());
+
+ /* peer is not modified, lock it for reading (aThat is "master" so locked
+ * first) */
+ AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
+ AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
+
+ /* this will back up current data */
+ mData.assignCopy(aThat->mData);
+}
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */