<commit_before>/*
 * This file is part of the IAN project - https://github.com/Meoo/IAN
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "ClusterConnection.hpp"
#include "ClusterInternal.hpp"

#include <common/EasyProfiler.hpp>

#include <proto-in/ClusterHandshake_generated.h>

#include <boost/asio/bind_executor.hpp>
#include <boost/asio/read.hpp>
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/endian/conversion.hpp>

namespace asio = boost::asio;

#define LOG_SOCKET_TUPLE                                                                           \
  socket_.remote_endpoint().address().to_string(), socket_.remote_endpoint().port()

namespace
{

const size_t read_chunk_size = 16 * 1024; // 16k

bool is_connection_reset_error(const boost::system::error_code & ec)
{
  if (ec.category() == asio::error::get_system_category() &&
      (ec == asio::error::connection_aborted || ec == asio::error::connection_reset))
  {
    return true;
  }

  if (ec.category() == asio::ssl::error::get_stream_category() &&
      ec == asio::ssl::error::stream_truncated)
  {
    return true;
  }

  return false;
}

bool is_connection_canceled_error(const boost::system::error_code & ec)
{
  if (ec.category() == asio::error::get_ssl_category() &&
      ERR_GET_REASON(ec.value()) == SSL_R_PROTOCOL_IS_SHUTDOWN)
  {
    return true;
  }

  return false;
}

} // namespace

ClusterConnection::ClusterConnection(const std::shared_ptr<spdlog::logger> & logger,
                                     TcpSocket && socket)
    : logger_(logger), stream_(std::move(socket), cluster::internal::get_ssl()),
      socket_(stream_.next_layer()), strand_(socket_.get_io_context()),
      timer_(socket_.get_io_context())
{
}

ClusterConnection::~ClusterConnection()
{
  // Close must be called last or using LOG_SOCKET_TUPLE will throw
  boost::system::error_code ec;
  socket_.close(ec);
}

void ClusterConnection::run(SslRole role, bool safe_link)
{
  safe_link_ = safe_link;

  // Force peer verification
  boost::system::error_code ec;
  stream_.set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert, ec);
  if (ec)
    IAN_ERROR(logger_, "Failed to force peer verification for cluster connection: : {}:{} : {}",
              LOG_SOCKET_TUPLE, ec.message());

  // SSL handshake
  stream_.async_handshake(
      role == Client ? asio::ssl::stream_base::client : asio::ssl::stream_base::server,
      asio::bind_executor(strand_, std::bind(&ClusterConnection::on_ssl_handshake,
                                             shared_from_this(), std::placeholders::_1)));
}

void ClusterConnection::abort()
{
  if (dropped_)
    return;

  boost::system::error_code ec;

  // Ignore errors
  timer_.cancel(ec);
  // TODO state_timer_.cancel(ec);

  // TODO message_queue_.clear();

  IAN_INFO(logger_, "Peer disconnected: {}:{}", LOG_SOCKET_TUPLE);

  // Abort stream at socket level
  socket_.shutdown(socket_.shutdown_both, ec);
  dropped_ = true;
}

void ClusterConnection::shutdown()
{
  if (dropped_ || shutting_down_)
    return;

  boost::system::error_code ec;

  // Ignore errors
  timer_.cancel(ec);
  // TODO state_timer_.cancel(ec);

  // TODO message_queue_.clear();

  if (!socket_.is_open())
    return;

  // Shutdown stream at SSL level
  stream_.async_shutdown(
      asio::bind_executor(strand_, std::bind(&ClusterConnection::on_shutdown, shared_from_this(),
                                             std::placeholders::_1)));
  shutting_down_ = true;
}

void ClusterConnection::on_shutdown(boost::system::error_code ec)
{
  if (dropped_)
    return;

  if (ec)
  {
    if (!is_connection_reset_error(ec) && !is_connection_canceled_error(ec))
      IAN_WARN(logger_, "Shutdown error for peer: {}:{} : {} {}", LOG_SOCKET_TUPLE, ec.message(),
               ec.value());
  }

  IAN_INFO(logger_, "Peer disconnected: {}:{}", LOG_SOCKET_TUPLE);

  // Abort stream at socket level
  socket_.shutdown(socket_.shutdown_both, ec);
  dropped_ = true;
}

void ClusterConnection::on_ssl_handshake(boost::system::error_code ec)
{
  if (dropped_)
    return;

  if (ec)
  {
    IAN_WARN(logger_, "SSL handshake failed for peer: {}:{} : {} {}", LOG_SOCKET_TUPLE,
             ec.message(), ec.value());
    this->abort();
    return;
  }

  IAN_TRACE(logger_, "SSL handshake complete for peer: {}:{}", LOG_SOCKET_TUPLE);

  // Read IAN handshake
  stream_.async_read_some(
      read_buffer_.prepare(read_chunk_size),
      asio::bind_executor(strand_,
                          std::bind(&ClusterConnection::on_ian_handshake, shared_from_this(),
                                    std::placeholders::_1, std::placeholders::_2)));
}

void ClusterConnection::on_ian_handshake(boost::system::error_code ec, std::size_t readlen)
{
  if (ec)
  {
    handle_read_error(ec);
    return;
  }

  read_buffer_.commit(readlen);

  if (message_len_ == -1U)
  {
    // Read handshake length
    if (read_buffer_.size() < sizeof(message_len_))
    {
      // If first packet contains less than 4 bytes something is really wrong
      IAN_ERROR(logger_, "Not enough data to read handshake length : {}:{} : {}", LOG_SOCKET_TUPLE,
                read_buffer_.size());
      shutdown();
      return;
    }

    // Extract message length (little endian)
    read_buffer_.consume(asio::buffer_copy(
        asio::buffer((void *)&message_len_, sizeof(message_len_)), read_buffer_.data()));
    boost::endian::little_to_native_inplace(message_len_);
  }

  if (read_buffer_.size() < message_len_)
  {
    IAN_DEBUG(logger_, "Not enough data to parse handshake : {}:{} : {} < {}", LOG_SOCKET_TUPLE,
              read_buffer_.size(), message_len_);
    // Not enough data (somehow), read again
    stream_.async_read_some(
        read_buffer_.prepare(read_chunk_size),
        asio::bind_executor(strand_,
                            std::bind(&ClusterConnection::on_ian_handshake, shared_from_this(),
                                      std::placeholders::_1, std::placeholders::_2)));
    return;
  }

  // Flatten
  boost::beast::flat_buffer buf;
  buf.commit(asio::buffer_copy(buf.prepare(read_buffer_.size()), read_buffer_.data()));

  // Verify handshake
  if (!proto::VerifyClusterHandshakeBuffer(
          flatbuffers::Verifier((const uint8_t *)buf.data().data(), buf.size())))
  {
    IAN_ERROR(logger_, "Cluster handshake verification failed : {}:{}", LOG_SOCKET_TUPLE);
    shutdown();
    return;
  }

  const proto::ClusterHandshake * handshake = proto::GetClusterHandshake(buf.data().data());

  auto major = handshake->version_major();
  auto minor = handshake->version_minor();

  IAN_DEBUG(logger_, "Proto version for peer : {}:{} : {}.{}", LOG_SOCKET_TUPLE, major, minor);

  safe_link_ = safe_link_ && handshake->safe_link();

  if (safe_link_)
  {
    // TODO Downgrade to clear messages
  }
  else
  {
    // TODO Start reading messages over SSL
  }
}

void ClusterConnection::handle_read_error(boost::system::error_code ec)
{
  if (dropped_)
    return;

  if (is_connection_reset_error(ec))
  {
    this->abort();
    return;
  }

  if (is_connection_canceled_error(ec))
    return;

  IAN_WARN(logger_, "Read failed for peer: {}:{} : {} {}", LOG_SOCKET_TUPLE, ec.message(),
           ec.value());

  shutdown();
}

void ClusterConnection::handle_write_error(boost::system::error_code ec)
{
  if (dropped_)
    return;

  if (is_connection_reset_error(ec))
  {
    this->abort();
    return;
  }

  if (is_connection_canceled_error(ec))
    return;

  IAN_WARN(logger_, "Write failed for peer: {}:{} : {} {}", LOG_SOCKET_TUPLE, ec.message(),
           ec.value());

  shutdown();
}
<commit_msg>Fix build<commit_after>/*
 * This file is part of the IAN project - https://github.com/Meoo/IAN
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include "ClusterConnection.hpp"
#include "ClusterInternal.hpp"

#include <common/EasyProfiler.hpp>

#include <proto-in/ClusterHandshake_generated.h>

#include <boost/asio/bind_executor.hpp>
#include <boost/asio/read.hpp>
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/endian/conversion.hpp>

namespace asio = boost::asio;

#define LOG_SOCKET_TUPLE                                                                           \
  socket_.remote_endpoint().address().to_string(), socket_.remote_endpoint().port()

namespace
{

const size_t read_chunk_size = 16 * 1024; // 16k

bool is_connection_reset_error(const boost::system::error_code & ec)
{
  if (ec.category() == asio::error::get_system_category() &&
      (ec == asio::error::connection_aborted || ec == asio::error::connection_reset))
  {
    return true;
  }

  if (ec.category() == asio::ssl::error::get_stream_category() &&
      ec == asio::ssl::error::stream_truncated)
  {
    return true;
  }

  return false;
}

bool is_connection_canceled_error(const boost::system::error_code & ec)
{
  if (ec.category() == asio::error::get_ssl_category() &&
      ERR_GET_REASON(ec.value()) == SSL_R_PROTOCOL_IS_SHUTDOWN)
  {
    return true;
  }

  return false;
}

} // namespace

ClusterConnection::ClusterConnection(const std::shared_ptr<spdlog::logger> & logger,
                                     TcpSocket && socket)
    : logger_(logger), stream_(std::move(socket), cluster::internal::get_ssl()),
      socket_(stream_.next_layer()), strand_(socket_.get_io_context()),
      timer_(socket_.get_io_context())
{
}

ClusterConnection::~ClusterConnection()
{
  // Close must be called last or using LOG_SOCKET_TUPLE will throw
  boost::system::error_code ec;
  socket_.close(ec);
}

void ClusterConnection::run(SslRole role, bool safe_link)
{
  safe_link_ = safe_link;

  // Force peer verification
  boost::system::error_code ec;
  stream_.set_verify_mode(asio::ssl::verify_peer | asio::ssl::verify_fail_if_no_peer_cert, ec);
  if (ec)
    IAN_ERROR(logger_, "Failed to force peer verification for cluster connection: : {}:{} : {}",
              LOG_SOCKET_TUPLE, ec.message());

  // SSL handshake
  stream_.async_handshake(
      role == Client ? asio::ssl::stream_base::client : asio::ssl::stream_base::server,
      asio::bind_executor(strand_, std::bind(&ClusterConnection::on_ssl_handshake,
                                             shared_from_this(), std::placeholders::_1)));
}

void ClusterConnection::abort()
{
  if (dropped_)
    return;

  boost::system::error_code ec;

  // Ignore errors
  timer_.cancel(ec);
  // TODO state_timer_.cancel(ec);

  // TODO message_queue_.clear();

  IAN_INFO(logger_, "Peer disconnected: {}:{}", LOG_SOCKET_TUPLE);

  // Abort stream at socket level
  socket_.shutdown(socket_.shutdown_both, ec);
  dropped_ = true;
}

void ClusterConnection::shutdown()
{
  if (dropped_ || shutting_down_)
    return;

  boost::system::error_code ec;

  // Ignore errors
  timer_.cancel(ec);
  // TODO state_timer_.cancel(ec);

  // TODO message_queue_.clear();

  if (!socket_.is_open())
    return;

  // Shutdown stream at SSL level
  stream_.async_shutdown(
      asio::bind_executor(strand_, std::bind(&ClusterConnection::on_shutdown, shared_from_this(),
                                             std::placeholders::_1)));
  shutting_down_ = true;
}

void ClusterConnection::on_shutdown(boost::system::error_code ec)
{
  if (dropped_)
    return;

  if (ec)
  {
    if (!is_connection_reset_error(ec) && !is_connection_canceled_error(ec))
      IAN_WARN(logger_, "Shutdown error for peer: {}:{} : {} {}", LOG_SOCKET_TUPLE, ec.message(),
               ec.value());
  }

  IAN_INFO(logger_, "Peer disconnected: {}:{}", LOG_SOCKET_TUPLE);

  // Abort stream at socket level
  socket_.shutdown(socket_.shutdown_both, ec);
  dropped_ = true;
}

void ClusterConnection::on_ssl_handshake(boost::system::error_code ec)
{
  if (dropped_)
    return;

  if (ec)
  {
    IAN_WARN(logger_, "SSL handshake failed for peer: {}:{} : {} {}", LOG_SOCKET_TUPLE,
             ec.message(), ec.value());
    this->abort();
    return;
  }

  IAN_TRACE(logger_, "SSL handshake complete for peer: {}:{}", LOG_SOCKET_TUPLE);

  // Read IAN handshake
  stream_.async_read_some(
      read_buffer_.prepare(read_chunk_size),
      asio::bind_executor(strand_,
                          std::bind(&ClusterConnection::on_ian_handshake, shared_from_this(),
                                    std::placeholders::_1, std::placeholders::_2)));
}

void ClusterConnection::on_ian_handshake(boost::system::error_code ec, std::size_t readlen)
{
  if (ec)
  {
    handle_read_error(ec);
    return;
  }

  read_buffer_.commit(readlen);

  if (message_len_ == -1U)
  {
    // Read handshake length
    if (read_buffer_.size() < sizeof(message_len_))
    {
      // If first packet contains less than 4 bytes something is really wrong
      IAN_ERROR(logger_, "Not enough data to read handshake length : {}:{} : {}", LOG_SOCKET_TUPLE,
                read_buffer_.size());
      shutdown();
      return;
    }

    // Extract message length (little endian)
    read_buffer_.consume(asio::buffer_copy(
        asio::buffer((void *)&message_len_, sizeof(message_len_)), read_buffer_.data()));
    boost::endian::little_to_native_inplace(message_len_);
  }

  if (read_buffer_.size() < message_len_)
  {
    IAN_DEBUG(logger_, "Not enough data to parse handshake : {}:{} : {} < {}", LOG_SOCKET_TUPLE,
              read_buffer_.size(), message_len_);
    // Not enough data (somehow), read again
    stream_.async_read_some(
        read_buffer_.prepare(read_chunk_size),
        asio::bind_executor(strand_,
                            std::bind(&ClusterConnection::on_ian_handshake, shared_from_this(),
                                      std::placeholders::_1, std::placeholders::_2)));
    return;
  }

  // Flatten
  boost::beast::flat_buffer buf;
  buf.commit(asio::buffer_copy(buf.prepare(read_buffer_.size()), read_buffer_.data()));

  // Verify handshake
  {
    flatbuffers::Verifier verifier((const uint8_t *)buf.data().data(), buf.size());
    if (!proto::VerifyClusterHandshakeBuffer(verifier))
    {
      IAN_ERROR(logger_, "Cluster handshake verification failed : {}:{}", LOG_SOCKET_TUPLE);
      shutdown();
      return;
    }
  }

  const proto::ClusterHandshake * handshake = proto::GetClusterHandshake(buf.data().data());

  auto major = handshake->version_major();
  auto minor = handshake->version_minor();

  IAN_DEBUG(logger_, "Proto version for peer : {}:{} : {}.{}", LOG_SOCKET_TUPLE, major, minor);

  safe_link_ = safe_link_ && handshake->safe_link();

  if (safe_link_)
  {
    // TODO Downgrade to clear messages
  }
  else
  {
    // TODO Start reading messages over SSL
  }
}

void ClusterConnection::handle_read_error(boost::system::error_code ec)
{
  if (dropped_)
    return;

  if (is_connection_reset_error(ec))
  {
    this->abort();
    return;
  }

  if (is_connection_canceled_error(ec))
    return;

  IAN_WARN(logger_, "Read failed for peer: {}:{} : {} {}", LOG_SOCKET_TUPLE, ec.message(),
           ec.value());

  shutdown();
}

void ClusterConnection::handle_write_error(boost::system::error_code ec)
{
  if (dropped_)
    return;

  if (is_connection_reset_error(ec))
  {
    this->abort();
    return;
  }

  if (is_connection_canceled_error(ec))
    return;

  IAN_WARN(logger_, "Write failed for peer: {}:{} : {} {}", LOG_SOCKET_TUPLE, ec.message(),
           ec.value());

  shutdown();
}
<|endoftext|>


<commit_before>#include <product.hpp>

#include "helpers.hpp"

#include <vector>
#include <string>
#include <iterator>

#include "catch.hpp"

using iter::product;
using Vec = const std::vector<int>;

TEST_CASE("product: basic test, two sequences", "[product]") {
    using TP = std::tuple<int, char>;
    using ResType = std::vector<TP>;

    Vec n1 = {0, 1};
    const std::string s{"abc"};

    auto p = product(n1, s);
    ResType v(std::begin(p), std::end(p));
    ResType vc = {TP{0,'a'}, TP{0,'b'}, TP{0,'c'},
                  TP{1,'a'}, TP{1,'b'}, TP{1,'c'}};

    REQUIRE( v == vc );
}

TEST_CASE("product: three sequences", "[product]") {
    using TP = std::tuple <int, char, int>;
    using ResType = const std::vector<TP>;

    Vec n1 = {0, 1};
    const std::string s{"ab"};
    Vec n2 = {2};

    auto p = product(n1, s, n2);
    ResType v(std::begin(p), std::end(p));

    ResType vc = {
        TP{0, 'a', 2},
        TP{0, 'b', 2},
        TP{1, 'a', 2},
        TP{1, 'b', 2}
    };

    REQUIRE( v == vc );
}

TEST_CASE("product: empty when any iterable is empty", "[product]") {
    Vec n1 = {0, 1};
    Vec n2 = {0, 1, 2};
    Vec emp = {};

    SECTION("first iterable is empty") {
        auto p = product(emp, n1, n2);
        REQUIRE( std::begin(p) == std::end(p) );
    }

    SECTION("middle iterable is empty") {
        auto p = product(n1, emp, n2);
        REQUIRE( std::begin(p) == std::end(p) );
    }

    SECTION("last iterable is empty") {
        auto p = product(n1, n2, emp);
        REQUIRE( std::begin(p) == std::end(p) );
    }
}

TEST_CASE("product: single iterable", "[product]") {
    const std::string s{"ab"};
    using TP = std::tuple<char>;
    using ResType = const std::vector<TP>;

    auto p = product(s);
    ResType v(std::begin(p), std::end(p));
    ResType vc = {TP{'a'}, TP{'b'}};

    REQUIRE( v == vc );
}

TEST_CASE("product: binds to lvalues and moves rvalues", "[product]") {
    itertest::BasicIterable<char> bi{'x', 'y'};
    itertest::BasicIterable<int> bi2{0, 1};

    SECTION("First ref'd, second moved") {
        product(bi, std::move(bi2));
        REQUIRE_FALSE( bi.was_moved_from() );
        REQUIRE( bi2.was_moved_from() );
    }

    SECTION("First moved, second ref'd") {
        product(std::move(bi), bi2);
        REQUIRE( bi.was_moved_from() );
        REQUIRE_FALSE( bi2.was_moved_from() );
    }
}

TEST_CASE("product: doesn't move or copy elements of iterable", "[product]") {
    constexpr itertest::SolidInt arr[] = {{1}, {0}, {2}};
    for (auto&& t : product(arr)) {
        (void)std::get<0>(t);
    }
}
<commit_msg>tests empty product()<commit_after>#include <product.hpp>

#include "helpers.hpp"

#include <vector>
#include <string>
#include <iterator>

#include "catch.hpp"

using iter::product;
using Vec = const std::vector<int>;

TEST_CASE("product: basic test, two sequences", "[product]") {
    using TP = std::tuple<int, char>;
    using ResType = std::vector<TP>;

    Vec n1 = {0, 1};
    const std::string s{"abc"};

    auto p = product(n1, s);
    ResType v(std::begin(p), std::end(p));
    ResType vc = {TP{0,'a'}, TP{0,'b'}, TP{0,'c'},
                  TP{1,'a'}, TP{1,'b'}, TP{1,'c'}};

    REQUIRE( v == vc );
}

TEST_CASE("product: three sequences", "[product]") {
    using TP = std::tuple <int, char, int>;
    using ResType = const std::vector<TP>;

    Vec n1 = {0, 1};
    const std::string s{"ab"};
    Vec n2 = {2};

    auto p = product(n1, s, n2);
    ResType v(std::begin(p), std::end(p));

    ResType vc = {
        TP{0, 'a', 2},
        TP{0, 'b', 2},
        TP{1, 'a', 2},
        TP{1, 'b', 2}
    };

    REQUIRE( v == vc );
}

TEST_CASE("product: empty when any iterable is empty", "[product]") {
    Vec n1 = {0, 1};
    Vec n2 = {0, 1, 2};
    Vec emp = {};

    SECTION("first iterable is empty") {
        auto p = product(emp, n1, n2);
        REQUIRE( std::begin(p) == std::end(p) );
    }

    SECTION("middle iterable is empty") {
        auto p = product(n1, emp, n2);
        REQUIRE( std::begin(p) == std::end(p) );
    }

    SECTION("last iterable is empty") {
        auto p = product(n1, n2, emp);
        REQUIRE( std::begin(p) == std::end(p) );
    }
}

TEST_CASE("product: single iterable", "[product]") {
    const std::string s{"ab"};
    using TP = std::tuple<char>;
    using ResType = const std::vector<TP>;

    auto p = product(s);
    ResType v(std::begin(p), std::end(p));
    ResType vc = {TP{'a'}, TP{'b'}};

    REQUIRE( v == vc );
}

TEST_CASE("product: no arguments gives one empty tuple", "[product") {
    auto p = product();
    auto it = std::begin(p);
    REQUIRE( it != std::end(p) );
    REQUIRE( *it == std::make_tuple() );
    ++it;
    REQUIRE( it == std::end(p) );
}

TEST_CASE("product: binds to lvalues and moves rvalues", "[product]") {
    itertest::BasicIterable<char> bi{'x', 'y'};
    itertest::BasicIterable<int> bi2{0, 1};

    SECTION("First ref'd, second moved") {
        product(bi, std::move(bi2));
        REQUIRE_FALSE( bi.was_moved_from() );
        REQUIRE( bi2.was_moved_from() );
    }

    SECTION("First moved, second ref'd") {
        product(std::move(bi), bi2);
        REQUIRE( bi.was_moved_from() );
        REQUIRE_FALSE( bi2.was_moved_from() );
    }
}

TEST_CASE("product: doesn't move or copy elements of iterable", "[product]") {
    constexpr itertest::SolidInt arr[] = {{1}, {0}, {2}};
    for (auto&& t : product(arr)) {
        (void)std::get<0>(t);
    }
}
<|endoftext|>


<commit_before>// Licensed 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.
//
///////////////////////////////////////////////////////////////////////////////

#include "tink/util/validation.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "tink/util/test_matchers.h"

namespace crypto {
namespace tink {

namespace {

using crypto::tink::test::IsOk;
using crypto::tink::test::StatusIs;

TEST(ValidateKey, ValidKey) {
  google::crypto::tink::Keyset::Key key;
  key.set_key_id(100);
  key.mutable_key_data()->set_value("some value");
  key.set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key.set_status(google::crypto::tink::KeyStatusType::ENABLED);
  EXPECT_THAT(crypto::tink::ValidateKey(key), IsOk());
}

TEST(ValidateKey, MissingOutputPrefixType) {
  google::crypto::tink::Keyset::Key key;
  key.set_key_id(100);
  key.mutable_key_data()->set_value("some value");
  key.set_status(google::crypto::tink::KeyStatusType::ENABLED);
  EXPECT_THAT(crypto::tink::ValidateKey(key),
              StatusIs(util::error::INVALID_ARGUMENT));
}

TEST(ValidateKey, MissingKeyData) {
  google::crypto::tink::Keyset::Key key;
  key.set_key_id(100);
  key.set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key.set_status(google::crypto::tink::KeyStatusType::ENABLED);
  EXPECT_THAT(crypto::tink::ValidateKey(key),
              StatusIs(util::error::INVALID_ARGUMENT));
}

TEST(ValidateKey, MissingStatus) {
  google::crypto::tink::Keyset::Key key;
  key.set_key_id(100);
  key.mutable_key_data()->set_value("some value");
  key.set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  EXPECT_THAT(crypto::tink::ValidateKey(key),
              StatusIs(util::error::INVALID_ARGUMENT));
}

TEST(ValidateKeyset, Valid) {
  google::crypto::tink::Keyset keyset;
  google::crypto::tink::Keyset::Key* key = keyset.add_key();
  key->set_key_id(100);
  key->mutable_key_data()->set_value("some value");
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  keyset.set_primary_key_id(100);
  EXPECT_THAT(crypto::tink::ValidateKeyset(keyset), IsOk());
}

TEST(ValidateKeyset, ValidMultipleKeys) {
  google::crypto::tink::Keyset keyset;
  google::crypto::tink::Keyset::Key* key = keyset.add_key();
  key->set_key_id(32);
  key->mutable_key_data()->set_value("some value");
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  key = keyset.add_key();
  key->set_key_id(100);
  key->mutable_key_data()->set_value("some other value");
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  key = keyset.add_key();
  key->set_key_id(18);
  key->mutable_key_data()->set_value("some third value");
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  keyset.set_primary_key_id(100);
  EXPECT_THAT(crypto::tink::ValidateKeyset(keyset), IsOk());
}

TEST(ValidateKeyset, PrimaryIdNonExistent) {
  google::crypto::tink::Keyset keyset;
  google::crypto::tink::Keyset::Key* key = keyset.add_key();
  key->set_key_id(100);
  key->mutable_key_data()->set_value("some value");
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  keyset.set_primary_key_id(99);
  EXPECT_THAT(crypto::tink::ValidateKeyset(keyset),
              StatusIs(util::error::INVALID_ARGUMENT));
}

TEST(ValidateKeyset, ValidHighId) {
  google::crypto::tink::Keyset keyset;
  google::crypto::tink::Keyset::Key* key = keyset.add_key();
  key->set_key_id(std::numeric_limits<uint32_t>::max());
  key->mutable_key_data()->set_value("some value");
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  keyset.set_primary_key_id(std::numeric_limits<uint32_t>::max());
  EXPECT_THAT(crypto::tink::ValidateKeyset(keyset), IsOk());
}

}  // namespace

}  // namespace tink
}  // namespace crypto
<commit_msg>Add some tests to validation_test<commit_after>// Licensed 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.
//
///////////////////////////////////////////////////////////////////////////////

#include "tink/util/validation.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "tink/util/test_matchers.h"

namespace crypto {
namespace tink {

namespace {

using crypto::tink::test::IsOk;
using crypto::tink::test::StatusIs;
using google::crypto::tink::KeyData;
using testing::Not;

TEST(ValidateKey, ValidKey) {
  google::crypto::tink::Keyset::Key key;
  key.set_key_id(100);
  key.mutable_key_data()->set_value("some value");
  key.set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key.set_status(google::crypto::tink::KeyStatusType::ENABLED);
  EXPECT_THAT(crypto::tink::ValidateKey(key), IsOk());
}

TEST(ValidateKey, MissingOutputPrefixType) {
  google::crypto::tink::Keyset::Key key;
  key.set_key_id(100);
  key.mutable_key_data()->set_value("some value");
  key.set_status(google::crypto::tink::KeyStatusType::ENABLED);
  EXPECT_THAT(crypto::tink::ValidateKey(key),
              StatusIs(util::error::INVALID_ARGUMENT));
}

TEST(ValidateKey, MissingKeyData) {
  google::crypto::tink::Keyset::Key key;
  key.set_key_id(100);
  key.set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key.set_status(google::crypto::tink::KeyStatusType::ENABLED);
  EXPECT_THAT(crypto::tink::ValidateKey(key),
              StatusIs(util::error::INVALID_ARGUMENT));
}

TEST(ValidateKey, MissingStatus) {
  google::crypto::tink::Keyset::Key key;
  key.set_key_id(100);
  key.mutable_key_data()->set_value("some value");
  key.set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  EXPECT_THAT(crypto::tink::ValidateKey(key),
              StatusIs(util::error::INVALID_ARGUMENT));
}

TEST(ValidateKeyset, Valid) {
  google::crypto::tink::Keyset keyset;
  google::crypto::tink::Keyset::Key* key = keyset.add_key();
  key->set_key_id(100);
  key->mutable_key_data()->set_value("some value");
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  keyset.set_primary_key_id(100);
  EXPECT_THAT(crypto::tink::ValidateKeyset(keyset), IsOk());
}

TEST(ValidateKeyset, ValidMultipleKeys) {
  google::crypto::tink::Keyset keyset;
  google::crypto::tink::Keyset::Key* key = keyset.add_key();
  key->set_key_id(32);
  key->mutable_key_data()->set_value("some value");
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  key = keyset.add_key();
  key->set_key_id(100);
  key->mutable_key_data()->set_value("some other value");
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  key = keyset.add_key();
  key->set_key_id(18);
  key->mutable_key_data()->set_value("some third value");
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  keyset.set_primary_key_id(100);
  EXPECT_THAT(crypto::tink::ValidateKeyset(keyset), IsOk());
}

// Tests that a keyset with duplicate primary id is rejected
TEST(ValidateKeyset, DuplicatePrimaryId) {
  google::crypto::tink::Keyset keyset;
  google::crypto::tink::Keyset::Key* key = keyset.add_key();
  key->set_key_id(100);
  key->mutable_key_data()->set_value("some value");
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  key = keyset.add_key();
  key->set_key_id(100);
  key->mutable_key_data()->set_value("some other value");
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  keyset.set_primary_key_id(100);
  EXPECT_THAT(crypto::tink::ValidateKeyset(keyset), Not(IsOk()));
}

// Tests that a keyset with public keys only doesn't need a primary id
TEST(ValidateKeyset, OnlyPublicKeys) {
  google::crypto::tink::Keyset keyset;
  google::crypto::tink::Keyset::Key* key = keyset.add_key();
  key->set_key_id(32);
  key->mutable_key_data()->set_value("some value");
  key->mutable_key_data()->set_key_material_type(KeyData::ASYMMETRIC_PUBLIC);
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  key = keyset.add_key();
  key->set_key_id(100);
  key->mutable_key_data()->set_value("some other value");
  key->mutable_key_data()->set_key_material_type(KeyData::ASYMMETRIC_PUBLIC);
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  key = keyset.add_key();
  key->set_key_id(18);
  key->mutable_key_data()->set_value("some third value");
  key->mutable_key_data()->set_key_material_type(KeyData::ASYMMETRIC_PUBLIC);
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  EXPECT_THAT(crypto::tink::ValidateKeyset(keyset), IsOk());
}

TEST(ValidateKeyset, PrimaryIdNonExistent) {
  google::crypto::tink::Keyset keyset;
  google::crypto::tink::Keyset::Key* key = keyset.add_key();
  key->set_key_id(100);
  key->mutable_key_data()->set_value("some value");
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  keyset.set_primary_key_id(99);
  EXPECT_THAT(crypto::tink::ValidateKeyset(keyset),
              StatusIs(util::error::INVALID_ARGUMENT));
}

TEST(ValidateKeyset, ValidHighId) {
  google::crypto::tink::Keyset keyset;
  google::crypto::tink::Keyset::Key* key = keyset.add_key();
  key->set_key_id(std::numeric_limits<uint32_t>::max());
  key->mutable_key_data()->set_value("some value");
  key->set_output_prefix_type(google::crypto::tink::OutputPrefixType::TINK);
  key->set_status(google::crypto::tink::KeyStatusType::ENABLED);
  keyset.set_primary_key_id(std::numeric_limits<uint32_t>::max());
  EXPECT_THAT(crypto::tink::ValidateKeyset(keyset), IsOk());
}

}  // namespace

}  // namespace tink
}  // namespace crypto
<|endoftext|>


<commit_before>#include "oddlib/compressiontype3.hpp"
#include "oddlib/stream.hpp"
#include "logger.hpp"
#include <windows.h>

namespace Oddlib
{
    static int Next6Bits(signed int& bitCounter, unsigned int& src_data, IStream& stream)
    {
        int ret = 0;
        if (bitCounter > 0)
        {
            if (bitCounter == 14)
            {
                bitCounter = 30;
                src_data = (ReadUint16(stream) << 14) | src_data;
                ret = 2;
            }
        }
        else
        {
            bitCounter = 32;
            src_data = ReadUint32(stream);
            ret = 4;
        }
        bitCounter -= 6;
        return ret;
    }

    // Function 0x004031E0 in AO
    std::vector<Uint8> CompressionType3::Decompress(IStream& stream, Uint32 finalW, Uint32 /*w*/, Uint32 h, Uint32 dataSize)
    {
        size_t dstPos = 0;
        std::vector<unsigned char> buffer;
        buffer.resize(finalW*h*4);

      //  const unsigned int whSize = w*h;
        int numBytesInFrameCnt = dataSize;// whSize & 0xFFFFFFFC;
        if (numBytesInFrameCnt > 0)
        {

            // unsigned int frame_data_index = 0;
            unsigned int src_data = 0;
            signed int bitCounter = 0;
            do
            {

                /*numBytesInFrameCnt -=*/ Next6Bits(bitCounter, src_data, stream);
                unsigned char bits = src_data & 0x3F;
                src_data = src_data >> 6;
                 --numBytesInFrameCnt;
                if (bits & 0x20)
                {
                    // 0x20= 0b100000
                    // 0x3F= 0b111111
                    int numberOfBytes = (bits & 0x1F) + 1;
                    if (numberOfBytes)
                    {
                        do
                        {
                            if (numBytesInFrameCnt && dstPos < buffer.size())
                            {
                                /*numBytesInFrameCnt -=*/ Next6Bits(bitCounter, src_data, stream);
                                bits = src_data & 0x3F;
                                src_data = src_data >> 6;
                                 --numBytesInFrameCnt;
                            }
                            else
                            {
                                // 6 bits from "other" source
                                bits = 0;// *srcPtr++ & 0x3F;
                                //--for_counter;
                            }
                          //  if (dstPos < buffer.size())
                            {
                                buffer[dstPos++] = bits;
                            }
                        } while (--numberOfBytes != 0);
                    }
                }
                else
                {
                    // literal flag isn't set, so we have up to 5 bits of "black" pixels
                    dstPos += bits + 1;
                }

            } while (numBytesInFrameCnt);
        }

        return buffer;
    }
}
<commit_msg>final bit of tidy up<commit_after>#include "oddlib/compressiontype3.hpp"
#include "oddlib/stream.hpp"
#include "logger.hpp"
#include <windows.h>

namespace Oddlib
{
    static void NextBits(signed int& bitCounter, unsigned int& src_data, IStream& stream)
    {
        if (bitCounter > 0)
        {
            if (bitCounter == 14)
            {
                bitCounter = 30;
                src_data = (ReadUint16(stream) << 14) | src_data;
            }
        }
        else
        {
            bitCounter = 32;
            src_data = ReadUint32(stream);
        }
        bitCounter -= 6;
    }

    // Function 0x004031E0 in AO
    // NOTE: A lot of the code in AbeWin.exe for this algorithm is dead, it attempts to gain some "other" buffer at the end of the
    // animation data which actually doesn't exist. Thus all this "extra" code does is write black pixels to an already black canvas.
    std::vector<Uint8> CompressionType3::Decompress(IStream& stream, Uint32 finalW, Uint32 /*w*/, Uint32 h, Uint32 dataSize)
    {
        size_t dstPos = 0;
        std::vector<unsigned char> buffer;
        buffer.resize(finalW*h);

        int numBytesInFrameCnt = dataSize & 0xFFFFFFFC;
        if (numBytesInFrameCnt > 0)
        {
            unsigned int src_data = 0;
            signed int bitCounter = 0;
            do
            {
                NextBits(bitCounter, src_data, stream);
                unsigned char bits = src_data & 0x3F;
                src_data = src_data >> 6;
                 --numBytesInFrameCnt;
                if (bits & 0x20)
                {
                    // 0x20 = 0b100000
                    // 0x3F = 0b111111
                    int numberOfBytes = (bits & 0x1F) + 1;
                    if (numberOfBytes)
                    {
                        do
                        {
                            if (numBytesInFrameCnt && dstPos < buffer.size())
                            {
                                NextBits(bitCounter, src_data, stream);
                                bits = src_data & 0x3F;
                                src_data = src_data >> 6;
                                 --numBytesInFrameCnt;
                            }
                            else
                            {
                                bits = 0;
                            }
                            buffer[dstPos++] = bits;
                        } while (--numberOfBytes != 0);
                    }
                }
                else
                {
                    // literal flag isn't set, so we have up to 5 bits of "black" pixels
                    dstPos += bits + 1;
                }

            } while (numBytesInFrameCnt);
        }

        return buffer;
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2017.  All rights reserved
 */
#include "../../StroikaPreComp.h"

#if qPlatform_Linux
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#elif qPlatform_Windows
#include <Windows.h>
#include <winioctl.h>
#endif

#include "../../Characters/CString/Utilities.h"
#include "../../Characters/Format.h"
#include "../../Characters/String.h"
#include "../../Characters/StringBuilder.h"
#include "../../Characters/String_Constant.h"
#include "../../Characters/ToString.h"
#include "../../DataExchange/Variant/CharacterDelimitedLines/Reader.h"
#include "../../Execution/Finally.h"
#include "../../Execution/Synchronized.h"
#include "../../Memory/SmallStackBuffer.h"

#include "FileInputStream.h"
#include "FileSystem.h"

#include "MountedFilesystem.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::Containers;
using namespace Stroika::Foundation::IO;
using namespace Stroika::Foundation::IO::FileSystem;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

#if qPlatform_Linux
namespace {
    // This is quirky, and only works for Linux, and /proc/mounts
    struct Watcher_Proc_Mounts_ {
        int mfd;
        Watcher_Proc_Mounts_ (const String& fn)
            : mfd (::open (fn.AsNarrowSDKString ().c_str (), O_RDONLY, 0))
        {
        }
        ~Watcher_Proc_Mounts_ ()
        {
            ::close (mfd);
        }
        bool IsNewAvail () const
        {
            // according to http://stackoverflow.com/questions/5070801/monitoring-mount-point-changes-via-proc-mounts
            // have to use poll with POLLPRI | POLLERR flags
            //
            // and from https://linux.die.net/man/5/proc
            //      /proc/[pid]/mounts (since Linux 2.4.19)...
            //      Since kernel version 2.6.15, this file is pollable: after opening the file for reading, a change in this file
            //      (i.e., a file system mount or unmount) causes select(2) to mark the file descriptor as readable, and poll(2)
            //      and epoll_wait(2) mark the file as having an error condition.
            struct pollfd pfd;
            int           rv;
            int           changes = 0;
            pfd.fd                = mfd;
            pfd.events            = POLLERR | POLLPRI;
            pfd.revents           = 0;
            if ((rv = poll (&pfd, 1, 0)) >= 0) {
                if (pfd.revents & POLLERR) {
                    return true;
                }
            }
            return false;
        }
    };
}
#endif

namespace {
    /*
     *  Something like this is used on many unix systems.
     */
    Collection<MountedFilesystemType> ReadMountInfo_MTabLikeFile_ (const Streams::InputStream<Memory::Byte>& readStream)
    {
        /*
         *  I haven't found this clearly documented yet, but it appears that a filesystem can be over-mounted.
         *  See https://www.kernel.org/doc/Documentation/filesystems/ramfs-rootfs-initramfs.txt
         *
         *  So the last one with a given mount point in the file wins.
         */
        Collection<MountedFilesystemType>                      results;
        DataExchange::Variant::CharacterDelimitedLines::Reader reader{{' ', '\t'}};
        for (Sequence<String> line : reader.ReadMatrix (readStream)) {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
            DbgTrace (L"in IO::FileSystem::{}::ReadMountInfo_MTabLikeFile_ linesize=%d, line[0]=%s", line.size (), line.empty () ? L"" : line[0].c_str ());
#endif
            //
            // https://www.centos.org/docs/5/html/5.2/Deployment_Guide/s2-proc-mounts.html
            //
            //  1 - device name
            //  2 - mounted on
            //  3 - fstype
            //
            if (line.size () >= 3) {
                String devName = line[0];
                // procfs/mounts often contains symbolic links to device files
                // e.g. /dev/disk/by-uuid/e1d70192-1bb0-461d-b89f-b054e45bfa00
                if (devName.StartsWith (L"/")) {
                    IgnoreExceptionsExceptThreadAbortForCall (devName = IO::FileSystem::FileSystem::Default ().CanonicalizeName (devName));
                }
                String                       mountedAt = line[1];
                String                       fstype    = line[2];
                static const String_Constant kNone_{L"none"};
                results.Add (MountedFilesystemType{mountedAt, devName == kNone_ ? Set<String>{} : Set<String>{devName}, fstype}); // special name none often used when there is no name
            }
        }
        return results;
    }
}
#if qPlatform_Linux
namespace {
    Collection<MountedFilesystemType> ReadMountInfo_FromProcFSMounts_ ()
    {
        // Note - /procfs files always unseekable
        static const String_Constant kUseFile2List_{L"/proc/mounts"};
        constexpr bool               kUseWatcher_{true}; // seems safe and much faster
        if (kUseWatcher_) {
            static const Watcher_Proc_Mounts_                                 sWatcher_{kUseFile2List_};
            static Execution::Synchronized<Collection<MountedFilesystemType>> sLastResult_;
            static bool                                                       sFirstTime_{true};
            if (sFirstTime_ or sWatcher_.IsNewAvail ()) {
                sLastResult_ = ReadMountInfo_MTabLikeFile_ (FileInputStream::mk (kUseFile2List_, FileInputStream::eNotSeekable));
                sFirstTime_  = false;
            }
            return sLastResult_;
        }
        else {
            return ReadMountInfo_MTabLikeFile_ (FileInputStream::mk (kUseFile2List_, FileInputStream::eNotSeekable));
        }
    }
}
#endif
#if qPlatform_Linux
namespace {
    Collection<MountedFilesystemType> ReadMountInfo_ETC_MTAB_ ()
    {
        // Note - /procfs files always unseekable and this is sklink to /procfs
        static const String_Constant kUseFile2List_{L"/etc/mtab"};
        return ReadMountInfo_MTabLikeFile_ (FileInputStream::mk (kUseFile2List_, FileInputStream::eNotSeekable));
    }
}
#endif
#if qPlatform_Windows
namespace {
    using DynamicDiskIDType = String;
    String GetPhysNameForDriveNumber_ (unsigned int i)
    {
        // This format is NOT super well documented, and was mostly derived from reading the remarks section
        // of https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
        // (DeviceIoControl function)
        return Characters::Format (L"\\\\.\\PhysicalDrive%d", i);
    }
    Optional<Set<DynamicDiskIDType>> GetDisksForVolume_ (String volumeName)
    {
        wchar_t volPathsBuf[10 * 1024]; // intentionally uninitialized since we dont use it if GetVolumePathNamesForVolumeNameW () returns error, and its an OUT only parameter
        DWORD   retLen = 0;
        DWORD   x      = ::GetVolumePathNamesForVolumeNameW (volumeName.c_str (), volPathsBuf, static_cast<DWORD> (NEltsOf (volPathsBuf)), &retLen);
        if (x == 0) {
            return {}; // missing - no known - not empty - answer
        }
        else if (retLen <= 1) {
            return Set<DynamicDiskIDType>{};
        }
        Assert (1 <= Characters::CString::Length (volPathsBuf) and Characters::CString::Length (volPathsBuf) < NEltsOf (volPathsBuf));
        volumeName = L"\\\\.\\" + String::FromSDKString (volPathsBuf).CircularSubString (0, -1);

        // @todo - rewrite this - must somehow otherwise callocate this to be large enuf (dynamic alloc) - if we want more disk exents, but not sure when that happens...
        VOLUME_DISK_EXTENTS volumeDiskExtents;
        {
            /*
             *  For reasons I don't understand (maybe a hit at http://superuser.com/questions/733687/give-regular-user-permission-to-access-physical-drive-on-windows)
             *  this only works with admin privilges
             */
            HANDLE hHandle = ::CreateFileW (volumeName.c_str (), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
            if (hHandle == INVALID_HANDLE_VALUE) {
                return {};
            }
            DWORD dwBytesReturned = 0;
            BOOL  bResult         = ::DeviceIoControl (hHandle, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, nullptr, 0, &volumeDiskExtents, sizeof (volumeDiskExtents), &dwBytesReturned, NULL);
            ::CloseHandle (hHandle);
            if (not bResult) {
                return {};
            }
        }
        Set<DynamicDiskIDType> result;
        for (DWORD n = 0; n < volumeDiskExtents.NumberOfDiskExtents; ++n) {
            result.Add (GetPhysNameForDriveNumber_ (volumeDiskExtents.Extents[n].DiskNumber));
        }
        return result;
    }

    Collection<MountedFilesystemType> GetMountedFilesystems_Windows_ ()
    {
        Collection<MountedFilesystemType> results{};
        TCHAR                             volumeNameBuf[1024]; // intentionally uninitialized since OUT parameter and not used unless FindFirstVolume success

        HANDLE hVol    = INVALID_HANDLE_VALUE;
        auto&& cleanup = Execution::Finally ([&]() noexcept { if (hVol != INVALID_HANDLE_VALUE) { ::CloseHandle (hVol); } });

        for (HANDLE hVol = ::FindFirstVolume (volumeNameBuf, static_cast<DWORD> (NEltsOf (volumeNameBuf))); hVol != INVALID_HANDLE_VALUE;) {
            DWORD lpMaximumComponentLength;
            DWORD dwSysFlags;
            TCHAR fileSysNameBuf[1024];
            if (::GetVolumeInformation (volumeNameBuf, nullptr, static_cast<DWORD> (NEltsOf (volumeNameBuf)), nullptr, &lpMaximumComponentLength, &dwSysFlags, fileSysNameBuf, static_cast<DWORD> (NEltsOf (fileSysNameBuf)))) {
                MountedFilesystemType v;
                v.fFileSystemType = String::FromSDKString (fileSysNameBuf);
                v.fVolumeID       = String::FromSDKString (volumeNameBuf);
                v.fDevicePaths    = GetDisksForVolume_ (volumeNameBuf);

                TCHAR volPathsBuf[10 * 1024]; // intentionally uninitialized
                DWORD retLen = 0;
                DWORD x      = ::GetVolumePathNamesForVolumeName (volumeNameBuf, volPathsBuf, static_cast<DWORD> (NEltsOf (volPathsBuf)), &retLen);
                if (x == 0) {
                    DbgTrace (SDKSTR ("Ignoring error getting paths (volume='%s')"), volumeNameBuf);
                }
                else if (volPathsBuf[0] == 0) {
                    // Ignore - unmounted!
                    DbgTrace (SDKSTR ("Ignoring unmounted filesystem (volume='%s')"), volumeNameBuf);
                }
                else {
                    for (const TCHAR* NameIdx = volPathsBuf; NameIdx[0] != L'\0'; NameIdx += Characters::CString::Length (NameIdx) + 1) {
                        v.fMountedOn = String::FromSDKString (NameIdx);
                        results.Add (v);
                    }
                }
            }

            // find next
            if (not::FindNextVolume (hVol, volumeNameBuf, static_cast<DWORD> (NEltsOf (volumeNameBuf)))) {
                ::FindVolumeClose (hVol);
                hVol = INVALID_HANDLE_VALUE;
            }
        }
        return results;
    }
}
#endif

/*
 ********************************************************************************
 ******************** IO::FileSystem::MountedFilesystemType *********************
 ********************************************************************************
 */
String MountedFilesystemType::ToString () const
{
    StringBuilder sb;
    sb += L"{";
    sb += L"Mounted-On: '" + fMountedOn + L"', ";
    if (fDevicePaths) {
        sb += L"Device-Paths: " + Characters::ToString (*fDevicePaths) + L", ";
    }
    if (fFileSystemType) {
        sb += L"FileSystem-Type: '" + *fFileSystemType + L"', ";
    }
    if (fVolumeID) {
        sb += L"Volume-ID: '" + *fVolumeID + L"', ";
    }
    sb += L"}";
    return sb.str ();
}

/*
 ********************************************************************************
 ******************** IO::FileSystem::GetMountedFilesystems *********************
 ********************************************************************************
 */
Containers::Collection<MountedFilesystemType> IO::FileSystem::GetMountedFilesystems ()
{
#if qPlatform_Linux
    return ReadMountInfo_FromProcFSMounts_ ();
#elif qPlatform_Windows
    return GetMountedFilesystems_Windows_ ();
#else
    AssertNotImplemented ();
    return {};
#endif
}
<commit_msg>for macos - use IO::FileSystem::GetMountedFilesystems () WeakAssert<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2017.  All rights reserved
 */
#include "../../StroikaPreComp.h"

#if qPlatform_Linux
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>
#elif qPlatform_Windows
#include <Windows.h>
#include <winioctl.h>
#endif

#include "../../Characters/CString/Utilities.h"
#include "../../Characters/Format.h"
#include "../../Characters/String.h"
#include "../../Characters/StringBuilder.h"
#include "../../Characters/String_Constant.h"
#include "../../Characters/ToString.h"
#include "../../DataExchange/Variant/CharacterDelimitedLines/Reader.h"
#include "../../Execution/Finally.h"
#include "../../Execution/Synchronized.h"
#include "../../Memory/SmallStackBuffer.h"

#include "FileInputStream.h"
#include "FileSystem.h"

#include "MountedFilesystem.h"

using namespace Stroika::Foundation;
using namespace Stroika::Foundation::Characters;
using namespace Stroika::Foundation::Containers;
using namespace Stroika::Foundation::IO;
using namespace Stroika::Foundation::IO::FileSystem;

// Comment this in to turn on aggressive noisy DbgTrace in this module
//#define   USE_NOISY_TRACE_IN_THIS_MODULE_       1

#if qPlatform_Linux
namespace {
    // This is quirky, and only works for Linux, and /proc/mounts
    struct Watcher_Proc_Mounts_ {
        int mfd;
        Watcher_Proc_Mounts_ (const String& fn)
            : mfd (::open (fn.AsNarrowSDKString ().c_str (), O_RDONLY, 0))
        {
        }
        ~Watcher_Proc_Mounts_ ()
        {
            ::close (mfd);
        }
        bool IsNewAvail () const
        {
            // according to http://stackoverflow.com/questions/5070801/monitoring-mount-point-changes-via-proc-mounts
            // have to use poll with POLLPRI | POLLERR flags
            //
            // and from https://linux.die.net/man/5/proc
            //      /proc/[pid]/mounts (since Linux 2.4.19)...
            //      Since kernel version 2.6.15, this file is pollable: after opening the file for reading, a change in this file
            //      (i.e., a file system mount or unmount) causes select(2) to mark the file descriptor as readable, and poll(2)
            //      and epoll_wait(2) mark the file as having an error condition.
            struct pollfd pfd;
            int           rv;
            int           changes = 0;
            pfd.fd                = mfd;
            pfd.events            = POLLERR | POLLPRI;
            pfd.revents           = 0;
            if ((rv = poll (&pfd, 1, 0)) >= 0) {
                if (pfd.revents & POLLERR) {
                    return true;
                }
            }
            return false;
        }
    };
}
#endif

namespace {
    /*
     *  Something like this is used on many unix systems.
     */
    Collection<MountedFilesystemType> ReadMountInfo_MTabLikeFile_ (const Streams::InputStream<Memory::Byte>& readStream)
    {
        /*
         *  I haven't found this clearly documented yet, but it appears that a filesystem can be over-mounted.
         *  See https://www.kernel.org/doc/Documentation/filesystems/ramfs-rootfs-initramfs.txt
         *
         *  So the last one with a given mount point in the file wins.
         */
        Collection<MountedFilesystemType>                      results;
        DataExchange::Variant::CharacterDelimitedLines::Reader reader{{' ', '\t'}};
        for (Sequence<String> line : reader.ReadMatrix (readStream)) {
#if USE_NOISY_TRACE_IN_THIS_MODULE_
            DbgTrace (L"in IO::FileSystem::{}::ReadMountInfo_MTabLikeFile_ linesize=%d, line[0]=%s", line.size (), line.empty () ? L"" : line[0].c_str ());
#endif
            //
            // https://www.centos.org/docs/5/html/5.2/Deployment_Guide/s2-proc-mounts.html
            //
            //  1 - device name
            //  2 - mounted on
            //  3 - fstype
            //
            if (line.size () >= 3) {
                String devName = line[0];
                // procfs/mounts often contains symbolic links to device files
                // e.g. /dev/disk/by-uuid/e1d70192-1bb0-461d-b89f-b054e45bfa00
                if (devName.StartsWith (L"/")) {
                    IgnoreExceptionsExceptThreadAbortForCall (devName = IO::FileSystem::FileSystem::Default ().CanonicalizeName (devName));
                }
                String                       mountedAt = line[1];
                String                       fstype    = line[2];
                static const String_Constant kNone_{L"none"};
                results.Add (MountedFilesystemType{mountedAt, devName == kNone_ ? Set<String>{} : Set<String>{devName}, fstype}); // special name none often used when there is no name
            }
        }
        return results;
    }
}
#if qPlatform_Linux
namespace {
    Collection<MountedFilesystemType> ReadMountInfo_FromProcFSMounts_ ()
    {
        // Note - /procfs files always unseekable
        static const String_Constant kUseFile2List_{L"/proc/mounts"};
        constexpr bool               kUseWatcher_{true}; // seems safe and much faster
        if (kUseWatcher_) {
            static const Watcher_Proc_Mounts_                                 sWatcher_{kUseFile2List_};
            static Execution::Synchronized<Collection<MountedFilesystemType>> sLastResult_;
            static bool                                                       sFirstTime_{true};
            if (sFirstTime_ or sWatcher_.IsNewAvail ()) {
                sLastResult_ = ReadMountInfo_MTabLikeFile_ (FileInputStream::mk (kUseFile2List_, FileInputStream::eNotSeekable));
                sFirstTime_  = false;
            }
            return sLastResult_;
        }
        else {
            return ReadMountInfo_MTabLikeFile_ (FileInputStream::mk (kUseFile2List_, FileInputStream::eNotSeekable));
        }
    }
}
#endif
#if qPlatform_Linux
namespace {
    Collection<MountedFilesystemType> ReadMountInfo_ETC_MTAB_ ()
    {
        // Note - /procfs files always unseekable and this is sklink to /procfs
        static const String_Constant kUseFile2List_{L"/etc/mtab"};
        return ReadMountInfo_MTabLikeFile_ (FileInputStream::mk (kUseFile2List_, FileInputStream::eNotSeekable));
    }
}
#endif
#if qPlatform_Windows
namespace {
    using DynamicDiskIDType = String;
    String GetPhysNameForDriveNumber_ (unsigned int i)
    {
        // This format is NOT super well documented, and was mostly derived from reading the remarks section
        // of https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396
        // (DeviceIoControl function)
        return Characters::Format (L"\\\\.\\PhysicalDrive%d", i);
    }
    Optional<Set<DynamicDiskIDType>> GetDisksForVolume_ (String volumeName)
    {
        wchar_t volPathsBuf[10 * 1024]; // intentionally uninitialized since we dont use it if GetVolumePathNamesForVolumeNameW () returns error, and its an OUT only parameter
        DWORD   retLen = 0;
        DWORD   x      = ::GetVolumePathNamesForVolumeNameW (volumeName.c_str (), volPathsBuf, static_cast<DWORD> (NEltsOf (volPathsBuf)), &retLen);
        if (x == 0) {
            return {}; // missing - no known - not empty - answer
        }
        else if (retLen <= 1) {
            return Set<DynamicDiskIDType>{};
        }
        Assert (1 <= Characters::CString::Length (volPathsBuf) and Characters::CString::Length (volPathsBuf) < NEltsOf (volPathsBuf));
        volumeName = L"\\\\.\\" + String::FromSDKString (volPathsBuf).CircularSubString (0, -1);

        // @todo - rewrite this - must somehow otherwise callocate this to be large enuf (dynamic alloc) - if we want more disk exents, but not sure when that happens...
        VOLUME_DISK_EXTENTS volumeDiskExtents;
        {
            /*
             *  For reasons I don't understand (maybe a hit at http://superuser.com/questions/733687/give-regular-user-permission-to-access-physical-drive-on-windows)
             *  this only works with admin privilges
             */
            HANDLE hHandle = ::CreateFileW (volumeName.c_str (), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
            if (hHandle == INVALID_HANDLE_VALUE) {
                return {};
            }
            DWORD dwBytesReturned = 0;
            BOOL  bResult         = ::DeviceIoControl (hHandle, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, nullptr, 0, &volumeDiskExtents, sizeof (volumeDiskExtents), &dwBytesReturned, NULL);
            ::CloseHandle (hHandle);
            if (not bResult) {
                return {};
            }
        }
        Set<DynamicDiskIDType> result;
        for (DWORD n = 0; n < volumeDiskExtents.NumberOfDiskExtents; ++n) {
            result.Add (GetPhysNameForDriveNumber_ (volumeDiskExtents.Extents[n].DiskNumber));
        }
        return result;
    }

    Collection<MountedFilesystemType> GetMountedFilesystems_Windows_ ()
    {
        Collection<MountedFilesystemType> results{};
        TCHAR                             volumeNameBuf[1024]; // intentionally uninitialized since OUT parameter and not used unless FindFirstVolume success

        HANDLE hVol    = INVALID_HANDLE_VALUE;
        auto&& cleanup = Execution::Finally ([&]() noexcept { if (hVol != INVALID_HANDLE_VALUE) { ::CloseHandle (hVol); } });

        for (HANDLE hVol = ::FindFirstVolume (volumeNameBuf, static_cast<DWORD> (NEltsOf (volumeNameBuf))); hVol != INVALID_HANDLE_VALUE;) {
            DWORD lpMaximumComponentLength;
            DWORD dwSysFlags;
            TCHAR fileSysNameBuf[1024];
            if (::GetVolumeInformation (volumeNameBuf, nullptr, static_cast<DWORD> (NEltsOf (volumeNameBuf)), nullptr, &lpMaximumComponentLength, &dwSysFlags, fileSysNameBuf, static_cast<DWORD> (NEltsOf (fileSysNameBuf)))) {
                MountedFilesystemType v;
                v.fFileSystemType = String::FromSDKString (fileSysNameBuf);
                v.fVolumeID       = String::FromSDKString (volumeNameBuf);
                v.fDevicePaths    = GetDisksForVolume_ (volumeNameBuf);

                TCHAR volPathsBuf[10 * 1024]; // intentionally uninitialized
                DWORD retLen = 0;
                DWORD x      = ::GetVolumePathNamesForVolumeName (volumeNameBuf, volPathsBuf, static_cast<DWORD> (NEltsOf (volPathsBuf)), &retLen);
                if (x == 0) {
                    DbgTrace (SDKSTR ("Ignoring error getting paths (volume='%s')"), volumeNameBuf);
                }
                else if (volPathsBuf[0] == 0) {
                    // Ignore - unmounted!
                    DbgTrace (SDKSTR ("Ignoring unmounted filesystem (volume='%s')"), volumeNameBuf);
                }
                else {
                    for (const TCHAR* NameIdx = volPathsBuf; NameIdx[0] != L'\0'; NameIdx += Characters::CString::Length (NameIdx) + 1) {
                        v.fMountedOn = String::FromSDKString (NameIdx);
                        results.Add (v);
                    }
                }
            }

            // find next
            if (not::FindNextVolume (hVol, volumeNameBuf, static_cast<DWORD> (NEltsOf (volumeNameBuf)))) {
                ::FindVolumeClose (hVol);
                hVol = INVALID_HANDLE_VALUE;
            }
        }
        return results;
    }
}
#endif

/*
 ********************************************************************************
 ******************** IO::FileSystem::MountedFilesystemType *********************
 ********************************************************************************
 */
String MountedFilesystemType::ToString () const
{
    StringBuilder sb;
    sb += L"{";
    sb += L"Mounted-On: '" + fMountedOn + L"', ";
    if (fDevicePaths) {
        sb += L"Device-Paths: " + Characters::ToString (*fDevicePaths) + L", ";
    }
    if (fFileSystemType) {
        sb += L"FileSystem-Type: '" + *fFileSystemType + L"', ";
    }
    if (fVolumeID) {
        sb += L"Volume-ID: '" + *fVolumeID + L"', ";
    }
    sb += L"}";
    return sb.str ();
}

/*
 ********************************************************************************
 ******************** IO::FileSystem::GetMountedFilesystems *********************
 ********************************************************************************
 */
Containers::Collection<MountedFilesystemType> IO::FileSystem::GetMountedFilesystems ()
{
#if qPlatform_Linux
    return ReadMountInfo_FromProcFSMounts_ ();
#elif qPlatform_Windows
    return GetMountedFilesystems_Windows_ ();
#else
	// @todo - maybe a start on macos would be to walk the directory /Volumes
    WeakAssertNotImplemented ();
    return {};
#endif
}
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2011.  All rights reserved
 */
#include	"../../StroikaPreComp.h"

#include	"../../Execution/Exceptions.h"
#include	"../../Execution/ErrNoException.h"

#include	"FStreamSupport.h"

using	namespace	Stroika::Foundation;
using	namespace	Stroika::Foundation::Streams;
using	namespace	Stroika::Foundation::Streams::iostream;

/*
 ********************************************************************************
 ********************** iostream::OpenInputFileStream ***************************
 ********************************************************************************
 */
void	Streams::iostream::OpenInputFileStream (ifstream* ifStream, const TString& fileName, ios_base::openmode _Mode)
{
	RequireNotNull (ifStream);
	ifStream->open (fileName.c_str (), _Mode);
	if (!(*ifStream)) {
		#if		qPlatform_Windows
			Execution::ThrowIfNotERROR_SUCCESS (::GetLastError ());
		#elif	qPlatform_POSIX
			Execution::ThrowIfError_errno_t ();
			AssertNotReached ();// errno sb set
		#else
			AssertNotImplemented ();
		#endif
	}
}

/*
 ********************************************************************************
 ****************** StreamUtils::OpenOutputFileStream ***************************
 ********************************************************************************
 */
void	Streams::iostream::OpenOutputFileStream (ofstream* ofStream, const TString& fileName, ios_base::openmode _Mode)
{
	RequireNotNull (ofStream);
	ofStream->open (fileName.c_str (), _Mode);
	if (!(*ofStream)) {
		#if		qPlatform_Windows
			Execution::ThrowIfNotERROR_SUCCESS (::GetLastError ());
		#elif	qPlatform_POSIX
			Execution::ThrowIfError_errno_t ();
			AssertNotReached ();// errno sb set
		#else
			AssertNotImplemented ();
		#endif
	}
}

<commit_msg>fix Streams::iostream::Open-READ/WRITE Streams now use CATCH_REBIND_FILENAMES_HELPER_() to include filename in exceptions<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2011.  All rights reserved
 */
#include	"../../StroikaPreComp.h"

#include	"../../Execution/Exceptions.h"
#include	"../../Execution/ErrNoException.h"
#include	"../../IO/FileAccessException.h"
#include	"../../IO/FileBusyException.h"
#include	"../../IO/FileFormatException.h"

#include	"FStreamSupport.h"

using	namespace	Stroika::Foundation;
using	namespace	Stroika::Foundation::Streams;
using	namespace	Stroika::Foundation::Streams::iostream;
using	namespace	Stroika::Foundation::IO;

/*
 * Stuff  INSIDE try section raises exceptions. Catch and rethow SOME binding in a new filename (if none was known).
 * Otehr exceptions just ignore (so they auto-propagate)
 */
#define		CATCH_REBIND_FILENAMES_HELPER_(USEFILENAME)	\
	catch (const FileBusyException& e) {	\
		if (e.fFileName.empty ()) {\
			Execution::DoThrow (FileBusyException (USEFILENAME));\
		}\
		Execution::DoReThrow ();\
	}\
	catch (const FileAccessException& e) {	\
		if (e.fFileName.empty ()) {\
			Execution::DoThrow (FileAccessException (USEFILENAME, e.fFileAccessMode));\
		}\
		Execution::DoReThrow ();\
	}\
	catch (const FileFormatException& e) {	\
		if (e.fFileName.empty ()) {\
			Execution::DoThrow (FileFormatException (USEFILENAME));\
		}\
		Execution::DoReThrow ();\
	}\

/*
 ********************************************************************************
 ********************** iostream::OpenInputFileStream ***************************
 ********************************************************************************
 */
void	Streams::iostream::OpenInputFileStream (ifstream* ifStream, const TString& fileName, ios_base::openmode _Mode)
{
	RequireNotNull (ifStream);
	try {
		ifStream->open (fileName.c_str (), _Mode);
		if (!(*ifStream)) {
			#if		qPlatform_Windows
				Execution::ThrowIfNotERROR_SUCCESS (::GetLastError ());
			#elif	qPlatform_POSIX
				Execution::ThrowIfError_errno_t ();
				AssertNotReached ();// errno sb set
			#else
				AssertNotImplemented ();
			#endif
		}
	}
	CATCH_REBIND_FILENAMES_HELPER_(fileName);
}

/*
 ********************************************************************************
 ****************** StreamUtils::OpenOutputFileStream ***************************
 ********************************************************************************
 */
void	Streams::iostream::OpenOutputFileStream (ofstream* ofStream, const TString& fileName, ios_base::openmode _Mode)
{
	RequireNotNull (ofStream);
	try {
		ofStream->open (fileName.c_str (), _Mode);
		if (!(*ofStream)) {
			#if		qPlatform_Windows
				Execution::ThrowIfNotERROR_SUCCESS (::GetLastError ());
			#elif	qPlatform_POSIX
				Execution::ThrowIfError_errno_t ();
				AssertNotReached ();// errno sb set
			#else
				AssertNotImplemented ();
			#endif
		}
	}
	CATCH_REBIND_FILENAMES_HELPER_(fileName);
}

<|endoftext|>


<commit_before>/*
 * LootManagerImplementation.cpp
 *
 *  Created on: Jun 20, 2011
 *      Author: Kyle
 */

#include "engine/engine.h"

#include "LootManager.h"
#include "server/zone/objects/scene/SceneObject.h"
#include "server/zone/objects/creature/CreatureObject.h"
#include "server/zone/objects/creature/AiAgent.h"
#include "server/zone/managers/crafting/CraftingManager.h"
#include "server/zone/managers/templates/TemplateManager.h"
#include "server/zone/templates/LootItemTemplate.h"
#include "server/zone/templates/LootGroupTemplate.h"
#include "server/zone/ZoneServer.h"
#include "server/zone/managers/stringid/StringIdManager.h"
#include "LootGroupMap.h"

void LootManagerImplementation::initialize() {
	lua = new Lua();
	lua->init();

	info("Loading configuration.");

	if(!loadConfigData()) {

		loadDefaultConfig();

		info("Failed to load configuration values. Using default.");
	}

	lootGroupMap = LootGroupMap::instance();
	lootGroupMap->initialize();

	info("Loaded " + String::valueOf(lootableMods.size()) + " lootable stat mods.", true);
	info("Loaded " + String::valueOf(lootGroupMap->countLootItemTemplates()) + " loot items.", true);
	info("Loaded " + String::valueOf(lootGroupMap->countLootGroupTemplates()) + " loot groups.", true);

	info("Initialized.");
}

bool LootManagerImplementation::loadConfigFile() {
	return lua->runFile("scripts/managers/loot_manager.lua");
}

bool LootManagerImplementation::loadConfigData() {
	if (!loadConfigFile())
		return false;

	exceptionalChance = lua->getGlobalFloat("exceptionalChance");
	exceptionalModifier = lua->getGlobalFloat("exceptionalModifier");
	legendaryChance = lua->getGlobalFloat("legendaryChance");
	legendaryModifier = lua->getGlobalFloat("legendaryModifier");

	LuaObject lootableModsTable = lua->getGlobalObject("lootableStatMods");

	if (!lootableModsTable.isValidTable())
		return false;

	for (int i = 1; i <= lootableModsTable.getTableSize(); ++i) {
		String mod = lootableModsTable.getStringAt(i);
		lootableMods.put(mod);
	}

	lootableModsTable.pop();

	return true;
}

void LootManagerImplementation::loadDefaultConfig() {

}

void LootManagerImplementation::setInitialObjectStats(LootItemTemplate* templateObject, CraftingValues* craftingValues, TangibleObject* prototype) {
	SharedTangibleObjectTemplate* tanoTemplate = dynamic_cast<SharedTangibleObjectTemplate*>(prototype->getObjectTemplate());

	if (tanoTemplate != NULL) {
		Vector<String>* titles = tanoTemplate->getExperimentalGroupTitles();
		Vector<String>* props = tanoTemplate->getExperimentalSubGroupTitles();
		Vector<float>* mins = tanoTemplate->getExperimentalMin();
		Vector<float>* maxs = tanoTemplate->getExperimentalMax();
		Vector<short>* prec = tanoTemplate->getExperimentalPrecision();

		for (int i = 0; i < props->size(); ++i) {
			String title = titles->get(i);
			String property = props->get(i);

			if (craftingValues->hasProperty(property))
				continue;

			craftingValues->addExperimentalProperty(property, property, mins->get(i), maxs->get(i), prec->get(i), false);
			if(title == "null")
				craftingValues->setHidden(property);
		}
	}

	Vector<String>* customizationData = templateObject->getCustomizationStringNames();
	Vector<Vector<int> >* customizationValues = templateObject->getCustomizationValues();

	for (int i = 0; i < customizationData->size(); ++i) {
		String customizationString = customizationData->get(i);
		Vector<int>* values = &customizationValues->get(i);

		int idx = customizationString.lastIndexOf("/");

		if (idx != -1)
			customizationString = customizationString.subString(idx + 1);

		if (values->size() > 0) {
			int randomValue = values->get(System::random(values->size() - 1));

			prototype->setCustomizationVariable(customizationString, randomValue, false);
		}
	}
}

void LootManagerImplementation::setCustomObjectName(TangibleObject* object, LootItemTemplate* templateObject) {
	String customName = templateObject->getCustomObjectName();

	if (!customName.isEmpty()) {
		if (customName.charAt(0) == '@') {
			StringId stringId(customName);

			object->setObjectName(stringId);
		} else {
			object->setCustomObjectName(customName, false);
		}
	}
}

int LootManagerImplementation::calculateLootCredits(int level) {
	int maxcredits = (int) round((.03f * level * level) + (3 * level) + 50);
	int mincredits = (int) round((((float) maxcredits) * .5f) + (2.0f * level));

	int credits = mincredits + System::random(maxcredits - mincredits);

	return credits;
}

TangibleObject* LootManagerImplementation::createLootObject(LootItemTemplate* templateObject, int level) {

	if(level > 300)
		level = 300;

	String directTemplateObject = templateObject->getDirectObjectTemplate();

	ManagedReference<TangibleObject*> prototype = dynamic_cast<TangibleObject*> (zoneServer->createObject(directTemplateObject.hashCode(), 2));

	if (prototype == NULL)
		return NULL;

	prototype->createChildObjects();

	String serial = craftingManager->generateSerial();
	prototype->setSerialNumber(serial);

	CraftingValues craftingValues = templateObject->getCraftingValuesCopy();

	setInitialObjectStats(templateObject, &craftingValues, prototype);

	setCustomObjectName(prototype, templateObject);

	float excMod = 1.0;

	if (System::random(exceptionalChance) == exceptionalChance) {
		UnicodeString objectName = prototype->getCustomObjectName();
		uint32 bitmask = prototype->getOptionsBitmask() | OptionBitmask::YELLOW;

		if (objectName.isEmpty())
			objectName = StringIdManager::instance()->getStringId(prototype->getObjectName()->getFullPath().hashCode());

		UnicodeString newName = objectName + " (Exceptional)";
		prototype->setCustomObjectName(newName, false);

		excMod = exceptionalModifier;

		prototype->setOptionsBitmask(bitmask, false);
	} else if (System::random(legendaryChance) == legendaryChance) {
		UnicodeString objectName = prototype->getCustomObjectName();
		uint32 bitmask = prototype->getOptionsBitmask() | OptionBitmask::YELLOW;

		if (objectName.isEmpty())
			objectName = StringIdManager::instance()->getStringId(prototype->getObjectName()->getFullPath().hashCode());

		UnicodeString newName = objectName + " (Legendary)";
		prototype->setCustomObjectName(newName, false);

		excMod = legendaryModifier;

		prototype->setOptionsBitmask(bitmask, false);
	}

	String subtitle;

	float percentage = System::random(10000) / 10000.f; //Generate a base percentage. We will deviate slightly from this on each stat.

	for (int i = 0; i < craftingValues.getExperimentalPropertySubtitleSize(); ++i) {
		subtitle = craftingValues.getExperimentalPropertySubtitle(i);

		if (subtitle == "hitpoints")
			continue;

		float min = craftingValues.getMinValue(subtitle);
		float max = craftingValues.getMaxValue(subtitle);

		if(min == max)
			continue;

		if (subtitle != "useCount" &&
				subtitle != "quantity" &&
				subtitle != "charges" &&
				subtitle != "uses" &&
				subtitle != "charge") {

			float minMod = (max >= min) ? 2000.f : -2000.f;
			float maxMod = (max >= min) ? 500.f : -500.f;

			min = (min * level / minMod) + min;
			max = (max * level / maxMod) + max;

			if (max >= min) {
				min *= excMod;
				max *= excMod;
			} else {
				min /= excMod;
				max /= excMod;
			}

			craftingValues.setMinValue(subtitle, min);
			craftingValues.setMaxValue(subtitle, max);
		}

		float deviation = (((float) System::random(400)) - 200) / 1000.f; //Deviate up to 2%

		craftingValues.setCurrentPercentage(subtitle, percentage + deviation);
	}

	// Use percentages to recalculate the values
	craftingValues.recalculateValues(false);

	craftingValues.addExperimentalProperty("creatureLevel", "creatureLevel", level, level, 0, false);
	craftingValues.setHidden("creatureLevel");

	// Update the Tano with new values
	prototype->updateCraftingValues(&craftingValues, true);

	return prototype;
}

bool LootManagerImplementation::createLoot(SceneObject* container, AiAgent* creature) {
	LootGroupCollection* lootCollection = creature->getLootGroups();

	if (lootCollection == NULL)
		return false;

	for (int i = 0; i < lootCollection->count(); ++i) {
		LootGroupCollectionEntry* entry = lootCollection->get(i);
		int lootChance = entry->getLootChance();

		if (lootChance <= 0)
			continue;

		int roll = System::random(10000000);

		if (roll > lootChance)
			continue;

		int tempChance = 0; //Start at 0.

		LootGroups* lootGroups = entry->getLootGroups();

		//Select the loot group to use.
		for (int i = 0; i < lootGroups->count(); ++i) {
			LootGroupEntry* entry = lootGroups->get(i);

			tempChance += entry->getLootChance();

			//Is this entry lower than the roll? If yes, then we want to try the next entry.
			if (tempChance < roll)
				continue;

			createLoot(container, entry->getLootGroupName(), creature->getLevel());

			break;
		}
	}

	return true;
}

bool LootManagerImplementation::createLoot(SceneObject* container, const String& lootGroup, int level) {
	if (container->hasFullContainerObjects())
		return false;

	Reference<LootGroupTemplate*> group = lootGroupMap->getLootGroupTemplate(lootGroup);

	if (group == NULL) {
		warning("Loot group template requested does not exist: " + lootGroup);
		return false;
	}

	//Now we do the second roll for the item out of the group.
	int roll = System::random(10000000);

	Reference<LootItemTemplate*> itemTemplate = lootGroupMap->getLootItemTemplate(group->getLootItemTemplateForRoll(roll));

	if (itemTemplate == NULL) {
		warning("Loot item template requested does not exist: " + group->getLootItemTemplateForRoll(roll) + " for templateName: " + group->getTemplateName());
		return false;
	}

	TangibleObject* obj = createLootObject(itemTemplate, level);

	if (obj == NULL)
		return false;

	if (container->transferObject(obj, -1, false))
		container->broadcastObject(obj, true);

	return true;
}
<commit_msg>[added] debug message when creating unexistent loot<commit_after>/*
 * LootManagerImplementation.cpp
 *
 *  Created on: Jun 20, 2011
 *      Author: Kyle
 */

#include "engine/engine.h"

#include "LootManager.h"
#include "server/zone/objects/scene/SceneObject.h"
#include "server/zone/objects/creature/CreatureObject.h"
#include "server/zone/objects/creature/AiAgent.h"
#include "server/zone/managers/crafting/CraftingManager.h"
#include "server/zone/managers/templates/TemplateManager.h"
#include "server/zone/templates/LootItemTemplate.h"
#include "server/zone/templates/LootGroupTemplate.h"
#include "server/zone/ZoneServer.h"
#include "server/zone/managers/stringid/StringIdManager.h"
#include "LootGroupMap.h"

void LootManagerImplementation::initialize() {
	lua = new Lua();
	lua->init();

	info("Loading configuration.");

	if(!loadConfigData()) {

		loadDefaultConfig();

		info("Failed to load configuration values. Using default.");
	}

	lootGroupMap = LootGroupMap::instance();
	lootGroupMap->initialize();

	info("Loaded " + String::valueOf(lootableMods.size()) + " lootable stat mods.", true);
	info("Loaded " + String::valueOf(lootGroupMap->countLootItemTemplates()) + " loot items.", true);
	info("Loaded " + String::valueOf(lootGroupMap->countLootGroupTemplates()) + " loot groups.", true);

	info("Initialized.");
}

bool LootManagerImplementation::loadConfigFile() {
	return lua->runFile("scripts/managers/loot_manager.lua");
}

bool LootManagerImplementation::loadConfigData() {
	if (!loadConfigFile())
		return false;

	exceptionalChance = lua->getGlobalFloat("exceptionalChance");
	exceptionalModifier = lua->getGlobalFloat("exceptionalModifier");
	legendaryChance = lua->getGlobalFloat("legendaryChance");
	legendaryModifier = lua->getGlobalFloat("legendaryModifier");

	LuaObject lootableModsTable = lua->getGlobalObject("lootableStatMods");

	if (!lootableModsTable.isValidTable())
		return false;

	for (int i = 1; i <= lootableModsTable.getTableSize(); ++i) {
		String mod = lootableModsTable.getStringAt(i);
		lootableMods.put(mod);
	}

	lootableModsTable.pop();

	return true;
}

void LootManagerImplementation::loadDefaultConfig() {

}

void LootManagerImplementation::setInitialObjectStats(LootItemTemplate* templateObject, CraftingValues* craftingValues, TangibleObject* prototype) {
	SharedTangibleObjectTemplate* tanoTemplate = dynamic_cast<SharedTangibleObjectTemplate*>(prototype->getObjectTemplate());

	if (tanoTemplate != NULL) {
		Vector<String>* titles = tanoTemplate->getExperimentalGroupTitles();
		Vector<String>* props = tanoTemplate->getExperimentalSubGroupTitles();
		Vector<float>* mins = tanoTemplate->getExperimentalMin();
		Vector<float>* maxs = tanoTemplate->getExperimentalMax();
		Vector<short>* prec = tanoTemplate->getExperimentalPrecision();

		for (int i = 0; i < props->size(); ++i) {
			String title = titles->get(i);
			String property = props->get(i);

			if (craftingValues->hasProperty(property))
				continue;

			craftingValues->addExperimentalProperty(property, property, mins->get(i), maxs->get(i), prec->get(i), false);
			if(title == "null")
				craftingValues->setHidden(property);
		}
	}

	Vector<String>* customizationData = templateObject->getCustomizationStringNames();
	Vector<Vector<int> >* customizationValues = templateObject->getCustomizationValues();

	for (int i = 0; i < customizationData->size(); ++i) {
		String customizationString = customizationData->get(i);
		Vector<int>* values = &customizationValues->get(i);

		int idx = customizationString.lastIndexOf("/");

		if (idx != -1)
			customizationString = customizationString.subString(idx + 1);

		if (values->size() > 0) {
			int randomValue = values->get(System::random(values->size() - 1));

			prototype->setCustomizationVariable(customizationString, randomValue, false);
		}
	}
}

void LootManagerImplementation::setCustomObjectName(TangibleObject* object, LootItemTemplate* templateObject) {
	String customName = templateObject->getCustomObjectName();

	if (!customName.isEmpty()) {
		if (customName.charAt(0) == '@') {
			StringId stringId(customName);

			object->setObjectName(stringId);
		} else {
			object->setCustomObjectName(customName, false);
		}
	}
}

int LootManagerImplementation::calculateLootCredits(int level) {
	int maxcredits = (int) round((.03f * level * level) + (3 * level) + 50);
	int mincredits = (int) round((((float) maxcredits) * .5f) + (2.0f * level));

	int credits = mincredits + System::random(maxcredits - mincredits);

	return credits;
}

TangibleObject* LootManagerImplementation::createLootObject(LootItemTemplate* templateObject, int level) {

	if(level > 300)
		level = 300;

	String directTemplateObject = templateObject->getDirectObjectTemplate();

	ManagedReference<TangibleObject*> prototype = dynamic_cast<TangibleObject*> (zoneServer->createObject(directTemplateObject.hashCode(), 2));

	if (prototype == NULL) {
		error("could not create loot object: " + directTemplateObject);
		return NULL;
	}

	prototype->createChildObjects();

	String serial = craftingManager->generateSerial();
	prototype->setSerialNumber(serial);

	CraftingValues craftingValues = templateObject->getCraftingValuesCopy();

	setInitialObjectStats(templateObject, &craftingValues, prototype);

	setCustomObjectName(prototype, templateObject);

	float excMod = 1.0;

	if (System::random(exceptionalChance) == exceptionalChance) {
		UnicodeString objectName = prototype->getCustomObjectName();
		uint32 bitmask = prototype->getOptionsBitmask() | OptionBitmask::YELLOW;

		if (objectName.isEmpty())
			objectName = StringIdManager::instance()->getStringId(prototype->getObjectName()->getFullPath().hashCode());

		UnicodeString newName = objectName + " (Exceptional)";
		prototype->setCustomObjectName(newName, false);

		excMod = exceptionalModifier;

		prototype->setOptionsBitmask(bitmask, false);
	} else if (System::random(legendaryChance) == legendaryChance) {
		UnicodeString objectName = prototype->getCustomObjectName();
		uint32 bitmask = prototype->getOptionsBitmask() | OptionBitmask::YELLOW;

		if (objectName.isEmpty())
			objectName = StringIdManager::instance()->getStringId(prototype->getObjectName()->getFullPath().hashCode());

		UnicodeString newName = objectName + " (Legendary)";
		prototype->setCustomObjectName(newName, false);

		excMod = legendaryModifier;

		prototype->setOptionsBitmask(bitmask, false);
	}

	String subtitle;

	float percentage = System::random(10000) / 10000.f; //Generate a base percentage. We will deviate slightly from this on each stat.

	for (int i = 0; i < craftingValues.getExperimentalPropertySubtitleSize(); ++i) {
		subtitle = craftingValues.getExperimentalPropertySubtitle(i);

		if (subtitle == "hitpoints")
			continue;

		float min = craftingValues.getMinValue(subtitle);
		float max = craftingValues.getMaxValue(subtitle);

		if(min == max)
			continue;

		if (subtitle != "useCount" &&
				subtitle != "quantity" &&
				subtitle != "charges" &&
				subtitle != "uses" &&
				subtitle != "charge") {

			float minMod = (max >= min) ? 2000.f : -2000.f;
			float maxMod = (max >= min) ? 500.f : -500.f;

			min = (min * level / minMod) + min;
			max = (max * level / maxMod) + max;

			if (max >= min) {
				min *= excMod;
				max *= excMod;
			} else {
				min /= excMod;
				max /= excMod;
			}

			craftingValues.setMinValue(subtitle, min);
			craftingValues.setMaxValue(subtitle, max);
		}

		float deviation = (((float) System::random(400)) - 200) / 1000.f; //Deviate up to 2%

		craftingValues.setCurrentPercentage(subtitle, percentage + deviation);
	}

	// Use percentages to recalculate the values
	craftingValues.recalculateValues(false);

	craftingValues.addExperimentalProperty("creatureLevel", "creatureLevel", level, level, 0, false);
	craftingValues.setHidden("creatureLevel");

	// Update the Tano with new values
	prototype->updateCraftingValues(&craftingValues, true);

	return prototype;
}

bool LootManagerImplementation::createLoot(SceneObject* container, AiAgent* creature) {
	LootGroupCollection* lootCollection = creature->getLootGroups();

	if (lootCollection == NULL)
		return false;

	for (int i = 0; i < lootCollection->count(); ++i) {
		LootGroupCollectionEntry* entry = lootCollection->get(i);
		int lootChance = entry->getLootChance();

		if (lootChance <= 0)
			continue;

		int roll = System::random(10000000);

		if (roll > lootChance)
			continue;

		int tempChance = 0; //Start at 0.

		LootGroups* lootGroups = entry->getLootGroups();

		//Select the loot group to use.
		for (int i = 0; i < lootGroups->count(); ++i) {
			LootGroupEntry* entry = lootGroups->get(i);

			tempChance += entry->getLootChance();

			//Is this entry lower than the roll? If yes, then we want to try the next entry.
			if (tempChance < roll)
				continue;

			createLoot(container, entry->getLootGroupName(), creature->getLevel());

			break;
		}
	}

	return true;
}

bool LootManagerImplementation::createLoot(SceneObject* container, const String& lootGroup, int level) {
	if (container->hasFullContainerObjects())
		return false;

	Reference<LootGroupTemplate*> group = lootGroupMap->getLootGroupTemplate(lootGroup);

	if (group == NULL) {
		warning("Loot group template requested does not exist: " + lootGroup);
		return false;
	}

	//Now we do the second roll for the item out of the group.
	int roll = System::random(10000000);

	Reference<LootItemTemplate*> itemTemplate = lootGroupMap->getLootItemTemplate(group->getLootItemTemplateForRoll(roll));

	if (itemTemplate == NULL) {
		warning("Loot item template requested does not exist: " + group->getLootItemTemplateForRoll(roll) + " for templateName: " + group->getTemplateName());
		return false;
	}

	TangibleObject* obj = createLootObject(itemTemplate, level);

	if (obj == NULL)
		return false;

	if (container->transferObject(obj, -1, false))
		container->broadcastObject(obj, true);

	return true;
}
<|endoftext|>