summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/rest
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/common/rest')
-rw-r--r--src/VBox/Runtime/common/rest/Makefile.kup0
-rw-r--r--src/VBox/Runtime/common/rest/RTCRestAnyObject.cpp606
-rw-r--r--src/VBox/Runtime/common/rest/RTCRestArrayBase.cpp496
-rw-r--r--src/VBox/Runtime/common/rest/RTCRestClientApiBase.cpp326
-rw-r--r--src/VBox/Runtime/common/rest/RTCRestClientApiBaseOci.cpp185
-rw-r--r--src/VBox/Runtime/common/rest/RTCRestClientRequestBase.cpp280
-rw-r--r--src/VBox/Runtime/common/rest/RTCRestClientResponseBase.cpp413
-rw-r--r--src/VBox/Runtime/common/rest/RTCRestJsonPrimaryCursor.cpp120
-rw-r--r--src/VBox/Runtime/common/rest/RTCRestOutputBase.cpp129
-rw-r--r--src/VBox/Runtime/common/rest/RTCRestOutputPrettyBase.cpp134
-rw-r--r--src/VBox/Runtime/common/rest/RTCRestOutputPrettyToString.cpp130
-rw-r--r--src/VBox/Runtime/common/rest/RTCRestOutputToString.cpp130
-rw-r--r--src/VBox/Runtime/common/rest/RTCRestStringMapBase.cpp467
-rw-r--r--src/VBox/Runtime/common/rest/rest-binary.cpp708
-rw-r--r--src/VBox/Runtime/common/rest/rest-primary-object-types.cpp2403
15 files changed, 6527 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/rest/Makefile.kup b/src/VBox/Runtime/common/rest/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/common/rest/Makefile.kup
diff --git a/src/VBox/Runtime/common/rest/RTCRestAnyObject.cpp b/src/VBox/Runtime/common/rest/RTCRestAnyObject.cpp
new file mode 100644
index 00000000..cadfce5f
--- /dev/null
+++ b/src/VBox/Runtime/common/rest/RTCRestAnyObject.cpp
@@ -0,0 +1,606 @@
+/* $Id: RTCRestAnyObject.cpp $ */
+/** @file
+ * IPRT - C++ REST, RTCRestAnyObject implementation.
+ */
+
+/*
+ * Copyright (C) 2018-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_REST
+#include <iprt/cpp/restanyobject.h>
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/cpp/restoutput.h>
+
+
+
+/**
+ * Default constructor.
+ */
+RTCRestAnyObject::RTCRestAnyObject() RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , m_pData(NULL)
+{
+ m_fNullIndicator = true;
+}
+
+
+/**
+ * Destructor.
+ */
+RTCRestAnyObject::~RTCRestAnyObject()
+{
+ if (m_pData)
+ {
+ delete m_pData;
+ m_pData = NULL;
+ }
+}
+
+
+/**
+ * Copy constructor.
+ */
+RTCRestAnyObject::RTCRestAnyObject(RTCRestAnyObject const &a_rThat)
+ : RTCRestObjectBase()
+ , m_pData(NULL)
+{
+ int rc = assignCopy(a_rThat);
+ if (RT_FAILURE(rc))
+ throw std::bad_alloc();
+}
+
+
+/**
+ * Copy assignment operator.
+ */
+RTCRestAnyObject &RTCRestAnyObject::operator=(RTCRestAnyObject const &a_rThat)
+{
+ int rc = assignCopy(a_rThat);
+ if (RT_FAILURE(rc))
+ throw std::bad_alloc();
+ return *this;
+}
+
+
+/**
+ * Safe copy assignment method.
+ */
+int RTCRestAnyObject::assignCopy(RTCRestAnyObject const &a_rThat) RT_NOEXCEPT
+{
+ setNull();
+ if ( !a_rThat.m_fNullIndicator
+ && a_rThat.m_pData != NULL)
+ {
+ kTypeClass enmType = a_rThat.m_pData->typeClass();
+ switch (enmType)
+ {
+ case kTypeClass_Bool: return assignCopy(*(RTCRestBool const *)a_rThat.m_pData);
+ case kTypeClass_Int64: return assignCopy(*(RTCRestInt64 const *)a_rThat.m_pData);
+ case kTypeClass_Int32: return assignCopy(*(RTCRestInt32 const *)a_rThat.m_pData);
+ case kTypeClass_Int16: return assignCopy(*(RTCRestInt16 const *)a_rThat.m_pData);
+ case kTypeClass_Double: return assignCopy(*(RTCRestDouble const *)a_rThat.m_pData);
+ case kTypeClass_String: return assignCopy(*(RTCRestString const *)a_rThat.m_pData);
+ case kTypeClass_Array: return assignCopy(*(RTCRestArray<RTCRestAnyObject> const *)a_rThat.m_pData);
+ case kTypeClass_StringMap: return assignCopy(*(RTCRestStringMap<RTCRestAnyObject> const *)a_rThat.m_pData);
+
+ /* Currently unused of invalid: */
+ case kTypeClass_Date:
+ case kTypeClass_Uuid:
+ case kTypeClass_Binary:
+ case kTypeClass_StringEnum:
+ case kTypeClass_AnyObject:
+ case kTypeClass_DataObject:
+ case kTypeClass_Invalid:
+ AssertFailedReturn(VERR_REST_INTERNAL_ERROR_7);
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Safe copy assignment method, boolean variant.
+ */
+int RTCRestAnyObject::assignCopy(RTCRestBool const &a_rThat) RT_NOEXCEPT
+{
+ setNull();
+ RTCRestBool *pData = new (std::nothrow) RTCRestBool();
+ if (pData)
+ {
+ m_pData = pData;
+ m_fNullIndicator = false;
+ return pData->assignCopy(a_rThat);
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Safe copy assignment method, int64_t variant.
+ */
+int RTCRestAnyObject::assignCopy(RTCRestInt64 const &a_rThat) RT_NOEXCEPT
+{
+ setNull();
+ RTCRestInt64 *pData = new (std::nothrow) RTCRestInt64();
+ if (pData)
+ {
+ m_pData = pData;
+ m_fNullIndicator = false;
+ return pData->assignCopy(a_rThat);
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Safe copy assignment method, int32_t variant.
+ */
+int RTCRestAnyObject::assignCopy(RTCRestInt32 const &a_rThat) RT_NOEXCEPT
+{
+ setNull();
+ RTCRestInt32 *pData = new (std::nothrow) RTCRestInt32();
+ if (pData)
+ {
+ m_pData = pData;
+ m_fNullIndicator = false;
+ return pData->assignCopy(a_rThat);
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Safe copy assignment method, int16_t variant.
+ */
+int RTCRestAnyObject::assignCopy(RTCRestInt16 const &a_rThat) RT_NOEXCEPT
+{
+ setNull();
+ RTCRestInt16 *pData = new (std::nothrow) RTCRestInt16();
+ if (pData)
+ {
+ m_pData = pData;
+ m_fNullIndicator = false;
+ return pData->assignCopy(a_rThat);
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Safe copy assignment method, double variant.
+ */
+int RTCRestAnyObject::assignCopy(RTCRestDouble const &a_rThat) RT_NOEXCEPT
+{
+ setNull();
+ RTCRestDouble *pData = new (std::nothrow) RTCRestDouble();
+ if (pData)
+ {
+ m_pData = pData;
+ m_fNullIndicator = false;
+ return pData->assignCopy(a_rThat);
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Safe copy assignment method, string variant.
+ */
+int RTCRestAnyObject::assignCopy(RTCRestString const &a_rThat) RT_NOEXCEPT
+{
+ setNull();
+ RTCRestString *pData = new (std::nothrow) RTCRestString();
+ if (pData)
+ {
+ m_pData = pData;
+ m_fNullIndicator = false;
+ return pData->assignCopy(a_rThat);
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Safe copy assignment method, array variant.
+ */
+int RTCRestAnyObject::assignCopy(RTCRestArray<RTCRestAnyObject> const &a_rThat) RT_NOEXCEPT
+{
+ setNull();
+ RTCRestArray<RTCRestAnyObject> *pData = new (std::nothrow) RTCRestArray<RTCRestAnyObject>();
+ if (pData)
+ {
+ m_pData = pData;
+ m_fNullIndicator = false;
+ return pData->assignCopy(a_rThat);
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Safe copy assignment method, string map variant.
+ */
+int RTCRestAnyObject::assignCopy(RTCRestStringMap<RTCRestAnyObject> const &a_rThat) RT_NOEXCEPT
+{
+ setNull();
+ RTCRestStringMap<RTCRestAnyObject> *pData = new (std::nothrow) RTCRestStringMap<RTCRestAnyObject>();
+ if (pData)
+ {
+ m_pData = pData;
+ m_fNullIndicator = false;
+ return pData->assignCopy(a_rThat);
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Safe value assignment method, boolean variant.
+ */
+int RTCRestAnyObject::assignValue(bool a_fValue) RT_NOEXCEPT
+{
+ setNull();
+ RTCRestBool *pData = new (std::nothrow) RTCRestBool();
+ if (pData)
+ {
+ m_pData = pData;
+ pData->assignValue(a_fValue);
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Safe value assignment method, int64_t variant.
+ */
+int RTCRestAnyObject::assignValue(int64_t a_iValue) RT_NOEXCEPT
+{
+ setNull();
+ RTCRestInt64 *pData = new (std::nothrow) RTCRestInt64();
+ if (pData)
+ {
+ m_pData = pData;
+ pData->assignValue(a_iValue);
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Safe value assignment method, int32_t variant.
+ */
+int RTCRestAnyObject::assignValue(int32_t a_iValue) RT_NOEXCEPT
+{
+ setNull();
+ RTCRestInt32 *pData = new (std::nothrow) RTCRestInt32();
+ if (pData)
+ {
+ m_pData = pData;
+ pData->assignValue(a_iValue);
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Safe value assignment method, int16_t variant.
+ */
+int RTCRestAnyObject::assignValue(int16_t a_iValue) RT_NOEXCEPT
+{
+ setNull();
+ RTCRestInt16 *pData = new (std::nothrow) RTCRestInt16();
+ if (pData)
+ {
+ m_pData = pData;
+ pData->assignValue(a_iValue);
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Safe value assignment method, double variant.
+ */
+int RTCRestAnyObject::assignValue(double a_iValue) RT_NOEXCEPT
+{
+ setNull();
+ RTCRestDouble *pData = new (std::nothrow) RTCRestDouble();
+ if (pData)
+ {
+ m_pData = pData;
+ pData->assignValue(a_iValue);
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Safe value assignment method, string variant.
+ */
+int RTCRestAnyObject::assignValue(RTCString const &a_rValue) RT_NOEXCEPT
+{
+ setNull();
+ RTCRestString *pData = new (std::nothrow) RTCRestString();
+ if (pData)
+ {
+ m_pData = pData;
+ m_fNullIndicator = false;
+ return pData->assignNoThrow(a_rValue);
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+/**
+ * Safe value assignment method, C-string variant.
+ */
+int RTCRestAnyObject::assignValue(const char *a_pszValue) RT_NOEXCEPT
+{
+ setNull();
+ RTCRestString *pData = new (std::nothrow) RTCRestString();
+ if (pData)
+ {
+ m_pData = pData;
+ m_fNullIndicator = false;
+ return pData->assignNoThrow(a_pszValue);
+ }
+ return VERR_NO_MEMORY;
+}
+
+
+RTCRestObjectBase *RTCRestAnyObject::baseClone() const RT_NOEXCEPT
+{
+ RTCRestAnyObject *pClone = new (std::nothrow) RTCRestAnyObject();
+ if (pClone)
+ {
+ int rc = pClone->assignCopy(*this);
+ if (RT_SUCCESS(rc))
+ return pClone;
+ delete pClone;
+ }
+ return NULL;
+}
+
+
+int RTCRestAnyObject::setNull(void) RT_NOEXCEPT
+{
+ if (m_pData)
+ {
+ delete m_pData;
+ m_pData = NULL;
+ }
+ return RTCRestObjectBase::setNull();
+}
+
+
+int RTCRestAnyObject::resetToDefault() RT_NOEXCEPT
+{
+ if (m_pData)
+ return m_pData->resetToDefault();
+ return VINF_SUCCESS;
+}
+
+
+RTCRestOutputBase &RTCRestAnyObject::serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
+{
+ if (m_pData)
+ return m_pData->serializeAsJson(a_rDst);
+ a_rDst.nullValue();
+ return a_rDst;
+}
+
+
+int RTCRestAnyObject::deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ setNull();
+
+ RTJSONVALTYPE enmType = RTJsonValueGetType(a_rCursor.m_hValue);
+ switch (enmType)
+ {
+ case RTJSONVALTYPE_OBJECT:
+ {
+ RTCRestStringMap<RTCRestAnyObject> *pMap = new (std::nothrow) RTCRestStringMap<RTCRestAnyObject>();
+ if (pMap)
+ {
+ m_pData = pMap;
+ m_fNullIndicator = false;
+ return pMap->deserializeFromJson(a_rCursor);
+ }
+ break;
+ }
+
+ case RTJSONVALTYPE_ARRAY:
+ {
+ RTCRestArray<RTCRestAnyObject> *pArray = new (std::nothrow) RTCRestArray<RTCRestAnyObject>();
+ if (pArray)
+ {
+ m_pData = pArray;
+ m_fNullIndicator = false;
+ return pArray->deserializeFromJson(a_rCursor);
+ }
+ break;
+ }
+
+ case RTJSONVALTYPE_STRING:
+ {
+ RTCRestString *pString = new (std::nothrow) RTCRestString();
+ if (pString)
+ {
+ m_pData = pString;
+ m_fNullIndicator = false;
+ return pString->deserializeFromJson(a_rCursor);
+ }
+ break;
+ }
+
+ case RTJSONVALTYPE_INTEGER:
+ {
+ RTCRestInt64 *pInt64 = new (std::nothrow) RTCRestInt64();
+ if (pInt64)
+ {
+ m_pData = pInt64;
+ m_fNullIndicator = false;
+ return pInt64->deserializeFromJson(a_rCursor);
+ }
+ break;
+ }
+
+ case RTJSONVALTYPE_NUMBER:
+ {
+ RTCRestDouble *pDouble = new (std::nothrow) RTCRestDouble();
+ if (pDouble)
+ {
+ m_pData = pDouble;
+ m_fNullIndicator = false;
+ return pDouble->deserializeFromJson(a_rCursor);
+ }
+ break;
+ }
+
+ case RTJSONVALTYPE_NULL:
+ return VINF_SUCCESS;
+
+ case RTJSONVALTYPE_TRUE:
+ case RTJSONVALTYPE_FALSE:
+ {
+ RTCRestBool *pBool = new (std::nothrow) RTCRestBool();
+ if (pBool)
+ {
+ m_pData = pBool;
+ m_fNullIndicator = false;
+ pBool->assignValue(enmType == RTJSONVALTYPE_TRUE);
+ return VINF_SUCCESS;
+ }
+ break;
+ }
+
+ /* no break. */
+ case RTJSONVALTYPE_INVALID:
+ case RTJSONVALTYPE_32BIT_HACK:
+ break;
+ }
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_WRONG_TYPE, "RTCRestAnyObject found %d (%s)",
+ enmType, RTJsonValueTypeName(enmType));
+}
+
+
+int RTCRestAnyObject::toString(RTCString *a_pDst, uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) const RT_NOEXCEPT
+{
+ if (m_pData)
+ return m_pData->toString(a_pDst, a_fFlags);
+ if (a_fFlags & kToString_Append)
+ return a_pDst->appendNoThrow(RT_STR_TUPLE("null"));
+ return a_pDst->assignNoThrow(RT_STR_TUPLE("null"));
+}
+
+
+int RTCRestAnyObject::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo /*= NULL*/,
+ uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) RT_NOEXCEPT
+{
+ return RTCRestObjectBase::fromString(a_rValue, a_pszName, a_pErrInfo, a_fFlags);
+}
+
+
+RTCRestObjectBase::kTypeClass RTCRestAnyObject::typeClass(void) const RT_NOEXCEPT
+{
+ return kTypeClass_AnyObject;
+}
+
+
+const char *RTCRestAnyObject::typeName(void) const RT_NOEXCEPT
+{
+ if (m_pData)
+ {
+ kTypeClass enmType = m_pData->typeClass();
+ switch (enmType)
+ {
+ case kTypeClass_Bool: return "RTCRestAnyObject[Bool]";
+ case kTypeClass_Int64: return "RTCRestAnyObject[Int64]";
+ case kTypeClass_Int32: return "RTCRestAnyObject[Int32]";
+ case kTypeClass_Int16: return "RTCRestAnyObject[Int16]";
+ case kTypeClass_Double: return "RTCRestAnyObject[Double]";
+ case kTypeClass_String: return "RTCRestAnyObject[String]";
+ case kTypeClass_Array: return "RTCRestAnyObject[Array]";
+ case kTypeClass_StringMap: return "RTCRestAnyObject[StringMap]";
+
+ /* Currently unused of invalid: */
+ case kTypeClass_Date:
+ case kTypeClass_Uuid:
+ case kTypeClass_Binary:
+ case kTypeClass_StringEnum:
+ case kTypeClass_DataObject:
+ case kTypeClass_AnyObject:
+ case kTypeClass_Invalid:
+ AssertFailed();
+ }
+ }
+ return "RTCRestAnyObject";
+}
+
+
+/**
+ * Factory method.
+ */
+/*static*/ DECLCALLBACK(RTCRestObjectBase *) RTCRestAnyObject::createInstance(void) RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestAnyObject();
+}
+
+
+/**
+ * @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON
+ */
+/*static*/ DECLCALLBACK(int)
+RTCRestAnyObject::deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance)
+{
+ RTCRestObjectBase *pObj = createInstance();
+ *a_ppInstance = pObj;
+ if (pObj)
+ return pObj->deserializeFromJson(a_rCursor);
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_NO_MEMORY, "Out of memory");
+}
+
diff --git a/src/VBox/Runtime/common/rest/RTCRestArrayBase.cpp b/src/VBox/Runtime/common/rest/RTCRestArrayBase.cpp
new file mode 100644
index 00000000..630166bb
--- /dev/null
+++ b/src/VBox/Runtime/common/rest/RTCRestArrayBase.cpp
@@ -0,0 +1,496 @@
+/* $Id: RTCRestArrayBase.cpp $ */
+/** @file
+ * IPRT - C++ REST, RTCRestArrayBase implementation.
+ */
+
+/*
+ * Copyright (C) 2018-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_REST
+#include <iprt/cpp/restarray.h>
+
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/cpp/restoutput.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Separator characters. */
+static char const g_szSep[RTCRestObjectBase::kCollectionFormat_Mask + 1] = ",, \t|,,";
+
+
+/**
+ * Default destructor.
+ */
+RTCRestArrayBase::RTCRestArrayBase() RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , m_papElements(NULL)
+ , m_cElements(0)
+ , m_cCapacity(0)
+{
+}
+
+
+#if 0 /* should not be used */
+/**
+ * Copy constructor.
+ */
+RTCRestArrayBase::RTCRestArrayBase(RTCRestArrayBase const &a_rThat);
+#endif
+
+/**
+ * Destructor.
+ */
+RTCRestArrayBase::~RTCRestArrayBase()
+{
+ clear();
+
+ if (m_papElements)
+ {
+ RTMemFree(m_papElements);
+ m_papElements = NULL;
+ m_cCapacity = 0;
+ }
+}
+
+
+#if 0 /* should not be used */
+/**
+ * Copy assignment operator.
+ */
+RTCRestArrayBase &RTCRestArrayBase::operator=(RTCRestArrayBase const &a_rThat);
+#endif
+
+
+/*********************************************************************************************************************************
+* Overridden methods *
+*********************************************************************************************************************************/
+
+RTCRestObjectBase *RTCRestArrayBase::baseClone() const RT_NOEXCEPT
+{
+ RTCRestArrayBase *pClone = createClone();
+ if (pClone)
+ {
+ int rc = pClone->copyArrayWorkerNoThrow(*this);
+ if (RT_SUCCESS(rc))
+ return pClone;
+ delete pClone;
+ }
+ return NULL;
+}
+
+
+int RTCRestArrayBase::resetToDefault() RT_NOEXCEPT
+{
+ /* The default state of an array is empty. At least for now. */
+ clear();
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+}
+
+
+RTCRestOutputBase &RTCRestArrayBase::serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
+{
+ if (!m_fNullIndicator)
+ {
+ uint32_t const uOldState = a_rDst.beginArray();
+ for (size_t i = 0; i < m_cElements; i++)
+ {
+ a_rDst.valueSeparator();
+ m_papElements[i]->serializeAsJson(a_rDst);
+ }
+ a_rDst.endArray(uOldState);
+ }
+ else
+ a_rDst.nullValue();
+ return a_rDst;
+}
+
+
+int RTCRestArrayBase::deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ /*
+ * Make sure the object starts out with an empty map.
+ */
+ if (m_cElements > 0)
+ clear();
+ m_fNullIndicator = false;
+
+ /*
+ * Iterate the array values.
+ */
+ RTJSONIT hIterator;
+ int rcRet = RTJsonIteratorBeginArray(a_rCursor.m_hValue, &hIterator);
+ if (RT_SUCCESS(rcRet))
+ {
+ for (size_t idxName = 0;; idxName++)
+ {
+ /* Setup sub-cursor. */
+ RTCRestJsonCursor SubCursor(a_rCursor);
+ int rc = RTJsonIteratorQueryValue(hIterator, &SubCursor.m_hValue, &SubCursor.m_pszName);
+ if (RT_SUCCESS(rc))
+ {
+ char szName[32];
+ RTStrPrintf(szName, sizeof(szName), "[%u]", idxName);
+ SubCursor.m_pszName = szName;
+
+ /* Call the static deserializeInstanceFromJson method of the value class. */
+ RTCRestObjectBase *pObj = NULL;
+ rc = deserializeValueInstanceFromJson(SubCursor, &pObj);
+ if (RT_SUCCESS(rc))
+ Assert(pObj);
+ else if (RT_SUCCESS(rcRet))
+ rcRet = rc;
+ if (pObj)
+ {
+ rc = insertWorker(~(size_t)0, pObj, false /*a_fReplace*/);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ {
+ rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "Array insert failed (index %zu): %Rrc",
+ idxName, rc);
+ delete pObj;
+ }
+ }
+ }
+ else
+ rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonIteratorQueryValue failed: %Rrc", rc);
+
+ /*
+ * Advance.
+ */
+ rc = RTJsonIteratorNext(hIterator);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else if (rc == VERR_JSON_ITERATOR_END)
+ break;
+ else
+ {
+ rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonIteratorNext failed: %Rrc", rc);
+ break;
+ }
+ }
+
+ RTJsonIteratorFree(hIterator);
+ }
+ else if (rcRet == VERR_JSON_IS_EMPTY)
+ rcRet = VINF_SUCCESS;
+ else if ( rcRet == VERR_JSON_VALUE_INVALID_TYPE
+ && RTJsonValueGetType(a_rCursor.m_hValue) == RTJSONVALTYPE_NULL)
+ {
+ m_fNullIndicator = true;
+ rcRet = VINF_SUCCESS;
+ }
+ else
+ rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rcRet,
+ "RTJsonIteratorBeginrray failed: %Rrc (type %s)",
+ rcRet, RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
+ return rcRet;
+
+}
+
+
+int RTCRestArrayBase::toString(RTCString *a_pDst, uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) const RT_NOEXCEPT
+{
+ int rc;
+ if (!m_fNullIndicator)
+ {
+ if (m_cElements)
+ {
+ char const chSep = g_szSep[a_fFlags & kCollectionFormat_Mask];
+
+ rc = m_papElements[0]->toString(a_pDst, a_fFlags);
+ for (size_t i = 1; RT_SUCCESS(rc) && i < m_cElements; i++)
+ {
+ rc = a_pDst->appendNoThrow(chSep);
+ if (RT_SUCCESS(rc))
+ rc = m_papElements[i]->toString(a_pDst, a_fFlags | kToString_Append);
+ }
+ }
+ else
+ {
+ if (!(a_fFlags & kToString_Append))
+ a_pDst->setNull();
+ rc = VINF_SUCCESS;
+ }
+ }
+ else if (a_fFlags & kToString_Append)
+ rc = a_pDst->appendNoThrow(RT_STR_TUPLE("null"));
+ else
+ rc = a_pDst->appendNoThrow(RT_STR_TUPLE("null"));
+
+ return rc;
+}
+
+
+int RTCRestArrayBase::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo /*= NULL*/,
+ uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) RT_NOEXCEPT
+{
+ /*
+ * Clear the array. If the string is empty, we have an empty array and is done.
+ */
+ if (!(a_fFlags & kToString_Append))
+ clear();
+ if (a_rValue.isEmpty())
+ return VINF_SUCCESS;
+
+ /*
+ * Look for a separator so we don't mistake a initial null element for a null array.
+ */
+ char const chSep = g_szSep[a_fFlags & kCollectionFormat_Mask];
+ size_t offSep = a_rValue.find(chSep);
+ if ( offSep != RTCString::npos
+ || !a_rValue.startsWithWord("null", RTCString::CaseInsensitive))
+ {
+ RTCString strTmp;
+ size_t offStart = 0;
+ int rcRet = VINF_SUCCESS;
+ for (;;)
+ {
+ /* Copy the element value into its own string buffer. */
+ int rc = strTmp.assignNoThrow(a_rValue, offStart, (offSep == RTCString::npos ? a_rValue.length() : offSep) - offStart);
+ AssertRCReturn(rc, rc);
+
+ /* Create a new element, insert it and pass it the value string. */
+ RTCRestObjectBase *pObj = createValue();
+ AssertPtrReturn(pObj, VERR_NO_MEMORY);
+
+ rc = insertWorker(~(size_t)0, pObj, false);
+ AssertRCReturnStmt(rc, delete pObj, rc);
+
+ char szName[128];
+ RTStrPrintf(szName, sizeof(szName), "%.*s[%zu]", 116, a_pszName ? a_pszName : "", size());
+ rc = pObj->fromString(strTmp, a_pszName, a_pErrInfo, 0);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else if (RT_SUCCESS(rcRet))
+ rcRet = rc;
+
+ /*
+ * Done? Otherwise advance.
+ */
+ if (offSep == RTCString::npos)
+ break;
+ offStart = offSep + 1;
+ offSep = a_rValue.find(chSep, offStart);
+ }
+ return rcRet;
+ }
+
+ /*
+ * Consider this a null array even if it could also be an array with a single
+ * null element. This is just an artifact of an imperfect serialization format.
+ */
+ setNull();
+ return VINF_SUCCESS;
+}
+
+
+RTCRestObjectBase::kTypeClass RTCRestArrayBase::typeClass(void) const RT_NOEXCEPT
+{
+ return kTypeClass_Array;
+}
+
+
+const char *RTCRestArrayBase::typeName(void) const RT_NOEXCEPT
+{
+ return "RTCRestArray<ElementType>";
+}
+
+
+
+/*********************************************************************************************************************************
+* Array methods *
+*********************************************************************************************************************************/
+
+void RTCRestArrayBase::clear() RT_NOEXCEPT
+{
+ size_t i = m_cElements;
+ while (i-- > 0)
+ {
+ delete m_papElements[i];
+ m_papElements[i] = NULL;
+ }
+ m_cElements = 0;
+ m_fNullIndicator = false;
+}
+
+
+bool RTCRestArrayBase::removeAt(size_t a_idx) RT_NOEXCEPT
+{
+ if (a_idx == ~(size_t)0)
+ a_idx = m_cElements - 1;
+ if (a_idx < m_cElements)
+ {
+ delete m_papElements[a_idx];
+ m_papElements[a_idx] = NULL;
+
+ m_cElements--;
+ if (a_idx < m_cElements)
+ memmove(&m_papElements[a_idx], &m_papElements[a_idx + 1], (m_cElements - a_idx) * sizeof(m_papElements[0]));
+ }
+ return false;
+}
+
+
+int RTCRestArrayBase::ensureCapacity(size_t a_cEnsureCapacity) RT_NOEXCEPT
+{
+ if (m_cCapacity < a_cEnsureCapacity)
+ {
+ if (a_cEnsureCapacity < 512)
+ a_cEnsureCapacity = RT_ALIGN_Z(a_cEnsureCapacity, 16);
+ else if (a_cEnsureCapacity < 16384)
+ a_cEnsureCapacity = RT_ALIGN_Z(a_cEnsureCapacity, 128);
+ else
+ a_cEnsureCapacity = RT_ALIGN_Z(a_cEnsureCapacity, 512);
+
+ void *pvNew = RTMemRealloc(m_papElements, sizeof(m_papElements[0]) * a_cEnsureCapacity);
+ if (pvNew)
+ {
+ m_papElements = (RTCRestObjectBase **)pvNew;
+ memset(&m_papElements[m_cCapacity], 0, (a_cEnsureCapacity - m_cCapacity) * sizeof(sizeof(m_papElements[0])));
+ m_cCapacity = a_cEnsureCapacity;
+ }
+ else
+ return VERR_NO_MEMORY;
+ }
+ return VINF_SUCCESS;
+}
+
+
+int RTCRestArrayBase::copyArrayWorkerNoThrow(RTCRestArrayBase const &a_rThat) RT_NOEXCEPT
+{
+ int rc;
+ clear();
+ if (a_rThat.m_cElements == 0)
+ {
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ Assert(!a_rThat.m_fNullIndicator);
+ rc = ensureCapacity(a_rThat.m_cElements);
+ if (RT_SUCCESS(rc))
+ {
+ for (size_t i = 0; i < a_rThat.m_cElements; i++)
+ {
+ AssertPtr(a_rThat.m_papElements[i]);
+ rc = insertCopyWorker(i, *a_rThat.m_papElements[i], false);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+ }
+ }
+ }
+ return rc;
+}
+
+void RTCRestArrayBase::copyArrayWorkerMayThrow(RTCRestArrayBase const &a_rThat)
+{
+ int rc = copyArrayWorkerNoThrow(a_rThat);
+ if (RT_SUCCESS(rc))
+ return;
+ throw std::bad_alloc();
+}
+
+
+int RTCRestArrayBase::insertWorker(size_t a_idx, RTCRestObjectBase *a_pValue, bool a_fReplace) RT_NOEXCEPT
+{
+ AssertPtrReturn(a_pValue, VERR_INVALID_POINTER);
+
+ if (a_idx == ~(size_t)0)
+ a_idx = m_cElements;
+
+ if (a_idx <= m_cElements)
+ {
+ if (a_idx == m_cElements || !a_fReplace)
+ {
+ /* Make sure we've got array space. */
+ if (m_cElements + 1 < m_cCapacity)
+ { /* kind of likely */ }
+ else
+ {
+ int rc = ensureCapacity(m_cElements + 1);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+ }
+
+ /* Shift following elements before inserting. */
+ if (a_idx < m_cElements)
+ memmove(&m_papElements[a_idx + 1], &m_papElements[a_idx], (m_cElements - a_idx) * sizeof(m_papElements[0]));
+ m_papElements[a_idx] = a_pValue;
+ m_cElements++;
+#ifdef RT_STRICT
+ for (size_t i = 0; i < m_cElements; i++)
+ AssertPtr(m_papElements[i]);
+#endif
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+ }
+
+ /* Replace element. */
+ delete m_papElements[a_idx];
+ m_papElements[a_idx] = a_pValue;
+ m_fNullIndicator = false;
+ return VWRN_ALREADY_EXISTS;
+ }
+ return VERR_OUT_OF_RANGE;
+}
+
+
+int RTCRestArrayBase::insertCopyWorker(size_t a_idx, RTCRestObjectBase const &a_rValue, bool a_fReplace) RT_NOEXCEPT
+{
+ int rc;
+ RTCRestObjectBase *pValueCopy = a_rValue.baseClone();
+ if (pValueCopy)
+ {
+ rc = insertWorker(a_idx, pValueCopy, a_fReplace);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ delete pValueCopy;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/rest/RTCRestClientApiBase.cpp b/src/VBox/Runtime/common/rest/RTCRestClientApiBase.cpp
new file mode 100644
index 00000000..e402ed8d
--- /dev/null
+++ b/src/VBox/Runtime/common/rest/RTCRestClientApiBase.cpp
@@ -0,0 +1,326 @@
+/* $Id: RTCRestClientApiBase.cpp $ */
+/** @file
+ * IPRT - C++ REST, RTCRestClientApiBase implementation.
+ */
+
+/*
+ * Copyright (C) 2018-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_REST
+#include <iprt/cpp/restclient.h>
+
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/http.h>
+#include <iprt/log.h>
+#include <iprt/uri.h>
+
+
+/**
+ * Default constructor.
+ */
+RTCRestClientApiBase::RTCRestClientApiBase() RT_NOEXCEPT
+ : m_hHttp(NIL_RTHTTP)
+{
+}
+
+
+/**
+ * The destructor.
+ */
+RTCRestClientApiBase::~RTCRestClientApiBase()
+{
+ if (m_hHttp != NIL_RTHTTP)
+ {
+ int rc = RTHttpDestroy(m_hHttp);
+ AssertRC(rc);
+ m_hHttp = NIL_RTHTTP;
+ }
+}
+
+
+int RTCRestClientApiBase::setCAFile(const char *pcszCAFile) RT_NOEXCEPT
+{
+ return m_strCAFile.assignNoThrow(pcszCAFile);
+}
+
+
+int RTCRestClientApiBase::setCAFile(const RTCString &strCAFile) RT_NOEXCEPT
+{
+ return m_strCAFile.assignNoThrow(strCAFile);
+}
+
+
+const char *RTCRestClientApiBase::getServerUrl(void) const RT_NOEXCEPT
+{
+ if (m_strServerUrl.isEmpty())
+ return getDefaultServerUrl();
+ return m_strServerUrl.c_str();
+}
+
+
+int RTCRestClientApiBase::setServerUrl(const char *a_pszUrl) RT_NOEXCEPT
+{
+#ifdef RT_STRICT
+ if (a_pszUrl)
+ {
+ RTURIPARSED Parsed;
+ int rc = RTUriParse(a_pszUrl, &Parsed);
+ AssertRC(rc);
+ }
+#endif
+
+ return m_strServerUrl.assignNoThrow(a_pszUrl);
+}
+
+
+int RTCRestClientApiBase::setServerUrlPart(const char *a_pszServerUrl, size_t a_offDst, size_t a_cchDst,
+ const char *a_pszSrc, size_t a_cchSrc) RT_NOEXCEPT
+{
+ if ( a_cchDst == a_cchSrc
+ && memcmp(&a_pszServerUrl[0], a_pszSrc, a_cchSrc) == 0)
+ return VINF_SUCCESS;
+
+ if (m_strServerUrl.isEmpty())
+ {
+ int rc = m_strServerUrl.assignNoThrow(a_pszServerUrl);
+ AssertRCReturn(rc, rc);
+ }
+ return m_strServerUrl.replaceNoThrow(a_offDst, a_cchDst, a_pszSrc, a_cchSrc);
+}
+
+
+int RTCRestClientApiBase::setServerScheme(const char *a_pszScheme) RT_NOEXCEPT
+{
+ /*
+ * Validate.
+ */
+ AssertReturn(a_pszScheme, VERR_INVALID_POINTER);
+ size_t const cchScheme = strlen(a_pszScheme);
+ AssertReturn(cchScheme > 0, VERR_INVALID_PARAMETER);
+ Assert(cchScheme < 16);
+#ifdef RT_STRICT
+ for (size_t i = 0; i < cchScheme; i++)
+ Assert(RT_C_IS_ALNUM(a_pszScheme[i]));
+#endif
+
+ /*
+ * Parse, compare & replace.
+ */
+ RTURIPARSED Parsed;
+ const char *pszUrl = getServerUrl();
+ int rc = RTUriParse(pszUrl, &Parsed);
+ AssertRCReturn(rc, rc);
+ return setServerUrlPart(pszUrl, 0, Parsed.cchScheme, a_pszScheme, cchScheme);
+}
+
+
+int RTCRestClientApiBase::setServerAuthority(const char *a_pszAuthority) RT_NOEXCEPT
+{
+ /*
+ * Validate.
+ */
+ AssertReturn(a_pszAuthority, VERR_INVALID_POINTER);
+ size_t const cchAuthority = strlen(a_pszAuthority);
+ AssertReturn(cchAuthority > 0, VERR_INVALID_PARAMETER);
+ Assert(memchr(a_pszAuthority, '/', cchAuthority) == NULL);
+ Assert(memchr(a_pszAuthority, '\\', cchAuthority) == NULL);
+ Assert(memchr(a_pszAuthority, '#', cchAuthority) == NULL);
+ Assert(memchr(a_pszAuthority, '?', cchAuthority) == NULL);
+
+ /*
+ * Parse, compare & replace.
+ */
+ RTURIPARSED Parsed;
+ const char *pszUrl = getServerUrl();
+ int rc = RTUriParse(pszUrl, &Parsed);
+ AssertRCReturn(rc, rc);
+ return setServerUrlPart(pszUrl, Parsed.offAuthority, Parsed.cchAuthority, a_pszAuthority, cchAuthority);
+}
+
+
+int RTCRestClientApiBase::setServerBasePath(const char *a_pszBasePath) RT_NOEXCEPT
+{
+ /*
+ * Validate.
+ */
+ AssertReturn(a_pszBasePath, VERR_INVALID_POINTER);
+ size_t const cchBasePath = strlen(a_pszBasePath);
+ AssertReturn(cchBasePath > 0, VERR_INVALID_PARAMETER);
+ Assert(memchr(a_pszBasePath, '?', cchBasePath) == NULL);
+ Assert(memchr(a_pszBasePath, '#', cchBasePath) == NULL);
+
+ /*
+ * Parse, compare & replace.
+ */
+ RTURIPARSED Parsed;
+ const char *pszUrl = getServerUrl();
+ int rc = RTUriParse(pszUrl, &Parsed);
+ AssertRCReturn(rc, rc);
+ return setServerUrlPart(pszUrl, Parsed.offPath, Parsed.cchPath, a_pszBasePath, cchBasePath);
+}
+
+
+int RTCRestClientApiBase::reinitHttpInstance() RT_NOEXCEPT
+{
+ if (m_hHttp != NIL_RTHTTP)
+ return RTHttpReset(m_hHttp, 0 /*fFlags*/);
+
+ int rc = RTHttpCreate(&m_hHttp);
+ if (RT_SUCCESS(rc) && m_strCAFile.isNotEmpty())
+ rc = RTHttpSetCAFile(m_hHttp, m_strCAFile.c_str());
+
+ if (RT_FAILURE(rc) && m_hHttp != NIL_RTHTTP)
+ {
+ RTHttpDestroy(m_hHttp);
+ m_hHttp = NIL_RTHTTP;
+ }
+ return rc;
+}
+
+
+int RTCRestClientApiBase::xmitReady(RTHTTP a_hHttp, RTCString const &a_rStrFullUrl, RTHTTPMETHOD a_enmHttpMethod,
+ RTCString const &a_rStrXmitBody, uint32_t a_fFlags) RT_NOEXCEPT
+{
+ RT_NOREF(a_hHttp, a_rStrFullUrl, a_enmHttpMethod, a_rStrXmitBody, a_fFlags);
+ return VINF_SUCCESS;
+}
+
+
+int RTCRestClientApiBase::doCall(RTCRestClientRequestBase const &a_rRequest, RTHTTPMETHOD a_enmHttpMethod,
+ RTCRestClientResponseBase *a_pResponse, const char *a_pszMethod, uint32_t a_fFlags) RT_NOEXCEPT
+{
+ LogFlow(("doCall: %s %s\n", a_pszMethod, RTHttpMethodToStr(a_enmHttpMethod)));
+
+
+ /*
+ * Reset the response object (allowing reuse of such) and check the request
+ * object for assignment errors.
+ */
+ int rc;
+ RTHTTP hHttp = NIL_RTHTTP;
+
+ a_pResponse->reset();
+ if (!a_rRequest.hasAssignmentErrors())
+ {
+ /*
+ * Initialize the HTTP instance.
+ */
+ rc = reinitHttpInstance();
+ if (RT_SUCCESS(rc))
+ {
+ hHttp = m_hHttp;
+ Assert(hHttp != NIL_RTHTTP);
+
+ /*
+ * Prepare the response side.
+ */
+ rc = a_pResponse->receivePrepare(hHttp);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Prepare the request for the transmission.
+ */
+ RTCString strExtraPath;
+ RTCString strQuery;
+ RTCString strXmitBody;
+ rc = a_rRequest.xmitPrepare(&strExtraPath, &strQuery, hHttp, &strXmitBody);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Construct the full URL.
+ */
+ RTCString strFullUrl;
+ rc = strFullUrl.assignNoThrow(getServerUrl());
+ if (strExtraPath.isNotEmpty())
+ {
+ if (!strExtraPath.startsWith("/") && !strFullUrl.endsWith("/") && RT_SUCCESS(rc))
+ rc = strFullUrl.appendNoThrow('/');
+ if (RT_SUCCESS(rc))
+ rc = strFullUrl.appendNoThrow(strExtraPath);
+ strExtraPath.setNull();
+ }
+ if (strQuery.isNotEmpty())
+ {
+ Assert(strQuery.startsWith("?"));
+ rc = strFullUrl.appendNoThrow(strQuery);
+ strQuery.setNull();
+ }
+ if (RT_SUCCESS(rc))
+ {
+ rc = xmitReady(hHttp, strFullUrl, a_enmHttpMethod, strXmitBody, a_fFlags);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Perform HTTP request.
+ */
+ uint32_t uHttpStatus = 0;
+ size_t cbBody = 0;
+ void *pvBody = NULL;
+ rc = RTHttpPerform(hHttp, strFullUrl.c_str(), a_enmHttpMethod,
+ strXmitBody.c_str(), strXmitBody.length(),
+ &uHttpStatus, NULL /*ppvHdrs*/, NULL /*pcbHdrs*/, &pvBody, &cbBody);
+ if (RT_SUCCESS(rc))
+ {
+ a_rRequest.xmitComplete(uHttpStatus, hHttp);
+
+ /*
+ * Do response processing.
+ */
+ a_pResponse->receiveComplete(uHttpStatus, hHttp);
+ a_pResponse->consumeBody((const char *)pvBody, cbBody);
+ if (pvBody)
+ RTHttpFreeResponse(pvBody);
+ a_pResponse->receiveFinal();
+
+ return a_pResponse->getStatus();
+ }
+ }
+ }
+ }
+ a_rRequest.xmitComplete(rc, hHttp);
+ }
+ }
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ a_pResponse->receiveComplete(rc, hHttp);
+ RT_NOREF_PV(a_pszMethod);
+
+ return a_pResponse->getStatus();
+}
+
diff --git a/src/VBox/Runtime/common/rest/RTCRestClientApiBaseOci.cpp b/src/VBox/Runtime/common/rest/RTCRestClientApiBaseOci.cpp
new file mode 100644
index 00000000..a14f9723
--- /dev/null
+++ b/src/VBox/Runtime/common/rest/RTCRestClientApiBaseOci.cpp
@@ -0,0 +1,185 @@
+/* $Id: RTCRestClientApiBaseOci.cpp $ */
+/** @file
+ * IPRT - C++ REST, RTCRestClientApiBase implementation, OCI specific bits.
+ */
+
+/*
+ * Copyright (C) 2018-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_REST
+#include <iprt/cpp/restclient.h>
+
+#include <iprt/assert.h>
+#include <iprt/base64.h>
+#include <iprt/errcore.h>
+#include <iprt/http.h>
+#include <iprt/log.h>
+#include <iprt/sha.h>
+#include <iprt/time.h>
+#include <iprt/uri.h>
+
+
+
+/**
+ * Ensures that we've got an 'X-Date' or 'Date' header.
+ *
+ * @returns IPRT status code.
+ * @param hHttp The HTTP client handle.
+ * @param pvContent
+ */
+static int ociSignRequestEnsureDateOrXDate(RTHTTP hHttp) RT_NOEXCEPT
+{
+ if (RTHttpGetHeader(hHttp, RT_STR_TUPLE("x-date")))
+ return VINF_SUCCESS;
+ if (RTHttpGetHeader(hHttp, RT_STR_TUPLE("date")))
+ return VINF_SUCCESS;
+
+ RTTIMESPEC NowSpec;
+ RTTIME Now;
+ char szDate[RTTIME_RFC2822_LEN];
+ ssize_t cch = RTTimeToRfc2822(RTTimeExplode(&Now, RTTimeNow(&NowSpec)), szDate, sizeof(szDate), RTTIME_RFC2822_F_GMT);
+ AssertRCReturn((int)cch, (int)cch);
+
+ return RTHttpAddHeader(hHttp, "x-date", szDate, cch, RTHTTPADDHDR_F_BACK);
+}
+
+
+/**
+ * Ensures that we've got a 'x-content-sha256' header.
+ *
+ * @returns IPRT status code.
+ * @param hHttp The HTTP client handle.
+ * @param pvContent
+ */
+static int ociSignRequestEnsureXContentSha256(RTHTTP hHttp, void const *pvContent, size_t cbContent) RT_NOEXCEPT
+{
+ if (RTHttpGetHeader(hHttp, RT_STR_TUPLE("x-content-sha256")))
+ return VINF_SUCCESS;
+
+#ifdef RT_STRICT
+ if (cbContent != 0)
+ {
+ const char *pszContentLength = RTHttpGetHeader(hHttp, RT_STR_TUPLE("Content-Length"));
+ Assert(pszContentLength);
+ AssertMsg(!pszContentLength || RTStrToUInt64(pszContentLength) == cbContent,
+ ("'%s' vs %RU64\n", pszContentLength, cbContent));
+ }
+#endif
+
+ uint8_t abHash[RTSHA256_HASH_SIZE];
+ RTSha256(pvContent, cbContent, abHash);
+
+ char szBase64[RTSHA256_DIGEST_LEN + 1]; /* (base64 should be shorter) */
+ int rc = RTBase64EncodeEx(abHash, sizeof(abHash), RTBASE64_FLAGS_NO_LINE_BREAKS, szBase64, sizeof(szBase64), NULL);
+ AssertRCReturn(rc, rc);
+
+ return RTHttpAddHeader(hHttp, "x-content-sha256", szBase64, RTSTR_MAX, RTHTTPADDHDR_F_BACK);
+}
+
+
+/**
+ * Ensures that we've got a 'Content-Length' header.
+ *
+ * @returns IPRT status code.
+ * @param hHttp The HTTP client handle.
+ * @param cbContent The content length.
+ */
+static int ociSignRequestEnsureContentLength(RTHTTP hHttp, uint64_t cbContent) RT_NOEXCEPT
+{
+ if (RTHttpGetHeader(hHttp, RT_STR_TUPLE("Content-Length")))
+ return VINF_SUCCESS;
+ char szValue[64];
+ ssize_t cchValue = RTStrFormatU64(szValue, sizeof(szValue), cbContent, 10, 0, 0, 0);
+ AssertRCReturn((int)cchValue, (int)cchValue);
+ return RTHttpAddHeader(hHttp, "Content-Length", szValue, cchValue, RTHTTPADDHDR_F_BACK);
+}
+
+
+/**
+ * Ensures that we've got a host header.
+ *
+ * @returns IPRT status code.
+ * @param hHttp The HTTP client handle.
+ * @param pszUrl The URL.
+ */
+static int ociSignRequestEnsureHost(RTHTTP hHttp, const char *pszUrl) RT_NOEXCEPT
+{
+ if (RTHttpGetHeader(hHttp, RT_STR_TUPLE("host")))
+ return VINF_SUCCESS;
+
+ RTURIPARSED ParsedUrl;
+ int rc = RTUriParse(pszUrl, &ParsedUrl);
+ AssertRCReturn(rc, rc);
+
+ return RTHttpAddHeader(hHttp, "host", &pszUrl[ParsedUrl.offAuthorityHost], ParsedUrl.cchAuthorityHost, RTHTTPADDHDR_F_BACK);
+}
+
+
+int RTCRestClientApiBase::ociSignRequest(RTHTTP a_hHttp, RTCString const &a_rStrFullUrl, RTHTTPMETHOD a_enmHttpMethod,
+ RTCString const &a_rStrXmitBody, uint32_t a_fFlags,
+ RTCRKEY a_hKey, RTCString const &a_rStrKeyId) RT_NOEXCEPT
+{
+ /*
+ * First make sure required headers are present, adding them as needed.
+ */
+ int rc = ociSignRequestEnsureHost(a_hHttp, a_rStrFullUrl.c_str());
+ if (RT_SUCCESS(rc))
+ {
+ bool fHasBody
+ = a_rStrXmitBody.isNotEmpty()
+ /* but sometimes we need an empty body signed too */
+ || (a_fFlags & kDoCall_RequireBody)
+ || a_enmHttpMethod == RTHTTPMETHOD_POST
+ || a_enmHttpMethod == RTHTTPMETHOD_PUT;
+
+ if (fHasBody)
+ {
+ rc = ociSignRequestEnsureContentLength(a_hHttp, a_rStrXmitBody.length());
+ if (RT_SUCCESS(rc))
+ rc = ociSignRequestEnsureXContentSha256(a_hHttp, a_rStrXmitBody.c_str(), a_rStrXmitBody.length());
+ }
+ if (RT_SUCCESS(rc))
+ rc = ociSignRequestEnsureDateOrXDate(a_hHttp);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Do the signing.
+ */
+ rc = RTHttpSignHeaders(a_hHttp, a_enmHttpMethod, a_rStrFullUrl.c_str(), a_hKey, a_rStrKeyId.c_str(), 0 /*fFlags*/);
+ }
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/common/rest/RTCRestClientRequestBase.cpp b/src/VBox/Runtime/common/rest/RTCRestClientRequestBase.cpp
new file mode 100644
index 00000000..11290219
--- /dev/null
+++ b/src/VBox/Runtime/common/rest/RTCRestClientRequestBase.cpp
@@ -0,0 +1,280 @@
+/* $Id: RTCRestClientRequestBase.cpp $ */
+/** @file
+ * IPRT - C++ REST, RTCRestClientRequestBase implementation.
+ */
+
+/*
+ * Copyright (C) 2018-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_REST
+#include <iprt/cpp/restclient.h>
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/cpp/restarray.h>
+#include <iprt/cpp/reststringmap.h>
+
+
+/**
+ * Default constructor.
+ */
+RTCRestClientRequestBase::RTCRestClientRequestBase() RT_NOEXCEPT
+ : m_fIsSet(0)
+ , m_fErrorSet(0)
+{
+}
+
+
+/**
+ * Copy constructor.
+ */
+RTCRestClientRequestBase::RTCRestClientRequestBase(RTCRestClientRequestBase const &a_rThat) RT_NOEXCEPT
+ : m_fIsSet(a_rThat.m_fIsSet)
+ , m_fErrorSet(a_rThat.m_fErrorSet)
+{
+}
+
+
+/**
+ * Destructor
+ */
+RTCRestClientRequestBase::~RTCRestClientRequestBase()
+{
+ /* nothing to do */
+}
+
+
+/**
+ * Copy assignment operator.
+ */
+RTCRestClientRequestBase &RTCRestClientRequestBase::operator=(RTCRestClientRequestBase const &a_rThat) RT_NOEXCEPT
+{
+ m_fIsSet = a_rThat.m_fIsSet;
+ m_fErrorSet = a_rThat.m_fErrorSet;
+ return *this;
+}
+
+
+int RTCRestClientRequestBase::doPathParameters(RTCString *a_pStrPath, const char *a_pszPathTemplate, size_t a_cchPathTemplate,
+ PATHPARAMDESC const *a_paPathParams, PATHPARAMSTATE *a_paPathParamStates,
+ size_t a_cPathParams) const RT_NOEXCEPT
+{
+ int rc = a_pStrPath->assignNoThrow(a_pszPathTemplate, a_cchPathTemplate);
+ AssertRCReturn(rc, rc);
+
+ /* Locate the sub-string to replace with values first: */
+ for (size_t i = 0; i < a_cPathParams; i++)
+ {
+ char const *psz = strstr(a_pszPathTemplate, a_paPathParams[i].pszName);
+ AssertReturn(psz, VERR_INTERNAL_ERROR_5);
+ a_paPathParamStates[i].offName = psz - a_pszPathTemplate;
+ }
+
+ /* Replace with actual values: */
+ for (size_t i = 0; i < a_cPathParams; i++)
+ {
+ AssertReturn( (a_paPathParams[i].fFlags & RTCRestObjectBase::kCollectionFormat_Mask)
+ != RTCRestObjectBase::kCollectionFormat_multi,
+ VERR_INTERNAL_ERROR_3);
+ AssertMsgReturn(a_paPathParamStates[i].pObj != NULL,
+ ("%s: Path parameter '%s' is not set!\n",
+ getOperationName(), a_paPathParams[i].pszName),
+ VERR_REST_PATH_PARAMETER_NOT_SET);
+ AssertMsgReturn(m_fIsSet & RT_BIT_64(a_paPathParams[i].iBitNo),
+ ("%s: Path parameter '%s' is not set!\n",
+ getOperationName(), a_paPathParams[i].pszName),
+ VERR_REST_PATH_PARAMETER_NOT_SET);
+
+ RTCString strPathParam;
+ rc = a_paPathParamStates[i].pObj->toString(&strPathParam, a_paPathParams[i].fFlags);
+ AssertRCReturn(rc, rc);
+
+ LogRel5(("> %s: /%s = %s\n",
+ getOperationName(), a_paPathParams[i].pszName, strPathParam.c_str()));
+
+ RTCString strTmpVal;
+ rc = strTmpVal.printfNoThrow("%RMpa", strPathParam.c_str()); /* urlencode */
+ AssertRCReturn(rc, rc);
+
+ /* Replace. */
+ ssize_t cchAdjust = strTmpVal.length() - a_paPathParams[i].cchName;
+ rc = a_pStrPath->replaceNoThrow(a_paPathParamStates[i].offName, a_paPathParams[i].cchName, strTmpVal);
+ AssertRCReturn(rc, rc);
+
+ /* Adjust subsequent fields. */
+ if (cchAdjust != 0)
+ for (size_t j = i + 1; j < a_cPathParams; j++)
+ if (a_paPathParamStates[j].offName > a_paPathParamStates[i].offName)
+ a_paPathParamStates[j].offName += cchAdjust;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int RTCRestClientRequestBase::doQueryParameters(RTCString *a_pStrQuery, QUERYPARAMDESC const *a_paQueryParams,
+ RTCRestObjectBase const **a_papQueryParamObjs, size_t a_cQueryParams) const RT_NOEXCEPT
+{
+ RTCString strTmpVal;
+ char chSep = a_pStrQuery->isEmpty() ? '?' : '&';
+ for (size_t i = 0; i < a_cQueryParams; i++)
+ {
+ if ( a_paQueryParams[i].fRequired
+ || (m_fIsSet & RT_BIT_64(a_paQueryParams[i].iBitNo)) )
+ {
+ AssertMsgReturn(a_papQueryParamObjs[i] != NULL,
+ ("%s: Required query parameter '%s' is not set!\n",
+ getOperationName(), a_paQueryParams[i].pszName),
+ VERR_REST_REQUIRED_QUERY_PARAMETER_NOT_SET);
+ AssertMsgReturn(m_fIsSet & RT_BIT_64(a_paQueryParams[i].iBitNo),
+ ("%s: Required query parameter '%s' is not set!\n",
+ getOperationName(), a_paQueryParams[i].pszName),
+ VERR_REST_REQUIRED_QUERY_PARAMETER_NOT_SET);
+
+ if ( (a_paQueryParams[i].fFlags & RTCRestObjectBase::kCollectionFormat_Mask)
+ != RTCRestObjectBase::kCollectionFormat_multi)
+ {
+ int rc = a_papQueryParamObjs[i]->toString(&strTmpVal, a_paQueryParams[i].fFlags);
+ AssertRCReturn(rc, rc);
+
+ rc = a_pStrQuery->appendPrintfNoThrow("%c%RMpa=%RMpa", chSep, a_paQueryParams[i].pszName, strTmpVal.c_str());
+ AssertRCReturn(rc, rc);
+
+ LogRel5(("> %s: ?%s = %s\n",
+ getOperationName(), a_paQueryParams[i].pszName, strTmpVal.c_str()));
+
+ chSep = '&';
+ }
+ else
+ {
+ /*
+ * Enumerate array and add 'name=element' for each element in it.
+ */
+ AssertReturn(a_papQueryParamObjs[i]->typeClass() == RTCRestObjectBase::kTypeClass_Array,
+ VERR_REST_INTERNAL_ERROR_2);
+ RTCRestArrayBase const *pArray = (RTCRestArrayBase const *)a_papQueryParamObjs[i];
+ for (size_t j = 0; j < pArray->size(); j++)
+ {
+ RTCRestObjectBase const *pObj = pArray->atBase(j);
+ int rc = pObj->toString(&strTmpVal, a_paQueryParams[i].fFlags & ~RTCRestObjectBase::kCollectionFormat_Mask);
+ AssertRCReturn(rc, rc);
+
+ rc = a_pStrQuery->appendPrintfNoThrow("%c%RMpa=%RMpa", chSep, a_paQueryParams[i].pszName, strTmpVal.c_str());
+ AssertRCReturn(rc, rc);
+
+ LogRel5(("> %s: ?%s[%d] = %s\n",
+ getOperationName(), a_paQueryParams[i].pszName, j, strTmpVal.c_str()));
+
+ chSep = '&';
+ }
+ }
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+int RTCRestClientRequestBase::doHeaderParameters(RTHTTP a_hHttp, HEADERPARAMDESC const *a_paHeaderParams,
+ RTCRestObjectBase const **a_papHeaderParamObjs, size_t a_cHeaderParams) const RT_NOEXCEPT
+{
+ RTCString strTmpVal;
+ for (size_t i = 0; i < a_cHeaderParams; i++)
+ {
+ AssertReturn( (a_paHeaderParams[i].fFlags & RTCRestObjectBase::kCollectionFormat_Mask)
+ != RTCRestObjectBase::kCollectionFormat_multi,
+ VERR_INTERNAL_ERROR_3);
+
+ if ( a_paHeaderParams[i].fRequired
+ || (m_fIsSet & RT_BIT_64(a_paHeaderParams[i].iBitNo)) )
+ {
+ AssertMsgReturn(m_fIsSet & RT_BIT_64(a_paHeaderParams[i].iBitNo),
+ ("%s: Required header parameter '%s' is not set!\n",
+ getOperationName(), a_paHeaderParams[i].pszName),
+ VERR_REST_REQUIRED_HEADER_PARAMETER_NOT_SET);
+ AssertMsgReturn(a_papHeaderParamObjs[i] != NULL,
+ ("%s: Required header parameter '%s' is not set!\n",
+ getOperationName(), a_paHeaderParams[i].pszName),
+ VERR_REST_REQUIRED_HEADER_PARAMETER_NOT_SET);
+
+ if (!a_paHeaderParams[i].fMapCollection)
+ {
+ int rc = a_papHeaderParamObjs[i]->toString(&strTmpVal, a_paHeaderParams[i].fFlags);
+ AssertRCReturn(rc, rc);
+
+ rc = RTHttpAddHeader(a_hHttp, a_paHeaderParams[i].pszName, strTmpVal.c_str(), strTmpVal.length(),
+ RTHTTPADDHDR_F_BACK);
+ AssertRCReturn(rc, rc);
+
+ LogRel5(("> %s: :%s = %s\n",
+ getOperationName(), a_paHeaderParams[i].pszName, strTmpVal.c_str()));
+ }
+ else if (!a_papHeaderParamObjs[i]->isNull())
+ {
+ /*
+ * Enumerate the map and produce a series of head fields on the form:
+ * (a_paHeaderParams[i].pszName + key): value.toString()
+ */
+ AssertReturn(a_papHeaderParamObjs[i]->typeClass() == RTCRestObjectBase::kTypeClass_StringMap,
+ VERR_REST_INTERNAL_ERROR_1);
+ RTCRestStringMapBase const *pMap = (RTCRestStringMapBase const *)a_papHeaderParamObjs[i];
+ const size_t cchName = strlen(a_paHeaderParams[i].pszName);
+ Assert(a_paHeaderParams[i].pszName[cchName - 1] != '*');
+ RTCString strTmpName;
+ for (RTCRestStringMapBase::ConstIterator it = pMap->begin(); it != pMap->end(); ++it)
+ {
+ int rc = strTmpName.assignNoThrow(a_paHeaderParams[i].pszName, cchName);
+ AssertRCReturn(rc, rc);
+ rc = strTmpName.appendNoThrow(it.getKey());
+ AssertRCReturn(rc, rc);
+
+ rc = it.getValue()->toString(&strTmpVal, a_paHeaderParams[i].fFlags);
+ AssertRCReturn(rc, rc);
+
+ rc = RTHttpAddHeader(a_hHttp, strTmpName.c_str(), strTmpVal.c_str(), strTmpVal.length(),
+ RTHTTPADDHDR_F_BACK);
+ AssertRCReturn(rc, rc);
+
+ LogRel5(("> %s: :%s = %s\n",
+ getOperationName(), strTmpName.c_str(), strTmpVal.c_str()));
+ }
+ }
+ else
+ Assert(!a_paHeaderParams[i].fRequired);
+ }
+ }
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/common/rest/RTCRestClientResponseBase.cpp b/src/VBox/Runtime/common/rest/RTCRestClientResponseBase.cpp
new file mode 100644
index 00000000..5c9bb39f
--- /dev/null
+++ b/src/VBox/Runtime/common/rest/RTCRestClientResponseBase.cpp
@@ -0,0 +1,413 @@
+/* $Id: RTCRestClientResponseBase.cpp $ */
+/** @file
+ * IPRT - C++ REST, RTCRestClientResponseBase implementation.
+ */
+
+/*
+ * Copyright (C) 2018-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_REST
+#include <iprt/cpp/restclient.h>
+
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/cpp/reststringmap.h>
+
+
+/**
+ * Default constructor.
+ */
+RTCRestClientResponseBase::RTCRestClientResponseBase() RT_NOEXCEPT
+ : m_rcStatus(VERR_WRONG_ORDER)
+ , m_rcHttp(VERR_NOT_AVAILABLE)
+ , m_pErrInfo(NULL)
+{
+}
+
+
+/**
+ * Destructor.
+ */
+RTCRestClientResponseBase::~RTCRestClientResponseBase()
+{
+ deleteErrInfo();
+}
+
+
+/**
+ * Copy constructor.
+ */
+RTCRestClientResponseBase::RTCRestClientResponseBase(RTCRestClientResponseBase const &a_rThat)
+ : m_rcStatus(a_rThat.m_rcStatus)
+ , m_rcHttp(a_rThat.m_rcHttp)
+ , m_pErrInfo(NULL)
+ , m_strContentType(a_rThat.m_strContentType)
+{
+ if (a_rThat.m_pErrInfo)
+ copyErrInfo(a_rThat.m_pErrInfo);
+}
+
+
+/**
+ * Copy assignment operator.
+ */
+RTCRestClientResponseBase &RTCRestClientResponseBase::operator=(RTCRestClientResponseBase const &a_rThat)
+{
+ m_rcStatus = a_rThat.m_rcStatus;
+ m_rcHttp = a_rThat.m_rcHttp;
+ m_strContentType = a_rThat.m_strContentType;
+ if (a_rThat.m_pErrInfo)
+ copyErrInfo(a_rThat.m_pErrInfo);
+ else if (m_pErrInfo)
+ deleteErrInfo();
+
+ return *this;
+}
+
+
+void RTCRestClientResponseBase::reset() RT_NOEXCEPT
+{
+ /* Return to default constructor state. */
+ m_rcStatus = VERR_WRONG_ORDER;
+ m_rcHttp = VERR_NOT_AVAILABLE;
+ if (m_pErrInfo)
+ deleteErrInfo();
+ m_strContentType.setNull();
+}
+
+
+int RTCRestClientResponseBase::receivePrepare(RTHTTP a_hHttp) RT_NOEXCEPT
+{
+ int rc = RTHttpSetHeaderCallback(a_hHttp, receiveHttpHeaderCallback, this);
+ AssertRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+}
+
+
+void RTCRestClientResponseBase::receiveComplete(int a_rcStatus, RTHTTP a_hHttp) RT_NOEXCEPT
+{
+ RT_NOREF_PV(a_hHttp);
+ m_rcStatus = a_rcStatus;
+ if (a_rcStatus >= 0)
+ m_rcHttp = a_rcStatus;
+
+ int rc = RTHttpSetHeaderCallback(a_hHttp, NULL, NULL);
+ AssertRC(rc);
+}
+
+
+int RTCRestClientResponseBase::consumeHeader(uint32_t a_uMatchWord, const char *a_pchField, size_t a_cchField,
+ const char *a_pchValue, size_t a_cchValue) RT_NOEXCEPT
+{
+ if ( a_uMatchWord == RTHTTP_MAKE_HDR_MATCH_WORD(sizeof("Content-Type") - 1, 'c', 'o', 'n')
+ && RTStrNICmpAscii(a_pchField, RT_STR_TUPLE("Content-Type")) == 0)
+ {
+ int rc = RTStrValidateEncodingEx(a_pchValue, a_cchValue, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ return m_strContentType.assignNoThrow(a_pchValue, a_cchValue);
+ }
+ RT_NOREF(a_cchField);
+ return VINF_SUCCESS;
+}
+
+
+/*static*/ DECLCALLBACK(int)
+RTCRestClientResponseBase::receiveHttpHeaderCallback(RTHTTP hHttp, uint32_t uMatchWord, const char *pchField, size_t cchField,
+ const char *pchValue, size_t cchValue, void *pvUser) RT_NOEXCEPT
+{
+ RTCRestClientResponseBase *pThis = (RTCRestClientResponseBase *)pvUser;
+ RT_NOREF(hHttp);
+ return pThis->consumeHeader(uMatchWord, pchField, cchField, pchValue, cchValue);
+}
+
+
+void RTCRestClientResponseBase::consumeBody(const char *a_pchData, size_t a_cbData) RT_NOEXCEPT
+{
+ RT_NOREF(a_pchData, a_cbData);
+}
+
+
+void RTCRestClientResponseBase::receiveFinal() RT_NOEXCEPT
+{
+}
+
+
+PRTERRINFO RTCRestClientResponseBase::getErrInfoInternal(void) RT_NOEXCEPT
+{
+ if (m_pErrInfo)
+ return m_pErrInfo;
+ size_t cbMsg = _4K;
+ m_pErrInfo = (PRTERRINFO)RTMemAllocZ(sizeof(*m_pErrInfo) + cbMsg);
+ if (m_pErrInfo)
+ return RTErrInfoInit(m_pErrInfo, (char *)(m_pErrInfo + 1), cbMsg);
+ return NULL;
+}
+
+
+void RTCRestClientResponseBase::deleteErrInfo(void) RT_NOEXCEPT
+{
+ if (m_pErrInfo)
+ {
+ RTMemFree(m_pErrInfo);
+ m_pErrInfo = NULL;
+ }
+}
+
+
+void RTCRestClientResponseBase::copyErrInfo(PCRTERRINFO pErrInfo) RT_NOEXCEPT
+{
+ deleteErrInfo();
+ m_pErrInfo = (PRTERRINFO)RTMemDup(pErrInfo, pErrInfo->cbMsg + sizeof(*pErrInfo));
+ if (m_pErrInfo)
+ {
+ m_pErrInfo->pszMsg = (char *)(m_pErrInfo + 1);
+ m_pErrInfo->apvReserved[0] = NULL;
+ m_pErrInfo->apvReserved[1] = NULL;
+ }
+}
+
+
+int RTCRestClientResponseBase::addError(int rc, const char *pszFormat, ...) RT_NOEXCEPT
+{
+ PRTERRINFO pErrInfo = getErrInfoInternal();
+ if (pErrInfo)
+ {
+ va_list va;
+ va_start(va, pszFormat);
+ if ( !RTErrInfoIsSet(pErrInfo)
+ || pErrInfo->cbMsg == 0
+ || pErrInfo->pszMsg[pErrInfo->cbMsg - 1] == '\n')
+ RTErrInfoAddV(pErrInfo, rc, pszFormat, va);
+ else
+ RTErrInfoAddF(pErrInfo, rc, "\n%N", pszFormat, &va);
+ va_end(va);
+ }
+ if (RT_SUCCESS(m_rcStatus) && RT_FAILURE_NP(rc))
+ m_rcStatus = rc;
+ return rc;
+}
+
+
+RTCRestClientResponseBase::PrimaryJsonCursorForBody::PrimaryJsonCursorForBody(RTJSONVAL hValue, const char *pszName,
+ RTCRestClientResponseBase *a_pThat) RT_NOEXCEPT
+ : RTCRestJsonPrimaryCursor(hValue, pszName, a_pThat->getErrInfoInternal())
+ , m_pThat(a_pThat)
+{
+}
+
+
+int RTCRestClientResponseBase::PrimaryJsonCursorForBody::addError(RTCRestJsonCursor const &a_rCursor, int a_rc,
+ const char *a_pszFormat, ...) RT_NOEXCEPT
+{
+ va_list va;
+ va_start(va, a_pszFormat);
+ char szPath[256];
+ m_pThat->addError(a_rc, "response body/%s: %N", getPath(a_rCursor, szPath, sizeof(szPath)), a_pszFormat, &va);
+ va_end(va);
+ return a_rc;
+}
+
+
+int RTCRestClientResponseBase::PrimaryJsonCursorForBody::unknownField(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ char szPath[256];
+ m_pThat->addError(VWRN_NOT_FOUND, "response body/%s: unknown field (type %s)",
+ getPath(a_rCursor, szPath, sizeof(szPath)), RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
+ return VWRN_NOT_FOUND;
+}
+
+
+int RTCRestClientResponseBase::deserializeHeader(RTCRestObjectBase *a_pObj, const char *a_pchValue, size_t a_cchValue,
+ uint32_t a_fFlags, const char *a_pszErrorTag) RT_NOEXCEPT
+{
+ /*
+ * Start by checking the encoding and transfering the value to a RTCString object.
+ */
+ int rc = RTStrValidateEncodingEx(a_pchValue, a_cchValue, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
+ if (RT_SUCCESS(rc))
+ {
+ RTCString strValue;
+ rc = strValue.assignNoThrow(a_pchValue, a_cchValue);
+ if (RT_SUCCESS(rc))
+ {
+ LogRel7(("< %s: :%s = %s\n",
+ getOperationName(), a_pszErrorTag, strValue.c_str()));
+
+ /*
+ * Try deserialize it.
+ */
+ RTERRINFOSTATIC ErrInfo;
+ rc = a_pObj->fromString(strValue, a_pszErrorTag, RTErrInfoInitStatic(&ErrInfo), a_fFlags);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else if (RTErrInfoIsSet(&ErrInfo.Core))
+ addError(rc, "Error %Rrc parsing header field '%s': %s", rc, a_pszErrorTag, ErrInfo.Core.pszMsg);
+ else
+ addError(rc, "Error %Rrc parsing header field '%s'", rc, a_pszErrorTag);
+ }
+ }
+ else
+ {
+ addError(rc, "Error %Rrc validating value encoding of header field '%s': %.*Rhxs",
+ rc, a_pszErrorTag, a_cchValue, a_pchValue);
+ rc = VINF_SUCCESS; /* ignore */
+ }
+ return rc;
+}
+
+
+int RTCRestClientResponseBase::deserializeHeaderIntoMap(RTCRestStringMapBase *a_pMap, const char *a_pchField, size_t a_cchField,
+ const char *a_pchValue, size_t a_cchValue, uint32_t a_fFlags,
+ const char *a_pszErrorTag) RT_NOEXCEPT
+{
+ /*
+ * Start by checking the encoding of both the field and value,
+ * then transfering the value to a RTCString object.
+ */
+ int rc = RTStrValidateEncodingEx(a_pchField, a_cchField, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrValidateEncodingEx(a_pchValue, a_cchValue, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
+ if (RT_SUCCESS(rc))
+ {
+ RTCString strValue;
+ rc = strValue.assignNoThrow(a_pchValue, a_cchValue);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a value object and put it into the map.
+ */
+ RTCRestObjectBase *pValue;
+ rc = a_pMap->putNewValue(&pValue, a_pchField, a_cchField);
+ if (RT_SUCCESS(rc))
+ {
+ LogRel7(("< %s: :%s%.*s = %s\n",
+ getOperationName(), a_pszErrorTag, a_cchField, a_pchField, strValue.c_str()));
+
+ /*
+ * Try deserialize the value.
+ */
+ RTERRINFOSTATIC ErrInfo;
+ rc = pValue->fromString(strValue, a_pszErrorTag, RTErrInfoInitStatic(&ErrInfo), a_fFlags);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else if (RTErrInfoIsSet(&ErrInfo.Core))
+ addError(rc, "Error %Rrc parsing header field '%s' subfield '%.*s': %s",
+ rc, a_pszErrorTag, a_cchField, a_pchField, ErrInfo.Core.pszMsg);
+ else
+ addError(rc, "Error %Rrc parsing header field '%s' subfield '%.*s'",
+ rc, a_pszErrorTag, a_cchField, a_pchField);
+ }
+ }
+ }
+ else
+ {
+ addError(rc, "Error %Rrc validating value encoding of header field '%s': %.*Rhxs",
+ rc, a_pszErrorTag, a_cchValue, a_pchValue);
+ rc = VINF_SUCCESS; /* ignore */
+ }
+ }
+ else
+ {
+ addError(rc, "Error %Rrc validating sub-field encoding of header field '%s*': %.*Rhxs",
+ rc, a_pszErrorTag, a_cchField, a_pchField);
+ rc = VINF_SUCCESS; /* ignore */
+ }
+ return rc;
+}
+
+
+void RTCRestClientResponseBase::deserializeBody(const char *a_pchData, size_t a_cbData, const char *a_pszBodyName) RT_NOEXCEPT
+{
+ if (m_strContentType.startsWith("application/json"))
+ {
+ int rc = RTStrValidateEncodingEx(a_pchData, a_cbData, RTSTR_VALIDATE_ENCODING_EXACT_LENGTH);
+ if (RT_SUCCESS(rc))
+ {
+ if (LogRelIs7Enabled())
+ {
+ /* skip m_ or m_p prefix */
+ const char *pszName = a_pszBodyName;
+ if (pszName[0] == 'm' && pszName[1] == '_')
+ {
+ if (pszName[2] == 'p')
+ pszName += 3;
+ else
+ pszName += 2;
+ }
+
+ LogRel7(("< %s: %d: %s = %.*s\n",
+ getOperationName(), m_rcHttp, pszName, a_cbData, a_pchData));
+ }
+
+ RTERRINFOSTATIC ErrInfo;
+ RTJSONVAL hValue;
+ rc = RTJsonParseFromBuf(&hValue, (const uint8_t *)a_pchData, a_cbData, RTErrInfoInitStatic(&ErrInfo));
+ if (RT_SUCCESS(rc))
+ {
+ PrimaryJsonCursorForBody PrimaryCursor(hValue, a_pszBodyName, this); /* note: consumes hValue */
+ deserializeBodyFromJsonCursor(PrimaryCursor.m_Cursor);
+ }
+ else if (RTErrInfoIsSet(&ErrInfo.Core))
+ addError(rc, "Error %Rrc parsing server response as JSON (type %s): %s",
+ rc, a_pszBodyName, ErrInfo.Core.pszMsg);
+ else
+ addError(rc, "Error %Rrc parsing server response as JSON (type %s)", rc, a_pszBodyName);
+ }
+ else if (rc == VERR_INVALID_UTF8_ENCODING)
+ addError(VERR_REST_RESPONSE_INVALID_UTF8_ENCODING, "Invalid UTF-8 body encoding (object type %s; Content-Type: %s)",
+ a_pszBodyName, m_strContentType.c_str());
+ else if (rc == VERR_BUFFER_UNDERFLOW)
+ addError(VERR_REST_RESPONSE_EMBEDDED_ZERO_CHAR, "Embedded zero character in response (object type %s; Content-Type: %s)",
+ a_pszBodyName, m_strContentType.c_str());
+ else
+ addError(rc, "Unexpected body validation error (object type %s; Content-Type: %s): %Rrc",
+ a_pszBodyName, m_strContentType.c_str(), rc);
+ }
+ else
+ addError(VERR_REST_RESPONSE_CONTENT_TYPE_NOT_SUPPORTED, "Unsupported content type for '%s': %s",
+ a_pszBodyName, m_strContentType.c_str());
+}
+
+
+void RTCRestClientResponseBase::deserializeBodyFromJsonCursor(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ a_rCursor.m_pPrimary->addError(a_rCursor, VERR_REST_INTERNAL_ERROR_8, "deserializeBodyFromJsonCursor must be overridden!");
+ AssertFailed();
+}
+
diff --git a/src/VBox/Runtime/common/rest/RTCRestJsonPrimaryCursor.cpp b/src/VBox/Runtime/common/rest/RTCRestJsonPrimaryCursor.cpp
new file mode 100644
index 00000000..3092b934
--- /dev/null
+++ b/src/VBox/Runtime/common/rest/RTCRestJsonPrimaryCursor.cpp
@@ -0,0 +1,120 @@
+/* $Id: RTCRestJsonPrimaryCursor.cpp $ */
+/** @file
+ * IPRT - C++ REST, RTCRestJsonPrimaryCursor implementation.
+ */
+
+/*
+ * Copyright (C) 2018-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_REST
+#include <iprt/cpp/restbase.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+
+char *RTCRestJsonPrimaryCursor::getPath(RTCRestJsonCursor const &a_rCursor, char *pszDst, size_t cbDst) const RT_NOEXCEPT
+{
+ AssertReturn(cbDst > 0, NULL);
+
+ /*
+ * To avoid using recursion, we need to first do a pass to figure sizes
+ * and depth. To keep things simple, with the exception of the top name
+ * we only copy out full names.
+ */
+ /* Special case: Insufficient space for the top name. */
+ size_t const cchTopName = strlen(a_rCursor.m_pszName);
+ if (cchTopName >= cbDst)
+ {
+ memcpy(pszDst, a_rCursor.m_pszName, cbDst - 1);
+ pszDst[cbDst - 1] = '\0';
+ }
+ else
+ {
+ /* Determin how deep we should go and the resulting length. */
+ size_t iMaxDepth = 0;
+ size_t cchTotal = cchTopName;
+ for (RTCRestJsonCursor const *pCur = a_rCursor.m_pParent; pCur; pCur = pCur->m_pParent)
+ {
+ size_t const cchName = strlen(pCur->m_pszName);
+ size_t cchNewTotal = cchName + 1 + cchTotal;
+ if (cchNewTotal < cbDst)
+ cchTotal = cchNewTotal;
+ else
+ break;
+ iMaxDepth++;
+ }
+
+ /* Produce the string, in reverse. */
+ char *psz = &pszDst[cchTotal];
+ *psz = '\0';
+ psz -= cchTopName;
+ memcpy(psz, a_rCursor.m_pszName, cchTopName);
+ for (RTCRestJsonCursor const *pCur = a_rCursor.m_pParent; pCur && iMaxDepth > 0; pCur = pCur->m_pParent)
+ {
+ psz -= 1;
+ *psz = '.';
+
+ size_t const cchName = strlen(pCur->m_pszName);
+ psz -= cchName;
+ memcpy(psz, pCur->m_pszName, cchName);
+
+ iMaxDepth--;
+ }
+ Assert(psz == pszDst);
+ }
+ return pszDst;
+}
+
+
+int RTCRestJsonPrimaryCursor::addError(RTCRestJsonCursor const &a_rCursor, int a_rc, const char *a_pszFormat, ...) RT_NOEXCEPT
+{
+ va_list va;
+ va_start(va, a_pszFormat);
+ char szPath[128];
+ a_rc = RTErrInfoAddF(m_pErrInfo, a_rc, "%s: %N\n", getPath(a_rCursor, szPath, sizeof(szPath)), a_pszFormat, &va);
+ va_end(va);
+ return a_rc;
+}
+
+
+int RTCRestJsonPrimaryCursor::unknownField(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ char szPath[128];
+ return RTErrInfoAddF(m_pErrInfo, VWRN_NOT_FOUND, "%s: unknown field (type %s)\n",
+ getPath(a_rCursor, szPath, sizeof(szPath)),
+ RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
+}
+
diff --git a/src/VBox/Runtime/common/rest/RTCRestOutputBase.cpp b/src/VBox/Runtime/common/rest/RTCRestOutputBase.cpp
new file mode 100644
index 00000000..7226b45e
--- /dev/null
+++ b/src/VBox/Runtime/common/rest/RTCRestOutputBase.cpp
@@ -0,0 +1,129 @@
+/* $Id: RTCRestOutputBase.cpp $ */
+/** @file
+ * IPRT - C++ REST, RTCRestOutputBase implementation.
+ */
+
+/*
+ * Copyright (C) 2018-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_REST
+#include <iprt/cpp/restoutput.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+
+RTCRestOutputBase::RTCRestOutputBase() RT_NOEXCEPT
+ : m_uState(0)
+{
+}
+
+
+RTCRestOutputBase::~RTCRestOutputBase()
+{
+}
+
+
+size_t RTCRestOutputBase::vprintf(const char *pszFormat, va_list va) RT_NOEXCEPT
+{
+ return RTStrFormatV(printfOutputCallback, this, NULL, NULL, pszFormat, va);
+}
+
+
+/*static*/ DECLCALLBACK(size_t) RTCRestOutputBase::printfOutputCallback(void *pvArg, const char *pachChars, size_t cbChars) RT_NOEXCEPT
+{
+ return ((RTCRestOutputBase *)pvArg)->output(pachChars, cbChars);
+}
+
+
+uint32_t RTCRestOutputBase::beginArray() RT_NOEXCEPT
+{
+ output(RT_STR_TUPLE("["));
+ uint32_t const uOldState = m_uState;
+ m_uState = (uOldState & 0xffff) + 1;
+ return uOldState;
+}
+
+
+void RTCRestOutputBase::endArray(uint32_t a_uOldState) RT_NOEXCEPT
+{
+ m_uState = a_uOldState;
+ output(RT_STR_TUPLE("]"));
+}
+
+
+uint32_t RTCRestOutputBase::beginObject() RT_NOEXCEPT
+{
+ output(RT_STR_TUPLE("{"));
+ uint32_t const uOldState = m_uState;
+ m_uState = (uOldState & 0xffff) + 1;
+ return uOldState;
+}
+
+
+void RTCRestOutputBase::endObject(uint32_t a_uOldState) RT_NOEXCEPT
+{
+ m_uState = a_uOldState;
+ output(RT_STR_TUPLE("}"));
+}
+
+
+void RTCRestOutputBase::valueSeparator() RT_NOEXCEPT
+{
+ if (m_uState & RT_BIT_32(31))
+ output(RT_STR_TUPLE(","));
+ else
+ m_uState |= RT_BIT_32(31);
+}
+
+
+void RTCRestOutputBase::valueSeparatorAndName(const char *a_pszName, size_t a_cchName) RT_NOEXCEPT
+{
+ RT_NOREF(a_cchName);
+ if (m_uState & RT_BIT_32(31))
+ printf(",%RMjs:", a_pszName);
+ else
+ {
+ m_uState |= RT_BIT_32(31);
+ printf("%RMjs:", a_pszName);
+ }
+}
+
+
+void RTCRestOutputBase::nullValue() RT_NOEXCEPT
+{
+ output(RT_STR_TUPLE("null"));
+}
+
diff --git a/src/VBox/Runtime/common/rest/RTCRestOutputPrettyBase.cpp b/src/VBox/Runtime/common/rest/RTCRestOutputPrettyBase.cpp
new file mode 100644
index 00000000..f92581ee
--- /dev/null
+++ b/src/VBox/Runtime/common/rest/RTCRestOutputPrettyBase.cpp
@@ -0,0 +1,134 @@
+/* $Id: RTCRestOutputPrettyBase.cpp $ */
+/** @file
+ * IPRT - C++ REST, RTCRestOutputPrettyBase implementation.
+ */
+
+/*
+ * Copyright (C) 2018-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_REST
+#include <iprt/cpp/restoutput.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+
+RTCRestOutputPrettyBase::RTCRestOutputPrettyBase() RT_NOEXCEPT
+ : RTCRestOutputBase()
+{
+}
+
+
+RTCRestOutputPrettyBase::~RTCRestOutputPrettyBase()
+{
+}
+
+
+uint32_t RTCRestOutputPrettyBase::beginArray() RT_NOEXCEPT
+{
+ output(RT_STR_TUPLE("["));
+ uint32_t const uOldState = m_uState;
+ m_uState = (uOldState & 0xffff) + 1;
+ return uOldState;
+}
+
+
+void RTCRestOutputPrettyBase::endArray(uint32_t a_uOldState) RT_NOEXCEPT
+{
+ m_uState = a_uOldState;
+ output(RT_STR_TUPLE("\n"));
+ outputIndentation();
+ output(RT_STR_TUPLE("]"));
+}
+
+
+uint32_t RTCRestOutputPrettyBase::beginObject() RT_NOEXCEPT
+{
+ output(RT_STR_TUPLE("{"));
+ uint32_t const uOldState = m_uState;
+ m_uState = (uOldState & 0xffff) + 1;
+ return uOldState;
+}
+
+
+void RTCRestOutputPrettyBase::endObject(uint32_t a_uOldState) RT_NOEXCEPT
+{
+ m_uState = a_uOldState;
+ output(RT_STR_TUPLE("\n"));
+ outputIndentation();
+ output(RT_STR_TUPLE("}"));
+}
+
+
+void RTCRestOutputPrettyBase::valueSeparator() RT_NOEXCEPT
+{
+ if (m_uState & RT_BIT_32(31))
+ output(RT_STR_TUPLE(",\n"));
+ else
+ {
+ m_uState |= RT_BIT_32(31);
+ output(RT_STR_TUPLE("\n"));
+ }
+ outputIndentation();
+}
+
+
+void RTCRestOutputPrettyBase::valueSeparatorAndName(const char *a_pszName, size_t a_cchName) RT_NOEXCEPT
+{
+ RT_NOREF(a_cchName);
+ if (m_uState & RT_BIT_32(31))
+ output(RT_STR_TUPLE(",\n"));
+ else
+ {
+ m_uState |= RT_BIT_32(31);
+ output(RT_STR_TUPLE("\n"));
+ }
+ outputIndentation();
+ printf("%RMjs: ", a_pszName);
+}
+
+
+void RTCRestOutputPrettyBase::outputIndentation() RT_NOEXCEPT
+{
+ static char const s_szSpaces[] = " ";
+ size_t cchIndent = (m_uState & 0xffff) << 1;
+ while (cchIndent > 0)
+ {
+ size_t cbToWrite = RT_MIN(cchIndent, sizeof(s_szSpaces) - 1);
+ output(s_szSpaces, cbToWrite);
+ cchIndent -= cbToWrite;
+ }
+}
+
diff --git a/src/VBox/Runtime/common/rest/RTCRestOutputPrettyToString.cpp b/src/VBox/Runtime/common/rest/RTCRestOutputPrettyToString.cpp
new file mode 100644
index 00000000..d968c7a0
--- /dev/null
+++ b/src/VBox/Runtime/common/rest/RTCRestOutputPrettyToString.cpp
@@ -0,0 +1,130 @@
+/* $Id: RTCRestOutputPrettyToString.cpp $ */
+/** @file
+ * IPRT - C++ REST, RTCRestOutputPrettyToString implementation.
+ */
+
+/*
+ * Copyright (C) 2018-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_REST
+#include <iprt/cpp/restoutput.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+
+RTCRestOutputPrettyToString::RTCRestOutputPrettyToString(RTCString *a_pDst, bool a_fAppend /*= false*/) RT_NOEXCEPT
+ : RTCRestOutputPrettyBase()
+ , m_pDst(a_pDst)
+ , m_fOutOfMemory(false)
+{
+ if (!a_fAppend)
+ m_pDst->setNull();
+}
+
+
+RTCRestOutputPrettyToString::~RTCRestOutputPrettyToString()
+{
+ /* We don't own the string, so we don't delete it! */
+ m_pDst = NULL;
+}
+
+
+size_t RTCRestOutputPrettyToString::output(const char *a_pchString, size_t a_cchToWrite) RT_NOEXCEPT
+{
+ if (a_cchToWrite)
+ {
+ RTCString *pDst = m_pDst;
+ if (pDst && !m_fOutOfMemory)
+ {
+ /*
+ * Make sure we've got sufficient space available before we append.
+ */
+ size_t cchCurrent = pDst->length();
+ size_t cbCapacity = pDst->capacity();
+ size_t cbNeeded = cchCurrent + a_cchToWrite + 1;
+ if (cbNeeded <= cbCapacity)
+ { /* likely */ }
+ else
+ {
+ /* Grow it. */
+ if (cbNeeded < _16M)
+ {
+ if (cbCapacity <= _1K)
+ cbCapacity = _1K;
+ else
+ cbCapacity = RT_ALIGN_Z(cbCapacity, _1K);
+ while (cbCapacity < cbNeeded)
+ cbCapacity <<= 1;
+ }
+ else
+ {
+ cbCapacity = RT_ALIGN_Z(cbCapacity, _2M);
+ while (cbCapacity < cbNeeded)
+ cbCapacity += _2M;
+ }
+ int rc = pDst->reserveNoThrow(cbCapacity);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pDst->reserveNoThrow(cbNeeded);
+ if (RT_FAILURE(rc))
+ {
+ m_fOutOfMemory = true;
+ return a_cchToWrite;
+ }
+ }
+ }
+
+ /*
+ * Do the appending.
+ */
+ pDst->append(a_pchString, a_cchToWrite);
+ }
+ }
+ return a_cchToWrite;
+}
+
+
+RTCString *RTCRestOutputPrettyToString::finalize() RT_NOEXCEPT
+{
+ RTCString *pRet;
+ if (!m_fOutOfMemory)
+ pRet = m_pDst;
+ else
+ pRet = NULL;
+ m_pDst = NULL;
+ return pRet;
+}
+
diff --git a/src/VBox/Runtime/common/rest/RTCRestOutputToString.cpp b/src/VBox/Runtime/common/rest/RTCRestOutputToString.cpp
new file mode 100644
index 00000000..4b65faaa
--- /dev/null
+++ b/src/VBox/Runtime/common/rest/RTCRestOutputToString.cpp
@@ -0,0 +1,130 @@
+/* $Id: RTCRestOutputToString.cpp $ */
+/** @file
+ * IPRT - C++ REST, RTCRestOutputToString implementation.
+ */
+
+/*
+ * Copyright (C) 2018-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_REST
+#include <iprt/cpp/restoutput.h>
+
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+
+RTCRestOutputToString::RTCRestOutputToString(RTCString *a_pDst, bool a_fAppend /*= false*/) RT_NOEXCEPT
+ : RTCRestOutputBase()
+ , m_pDst(a_pDst)
+ , m_fOutOfMemory(false)
+{
+ if (!a_fAppend)
+ m_pDst->setNull();
+}
+
+
+RTCRestOutputToString::~RTCRestOutputToString()
+{
+ /* We don't own the string, so we don't delete it! */
+ m_pDst = NULL;
+}
+
+
+size_t RTCRestOutputToString::output(const char *a_pchString, size_t a_cchToWrite) RT_NOEXCEPT
+{
+ if (a_cchToWrite)
+ {
+ RTCString *pDst = m_pDst;
+ if (pDst && !m_fOutOfMemory)
+ {
+ /*
+ * Make sure we've got sufficient space available before we append.
+ */
+ size_t cchCurrent = pDst->length();
+ size_t cbCapacity = pDst->capacity();
+ size_t cbNeeded = cchCurrent + a_cchToWrite + 1;
+ if (cbNeeded <= cbCapacity)
+ { /* likely */ }
+ else
+ {
+ /* Grow it. */
+ if (cbNeeded < _16M)
+ {
+ if (cbCapacity <= _1K)
+ cbCapacity = _1K;
+ else
+ cbCapacity = RT_ALIGN_Z(cbCapacity, _1K);
+ while (cbCapacity < cbNeeded)
+ cbCapacity <<= 1;
+ }
+ else
+ {
+ cbCapacity = RT_ALIGN_Z(cbCapacity, _2M);
+ while (cbCapacity < cbNeeded)
+ cbCapacity += _2M;
+ }
+ int rc = pDst->reserveNoThrow(cbCapacity);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pDst->reserveNoThrow(cbNeeded);
+ if (RT_FAILURE(rc))
+ {
+ m_fOutOfMemory = true;
+ return a_cchToWrite;
+ }
+ }
+ }
+
+ /*
+ * Do the appending.
+ */
+ pDst->append(a_pchString, a_cchToWrite);
+ }
+ }
+ return a_cchToWrite;
+}
+
+
+RTCString *RTCRestOutputToString::finalize() RT_NOEXCEPT
+{
+ RTCString *pRet;
+ if (!m_fOutOfMemory)
+ pRet = m_pDst;
+ else
+ pRet = NULL;
+ m_pDst = NULL;
+ return pRet;
+}
+
diff --git a/src/VBox/Runtime/common/rest/RTCRestStringMapBase.cpp b/src/VBox/Runtime/common/rest/RTCRestStringMapBase.cpp
new file mode 100644
index 00000000..8d11fbd6
--- /dev/null
+++ b/src/VBox/Runtime/common/rest/RTCRestStringMapBase.cpp
@@ -0,0 +1,467 @@
+/* $Id: RTCRestStringMapBase.cpp $ */
+/** @file
+ * IPRT - C++ REST, RTCRestStringMapBase implementation.
+ */
+
+/*
+ * Copyright (C) 2018-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_REST
+#include <iprt/cpp/reststringmap.h>
+
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/cpp/restoutput.h>
+
+
+/**
+ * Default destructor.
+ */
+RTCRestStringMapBase::RTCRestStringMapBase() RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , m_Map(NULL)
+ , m_cEntries(0)
+{
+ RTListInit(&m_ListHead);
+}
+
+
+#if 0 /* trigger link error for now. */
+/** Copy constructor. */
+RTCRestStringMapBase::RTCRestStringMapBase(RTCRestStringMapBase const &a_rThat);
+#endif
+
+
+/**
+ * Destructor.
+ */
+RTCRestStringMapBase::~RTCRestStringMapBase()
+{
+ clear();
+}
+
+
+
+#if 0 /* trigger link error for now. */
+/** Copy assignment operator. */
+RTCRestStringMapBase &RTCRestStringMapBase::operator=(RTCRestStringMapBase const &a_rThat);
+#endif
+
+
+/*********************************************************************************************************************************
+* Overridden base object methods *
+*********************************************************************************************************************************/
+
+RTCRestObjectBase *RTCRestStringMapBase::baseClone() const RT_NOEXCEPT
+{
+ RTCRestStringMapBase *pClone = createClone();
+ if (pClone)
+ {
+ int rc = pClone->copyMapWorkerNoThrow(*this);
+ if (RT_SUCCESS(rc))
+ return pClone;
+ delete pClone;
+ }
+ return NULL;
+}
+
+
+int RTCRestStringMapBase::resetToDefault() RT_NOEXCEPT
+{
+ /* Default is an empty map. */
+ clear();
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+}
+
+
+RTCRestOutputBase &RTCRestStringMapBase::serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
+{
+ if (!m_fNullIndicator)
+ {
+ uint32_t const uOldState = a_rDst.beginObject();
+ MapEntry const *pCur;
+ RTListForEachCpp(&m_ListHead, pCur, MapEntry, ListEntry)
+ {
+ a_rDst.valueSeparatorAndName(pCur->strKey.c_str(), pCur->strKey.length());
+ pCur->pValue->serializeAsJson(a_rDst);
+ }
+ a_rDst.endObject(uOldState);
+ }
+ else
+ a_rDst.nullValue();
+ return a_rDst;
+}
+
+
+int RTCRestStringMapBase::deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ if (RTJsonValueGetType(a_rCursor.m_hValue) == RTJSONVALTYPE_NULL)
+ {
+ setNull();
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Make sure the object starts out with an empty map.
+ */
+ if (m_cEntries > 0)
+ clear();
+ m_fNullIndicator = false;
+
+ /*
+ * Iterate the object values.
+ */
+ RTJSONIT hIterator;
+ int rcRet = RTJsonIteratorBeginObject(a_rCursor.m_hValue, &hIterator);
+ if (RT_SUCCESS(rcRet))
+ {
+ for (;;)
+ {
+ /* Set up the sub-cursor. */
+ RTCRestJsonCursor SubCursor(a_rCursor);
+ int rc = RTJsonIteratorQueryValue(hIterator, &SubCursor.m_hValue, &SubCursor.m_pszName);
+ if (RT_SUCCESS(rc))
+ {
+ /* Call the static deserializeInstanceFromJson method of the value class. */
+ RTCRestObjectBase *pObj = NULL;
+ rc = deserializeValueInstanceFromJson(SubCursor, &pObj);
+ if (RT_SUCCESS(rc))
+ Assert(pObj);
+ else if (RT_SUCCESS(rcRet))
+ rcRet = rc;
+ if (pObj)
+ {
+ /* Insert the value. */
+ rc = putWorker(SubCursor.m_pszName, pObj, true /*a_fReplace*/);
+ if (rc == VINF_SUCCESS)
+ { /* likely */ }
+ else if (RT_SUCCESS(rc))
+ {
+ a_rCursor.m_pPrimary->addError(a_rCursor, rc, "warning %Rrc inserting '%s' into map",
+ rc, SubCursor.m_pszName);
+ if (rcRet == VINF_SUCCESS)
+ rcRet = rc;
+ }
+ else
+ {
+ rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "Failed to insert '%s' into map: %Rrc",
+ SubCursor.m_pszName, rc);
+ delete pObj;
+ }
+ }
+ }
+ else
+ rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonIteratorQueryValue failed: %Rrc", rc);
+
+ /*
+ * Advance.
+ */
+ rc = RTJsonIteratorNext(hIterator);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else if (rc == VERR_JSON_ITERATOR_END)
+ break;
+ else
+ {
+ rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonIteratorNext failed: %Rrc", rc);
+ break;
+ }
+ }
+
+ RTJsonIteratorFree(hIterator);
+ }
+ else if (rcRet == VERR_JSON_IS_EMPTY)
+ rcRet = VINF_SUCCESS;
+ else if ( rcRet == VERR_JSON_VALUE_INVALID_TYPE
+ && RTJsonValueGetType(a_rCursor.m_hValue) == RTJSONVALTYPE_NULL)
+ {
+ m_fNullIndicator = true;
+ rcRet = VINF_SUCCESS;
+ }
+ else
+ rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rcRet, "RTJsonIteratorBegin failed: %Rrc (type %s)",
+ rcRet, RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
+ return rcRet;
+}
+
+// later?
+// virtual int RTCRestStringMapBase::toString(RTCString *a_pDst, uint32_t a_fFlags = kCollectionFormat_Unspecified) const ;
+// virtual int RTCRestStringMapBase::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo = NULL,
+// uint32_t a_fFlags = kCollectionFormat_Unspecified) ;
+//
+
+
+RTCRestObjectBase::kTypeClass RTCRestStringMapBase::typeClass(void) const RT_NOEXCEPT
+{
+ return kTypeClass_StringMap;
+}
+
+
+const char *RTCRestStringMapBase::typeName(void) const RT_NOEXCEPT
+{
+ return "RTCRestStringMap<ValueType>";
+}
+
+
+/*********************************************************************************************************************************
+* Generic map methods *
+*********************************************************************************************************************************/
+
+/**
+ * @callback_method_impl{FNRTSTRSPACECALLBACK}
+ */
+/*static*/ DECLCALLBACK(int) RTCRestStringMapBase::stringSpaceDestructorCallback(PRTSTRSPACECORE pStr, void *pvUser) RT_NOEXCEPT
+{
+ MapEntry *pNode = (MapEntry *)pStr;
+ if (pNode->pValue)
+ {
+ delete pNode->pValue;
+ pNode->pValue = NULL;
+ }
+ pNode->strKey.setNull();
+ delete pNode;
+
+ RT_NOREF(pvUser);
+ return VINF_SUCCESS;
+}
+
+
+void RTCRestStringMapBase::clear() RT_NOEXCEPT
+{
+ RTStrSpaceDestroy(&m_Map, stringSpaceDestructorCallback, NULL);
+ RTListInit(&m_ListHead);
+ m_cEntries = 0;
+ m_fNullIndicator = false;
+}
+
+
+size_t RTCRestStringMapBase::size() const RT_NOEXCEPT
+{
+ return m_cEntries;
+}
+
+
+bool RTCRestStringMapBase::containsKey(const char *a_pszKey) const RT_NOEXCEPT
+{
+ if (isNull())
+ return false;
+
+ return RTStrSpaceGet((PRTSTRSPACE)&m_Map, a_pszKey) != NULL;
+}
+
+
+bool RTCRestStringMapBase::containsKey(RTCString const &a_rStrKey) const RT_NOEXCEPT
+{
+ return containsKey(a_rStrKey.c_str());
+}
+
+
+bool RTCRestStringMapBase::remove(const char *a_pszKey) RT_NOEXCEPT
+{
+ if (isNull())
+ return false;
+
+ MapEntry *pRemoved = (MapEntry *)RTStrSpaceRemove(&m_Map, a_pszKey);
+ if (pRemoved)
+ {
+ m_cEntries--;
+ RTListNodeRemove(&pRemoved->ListEntry);
+ stringSpaceDestructorCallback(&pRemoved->Core, NULL);
+ return true;
+ }
+ return false;
+}
+
+
+bool RTCRestStringMapBase::remove(RTCString const &a_rStrKey) RT_NOEXCEPT
+{
+ return remove(a_rStrKey.c_str());
+}
+
+
+int RTCRestStringMapBase::putNewValue(RTCRestObjectBase **a_ppValue, const char *a_pszKey, size_t a_cchKey /*= RTSTR_MAX*/,
+ bool a_fReplace /*= false*/) RT_NOEXCEPT
+{
+ RTCRestObjectBase *pValue = createValue();
+ if (pValue)
+ {
+ int rc = putWorker(a_pszKey, pValue, a_fReplace, a_cchKey);
+ if (RT_SUCCESS(rc))
+ *a_ppValue = pValue;
+ else
+ {
+ delete pValue;
+ *a_ppValue = NULL;
+ }
+ return rc;
+ }
+ *a_ppValue = NULL;
+ return VERR_NO_MEMORY;
+}
+
+
+int RTCRestStringMapBase::putNewValue(RTCRestObjectBase **a_ppValue, RTCString const &a_rStrKey, bool a_fReplace /*= false*/) RT_NOEXCEPT
+{
+ return putNewValue(a_ppValue, a_rStrKey.c_str(), a_rStrKey.length(), a_fReplace);
+}
+
+
+/*********************************************************************************************************************************
+* Protected methods *
+*********************************************************************************************************************************/
+
+int RTCRestStringMapBase::copyMapWorkerNoThrow(RTCRestStringMapBase const &a_rThat) RT_NOEXCEPT
+{
+ Assert(this != &a_rThat);
+ clear();
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+
+ if (!a_rThat.m_fNullIndicator)
+ {
+ MapEntry const *pCur;
+ RTListForEachCpp(&a_rThat.m_ListHead, pCur, MapEntry, ListEntry)
+ {
+ int rc = putCopyWorker(pCur->strKey.c_str(), *pCur->pValue, true /*a_fReplace*/);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ return rc;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+void RTCRestStringMapBase::copyMapWorkerMayThrow(RTCRestStringMapBase const &a_rThat)
+{
+ int rc = copyMapWorkerNoThrow(a_rThat);
+ if (RT_SUCCESS(rc))
+ return;
+ throw std::bad_alloc();
+}
+
+
+int RTCRestStringMapBase::putWorker(const char *a_pszKey, RTCRestObjectBase *a_pValue, bool a_fReplace,
+ size_t a_cchKey /*= RTSTR_MAX*/) RT_NOEXCEPT
+{
+ int rc;
+ MapEntry *pEntry = new (std::nothrow) MapEntry;
+ if (pEntry)
+ {
+ rc = pEntry->strKey.assignNoThrow(a_pszKey, a_cchKey);
+ if (RT_SUCCESS(rc))
+ {
+ pEntry->Core.pszString = pEntry->strKey.c_str();
+ pEntry->Core.cchString = pEntry->strKey.length();
+ pEntry->pValue = a_pValue;
+ if (RTStrSpaceInsert(&m_Map, &pEntry->Core))
+ {
+ RTListAppend(&m_ListHead, &pEntry->ListEntry);
+ m_cEntries++;
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+ }
+
+ Assert(!m_fNullIndicator);
+ if (!a_fReplace)
+ rc = VERR_ALREADY_EXISTS;
+ else
+ {
+ /* Just replace the pValue in the existing entry. */
+ MapEntry *pCollision = (MapEntry *)RTStrSpaceGet(&m_Map, a_pszKey);
+ if (pCollision)
+ {
+ if (pCollision->pValue)
+ delete pCollision->pValue;
+ pCollision->pValue = a_pValue;
+ pEntry->pValue = NULL; /* paranoia */
+ rc = VWRN_ALREADY_EXISTS;
+ }
+ else
+ rc = VERR_INTERNAL_ERROR;
+ }
+ }
+ delete pEntry;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+int RTCRestStringMapBase::putCopyWorker(const char *a_pszKey, RTCRestObjectBase const &a_rValue, bool a_fReplace,
+ size_t a_cchKey /*= RTSTR_MAX*/) RT_NOEXCEPT
+{
+ int rc;
+ RTCRestObjectBase *pValueCopy = a_rValue.baseClone();
+ if (pValueCopy)
+ {
+ rc = putWorker(a_pszKey, pValueCopy, a_fReplace, a_cchKey);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else
+ delete pValueCopy;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+RTCRestObjectBase *RTCRestStringMapBase::getWorker(const char *a_pszKey) RT_NOEXCEPT
+{
+ if (isNull())
+ return NULL;
+
+ MapEntry *pHit = (MapEntry *)RTStrSpaceGet(&m_Map, a_pszKey);
+ if (pHit)
+ return pHit->pValue;
+ return NULL;
+}
+
+
+RTCRestObjectBase const *RTCRestStringMapBase::getWorker(const char *a_pszKey) const RT_NOEXCEPT
+{
+ if (isNull())
+ return NULL;
+
+ MapEntry const *pHit = (MapEntry const *)RTStrSpaceGet((PRTSTRSPACE)&m_Map, a_pszKey);
+ if (pHit)
+ return pHit->pValue;
+ return NULL;
+}
+
diff --git a/src/VBox/Runtime/common/rest/rest-binary.cpp b/src/VBox/Runtime/common/rest/rest-binary.cpp
new file mode 100644
index 00000000..301c2e04
--- /dev/null
+++ b/src/VBox/Runtime/common/rest/rest-binary.cpp
@@ -0,0 +1,708 @@
+/* $Id: rest-binary.cpp $ */
+/** @file
+ * IPRT - C++ REST, RTCRestBinary and Descendants.
+ */
+
+/*
+ * Copyright (C) 2018-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_REST
+#include <iprt/cpp/restbase.h>
+#include <iprt/cpp/restclient.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/cpp/restoutput.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The default maximum download size. */
+#if ARCH_BITS == 32
+# define RTCREST_MAX_DOWNLOAD_SIZE_DEFAULT _32M
+#else
+# define RTCREST_MAX_DOWNLOAD_SIZE_DEFAULT _128M
+#endif
+
+
+
+/*********************************************************************************************************************************
+* RTCRestBinary Implementation. *
+*********************************************************************************************************************************/
+/**
+ * Default constructor.
+ */
+RTCRestBinary::RTCRestBinary() RT_NOEXCEPT
+ : m_pbData(NULL)
+ , m_cbData(0)
+ , m_cbAllocated(0)
+ , m_fFreeable(true)
+ , m_fReadOnly(false)
+{
+}
+
+
+/**
+ * Destructor.
+ */
+RTCRestBinary::~RTCRestBinary()
+{
+ freeData();
+}
+
+/**
+ * Safe copy assignment method.
+ */
+int RTCRestBinary::assignCopy(RTCRestBinary const &a_rThat) RT_NOEXCEPT
+{
+ freeData();
+ if (a_rThat.m_pbData)
+ {
+ m_pbData = (uint8_t *)RTMemDup(a_rThat.m_pbData, a_rThat.m_cbAllocated);
+ AssertReturn(m_pbData, VERR_NO_MEMORY);
+ m_cbData = a_rThat.m_cbData;
+ m_cbAllocated = a_rThat.m_cbAllocated;
+ m_fFreeable = true;
+ m_fReadOnly = false;
+ m_fNullIndicator = false;
+ }
+ else
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Safe buffer copy method.
+ */
+int RTCRestBinary::assignCopy(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT
+{
+ if ( m_pbData == NULL
+ || m_fReadOnly
+ || a_cbData > m_cbAllocated)
+ {
+ freeData();
+ m_pbData = (uint8_t *)RTMemDup(a_pvData, a_cbData);
+ AssertReturn(m_pbData, VERR_NO_MEMORY);
+ m_cbData = a_cbData;
+ m_cbAllocated = a_cbData;
+ m_fFreeable = true;
+ m_fReadOnly = false;
+ }
+ else
+ {
+ m_cbData = a_cbData;
+ memcpy(m_pbData, a_pvData, a_cbData);
+ }
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Use the specified data buffer directly.
+ */
+int RTCRestBinary::assignReadOnly(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT
+{
+ freeData();
+ if (a_pvData)
+ {
+ m_pbData = (uint8_t *)a_pvData;
+ m_cbData = a_cbData;
+ m_cbAllocated = 0;
+ m_fFreeable = false;
+ m_fReadOnly = true;
+ m_fNullIndicator = false;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Use the specified data buffer directly.
+ */
+int RTCRestBinary::assignWriteable(void *a_pvBuf, size_t a_cbBuf) RT_NOEXCEPT
+{
+ freeData();
+ if (a_pvBuf)
+ {
+ m_pbData = (uint8_t *)a_pvBuf;
+ m_cbData = a_cbBuf;
+ m_cbAllocated = a_cbBuf;
+ m_fFreeable = false;
+ m_fReadOnly = false;
+ m_fNullIndicator = false;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Frees the data held by the object and resets it default state.
+ */
+void RTCRestBinary::freeData() RT_NOEXCEPT
+{
+ if (m_fFreeable)
+ RTMemFree(m_pbData);
+ m_pbData = NULL;
+ m_cbData = 0;
+ m_cbAllocated = 0;
+ m_fFreeable = true;
+ m_fReadOnly = false;
+}
+
+
+/* Overridden methods: */
+
+RTCRestObjectBase *RTCRestBinary::baseClone() const RT_NOEXCEPT
+{
+ RTCRestBinary *pClone = new (std::nothrow) RTCRestBinary();
+ if (pClone)
+ {
+ int rc = pClone->assignCopy(*this);
+ if (RT_SUCCESS(rc))
+ return pClone;
+ delete pClone;
+ }
+ return NULL;
+}
+
+
+int RTCRestBinary::setNull(void) RT_NOEXCEPT
+{
+ freeData();
+ m_fNullIndicator = true;
+ return VINF_SUCCESS;
+}
+
+
+int RTCRestBinary::resetToDefault(void) RT_NOEXCEPT
+{
+ freeData();
+ return VINF_SUCCESS;
+}
+
+
+RTCRestOutputBase &RTCRestBinary::serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
+{
+ AssertMsgFailed(("We should never get here!\n"));
+ a_rDst.nullValue();
+ return a_rDst;
+}
+
+
+int RTCRestBinary::deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_NOT_SUPPORTED, "RTCRestBinary does not support deserialization!");
+}
+
+
+int RTCRestBinary::toString(RTCString *a_pDst, uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) const RT_NOEXCEPT
+{
+ RT_NOREF(a_pDst, a_fFlags);
+ AssertFailedReturn(VERR_NOT_SUPPORTED);
+}
+
+
+int RTCRestBinary::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo/*= NULL*/,
+ uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) RT_NOEXCEPT
+{
+ RT_NOREF(a_rValue, a_pszName, a_fFlags);
+ AssertFailedReturn(RTErrInfoSet(a_pErrInfo, VERR_NOT_SUPPORTED, "RTCRestBinary does not support fromString()!"));
+}
+
+
+RTCRestObjectBase::kTypeClass RTCRestBinary::typeClass(void) const RT_NOEXCEPT
+{
+ return kTypeClass_Binary;
+}
+
+
+const char *RTCRestBinary::typeName(void) const RT_NOEXCEPT
+{
+ return "RTCRestBinary";
+}
+
+
+/** Factory method. */
+/*static*/ DECLCALLBACK(RTCRestObjectBase *) RTCRestBinary::createInstance(void) RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestBinary();
+}
+
+
+/**
+ * @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON
+ */
+/*static*/ DECLCALLBACK(int)
+RTCRestBinary::deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT
+{
+ RTCRestObjectBase *pObj;
+ *a_ppInstance = pObj = createInstance();
+ if (pObj)
+ return pObj->deserializeFromJson(a_rCursor);
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_NO_MEMORY, "Out of memory");
+}
+
+
+
+/*********************************************************************************************************************************
+* RTCRestBinaryParameter Implementation. *
+*********************************************************************************************************************************/
+
+/**
+ * Default constructor.
+ */
+RTCRestBinaryParameter::RTCRestBinaryParameter() RT_NOEXCEPT
+ : RTCRestBinary()
+ , m_cbContentLength(UINT64_MAX)
+ , m_strContentType()
+ , m_pfnProducer(NULL)
+ , m_pvCallbackData(NULL)
+{
+}
+
+
+int RTCRestBinaryParameter::assignCopy(RTCRestBinaryParameter const &a_rThat) RT_NOEXCEPT
+{
+ AssertReturn(a_rThat.m_pfnProducer, VERR_INVALID_STATE);
+ int rc = assignCopy(*(RTCRestBinary const *)&a_rThat);
+ if (RT_SUCCESS(rc))
+ rc = m_strContentType.assignNoThrow(a_rThat.m_strContentType);
+ m_cbContentLength = a_rThat.m_cbContentLength;
+ m_pfnProducer = a_rThat.m_pfnProducer;
+ m_pvCallbackData = a_rThat.m_pvCallbackData;
+ return VINF_SUCCESS;
+}
+
+
+int RTCRestBinaryParameter::assignCopy(RTCRestBinary const &a_rThat) RT_NOEXCEPT
+{
+ m_cbContentLength = a_rThat.getSize();
+ m_strContentType.setNull();
+ m_pfnProducer = NULL;
+ m_pvCallbackData = NULL;
+ return RTCRestBinary::assignCopy(a_rThat);
+}
+
+
+int RTCRestBinaryParameter::assignCopy(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT
+{
+ m_cbContentLength = a_cbData;
+ m_pfnProducer = NULL;
+ m_pvCallbackData = NULL;
+ return RTCRestBinary::assignCopy(a_pvData, a_cbData);
+}
+
+
+int RTCRestBinaryParameter::assignReadOnly(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT
+{
+ m_cbContentLength = a_cbData;
+ m_pfnProducer = NULL;
+ m_pvCallbackData = NULL;
+ return RTCRestBinary::assignReadOnly(a_pvData, a_cbData);
+}
+
+
+int RTCRestBinaryParameter::assignWriteable(void *a_pvBuf, size_t a_cbBuf) RT_NOEXCEPT
+{
+ AssertMsgFailed(("Please use assignReadOnly!\n"));
+ return assignReadOnly(a_pvBuf, a_cbBuf);
+}
+
+
+RTCRestObjectBase *RTCRestBinaryParameter::baseClone() const RT_NOEXCEPT
+{
+ RTCRestBinaryParameter *pClone = new (std::nothrow) RTCRestBinaryParameter();
+ if (pClone)
+ {
+ int rc = pClone->assignCopy(*this);
+ if (RT_SUCCESS(rc))
+ return pClone;
+ delete pClone;
+ }
+ return NULL;
+}
+
+
+int RTCRestBinaryParameter::resetToDefault() RT_NOEXCEPT
+{
+ m_cbContentLength = UINT64_MAX;
+ m_pfnProducer = NULL;
+ m_pvCallbackData = NULL;
+ return RTCRestBinary::resetToDefault();
+}
+
+
+const char *RTCRestBinaryParameter::typeName(void) const RT_NOEXCEPT
+{
+ return "RTCRestBinaryParameter";
+}
+
+
+/*static*/ DECLCALLBACK(RTCRestObjectBase *) RTCRestBinaryParameter::createInstance(void) RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestBinaryParameter();
+}
+
+
+int RTCRestBinaryParameter::setContentType(const char *a_pszContentType) RT_NOEXCEPT
+{
+ return m_strContentType.assignNoThrow(a_pszContentType);
+}
+
+
+void RTCRestBinaryParameter::setProducerCallback(PFNPRODUCER a_pfnProducer, void *a_pvCallbackData /*= NULL*/,
+ uint64_t a_cbContentLength /*= UINT64_MAX*/) RT_NOEXCEPT
+{
+ freeData();
+
+ m_pfnProducer = a_pfnProducer;
+ m_pvCallbackData = a_pvCallbackData;
+ m_cbContentLength = a_cbContentLength;
+}
+
+
+int RTCRestBinaryParameter::xmitPrepare(RTHTTP a_hHttp) const RT_NOEXCEPT
+{
+ AssertReturn(m_pbData != NULL || m_pfnProducer != NULL || m_cbContentLength == 0, VERR_INVALID_STATE);
+
+
+ /*
+ * Set the content type if given.
+ */
+ if (m_strContentType.isNotEmpty())
+ {
+ Assert(!RTHttpGetHeader(a_hHttp, RT_STR_TUPLE("Content-Type")));
+ int rc = RTHttpAddHeader(a_hHttp, "Content-Type", m_strContentType.c_str(), m_strContentType.length(),
+ RTHTTPADDHDR_F_BACK);
+ AssertRCReturn(rc, rc);
+ }
+
+ /*
+ * Set the content length if given.
+ */
+ if (m_cbContentLength != UINT64_MAX)
+ {
+ const char *pszContentLength = RTHttpGetHeader(a_hHttp, RT_STR_TUPLE("Content-Length"));
+ AssertMsgReturn(!pszContentLength || RTStrToUInt64(pszContentLength) == m_cbContentLength,
+ ("pszContentLength=%s does not match m_cbContentLength=%RU64\n", pszContentLength, m_cbContentLength),
+ VERR_MISMATCH);
+ if (!pszContentLength)
+ {
+ char szValue[64];
+ ssize_t cchValue = RTStrFormatU64(szValue, sizeof(szValue), m_cbContentLength, 10, 0, 0, 0);
+ int rc = RTHttpAddHeader(a_hHttp, "Content-Length", szValue, cchValue, RTHTTPADDHDR_F_BACK);
+ AssertRCReturn(rc, rc);
+ }
+ }
+
+ /*
+ * Register an upload callback.
+ */
+ int rc = RTHttpSetUploadCallback(a_hHttp, m_cbContentLength, xmitHttpCallback, (RTCRestBinaryParameter *)this);
+ AssertRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+
+}
+
+
+/*static*/ DECLCALLBACK(int)
+RTCRestBinaryParameter::xmitHttpCallback(RTHTTP hHttp, void *pvBuf, size_t cbBuf,
+ uint64_t offContent, size_t *pcbActual, void *pvUser) RT_NOEXCEPT
+{
+ RTCRestBinaryParameter *pThis = (RTCRestBinaryParameter *)pvUser;
+
+ /*
+ * Call the user upload callback if we've got one.
+ */
+ if (pThis->m_pfnProducer)
+ return pThis->m_pfnProducer(pThis, pvBuf, cbBuf, offContent, pcbActual);
+
+ /*
+ * Feed from the memory buffer.
+ */
+ if (offContent < pThis->m_cbContentLength)
+ {
+ uint64_t const cbLeft = pThis->m_cbContentLength - offContent;
+ size_t const cbToCopy = cbLeft >= cbBuf ? cbBuf : (size_t)cbLeft;
+ memcpy(pvBuf, &pThis->m_pbData[(size_t)offContent], cbToCopy);
+ *pcbActual = cbToCopy;
+ }
+ else
+ *pcbActual = 0;
+
+ RT_NOREF(hHttp);
+ return VINF_SUCCESS;
+}
+
+
+void RTCRestBinaryParameter::xmitComplete(RTHTTP a_hHttp) const RT_NOEXCEPT
+{
+ /* Unset the callback. */
+ int rc = RTHttpSetUploadCallback(a_hHttp, UINT64_MAX, NULL, NULL);
+ AssertRC(rc);
+}
+
+
+/*********************************************************************************************************************************
+* RTCRestBinaryResponse Implementation. *
+*********************************************************************************************************************************/
+
+/**
+ * Default constructor.
+ */
+RTCRestBinaryResponse::RTCRestBinaryResponse() RT_NOEXCEPT
+ : RTCRestBinary()
+ , m_cbContentLength(UINT64_MAX)
+ , m_cbDownloaded(0)
+ , m_pfnConsumer(NULL)
+ , m_pvCallbackData(NULL)
+ , m_cbMaxDownload(RTCREST_MAX_DOWNLOAD_SIZE_DEFAULT)
+{
+}
+
+
+int RTCRestBinaryResponse::assignCopy(RTCRestBinaryResponse const &a_rThat) RT_NOEXCEPT
+{
+ AssertReturn(a_rThat.m_pfnConsumer, VERR_INVALID_STATE);
+ int rc = assignCopy(*(RTCRestBinary const *)&a_rThat);
+ m_cbContentLength = a_rThat.m_cbContentLength;
+ m_cbDownloaded = a_rThat.m_cbDownloaded;
+ m_cbMaxDownload = a_rThat.m_cbMaxDownload;
+ return rc;
+}
+
+
+int RTCRestBinaryResponse::assignCopy(RTCRestBinary const &a_rThat) RT_NOEXCEPT
+{
+ m_cbContentLength = UINT64_MAX;
+ m_cbDownloaded = 0;
+ m_pfnConsumer = NULL;
+ m_pvCallbackData = NULL;
+ return RTCRestBinary::assignCopy(a_rThat);
+}
+
+
+int RTCRestBinaryResponse::assignCopy(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT
+{
+ RT_NOREF(a_pvData, a_cbData);
+ AssertMsgFailedReturn(("Makes no sense for downloads.\n"), VERR_INVALID_STATE);
+}
+
+
+int RTCRestBinaryResponse::assignReadOnly(void const *a_pvData, size_t a_cbData) RT_NOEXCEPT
+{
+ RT_NOREF(a_pvData, a_cbData);
+ AssertMsgFailedReturn(("Makes no sense for downloads.\n"), VERR_INVALID_STATE);
+}
+
+
+int RTCRestBinaryResponse::assignWriteable(void *a_pvBuf, size_t a_cbBuf) RT_NOEXCEPT
+{
+ m_cbContentLength = UINT64_MAX;
+ m_cbDownloaded = 0;
+ m_pfnConsumer = NULL;
+ m_pvCallbackData = NULL;
+ AssertStmt(a_cbBuf <= m_cbMaxDownload, m_cbMaxDownload = a_cbBuf);
+ return RTCRestBinary::assignWriteable(a_pvBuf, a_cbBuf);
+}
+
+
+RTCRestObjectBase *RTCRestBinaryResponse::baseClone() const RT_NOEXCEPT
+{
+ RTCRestBinaryResponse *pClone = new (std::nothrow) RTCRestBinaryResponse();
+ if (pClone)
+ {
+ int rc = pClone->assignCopy(*this);
+ if (RT_SUCCESS(rc))
+ return pClone;
+ delete pClone;
+ }
+ return NULL;
+}
+
+
+int RTCRestBinaryResponse::resetToDefault() RT_NOEXCEPT
+{
+ m_cbContentLength = UINT64_MAX;
+ m_cbDownloaded = 0;
+ m_pfnConsumer = NULL;
+ m_pvCallbackData = NULL;
+ m_cbMaxDownload = RTCREST_MAX_DOWNLOAD_SIZE_DEFAULT;
+ return RTCRestBinary::resetToDefault();
+}
+
+
+const char *RTCRestBinaryResponse::typeName(void) const RT_NOEXCEPT
+{
+ return "RTCRestBinaryResponse";
+}
+
+
+/*static*/ DECLCALLBACK(RTCRestObjectBase *) RTCRestBinaryResponse::createInstance(void) RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestBinaryResponse();
+}
+
+
+void RTCRestBinaryResponse::setMaxDownloadSize(size_t a_cbMaxDownload) RT_NOEXCEPT
+{
+ if (a_cbMaxDownload == 0)
+ m_cbMaxDownload = RTCREST_MAX_DOWNLOAD_SIZE_DEFAULT;
+ else
+ m_cbMaxDownload = a_cbMaxDownload;
+}
+
+
+void RTCRestBinaryResponse::setConsumerCallback(PFNCONSUMER a_pfnConsumer, void *a_pvCallbackData /*= NULL*/) RT_NOEXCEPT
+{
+ freeData();
+
+ m_pfnConsumer = a_pfnConsumer;
+ m_pvCallbackData = a_pvCallbackData;
+ m_cbDownloaded = 0;
+ m_cbContentLength = UINT64_MAX;
+}
+
+
+int RTCRestBinaryResponse::receivePrepare(RTHTTP a_hHttp, uint32_t a_fCallbackFlags) RT_NOEXCEPT
+{
+ AssertReturn(!m_fReadOnly, VERR_INVALID_STATE);
+
+ /*
+ * Register the download callback.
+ */
+ int rc = RTHttpSetDownloadCallback(a_hHttp, a_fCallbackFlags, receiveHttpCallback, this);
+ AssertRC(rc);
+ return rc;
+}
+
+
+/*static*/ DECLCALLBACK(int)
+RTCRestBinaryResponse::receiveHttpCallback(RTHTTP hHttp, void const *pvBuf, size_t cbBuf, uint32_t uHttpStatus,
+ uint64_t offContent, uint64_t cbContent, void *pvUser) RT_NOEXCEPT
+{
+ RTCRestBinaryResponse *pThis = (RTCRestBinaryResponse *)pvUser;
+ Assert(offContent == pThis->m_cbDownloaded);
+ pThis->m_cbContentLength = cbContent;
+
+ /*
+ * Call the user download callback if we've got one.
+ */
+ if (pThis->m_pfnConsumer)
+ {
+ int rc = pThis->m_pfnConsumer(pThis, pvBuf, cbBuf, uHttpStatus, offContent, cbContent);
+ if (RT_SUCCESS(rc))
+ pThis->m_cbDownloaded = offContent + cbBuf;
+ return rc;
+ }
+
+ /*
+ * Check download limit before adding more data.
+ */
+ AssertMsgReturn(offContent + cbBuf <= pThis->m_cbMaxDownload,
+ ("%RU64 + %zu = %RU64; max=%RU64", offContent, cbBuf, offContent + cbBuf, pThis->m_cbMaxDownload),
+ VERR_TOO_MUCH_DATA);
+
+ /*
+ * Make sure we've got sufficient writable buffer space before we copy in the data.
+ */
+ AssertReturn(!pThis->m_fReadOnly, VERR_INVALID_STATE);
+ if (offContent + cbBuf <= pThis->m_cbAllocated)
+ { /* likely, except for the first time. */ }
+ else
+ {
+ AssertMsgReturn(pThis->m_fFreeable,
+ ("offContent=%RU64 cbBuf=%zu m_cbAllocated=%zu", offContent, cbBuf, pThis->m_cbAllocated),
+ VERR_TOO_MUCH_DATA);
+ AssertMsgReturn(cbContent <= pThis->m_cbMaxDownload || cbContent == UINT64_MAX,
+ ("cbContent: %RU64; max=%RU64", cbContent, pThis->m_cbMaxDownload),
+ VERR_TOO_MUCH_DATA);
+
+ if (offContent == 0 && cbContent != UINT64_MAX)
+ {
+ void *pvNew = RTMemRealloc(pThis->m_pbData, (size_t)cbContent);
+ if (!pvNew)
+ return VERR_NO_MEMORY;
+ pThis->m_pbData = (uint8_t *)pvNew;
+ pThis->m_cbAllocated = (size_t)cbContent;
+ }
+ else
+ {
+ size_t cbNeeded = offContent + cbBuf;
+ size_t cbNew;
+ if (pThis->m_cbAllocated == 0)
+ cbNew = RT_MAX(_64K, RT_ALIGN_Z(cbNeeded, _64K));
+ else if (pThis->m_cbAllocated < _64M && cbNeeded <= _64M)
+ {
+ cbNew = pThis->m_cbAllocated * 2;
+ while (cbNew < cbNeeded)
+ cbNew *= 2;
+ }
+ else
+ cbNew = RT_ALIGN_Z(cbNeeded, _32M);
+
+ void *pvNew = RTMemRealloc(pThis->m_pbData, cbNew);
+ if (!pvNew)
+ return VERR_NO_MEMORY;
+ pThis->m_pbData = (uint8_t *)pvNew;
+ pThis->m_cbAllocated = cbNew;
+ }
+ }
+
+ /*
+ * Do the copying.
+ */
+ memcpy(&pThis->m_pbData[(size_t)offContent], pvBuf, cbBuf);
+ pThis->m_cbDownloaded = offContent + cbBuf;
+
+ /* we cap it at m_cbMaxDownload which is size_t so this cast is safe */
+ pThis->m_cbData = (size_t)pThis->m_cbDownloaded;
+
+ RT_NOREF(hHttp);
+ return VINF_SUCCESS;
+}
+
+
+void RTCRestBinaryResponse::receiveComplete(RTHTTP a_hHttp) RT_NOEXCEPT
+{
+ /* Unset the callback. */
+ int rc = RTHttpSetDownloadCallback(a_hHttp, RTHTTPDOWNLOAD_F_ANY_STATUS, NULL, NULL);
+ AssertRC(rc);
+}
+
diff --git a/src/VBox/Runtime/common/rest/rest-primary-object-types.cpp b/src/VBox/Runtime/common/rest/rest-primary-object-types.cpp
new file mode 100644
index 00000000..5101801f
--- /dev/null
+++ b/src/VBox/Runtime/common/rest/rest-primary-object-types.cpp
@@ -0,0 +1,2403 @@
+/* $Id: rest-primary-object-types.cpp $ */
+/** @file
+ * IPRT - C++ REST, RTCRestObjectBase implementation.
+ */
+
+/*
+ * Copyright (C) 2018-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_REST
+#include <iprt/cpp/restbase.h>
+
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/cpp/restoutput.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+
+
+/*********************************************************************************************************************************
+* RTCRestObjectBase implementation *
+*********************************************************************************************************************************/
+
+/** Default constructor. */
+RTCRestObjectBase::RTCRestObjectBase() RT_NOEXCEPT
+ : m_fNullIndicator(false)
+{
+}
+
+
+/** Copy constructor. */
+RTCRestObjectBase::RTCRestObjectBase(RTCRestObjectBase const &a_rThat) RT_NOEXCEPT
+ : m_fNullIndicator(a_rThat.m_fNullIndicator)
+{
+}
+
+
+/** Destructor. */
+RTCRestObjectBase::~RTCRestObjectBase()
+{
+ /* nothing to do */
+}
+
+
+/** Copy assignment operator. */
+RTCRestObjectBase &RTCRestObjectBase::operator=(RTCRestObjectBase const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ return *this;
+}
+
+
+int RTCRestObjectBase::setNull() RT_NOEXCEPT
+{
+ int rc = resetToDefault();
+ m_fNullIndicator = true;
+ return rc;
+}
+
+
+void RTCRestObjectBase::setNotNull() RT_NOEXCEPT
+{
+ m_fNullIndicator = false;
+}
+
+
+int RTCRestObjectBase::toString(RTCString *a_pDst, uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) const RT_NOEXCEPT
+{
+ /*
+ * Just wrap the JSON serialization method.
+ */
+ RTCRestOutputToString Tmp(a_pDst, RT_BOOL(a_fFlags & kToString_Append));
+ serializeAsJson(Tmp);
+ return Tmp.finalize() ? VINF_SUCCESS : VERR_NO_MEMORY;
+}
+
+
+RTCString RTCRestObjectBase::toString() const
+{
+ RTCString strRet;
+ toString(&strRet, 0);
+ return strRet;
+}
+
+
+int RTCRestObjectBase::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo /*= NULL*/,
+ uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) RT_NOEXCEPT
+{
+ RT_NOREF(a_fFlags);
+
+ /*
+ * Just wrap the JSON serialization method.
+ */
+ RTJSONVAL hValue = NIL_RTJSONVAL;
+ int rc = RTJsonParseFromString(&hValue, a_rValue.c_str(), a_pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ RTCRestJsonPrimaryCursor PrimaryCursor(hValue, a_pszName, a_pErrInfo);
+ rc = deserializeFromJson(PrimaryCursor.m_Cursor);
+ }
+ return rc;
+}
+
+
+
+/*********************************************************************************************************************************
+* RTCRestBool implementation *
+*********************************************************************************************************************************/
+
+/** Factory method. */
+/*static*/ DECLCALLBACK(RTCRestObjectBase *) RTCRestBool::createInstance(void) RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestBool();
+}
+
+
+/**
+ * @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON
+ */
+/*static*/ DECLCALLBACK(int)
+RTCRestBool::deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT
+{
+ RTCRestObjectBase *pObj = createInstance();
+ *a_ppInstance = pObj;
+ if (pObj)
+ return pObj->deserializeFromJson(a_rCursor);
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_NO_MEMORY, "Out of memory");
+}
+
+
+/** Default constructor. */
+RTCRestBool::RTCRestBool() RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , m_fValue(false)
+{
+}
+
+
+/** Copy constructor. */
+RTCRestBool::RTCRestBool(RTCRestBool const &a_rThat) RT_NOEXCEPT
+ : RTCRestObjectBase(a_rThat)
+ , m_fValue(a_rThat.m_fValue)
+{
+}
+
+
+/** From value constructor. */
+RTCRestBool::RTCRestBool(bool fValue) RT_NOEXCEPT
+ : m_fValue(fValue)
+{
+}
+
+
+/** Destructor. */
+RTCRestBool::~RTCRestBool()
+{
+ /* nothing to do */
+}
+
+
+/** Copy assignment operator. */
+RTCRestBool &RTCRestBool::operator=(RTCRestBool const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ m_fValue = a_rThat.m_fValue;
+ return *this;
+}
+
+
+int RTCRestBool::assignCopy(RTCRestBool const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ m_fValue = a_rThat.m_fValue;
+ return VINF_SUCCESS;
+}
+
+
+void RTCRestBool::assignValue(bool a_fValue) RT_NOEXCEPT
+{
+ m_fValue = a_fValue;
+ m_fNullIndicator = false;
+}
+
+
+RTCRestObjectBase *RTCRestBool::baseClone() const RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestBool(*this);
+}
+
+
+int RTCRestBool::resetToDefault() RT_NOEXCEPT
+{
+ m_fValue = false;
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+}
+
+
+RTCRestOutputBase &RTCRestBool::serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
+{
+ a_rDst.printf(!m_fNullIndicator ? m_fValue ? "true" : "false" : "null");
+ return a_rDst;
+}
+
+
+int RTCRestBool::deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ m_fValue = false;
+ m_fNullIndicator = false;
+
+ RTJSONVALTYPE enmType = RTJsonValueGetType(a_rCursor.m_hValue);
+
+ if (enmType == RTJSONVALTYPE_TRUE)
+ {
+ m_fValue = true;
+ return VINF_SUCCESS;
+ }
+
+ if (enmType == RTJSONVALTYPE_FALSE)
+ return VINF_SUCCESS;
+
+ if (enmType == RTJSONVALTYPE_NULL)
+ {
+ m_fNullIndicator = true;
+ return VINF_SUCCESS;
+ }
+
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_REST_WRONG_JSON_TYPE_FOR_BOOL, "wrong JSON type %s for boolean",
+ RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
+}
+
+
+int RTCRestBool::toString(RTCString *a_pDst, uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) const RT_NOEXCEPT
+{
+ if (!(a_fFlags & kToString_Append))
+ {
+ if (!m_fNullIndicator)
+ {
+ if (m_fValue)
+ return a_pDst->assignNoThrow(RT_STR_TUPLE("true"));
+ return a_pDst->assignNoThrow(RT_STR_TUPLE("false"));
+ }
+ return a_pDst->assignNoThrow(RT_STR_TUPLE("null"));
+ }
+
+ if (!m_fNullIndicator)
+ {
+ if (m_fValue)
+ return a_pDst->appendNoThrow(RT_STR_TUPLE("true"));
+ return a_pDst->appendNoThrow(RT_STR_TUPLE("false"));
+ }
+ return a_pDst->appendNoThrow(RT_STR_TUPLE("null"));
+}
+
+
+int RTCRestBool::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo /*= NULL*/,
+ uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) RT_NOEXCEPT
+{
+ RT_NOREF(a_fFlags);
+
+ if (a_rValue.startsWithWord("true", RTCString::CaseInsensitive))
+ {
+ m_fValue = true;
+ m_fNullIndicator = false;
+ }
+ else if (a_rValue.startsWithWord("false", RTCString::CaseInsensitive))
+ {
+ m_fValue = false;
+ m_fNullIndicator = false;
+ }
+ else if (a_rValue.startsWithWord("null", RTCString::CaseInsensitive))
+ {
+ m_fValue = false;
+ m_fNullIndicator = true;
+ }
+ else
+ return RTErrInfoSetF(a_pErrInfo, VERR_REST_UNABLE_TO_PARSE_STRING_AS_BOOL,
+ "%s: unable to parse '%s' as bool", a_pszName, a_rValue.c_str());
+ return VINF_SUCCESS;
+}
+
+
+RTCRestObjectBase::kTypeClass RTCRestBool::typeClass() const RT_NOEXCEPT
+{
+ return kTypeClass_Bool;
+}
+
+
+const char *RTCRestBool::typeName() const RT_NOEXCEPT
+{
+ return "bool";
+}
+
+
+
+/*********************************************************************************************************************************
+* RTCRestInt64 implementation *
+*********************************************************************************************************************************/
+
+/** Factory method. */
+/*static*/ DECLCALLBACK(RTCRestObjectBase *) RTCRestInt64::createInstance(void) RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestInt64();
+}
+
+
+/**
+ * @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON
+ */
+/*static*/ DECLCALLBACK(int)
+RTCRestInt64::deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT
+{
+ RTCRestObjectBase *pObj = createInstance();
+ *a_ppInstance = pObj;
+ if (pObj)
+ return pObj->deserializeFromJson(a_rCursor);
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_NO_MEMORY, "Out of memory");
+}
+
+
+/** Default constructor. */
+RTCRestInt64::RTCRestInt64() RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , m_iValue(0)
+{
+}
+
+
+/** Copy constructor. */
+RTCRestInt64::RTCRestInt64(RTCRestInt64 const &a_rThat) RT_NOEXCEPT
+ : RTCRestObjectBase(a_rThat)
+ , m_iValue(a_rThat.m_iValue)
+{
+}
+
+
+/** From value constructor. */
+RTCRestInt64::RTCRestInt64(int64_t iValue) RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , m_iValue(iValue)
+{
+}
+
+
+/** Destructor. */
+RTCRestInt64::~RTCRestInt64()
+{
+ /* nothing to do */
+}
+
+
+/** Copy assignment operator. */
+RTCRestInt64 &RTCRestInt64::operator=(RTCRestInt64 const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ m_iValue = a_rThat.m_iValue;
+ return *this;
+}
+
+
+int RTCRestInt64::assignCopy(RTCRestInt64 const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ m_iValue = a_rThat.m_iValue;
+ return VINF_SUCCESS;
+}
+
+
+void RTCRestInt64::assignValue(int64_t a_iValue) RT_NOEXCEPT
+{
+ m_iValue = a_iValue;
+ m_fNullIndicator = false;
+}
+
+
+RTCRestObjectBase *RTCRestInt64::baseClone() const RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestInt64(*this);
+}
+
+
+int RTCRestInt64::resetToDefault() RT_NOEXCEPT
+{
+ m_iValue = 0;
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+}
+
+
+RTCRestOutputBase &RTCRestInt64::serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
+{
+ if (!m_fNullIndicator)
+ a_rDst.printf("%RI64", m_iValue);
+ else
+ a_rDst.nullValue();
+ return a_rDst;
+}
+
+
+int RTCRestInt64::deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ m_iValue = 0;
+ m_fNullIndicator = false;
+
+ RTJSONVALTYPE enmType = RTJsonValueGetType(a_rCursor.m_hValue);
+ if (enmType == RTJSONVALTYPE_INTEGER)
+ {
+ int rc = RTJsonValueQueryInteger(a_rCursor.m_hValue, &m_iValue);
+ if (RT_SUCCESS(rc))
+ return rc;
+ return a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonValueQueryInteger failed with %Rrc", rc);
+ }
+
+ if (enmType == RTJSONVALTYPE_NULL)
+ {
+ m_fNullIndicator = true;
+ return VINF_SUCCESS;
+ }
+
+ /* This is probably non-sense... */
+ if (enmType == RTJSONVALTYPE_TRUE)
+ m_iValue = 1;
+
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_REST_WRONG_JSON_TYPE_FOR_INTEGER, "wrong JSON type %s for 64-bit integer",
+ RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
+}
+
+
+int RTCRestInt64::toString(RTCString *a_pDst, uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) const RT_NOEXCEPT
+{
+ if (!(a_fFlags & kToString_Append))
+ {
+ if (!m_fNullIndicator)
+ return a_pDst->printfNoThrow("%RI64", m_iValue);
+ return a_pDst->assignNoThrow(RT_STR_TUPLE("null"));
+ }
+ if (!m_fNullIndicator)
+ return a_pDst->appendPrintfNoThrow("%RI64", m_iValue);
+ return a_pDst->appendNoThrow(RT_STR_TUPLE("null"));
+}
+
+
+int RTCRestInt64::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo /*= NULL*/,
+ uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) RT_NOEXCEPT
+{
+ RT_NOREF(a_fFlags);
+
+ m_iValue = 0;
+ m_fNullIndicator = false;
+
+/** @todo RTStrStripL and RTStrToInt64Full has a different idea what consitutes spaces... */
+ int rc = RTStrToInt64Full(RTStrStripL(a_rValue.c_str()), 0, &m_iValue);
+ if (rc == VINF_SUCCESS || rc == VERR_TRAILING_SPACES)
+ return VINF_SUCCESS;
+
+ if (a_rValue.startsWithWord("null", RTCString::CaseInsensitive))
+ {
+ m_iValue = 0;
+ m_fNullIndicator = true;
+ return VINF_SUCCESS;
+ }
+
+ return RTErrInfoSetF(a_pErrInfo, rc, "%s: error %Rrc parsing '%s' as int64_t", a_pszName, rc, a_rValue.c_str());
+}
+
+
+RTCRestObjectBase::kTypeClass RTCRestInt64::typeClass() const RT_NOEXCEPT
+{
+ return kTypeClass_Int64;
+}
+
+
+const char *RTCRestInt64::typeName() const RT_NOEXCEPT
+{
+ return "int64_t";
+}
+
+
+
+/*********************************************************************************************************************************
+* RTCRestInt32 implementation *
+*********************************************************************************************************************************/
+
+/** Factory method. */
+/*static*/ DECLCALLBACK(RTCRestObjectBase *) RTCRestInt32::createInstance(void) RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestInt32();
+}
+
+
+/**
+ * @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON
+ */
+/*static*/ DECLCALLBACK(int)
+RTCRestInt32::deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT
+{
+ RTCRestObjectBase *pObj = createInstance();
+ *a_ppInstance = pObj;
+ if (pObj)
+ return pObj->deserializeFromJson(a_rCursor);
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_NO_MEMORY, "Out of memory");
+}
+
+
+/** Default constructor. */
+RTCRestInt32::RTCRestInt32() RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , m_iValue(0)
+{
+}
+
+
+/** Copy constructor. */
+RTCRestInt32::RTCRestInt32(RTCRestInt32 const &a_rThat) RT_NOEXCEPT
+ : RTCRestObjectBase(a_rThat)
+ , m_iValue(a_rThat.m_iValue)
+{
+}
+
+
+/** From value constructor. */
+RTCRestInt32::RTCRestInt32(int32_t iValue) RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , m_iValue(iValue)
+{
+}
+
+
+/** Destructor. */
+RTCRestInt32::~RTCRestInt32() RT_NOEXCEPT
+{
+ /* nothing to do */
+}
+
+
+/** Copy assignment operator. */
+RTCRestInt32 &RTCRestInt32::operator=(RTCRestInt32 const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ m_iValue = a_rThat.m_iValue;
+ return *this;
+}
+
+
+int RTCRestInt32::assignCopy(RTCRestInt32 const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ m_iValue = a_rThat.m_iValue;
+ return VINF_SUCCESS;
+}
+
+
+RTCRestObjectBase *RTCRestInt32::baseClone() const RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestInt32(*this);
+}
+
+
+int RTCRestInt32::resetToDefault() RT_NOEXCEPT
+{
+ m_iValue = 0;
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+}
+
+
+void RTCRestInt32::assignValue(int32_t a_iValue) RT_NOEXCEPT
+{
+ m_iValue = a_iValue;
+ m_fNullIndicator = false;
+}
+
+
+RTCRestOutputBase &RTCRestInt32::serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
+{
+ if (!m_fNullIndicator)
+ a_rDst.printf("%RI32", m_iValue);
+ else
+ a_rDst.nullValue();
+ return a_rDst;
+}
+
+
+int RTCRestInt32::deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ m_iValue = 0;
+ m_fNullIndicator = false;
+
+ RTJSONVALTYPE enmType = RTJsonValueGetType(a_rCursor.m_hValue);
+ if (enmType == RTJSONVALTYPE_INTEGER)
+ {
+ int64_t iTmp = m_iValue;
+ int rc = RTJsonValueQueryInteger(a_rCursor.m_hValue, &iTmp);
+ if (RT_SUCCESS(rc))
+ {
+ m_iValue = (int32_t)iTmp;
+ if (m_iValue == iTmp)
+ return rc;
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_OUT_OF_RANGE, "value %RI64 does not fit in 32 bits", iTmp);
+ }
+ return a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonValueQueryInteger failed with %Rrc", rc);
+ }
+
+ if (enmType == RTJSONVALTYPE_NULL)
+ {
+ m_fNullIndicator = true;
+ return VINF_SUCCESS;
+ }
+
+ /* This is probably non-sense... */
+ if (enmType == RTJSONVALTYPE_TRUE)
+ m_iValue = 1;
+
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_REST_WRONG_JSON_TYPE_FOR_INTEGER, "wrong JSON type %s for 32-bit integer",
+ RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
+}
+
+
+int RTCRestInt32::toString(RTCString *a_pDst, uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) const RT_NOEXCEPT
+{
+ if (!(a_fFlags & kToString_Append))
+ {
+ if (!m_fNullIndicator)
+ return a_pDst->printfNoThrow("%RI32", m_iValue);
+ return a_pDst->assignNoThrow(RT_STR_TUPLE("null"));
+ }
+ if (!m_fNullIndicator)
+ return a_pDst->appendPrintfNoThrow("%RI32", m_iValue);
+ return a_pDst->appendNoThrow(RT_STR_TUPLE("null"));
+}
+
+
+int RTCRestInt32::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo /*= NULL*/,
+ uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) RT_NOEXCEPT
+{
+ RT_NOREF(a_fFlags);
+
+ m_iValue = 0;
+ m_fNullIndicator = false;
+
+/** @todo RTStrStripL and RTStrToInt32Full has a different idea what consitutes spaces... */
+ int rc = RTStrToInt32Full(RTStrStripL(a_rValue.c_str()), 0, &m_iValue);
+ if (rc == VINF_SUCCESS || rc == VERR_TRAILING_SPACES)
+ return VINF_SUCCESS;
+
+ if (a_rValue.startsWithWord("null", RTCString::CaseInsensitive))
+ {
+ m_iValue = 0;
+ m_fNullIndicator = true;
+ return VINF_SUCCESS;
+ }
+
+ return RTErrInfoSetF(a_pErrInfo, rc, "%s: error %Rrc parsing '%s' as int32_t", a_pszName, rc, a_rValue.c_str());
+}
+
+
+RTCRestObjectBase::kTypeClass RTCRestInt32::typeClass() const RT_NOEXCEPT
+{
+ return kTypeClass_Int32;
+}
+
+
+const char *RTCRestInt32::typeName() const RT_NOEXCEPT
+{
+ return "int32_t";
+}
+
+
+
+/*********************************************************************************************************************************
+* RTCRestInt16 implementation *
+*********************************************************************************************************************************/
+
+/** Factory method. */
+/*static*/ DECLCALLBACK(RTCRestObjectBase *) RTCRestInt16::createInstance(void) RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestInt16();
+}
+
+
+/**
+ * @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON
+ */
+/*static*/ DECLCALLBACK(int)
+RTCRestInt16::deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT
+{
+ RTCRestObjectBase *pObj = createInstance();
+ *a_ppInstance = pObj;
+ if (pObj)
+ return pObj->deserializeFromJson(a_rCursor);
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_NO_MEMORY, "Out of memory");
+}
+
+
+/** Default constructor. */
+RTCRestInt16::RTCRestInt16() RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , m_iValue(0)
+{
+}
+
+
+/** Copy constructor. */
+RTCRestInt16::RTCRestInt16(RTCRestInt16 const &a_rThat) RT_NOEXCEPT
+ : RTCRestObjectBase(a_rThat)
+ , m_iValue(a_rThat.m_iValue)
+{
+}
+
+
+/** From value constructor. */
+RTCRestInt16::RTCRestInt16(int16_t iValue) RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , m_iValue(iValue)
+{
+}
+
+
+/** Destructor. */
+RTCRestInt16::~RTCRestInt16()
+{
+ /* nothing to do */
+}
+
+
+/** Copy assignment operator. */
+RTCRestInt16 &RTCRestInt16::operator=(RTCRestInt16 const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ m_iValue = a_rThat.m_iValue;
+ return *this;
+}
+
+
+int RTCRestInt16::assignCopy(RTCRestInt16 const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ m_iValue = a_rThat.m_iValue;
+ return VINF_SUCCESS;
+}
+
+
+void RTCRestInt16::assignValue(int16_t a_iValue) RT_NOEXCEPT
+{
+ m_iValue = a_iValue;
+ m_fNullIndicator = false;
+}
+
+
+RTCRestObjectBase *RTCRestInt16::baseClone() const RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestInt16(*this);
+}
+
+
+int RTCRestInt16::resetToDefault() RT_NOEXCEPT
+{
+ m_iValue = 0;
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+}
+
+
+RTCRestOutputBase &RTCRestInt16::serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
+{
+ if (!m_fNullIndicator)
+ a_rDst.printf("%RI16", m_iValue);
+ else
+ a_rDst.nullValue();
+ return a_rDst;
+}
+
+
+int RTCRestInt16::deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ m_iValue = 0;
+ m_fNullIndicator = false;
+
+ RTJSONVALTYPE enmType = RTJsonValueGetType(a_rCursor.m_hValue);
+ if (enmType == RTJSONVALTYPE_INTEGER)
+ {
+ int64_t iTmp = m_iValue;
+ int rc = RTJsonValueQueryInteger(a_rCursor.m_hValue, &iTmp);
+ if (RT_SUCCESS(rc))
+ {
+ m_iValue = (int16_t)iTmp;
+ if (m_iValue == iTmp)
+ return rc;
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_OUT_OF_RANGE, "value %RI64 does not fit in 16 bits", iTmp);
+ }
+ return a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonValueQueryInteger failed with %Rrc", rc);
+ }
+
+ if (enmType == RTJSONVALTYPE_NULL)
+ {
+ m_fNullIndicator = true;
+ return VINF_SUCCESS;
+ }
+
+ /* This is probably non-sense... */
+ if (enmType == RTJSONVALTYPE_TRUE)
+ m_iValue = 1;
+
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_REST_WRONG_JSON_TYPE_FOR_INTEGER, "wrong JSON type %s for 16-bit integer",
+ RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
+}
+
+
+int RTCRestInt16::toString(RTCString *a_pDst, uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) const RT_NOEXCEPT
+{
+ if (!(a_fFlags & kToString_Append))
+ {
+ if (!m_fNullIndicator)
+ return a_pDst->printfNoThrow("%RI16", m_iValue);
+ return a_pDst->assignNoThrow(RT_STR_TUPLE("null"));
+ }
+ if (!m_fNullIndicator)
+ return a_pDst->appendPrintfNoThrow("%RI16", m_iValue);
+ return a_pDst->appendNoThrow(RT_STR_TUPLE("null"));
+}
+
+
+int RTCRestInt16::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo /*= NULL*/,
+ uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) RT_NOEXCEPT
+{
+ RT_NOREF(a_fFlags);
+
+ m_iValue = 0;
+ m_fNullIndicator = false;
+
+/** @todo RTStrStripL and RTStrToInt16Full has a different idea what consitutes spaces... */
+ int rc = RTStrToInt16Full(RTStrStripL(a_rValue.c_str()), 0, &m_iValue);
+ if (rc == VINF_SUCCESS || rc == VERR_TRAILING_SPACES)
+ return VINF_SUCCESS;
+
+ if (a_rValue.startsWithWord("null", RTCString::CaseInsensitive))
+ {
+ m_iValue = 0;
+ m_fNullIndicator = true;
+ return VINF_SUCCESS;
+ }
+
+ return RTErrInfoSetF(a_pErrInfo, rc, "%s: error %Rrc parsing '%s' as int16_t", a_pszName, rc, a_rValue.c_str());
+}
+
+
+RTCRestObjectBase::kTypeClass RTCRestInt16::typeClass() const RT_NOEXCEPT
+{
+ return kTypeClass_Int16;
+}
+
+
+const char *RTCRestInt16::typeName() const RT_NOEXCEPT
+{
+ return "int16_t";
+}
+
+
+
+/*********************************************************************************************************************************
+* RTCRestDouble implementation *
+*********************************************************************************************************************************/
+
+/** Factory method. */
+/*static*/ DECLCALLBACK(RTCRestObjectBase *) RTCRestDouble::createInstance(void) RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestDouble();
+}
+
+
+/**
+ * @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON
+ */
+/*static*/ DECLCALLBACK(int)
+RTCRestDouble::deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT
+{
+ RTCRestObjectBase *pObj = createInstance();
+ *a_ppInstance = pObj;
+ if (pObj)
+ return pObj->deserializeFromJson(a_rCursor);
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_NO_MEMORY, "Out of memory");
+}
+
+
+/** Default constructor. */
+RTCRestDouble::RTCRestDouble() RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , m_rdValue(0.0)
+{
+}
+
+
+/** Copy constructor. */
+RTCRestDouble::RTCRestDouble(RTCRestDouble const &a_rThat) RT_NOEXCEPT
+ : RTCRestObjectBase(a_rThat)
+ , m_rdValue(a_rThat.m_rdValue)
+{
+}
+
+
+/** From value constructor. */
+RTCRestDouble::RTCRestDouble(double rdValue) RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , m_rdValue(rdValue)
+{
+}
+
+
+/** Destructor. */
+RTCRestDouble::~RTCRestDouble()
+{
+ /* nothing to do */
+}
+
+
+/** Copy assignment operator. */
+RTCRestDouble &RTCRestDouble::operator=(RTCRestDouble const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ m_rdValue = a_rThat.m_rdValue;
+ return *this;
+}
+
+
+int RTCRestDouble::assignCopy(RTCRestDouble const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ m_rdValue = a_rThat.m_rdValue;
+ return VINF_SUCCESS;
+}
+
+
+void RTCRestDouble::assignValue(double a_rdValue) RT_NOEXCEPT
+{
+ m_rdValue = a_rdValue;
+ m_fNullIndicator = false;
+}
+
+
+RTCRestObjectBase *RTCRestDouble::baseClone() const RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestDouble(*this);
+}
+
+
+int RTCRestDouble::resetToDefault() RT_NOEXCEPT
+{
+ m_rdValue = 0.0;
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+}
+
+
+RTCRestOutputBase &RTCRestDouble::serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
+{
+ if (!m_fNullIndicator)
+ {
+
+ /* Just a simple approximation here. */
+ /** @todo Not 100% sure printf %g produces the right result for JSON floating point, but it'll have to do for now... */
+ char szValue[128];
+#ifdef _MSC_VER
+ _snprintf(szValue, sizeof(szValue), "%.18g", m_rdValue);
+#else
+ snprintf(szValue, sizeof(szValue), "%.18g", m_rdValue);
+#endif
+ size_t cchValue = strlen(szValue);
+ while (cchValue > 0 && szValue[cchValue - 1] == '0')
+ cchValue--;
+ szValue[cchValue] = '\0';
+
+ a_rDst.printf("%s", szValue);
+ }
+ else
+ a_rDst.nullValue();
+ return a_rDst;
+}
+
+
+int RTCRestDouble::deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ m_rdValue = 0.0;
+ m_fNullIndicator = false;
+
+ RTJSONVALTYPE enmType = RTJsonValueGetType(a_rCursor.m_hValue);
+ if (enmType == RTJSONVALTYPE_NUMBER)
+ {
+ int rc = RTJsonValueQueryNumber(a_rCursor.m_hValue, &m_rdValue);
+ if (RT_SUCCESS(rc))
+ return rc;
+ return a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonValueQueryNumber failed with %Rrc", rc);
+ }
+
+ if (enmType == RTJSONVALTYPE_INTEGER)
+ {
+ int64_t iTmp = 0;
+ int rc = RTJsonValueQueryInteger(a_rCursor.m_hValue, &iTmp);
+ if (RT_SUCCESS(rc))
+ {
+ m_rdValue = iTmp;
+ if ((int64_t)m_rdValue == iTmp)
+ return rc;
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_OUT_OF_RANGE, "value %RI64 does not fit in a double", iTmp);
+ }
+ return a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonValueQueryInteger failed with %Rrc", rc);
+ }
+
+ if (enmType == RTJSONVALTYPE_NULL)
+ {
+ m_fNullIndicator = true;
+ return VINF_SUCCESS;
+ }
+
+ /* This is probably non-sense... */
+ if (enmType == RTJSONVALTYPE_TRUE)
+ m_rdValue = 1.0;
+
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_REST_WRONG_JSON_TYPE_FOR_DOUBLE, "wrong JSON type %s for a double",
+ RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
+}
+
+
+int RTCRestDouble::toString(RTCString *a_pDst, uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) const RT_NOEXCEPT
+{
+ if (!m_fNullIndicator)
+ {
+ /* Just a simple approximation here. */
+ /** @todo Not 100% sure printf %g produces the right result for JSON floating point, but it'll have to do for now... */
+ char szValue[128];
+#ifdef _MSC_VER
+ _snprintf(szValue, sizeof(szValue), "%.18g", m_rdValue);
+#else
+ snprintf(szValue, sizeof(szValue), "%.18g", m_rdValue);
+#endif
+ size_t cchValue = strlen(szValue);
+ while (cchValue > 0 && szValue[cchValue - 1] == '0')
+ cchValue--;
+ szValue[cchValue] = '\0';
+
+ if (!(a_fFlags & kToString_Append))
+ return a_pDst->assignNoThrow(szValue, cchValue);
+ return a_pDst->appendNoThrow(szValue, cchValue);
+ }
+
+ if (!(a_fFlags & kToString_Append))
+ return a_pDst->assignNoThrow(RT_STR_TUPLE("null"));
+ return a_pDst->appendNoThrow(RT_STR_TUPLE("null"));
+}
+
+
+int RTCRestDouble::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo /*= NULL*/,
+ uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) RT_NOEXCEPT
+{
+ RT_NOREF(a_fFlags);
+
+ if (a_rValue.startsWithWord("null", RTCString::CaseInsensitive))
+ {
+ m_rdValue = 0.0;
+ m_fNullIndicator = true;
+ return VINF_SUCCESS;
+ }
+
+ m_fNullIndicator = false;
+
+ const char *pszValue = RTStrStripL(a_rValue.c_str());
+ errno = 0;
+ char *pszNext = NULL;
+ m_rdValue = strtod(pszValue, &pszNext);
+ if (errno == 0 && pszNext != pszValue)
+ {
+ if (!pszNext || *pszNext == '\0')
+ return VINF_SUCCESS;
+
+ while (RT_C_IS_SPACE(*pszNext))
+ pszNext++;
+ if (*pszNext == '\0')
+ return VINF_SUCCESS;
+
+ return RTErrInfoSetF(a_pErrInfo, VERR_TRAILING_CHARS, "%s: error VERR_TRAILING_CHARS parsing '%s' as double",
+ a_pszName, a_rValue.c_str());
+ }
+
+ if (!RT_C_IS_DIGIT(*pszValue) && *pszValue != '.')
+ return RTErrInfoSetF(a_pErrInfo, VERR_NO_DIGITS, "%s: error VERR_NO_DIGITS parsing '%s' as double",
+ a_pszName, a_rValue.c_str());
+ int rc = RTErrConvertFromErrno(errno);
+ return RTErrInfoSetF(a_pErrInfo, rc, "%s: error %Rrc parsing '%s' as double", a_pszName, rc, a_rValue.c_str());
+}
+
+
+RTCRestObjectBase::kTypeClass RTCRestDouble::typeClass() const RT_NOEXCEPT
+{
+ return kTypeClass_Double;
+}
+
+
+const char *RTCRestDouble::typeName() const RT_NOEXCEPT
+{
+ return "double";
+}
+
+
+
+/*********************************************************************************************************************************
+* RTCRestString implementation *
+*********************************************************************************************************************************/
+
+/** Factory method. */
+/*static*/ DECLCALLBACK(RTCRestObjectBase *) RTCRestString::createInstance(void) RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestString();
+}
+
+
+/**
+ * @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON
+ */
+/*static*/ DECLCALLBACK(int)
+RTCRestString::deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT
+{
+ RTCRestObjectBase *pObj = createInstance();
+ *a_ppInstance = pObj;
+ if (pObj)
+ return pObj->deserializeFromJson(a_rCursor);
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_NO_MEMORY, "Out of memory");
+}
+
+
+/** Default constructor. */
+RTCRestString::RTCRestString() RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , RTCString()
+{
+}
+
+
+/** Copy constructor. */
+RTCRestString::RTCRestString(RTCRestString const &a_rThat)
+ : RTCRestObjectBase(a_rThat)
+ , RTCString(a_rThat)
+{
+}
+
+
+/** From value constructor. */
+RTCRestString::RTCRestString(RTCString const &a_rThat)
+ : RTCString(a_rThat)
+{
+}
+
+
+/** From value constructor. */
+RTCRestString::RTCRestString(const char *a_pszSrc)
+ : RTCRestObjectBase()
+ , RTCString(a_pszSrc)
+{
+}
+
+
+/** Destructor. */
+RTCRestString::~RTCRestString()
+{
+ /* nothing to do */
+}
+
+
+int RTCRestString::assignCopy(RTCRestString const &a_rThat) RT_NOEXCEPT
+{
+ int rc = assignNoThrow(a_rThat);
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ return rc;
+}
+
+
+int RTCRestString::assignCopy(RTCString const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = false;
+ return assignNoThrow(a_rThat);
+}
+
+
+int RTCRestString::assignCopy(const char *a_pszThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = false;
+ return assignNoThrow(a_pszThat);
+}
+
+
+int RTCRestString::setNull() RT_NOEXCEPT
+{
+ RTCString::setNull();
+ m_fNullIndicator = true;
+ return VINF_SUCCESS;
+}
+
+
+RTCRestObjectBase *RTCRestString::baseClone() const RT_NOEXCEPT
+{
+ RTCRestString *pClone = new (std::nothrow) RTCRestString();
+ if (pClone)
+ {
+ int rc = pClone->assignCopy(*this);
+ if (RT_SUCCESS(rc))
+ return pClone;
+ delete pClone;
+ }
+ return NULL;
+}
+
+
+int RTCRestString::resetToDefault() RT_NOEXCEPT
+{
+ RTCString::setNull();
+ m_fNullIndicator = false;
+ return VINF_SUCCESS;
+}
+
+
+RTCRestOutputBase &RTCRestString::serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
+{
+ if (!m_fNullIndicator)
+ a_rDst.printf("%RMjs", m_psz ? m_psz : "");
+ else
+ a_rDst.nullValue();
+ return a_rDst;
+}
+
+
+int RTCRestString::deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ m_fNullIndicator = false;
+
+ RTJSONVALTYPE enmType = RTJsonValueGetType(a_rCursor.m_hValue);
+ if (enmType == RTJSONVALTYPE_STRING)
+ {
+ const char *pszValue = RTJsonValueGetString(a_rCursor.m_hValue);
+ const size_t cchValue = strlen(pszValue);
+ int rc = assignNoThrow(pszValue, cchValue);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ return a_rCursor.m_pPrimary->addError(a_rCursor, rc, "no memory for %zu char long string", cchValue);
+ }
+
+ RTCString::setNull();
+
+ if (enmType == RTJSONVALTYPE_NULL)
+ {
+ m_fNullIndicator = true;
+ return VINF_SUCCESS;
+ }
+
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_REST_WRONG_JSON_TYPE_FOR_STRING, "wrong JSON type %s for string",
+ RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
+}
+
+
+int RTCRestString::toString(RTCString *a_pDst, uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) const RT_NOEXCEPT
+{
+ /* Note! m_fNullIndicator == true: empty string. */
+ if (!(a_fFlags & kToString_Append))
+ return a_pDst->assignNoThrow(*this);
+ return a_pDst->appendNoThrow(*this);
+}
+
+
+int RTCRestString::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo /*= NULL*/,
+ uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) RT_NOEXCEPT
+{
+ RT_NOREF(a_fFlags); RT_NOREF(a_pszName); RT_NOREF(a_pErrInfo);
+
+ /* Note! Unable to set m_fNullIndicator = true here. */
+ m_fNullIndicator = false;
+ return assignNoThrow(a_rValue);
+}
+
+
+RTCRestObjectBase::kTypeClass RTCRestString::typeClass() const RT_NOEXCEPT
+{
+ return kTypeClass_String;
+}
+
+
+const char *RTCRestString::typeName() const RT_NOEXCEPT
+{
+ return "RTCString";
+}
+
+
+int RTCRestString::assignNoThrow(const RTCString &a_rSrc) RT_NOEXCEPT
+{
+ m_fNullIndicator = false;
+ return RTCString::assignNoThrow(a_rSrc);
+}
+
+
+int RTCRestString::assignNoThrow(const char *a_pszSrc) RT_NOEXCEPT
+{
+ m_fNullIndicator = false;
+ return RTCString::assignNoThrow(a_pszSrc);
+}
+
+
+int RTCRestString::assignNoThrow(const RTCString &a_rSrc, size_t a_offSrc, size_t a_cchSrc /*= npos*/) RT_NOEXCEPT
+{
+ m_fNullIndicator = false;
+ return RTCString::assignNoThrow(a_rSrc, a_offSrc, a_cchSrc);
+}
+
+
+int RTCRestString::assignNoThrow(const char *a_pszSrc, size_t a_cchSrc) RT_NOEXCEPT
+{
+ m_fNullIndicator = false;
+ return RTCString::assignNoThrow(a_pszSrc, a_cchSrc);
+}
+
+
+int RTCRestString::assignNoThrow(size_t a_cTimes, char a_ch) RT_NOEXCEPT
+{
+ m_fNullIndicator = false;
+ return RTCString::assignNoThrow(a_cTimes, a_ch);
+}
+
+
+int RTCRestString::printfNoThrow(const char *pszFormat, ...) RT_NOEXCEPT
+{
+ m_fNullIndicator = false;
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTCString::printfVNoThrow(pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+
+int RTCRestString::printfVNoThrow(const char *pszFormat, va_list va) RT_NOEXCEPT
+{
+ m_fNullIndicator = false;
+ return RTCString::printfVNoThrow(pszFormat, va);
+}
+
+
+RTCRestString &RTCRestString::operator=(const char *a_pcsz)
+{
+ m_fNullIndicator = false;
+ RTCString::operator=(a_pcsz);
+ return *this;
+}
+
+
+RTCRestString &RTCRestString::operator=(const RTCString &a_rThat)
+{
+ m_fNullIndicator = false;
+ RTCString::operator=(a_rThat);
+ return *this;
+}
+
+
+RTCRestString &RTCRestString::operator=(const RTCRestString &a_rThat)
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ RTCString::operator=(a_rThat);
+ return *this;
+}
+
+
+RTCRestString &RTCRestString::assign(const RTCString &a_rSrc)
+{
+ m_fNullIndicator = false;
+ RTCString::assign(a_rSrc);
+ return *this;
+}
+
+
+RTCRestString &RTCRestString::assign(const char *a_pszSrc)
+{
+ m_fNullIndicator = false;
+ RTCString::assign(a_pszSrc);
+ return *this;
+}
+
+
+RTCRestString &RTCRestString::assign(const RTCString &a_rSrc, size_t a_offSrc, size_t a_cchSrc /*= npos*/)
+{
+ m_fNullIndicator = false;
+ RTCString::assign(a_rSrc, a_offSrc, a_cchSrc);
+ return *this;
+}
+
+
+RTCRestString &RTCRestString::assign(const char *a_pszSrc, size_t a_cchSrc)
+{
+ m_fNullIndicator = false;
+ RTCString::assign(a_pszSrc, a_cchSrc);
+ return *this;
+}
+
+
+RTCRestString &RTCRestString::assign(size_t a_cTimes, char a_ch)
+{
+ m_fNullIndicator = false;
+ RTCString::assign(a_cTimes, a_ch);
+ return *this;
+}
+
+
+RTCRestString &RTCRestString::printf(const char *pszFormat, ...)
+{
+ m_fNullIndicator = false;
+ va_list va;
+ va_start(va, pszFormat);
+ RTCString::printfV(pszFormat, va);
+ va_end(va);
+ return *this;
+}
+
+
+RTCRestString &RTCRestString::printfV(const char *pszFormat, va_list va)
+{
+ m_fNullIndicator = false;
+ RTCString::printfV(pszFormat, va);
+ return *this;
+}
+
+
+
+/*********************************************************************************************************************************
+* RTCRestDate implementation *
+*********************************************************************************************************************************/
+/*static*/ DECLCALLBACK(RTCRestObjectBase *) RTCRestDate::createInstance(void) RT_NOEXCEPT
+{
+ return new (std::nothrow) RTCRestDate();
+}
+
+
+/**
+ * @copydoc RTCRestObjectBase::FNDESERIALIZEINSTANCEFROMJSON
+ */
+/*static*/ DECLCALLBACK(int)
+RTCRestDate::deserializeInstanceFromJson(RTCRestJsonCursor const &a_rCursor, RTCRestObjectBase **a_ppInstance) RT_NOEXCEPT
+{
+ RTCRestObjectBase *pObj = createInstance();
+ *a_ppInstance = pObj;
+ if (pObj)
+ return pObj->deserializeFromJson(a_rCursor);
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_NO_MEMORY, "Out of memory");
+}
+
+
+RTCRestDate::RTCRestDate() RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , m_fTimeSpecOkay(false)
+ , m_enmFormat(kFormat_Invalid)
+ , m_strFormatted()
+{
+ RTTimeSpecSetNano(&m_TimeSpec, 0);
+ RT_ZERO(m_Exploded);
+
+ /* Since we need to know the format, all date-time values default to 'null'. */
+ m_fNullIndicator = true;
+}
+
+
+RTCRestDate::RTCRestDate(RTCRestDate const &a_rThat)
+ : RTCRestObjectBase(a_rThat)
+ , m_fTimeSpecOkay(a_rThat.m_fTimeSpecOkay)
+ , m_enmFormat(a_rThat.m_enmFormat)
+ , m_strFormatted(a_rThat.m_strFormatted)
+{
+ m_TimeSpec = a_rThat.m_TimeSpec;
+ m_Exploded = a_rThat.m_Exploded;
+}
+
+
+RTCRestDate::~RTCRestDate()
+{
+ /* nothing to do */
+}
+
+
+RTCRestDate &RTCRestDate::operator=(RTCRestDate const &a_rThat)
+{
+ RTCRestObjectBase::operator=(a_rThat);
+ m_TimeSpec = a_rThat.m_TimeSpec;
+ m_Exploded = a_rThat.m_Exploded;
+ m_fTimeSpecOkay = a_rThat.m_fTimeSpecOkay;
+ m_enmFormat = a_rThat.m_enmFormat;
+ m_strFormatted = a_rThat.m_strFormatted;
+ return *this;
+}
+
+
+int RTCRestDate::assignCopy(RTCRestDate const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ m_TimeSpec = a_rThat.m_TimeSpec;
+ m_Exploded = a_rThat.m_Exploded;
+ m_fTimeSpecOkay = a_rThat.m_fTimeSpecOkay;
+ m_enmFormat = a_rThat.m_enmFormat;
+ return m_strFormatted.assignNoThrow(a_rThat.m_strFormatted);
+}
+
+
+RTCRestObjectBase *RTCRestDate::baseClone() const RT_NOEXCEPT
+{
+ RTCRestDate *pClone = new (std::nothrow) RTCRestDate();
+ if (pClone)
+ {
+ int rc = pClone->assignCopy(*this);
+ if (RT_SUCCESS(rc))
+ return pClone;
+ delete pClone;
+ }
+ return NULL;
+}
+
+
+int RTCRestDate::resetToDefault() RT_NOEXCEPT
+{
+ m_fNullIndicator = true;
+ RTTimeSpecSetNano(&m_TimeSpec, 0);
+ RT_ZERO(m_Exploded);
+ m_fTimeSpecOkay = false;
+ m_strFormatted.setNull();
+ /*m_enmFormat - leave as hint. */
+ return VINF_SUCCESS;
+}
+
+
+RTCRestOutputBase &RTCRestDate::serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
+{
+ if (m_fNullIndicator)
+ a_rDst.nullValue();
+ else
+ a_rDst.printf("%RMjs", m_strFormatted.c_str());
+ return a_rDst;
+}
+
+
+int RTCRestDate::deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ setNull();
+
+ RTJSONVALTYPE enmType = RTJsonValueGetType(a_rCursor.m_hValue);
+ if (enmType == RTJSONVALTYPE_STRING)
+ {
+ int rc = m_strFormatted.assignNoThrow(RTJsonValueGetString(a_rCursor.m_hValue));
+ AssertRCReturn(rc, rc);
+
+ m_fNullIndicator = false;
+ rc = decodeFormattedString(m_enmFormat);
+ if (RT_SUCCESS(rc))
+ return rc;
+ if (m_enmFormat != kFormat_Invalid)
+ {
+ rc = decodeFormattedString();
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VWRN_REST_UNABLE_TO_DECODE_DATE,
+ "Unable to decode date value: %s", m_strFormatted.c_str());
+ }
+
+ if (enmType == RTJSONVALTYPE_NULL)
+ return VINF_SUCCESS;
+
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_REST_WRONG_JSON_TYPE_FOR_DATE, "wrong JSON type for date: %s",
+ RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
+}
+
+
+int RTCRestDate::toString(RTCString *a_pDst, uint32_t a_fFlags /*= 0*/) const RT_NOEXCEPT
+{
+ if (m_fNullIndicator)
+ {
+ if (a_fFlags & kToString_Append)
+ return a_pDst->appendNoThrow(RT_STR_TUPLE("null"));
+ return a_pDst->assignNoThrow(RT_STR_TUPLE("null"));
+ }
+ if (a_fFlags & kToString_Append)
+ return a_pDst->appendNoThrow(m_strFormatted);
+ return a_pDst->assignNoThrow(m_strFormatted);
+}
+
+
+int RTCRestDate::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo /*= NULL*/,
+ uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) RT_NOEXCEPT
+{
+ setNull();
+ if (a_rValue.startsWithWord("null", RTCString::CaseInsensitive))
+ return VINF_SUCCESS;
+
+ int rc = m_strFormatted.assignNoThrow(a_rValue);
+ AssertRCReturn(rc, rc);
+
+ m_fNullIndicator = false;
+ rc = decodeFormattedString(m_enmFormat);
+ if (RT_SUCCESS(rc))
+ return rc;
+ if (m_enmFormat != kFormat_Invalid)
+ {
+ rc = decodeFormattedString();
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+ RT_NOREF(a_fFlags);
+ return RTErrInfoSetF(a_pErrInfo, VERR_REST_UNABLE_TO_DECODE_DATE,
+ "Unable to decode date value (%s): %s", a_pszName, m_strFormatted.c_str());
+}
+
+
+RTCRestObjectBase::kTypeClass RTCRestDate::typeClass(void) const RT_NOEXCEPT
+{
+ return kTypeClass_Date;
+}
+
+
+const char *RTCRestDate::typeName(void) const RT_NOEXCEPT
+{
+ return "RTCRestDate";
+}
+
+
+int RTCRestDate::assignValue(PCRTTIMESPEC a_pTimeSpec, kFormat a_enmFormat) RT_NOEXCEPT
+{
+ AssertPtrReturn(a_pTimeSpec, VERR_INVALID_PARAMETER);
+ AssertReturn(a_enmFormat > kFormat_Invalid && a_enmFormat < kFormat_End, VERR_INVALID_PARAMETER);
+
+ m_TimeSpec = *a_pTimeSpec;
+ return explodeAndFormat(a_enmFormat);
+}
+
+
+int RTCRestDate::assignValueRfc2822(PCRTTIMESPEC a_pTimeSpec) RT_NOEXCEPT
+{
+ AssertPtrReturn(a_pTimeSpec, VERR_INVALID_PARAMETER);
+ m_TimeSpec = *a_pTimeSpec;
+ return explodeAndFormat(kFormat_Rfc2822);
+}
+
+
+int RTCRestDate::assignValueRfc7131(PCRTTIMESPEC a_pTimeSpec) RT_NOEXCEPT
+{
+ AssertPtrReturn(a_pTimeSpec, VERR_INVALID_PARAMETER);
+ m_TimeSpec = *a_pTimeSpec;
+ return explodeAndFormat(kFormat_Rfc7131);
+}
+
+
+int RTCRestDate::assignValueRfc3339(PCRTTIMESPEC a_pTimeSpec) RT_NOEXCEPT
+{
+ AssertPtrReturn(a_pTimeSpec, VERR_INVALID_PARAMETER);
+ m_TimeSpec = *a_pTimeSpec;
+ return explodeAndFormat(kFormat_Rfc3339);
+}
+
+
+int RTCRestDate::assignNow(kFormat a_enmFormat) RT_NOEXCEPT
+{
+ RTTIMESPEC Now;
+ return assignValue(RTTimeNow(&Now), a_enmFormat);
+}
+
+
+int RTCRestDate::assignNowRfc2822() RT_NOEXCEPT
+{
+ RTTIMESPEC Now;
+ return assignValueRfc2822(RTTimeNow(&Now));
+}
+
+
+int RTCRestDate::assignNowRfc7131() RT_NOEXCEPT
+{
+ RTTIMESPEC Now;
+ return assignValueRfc7131(RTTimeNow(&Now));
+}
+
+
+int RTCRestDate::assignNowRfc3339() RT_NOEXCEPT
+{
+ RTTIMESPEC Now;
+ return assignValueRfc3339(RTTimeNow(&Now));
+}
+
+
+int RTCRestDate::setFormat(kFormat a_enmFormat) RT_NOEXCEPT
+{
+ /*
+ * If this is a null object, just set the format as a hint for upcoming deserialization.
+ */
+ if (m_fNullIndicator)
+ {
+ AssertReturn(a_enmFormat >= kFormat_Invalid && a_enmFormat < kFormat_End, VERR_INVALID_PARAMETER);
+ m_enmFormat = a_enmFormat;
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * If the tiem spec is okay, just reformat the string value accordingly.
+ */
+ if (m_fTimeSpecOkay)
+ {
+ AssertReturn(a_enmFormat > kFormat_Invalid && a_enmFormat < kFormat_End, VERR_INVALID_PARAMETER);
+ if (a_enmFormat == m_enmFormat)
+ return VINF_SUCCESS;
+ return format(a_enmFormat);
+ }
+
+ /*
+ * Try decode
+ */
+ AssertReturn(a_enmFormat > kFormat_Invalid && a_enmFormat < kFormat_End, VERR_INVALID_PARAMETER);
+ return decodeFormattedString(a_enmFormat);
+}
+
+
+int RTCRestDate::explodeAndFormat(kFormat a_enmFormat) RT_NOEXCEPT
+{
+ RTTimeExplode(&m_Exploded, &m_TimeSpec);
+ return format(a_enmFormat);
+}
+
+
+/**
+ * Formats the m_Exploded value.
+ *
+ * Sets m_strFormatted, m_fTimeSpecOkay, and m_enmFormat, clears m_fNullIndicator.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_STR_MEMORY.
+ * @param a_enmFormat The format to use.
+ */
+int RTCRestDate::format(kFormat a_enmFormat) RT_NOEXCEPT
+{
+ m_fNullIndicator = false;
+ m_fTimeSpecOkay = true;
+ m_enmFormat = a_enmFormat;
+ int rc;
+ switch (a_enmFormat)
+ {
+ case kFormat_Rfc2822:
+ case kFormat_Rfc7131:
+ rc = m_strFormatted.reserveNoThrow(RTTIME_RFC2822_LEN);
+ AssertRCReturn(rc, rc);
+ RTTimeToRfc2822(&m_Exploded, m_strFormatted.mutableRaw(), m_strFormatted.capacity(),
+ a_enmFormat == kFormat_Rfc7131 ? RTTIME_RFC2822_F_GMT : 0);
+ m_strFormatted.jolt();
+ return VINF_SUCCESS;
+
+ case kFormat_Rfc3339:
+ case kFormat_Rfc3339_Fraction_2:
+ case kFormat_Rfc3339_Fraction_3:
+ case kFormat_Rfc3339_Fraction_6:
+ case kFormat_Rfc3339_Fraction_9:
+ rc = m_strFormatted.reserveNoThrow(RTTIME_STR_LEN);
+ AssertRCReturn(rc, rc);
+ RTTimeToStringEx(&m_Exploded, m_strFormatted.mutableRaw(), m_strFormatted.capacity(),
+ a_enmFormat == kFormat_Rfc3339 ? 0
+ : a_enmFormat == kFormat_Rfc3339_Fraction_2 ? 2
+ : a_enmFormat == kFormat_Rfc3339_Fraction_3 ? 3
+ : a_enmFormat == kFormat_Rfc3339_Fraction_6 ? 6 : 9);
+ m_strFormatted.jolt();
+ return VINF_SUCCESS;
+
+ /* no default */
+ case kFormat_Invalid:
+ case kFormat_End:
+ break;
+ }
+ AssertFailedReturn(VERR_REST_INTERNAL_ERROR_7);
+}
+
+
+/**
+ * Internal worker that attempts to decode m_strFormatted.
+ *
+ * Sets m_fTimeSpecOkay.
+ *
+ * @returns IPRT status code.
+ * @param enmFormat Specific format to try, kFormat_Invalid (default) to try guess it.
+ */
+int RTCRestDate::decodeFormattedString(kFormat enmFormat /*= kFormat_Invalid*/) RT_NOEXCEPT
+{
+ /*
+ * Take empty string to mean null.
+ */
+ const char *pszTmp = RTStrStripL(m_strFormatted.c_str());
+ if (*pszTmp == '\0')
+ {
+ setNull();
+ return VINF_SUCCESS;
+ }
+
+ switch (enmFormat)
+ {
+ case kFormat_Invalid:
+ {
+ size_t cch = strlen(pszTmp);
+ if (cch >= 6)
+ {
+ if ( !RT_C_IS_DIGIT(pszTmp[0])
+ || RT_C_IS_SPACE(pszTmp[5])
+ || RT_C_IS_SPACE(pszTmp[2])
+ || RT_C_IS_SPACE(pszTmp[1])
+ || RT_C_IS_SPACE(pszTmp[3])
+ || RT_C_IS_SPACE(pszTmp[4]))
+ return decodeFormattedString(kFormat_Rfc2822);
+ return decodeFormattedString(kFormat_Rfc3339);
+ }
+ return VERR_REST_UNABLE_TO_DECODE_DATE;
+ }
+
+ /*
+ * Examples:
+ * Fri, 31 Aug 2018 00:00:00 +0200
+ * Mon, 3 Sep 2018 00:00:00 GMT
+ * Mon, 3 Sep 2018 00:00:00 -0000
+ * 3 Sep 2018 00:00:00 -0000 (?)
+ * 3 Sep 2018 00:00:00 GMT (?)
+ */
+ case kFormat_Rfc2822:
+ case kFormat_Rfc7131:
+ if (RTTimeFromRfc2822(&m_Exploded, pszTmp))
+ {
+ RTTimeImplode(&m_TimeSpec, &m_Exploded);
+
+ pszTmp = strchr(pszTmp, '\0');
+ if (pszTmp[-1] == 'T' || pszTmp[-1] == 't')
+ m_enmFormat = kFormat_Rfc7131;
+ else
+ m_enmFormat = kFormat_Rfc2822;
+ m_fTimeSpecOkay = true;
+ return VINF_SUCCESS;
+ }
+ return VERR_REST_UNABLE_TO_DECODE_DATE;
+
+ /*
+ * Examples:
+ * 2018-08-31T00:00:00+0200
+ * 2018-09-03T00:00:00Z
+ * 2018-09-03T00:00:00+0000
+ * 2018-09-03T00:00:00.123456789Z
+ */
+ case kFormat_Rfc3339:
+ case kFormat_Rfc3339_Fraction_2:
+ case kFormat_Rfc3339_Fraction_3:
+ case kFormat_Rfc3339_Fraction_6:
+ case kFormat_Rfc3339_Fraction_9:
+ if (RTTimeFromString(&m_Exploded, pszTmp))
+ {
+ RTTimeImplode(&m_TimeSpec, &m_Exploded);
+
+ pszTmp = strchr(pszTmp, '.');
+ if (!pszTmp)
+ m_enmFormat = kFormat_Rfc3339;
+ else
+ {
+ size_t cchFraction = 0;
+ pszTmp++;
+ while (RT_C_IS_DIGIT(pszTmp[cchFraction]))
+ cchFraction++;
+ if (cchFraction == 0)
+ m_enmFormat = kFormat_Rfc3339;
+ else if (cchFraction <= 2)
+ m_enmFormat = kFormat_Rfc3339_Fraction_2;
+ else if (cchFraction <= 3)
+ m_enmFormat = kFormat_Rfc3339_Fraction_3;
+ else if (cchFraction <= 6)
+ m_enmFormat = kFormat_Rfc3339_Fraction_6;
+ else
+ m_enmFormat = kFormat_Rfc3339_Fraction_9;
+ }
+ m_fTimeSpecOkay = true;
+ return VINF_SUCCESS;
+ }
+ return VERR_REST_UNABLE_TO_DECODE_DATE;
+
+ /* no default */
+ case kFormat_End:
+ break;
+ }
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+}
+
+
+/*********************************************************************************************************************************
+* RTCRestStringEnumBase implementation *
+*********************************************************************************************************************************/
+
+/** Default constructor. */
+RTCRestStringEnumBase::RTCRestStringEnumBase() RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , m_iEnumValue(0 /*invalid*/)
+ , m_strValue()
+{
+}
+
+
+/** Destructor. */
+RTCRestStringEnumBase::~RTCRestStringEnumBase()
+{
+ /* nothing to do */
+}
+
+
+/** Copy constructor. */
+RTCRestStringEnumBase::RTCRestStringEnumBase(RTCRestStringEnumBase const &a_rThat)
+ : RTCRestObjectBase(a_rThat)
+ , m_iEnumValue(a_rThat.m_iEnumValue)
+ , m_strValue(a_rThat.m_strValue)
+{
+}
+
+
+/** Copy assignment operator. */
+RTCRestStringEnumBase &RTCRestStringEnumBase::operator=(RTCRestStringEnumBase const &a_rThat)
+{
+ RTCRestObjectBase::operator=(a_rThat);
+ m_iEnumValue = a_rThat.m_iEnumValue;
+ m_strValue = a_rThat.m_strValue;
+ return *this;
+}
+
+
+int RTCRestStringEnumBase::assignCopy(RTCRestStringEnumBase const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ m_iEnumValue = a_rThat.m_iEnumValue;
+ return m_strValue.assignNoThrow(a_rThat.m_strValue);
+}
+
+
+int RTCRestStringEnumBase::resetToDefault() RT_NOEXCEPT
+{
+ m_iEnumValue = 0; /*invalid*/
+ m_strValue.setNull();
+ return VINF_SUCCESS;
+}
+
+
+RTCRestOutputBase &RTCRestStringEnumBase::serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
+{
+ if (!m_fNullIndicator)
+ a_rDst.printf("%RMjs", getString());
+ else
+ a_rDst.nullValue();
+ return a_rDst;
+}
+
+
+int RTCRestStringEnumBase::deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ m_fNullIndicator = false;
+ m_iEnumValue = 0;
+
+ RTJSONVALTYPE enmType = RTJsonValueGetType(a_rCursor.m_hValue);
+ if (enmType == RTJSONVALTYPE_STRING)
+ {
+ const char *pszValue = RTJsonValueGetString(a_rCursor.m_hValue);
+ const size_t cchValue = strlen(pszValue);
+ int rc = setByString(pszValue, cchValue);
+ if (RT_SUCCESS(rc))
+ return rc;
+ return a_rCursor.m_pPrimary->addError(a_rCursor, rc, "no memory for %zu char long string", cchValue);
+ }
+
+ m_strValue.setNull();
+ if (enmType == RTJSONVALTYPE_NULL)
+ {
+ m_fNullIndicator = true;
+ return VINF_SUCCESS;
+ }
+
+ return a_rCursor.m_pPrimary->addError(a_rCursor, VERR_REST_WRONG_JSON_TYPE_FOR_STRING, "wrong JSON type %s for string/enum",
+ RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
+}
+
+
+int RTCRestStringEnumBase::toString(RTCString *a_pDst, uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) const RT_NOEXCEPT
+{
+ if (!m_fNullIndicator)
+ {
+ if (m_iEnumValue > 0)
+ {
+ size_t cEntries = 0;
+ ENUMMAPENTRY const *paEntries = getMappingTable(&cEntries);
+ AssertReturn((unsigned)(m_iEnumValue - 1) < cEntries, VERR_REST_INTERNAL_ERROR_3);
+ Assert(paEntries[m_iEnumValue - 1].iValue == m_iEnumValue);
+
+ if (a_fFlags & kToString_Append)
+ return a_pDst->appendNoThrow(paEntries[m_iEnumValue - 1].pszName, paEntries[m_iEnumValue - 1].cchName);
+ return a_pDst->assignNoThrow(paEntries[m_iEnumValue - 1].pszName, paEntries[m_iEnumValue - 1].cchName);
+ }
+ if (a_fFlags & kToString_Append)
+ return a_pDst->appendNoThrow(m_strValue);
+ return a_pDst->assignNoThrow(m_strValue);
+ }
+ if (a_fFlags & kToString_Append)
+ return a_pDst->appendNoThrow(RT_STR_TUPLE("null"));
+ return a_pDst->assignNoThrow(RT_STR_TUPLE("null"));
+}
+
+
+int RTCRestStringEnumBase::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo /*= NULL*/,
+ uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) RT_NOEXCEPT
+{
+ int iEnumValue = stringToEnum(a_rValue);
+ if (iEnumValue > 0)
+ {
+ m_iEnumValue = iEnumValue;
+ m_strValue.setNull();
+ return VINF_SUCCESS;
+ }
+
+ /* No translation. Check for null... */
+ m_iEnumValue = 0;
+ if (a_rValue.startsWithWord("null", RTCString::CaseInsensitive))
+ {
+ m_strValue.setNull();
+ setNull();
+ return VINF_SUCCESS;
+ }
+
+ /* Try copy the string. */
+ int rc = m_strValue.assignNoThrow(a_rValue);
+ if (RT_SUCCESS(rc))
+ return VWRN_NOT_FOUND;
+
+ RT_NOREF(a_pszName, a_pErrInfo, a_fFlags);
+ return rc;
+}
+
+
+RTCRestObjectBase::kTypeClass RTCRestStringEnumBase::typeClass(void) const RT_NOEXCEPT
+{
+ return kTypeClass_StringEnum;
+}
+
+
+int RTCRestStringEnumBase::setByString(const char *a_pszValue, size_t a_cchValue /*= RTSTR_MAX*/) RT_NOEXCEPT
+{
+ if (a_cchValue == RTSTR_MAX)
+ a_cchValue = strlen(a_pszValue);
+ int iEnumValue = stringToEnum(a_pszValue, a_cchValue);
+ if (iEnumValue > 0)
+ {
+ m_iEnumValue = iEnumValue;
+ m_strValue.setNull();
+ return VINF_SUCCESS;
+ }
+
+ /* No translation. */
+ m_iEnumValue = 0;
+ int rc = m_strValue.assignNoThrow(a_pszValue, a_cchValue);
+ if (RT_SUCCESS(rc))
+ return VWRN_NOT_FOUND;
+ return rc;
+}
+
+
+int RTCRestStringEnumBase::setByString(RTCString const &a_rValue) RT_NOEXCEPT
+{
+ return setByString(a_rValue.c_str(), a_rValue.length());
+}
+
+
+const char *RTCRestStringEnumBase::getString() const RT_NOEXCEPT
+{
+ /* We ASSUME a certain mapping table layout here. */
+ if (m_iEnumValue > 0)
+ {
+ size_t cEntries = 0;
+ ENUMMAPENTRY const *paEntries = getMappingTable(&cEntries);
+ AssertReturn((unsigned)(m_iEnumValue - 1) < cEntries, "<internal-error-#1>");
+ Assert(paEntries[m_iEnumValue - 1].iValue == m_iEnumValue);
+ return paEntries[m_iEnumValue - 1].pszName;
+ }
+
+ AssertReturn(m_iEnumValue == 0, "<internal-error-#2>");
+ if (m_strValue.isEmpty())
+ return "invalid";
+
+ return m_strValue.c_str();
+}
+
+
+int RTCRestStringEnumBase::stringToEnum(const char *a_pszValue, size_t a_cchValue) RT_NOEXCEPT
+{
+ if (a_cchValue == RTSTR_MAX)
+ a_cchValue = strlen(a_pszValue);
+
+ size_t cEntries = 0;
+ ENUMMAPENTRY const *paEntries = getMappingTable(&cEntries);
+ for (size_t i = 0; i < cEntries; i++)
+ if ( paEntries[i].cchName == a_cchValue
+ && memcmp(paEntries[i].pszName, a_pszValue, a_cchValue) == 0)
+ return paEntries[i].iValue;
+ return 0;
+}
+
+
+int RTCRestStringEnumBase::stringToEnum(RTCString const &a_rStrValue) RT_NOEXCEPT
+{
+ return stringToEnum(a_rStrValue.c_str(), a_rStrValue.length());
+}
+
+
+const char *RTCRestStringEnumBase::enumToString(int a_iEnumValue, size_t *a_pcchString) RT_NOEXCEPT
+{
+ /* We ASSUME a certain mapping table layout here. */
+ if (a_iEnumValue > 0)
+ {
+ size_t cEntries = 0;
+ ENUMMAPENTRY const *paEntries = getMappingTable(&cEntries);
+ if ((unsigned)(a_iEnumValue - 1) < cEntries)
+ {
+ Assert(paEntries[a_iEnumValue - 1].iValue == a_iEnumValue);
+ if (a_pcchString)
+ *a_pcchString = paEntries[a_iEnumValue - 1].cchName;
+ return paEntries[a_iEnumValue - 1].pszName;
+ }
+ }
+ /* Zero is the special invalid value. */
+ else if (a_iEnumValue == 0)
+ {
+ if (a_pcchString)
+ *a_pcchString = 7;
+ return "invalid";
+ }
+ return NULL;
+}
+
+
+bool RTCRestStringEnumBase::setWorker(int a_iEnumValue) RT_NOEXCEPT
+{
+ /* We ASSUME a certain mapping table layout here. */
+ if (a_iEnumValue > 0)
+ {
+ size_t cEntries = 0;
+ ENUMMAPENTRY const *paEntries = getMappingTable(&cEntries);
+ AssertReturn((unsigned)(a_iEnumValue - 1) < cEntries, false);
+ Assert(paEntries[a_iEnumValue - 1].iValue == a_iEnumValue);
+ RT_NOREF(paEntries);
+ }
+ /* Zero is the special invalid value. */
+ else if (a_iEnumValue != 0)
+ AssertFailedReturn(false);
+
+ m_iEnumValue = a_iEnumValue;
+ m_strValue.setNull();
+ return true;
+}
+
+
+RTCRestObjectBase *RTCRestStringEnumBase::cloneWorker(RTCRestStringEnumBase *a_pDst) const RT_NOEXCEPT
+{
+ if (a_pDst)
+ {
+ int rc = a_pDst->assignCopy(*this);
+ if (RT_SUCCESS(rc))
+ return a_pDst;
+ delete a_pDst;
+ }
+ return NULL;
+}
+
+
+
+/*********************************************************************************************************************************
+* RTCRestDataObject *
+*********************************************************************************************************************************/
+
+RTCRestDataObject::RTCRestDataObject() RT_NOEXCEPT
+ : RTCRestObjectBase()
+ , m_fIsSet(0)
+{
+}
+
+
+RTCRestDataObject::~RTCRestDataObject()
+{
+}
+
+
+RTCRestDataObject::RTCRestDataObject(RTCRestDataObject const &a_rThat) RT_NOEXCEPT
+ : RTCRestObjectBase(a_rThat)
+ , m_fIsSet(a_rThat.m_fIsSet)
+{
+}
+
+
+RTCRestDataObject &RTCRestDataObject::operator=(RTCRestDataObject const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ m_fIsSet = a_rThat.m_fIsSet;
+ return *this;
+}
+
+
+int RTCRestDataObject::assignCopy(RTCRestDataObject const &a_rThat) RT_NOEXCEPT
+{
+ m_fNullIndicator = a_rThat.m_fNullIndicator;
+ m_fIsSet = a_rThat.m_fIsSet;
+ return VINF_SUCCESS;
+}
+
+
+int RTCRestDataObject::resetToDefault() RT_NOEXCEPT
+{
+ m_fNullIndicator = false;
+ m_fIsSet = 0;
+ return VINF_SUCCESS;
+}
+
+
+RTCRestOutputBase &RTCRestDataObject::serializeMembersAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
+{
+ RT_NOREF(a_rDst);
+ return a_rDst;
+}
+
+
+RTCRestOutputBase &RTCRestDataObject::serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
+{
+ if (!m_fNullIndicator)
+ {
+ uint32_t const uOldState = a_rDst.beginObject();
+ serializeMembersAsJson(a_rDst);
+ a_rDst.endObject(uOldState);
+ }
+ else
+ a_rDst.nullValue();
+ return a_rDst;
+}
+
+
+int RTCRestDataObject::deserializeMemberFromJson(RTCRestJsonCursor const &a_rCursor, size_t a_cchName) RT_NOEXCEPT
+{
+ RT_NOREF(a_rCursor, a_cchName);
+ return VERR_NOT_FOUND;
+}
+
+
+int RTCRestDataObject::deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
+{
+ if (RTJsonValueGetType(a_rCursor.m_hValue) == RTJSONVALTYPE_NULL)
+ {
+ setNull();
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Make sure the object starts out with default values.
+ */
+ if (m_fIsSet == 0)
+ m_fNullIndicator = false;
+ else
+ resetToDefault();
+
+ /*
+ * Iterate the object values.
+ */
+ RTJSONIT hIterator;
+ int rcRet = RTJsonIteratorBeginObject(a_rCursor.m_hValue, &hIterator);
+ if (RT_SUCCESS(rcRet))
+ {
+ for (;;)
+ {
+ RTCRestJsonCursor SubCursor(a_rCursor);
+ int rc = RTJsonIteratorQueryValue(hIterator, &SubCursor.m_hValue, &SubCursor.m_pszName);
+ if (RT_SUCCESS(rc))
+ {
+ size_t const cchName = strlen(SubCursor.m_pszName);
+
+ rc = deserializeMemberFromJson(SubCursor, cchName);
+ if (rc == VINF_SUCCESS)
+ { /* likely */ }
+ else if (rc == VERR_NOT_FOUND)
+ {
+ rc = SubCursor.m_pPrimary->unknownField(SubCursor);
+ if (rcRet == VINF_SUCCESS)
+ rcRet = rc;
+ }
+ else if (RT_SUCCESS(rc))
+ {
+ if (rcRet == VINF_SUCCESS)
+ rcRet = rc;
+ }
+ else if (RT_SUCCESS(rcRet))
+ rcRet = rc;
+ }
+ else
+ rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonIteratorQueryValue failed: %Rrc", rc);
+
+ /*
+ * Advance.
+ */
+ rc = RTJsonIteratorNext(hIterator);
+ if (RT_SUCCESS(rc))
+ { /* likely */ }
+ else if (rc == VERR_JSON_ITERATOR_END)
+ break;
+ else
+ {
+ rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonIteratorNext failed: %Rrc", rc);
+ break;
+ }
+ }
+
+ RTJsonIteratorFree(hIterator);
+ }
+ else if ( rcRet == VERR_JSON_VALUE_INVALID_TYPE
+ && RTJsonValueGetType(a_rCursor.m_hValue) == RTJSONVALTYPE_NULL)
+ {
+ m_fNullIndicator = true;
+ rcRet = VINF_SUCCESS;
+ }
+ else
+ rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rcRet, "RTJsonIteratorBeginObject failed: %Rrc (type %s)",
+ rcRet, RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
+ return rcRet;
+}
+
+
+RTCRestObjectBase::kTypeClass RTCRestDataObject::typeClass(void) const RT_NOEXCEPT
+{
+ return kTypeClass_DataObject;
+}
+
+
+
+/*********************************************************************************************************************************
+* RTCRestPolyDataObject *
+*********************************************************************************************************************************/
+
+RTCRestPolyDataObject::RTCRestPolyDataObject() RT_NOEXCEPT
+ : RTCRestDataObject()
+{
+}
+
+
+RTCRestPolyDataObject::RTCRestPolyDataObject(RTCRestPolyDataObject const &a_rThat) RT_NOEXCEPT
+ : RTCRestDataObject(a_rThat)
+{
+}
+
+
+RTCRestPolyDataObject::~RTCRestPolyDataObject()
+{
+}
+
+
+int RTCRestPolyDataObject::resetToDefault() RT_NOEXCEPT
+{
+ return RTCRestDataObject::resetToDefault();
+}
+
+
+bool RTCRestPolyDataObject::isChild() const RT_NOEXCEPT
+{
+ return false;
+}
+
+
+RTCRestPolyDataObject &RTCRestPolyDataObject::operator=(RTCRestPolyDataObject const &a_rThat) RT_NOEXCEPT
+{
+ RTCRestDataObject::operator=(a_rThat);
+ return *this;
+}
+