diff options
Diffstat (limited to 'src/VBox/Runtime/common/rest')
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; +} + |