<commit_before>/**
 * @file	container_traits.hpp
 * @ingroup utils
 * @author	Tony Pan <tpan7@gatech.edu>
 * @brief   helper functions to check if a container has the required methods.
 * @details
 *
 * Copyright (c) 2014 Georgia Institute of Technology.  All Rights Reserved.
 *
 * TODO add License
 */
#ifndef CONTAINER_TRAITS_HPP_
#define CONTAINER_TRAITS_HPP_

#include <utility>
#include <type_traits>

#include <vector>
#include <string>
#include <valarray>
#include <array>

    // preprocessor macro to save some typing
#define BLISS_UTILS_CONTAINER_HAS_METHOD_WITH_NAME(NAME) \
        template<typename CC = C> \
        static constexpr auto has_##NAME(CC*) -> decltype(std::declval<CC>().NAME(), ::std::true_type()); \
        template<typename> \
        static constexpr ::std::false_type has_##NAME(...);

namespace bliss {
  namespace utils {

    // TCP test if iterator may be from a contiguous container.
    template <typename Container>
    struct container_mem_traits { static constexpr bool is_contiguous = false; };

    template <>
    template <typename T, typename A>
    struct container_mem_traits<::std::vector<T, A> > { static constexpr bool is_contiguous = true; };

    template <>
    template <typename T, ::std::size_t N>
    struct container_mem_traits<::std::array<T, N> > { static constexpr bool is_contiguous = true; };

    template <>
    template <typename CharT, typename Traits, typename Allocator>
    struct container_mem_traits<::std::basic_string<CharT, Traits, Allocator>> { static constexpr bool is_contiguous = true; };

    template <>
    template <typename T>
    struct container_mem_traits<::std::valarray<T> > { static constexpr bool is_contiguous = true; };

    template <>
    template <typename T>
    struct container_mem_traits<::std::initializer_list<T> > { static constexpr bool is_contiguous = true; };

    template <>
    template <typename T>
    struct container_mem_traits< T* > { static constexpr bool is_contiguous = true; };

    template <typename Iterator>
    static constexpr bool is_contiguous(Iterator) { return false; }

    template <typename Container>
    static constexpr bool is_contiguous(typename Container::iterator) { return container_mem_traits<Container>::is_contiguous; }

    template <typename Container>
    static constexpr bool is_contiguous(typename Container::const_iterator) { return container_mem_traits<Container>::is_contiguous; }

    template <typename Container>
    static constexpr bool is_contiguous(typename Container::reverse_iterator) { return container_mem_traits<Container>::is_contiguous; }

    template <typename Container>
    static constexpr bool is_contiguous(typename Container::const_reverse_iterator) { return container_mem_traits<Container>::is_contiguous; }

    template <typename T>
    static constexpr bool is_contiguous(decltype(::std::begin(std::declval<::std::valarray<T>& >()))) { return true; }

    template <typename T>
    static constexpr bool is_contiguous(decltype(::std::begin(std::declval<const ::std::valarray<T>& >()))) { return true; }

    template <typename T, std::size_t N>
    static constexpr bool is_contiguous(T (&array)[N]) { return true; }

    template <typename T>
    static constexpr bool is_contiguous(T*) { return true; }

    /**
     * @class container_traits
     * @brief This class is used to determine if certain methods are present.
     * @tparam C    container type, whose traits this class extracts.
     */
    template<typename C>
    class container_traits {

      private:

        /**
         * @brief   determine if the container has an begin method
         * @detail  uses declval to get lvalues, and use decltype and SFINAE to do "enable_if".
         *          decltype evaluates all expressions, but returns type of last (true/false).
         *
         *          method is defined only if begin exists.
         *          adopted from  http://dev.krzaq.cc/checking-whether-a-class-has-a-member-function-with-a-given-signature/
         *
         * @tparam  CC   type to check, should be a container.
         * @param       unamed pointer parameter for automatic type deduction for the templates
         * @return      true if method is defined, for all else return false.
         */
        BLISS_UTILS_CONTAINER_HAS_METHOD_WITH_NAME(begin);

        /**
         * @brief   determine if a type has an end method
         * @detail  uses declval to get lvalues, and use decltype and SFINAE to do "enable_if".
         *          decltype evaluates all expressions, but returns type of last (true/false).
         *
         *          method is defined only if end exists.
         *          adopted from  http://dev.krzaq.cc/checking-whether-a-class-has-a-member-function-with-a-given-signature/
         *
         * @tparam  CC   type to check, should be a container.
         * @param       unamed pointer parameter for automatic type deduction for the templates
         * @return      true if end method is defined, for all else return false.
         */
        BLISS_UTILS_CONTAINER_HAS_METHOD_WITH_NAME(end);

        /**
         * @brief   determine if a type has an cbegin method
         * @detail  uses declval to get lvalues, and use decltype and SFINAE to do "enable_if".
         *          decltype evaluates all expressions, but returns type of last (true/false).
         *
         *          method is defined only if cbegin exists.
         *          adopted from  http://dev.krzaq.cc/checking-whether-a-class-has-a-member-function-with-a-given-signature/
         *
         * @tparam  CC   type to check, should be a container.
         * @param       unamed pointer parameter for automatic type deduction for the templates
         * @return      true if method is defined, for all else return false.
         */
        BLISS_UTILS_CONTAINER_HAS_METHOD_WITH_NAME(cbegin);

        /**
         * @brief   determine if a type has an cend method
         * @detail  uses declval to get lvalues, and use decltype and SFINAE to do "enable_if".
         *          decltype evaluates all expressions, but returns type of last (true/false).
         *
         *          method is defined only if cend exists.
         *          adopted from  http://dev.krzaq.cc/checking-whether-a-class-has-a-member-function-with-a-given-signature/
         *
         * @tparam  CC   type to check, should be a container.
         * @param       unamed pointer parameter for automatic type deduction for the templates
         * @return      true if method is defined, for all else return false.
         */
        BLISS_UTILS_CONTAINER_HAS_METHOD_WITH_NAME(cend);

        /**
         * @brief   determine if the container has an assign method
         * @details  uses declval to get lvalues, and use decltype and SFINAE to do "enable_if".
         *          decltype evaluates all expressions, but returns type of last (true/false).
         *
         *          method is defined only if assign exists.
         *          test with the container's own iterator, so also depend on begin() and end() existing.
         *
         *          adopted from  http://dev.krzaq.cc/checking-whether-a-class-has-a-member-function-with-a-given-signature/
         *
         * @tparam  CC   type to check, should be a container.
         * @param       unamed pointer parameter for automatic type deduction for the templates
         * @return      true if method is defined, for all else return false.
         */
        template<typename CC = C>
        static constexpr auto has_assign(CC*) ->
            decltype(std::declval<CC>().assign(::std::declval<decltype(std::declval<CC>().begin())>(),
                                               ::std::declval<decltype(std::declval<CC>().end())>()
                                               ), ::std::true_type());
        template<typename>
        static constexpr ::std::false_type has_assign(...);

      public:

        /**
         * @var       isConstIterable
         * @brief     static bool indicating if container supports constant iteration (has cbegin and cend)
         */
        static constexpr bool isConstIterable = ::std::is_same<decltype(has_cbegin<C>(nullptr)), ::std::true_type>::value
                  && ::std::is_same<decltype(has_cend<C>(nullptr)), ::std::true_type>::value;

        /**
         * @var       isIterable
         * @brief     static bool indicating if container supports iteration (has begin and end)
         */
        static constexpr bool isIterable = ::std::is_same<decltype(has_begin<C>(nullptr)), ::std::true_type>::value
            && ::std::is_same<decltype(has_end<C>(nullptr)), ::std::true_type>::value;

        /**
         * @var       isAssignable
         * @brief     static bool indicating if container supports assignment.
         */
        static constexpr bool isAssignable = ::std::is_same<decltype(has_assign<C>(nullptr)), ::std::true_type>::value;

    };

  } //namespace utils
} //namespace bliss

#endif /* CONTAINER_TRAITS_HPP_ */
<commit_msg>FIX: clang warns of missing definition for has_xxx methods. gcc does not complain.  fixed for clang.  Removed unneeded template<> markers.<commit_after>/**
 * @file	container_traits.hpp
 * @ingroup utils
 * @author	Tony Pan <tpan7@gatech.edu>
 * @brief   helper functions to check if a container has the required methods.
 * @details
 *
 * Copyright (c) 2014 Georgia Institute of Technology.  All Rights Reserved.
 *
 * TODO add License
 */
#ifndef CONTAINER_TRAITS_HPP_
#define CONTAINER_TRAITS_HPP_

#include <utility>
#include <type_traits>

#include <vector>
#include <string>
#include <valarray>
#include <array>

    // preprocessor macro to save some typing
#define BLISS_UTILS_CONTAINER_HAS_METHOD_WITH_NAME(NAME) \
        template<typename CC = C> \
        static constexpr auto has_##NAME(CC*) -> decltype(std::declval<CC>().NAME(), ::std::true_type()) { return ::std::true_type(); }; \
        template<typename> \
        static constexpr ::std::false_type has_##NAME(...) { return ::std::false_type(); };

namespace bliss {
  namespace utils {

    // TCP test if iterator may be from a contiguous container.
    template <typename Container>
    struct container_mem_traits { static constexpr bool is_contiguous = false; };

//    template <>
    template <typename T, typename A>
    struct container_mem_traits<::std::vector<T, A> > { static constexpr bool is_contiguous = true; };

//    template <>
    template <typename T, ::std::size_t N>
    struct container_mem_traits<::std::array<T, N> > { static constexpr bool is_contiguous = true; };

//    template <>
    template <typename CharT, typename Traits, typename Allocator>
    struct container_mem_traits<::std::basic_string<CharT, Traits, Allocator> > { static constexpr bool is_contiguous = true; };

//    template <>
    template <typename T>
    struct container_mem_traits<::std::valarray<T> > { static constexpr bool is_contiguous = true; };

//    template <>
    template <typename T>
    struct container_mem_traits<::std::initializer_list<T> > { static constexpr bool is_contiguous = true; };

//    template <>
    template <typename T>
    struct container_mem_traits< T* > { static constexpr bool is_contiguous = true; };

    template <typename Iterator>
    static constexpr bool is_contiguous(Iterator) { return false; }

    template <typename Container>
    static constexpr bool is_contiguous(typename Container::iterator) { return container_mem_traits<Container>::is_contiguous; }

    template <typename Container>
    static constexpr bool is_contiguous(typename Container::const_iterator) { return container_mem_traits<Container>::is_contiguous; }

    template <typename Container>
    static constexpr bool is_contiguous(typename Container::reverse_iterator) { return container_mem_traits<Container>::is_contiguous; }

    template <typename Container>
    static constexpr bool is_contiguous(typename Container::const_reverse_iterator) { return container_mem_traits<Container>::is_contiguous; }

    template <typename T>
    static constexpr bool is_contiguous(decltype(::std::begin(std::declval<::std::valarray<T>& >()))) { return true; }

    template <typename T>
    static constexpr bool is_contiguous(decltype(::std::begin(std::declval<const ::std::valarray<T>& >()))) { return true; }

    template <typename T, std::size_t N>
    static constexpr bool is_contiguous(T (&array)[N]) { return true; }

    template <typename T>
    static constexpr bool is_contiguous(T*) { return true; }

    /**
     * @class container_traits
     * @brief This class is used to determine if certain methods are present.
     * @tparam C    container type, whose traits this class extracts.
     */
    template<typename C>
    class container_traits {

      private:

        /**
         * @brief   determine if the container has an begin method
         * @detail  uses declval to get lvalues, and use decltype and SFINAE to do "enable_if".
         *          decltype evaluates all expressions, but returns type of last (true/false).
         *
         *          method is defined only if begin exists.
         *          adopted from  http://dev.krzaq.cc/checking-whether-a-class-has-a-member-function-with-a-given-signature/
         *
         * @tparam  CC   type to check, should be a container.
         * @param       unamed pointer parameter for automatic type deduction for the templates
         * @return      true if method is defined, for all else return false.
         */
        BLISS_UTILS_CONTAINER_HAS_METHOD_WITH_NAME(begin);

        /**
         * @brief   determine if a type has an end method
         * @detail  uses declval to get lvalues, and use decltype and SFINAE to do "enable_if".
         *          decltype evaluates all expressions, but returns type of last (true/false).
         *
         *          method is defined only if end exists.
         *          adopted from  http://dev.krzaq.cc/checking-whether-a-class-has-a-member-function-with-a-given-signature/
         *
         * @tparam  CC   type to check, should be a container.
         * @param       unamed pointer parameter for automatic type deduction for the templates
         * @return      true if end method is defined, for all else return false.
         */
        BLISS_UTILS_CONTAINER_HAS_METHOD_WITH_NAME(end);

        /**
         * @brief   determine if a type has an cbegin method
         * @detail  uses declval to get lvalues, and use decltype and SFINAE to do "enable_if".
         *          decltype evaluates all expressions, but returns type of last (true/false).
         *
         *          method is defined only if cbegin exists.
         *          adopted from  http://dev.krzaq.cc/checking-whether-a-class-has-a-member-function-with-a-given-signature/
         *
         * @tparam  CC   type to check, should be a container.
         * @param       unamed pointer parameter for automatic type deduction for the templates
         * @return      true if method is defined, for all else return false.
         */
        BLISS_UTILS_CONTAINER_HAS_METHOD_WITH_NAME(cbegin);

        /**
         * @brief   determine if a type has an cend method
         * @detail  uses declval to get lvalues, and use decltype and SFINAE to do "enable_if".
         *          decltype evaluates all expressions, but returns type of last (true/false).
         *
         *          method is defined only if cend exists.
         *          adopted from  http://dev.krzaq.cc/checking-whether-a-class-has-a-member-function-with-a-given-signature/
         *
         * @tparam  CC   type to check, should be a container.
         * @param       unamed pointer parameter for automatic type deduction for the templates
         * @return      true if method is defined, for all else return false.
         */
        BLISS_UTILS_CONTAINER_HAS_METHOD_WITH_NAME(cend);

        /**
         * @brief   determine if the container has an assign method
         * @details  uses declval to get lvalues, and use decltype and SFINAE to do "enable_if".
         *          decltype evaluates all expressions, but returns type of last (true/false).
         *
         *          method is defined only if assign exists.
         *          test with the container's own iterator, so also depend on begin() and end() existing.
         *
         *          adopted from  http://dev.krzaq.cc/checking-whether-a-class-has-a-member-function-with-a-given-signature/
         *
         * @tparam  CC   type to check, should be a container.
         * @param       unamed pointer parameter for automatic type deduction for the templates
         * @return      true if method is defined, for all else return false.
         */
        template<typename CC = C>
        static constexpr auto has_assign(CC*) ->
            decltype(std::declval<CC>().assign(::std::declval<decltype(std::declval<CC>().begin())>(),
                                               ::std::declval<decltype(std::declval<CC>().end())>()
                                               ), ::std::true_type()) { return ::std::true_type(); };
        template<typename>
        static constexpr ::std::false_type has_assign(...) { return ::std::false_type(); };

      public:

        /**
         * @var       isConstIterable
         * @brief     static bool indicating if container supports constant iteration (has cbegin and cend)
         */
        static constexpr bool isConstIterable = ::std::is_same<decltype(has_cbegin<C>(nullptr)), ::std::true_type>::value
                  && ::std::is_same<decltype(has_cend<C>(nullptr)), ::std::true_type>::value;

        /**
         * @var       isIterable
         * @brief     static bool indicating if container supports iteration (has begin and end)
         */
        static constexpr bool isIterable = ::std::is_same<decltype(has_begin<C>(nullptr)), ::std::true_type>::value
            && ::std::is_same<decltype(has_end<C>(nullptr)), ::std::true_type>::value;

        /**
         * @var       isAssignable
         * @brief     static bool indicating if container supports assignment.
         */
        static constexpr bool isAssignable = ::std::is_same<decltype(has_assign<C>(nullptr)), ::std::true_type>::value;

    };

  } //namespace utils
} //namespace bliss

#endif /* CONTAINER_TRAITS_HPP_ */
<|endoftext|>


<commit_before>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/*
 * XSEC
 *
 * OpenSSLCryptoKeyDSA := DSA Keys
 *
 * Author(s): Berin Lautenbach
 *
 * $Id$
 *
 */
#include <xsec/framework/XSECDefs.hpp>
#if defined (XSEC_HAVE_OPENSSL)

#include <xsec/enc/OpenSSL/OpenSSLSupport.hpp>
#include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
#include <xsec/enc/OpenSSL/OpenSSLCryptoBase64.hpp>
#include <xsec/enc/XSECCryptoException.hpp>
#include <xsec/enc/XSECCryptoUtils.hpp>
#include <xsec/framework/XSECError.hpp>

#include <xercesc/util/Janitor.hpp>

XSEC_USING_XERCES(ArrayJanitor);

#include <openssl/dsa.h>

OpenSSLCryptoKeyDSA::OpenSSLCryptoKeyDSA() : mp_dsaKey(NULL), mp_accumP(NULL), mp_accumQ(NULL), mp_accumG(NULL) {
};

OpenSSLCryptoKeyDSA::~OpenSSLCryptoKeyDSA() {

    // If we have a DSA, delete it
    // OpenSSL will ensure the memory holding any private key is freed.

    if (mp_dsaKey)
        DSA_free(mp_dsaKey);

    if (mp_accumG)
        BN_free(mp_accumG);

    if (mp_accumP)
        BN_free(mp_accumP);

    if (mp_accumQ)
        BN_free(mp_accumQ);
};

const XMLCh* OpenSSLCryptoKeyDSA::getProviderName() const {
	return DSIGConstants::s_unicodeStrPROVOpenSSL;
}

// Generic key functions

XSECCryptoKey::KeyType OpenSSLCryptoKeyDSA::getKeyType() const {

    // Find out what we have
    if (mp_dsaKey == NULL)
        return KEY_NONE;

    if (DSA_get0_privkey(mp_dsaKey) != NULL && DSA_get0_pubkey(mp_dsaKey) != NULL)
        return KEY_DSA_PAIR;

    if (DSA_get0_privkey(mp_dsaKey) != NULL)
        return KEY_DSA_PRIVATE;

    if (DSA_get0_pubkey(mp_dsaKey) != NULL)
        return KEY_DSA_PUBLIC;

    return KEY_NONE;

}

void OpenSSLCryptoKeyDSA::loadPBase64BigNums(const char * b64, unsigned int len)  {

    setPBase(OpenSSLCryptoBase64::b642BN((char *) b64, len));

}

void OpenSSLCryptoKeyDSA::setPBase(BIGNUM  * p) {

    if (mp_dsaKey == NULL)
        mp_dsaKey = DSA_new();

#if (OPENSSL_VERSION_NUMBER < 0x10100000L)

    // Do it immediately
    mp_dsaKey->p = p;

#else
    // Save it for later
    if (mp_accumP != NULL)
        BN_free(mp_accumP);

    mp_accumP = p;

    commitPQG();

#endif

}

void OpenSSLCryptoKeyDSA::loadQBase64BigNums(const char * b64, unsigned int len) {

    setQBase(OpenSSLCryptoBase64::b642BN((char *) b64, len));

}

void OpenSSLCryptoKeyDSA::setQBase(BIGNUM  * q) {

    if (mp_dsaKey == NULL)
        mp_dsaKey = DSA_new();

#if (OPENSSL_VERSION_NUMBER <   0x10100000L)

    mp_dsaKey->q = q;

#else
    if (mp_accumQ != NULL)
        BN_free(mp_accumQ);

    mp_accumQ = q;
    commitPQG();

#endif

}

void OpenSSLCryptoKeyDSA::loadGBase64BigNums(const char * b64, unsigned int len) {

    setGBase(OpenSSLCryptoBase64::b642BN((char *) b64, len));

}

void OpenSSLCryptoKeyDSA::setGBase(BIGNUM  * g) {

    if (mp_dsaKey == NULL)
        mp_dsaKey = DSA_new();

#if (OPENSSL_VERSION_NUMBER <   0x10100000L)

    mp_dsaKey->g = g;

#else
    if (mp_accumG != NULL)
        BN_free(mp_accumG);

    mp_accumG = g;
    commitPQG();

#endif

}

#if (OPENSSL_VERSION_NUMBER >=  0x10100000L)
void OpenSSLCryptoKeyDSA::commitPQG() {

    if (mp_accumP != NULL && mp_accumQ != NULL && mp_accumG != NULL) {

        DSA_set0_pqg(mp_dsaKey, mp_accumP, mp_accumQ, mp_accumG);
        mp_accumP = NULL;
        mp_accumQ = NULL;
        mp_accumG = NULL;

    }
}
#endif

void OpenSSLCryptoKeyDSA::loadYBase64BigNums(const char * b64, unsigned int len) {

    if (mp_dsaKey == NULL)
        mp_dsaKey = DSA_new();

    BIGNUM *newPub = OpenSSLCryptoBase64::b642BN((char *) b64, len);
    const BIGNUM *oldPriv;
    DSA_get0_key(mp_dsaKey, NULL, &oldPriv);

    DSA_set0_key(mp_dsaKey, newPub, (oldPriv?BN_dup(oldPriv):NULL));
}

void OpenSSLCryptoKeyDSA::loadJBase64BigNums(const char * b64, unsigned int len) {

    if (mp_dsaKey == NULL)
        mp_dsaKey = DSA_new();

    // Do nothing
}

// "Hidden" OpenSSL functions

OpenSSLCryptoKeyDSA::OpenSSLCryptoKeyDSA(EVP_PKEY *k) : mp_accumP(NULL), mp_accumQ(NULL), mp_accumG(NULL) {

    // Create a new key to be loaded as we go

    mp_dsaKey = DSA_new();
    mp_accumG = NULL;
    mp_accumP = NULL;
    mp_accumQ = NULL;

    if (k == NULL || EVP_PKEY_id(k) != EVP_PKEY_DSA)
        return; // Nothing to do with us

    const BIGNUM *otherP = NULL, *otherQ = NULL, *otherG = NULL;
    DSA_get0_pqg(EVP_PKEY_get0_DSA(k), &otherP, &otherQ, &otherG);

    if (otherP != NULL && otherQ != NULL && otherG != NULL) {
        DSA_set0_pqg(mp_dsaKey, BN_dup(otherP), BN_dup(otherQ), BN_dup(otherG));
    }

    const BIGNUM *otherPriv = NULL, *otherPub = NULL;
    DSA_get0_key(EVP_PKEY_get0_DSA(k), &otherPub, &otherPriv);

    if (otherPub != NULL) {

        BIGNUM *newPriv = NULL;

        if (otherPriv != NULL)
            newPriv = BN_dup(otherPriv);

        DSA_set0_key(mp_dsaKey, BN_dup(otherPub), newPriv);

    }
}

// --------------------------------------------------------------------------------
//           Verify a signature encoded as a Base64 string
// --------------------------------------------------------------------------------

bool OpenSSLCryptoKeyDSA::verifyBase64Signature(unsigned char * hashBuf,
                                 unsigned int hashLen,
                                 char * base64Signature,
                                 unsigned int sigLen) const {

    // Use the currently loaded key to validate the Base64 encoded signature

    if (mp_dsaKey == NULL) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Attempt to validate signature with empty key");
    }

    XSECCryptoKey::KeyType keyType = getKeyType();
    if (keyType != KEY_DSA_PAIR && keyType != KEY_DSA_PUBLIC) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Attempt to validate signature without public key");
    }

    char* cleanedBase64Signature;
    unsigned int cleanedBase64SignatureLen = 0;

    cleanedBase64Signature =
        XSECCryptoBase64::cleanBuffer(base64Signature, sigLen, cleanedBase64SignatureLen);
    ArrayJanitor<char> j_cleanedBase64Signature(cleanedBase64Signature);

    int sigValLen;
    unsigned char* sigVal = new unsigned char[sigLen + 1];
    ArrayJanitor<unsigned char> j_sigVal(sigVal);

    EvpEncodeCtxRAII dctx;

    if (!dctx.of()) {
        throw XSECCryptoException(XSECCryptoException::ECError,
            "OpenSSL:DSA - allocation fail during Context Creation");
    }

    EVP_DecodeInit(dctx.of());

    int rc = EVP_DecodeUpdate(dctx.of(),
                          sigVal,
                          &sigValLen,
                          (unsigned char *) cleanedBase64Signature,
                          cleanedBase64SignatureLen);

    if (rc < 0) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Error during Base64 Decode");
    }

    int t = 0;

    EVP_DecodeFinal(dctx.of(), &sigVal[sigValLen], &t);

    sigValLen += t;

    // Translate to BNs and thence to DSA_SIG
    BIGNUM * R;
    BIGNUM * S;

    if (sigValLen == 40) {

        R = BN_bin2bn(sigVal, 20, NULL);
        S = BN_bin2bn(&sigVal[20], 20, NULL);
    }
    else {

        unsigned char rb[20];
        unsigned char sb[20];

        if (sigValLen == 46 && ASN2DSASig(sigVal, rb, sb) == true) {

            R = BN_bin2bn(rb, 20, NULL);
            S = BN_bin2bn(sb, 20, NULL);

        }

        else {

            throw XSECCryptoException(XSECCryptoException::DSAError,
                "OpenSSL:DSA - Signature Length incorrect");
        }
    }

    DSA_SIG * dsa_sig = DSA_SIG_new();

    DSA_SIG_set0(dsa_sig, BN_dup(R), BN_dup(S));

    BN_free(R);
    BN_free(S);

    // Now we have a signature and a key - lets check

    int err = DSA_do_verify(hashBuf, hashLen, dsa_sig, mp_dsaKey);

    DSA_SIG_free(dsa_sig);

    if (err < 0) {

        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Error validating signature");
    }

    return (err == 1);

}

// --------------------------------------------------------------------------------
//           Sign and encode result as a Base64 string
// --------------------------------------------------------------------------------

unsigned int OpenSSLCryptoKeyDSA::signBase64Signature(unsigned char * hashBuf,
        unsigned int hashLen,
        char * base64SignatureBuf,
        unsigned int base64SignatureBufLen) const {

    // Sign a pre-calculated hash using this key

    if (mp_dsaKey == NULL) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Attempt to sign data with empty key");
    }

    KeyType keyType = getKeyType();
    if (keyType != KEY_DSA_PAIR && keyType != KEY_DSA_PRIVATE) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Attempt to sign data without private key");
    }

    DSA_SIG* dsa_sig = DSA_do_sign(hashBuf, hashLen, mp_dsaKey);

    if (dsa_sig == NULL) {

        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Error signing data");

    }

    // Now turn the signature into a base64 string

    const BIGNUM *dsaSigR;
    const BIGNUM *dsaSigS;

    DSA_SIG_get0(dsa_sig, &dsaSigR, &dsaSigS);

    const int DSAsigCompLen = 20; // XMLDSIG spec 6.4.1
    unsigned char rawSigBuf[2*DSAsigCompLen];

    if (BN_bn2binpad(dsaSigR, rawSigBuf, DSAsigCompLen) <= 0) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Error converting signature to raw buffer");
    }

    if (BN_bn2binpad(dsaSigS, rawSigBuf+DSAsigCompLen, DSAsigCompLen) <= 0) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Error converting signature to raw buffer");
    }

    // Now convert to Base 64

    BIO * b64 = BIO_new(BIO_f_base64());
    BIO * bmem = BIO_new(BIO_s_mem());

    BIO_set_mem_eof_return(bmem, 0);
    b64 = BIO_push(b64, bmem);

    // Translate signature from Base64

    BIO_write(b64, rawSigBuf, 2*DSAsigCompLen);
    BIO_flush(b64);

    unsigned int sigValLen = BIO_read(bmem, base64SignatureBuf, base64SignatureBufLen);

    BIO_free_all(b64);

    if (sigValLen <= 0) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Error base64 encoding signature");
    }

    return sigValLen;

}

XSECCryptoKey * OpenSSLCryptoKeyDSA::clone() const {

    OpenSSLCryptoKeyDSA * ret;

    XSECnew(ret, OpenSSLCryptoKeyDSA);

    ret->mp_dsaKey = DSA_new();

    // Duplicate parameters

    const BIGNUM *p=NULL, *q=NULL, *g=NULL;
    DSA_get0_pqg(mp_dsaKey, &p, &q, &g);

    if (p && q && g) // DSA_set0_pqg only works if all three params are non zero
        DSA_set0_pqg(ret->mp_dsaKey, BN_dup(p), BN_dup(q), BN_dup(g));

    const BIGNUM *oldPub= NULL, *oldPriv=NULL;
    DSA_get0_key(mp_dsaKey, &oldPub, &oldPriv);

    if (oldPub) {

        // DSA_setKey requires non-null Public

        DSA_set0_key(ret->mp_dsaKey, BN_dup(oldPub), (oldPriv?BN_dup(oldPriv):NULL));

    }
    return ret;
}

#endif /* XSEC_HAVE_OPENSSL */
<commit_msg>SANTUARIO-498 - DSA signature generation is unreliable<commit_after>/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

/*
 * XSEC
 *
 * OpenSSLCryptoKeyDSA := DSA Keys
 *
 * Author(s): Berin Lautenbach
 *
 * $Id$
 *
 */
#include <xsec/framework/XSECDefs.hpp>
#if defined (XSEC_HAVE_OPENSSL)

#include <xsec/enc/OpenSSL/OpenSSLSupport.hpp>
#include <xsec/enc/OpenSSL/OpenSSLCryptoKeyDSA.hpp>
#include <xsec/enc/OpenSSL/OpenSSLCryptoBase64.hpp>
#include <xsec/enc/XSECCryptoException.hpp>
#include <xsec/enc/XSECCryptoUtils.hpp>
#include <xsec/framework/XSECError.hpp>

#include <xercesc/util/Janitor.hpp>

XSEC_USING_XERCES(ArrayJanitor);

#include <openssl/dsa.h>

OpenSSLCryptoKeyDSA::OpenSSLCryptoKeyDSA() : mp_dsaKey(NULL), mp_accumP(NULL), mp_accumQ(NULL), mp_accumG(NULL) {
};

OpenSSLCryptoKeyDSA::~OpenSSLCryptoKeyDSA() {

    // If we have a DSA, delete it
    // OpenSSL will ensure the memory holding any private key is freed.

    if (mp_dsaKey)
        DSA_free(mp_dsaKey);

    if (mp_accumG)
        BN_free(mp_accumG);

    if (mp_accumP)
        BN_free(mp_accumP);

    if (mp_accumQ)
        BN_free(mp_accumQ);
};

const XMLCh* OpenSSLCryptoKeyDSA::getProviderName() const {
	return DSIGConstants::s_unicodeStrPROVOpenSSL;
}

// Generic key functions

XSECCryptoKey::KeyType OpenSSLCryptoKeyDSA::getKeyType() const {

    // Find out what we have
    if (mp_dsaKey == NULL)
        return KEY_NONE;

    if (DSA_get0_privkey(mp_dsaKey) != NULL && DSA_get0_pubkey(mp_dsaKey) != NULL)
        return KEY_DSA_PAIR;

    if (DSA_get0_privkey(mp_dsaKey) != NULL)
        return KEY_DSA_PRIVATE;

    if (DSA_get0_pubkey(mp_dsaKey) != NULL)
        return KEY_DSA_PUBLIC;

    return KEY_NONE;

}

void OpenSSLCryptoKeyDSA::loadPBase64BigNums(const char * b64, unsigned int len)  {

    setPBase(OpenSSLCryptoBase64::b642BN((char *) b64, len));

}

void OpenSSLCryptoKeyDSA::setPBase(BIGNUM  * p) {

    if (mp_dsaKey == NULL)
        mp_dsaKey = DSA_new();

#if (OPENSSL_VERSION_NUMBER < 0x10100000L)

    // Do it immediately
    mp_dsaKey->p = p;

#else
    // Save it for later
    if (mp_accumP != NULL)
        BN_free(mp_accumP);

    mp_accumP = p;

    commitPQG();

#endif

}

void OpenSSLCryptoKeyDSA::loadQBase64BigNums(const char * b64, unsigned int len) {

    setQBase(OpenSSLCryptoBase64::b642BN((char *) b64, len));

}

void OpenSSLCryptoKeyDSA::setQBase(BIGNUM  * q) {

    if (mp_dsaKey == NULL)
        mp_dsaKey = DSA_new();

#if (OPENSSL_VERSION_NUMBER <   0x10100000L)

    mp_dsaKey->q = q;

#else
    if (mp_accumQ != NULL)
        BN_free(mp_accumQ);

    mp_accumQ = q;
    commitPQG();

#endif

}

void OpenSSLCryptoKeyDSA::loadGBase64BigNums(const char * b64, unsigned int len) {

    setGBase(OpenSSLCryptoBase64::b642BN((char *) b64, len));

}

void OpenSSLCryptoKeyDSA::setGBase(BIGNUM  * g) {

    if (mp_dsaKey == NULL)
        mp_dsaKey = DSA_new();

#if (OPENSSL_VERSION_NUMBER <   0x10100000L)

    mp_dsaKey->g = g;

#else
    if (mp_accumG != NULL)
        BN_free(mp_accumG);

    mp_accumG = g;
    commitPQG();

#endif

}

#if (OPENSSL_VERSION_NUMBER >=  0x10100000L)
void OpenSSLCryptoKeyDSA::commitPQG() {

    if (mp_accumP != NULL && mp_accumQ != NULL && mp_accumG != NULL) {

        DSA_set0_pqg(mp_dsaKey, mp_accumP, mp_accumQ, mp_accumG);
        mp_accumP = NULL;
        mp_accumQ = NULL;
        mp_accumG = NULL;

    }
}
#endif

void OpenSSLCryptoKeyDSA::loadYBase64BigNums(const char * b64, unsigned int len) {

    if (mp_dsaKey == NULL)
        mp_dsaKey = DSA_new();

    BIGNUM *newPub = OpenSSLCryptoBase64::b642BN((char *) b64, len);
    const BIGNUM *oldPriv;
    DSA_get0_key(mp_dsaKey, NULL, &oldPriv);

    DSA_set0_key(mp_dsaKey, newPub, (oldPriv?BN_dup(oldPriv):NULL));
}

void OpenSSLCryptoKeyDSA::loadJBase64BigNums(const char * b64, unsigned int len) {

    if (mp_dsaKey == NULL)
        mp_dsaKey = DSA_new();

    // Do nothing
}

// "Hidden" OpenSSL functions

OpenSSLCryptoKeyDSA::OpenSSLCryptoKeyDSA(EVP_PKEY *k) : mp_accumP(NULL), mp_accumQ(NULL), mp_accumG(NULL) {

    // Create a new key to be loaded as we go

    mp_dsaKey = DSA_new();
    mp_accumG = NULL;
    mp_accumP = NULL;
    mp_accumQ = NULL;

    if (k == NULL || EVP_PKEY_id(k) != EVP_PKEY_DSA)
        return; // Nothing to do with us

    const BIGNUM *otherP = NULL, *otherQ = NULL, *otherG = NULL;
    DSA_get0_pqg(EVP_PKEY_get0_DSA(k), &otherP, &otherQ, &otherG);

    if (otherP != NULL && otherQ != NULL && otherG != NULL) {
        DSA_set0_pqg(mp_dsaKey, BN_dup(otherP), BN_dup(otherQ), BN_dup(otherG));
    }

    const BIGNUM *otherPriv = NULL, *otherPub = NULL;
    DSA_get0_key(EVP_PKEY_get0_DSA(k), &otherPub, &otherPriv);

    if (otherPub != NULL) {

        BIGNUM *newPriv = NULL;

        if (otherPriv != NULL)
            newPriv = BN_dup(otherPriv);

        DSA_set0_key(mp_dsaKey, BN_dup(otherPub), newPriv);

    }
}

// --------------------------------------------------------------------------------
//           Verify a signature encoded as a Base64 string
// --------------------------------------------------------------------------------

bool OpenSSLCryptoKeyDSA::verifyBase64Signature(unsigned char * hashBuf,
                                 unsigned int hashLen,
                                 char * base64Signature,
                                 unsigned int sigLen) const {

    // Use the currently loaded key to validate the Base64 encoded signature

    if (mp_dsaKey == NULL) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Attempt to validate signature with empty key");
    }

    XSECCryptoKey::KeyType keyType = getKeyType();
    if (keyType != KEY_DSA_PAIR && keyType != KEY_DSA_PUBLIC) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Attempt to validate signature without public key");
    }

    char* cleanedBase64Signature;
    unsigned int cleanedBase64SignatureLen = 0;

    cleanedBase64Signature =
        XSECCryptoBase64::cleanBuffer(base64Signature, sigLen, cleanedBase64SignatureLen);
    ArrayJanitor<char> j_cleanedBase64Signature(cleanedBase64Signature);

    int sigValLen;
    unsigned char* sigVal = new unsigned char[sigLen + 1];
    ArrayJanitor<unsigned char> j_sigVal(sigVal);

    EvpEncodeCtxRAII dctx;

    if (!dctx.of()) {
        throw XSECCryptoException(XSECCryptoException::ECError,
            "OpenSSL:DSA - allocation fail during Context Creation");
    }

    EVP_DecodeInit(dctx.of());

    int rc = EVP_DecodeUpdate(dctx.of(),
                          sigVal,
                          &sigValLen,
                          (unsigned char *) cleanedBase64Signature,
                          cleanedBase64SignatureLen);

    if (rc < 0) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Error during Base64 Decode");
    }

    int t = 0;

    EVP_DecodeFinal(dctx.of(), &sigVal[sigValLen], &t);

    sigValLen += t;

    // Translate to BNs and thence to DSA_SIG
    BIGNUM * R;
    BIGNUM * S;

    if (sigValLen == 40) {

        R = BN_bin2bn(sigVal, 20, NULL);
        S = BN_bin2bn(&sigVal[20], 20, NULL);
    }
    else {

        unsigned char rb[20];
        unsigned char sb[20];

        if (sigValLen == 46 && ASN2DSASig(sigVal, rb, sb) == true) {

            R = BN_bin2bn(rb, 20, NULL);
            S = BN_bin2bn(sb, 20, NULL);

        }

        else {

            throw XSECCryptoException(XSECCryptoException::DSAError,
                "OpenSSL:DSA - Signature Length incorrect");
        }
    }

    DSA_SIG * dsa_sig = DSA_SIG_new();

    DSA_SIG_set0(dsa_sig, BN_dup(R), BN_dup(S));

    BN_free(R);
    BN_free(S);

    // Now we have a signature and a key - lets check

    int err = DSA_do_verify(hashBuf, hashLen, dsa_sig, mp_dsaKey);

    DSA_SIG_free(dsa_sig);

    if (err < 0) {

        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Error validating signature");
    }

    return (err == 1);

}

// --------------------------------------------------------------------------------
//           Sign and encode result as a Base64 string
// --------------------------------------------------------------------------------

unsigned int OpenSSLCryptoKeyDSA::signBase64Signature(unsigned char * hashBuf,
        unsigned int hashLen,
        char * base64SignatureBuf,
        unsigned int base64SignatureBufLen) const {

    // Sign a pre-calculated hash using this key

    if (mp_dsaKey == NULL) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Attempt to sign data with empty key");
    }

    KeyType keyType = getKeyType();
    if (keyType != KEY_DSA_PAIR && keyType != KEY_DSA_PRIVATE) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Attempt to sign data without private key");
    }

    DSA_SIG* dsa_sig = DSA_do_sign(hashBuf, hashLen, mp_dsaKey);

    if (dsa_sig == NULL) {

        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Error signing data");

    }

    // Now turn the signature into a base64 string

    const BIGNUM *dsaSigR;
    const BIGNUM *dsaSigS;

    DSA_SIG_get0(dsa_sig, &dsaSigR, &dsaSigS);

#if (OPENSSL_VERSION_NUMBER >=  0x10100000L)
    const int DSAsigCompLen = 20; // XMLDSIG spec 6.4.1
    unsigned char rawSigBuf[2*DSAsigCompLen];

    if (BN_bn2binpad(dsaSigR, rawSigBuf, DSAsigCompLen) <= 0) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Error converting signature to raw buffer");
    }

    if (BN_bn2binpad(dsaSigS, rawSigBuf+DSAsigCompLen, DSAsigCompLen) <= 0) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Error converting signature to raw buffer");
    }
#else
    // See SANTUARIO-498.
    // This code is apparently wrong, but I do not have a fix for OpenSSL < 1.1
    unsigned char* rawSigBuf = new unsigned char[(BN_num_bits(dsaSigR) + BN_num_bits(dsaSigS) + 7) / 8];
    ArrayJanitor<unsigned char> j_sigbuf(rawSigBuf);

    unsigned int rawLen = BN_bn2bin(dsaSigR, rawSigBuf);

    if (rawLen <= 0) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Error converting signature to raw buffer");
    }

    unsigned int rawLenS = BN_bn2bin(dsaSigS, (unsigned char *) &rawSigBuf[rawLen]);

    if (rawLenS <= 0) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Error converting signature to raw buffer");
    }

    rawLen += rawLenS;
#endif

    // Now convert to Base 64

    BIO * b64 = BIO_new(BIO_f_base64());
    BIO * bmem = BIO_new(BIO_s_mem());

    BIO_set_mem_eof_return(bmem, 0);
    b64 = BIO_push(b64, bmem);

    // Translate signature from Base64

#if (OPENSSL_VERSION_NUMBER >=  0x10100000L)
    BIO_write(b64, rawSigBuf, 2*DSAsigCompLen);
#else
    BIO_write(b64, rawSigBuf, rawLen);
#endif
    BIO_flush(b64);

    unsigned int sigValLen = BIO_read(bmem, base64SignatureBuf, base64SignatureBufLen);

    BIO_free_all(b64);

    if (sigValLen <= 0) {
        throw XSECCryptoException(XSECCryptoException::DSAError,
            "OpenSSL:DSA - Error base64 encoding signature");
    }

    return sigValLen;
}

XSECCryptoKey * OpenSSLCryptoKeyDSA::clone() const {

    OpenSSLCryptoKeyDSA * ret;

    XSECnew(ret, OpenSSLCryptoKeyDSA);

    ret->mp_dsaKey = DSA_new();

    // Duplicate parameters

    const BIGNUM *p=NULL, *q=NULL, *g=NULL;
    DSA_get0_pqg(mp_dsaKey, &p, &q, &g);

    if (p && q && g) // DSA_set0_pqg only works if all three params are non zero
        DSA_set0_pqg(ret->mp_dsaKey, BN_dup(p), BN_dup(q), BN_dup(g));

    const BIGNUM *oldPub= NULL, *oldPriv=NULL;
    DSA_get0_key(mp_dsaKey, &oldPub, &oldPriv);

    if (oldPub) {

        // DSA_setKey requires non-null Public

        DSA_set0_key(ret->mp_dsaKey, BN_dup(oldPub), (oldPriv?BN_dup(oldPriv):NULL));

    }
    return ret;
}

#endif /* XSEC_HAVE_OPENSSL */
<|endoftext|>


<commit_before>/**************************************************************************************/
/*                                                                                */
/*  Visualization Library                                                             */
/*  http://www.visualizationlibrary.org                                               */
/*                                                                                */
/*  Copyright (c) 2005-2011, Michele Bosi                                             */
/*  All rights reserved.                                                              */
/*                                                                                */
/*  Redistribution and use in source and binary forms, with or without modification,  */
/*  are permitted provided that the following conditions are met:                     */
/*                                                                                */
/*  - Redistributions of source code must retain the above copyright notice, this     */
/*  list of conditions and the following disclaimer.                                  */
/*                                                                                */
/*  - Redistributions in binary form must reproduce the above copyright notice, this  */
/*  list of conditions and the following disclaimer in the documentation and/or       */
/*  other materials provided with the distribution.                                   */
/*                                                                                */
/*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND   */
/*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED     */
/*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE            */
/*  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR  */
/*  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    */
/*  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      */
/*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON    */
/*  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT           */
/*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS     */
/*  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                      */
/*                                                                                */
/**************************************************************************************/

#ifndef Framebuffer_INCLUDE_ONCE
#define Framebuffer_INCLUDE_ONCE

#include <vlCore/vlnamespace.hpp>
#include <vlCore/Object.hpp>
#include <vlGraphics/OpenGL.hpp>
#include <vector>

namespace vl
{
  class OpenGLContext;
  //-----------------------------------------------------------------------------
  // Framebuffer
  //-----------------------------------------------------------------------------
  /** The Framebuffer class defines an abstract 'surface' where OpenGL can render into.
   * \sa OpenGLContext::framebuffer() and FramebufferObject
   */
  class VLGRAPHICS_EXPORT Framebuffer: public Object
  {
    VL_INSTRUMENT_CLASS(vl::Framebuffer, Object)

    friend class OpenGLContext;

  protected:
    /** Constructor. */
    Framebuffer(): mWidth(0), mHeight(0), mOpenGLContext(NULL)
    {
      VL_DEBUG_SET_OBJECT_NAME()
      setDrawBuffer(RDB_BACK_LEFT);
      setReadBuffer(RDB_BACK_LEFT);
    }

    /** Constructor. */
    Framebuffer(OpenGLContext* ctx, int w, int h): mWidth(w), mHeight(h), mOpenGLContext(ctx)
    {
      VL_DEBUG_SET_OBJECT_NAME()
      setDrawBuffer(RDB_BACK_LEFT);
      setReadBuffer(RDB_BACK_LEFT);
    }

  public:
    /** The OpenGLContext bound to a render target. */
    OpenGLContext* openglContext() { return mOpenGLContext; }

    /** The OpenGLContext bound to a render target. */
    const OpenGLContext* openglContext() const { return mOpenGLContext; }

    /** The width of a render target. */
    int width() const { return mWidth; }

    /** The height of a render target. */
    int height() const { return mHeight; }

    /** The width of a render target. */
    void setWidth(int width) { mWidth = width; }

    /** The height of a render target. */
    void setHeight(int height) { mHeight = height; }

    /** The framebuffer object id as used by glBindFramebuffer, Framebuffer::handle() always returns 0, i.e., the standard OpenGL framebuffer. */
    virtual GLuint handle() const { return 0; }

    /** Activates the Framebuffer by calling bindFramebuffer() and bindDrawBuffers() */
    void activate(EFramebufferBind target = FBB_FRAMEBUFFER)
    {
      bindFramebuffer(target);
    }

    /**
      * Calls glBindFramebuffer(target, 0) thus activating the the framebuffer 0, that is, the normal OpenGL buffers.
      * \note This method is overridden in FramebufferObject in order to activate the appropriate framebuffer object.
      */
    virtual void bindFramebuffer(EFramebufferBind target = FBB_FRAMEBUFFER)
    {
      VL_CHECK_OGL()

      // the base render target is the framebuffer 0, that is, the normal OpenGL buffers
      VL_glBindFramebuffer(target, 0); VL_CHECK_OGL()

#if defined(VL_OPENGL)
      // bind draw buffers
      bindDrawBuffers();

      // bind read buffer
      bindReadBuffer();
#endif

      VL_CHECK_OGL()
    }

    /** Binds to the currently active framebuffer object (including the 0 one) the read buffer specified by setReadBuffer(). */
    void bindReadBuffer();

    /** Binds to the currently active framebuffer object (including the 0 one) the draw buffers specified by setDrawBuffers(). */
    void bindDrawBuffers() const;

    /** Returns \p true if the draw buffers bound to this render target are legal for this render target type. */
    bool checkDrawBuffers() const;

    /** Specifies the color buffer to be drawn into. */
    void setDrawBuffer(EReadDrawBuffer draw_buffer)
    {
      mDrawBuffers.clear();
      mDrawBuffers.push_back(draw_buffer);
    }

    /** Specifies a list of color buffers to be drawn into. */
    void setDrawBuffers(EReadDrawBuffer draw_buffer1, EReadDrawBuffer draw_buffer2)
    {
      mDrawBuffers.clear();
      mDrawBuffers.push_back(draw_buffer1);
      mDrawBuffers.push_back(draw_buffer2);
    }

    /** Specifies a list of color buffers to be drawn into. */
    void setDrawBuffers(EReadDrawBuffer draw_buffer1, EReadDrawBuffer draw_buffer2, EReadDrawBuffer draw_buffer3)
    {
      mDrawBuffers.clear();
      mDrawBuffers.push_back(draw_buffer1);
      mDrawBuffers.push_back(draw_buffer2);
      mDrawBuffers.push_back(draw_buffer3);
    }

    /** Specifies a list of color buffers to be drawn into. */
    void setDrawBuffers(EReadDrawBuffer draw_buffer1, EReadDrawBuffer draw_buffer2, EReadDrawBuffer draw_buffer3, EReadDrawBuffer draw_buffer4)
    {
      mDrawBuffers.clear();
      mDrawBuffers.push_back(draw_buffer1);
      mDrawBuffers.push_back(draw_buffer2);
      mDrawBuffers.push_back(draw_buffer3);
      mDrawBuffers.push_back(draw_buffer4);
    }

    /** Specifies a list of color buffers to be drawn into. */
    void setDrawBuffers(const std::vector< EReadDrawBuffer >& draw_buffers) { mDrawBuffers = draw_buffers; }

    /** The color buffers to be drawn into. */
    const std::vector< EReadDrawBuffer >& drawBuffers() { return mDrawBuffers; }

    /** The read-buffer bound when the render target is activated. */
    EReadDrawBuffer readBuffer() const { return mReadBuffer; }

    /** The read-buffer bound when the render target is activated. */
    void setReadBuffer(EReadDrawBuffer read_buffer) { mReadBuffer = read_buffer; }

  private:
    std::vector< EReadDrawBuffer > mDrawBuffers;
    EReadDrawBuffer mReadBuffer;
    int mWidth;
    int mHeight;
    OpenGLContext* mOpenGLContext;
  };
  //------------------------------------------------------------------------------
}

#endif
<commit_msg>Framebuffer can now be publicly constructed.<commit_after>/**************************************************************************************/
/*                                                                                */
/*  Visualization Library                                                             */
/*  http://www.visualizationlibrary.org                                               */
/*                                                                                */
/*  Copyright (c) 2005-2011, Michele Bosi                                             */
/*  All rights reserved.                                                              */
/*                                                                                */
/*  Redistribution and use in source and binary forms, with or without modification,  */
/*  are permitted provided that the following conditions are met:                     */
/*                                                                                */
/*  - Redistributions of source code must retain the above copyright notice, this     */
/*  list of conditions and the following disclaimer.                                  */
/*                                                                                */
/*  - Redistributions in binary form must reproduce the above copyright notice, this  */
/*  list of conditions and the following disclaimer in the documentation and/or       */
/*  other materials provided with the distribution.                                   */
/*                                                                                */
/*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND   */
/*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED     */
/*  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE            */
/*  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR  */
/*  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    */
/*  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;      */
/*  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON    */
/*  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT           */
/*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS     */
/*  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                      */
/*                                                                                */
/**************************************************************************************/

#ifndef Framebuffer_INCLUDE_ONCE
#define Framebuffer_INCLUDE_ONCE

#include <vlCore/vlnamespace.hpp>
#include <vlCore/Object.hpp>
#include <vlGraphics/OpenGL.hpp>
#include <vector>

namespace vl
{
  class OpenGLContext;
  //-----------------------------------------------------------------------------
  // Framebuffer
  //-----------------------------------------------------------------------------
  /** The Framebuffer class defines an abstract 'surface' where OpenGL can render into.
   * \sa OpenGLContext::framebuffer() and FramebufferObject
   */
  class VLGRAPHICS_EXPORT Framebuffer: public Object
  {
    VL_INSTRUMENT_CLASS(vl::Framebuffer, Object)

    friend class OpenGLContext;

  public:
    /** Constructor, see also OpenGLContext::framebuffer(). */
    Framebuffer(OpenGLContext* ctx, int w, int h, EReadDrawBuffer draw_buffer, EReadDrawBuffer read_buffer):
    mOpenGLContext(ctx), mWidth(w), mHeight(h)
    {
      VL_DEBUG_SET_OBJECT_NAME()
      setDrawBuffer(draw_buffer);
      setReadBuffer(read_buffer);
    }

    /** The OpenGLContext bound to a render target. */
    OpenGLContext* openglContext() { return mOpenGLContext; }

    /** The OpenGLContext bound to a render target. */
    const OpenGLContext* openglContext() const { return mOpenGLContext; }

    /** The width of a render target. */
    int width() const { return mWidth; }

    /** The height of a render target. */
    int height() const { return mHeight; }

    /** The width of a render target. */
    void setWidth(int width) { mWidth = width; }

    /** The height of a render target. */
    void setHeight(int height) { mHeight = height; }

    /** The framebuffer object id as used by glBindFramebuffer, Framebuffer::handle() always returns 0, i.e., the standard OpenGL framebuffer. */
    virtual GLuint handle() const { return 0; }

    /** Activates the Framebuffer by calling bindFramebuffer() and bindDrawBuffers() */
    void activate(EFramebufferBind target = FBB_FRAMEBUFFER)
    {
      bindFramebuffer(target);
    }

    /**
      * Calls glBindFramebuffer(target, 0) thus activating the the framebuffer 0, that is, the normal OpenGL buffers.
      * \note This method is overridden in FramebufferObject in order to activate the appropriate framebuffer object.
      */
    virtual void bindFramebuffer(EFramebufferBind target = FBB_FRAMEBUFFER)
    {
      VL_CHECK_OGL()

      // the base render target is the framebuffer 0, that is, the normal OpenGL buffers
      VL_glBindFramebuffer(target, 0); VL_CHECK_OGL()

#if defined(VL_OPENGL)
      // bind draw buffers
      bindDrawBuffers();

      // bind read buffer
      bindReadBuffer();
#endif

      VL_CHECK_OGL()
    }

    /** Binds to the currently active framebuffer object (including the 0 one) the read buffer specified by setReadBuffer(). */
    void bindReadBuffer();

    /** Binds to the currently active framebuffer object (including the 0 one) the draw buffers specified by setDrawBuffers(). */
    void bindDrawBuffers() const;

    /** Returns \p true if the draw buffers bound to this render target are legal for this render target type. */
    bool checkDrawBuffers() const;

    /** Specifies the color buffer to be drawn into. */
    void setDrawBuffer(EReadDrawBuffer draw_buffer)
    {
      mDrawBuffers.clear();
      mDrawBuffers.push_back(draw_buffer);
    }

    /** Specifies a list of color buffers to be drawn into. */
    void setDrawBuffers(EReadDrawBuffer draw_buffer1, EReadDrawBuffer draw_buffer2)
    {
      mDrawBuffers.clear();
      mDrawBuffers.push_back(draw_buffer1);
      mDrawBuffers.push_back(draw_buffer2);
    }

    /** Specifies a list of color buffers to be drawn into. */
    void setDrawBuffers(EReadDrawBuffer draw_buffer1, EReadDrawBuffer draw_buffer2, EReadDrawBuffer draw_buffer3)
    {
      mDrawBuffers.clear();
      mDrawBuffers.push_back(draw_buffer1);
      mDrawBuffers.push_back(draw_buffer2);
      mDrawBuffers.push_back(draw_buffer3);
    }

    /** Specifies a list of color buffers to be drawn into. */
    void setDrawBuffers(EReadDrawBuffer draw_buffer1, EReadDrawBuffer draw_buffer2, EReadDrawBuffer draw_buffer3, EReadDrawBuffer draw_buffer4)
    {
      mDrawBuffers.clear();
      mDrawBuffers.push_back(draw_buffer1);
      mDrawBuffers.push_back(draw_buffer2);
      mDrawBuffers.push_back(draw_buffer3);
      mDrawBuffers.push_back(draw_buffer4);
    }

    /** Specifies a list of color buffers to be drawn into. */
    void setDrawBuffers(const std::vector< EReadDrawBuffer >& draw_buffers) { mDrawBuffers = draw_buffers; }

    /** The color buffers to be drawn into. */
    const std::vector< EReadDrawBuffer >& drawBuffers() { return mDrawBuffers; }

    /** The read-buffer bound when the render target is activated. */
    EReadDrawBuffer readBuffer() const { return mReadBuffer; }

    /** The read-buffer bound when the render target is activated. */
    void setReadBuffer(EReadDrawBuffer read_buffer) { mReadBuffer = read_buffer; }

  private:
    std::vector< EReadDrawBuffer > mDrawBuffers;
    EReadDrawBuffer mReadBuffer;
    OpenGLContext* mOpenGLContext;
    int mWidth;
    int mHeight;
  };
  //------------------------------------------------------------------------------
}

#endif
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2013 libbitcoin developers (see AUTHORS)
 *
 * This file is part of libbitcoin.
 *
 * libbitcoin is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
#include <bitcoin/poller.hpp>

#include <bitcoin/utility/logger.hpp>

namespace libbitcoin {

using std::placeholders::_1;
using std::placeholders::_2;

poller::poller(threadpool& pool, blockchain& chain)
  : strand_(pool.service()), chain_(chain),
    last_locator_begin_(null_hash), last_hash_stop_(null_hash),
    last_block_hash_(null_hash)
{
}

void poller::query(channel_ptr node)
{
    fetch_block_locator(chain_,
        std::bind(&poller::initial_ask_blocks,
            this, _1, _2, node));
}

void poller::monitor(channel_ptr node)
{
    node->subscribe_inventory(
        strand_.wrap(std::bind(&poller::receive_inv,
            this, _1, _2, node)));
    node->subscribe_block(
        std::bind(&poller::receive_block,
            this, _1, _2, node));
}

void poller::initial_ask_blocks(const std::error_code& ec,
    const block_locator_type& locator, channel_ptr node)
{
    if (ec)
    {
        log_error(LOG_POLLER)
            << "Fetching initial block locator: " << ec.message();
        return;
    }
    strand_.dispatch(std::bind(&poller::ask_blocks,
        this, ec, locator, null_hash, node));
}

void handle_send_packet(const std::error_code& ec)
{
    if (ec)
        log_error(LOG_POLLER) << "Send problem: " << ec.message();
}

void poller::receive_inv(const std::error_code& ec,
    const inventory_type& packet, channel_ptr node)
{
    if (ec)
    {
        log_error(LOG_POLLER) << "Received bad inventory: " << ec.message();
        return;
    }
    // Filter out only block inventories
    get_data_type getdata;
    for (const inventory_vector_type& ivv: packet.inventories)
    {
        if (ivv.type != inventory_type_id::block)
            continue;
        // Already got this block
        if (ivv.hash == last_block_hash_)
            continue;
        getdata.inventories.push_back(ivv);
    }
    if (!getdata.inventories.empty())
    {
        last_block_hash_ = getdata.inventories.back().hash;
        node->send(getdata, handle_send_packet);
    }
    node->subscribe_inventory(
        strand_.wrap(std::bind(&poller::receive_inv,
            this, _1, _2, node)));
}

void poller::receive_block(const std::error_code& ec,
    const block_type& blk, channel_ptr node)
{
    if (ec)
    {
        log_error(LOG_POLLER) << "Received bad block: " << ec.message();
        return;
    }
    chain_.store(blk,
        std::bind(&poller::handle_store,
            this, _1, _2, hash_block_header(blk.header), node));
    node->subscribe_block(
        std::bind(&poller::receive_block,
            this, _1, _2, node));
}

void poller::handle_store(const std::error_code& ec, block_info info,
    const hash_digest& block_hash, channel_ptr node)
{
    // We need orphan blocks so we can do the next getblocks round
    if (ec && info.status != block_status::orphan)
    {
        log_error(LOG_POLLER)
            << "Storing block " << encode_hex(block_hash)
            << ": " << ec.message();
        return;
    }
    switch (info.status)
    {
        case block_status::orphan:
            // TODO: Make more efficient by storing block hash
            // and next time do not download orphan block again.
            // Remember to remove from list once block is no longer orphan
            fetch_block_locator(chain_,
                strand_.wrap(std::bind(&poller::ask_blocks,
                    this, _1, _2, block_hash, node)));
            break;

        case block_status::rejected:
            log_warning(LOG_POLLER)
                << "Rejected block " << encode_hex(block_hash);
            break;

        case block_status::confirmed:
            log_info(LOG_POLLER)
                << "Block #" << info.height << " " << encode_hex(block_hash);
            break;
    }
}

void poller::ask_blocks(const std::error_code& ec,
    const block_locator_type& locator,
    const hash_digest& hash_stop, channel_ptr node)
{
    if (ec)
    {
        log_error(LOG_POLLER) << "Ask for blocks: " << ec.message();
        return;
    }
    if (last_locator_begin_ == locator.front() && last_hash_stop_ == hash_stop)
    {
        log_debug(LOG_POLLER) << "Skipping duplicate ask blocks: "
            << encode_hex(locator.front());
        return;
    }
    get_blocks_type packet;
    packet.start_hashes = locator;
    packet.hash_stop = hash_stop;
    node->send(packet, std::bind(&handle_send_packet, _1));
    last_locator_begin_ = locator.front();
    last_hash_stop_ = hash_stop;
}

} // namespace libbitcoin

<commit_msg>downgrade error to warning (thanks Noel Maersk): http://lists.dyne.org/lurker/message/20140113.093843.531c548b.en.html<commit_after>/*
 * Copyright (c) 2011-2013 libbitcoin developers (see AUTHORS)
 *
 * This file is part of libbitcoin.
 *
 * libbitcoin is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
#include <bitcoin/poller.hpp>

#include <bitcoin/utility/logger.hpp>

namespace libbitcoin {

using std::placeholders::_1;
using std::placeholders::_2;

poller::poller(threadpool& pool, blockchain& chain)
  : strand_(pool.service()), chain_(chain),
    last_locator_begin_(null_hash), last_hash_stop_(null_hash),
    last_block_hash_(null_hash)
{
}

void poller::query(channel_ptr node)
{
    fetch_block_locator(chain_,
        std::bind(&poller::initial_ask_blocks,
            this, _1, _2, node));
}

void poller::monitor(channel_ptr node)
{
    node->subscribe_inventory(
        strand_.wrap(std::bind(&poller::receive_inv,
            this, _1, _2, node)));
    node->subscribe_block(
        std::bind(&poller::receive_block,
            this, _1, _2, node));
}

void poller::initial_ask_blocks(const std::error_code& ec,
    const block_locator_type& locator, channel_ptr node)
{
    if (ec)
    {
        log_error(LOG_POLLER)
            << "Fetching initial block locator: " << ec.message();
        return;
    }
    strand_.dispatch(std::bind(&poller::ask_blocks,
        this, ec, locator, null_hash, node));
}

void handle_send_packet(const std::error_code& ec)
{
    if (ec)
        log_error(LOG_POLLER) << "Send problem: " << ec.message();
}

void poller::receive_inv(const std::error_code& ec,
    const inventory_type& packet, channel_ptr node)
{
    if (ec)
    {
        log_error(LOG_POLLER) << "Received bad inventory: " << ec.message();
        return;
    }
    // Filter out only block inventories
    get_data_type getdata;
    for (const inventory_vector_type& ivv: packet.inventories)
    {
        if (ivv.type != inventory_type_id::block)
            continue;
        // Already got this block
        if (ivv.hash == last_block_hash_)
            continue;
        getdata.inventories.push_back(ivv);
    }
    if (!getdata.inventories.empty())
    {
        last_block_hash_ = getdata.inventories.back().hash;
        node->send(getdata, handle_send_packet);
    }
    node->subscribe_inventory(
        strand_.wrap(std::bind(&poller::receive_inv,
            this, _1, _2, node)));
}

void poller::receive_block(const std::error_code& ec,
    const block_type& blk, channel_ptr node)
{
    if (ec)
    {
        log_error(LOG_POLLER) << "Received bad block: " << ec.message();
        return;
    }
    chain_.store(blk,
        std::bind(&poller::handle_store,
            this, _1, _2, hash_block_header(blk.header), node));
    node->subscribe_block(
        std::bind(&poller::receive_block,
            this, _1, _2, node));
}

void poller::handle_store(const std::error_code& ec, block_info info,
    const hash_digest& block_hash, channel_ptr node)
{
    // We need orphan blocks so we can do the next getblocks round
    if (ec && info.status != block_status::orphan)
    {
        log_warning(LOG_POLLER)
            << "Storing block " << encode_hex(block_hash)
            << ": " << ec.message();
        return;
    }
    switch (info.status)
    {
        case block_status::orphan:
            // TODO: Make more efficient by storing block hash
            // and next time do not download orphan block again.
            // Remember to remove from list once block is no longer orphan
            fetch_block_locator(chain_,
                strand_.wrap(std::bind(&poller::ask_blocks,
                    this, _1, _2, block_hash, node)));
            break;

        case block_status::rejected:
            log_warning(LOG_POLLER)
                << "Rejected block " << encode_hex(block_hash);
            break;

        case block_status::confirmed:
            log_info(LOG_POLLER)
                << "Block #" << info.height << " " << encode_hex(block_hash);
            break;
    }
}

void poller::ask_blocks(const std::error_code& ec,
    const block_locator_type& locator,
    const hash_digest& hash_stop, channel_ptr node)
{
    if (ec)
    {
        log_error(LOG_POLLER) << "Ask for blocks: " << ec.message();
        return;
    }
    if (last_locator_begin_ == locator.front() && last_hash_stop_ == hash_stop)
    {
        log_debug(LOG_POLLER) << "Skipping duplicate ask blocks: "
            << encode_hex(locator.front());
        return;
    }
    get_blocks_type packet;
    packet.start_hashes = locator;
    packet.hash_stop = hash_stop;
    node->send(packet, std::bind(&handle_send_packet, _1));
    last_locator_begin_ = locator.front();
    last_hash_stop_ = hash_stop;
}

} // namespace libbitcoin

<|endoftext|>


<commit_before>// Ylikuutio - A 3D game and simulation engine.
//
// Copyright (C) 2015-2020 Antti Nuortimo.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "entity.hpp"
#include "universe.hpp"
#include "parent_module.hpp"
#include "entity_struct.hpp"
#include "code/ylikuutio/config/setting_master.hpp"
#include "code/ylikuutio/config/setting.hpp"
#include "code/ylikuutio/config/setting_struct.hpp"
#include "code/ylikuutio/data/any_value.hpp"

// Include standard headers
#include <cstddef>       // std::size_t
#include <iostream>      // std::cout, std::cin, std::cerr
#include <limits>        // std::numeric_limits
#include <memory>        // std::make_shared, std::shared_ptr
#include <regex>         // std::regex, std::regex_match
#include <string>        // std::string
#include <unordered_map> // std::unordered_map
#include <vector>        // std::vector

namespace yli::ontology
{
    void Entity::bind_to_universe()
    {
        // Requirements:
        // `this->universe` must not be `nullptr`.
        yli::ontology::Universe* const universe = this->universe;

        if (universe == nullptr)
        {
            std::cerr << "ERROR: `Entity::bind_to_universe`: `universe` is `nullptr`!\n";
            return;
        }

        // Get `entityID` from the `Universe` and set pointer to this `Entity`.
        universe->bind_entity(this);
    }

    void Entity::bind_to_new_parent(yli::ontology::Entity* new_entity_parent)
    {
        // Do nothing.
        // `yli::ontology` classes which support binding must `override`
        // this `yli::ontology::Entity` base class implementation.
    }

    Entity::Entity(yli::ontology::Universe* const universe, const yli::ontology::EntityStruct& entity_struct)
        : parent_of_any_struct_entities(this)
    {
        // constructor.
        this->universe = universe;

        // Get `entityID` from `Universe` and set pointer to this `Entity`.
        this->bind_to_universe();

        this->childID = std::numeric_limits<std::size_t>::max(); // `std::numeric_limits<std::size_t>::max()` means that `childID` is not defined.

        this->prerender_callback = nullptr;
        this->postrender_callback = nullptr;
        this->setting_master = std::make_shared<yli::config::SettingMaster>(this);
        this->can_be_erased = false;
        this->should_be_rendered = false;

        this->set_local_name(entity_struct.local_name);

        if (this->universe != this)
        {
            this->set_global_name(entity_struct.global_name);

            this->should_be_rendered = (this->universe == nullptr ? false : !this->universe->get_is_headless());

            yli::config::SettingStruct should_be_rendered_setting_struct(std::make_shared<yli::data::AnyValue>(this->should_be_rendered));
            should_be_rendered_setting_struct.name = "should_be_rendered";
            should_be_rendered_setting_struct.activate_callback = &yli::config::Setting::activate_should_be_rendered;
            should_be_rendered_setting_struct.read_callback = &yli::config::Setting::read_should_be_rendered;
            should_be_rendered_setting_struct.should_ylikuutio_call_activate_callback_now = true;
            std::cout << "Executing `setting_master->create_setting(should_be_rendered_setting_struct);` ...\n";
            this->setting_master->create_setting(should_be_rendered_setting_struct);
        }
    }

    Entity::~Entity()
    {
        // destructor.
        std::cout << "`Entity` destructor called.\n";

        if (this->universe == nullptr)
        {
            std::cerr << "ERROR: `Entity::~Entity`: `this->universe` is `nullptr`.\n";
            return;
        }

        this->universe->unbind_entity(this->entityID);

        if (!this->global_name.empty())
        {
            // OK, this `Entity` had a global name, so it's global name shall be erased.
            this->universe->erase_entity(this->global_name);
        }

        // Local names must be erased in the destructors
        // of classes that inherit `yli::ontology::Entity`!
        // They can not be erased here in `Entity` destructor,
        // because `Entity` class does not keep track of the
        // parent. `Entity` only provides virtual function
        // `Entity::get_parent`, but that can not be called
        // from here.
    }

    void Entity::render()
    {
    }

    std::size_t Entity::get_childID() const
    {
        return this->childID;
    }

    std::string Entity::get_type() const
    {
        return this->type_string;
    }

    bool Entity::get_can_be_erased() const
    {
        return this->can_be_erased;
    }

    yli::ontology::Universe* Entity::get_universe() const
    {
        return this->universe;
    }

    yli::config::SettingMaster* Entity::get_setting_master() const
    {
        if (this->setting_master == nullptr)
        {
            std::cerr << "ERROR: `Entity::get_setting_master`: `this->setting_master` is `nullptr`.\n";
            return nullptr;
        }

        return this->setting_master.get();
    }

    bool Entity::is_entity(const std::string& name) const
    {
        return this->entity_map.count(name) == 1;
    }

    yli::ontology::Entity* Entity::get_entity(const std::string& name) const
    {
        if (this->entity_map.count(name) != 1)
        {
            return nullptr;
        }

        return this->entity_map.at(name);
    }

    std::string Entity::get_entity_names() const
    {
        std::string entity_names = "";

        std::vector<std::string> keys;
        keys.reserve(this->entity_map.size());

        for (auto it : this->entity_map)
        {
            if (!entity_names.empty())
            {
                entity_names += " ";
            }
            std::string key = static_cast<std::string>(it.first);
            entity_names += key;
        }

        return entity_names;
    }

    void Entity::add_entity(const std::string& name, yli::ontology::Entity* const entity)
    {
        if (!name.empty() && this->entity_map.count(name) == 0)
        {
            this->entity_map[name] = entity;
        }
    }

    void Entity::erase_entity(const std::string& name)
    {
        if (this->entity_map.count(name) == 1)
        {
            if (this->entity_map[name]->get_can_be_erased())
            {
                this->entity_map.erase(name);
            }
        }
    }

    void Entity::prerender() const
    {
        // Requirements:
        // `this->prerender_callback` must not be `nullptr`.
        // `this->universe` must not be `nullptr`.
        // `this->universe->setting_master` must not be `nullptr`.

        if (this->prerender_callback != nullptr &&
                this->universe != nullptr &&
                this->universe->setting_master != nullptr)
        {
            this->prerender_callback(this->universe, this->universe->setting_master.get());
        }
    }

    void Entity::postrender() const
    {
        // Requirements:
        // `this->postrender_callback` must not be `nullptr`.
        // `this->universe` must not be `nullptr`.
        // `this->universe->setting_master` must not be `nullptr`.

        if (this->postrender_callback != nullptr &&
                this->universe != nullptr &&
                this->universe->setting_master != nullptr)
        {
            this->postrender_callback(this->universe, this->universe->setting_master.get());
        }
    }

    std::string Entity::get_global_name() const
    {
        return this->global_name;
    }

    std::string Entity::get_local_name() const
    {
        return this->local_name;
    }

    void Entity::set_global_name(const std::string& global_name)
    {
        // Requirements:
        // `this->universe` must not be `nullptr`.
        // `global_name` must not be already in use.

        if (global_name.empty())
        {
            return;
        }

        if (!std::regex_match(global_name, std::regex("[a-zA-Z][a-zA-Z0-9_-]*")))
        {
            return;
        }

        if (this->universe == nullptr)
        {
            return;
        }

        if (this->universe->is_entity(global_name))
        {
            // The global name is already in use.
            return;
        }

        // Erase old global name.
        this->universe->erase_entity(this->global_name);

        // Set new global name.
        this->global_name = global_name;
        this->universe->add_entity(global_name, this);

        if (this->universe == this->get_parent())
        {
            // `Universe` is the parent of this `Entity`.
            // Therefore, local name and global name
            // are the same for this `Entity`.
            this->local_name = global_name;
        }
    }

    void Entity::set_local_name(const std::string& local_name)
    {
        if (!std::regex_match(local_name, std::regex("[a-zA-Z][a-zA-Z0-9_-]*")))
        {
            return;
        }

        yli::ontology::Entity* const parent = this->get_parent();

        if (parent == nullptr)
        {
            return;
        }

        if (parent->is_entity(local_name))
        {
            // The name is in use.
            return;
        }

        // Erase old local name.
        parent->erase_entity(this->local_name);

        // Set new local name.
        parent->add_entity(local_name, this);
        this->local_name = local_name;

        if (parent == this->universe)
        {
            // Special case: this `Entity` is a child of the `Universe`!
            // Therefore, the local name is also the global name,
            // and vice versa. This means that the requested
            // global name must not be in use.
            this->global_name = local_name;
        }
    }
}
<commit_msg>`Entity::set_local_name`: check `local_name` for emptiness.<commit_after>// Ylikuutio - A 3D game and simulation engine.
//
// Copyright (C) 2015-2020 Antti Nuortimo.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "entity.hpp"
#include "universe.hpp"
#include "parent_module.hpp"
#include "entity_struct.hpp"
#include "code/ylikuutio/config/setting_master.hpp"
#include "code/ylikuutio/config/setting.hpp"
#include "code/ylikuutio/config/setting_struct.hpp"
#include "code/ylikuutio/data/any_value.hpp"

// Include standard headers
#include <cstddef>       // std::size_t
#include <iostream>      // std::cout, std::cin, std::cerr
#include <limits>        // std::numeric_limits
#include <memory>        // std::make_shared, std::shared_ptr
#include <regex>         // std::regex, std::regex_match
#include <string>        // std::string
#include <unordered_map> // std::unordered_map
#include <vector>        // std::vector

namespace yli::ontology
{
    void Entity::bind_to_universe()
    {
        // Requirements:
        // `this->universe` must not be `nullptr`.
        yli::ontology::Universe* const universe = this->universe;

        if (universe == nullptr)
        {
            std::cerr << "ERROR: `Entity::bind_to_universe`: `universe` is `nullptr`!\n";
            return;
        }

        // Get `entityID` from the `Universe` and set pointer to this `Entity`.
        universe->bind_entity(this);
    }

    void Entity::bind_to_new_parent(yli::ontology::Entity* new_entity_parent)
    {
        // Do nothing.
        // `yli::ontology` classes which support binding must `override`
        // this `yli::ontology::Entity` base class implementation.
    }

    Entity::Entity(yli::ontology::Universe* const universe, const yli::ontology::EntityStruct& entity_struct)
        : parent_of_any_struct_entities(this)
    {
        // constructor.
        this->universe = universe;

        // Get `entityID` from `Universe` and set pointer to this `Entity`.
        this->bind_to_universe();

        this->childID = std::numeric_limits<std::size_t>::max(); // `std::numeric_limits<std::size_t>::max()` means that `childID` is not defined.

        this->prerender_callback = nullptr;
        this->postrender_callback = nullptr;
        this->setting_master = std::make_shared<yli::config::SettingMaster>(this);
        this->can_be_erased = false;
        this->should_be_rendered = false;

        this->set_local_name(entity_struct.local_name);

        if (this->universe != this)
        {
            this->set_global_name(entity_struct.global_name);

            this->should_be_rendered = (this->universe == nullptr ? false : !this->universe->get_is_headless());

            yli::config::SettingStruct should_be_rendered_setting_struct(std::make_shared<yli::data::AnyValue>(this->should_be_rendered));
            should_be_rendered_setting_struct.name = "should_be_rendered";
            should_be_rendered_setting_struct.activate_callback = &yli::config::Setting::activate_should_be_rendered;
            should_be_rendered_setting_struct.read_callback = &yli::config::Setting::read_should_be_rendered;
            should_be_rendered_setting_struct.should_ylikuutio_call_activate_callback_now = true;
            std::cout << "Executing `setting_master->create_setting(should_be_rendered_setting_struct);` ...\n";
            this->setting_master->create_setting(should_be_rendered_setting_struct);
        }
    }

    Entity::~Entity()
    {
        // destructor.
        std::cout << "`Entity` destructor called.\n";

        if (this->universe == nullptr)
        {
            std::cerr << "ERROR: `Entity::~Entity`: `this->universe` is `nullptr`.\n";
            return;
        }

        this->universe->unbind_entity(this->entityID);

        if (!this->global_name.empty())
        {
            // OK, this `Entity` had a global name, so it's global name shall be erased.
            this->universe->erase_entity(this->global_name);
        }

        // Local names must be erased in the destructors
        // of classes that inherit `yli::ontology::Entity`!
        // They can not be erased here in `Entity` destructor,
        // because `Entity` class does not keep track of the
        // parent. `Entity` only provides virtual function
        // `Entity::get_parent`, but that can not be called
        // from here.
    }

    void Entity::render()
    {
    }

    std::size_t Entity::get_childID() const
    {
        return this->childID;
    }

    std::string Entity::get_type() const
    {
        return this->type_string;
    }

    bool Entity::get_can_be_erased() const
    {
        return this->can_be_erased;
    }

    yli::ontology::Universe* Entity::get_universe() const
    {
        return this->universe;
    }

    yli::config::SettingMaster* Entity::get_setting_master() const
    {
        if (this->setting_master == nullptr)
        {
            std::cerr << "ERROR: `Entity::get_setting_master`: `this->setting_master` is `nullptr`.\n";
            return nullptr;
        }

        return this->setting_master.get();
    }

    bool Entity::is_entity(const std::string& name) const
    {
        return this->entity_map.count(name) == 1;
    }

    yli::ontology::Entity* Entity::get_entity(const std::string& name) const
    {
        if (this->entity_map.count(name) != 1)
        {
            return nullptr;
        }

        return this->entity_map.at(name);
    }

    std::string Entity::get_entity_names() const
    {
        std::string entity_names = "";

        std::vector<std::string> keys;
        keys.reserve(this->entity_map.size());

        for (auto it : this->entity_map)
        {
            if (!entity_names.empty())
            {
                entity_names += " ";
            }
            std::string key = static_cast<std::string>(it.first);
            entity_names += key;
        }

        return entity_names;
    }

    void Entity::add_entity(const std::string& name, yli::ontology::Entity* const entity)
    {
        if (!name.empty() && this->entity_map.count(name) == 0)
        {
            this->entity_map[name] = entity;
        }
    }

    void Entity::erase_entity(const std::string& name)
    {
        if (this->entity_map.count(name) == 1)
        {
            if (this->entity_map[name]->get_can_be_erased())
            {
                this->entity_map.erase(name);
            }
        }
    }

    void Entity::prerender() const
    {
        // Requirements:
        // `this->prerender_callback` must not be `nullptr`.
        // `this->universe` must not be `nullptr`.
        // `this->universe->setting_master` must not be `nullptr`.

        if (this->prerender_callback != nullptr &&
                this->universe != nullptr &&
                this->universe->setting_master != nullptr)
        {
            this->prerender_callback(this->universe, this->universe->setting_master.get());
        }
    }

    void Entity::postrender() const
    {
        // Requirements:
        // `this->postrender_callback` must not be `nullptr`.
        // `this->universe` must not be `nullptr`.
        // `this->universe->setting_master` must not be `nullptr`.

        if (this->postrender_callback != nullptr &&
                this->universe != nullptr &&
                this->universe->setting_master != nullptr)
        {
            this->postrender_callback(this->universe, this->universe->setting_master.get());
        }
    }

    std::string Entity::get_global_name() const
    {
        return this->global_name;
    }

    std::string Entity::get_local_name() const
    {
        return this->local_name;
    }

    void Entity::set_global_name(const std::string& global_name)
    {
        // Requirements:
        // `this->universe` must not be `nullptr`.
        // `global_name` must not be already in use.

        if (global_name.empty())
        {
            return;
        }

        if (!std::regex_match(global_name, std::regex("[a-zA-Z][a-zA-Z0-9_-]*")))
        {
            return;
        }

        if (this->universe == nullptr)
        {
            return;
        }

        if (this->universe->is_entity(global_name))
        {
            // The global name is already in use.
            return;
        }

        // Erase old global name.
        this->universe->erase_entity(this->global_name);

        // Set new global name.
        this->global_name = global_name;
        this->universe->add_entity(global_name, this);

        if (this->universe == this->get_parent())
        {
            // `Universe` is the parent of this `Entity`.
            // Therefore, local name and global name
            // are the same for this `Entity`.
            this->local_name = global_name;
        }
    }

    void Entity::set_local_name(const std::string& local_name)
    {
        if (local_name.empty())
        {
            return;
        }

        if (!std::regex_match(local_name, std::regex("[a-zA-Z][a-zA-Z0-9_-]*")))
        {
            return;
        }

        yli::ontology::Entity* const parent = this->get_parent();

        if (parent == nullptr)
        {
            return;
        }

        if (parent->is_entity(local_name))
        {
            // The name is in use.
            return;
        }

        // Erase old local name.
        parent->erase_entity(this->local_name);

        // Set new local name.
        parent->add_entity(local_name, this);
        this->local_name = local_name;

        if (parent == this->universe)
        {
            // Special case: this `Entity` is a child of the `Universe`!
            // Therefore, the local name is also the global name,
            // and vice versa. This means that the requested
            // global name must not be in use.
            this->global_name = local_name;
        }
    }
}
<|endoftext|>