<commit_before>#include "test/mocks/http/mocks.h"
#include "test/test_common/utility.h"

#include "gtest/gtest.h"
#include "library/common/api/external.h"
#include "library/common/extensions/filters/http/platform_bridge/filter.h"
#include "library/common/extensions/filters/http/platform_bridge/filter.pb.h"

using testing::ByMove;
using testing::Return;

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace PlatformBridge {
namespace {

std::string to_string(envoy_data data) {
  return std::string(reinterpret_cast<const char*>(data.bytes), data.length);
}

class PlatformBridgeFilterTest : public testing::Test {
public:
  void setUpFilter(std::string&& yaml, envoy_http_filter* platform_filter) {
    envoymobile::extensions::filters::http::platform_bridge::PlatformBridge config;
    TestUtility::loadFromYaml(yaml, config);
    Api::External::registerApi(config.platform_filter_name(), platform_filter);

    config_ = std::make_shared<PlatformBridgeFilterConfig>(config);
    filter_ = std::make_unique<PlatformBridgeFilter>(config_);
    filter_->setDecoderFilterCallbacks(decoder_callbacks_);
    filter_->setEncoderFilterCallbacks(encoder_callbacks_);
  }

  typedef struct {
    unsigned int init_filter_calls;
    unsigned int on_request_headers_calls;
    unsigned int on_request_data_calls;
    unsigned int on_request_trailers_calls;
    unsigned int on_response_headers_calls;
    unsigned int on_response_data_calls;
    unsigned int on_response_trailers_calls;
    unsigned int release_filter_calls;
  } filter_invocations;

  PlatformBridgeFilterConfigSharedPtr config_{};
  std::unique_ptr<PlatformBridgeFilter> filter_{};
  NiceMock<Http::MockStreamDecoderFilterCallbacks> decoder_callbacks_;
  NiceMock<Http::MockStreamEncoderFilterCallbacks> encoder_callbacks_;
};

TEST_F(PlatformBridgeFilterTest, BasicContinueOnRequestHeaders) {
  envoy_http_filter platform_filter;
  filter_invocations invocations = {0, 0, 0, 0, 0, 0, 0, 0};
  platform_filter.static_context = &invocations;
  platform_filter.init_filter = [](const void* context) -> const void* {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    invocations->init_filter_calls++;
    return context;
  };
  platform_filter.on_request_headers = [](envoy_headers c_headers, bool end_stream,
                                          const void* context) -> envoy_filter_headers_status {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    EXPECT_EQ(c_headers.length, 1);
    EXPECT_EQ(to_string(c_headers.headers[0].key), ":authority");
    EXPECT_EQ(to_string(c_headers.headers[0].value), "test.code");
    EXPECT_TRUE(end_stream);
    invocations->on_request_headers_calls++;
    return {kEnvoyFilterHeadersStatusContinue, c_headers};
  };

  setUpFilter(R"EOF(
platform_filter_name: BasicContinueOnRequestHeaders
)EOF",
              &platform_filter);
  EXPECT_EQ(invocations.init_filter_calls, 1);

  Http::TestRequestHeaderMapImpl request_headers{{":authority", "test.code"}};

  EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true));
  EXPECT_EQ(invocations.on_request_headers_calls, 1);
}

TEST_F(PlatformBridgeFilterTest, BasicContinueOnRequestData) {
  envoy_http_filter platform_filter;
  filter_invocations invocations = {0, 0, 0, 0, 0, 0, 0, 0};
  platform_filter.static_context = &invocations;
  platform_filter.init_filter = [](const void* context) -> const void* {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    invocations->init_filter_calls++;
    return context;
  };
  platform_filter.on_request_data = [](envoy_data c_data, bool end_stream,
                                       const void* context) -> envoy_filter_data_status {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    EXPECT_EQ(to_string(c_data), "request body");
    EXPECT_TRUE(end_stream);
    invocations->on_request_data_calls++;
    return {kEnvoyFilterDataStatusContinue, c_data};
  };

  setUpFilter(R"EOF(
platform_filter_name: BasicContinueOnRequestData
)EOF",
              &platform_filter);
  EXPECT_EQ(invocations.init_filter_calls, 1);

  Buffer::OwnedImpl request_data = Buffer::OwnedImpl("request body");

  EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(request_data, true));
  EXPECT_EQ(invocations.on_request_data_calls, 1);
}

TEST_F(PlatformBridgeFilterTest, BasicContinueOnRequestTrailers) {
  envoy_http_filter platform_filter;
  filter_invocations invocations = {0, 0, 0, 0, 0, 0, 0, 0};
  platform_filter.static_context = &invocations;
  platform_filter.init_filter = [](const void* context) -> const void* {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    invocations->init_filter_calls++;
    return context;
  };
  platform_filter.on_request_trailers = [](envoy_headers c_trailers,
                                           const void* context) -> envoy_filter_trailers_status {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    EXPECT_EQ(c_trailers.length, 1);
    EXPECT_EQ(to_string(c_trailers.headers[0].key), "x-test-trailer");
    EXPECT_EQ(to_string(c_trailers.headers[0].value), "test trailer");
    invocations->on_request_trailers_calls++;
    return {kEnvoyFilterTrailersStatusContinue, c_trailers};
  };

  setUpFilter(R"EOF(
platform_filter_name: BasicContinueOnRequestTrailers
)EOF",
              &platform_filter);
  EXPECT_EQ(invocations.init_filter_calls, 1);

  Http::TestRequestTrailerMapImpl request_trailers{{"x-test-trailer", "test trailer"}};

  EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers));
  EXPECT_EQ(invocations.on_request_trailers_calls, 1);
}

// DIVIDE

TEST_F(PlatformBridgeFilterTest, BasicContinueOnResponseHeaders) {
  envoy_http_filter platform_filter;
  filter_invocations invocations = {0, 0, 0, 0, 0, 0, 0, 0};
  platform_filter.static_context = &invocations;
  platform_filter.init_filter = [](const void* context) -> const void* {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    invocations->init_filter_calls++;
    return context;
  };
  platform_filter.on_response_headers = [](envoy_headers c_headers, bool end_stream,
                                           const void* context) -> envoy_filter_headers_status {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    EXPECT_EQ(c_headers.length, 1);
    EXPECT_EQ(to_string(c_headers.headers[0].key), ":status");
    EXPECT_EQ(to_string(c_headers.headers[0].value), "test.code");
    EXPECT_TRUE(end_stream);
    invocations->on_response_headers_calls++;
    return {kEnvoyFilterHeadersStatusContinue, c_headers};
  };

  setUpFilter(R"EOF(
platform_filter_name: BasicContinueOnResponseHeaders
)EOF",
              &platform_filter);
  EXPECT_EQ(invocations.init_filter_calls, 1);

  Http::TestResponseHeaderMapImpl response_headers{{":status", "test.code"}};

  EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers, true));
  EXPECT_EQ(invocations.on_response_headers_calls, 1);
}

TEST_F(PlatformBridgeFilterTest, BasicContinueOnResponseData) {
  envoy_http_filter platform_filter;
  filter_invocations invocations = {0, 0, 0, 0, 0, 0, 0, 0};
  platform_filter.static_context = &invocations;
  platform_filter.init_filter = [](const void* context) -> const void* {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    invocations->init_filter_calls++;
    return context;
  };
  platform_filter.on_response_data = [](envoy_data c_data, bool end_stream,
                                        const void* context) -> envoy_filter_data_status {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    EXPECT_EQ(to_string(c_data), "response body");
    EXPECT_TRUE(end_stream);
    invocations->on_response_data_calls++;
    return {kEnvoyFilterDataStatusContinue, c_data};
  };

  setUpFilter(R"EOF(
platform_filter_name: BasicContinueOnResponseData
)EOF",
              &platform_filter);
  EXPECT_EQ(invocations.init_filter_calls, 1);

  Buffer::OwnedImpl response_data = Buffer::OwnedImpl("response body");

  EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data, true));
  EXPECT_EQ(invocations.on_response_data_calls, 1);
}

TEST_F(PlatformBridgeFilterTest, BasicContinueOnResponseTrailers) {
  envoy_http_filter platform_filter;
  filter_invocations invocations = {0, 0, 0, 0, 0, 0, 0, 0};
  platform_filter.static_context = &invocations;
  platform_filter.init_filter = [](const void* context) -> const void* {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    invocations->init_filter_calls++;
    return context;
  };
  platform_filter.on_response_trailers = [](envoy_headers c_trailers,
                                            const void* context) -> envoy_filter_trailers_status {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    EXPECT_EQ(c_trailers.length, 1);
    EXPECT_EQ(to_string(c_trailers.headers[0].key), "x-test-trailer");
    EXPECT_EQ(to_string(c_trailers.headers[0].value), "test trailer");
    invocations->on_response_trailers_calls++;
    return {kEnvoyFilterTrailersStatusContinue, c_trailers};
  };

  setUpFilter(R"EOF(
platform_filter_name: BasicContinueOnResponseTrailers
)EOF",
              &platform_filter);
  EXPECT_EQ(invocations.init_filter_calls, 1);

  Http::TestResponseTrailerMapImpl response_trailers{{"x-test-trailer", "test trailer"}};

  EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers));
  EXPECT_EQ(invocations.on_response_trailers_calls, 1);
}

} // namespace
} // namespace PlatformBridge
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy
<commit_msg>filters: test null and noop filters (#1097)<commit_after>#include "test/mocks/http/mocks.h"
#include "test/test_common/utility.h"

#include "gtest/gtest.h"
#include "library/common/api/external.h"
#include "library/common/extensions/filters/http/platform_bridge/filter.h"
#include "library/common/extensions/filters/http/platform_bridge/filter.pb.h"

using testing::ByMove;
using testing::Return;

namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace PlatformBridge {
namespace {

std::string to_string(envoy_data data) {
  return std::string(reinterpret_cast<const char*>(data.bytes), data.length);
}

class PlatformBridgeFilterTest : public testing::Test {
public:
  void setUpFilter(std::string&& yaml, envoy_http_filter* platform_filter) {
    envoymobile::extensions::filters::http::platform_bridge::PlatformBridge config;
    TestUtility::loadFromYaml(yaml, config);
    Api::External::registerApi(config.platform_filter_name(), platform_filter);

    config_ = std::make_shared<PlatformBridgeFilterConfig>(config);
    filter_ = std::make_unique<PlatformBridgeFilter>(config_);
    filter_->setDecoderFilterCallbacks(decoder_callbacks_);
    filter_->setEncoderFilterCallbacks(encoder_callbacks_);
  }

  typedef struct {
    unsigned int init_filter_calls;
    unsigned int on_request_headers_calls;
    unsigned int on_request_data_calls;
    unsigned int on_request_trailers_calls;
    unsigned int on_response_headers_calls;
    unsigned int on_response_data_calls;
    unsigned int on_response_trailers_calls;
    unsigned int release_filter_calls;
  } filter_invocations;

  PlatformBridgeFilterConfigSharedPtr config_{};
  std::unique_ptr<PlatformBridgeFilter> filter_{};
  NiceMock<Http::MockStreamDecoderFilterCallbacks> decoder_callbacks_;
  NiceMock<Http::MockStreamEncoderFilterCallbacks> encoder_callbacks_;
};

TEST_F(PlatformBridgeFilterTest, NullImplementation) {
  envoy_http_filter* null_filter =
      static_cast<envoy_http_filter*>(safe_calloc(1, sizeof(envoy_http_filter)));
  setUpFilter("platform_filter_name: NullImplementation\n", null_filter);

  Http::TestRequestHeaderMapImpl request_headers{{":authority", "test.code"}};
  EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, false));

  Buffer::OwnedImpl request_data = Buffer::OwnedImpl("request body");
  EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(request_data, false));

  Http::TestRequestTrailerMapImpl request_trailers{{"x-test-trailer", "test trailer"}};
  EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers));

  Http::TestResponseHeaderMapImpl response_headers{{":status", "test.code"}};
  EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers, false));

  Buffer::OwnedImpl response_data = Buffer::OwnedImpl("response body");
  EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data, false));

  Http::TestResponseTrailerMapImpl response_trailers{{"x-test-trailer", "test trailer"}};
  EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers));

  free(null_filter);
}

TEST_F(PlatformBridgeFilterTest, PartialNullImplementation) {
  envoy_http_filter* noop_filter =
      static_cast<envoy_http_filter*>(safe_calloc(1, sizeof(envoy_http_filter)));
  filter_invocations invocations = {0, 0, 0, 0, 0, 0, 0, 0};
  noop_filter->static_context = &invocations;
  noop_filter->init_filter = [](const void* context) -> const void* {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    invocations->init_filter_calls++;
    return context;
  };
  setUpFilter("platform_filter_name: PartialNullImplementation\n", noop_filter);
  EXPECT_EQ(invocations.init_filter_calls, 1);

  Http::TestRequestHeaderMapImpl request_headers{{":authority", "test.code"}};
  EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, false));

  Buffer::OwnedImpl request_data = Buffer::OwnedImpl("request body");
  EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(request_data, false));

  Http::TestRequestTrailerMapImpl request_trailers{{"x-test-trailer", "test trailer"}};
  EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers));

  Http::TestResponseHeaderMapImpl response_headers{{":status", "test.code"}};
  EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers, false));

  Buffer::OwnedImpl response_data = Buffer::OwnedImpl("response body");
  EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data, false));

  Http::TestResponseTrailerMapImpl response_trailers{{"x-test-trailer", "test trailer"}};
  EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers));

  free(noop_filter);
}

TEST_F(PlatformBridgeFilterTest, BasicContinueOnRequestHeaders) {
  envoy_http_filter platform_filter;
  filter_invocations invocations = {0, 0, 0, 0, 0, 0, 0, 0};
  platform_filter.static_context = &invocations;
  platform_filter.init_filter = [](const void* context) -> const void* {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    invocations->init_filter_calls++;
    return context;
  };
  platform_filter.on_request_headers = [](envoy_headers c_headers, bool end_stream,
                                          const void* context) -> envoy_filter_headers_status {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    EXPECT_EQ(c_headers.length, 1);
    EXPECT_EQ(to_string(c_headers.headers[0].key), ":authority");
    EXPECT_EQ(to_string(c_headers.headers[0].value), "test.code");
    EXPECT_TRUE(end_stream);
    invocations->on_request_headers_calls++;
    return {kEnvoyFilterHeadersStatusContinue, c_headers};
  };

  setUpFilter(R"EOF(
platform_filter_name: BasicContinueOnRequestHeaders
)EOF",
              &platform_filter);
  EXPECT_EQ(invocations.init_filter_calls, 1);

  Http::TestRequestHeaderMapImpl request_headers{{":authority", "test.code"}};

  EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true));
  EXPECT_EQ(invocations.on_request_headers_calls, 1);
}

TEST_F(PlatformBridgeFilterTest, BasicContinueOnRequestData) {
  envoy_http_filter platform_filter;
  filter_invocations invocations = {0, 0, 0, 0, 0, 0, 0, 0};
  platform_filter.static_context = &invocations;
  platform_filter.init_filter = [](const void* context) -> const void* {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    invocations->init_filter_calls++;
    return context;
  };
  platform_filter.on_request_data = [](envoy_data c_data, bool end_stream,
                                       const void* context) -> envoy_filter_data_status {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    EXPECT_EQ(to_string(c_data), "request body");
    EXPECT_TRUE(end_stream);
    invocations->on_request_data_calls++;
    return {kEnvoyFilterDataStatusContinue, c_data};
  };

  setUpFilter(R"EOF(
platform_filter_name: BasicContinueOnRequestData
)EOF",
              &platform_filter);
  EXPECT_EQ(invocations.init_filter_calls, 1);

  Buffer::OwnedImpl request_data = Buffer::OwnedImpl("request body");

  EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->decodeData(request_data, true));
  EXPECT_EQ(invocations.on_request_data_calls, 1);
}

TEST_F(PlatformBridgeFilterTest, BasicContinueOnRequestTrailers) {
  envoy_http_filter platform_filter;
  filter_invocations invocations = {0, 0, 0, 0, 0, 0, 0, 0};
  platform_filter.static_context = &invocations;
  platform_filter.init_filter = [](const void* context) -> const void* {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    invocations->init_filter_calls++;
    return context;
  };
  platform_filter.on_request_trailers = [](envoy_headers c_trailers,
                                           const void* context) -> envoy_filter_trailers_status {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    EXPECT_EQ(c_trailers.length, 1);
    EXPECT_EQ(to_string(c_trailers.headers[0].key), "x-test-trailer");
    EXPECT_EQ(to_string(c_trailers.headers[0].value), "test trailer");
    invocations->on_request_trailers_calls++;
    return {kEnvoyFilterTrailersStatusContinue, c_trailers};
  };

  setUpFilter(R"EOF(
platform_filter_name: BasicContinueOnRequestTrailers
)EOF",
              &platform_filter);
  EXPECT_EQ(invocations.init_filter_calls, 1);

  Http::TestRequestTrailerMapImpl request_trailers{{"x-test-trailer", "test trailer"}};

  EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->decodeTrailers(request_trailers));
  EXPECT_EQ(invocations.on_request_trailers_calls, 1);
}

// DIVIDE

TEST_F(PlatformBridgeFilterTest, BasicContinueOnResponseHeaders) {
  envoy_http_filter platform_filter;
  filter_invocations invocations = {0, 0, 0, 0, 0, 0, 0, 0};
  platform_filter.static_context = &invocations;
  platform_filter.init_filter = [](const void* context) -> const void* {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    invocations->init_filter_calls++;
    return context;
  };
  platform_filter.on_response_headers = [](envoy_headers c_headers, bool end_stream,
                                           const void* context) -> envoy_filter_headers_status {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    EXPECT_EQ(c_headers.length, 1);
    EXPECT_EQ(to_string(c_headers.headers[0].key), ":status");
    EXPECT_EQ(to_string(c_headers.headers[0].value), "test.code");
    EXPECT_TRUE(end_stream);
    invocations->on_response_headers_calls++;
    return {kEnvoyFilterHeadersStatusContinue, c_headers};
  };

  setUpFilter(R"EOF(
platform_filter_name: BasicContinueOnResponseHeaders
)EOF",
              &platform_filter);
  EXPECT_EQ(invocations.init_filter_calls, 1);

  Http::TestResponseHeaderMapImpl response_headers{{":status", "test.code"}};

  EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->encodeHeaders(response_headers, true));
  EXPECT_EQ(invocations.on_response_headers_calls, 1);
}

TEST_F(PlatformBridgeFilterTest, BasicContinueOnResponseData) {
  envoy_http_filter platform_filter;
  filter_invocations invocations = {0, 0, 0, 0, 0, 0, 0, 0};
  platform_filter.static_context = &invocations;
  platform_filter.init_filter = [](const void* context) -> const void* {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    invocations->init_filter_calls++;
    return context;
  };
  platform_filter.on_response_data = [](envoy_data c_data, bool end_stream,
                                        const void* context) -> envoy_filter_data_status {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    EXPECT_EQ(to_string(c_data), "response body");
    EXPECT_TRUE(end_stream);
    invocations->on_response_data_calls++;
    return {kEnvoyFilterDataStatusContinue, c_data};
  };

  setUpFilter(R"EOF(
platform_filter_name: BasicContinueOnResponseData
)EOF",
              &platform_filter);
  EXPECT_EQ(invocations.init_filter_calls, 1);

  Buffer::OwnedImpl response_data = Buffer::OwnedImpl("response body");

  EXPECT_EQ(Http::FilterDataStatus::Continue, filter_->encodeData(response_data, true));
  EXPECT_EQ(invocations.on_response_data_calls, 1);
}

TEST_F(PlatformBridgeFilterTest, BasicContinueOnResponseTrailers) {
  envoy_http_filter platform_filter;
  filter_invocations invocations = {0, 0, 0, 0, 0, 0, 0, 0};
  platform_filter.static_context = &invocations;
  platform_filter.init_filter = [](const void* context) -> const void* {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    invocations->init_filter_calls++;
    return context;
  };
  platform_filter.on_response_trailers = [](envoy_headers c_trailers,
                                            const void* context) -> envoy_filter_trailers_status {
    filter_invocations* invocations = static_cast<filter_invocations*>(const_cast<void*>(context));
    EXPECT_EQ(c_trailers.length, 1);
    EXPECT_EQ(to_string(c_trailers.headers[0].key), "x-test-trailer");
    EXPECT_EQ(to_string(c_trailers.headers[0].value), "test trailer");
    invocations->on_response_trailers_calls++;
    return {kEnvoyFilterTrailersStatusContinue, c_trailers};
  };

  setUpFilter(R"EOF(
platform_filter_name: BasicContinueOnResponseTrailers
)EOF",
              &platform_filter);
  EXPECT_EQ(invocations.init_filter_calls, 1);

  Http::TestResponseTrailerMapImpl response_trailers{{"x-test-trailer", "test trailer"}};

  EXPECT_EQ(Http::FilterTrailersStatus::Continue, filter_->encodeTrailers(response_trailers));
  EXPECT_EQ(invocations.on_response_trailers_calls, 1);
}

} // namespace
} // namespace PlatformBridge
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy
<|endoftext|>


<commit_before>#include <utility>
#include <cmath>

#include "shape.hpp"
#include "data_structures.hpp"

Shape::Shape(Color color, float index_of_refraction, float ambient_coef,
             float diffuse_coef, float specular_coef)
    : index_of_refraction(index_of_refraction), color(color), ambient_coef(ambient_coef),
      diffuse_coef(diffuse_coef), specular_coef(specular_coef) {
}

float Shape::getIndexOfRefraction() const {
    return index_of_refraction;
}

float Shape::getAmbientCoefficient() const {
    return ambient_coef;
}

float Shape::getDiffuseCoefficient() const {
    return diffuse_coef;
}

float Shape::getSpecularCoefficient() const {
    return specular_coef;
}

Color Shape::getColor() const {
    return color;
}

Sphere::Sphere(Point center, float radius, Color color, float index_of_refraction,
               float ambient_coef, float diffuse_coef, float specular_coef)
    : Shape(color, index_of_refraction, ambient_coef, diffuse_coef, specular_coef),
      center(center), radius(radius)
{
}

Ray* Sphere::getIntersection(const Ray& ray) const {
    Vector c(center.x, center.y, center.z);
    float det = pow(ray.getDir().dotProduct(c), 2)
        - ray.getDir().dotProduct(ray.getDir())
        * (c.dotProduct(c) - pow(radius, 2));

    if (det < 0) {
        return NULL;
    }
    float d =  ray.dir.dotProduct(c);
    if (det == 0) {
        Point p(ray.dir.i * d, ray.dir.j * d, ray.dir.k * d);
        Vector v(c.i - p.x, c.j - p.y, c.k - p.z);
        return new Ray(p,v);
    }
    // two collisions
    else {
        float sdet = sqrt(det);
        if (fabsf(d - sdet) > fabsf(d + sdet)) {
            float d = d + sdet;
        } else {
            float d = d - sdet;
        }

        Point p(ray.dir.i * d, ray.dir.j * d, ray.dir.k * d);
        Vector v(c.i - p.x, c.j - p.y, c.k - p.z);
        return new Ray(p,v);
    }
}

Plane::Plane(Point center, Vector normal, Color color, float index_of_refraction,
             float ambient_coef, float diffuse_coef, float specular_coef)
    : Shape(color, index_of_refraction, ambient_coef, diffuse_coef, specular_coef),
      center(center), normal(normal) {
}
/*
Plane::Plane(Point center, Vector normal, Color color, float index_of_refraction,
             float ambient_coef, float diffuse_coef, float specular_coef)
    : Shape(color, index_of_refraction, ambient_coef, diffuse_coef, specular_coef),
      normal(normal) {

    distance = fabs((float)((normal.i * center.x + normal.j * center.y + normal.k * center.z)/
                sqrt(normal.i*normal.i+normal.j*normal.j+normal.k*normal.k)));
}
*/
Ray* Plane::getIntersection(const Ray& ray) const {
    if(normal.dotProduct(ray.dir)==0){  //ray is parallel to the plane
        //if(normal.dotProduct(center-ray.origin)==0) //ray is directly on the planex
        if(normal.dotProduct(Vector(center.x-ray.origin.x, center.y-ray.origin.y, center.z-ray.origin.z))==0) //ray is directly on the plane
            return new Ray(ray.origin,normal);
        else //ray never intersects with the plane
            return NULL;
    }else{ //must intersect with the at one specific point
        //solve for parametric equation of a line
        float t = (normal.i*(center.x-ray.origin.x)+
                    normal.j*(center.y-ray.origin.y)+
                    normal.k*(center.z-ray.origin.z))/
                    (normal.i*ray.dir.i+normal.j*ray.dir.j+normal.k*ray.dir.k);

        if(t<0)
            return NULL;
        else{
            Point p = Point(ray.origin.x+ray.dir.i*t, ray.origin.y+ray.dir.j*t, ray.origin.z+ray.dir.k*t);
            return new Ray(p,normal);
        }
        //should not reach this
        return NULL;
    }

/*
tyler's old method
    Vector v(center.x - ray.origin.x,
             center.y - ray.origin.y,
             center.z - ray.origin.z);
    float d = normal.dotProduct(v) / normal.dotProduct(ray.dir);

    if (fabsf(d) < 0.0000001) {
        Point p(ray.dir.i * d + ray.origin.x,
                ray.dir.j * d + ray.origin.y,
                ray.dir.k * d + ray.origin.z);
        return new Ray(p, normal);
    } else { // we have no collision
        return NULL;
    }

    */
 }

<commit_msg>Zev you don't need to leave old methods commented out. Also, not my code. Removed old code.<commit_after>#include <utility>
#include <cmath>

#include "shape.hpp"
#include "data_structures.hpp"

Shape::Shape(Color color, float index_of_refraction, float ambient_coef,
             float diffuse_coef, float specular_coef)
    : index_of_refraction(index_of_refraction), color(color), ambient_coef(ambient_coef),
      diffuse_coef(diffuse_coef), specular_coef(specular_coef) {
}

float Shape::getIndexOfRefraction() const {
    return index_of_refraction;
}

float Shape::getAmbientCoefficient() const {
    return ambient_coef;
}

float Shape::getDiffuseCoefficient() const {
    return diffuse_coef;
}

float Shape::getSpecularCoefficient() const {
    return specular_coef;
}

Color Shape::getColor() const {
    return color;
}

Sphere::Sphere(Point center, float radius, Color color, float index_of_refraction,
               float ambient_coef, float diffuse_coef, float specular_coef)
    : Shape(color, index_of_refraction, ambient_coef, diffuse_coef, specular_coef),
      center(center), radius(radius)
{
}

Ray* Sphere::getIntersection(const Ray& ray) const {
    Vector c(center.x, center.y, center.z);
    float det = pow(ray.getDir().dotProduct(c), 2)
        - ray.getDir().dotProduct(ray.getDir())
        * (c.dotProduct(c) - pow(radius, 2));

    if (det < 0) {
        return NULL;
    }
    float d =  ray.dir.dotProduct(c);
    if (det == 0) {
        Point p(ray.dir.i * d, ray.dir.j * d, ray.dir.k * d);
        Vector v(c.i - p.x, c.j - p.y, c.k - p.z);
        return new Ray(p,v);
    }
    // two collisions
    else {
        float sdet = sqrt(det);
        if (fabsf(d - sdet) > fabsf(d + sdet)) {
            float d = d + sdet;
        } else {
            float d = d - sdet;
        }

        Point p(ray.dir.i * d, ray.dir.j * d, ray.dir.k * d);
        Vector v(c.i - p.x, c.j - p.y, c.k - p.z);
        return new Ray(p,v);
    }
}

Plane::Plane(Point center, Vector normal, Color color, float index_of_refraction,
             float ambient_coef, float diffuse_coef, float specular_coef)
    : Shape(color, index_of_refraction, ambient_coef, diffuse_coef, specular_coef),
      center(center), normal(normal) {
}

Ray* Plane::getIntersection(const Ray& ray) const {
    if(normal.dotProduct(ray.dir)==0){  //ray is parallel to the plane
        //if(normal.dotProduct(center-ray.origin)==0) //ray is directly on the planex
        if(normal.dotProduct(Vector(center.x-ray.origin.x, center.y-ray.origin.y, center.z-ray.origin.z))==0) //ray is directly on the plane
            return new Ray(ray.origin,normal);
        else //ray never intersects with the plane
            return NULL;
    }else{ //must intersect with the at one specific point
        //solve for parametric equation of a line
        float t = (normal.i*(center.x-ray.origin.x)+
                    normal.j*(center.y-ray.origin.y)+
                    normal.k*(center.z-ray.origin.z))/
                    (normal.i*ray.dir.i+normal.j*ray.dir.j+normal.k*ray.dir.k);

        if(t<0)
            return NULL;
        else{
            Point p = Point(ray.origin.x+ray.dir.i*t, ray.origin.y+ray.dir.j*t, ray.origin.z+ray.dir.k*t);
            return new Ray(p,normal);
        }
        //should not reach this
        return NULL;
    }
 }

<|endoftext|>


<commit_before>#include "state.h"

#include "quadtree.h"

#include <glog/logging.h>

#include <limits.h>

namespace GameState {

bool initialized_ = false;

sf::Vector2f camera_;
sf::FloatRect cameraBounds_;

util::Tick ticks_ = 0;

std::unique_ptr<entities::Sprite> hero_;
float moveSpeed_;

// Jumping stuff
bool jumping_ = false;
int velocityY_ = 0;

std::unordered_map<std::string, bool> flags_;
std::unordered_map<std::string, std::list<FlagChangeCallback>>
    flagChangeCallbacks_;

std::unordered_map<std::string, int> values_;
std::unordered_map<std::string, std::list<ValueChangeCallback>>
    valueChangeCallbacks_;

// Stack is used to mimick maps_
std::stack<std::vector<std::unique_ptr<entities::Sprite>>> sprites_;

std::stack<std::unique_ptr<map::Map>> maps_;

std::unordered_map<int, TileCallback> tileEvents_;

chaiscript::ChaiScript chai_(chaiscript::Std_Lib::library());

#define ADD_METHOD(Class, Name) chai_.add(chaiscript::fun(&Class::Name), #Name)
#define ADD_FUNCTION(Name) ADD_METHOD(GameState, Name)
#define ADD_TYPE(Type, Name) chai_.add(chaiscript::user_type<Type>(), Name);

void initApi() {
  ADD_FUNCTION(mod);

  ADD_FUNCTION(initialized);
  ADD_FUNCTION(ticks);

  ADD_FUNCTION(loadMap);
  ADD_FUNCTION(popMap);
  ADD_FUNCTION(loadCharacter);
  ADD_FUNCTION(addItem);
  ADD_FUNCTION(setCharacterMoveSpeed);

  ADD_FUNCTION(addNpc);
  ADD_FUNCTION(setNpcCallback);

  ADD_FUNCTION(showDialog);
  ADD_FUNCTION(addDialogOption);
  ADD_FUNCTION(setDialogCallback);

  ADD_FUNCTION(clearEvents);
  ADD_FUNCTION(registerTileEvent);

  ADD_TYPE(entities::Sprite, "Sprite");
  ADD_METHOD(entities::Sprite, id);
  ADD_METHOD(entities::Sprite, hp);
  ADD_METHOD(entities::Sprite, damage);
  ADD_METHOD(entities::Sprite, heal);
  ADD_METHOD(entities::Sprite, startJump);
  ADD_METHOD(entities::Sprite, move);
  ADD_METHOD(entities::Sprite, setMaxHp);
  ADD_METHOD(entities::Sprite, width);
  ADD_METHOD(entities::Sprite, height);
  ADD_METHOD(entities::Sprite, jumping);
  ADD_METHOD(entities::Sprite, setCollisionCallback);
  ADD_METHOD(entities::Sprite, addItem);
  ADD_METHOD(entities::Sprite, removeItem);

  ADD_TYPE(entities::Item, "Item");
  ADD_METHOD(entities::Item, held);
  ADD_METHOD(entities::Item, hold);
  ADD_METHOD(entities::Item, drop);

  ADD_FUNCTION(getHero);
  ADD_FUNCTION(getSprite);
  ADD_FUNCTION(getNpc);
  ADD_FUNCTION(getItem);

  ADD_FUNCTION(getFlag);
  ADD_FUNCTION(setFlag);
  ADD_FUNCTION(addFlagChangeCallback);

  ADD_FUNCTION(getValue);
  ADD_FUNCTION(setValue);
  ADD_FUNCTION(addValueChangeCallback);
}

sf::Vector2f& camera() { return camera_; }

entities::Sprite* spriteCollision(
    const std::unique_ptr<entities::Sprite>& sprite,
    const sf::FloatRect& collisionRect) {
  for (const auto& s : sprites()) {
    if (!s || !s->active() || s->phased() || s->id == sprite->id) {
      continue;
    }

    if (collisionRect.intersects(s->getDimensions())) {
      return s.get();
    }
  }
  return nullptr;
}

float positionOfSpriteAbove(const std::unique_ptr<entities::Sprite>& sprite) {
  auto dim = sprite->getDimensions();
  sf::FloatRect collisionRect(dim.left, 0, dim.width, dim.top);
  const auto other = spriteCollision(sprite, collisionRect);
  if (other) {
    const auto oDim = other->getDimensions();
    return oDim.top + oDim.height;
  }
  return 0;
}

float densePositionAbove(const std::unique_ptr<entities::Sprite>& sprite) {
  float spriteAbove = positionOfSpriteAbove(sprite);
  float tileAbove = map()->positionOfTileAbove(sprite->getDimensions());
  return std::max<float>(spriteAbove, tileAbove);
}

float positionOfSpriteBelow(const std::unique_ptr<entities::Sprite>& sprite) {
  auto dim = sprite->getDimensions();
  sf::FloatRect collisionRect(dim.left, dim.top + dim.height, dim.width,
                              map()->pixelHeight());
  const auto other = spriteCollision(sprite, collisionRect);
  if (other) {
    const auto oDim = other->getDimensions();
    return oDim.top - dim.height;
  }
  return std::numeric_limits<float>::max();
}

// TODO(jsvana): make this and Above return the sprite if there is one
// for collisions
float densePositionBelow(const std::unique_ptr<entities::Sprite>& sprite) {
  float spriteBelow = positionOfSpriteBelow(sprite);
  float tileBelow = map()->positionOfTileBelow(sprite->getDimensions());
  return std::min<float>(spriteBelow, tileBelow);
}

int mod(int a, int b) { return a % b; }

void setTicks(util::Tick ticks) { ticks_ = ticks; }

int ticks() { return (int)(ticks_ % INT_MAX); }

void setHero(std::unique_ptr<entities::Sprite> hero) {
  hero_ = std::move(hero);
}

const std::unique_ptr<entities::Sprite>& hero() { return hero_; }

entities::Sprite* getHero() { return hero_.get(); }

void setHeroMoveSpeed(int amount, int total) {
  moveSpeed_ = (float)amount / (float)total;
}

int heroMoveSpeed() { return moveSpeed_; }

void pushSprites(std::vector<std::unique_ptr<entities::Sprite>> sprites) {
  sprites_.push(std::move(sprites));
}

std::vector<std::unique_ptr<entities::Sprite>>& sprites() {
  return sprites_.top();
}

void popSprites() { sprites_.pop(); }

const std::unique_ptr<map::Map>& map() { return maps_.top(); }

chaiscript::ChaiScript& chai() { return chai_; }

void addTileEvent(int id, TileCallback callback) { tileEvents_[id] = callback; }

bool tileHasEvent(int id) { return tileEvents_.find(id) != tileEvents_.end(); }

const TileCallback& tileCallback(int id) { return tileEvents_[id]; }

void clearTileEvent(int id) { tileEvents_.erase(id); }

void clearTileEvents() { tileEvents_.clear(); }

void runTileEvent() {
  int tileNumber = map()->pointToTileNumber(hero_->getPosition());
  if (!tileHasEvent(tileNumber)) {
    return;
  }

  TileCallback callback = tileCallback(tileNumber);
  callback();
  clearTileEvent(tileNumber);
}

bool positionWalkable(const std::unique_ptr<entities::Sprite>& sprite,
                      sf::FloatRect dim) {
  return positionWalkable(sprite.get(), dim);
}

bool positionWalkable(entities::Sprite* sprite, sf::FloatRect dim) {
  if (!map()->positionWalkable(dim)) {
    return false;
  }

  // Check sprite walkability
  for (const auto& s : sprites()) {
    if (!s || !s->active() || s->id == sprite->id) {
      continue;
    }
    sf::FloatRect intersection;
    if (dim.intersects(s->getDimensions(), intersection)) {
      dispatchCollision(sprite, s.get());
      if (!s->phased()) {
        return false;
      }
    }
  }

  if (sprite != hero_.get() && dim.intersects(hero_->getDimensions())) {
    dispatchCollision(sprite, hero_.get());
    if (!sprite->phased()) {
      return false;
    }
  }

  return true;
}

void dispatchCollision(entities::Sprite* mover, entities::Sprite* other) {
  if (!mover->collisionFunc) {
    return;
  }
  mover->collisionFunc(other->id);
}

void markInitialized() { initialized_ = true; }

bool initialized() { return initialized_; }

bool loadMap(std::string path) {
  maps_.push(std::make_unique<map::Map>(path));
  sprites_.emplace();
  sprites().emplace_back();

  return true;
}

bool popMap() {
  maps_.pop();
  sprites_.pop();

  return true;
}

bool loadCharacter(std::string path, int tile, int initX, int initY) {
  hero_ = std::make_unique<entities::Sprite>(path);
  hero_->setPosition(initX * map()->tileWidth(), initY * map()->tileHeight());
  hero_->setTile(tile);

  // IDs will start at 1, the hero gets 0
  hero_->id = 0;

  return true;
}

bool setSpritePosition(entities::Id spriteId, int x, int y) {
  auto sprite = findSprite<entities::Sprite>(spriteId);
  if (!sprite) {
    return false;
  }

  sf::Vector2f p = map()->mapToPixel(x, y);

  sprite->setPosition(p);
  return true;
}

bool setCharacterMoveSpeed(int amount, int total) {
  moveSpeed_ = (float)amount / (float)total;
  return true;
}

bool setCharacterMaxHp(int hp) {
  hero_->setMaxHp(hp);
  return true;
}

template <typename T>
entities::Id addSprite(const std::string& path, int tile, int x, int y) {
  sprites().push_back(std::make_unique<T>(path));
  auto item = sprites().back().get();
  item->setPosition(x * GameState::map()->tileWidth(),
                    y * GameState::map()->tileHeight());
  item->setTile(tile);

  const auto spriteId = sprites().size() - 1;
  item->id = spriteId;
  return spriteId;
}

entities::Id addItem(const std::string& path, int tile, int x, int y) {
  return addSprite<entities::Item>(path, tile, x, y);
}

entities::Id addNpc(const std::string& path, int tile, int x, int y) {
  return addSprite<entities::Npc>(path, tile, x, y);
}

template <typename T>
T* findSprite(const entities::Id spriteId) {
  if (spriteId >= sprites().size()) {
    LOG(WARNING) << "Bad sprite ID: " << spriteId;
    return nullptr;
  }
  return static_cast<T*>(sprites()[spriteId].get());
}

template <typename T>
T* findSprite(const entities::Id spriteId, const entities::SpriteType type) {
  auto sprite = findSprite<T>(spriteId);
  if (!sprite) {
    return nullptr;
  }
  if (sprites()[spriteId]->type() != type) {
    LOG(WARNING) << "ID " << spriteId << " is incorrect type "
                 << static_cast<int>(type) << " (wanted type "
                 << static_cast<int>(sprites()[spriteId]->type()) << ")";
    return nullptr;
  }
  return sprite;
}

entities::Sprite* getSprite(const entities::Id spriteId) {
  return findSprite<entities::Sprite>(spriteId);
}

entities::Npc* getNpc(const entities::Id npcId) {
  return findSprite<entities::Npc>(npcId, entities::SpriteType::NPC);
}

entities::Item* getItem(const entities::Id itemId) {
  return findSprite<entities::Item>(itemId, entities::SpriteType::ITEM);
}

bool setNpcCallback(const entities::Id npcId,
                    entities::SpriteCallback callback) {
  auto npc = findSprite<entities::Npc>(npcId, entities::SpriteType::NPC);
  if (!npc) {
    return false;
  }
  npc->callbackFunc = callback;
  return true;
}

visual::DialogManager::Id showDialog(std::string message) {
  auto dialog = new visual::Dialog(message);
  dialog->setPosition(0, SCREEN_HEIGHT - dialog->pixelHeight());
  return visual::DialogManager::queueDialog(dialog);
}

bool addDialogOption(visual::DialogManager::Id uid, std::string option) {
  return visual::DialogManager::addDialogOption(uid, option);
}

bool setDialogCallback(visual::DialogManager::Id uid,
                       visual::DialogCallback callback) {
  return visual::DialogManager::setDialogCallback(uid, callback);
}

bool registerTileEvent(int x, int y, TileCallback callback) {
  addTileEvent(map()->mapPointToTileNumber(x, y), callback);
  return true;
}

bool clearEvents() {
  clearTileEvents();
  return true;
}

void setFlag(const std::string& flag, bool value) {
  flags_[flag] = value;
  for (auto& callback : flagChangeCallbacks(flag)) {
    callback(value);
  }
}

bool getFlag(const std::string& flag) {
  const auto iter = flags_.find(flag);
  if (iter == flags_.end()) {
    return false;
  }
  return iter->second;
}

const std::list<FlagChangeCallback> flagChangeCallbacks(
    const std::string& flag) {
  const auto iter = flagChangeCallbacks_.find(flag);
  if (iter == flagChangeCallbacks_.end()) {
    return std::list<FlagChangeCallback>();
  }
  return iter->second;
}

void addFlagChangeCallback(const std::string& flag,
                           const FlagChangeCallback& func) {
  flagChangeCallbacks_[flag].push_back(func);
}

void setValue(const std::string& key, const int value) {
  values_[key] = value;
  for (auto& callback : valueChangeCallbacks(key)) {
    callback(value);
  }
}

int getValue(const std::string& key) {
  const auto iter = values_.find(flag);
  if (iter == values_.end()) {
    return 0;
  }
  return iter->second;
}

const std::list<ValueChangeCallback> valueChangeCallbacks(
    const std::string& key) {
  const auto iter = valueChangeCallbacks_.find(key);
  if (iter == valueChangeCallbacks_.end()) {
    return std::list<ValueChangeCallback>();
  }
  return iter->second;
}

void addValueChangeCallback(const std::string& key,
                            const ValueChangeCallback& func) {
  valueChangeCallbacks_[key].push_back(func);
}

}  // namespace GameState
<commit_msg>Fix bad commit<commit_after>#include "state.h"

#include "quadtree.h"

#include <glog/logging.h>

#include <limits.h>

namespace GameState {

bool initialized_ = false;

sf::Vector2f camera_;
sf::FloatRect cameraBounds_;

util::Tick ticks_ = 0;

std::unique_ptr<entities::Sprite> hero_;
float moveSpeed_;

// Jumping stuff
bool jumping_ = false;
int velocityY_ = 0;

std::unordered_map<std::string, bool> flags_;
std::unordered_map<std::string, std::list<FlagChangeCallback>>
    flagChangeCallbacks_;

std::unordered_map<std::string, int> values_;
std::unordered_map<std::string, std::list<ValueChangeCallback>>
    valueChangeCallbacks_;

// Stack is used to mimick maps_
std::stack<std::vector<std::unique_ptr<entities::Sprite>>> sprites_;

std::stack<std::unique_ptr<map::Map>> maps_;

std::unordered_map<int, TileCallback> tileEvents_;

chaiscript::ChaiScript chai_(chaiscript::Std_Lib::library());

#define ADD_METHOD(Class, Name) chai_.add(chaiscript::fun(&Class::Name), #Name)
#define ADD_FUNCTION(Name) ADD_METHOD(GameState, Name)
#define ADD_TYPE(Type, Name) chai_.add(chaiscript::user_type<Type>(), Name);

void initApi() {
  ADD_FUNCTION(mod);

  ADD_FUNCTION(initialized);
  ADD_FUNCTION(ticks);

  ADD_FUNCTION(loadMap);
  ADD_FUNCTION(popMap);
  ADD_FUNCTION(loadCharacter);
  ADD_FUNCTION(addItem);
  ADD_FUNCTION(setCharacterMoveSpeed);

  ADD_FUNCTION(addNpc);
  ADD_FUNCTION(setNpcCallback);

  ADD_FUNCTION(showDialog);
  ADD_FUNCTION(addDialogOption);
  ADD_FUNCTION(setDialogCallback);

  ADD_FUNCTION(clearEvents);
  ADD_FUNCTION(registerTileEvent);

  ADD_TYPE(entities::Sprite, "Sprite");
  ADD_METHOD(entities::Sprite, id);
  ADD_METHOD(entities::Sprite, hp);
  ADD_METHOD(entities::Sprite, damage);
  ADD_METHOD(entities::Sprite, heal);
  ADD_METHOD(entities::Sprite, startJump);
  ADD_METHOD(entities::Sprite, move);
  ADD_METHOD(entities::Sprite, setMaxHp);
  ADD_METHOD(entities::Sprite, width);
  ADD_METHOD(entities::Sprite, height);
  ADD_METHOD(entities::Sprite, jumping);
  ADD_METHOD(entities::Sprite, setCollisionCallback);
  ADD_METHOD(entities::Sprite, addItem);
  ADD_METHOD(entities::Sprite, removeItem);

  ADD_TYPE(entities::Item, "Item");
  ADD_METHOD(entities::Item, held);
  ADD_METHOD(entities::Item, hold);
  ADD_METHOD(entities::Item, drop);

  ADD_FUNCTION(getHero);
  ADD_FUNCTION(getSprite);
  ADD_FUNCTION(getNpc);
  ADD_FUNCTION(getItem);

  ADD_FUNCTION(getFlag);
  ADD_FUNCTION(setFlag);
  ADD_FUNCTION(addFlagChangeCallback);

  ADD_FUNCTION(getValue);
  ADD_FUNCTION(setValue);
  ADD_FUNCTION(addValueChangeCallback);
}

sf::Vector2f& camera() { return camera_; }

entities::Sprite* spriteCollision(
    const std::unique_ptr<entities::Sprite>& sprite,
    const sf::FloatRect& collisionRect) {
  for (const auto& s : sprites()) {
    if (!s || !s->active() || s->phased() || s->id == sprite->id) {
      continue;
    }

    if (collisionRect.intersects(s->getDimensions())) {
      return s.get();
    }
  }
  return nullptr;
}

float positionOfSpriteAbove(const std::unique_ptr<entities::Sprite>& sprite) {
  auto dim = sprite->getDimensions();
  sf::FloatRect collisionRect(dim.left, 0, dim.width, dim.top);
  const auto other = spriteCollision(sprite, collisionRect);
  if (other) {
    const auto oDim = other->getDimensions();
    return oDim.top + oDim.height;
  }
  return 0;
}

float densePositionAbove(const std::unique_ptr<entities::Sprite>& sprite) {
  float spriteAbove = positionOfSpriteAbove(sprite);
  float tileAbove = map()->positionOfTileAbove(sprite->getDimensions());
  return std::max<float>(spriteAbove, tileAbove);
}

float positionOfSpriteBelow(const std::unique_ptr<entities::Sprite>& sprite) {
  auto dim = sprite->getDimensions();
  sf::FloatRect collisionRect(dim.left, dim.top + dim.height, dim.width,
                              map()->pixelHeight());
  const auto other = spriteCollision(sprite, collisionRect);
  if (other) {
    const auto oDim = other->getDimensions();
    return oDim.top - dim.height;
  }
  return std::numeric_limits<float>::max();
}

// TODO(jsvana): make this and Above return the sprite if there is one
// for collisions
float densePositionBelow(const std::unique_ptr<entities::Sprite>& sprite) {
  float spriteBelow = positionOfSpriteBelow(sprite);
  float tileBelow = map()->positionOfTileBelow(sprite->getDimensions());
  return std::min<float>(spriteBelow, tileBelow);
}

int mod(int a, int b) { return a % b; }

void setTicks(util::Tick ticks) { ticks_ = ticks; }

int ticks() { return (int)(ticks_ % INT_MAX); }

void setHero(std::unique_ptr<entities::Sprite> hero) {
  hero_ = std::move(hero);
}

const std::unique_ptr<entities::Sprite>& hero() { return hero_; }

entities::Sprite* getHero() { return hero_.get(); }

void setHeroMoveSpeed(int amount, int total) {
  moveSpeed_ = (float)amount / (float)total;
}

int heroMoveSpeed() { return moveSpeed_; }

void pushSprites(std::vector<std::unique_ptr<entities::Sprite>> sprites) {
  sprites_.push(std::move(sprites));
}

std::vector<std::unique_ptr<entities::Sprite>>& sprites() {
  return sprites_.top();
}

void popSprites() { sprites_.pop(); }

const std::unique_ptr<map::Map>& map() { return maps_.top(); }

chaiscript::ChaiScript& chai() { return chai_; }

void addTileEvent(int id, TileCallback callback) { tileEvents_[id] = callback; }

bool tileHasEvent(int id) { return tileEvents_.find(id) != tileEvents_.end(); }

const TileCallback& tileCallback(int id) { return tileEvents_[id]; }

void clearTileEvent(int id) { tileEvents_.erase(id); }

void clearTileEvents() { tileEvents_.clear(); }

void runTileEvent() {
  int tileNumber = map()->pointToTileNumber(hero_->getPosition());
  if (!tileHasEvent(tileNumber)) {
    return;
  }

  TileCallback callback = tileCallback(tileNumber);
  callback();
  clearTileEvent(tileNumber);
}

bool positionWalkable(const std::unique_ptr<entities::Sprite>& sprite,
                      sf::FloatRect dim) {
  return positionWalkable(sprite.get(), dim);
}

bool positionWalkable(entities::Sprite* sprite, sf::FloatRect dim) {
  if (!map()->positionWalkable(dim)) {
    return false;
  }

  // Check sprite walkability
  for (const auto& s : sprites()) {
    if (!s || !s->active() || s->id == sprite->id) {
      continue;
    }
    sf::FloatRect intersection;
    if (dim.intersects(s->getDimensions(), intersection)) {
      dispatchCollision(sprite, s.get());
      if (!s->phased()) {
        return false;
      }
    }
  }

  if (sprite != hero_.get() && dim.intersects(hero_->getDimensions())) {
    dispatchCollision(sprite, hero_.get());
    if (!sprite->phased()) {
      return false;
    }
  }

  return true;
}

void dispatchCollision(entities::Sprite* mover, entities::Sprite* other) {
  if (!mover->collisionFunc) {
    return;
  }
  mover->collisionFunc(other->id);
}

void markInitialized() { initialized_ = true; }

bool initialized() { return initialized_; }

bool loadMap(std::string path) {
  maps_.push(std::make_unique<map::Map>(path));
  sprites_.emplace();
  sprites().emplace_back();

  return true;
}

bool popMap() {
  maps_.pop();
  sprites_.pop();

  return true;
}

bool loadCharacter(std::string path, int tile, int initX, int initY) {
  hero_ = std::make_unique<entities::Sprite>(path);
  hero_->setPosition(initX * map()->tileWidth(), initY * map()->tileHeight());
  hero_->setTile(tile);

  // IDs will start at 1, the hero gets 0
  hero_->id = 0;

  return true;
}

bool setSpritePosition(entities::Id spriteId, int x, int y) {
  auto sprite = findSprite<entities::Sprite>(spriteId);
  if (!sprite) {
    return false;
  }

  sf::Vector2f p = map()->mapToPixel(x, y);

  sprite->setPosition(p);
  return true;
}

bool setCharacterMoveSpeed(int amount, int total) {
  moveSpeed_ = (float)amount / (float)total;
  return true;
}

bool setCharacterMaxHp(int hp) {
  hero_->setMaxHp(hp);
  return true;
}

template <typename T>
entities::Id addSprite(const std::string& path, int tile, int x, int y) {
  sprites().push_back(std::make_unique<T>(path));
  auto item = sprites().back().get();
  item->setPosition(x * GameState::map()->tileWidth(),
                    y * GameState::map()->tileHeight());
  item->setTile(tile);

  const auto spriteId = sprites().size() - 1;
  item->id = spriteId;
  return spriteId;
}

entities::Id addItem(const std::string& path, int tile, int x, int y) {
  return addSprite<entities::Item>(path, tile, x, y);
}

entities::Id addNpc(const std::string& path, int tile, int x, int y) {
  return addSprite<entities::Npc>(path, tile, x, y);
}

template <typename T>
T* findSprite(const entities::Id spriteId) {
  if (spriteId >= sprites().size()) {
    LOG(WARNING) << "Bad sprite ID: " << spriteId;
    return nullptr;
  }
  return static_cast<T*>(sprites()[spriteId].get());
}

template <typename T>
T* findSprite(const entities::Id spriteId, const entities::SpriteType type) {
  auto sprite = findSprite<T>(spriteId);
  if (!sprite) {
    return nullptr;
  }
  if (sprites()[spriteId]->type() != type) {
    LOG(WARNING) << "ID " << spriteId << " is incorrect type "
                 << static_cast<int>(type) << " (wanted type "
                 << static_cast<int>(sprites()[spriteId]->type()) << ")";
    return nullptr;
  }
  return sprite;
}

entities::Sprite* getSprite(const entities::Id spriteId) {
  return findSprite<entities::Sprite>(spriteId);
}

entities::Npc* getNpc(const entities::Id npcId) {
  return findSprite<entities::Npc>(npcId, entities::SpriteType::NPC);
}

entities::Item* getItem(const entities::Id itemId) {
  return findSprite<entities::Item>(itemId, entities::SpriteType::ITEM);
}

bool setNpcCallback(const entities::Id npcId,
                    entities::SpriteCallback callback) {
  auto npc = findSprite<entities::Npc>(npcId, entities::SpriteType::NPC);
  if (!npc) {
    return false;
  }
  npc->callbackFunc = callback;
  return true;
}

visual::DialogManager::Id showDialog(std::string message) {
  auto dialog = new visual::Dialog(message);
  dialog->setPosition(0, SCREEN_HEIGHT - dialog->pixelHeight());
  return visual::DialogManager::queueDialog(dialog);
}

bool addDialogOption(visual::DialogManager::Id uid, std::string option) {
  return visual::DialogManager::addDialogOption(uid, option);
}

bool setDialogCallback(visual::DialogManager::Id uid,
                       visual::DialogCallback callback) {
  return visual::DialogManager::setDialogCallback(uid, callback);
}

bool registerTileEvent(int x, int y, TileCallback callback) {
  addTileEvent(map()->mapPointToTileNumber(x, y), callback);
  return true;
}

bool clearEvents() {
  clearTileEvents();
  return true;
}

void setFlag(const std::string& flag, bool value) {
  flags_[flag] = value;
  for (auto& callback : flagChangeCallbacks(flag)) {
    callback(value);
  }
}

bool getFlag(const std::string& flag) {
  const auto iter = flags_.find(flag);
  if (iter == flags_.end()) {
    return false;
  }
  return iter->second;
}

const std::list<FlagChangeCallback> flagChangeCallbacks(
    const std::string& flag) {
  const auto iter = flagChangeCallbacks_.find(flag);
  if (iter == flagChangeCallbacks_.end()) {
    return std::list<FlagChangeCallback>();
  }
  return iter->second;
}

void addFlagChangeCallback(const std::string& flag,
                           const FlagChangeCallback& func) {
  flagChangeCallbacks_[flag].push_back(func);
}

void setValue(const std::string& key, const int value) {
  values_[key] = value;
  for (auto& callback : valueChangeCallbacks(key)) {
    callback(value);
  }
}

int getValue(const std::string& key) {
  const auto iter = values_.find(key);
  if (iter == values_.end()) {
    return 0;
  }
  return iter->second;
}

const std::list<ValueChangeCallback> valueChangeCallbacks(
    const std::string& key) {
  const auto iter = valueChangeCallbacks_.find(key);
  if (iter == valueChangeCallbacks_.end()) {
    return std::list<ValueChangeCallback>();
  }
  return iter->second;
}

void addValueChangeCallback(const std::string& key,
                            const ValueChangeCallback& func) {
  valueChangeCallbacks_[key].push_back(func);
}

}  // namespace GameState
<|endoftext|>


<commit_before>/*************************************************************************
 *   Copyright (C) 2011-2012 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of Arcanet.                                       *
 *                                                                       *
 *   Arcanet 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.                 *
 *                                                                       *
 *   Arcanet 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 Arcanet.                                         *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#include "store.h"
#include "directory.h"
#include "sha512.h"
#include "html.h"

namespace arc
{

const String Store::DatabaseDirectory = "db";
const size_t Store::ChunkSize = 256*1024;		// 256 Kio

Store *Store::Instance = NULL;

Store::Store(void)
{
	Interface::Instance->add("/files", this);
}

Store::~Store(void)
{
	Interface::Instance->remove("/files");
}

void Store::addDirectory(const String &name, const String &path)
{
	mDirectories.insert(name, path);
}

void Store::removeDirectory(const String &name)
{
	mDirectories.erase(name);
}

void Store::refresh(void)
{
	mFiles.clear();

	for(StringMap::iterator it = mDirectories.begin();
			it != mDirectories.end();
			++it)
	{
		refreshDirectory(it->first, it->second);
	}
}

bool Store::get(const Identifier &identifier, Entry &entry, bool content)
{
	entry.content = NULL;

	try {
		String entryName = DatabaseDirectory+Directory::Separator+identifier.toString();
		String url;
		if(mFiles.get(identifier,url))	// Hash is on content
		{
			Identifier hash;
			Sha512::Hash(url, hash);
			entryName = DatabaseDirectory+Directory::Separator+hash.toString();

			if(File::Exist(entryName))		// TODO: force reindexation
			{
				File file(entryName, File::Read);
				file.readLine(entry.path);
				file.read(entry.info);

				Assert(entry.info.get("type") != "directory");
				if(content) entry.content = new File(entry.path, File::Read);	// content = file
			}
		}
		else if(File::Exist(entryName))		// Hash is on URL
		{
			entry.content = new File(entryName, File::Read);	// content = meta
			entry.content->readLine(entry.path);
			entry.content->read(entry.info);

			if(!content)
			{
				delete entry.content;
				entry.content = NULL;
			}
		}
	}
	catch(Exception &e)
	{
		if(entry.content)
		{
			delete entry.content;
			entry.content = NULL;
		}

		Log("Store", String("Corrupted entry for ")+identifier.toString()+": "+e.what());
		return false;
	}

	return true;
}

bool Store::get(const String &url, Entry &entry, bool content)
{
	Identifier hash;

	try {
		if(url.find('/') == String::NotFound)	// url is a hash
		{
			String s(url);
			s >> hash;
		}
		else {
			Sha512::Hash(url, hash);
		}
	}
	catch(...)
	{
		// TODO: Log
		return false;
	}

	return get(hash, entry, content);
}

void Store::http(const String &prefix, Http::Request &request)
{
	try {
		const String &url = request.url;

		if(request.url == "/")
		{
			Http::Response response(request,200);
			response.send();

			Html page(response.sock);
			page.header(request.url);
			page.open("h1");
			page.text(request.url);
			page.close("h1");

			for(StringMap::iterator it = mDirectories.begin();
						it != mDirectories.end();
						++it)
			{
				page.link(it->first, it->first);
				page.br();
			}

			page.footer();
		}
		else {
			String path = urlToPath(url);
			if(path[path.size()-1] == Directory::Separator) path.resize(path.size()-1);

			if(Directory::Exist(path))
			{
				if(url[url.size()-1] != '/')
				{
					Http::Response response(request, 301);	// Moved Permanently
					response.headers["Location"] = prefix+url+"/";
					response.send();
					return;
				}

				Http::Response response(request, 200);
				response.send();

				Html page(response.sock);
				page.header(request.url);
				page.open("h1");
				page.text(request.url);
				page.close("h1");

				Directory dir(path);
				while(dir.nextFile())
				{
					page.link(dir.fileName(), dir.fileName());
					page.br();
				}

				page.footer();
			}
			else if(File::Exist(path))
			{
				Http::Response response(request,200);
				response.headers["Content-Type"] = "application/octet-stream";	// TODO
				response.send();

				File file(path, File::Read);
				response.sock->writeBinary(file);
			}
			else throw Exception("File not found");
		}
	}
	catch(const Exception &e)
	{
		Log("Store::http",e.what());
		throw 404;	// Httpd handles integer exceptions
	}
}

void Store::refreshDirectory(const String &dirUrl, const String &dirPath)
{
	Log("Store", String("Refreshing directory: ")+dirUrl);

	Identifier hash;
	Sha512::Hash(dirUrl, hash);
	String entryName = DatabaseDirectory+Directory::Separator+hash.toString();
	File dirEntry(entryName, File::Write);
	dirEntry.writeLine(dirPath);

	StringMap header;
	header["type"] = "directory";
	header["time"] << time(NULL);
	dirEntry.write(header);

	Directory dir(dirPath);
	while(dir.nextFile())
	{
		String url(dirUrl + Directory::Separator + dir.fileName());
		ByteString urlHash;
		Sha512::Hash(url, urlHash);
		String entryName = DatabaseDirectory+Directory::Separator+urlHash.toString();

		if(!dir.fileIsDir() && File::Exist(entryName))
		{
			try {
				File file(entryName, File::Read);

				String path;
				StringMap header;
				file.readLine(path);
				file.read(header);

				StringMap origHeader(header);

				time_t time;
				header["time"] >> time;
				if(!header.contains("type")) throw Exception("Missing type field");
				if(header["type"] == "directory") throw Exception("Invalid type for file");

				size_t size;
				String hash;
				size_t chunkSize;
				unsigned chunkCount;
				header["size"] >> size;
				header["chunk-size"] >> chunkSize;
				header["chunk-count"] >> chunkCount;
				header["hash"] >> hash;

				// If the file has not changed, don't hash it again
				if(size == dir.fileSize() && time == dir.fileTime())
				{
					mFiles.insert(Identifier(hash), url);
					dirEntry.write(origHeader);
					continue;
				}
			}
			catch(Exception &e)
			{
				Log("Store", String("Corrupted entry for ")+dir.fileName()+": "+e.what());
			}
		}

		Log("Store", String("Processing: ")+dir.fileName());

		try {

			if(dir.fileIsDir())
			{
				refreshDirectory(url, dir.filePath());
			}
			else {
				ByteString dataHash;
				File data(dir.filePath(), File::Read);
				Sha512::Hash(data, dataHash);
				data.close();

				size_t   chunkSize = ChunkSize;
				unsigned chunkCount = dir.fileSize()/ChunkSize + 1;

				StringMap header;
				dir.getFileInfo(header);
				header["type"] = "file";
				header["chunk-size"] << chunkSize;
				header["chunk-count"] << chunkCount;
				header["hash"] << dataHash;

				mFiles.insert(Identifier(dataHash), url);

				File file(entryName, File::Write);
				file.writeLine(dir.filePath());
				file.write(header);
				dirEntry.write(header);

				data.open(dir.filePath(), File::Read);
				for(unsigned i=0; i<chunkCount; ++i)
				{
					dataHash.clear();
					AssertIO(Sha512::Hash(data, ChunkSize, dataHash));
					file.writeLine(dataHash);
				}
			}
		}
		catch(Exception &e)
		{
			Log("Store", String("Processing failed for ")+dir.fileName()+": "+e.what());
			File::Remove(entryName);
		}
	}
}

String Store::urlToPath(const String &url) const
{
	if(url.empty() || url[0] != '/') throw Exception("Invalid URL");

	String dir(url.substr(1));
	String path = dir.cut('/');

	// Do not accept the parent directory symbol as a security protection
	if(path.find("..") != String::NotFound) throw Exception("Invalid URL");

	String dirPath;
	if(!mDirectories.get(dir,dirPath)) throw Exception("Directory does not exists");

	path.replace('/',Directory::Separator);

	return dirPath + Directory::Separator + path;
}

}
<commit_msg>Store url handling<commit_after>/*************************************************************************
 *   Copyright (C) 2011-2012 by Paul-Louis Ageneau                       *
 *   paul-louis (at) ageneau (dot) org                                   *
 *                                                                       *
 *   This file is part of Arcanet.                                       *
 *                                                                       *
 *   Arcanet 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.                 *
 *                                                                       *
 *   Arcanet 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 Arcanet.                                         *
 *   If not, see <http://www.gnu.org/licenses/>.                         *
 *************************************************************************/

#include "store.h"
#include "directory.h"
#include "sha512.h"
#include "html.h"

namespace arc
{

const String Store::DatabaseDirectory = "db";
const size_t Store::ChunkSize = 256*1024;		// 256 Kio

Store *Store::Instance = NULL;

Store::Store(void)
{
	Interface::Instance->add("/files", this);
}

Store::~Store(void)
{
	Interface::Instance->remove("/files");
}

void Store::addDirectory(const String &name, const String &path)
{
	mDirectories.insert(name, path);
}

void Store::removeDirectory(const String &name)
{
	mDirectories.erase(name);
}

void Store::refresh(void)
{
	mFiles.clear();

	for(StringMap::iterator it = mDirectories.begin();
			it != mDirectories.end();
			++it)
	{
		refreshDirectory("/"+it->first, it->second);
	}
}

bool Store::get(const Identifier &identifier, Entry &entry, bool content)
{
	entry.content = NULL;

	try {
		String entryName = DatabaseDirectory+Directory::Separator+identifier.toString();
		String url;
		if(mFiles.get(identifier,url))	// Hash is on content
		{
			Identifier hash;
			Sha512::Hash(url, hash);
			entryName = DatabaseDirectory+Directory::Separator+hash.toString();

			if(File::Exist(entryName))		// TODO: force reindexation
			{
				File file(entryName, File::Read);
				file.readLine(entry.path);
				file.read(entry.info);

				Assert(entry.info.get("type") != "directory");
				if(content) entry.content = new File(entry.path, File::Read);	// content = file
			}
		}
		else if(File::Exist(entryName))		// Hash is on URL
		{
			entry.content = new File(entryName, File::Read);	// content = meta
			entry.content->readLine(entry.path);
			entry.content->read(entry.info);

			if(!content)
			{
				delete entry.content;
				entry.content = NULL;
			}
		}
	}
	catch(Exception &e)
	{
		if(entry.content)
		{
			delete entry.content;
			entry.content = NULL;
		}

		Log("Store", String("Corrupted entry for ")+identifier.toString()+": "+e.what());
		return false;
	}

	return true;
}

bool Store::get(const String &url, Entry &entry, bool content)
{
	Identifier hash;

	try {
		if(url.find('/') == String::NotFound)	// url is a hash
		{
			String s(url);
			s >> hash;
		}
		else {
			Sha512::Hash(url, hash);
		}
	}
	catch(...)
	{
		// TODO: Log
		return false;
	}

	return get(hash, entry, content);
}

void Store::http(const String &prefix, Http::Request &request)
{
	try {
		const String &url = request.url;

		if(request.url == "/")
		{
			Http::Response response(request,200);
			response.send();

			Html page(response.sock);
			page.header(request.url);
			page.open("h1");
			page.text(request.url);
			page.close("h1");

			for(StringMap::iterator it = mDirectories.begin();
						it != mDirectories.end();
						++it)
			{
				page.link(it->first, it->first);
				page.br();
			}

			page.footer();
		}
		else {
			String path = urlToPath(url);
			if(path[path.size()-1] == Directory::Separator) path.resize(path.size()-1);

			if(Directory::Exist(path))
			{
				if(url[url.size()-1] != '/')
				{
					Http::Response response(request, 301);	// Moved Permanently
					response.headers["Location"] = prefix+url+"/";
					response.send();
					return;
				}

				Http::Response response(request, 200);
				response.send();

				Html page(response.sock);
				page.header(request.url);
				page.open("h1");
				page.text(request.url);
				page.close("h1");

				Directory dir(path);
				while(dir.nextFile())
				{
					page.link(dir.fileName(), dir.fileName());
					page.br();
				}

				page.footer();
			}
			else if(File::Exist(path))
			{
				Http::Response response(request,200);
				response.headers["Content-Type"] = "application/octet-stream";	// TODO
				response.send();

				File file(path, File::Read);
				response.sock->writeBinary(file);
			}
			else throw Exception("File not found");
		}
	}
	catch(const Exception &e)
	{
		Log("Store::http",e.what());
		throw 404;	// Httpd handles integer exceptions
	}
}

void Store::refreshDirectory(const String &dirUrl, const String &dirPath)
{
	Log("Store", String("Refreshing directory: ")+dirUrl);

	Identifier hash;
	Sha512::Hash(dirUrl, hash);
	String entryName = DatabaseDirectory+Directory::Separator+hash.toString();
	File dirEntry(entryName, File::Write);
	dirEntry.writeLine(dirPath);

	StringMap header;
	header["type"] = "directory";
	header["time"] << time(NULL);
	dirEntry.write(header);

	Directory dir(dirPath);
	while(dir.nextFile())
	{
		String url(dirUrl + '/' + dir.fileName());
		ByteString urlHash;
		Sha512::Hash(url, urlHash);
		String entryName = DatabaseDirectory+Directory::Separator+urlHash.toString();

		if(!dir.fileIsDir() && File::Exist(entryName))
		{
			try {
				File file(entryName, File::Read);

				String path;
				StringMap header;
				file.readLine(path);
				file.read(header);

				StringMap origHeader(header);

				time_t time;
				header["time"] >> time;
				if(!header.contains("type")) throw Exception("Missing type field");
				if(header["type"] == "directory") throw Exception("Invalid type for file");

				size_t size;
				String hash;
				size_t chunkSize;
				unsigned chunkCount;
				header["size"] >> size;
				header["chunk-size"] >> chunkSize;
				header["chunk-count"] >> chunkCount;
				header["hash"] >> hash;

				// If the file has not changed, don't hash it again
				if(size == dir.fileSize() && time == dir.fileTime())
				{
					mFiles.insert(Identifier(hash), url);
					dirEntry.write(origHeader);
					continue;
				}
			}
			catch(Exception &e)
			{
				Log("Store", String("Corrupted entry for ")+dir.fileName()+": "+e.what());
			}
		}

		Log("Store", String("Processing: ")+dir.fileName());

		try {

			if(dir.fileIsDir())
			{
				refreshDirectory(url, dir.filePath());
			}
			else {
				ByteString dataHash;
				File data(dir.filePath(), File::Read);
				Sha512::Hash(data, dataHash);
				data.close();

				size_t   chunkSize = ChunkSize;
				unsigned chunkCount = dir.fileSize()/ChunkSize + 1;

				StringMap header;
				dir.getFileInfo(header);
				header["type"] = "file";
				header["chunk-size"] << chunkSize;
				header["chunk-count"] << chunkCount;
				header["hash"] << dataHash;

				mFiles.insert(Identifier(dataHash), url);

				File file(entryName, File::Write);
				file.writeLine(dir.filePath());
				file.write(header);
				dirEntry.write(header);

				data.open(dir.filePath(), File::Read);
				for(unsigned i=0; i<chunkCount; ++i)
				{
					dataHash.clear();
					AssertIO(Sha512::Hash(data, ChunkSize, dataHash));
					file.writeLine(dataHash);
				}
			}
		}
		catch(Exception &e)
		{
			Log("Store", String("Processing failed for ")+dir.fileName()+": "+e.what());
			File::Remove(entryName);
		}
	}
}

String Store::urlToPath(const String &url) const
{
	if(url.empty() || url[0] != '/') throw Exception("Invalid URL");

	String dir(url.substr(1));
	String path = dir.cut('/');

	// Do not accept the parent directory symbol as a security protection
	if(path.find("..") != String::NotFound) throw Exception("Invalid URL");

	String dirPath;
	if(!mDirectories.get(dir,dirPath)) throw Exception("Directory does not exists");

	path.replace('/',Directory::Separator);

	return dirPath + Directory::Separator + path;
}

}
<|endoftext|>


<commit_before>#pragma once

enum TokenType {
	// Symbols //////
	NUL,           // Reserved
	WHITESPACE,    // Spaces or tabs
	INDENT,        // Spaces or tabs
	DEDENT,        // Spaces or tabs
	NEWLINE,       // /n
	LEFT_BRACKET,  // {
	RIGHT_BRACKET, // }
	LEFT_SQUARE,   // [
	RIGHT_SQUARE,  // ]
	LEFT_PAREN,    // (
	RIGHT_PAREN,   // )
	ARROW,         // ->
	COMMA,         // ,
	DOT,           // .
	TRIPLE_DOT     // ...
	COLON,         // :
	DOUBLE_COLON,  // ::
	LEFT_CARROT,   // <
	RIGHT_CARROT,  // >
	SINGLE_QUOTE,  // '
	DOUBLE_QUOTE,  // "
	TD_QUOTE,      // """

	// Identifiers //
	IDENTIFIER, // Custom identifier

	// Keywords
	KEY_TYPE,     // type
	KEY_INTRIN,   // intrinsic
	KEY_VOCAB,    // vocab
	KEY_DEFINE,   // define
	KEY_TRAIT,    // trait
	KEY_INSTANCE, // instance
	KEY_MATCH,    // match
	KEY_CASE,     // case
	KEY_DO,       // do
	KEY_IF,       // if
	KEY_ELSE,     // else
	KEY_DATA      // data
};<commit_msg>More tokens<commit_after>#pragma once

enum TokenType {
	// Symbols //////
	NUL,           // Reserved
	WHITESPACE,    // Spaces or tabs
	INDENT,        // Spaces or tabs
	DEDENT,        // Spaces or tabs
	NEWLINE,       // /n
	LEFT_BRACKET,  // {
	RIGHT_BRACKET, // }
	LEFT_SQUARE,   // [
	RIGHT_SQUARE,  // ]
	LEFT_PAREN,    // (
	RIGHT_PAREN,   // )
	ARROW,         // ->
	COMMA,         // ,
	DOT,           // .
	TRIPLE_DOT     // ...
	COLON,         // :
	DOUBLE_COLON,  // ::
	LEFT_CARROT,   // <
	RIGHT_CARROT,  // >
	SINGLE_QUOTE,  // '
	DOUBLE_QUOTE,  // "
	TD_QUOTE,      // """

	// User defined stuff //
	IDENTIFIER, // foo in foo(->):
	LIT_INT,    // 3
	LIT_FLOAT,  // 3.14159
	LIT_STRING, // "FOO BAR!"

	// Keywords
	KEY_TYPE,     // type
	KEY_INTRIN,   // intrinsic
	KEY_VOCAB,    // vocab
	KEY_DEFINE,   // define
	KEY_TRAIT,    // trait
	KEY_INSTANCE, // instance
	KEY_MATCH,    // match
	KEY_CASE,     // case
	KEY_DO,       // do
	KEY_IF,       // if
	KEY_ELSE,     // else
	KEY_DATA,     // data
	KEY_ABOUT,    // about
	KEY_DOCS,     // docs
	KEY_OP,       // operator
	KEY_LEFT,     // left
	KEY_RIGHT     // right

};
<|endoftext|>


<commit_before>/*
   Copyright (c) 2011, The Mineserver Project
   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.
  * Neither the name of the The Mineserver Project nor the
    names of its contributors may be used to endorse or promote products
    derived from this software without specific prior written permission.

  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 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.
 */

#ifdef WIN32
#include <winsock2.h>
#else
#include <netinet/in.h>
#include <pwd.h>
#endif

#ifdef linux
#include <unistd.h>
#include <libgen.h>
#include <wordexp.h>
#include <climits>
#endif

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <ctime>
#include <cmath>
#include <cctype>

#include "tools.h"
#include "logger.h"
#include "mineserver.h"
#include "config.h"

void putSint64(uint8_t* buf, int64_t value)
{
  uint64_t nval = ntohll(value);
  memcpy(buf, &nval, 8);
}

void putSint32(uint8_t* buf, int32_t value)
{
  uint32_t nval = htonl(value);
  memcpy(buf, &nval, 4);
}

void putSint16(uint8_t* buf, int16_t value)
{
  short value2 = htons(value);
  memcpy(buf, &value2, 2);
}

void putFloat(uint8_t* buf, float value)
{
  uint32_t nval;
  memcpy(&nval, &value, 4);
  nval = htonl(nval);
  memcpy(buf, &nval, 4);
}

void putDouble(uint8_t* buf, double value)
{
  uint64_t nval;
  memcpy(&nval, &value, 8);
  nval = ntohll(nval);
  memcpy(buf, &nval, 8);
}

double getDouble(uint8_t* buf)
{
  double val;
  uint64_t ival;
  memcpy(&ival, buf, 8);
  ival = ntohll(ival);
  memcpy(&val, &ival, 8);
  return val;
}

float getFloat(uint8_t* buf)
{
  float val;
  int ival = ntohl(*reinterpret_cast<const int32_t*>(buf));
  memcpy(&val, &ival, 4);
  return val;
}

int64_t getSint64(uint8_t* buf)
{
  int64_t val;
  memcpy(&val, buf, 8);
  val = ntohll(val);
  return val;
}

int32_t getSint32(uint8_t* buf)
{
  int val = ntohl(*reinterpret_cast<const int32_t*>(buf));
  return val;
}

int32_t getSint16(uint8_t* buf)
{
  short val = ntohs(*reinterpret_cast<const int16_t*>(buf));

  return val;
}

std::string base36_encode(int value)
{
  std::string output;
  my_itoa((int)abs(value), output, 36);
  if (value < 0)
  {
    output.insert(output.begin(), '-');
  }

  return output;
}

void my_itoa(int value, std::string& buf, int base)
{
  std::string hexarray("0123456789abcdefghijklmnopqrstuvwxyz");
  int i = 30;
  buf = "";

  if (!value)
  {
    buf = "0";
  }

  for (; value && i; --i, value /= base)
  {
    buf.insert(buf.begin(), (char)hexarray[value % base]);
  }
}

std::string strToLower(std::string temp)
{
  const int len = temp.length();

  for (int i = 0; i != len; ++i)
  {
    temp[i] = std::tolower(temp[i]);
  }

  return temp;
}

std::string dtos(double n)
{
  std::ostringstream result;
  result << n;
  return result.str();
}

std::string hash(std::string value)
{
  // Hash the player's name along with a secret to generate a unique hash for this player
  // Uses the DJB2 algorithm
  unsigned long hash = 5381;
  int c;

  char* cvalue = const_cast<char*>(value.c_str());

  while ((c = *cvalue++))
  {
    hash = ((hash << 5) + hash) + c;  /* hash * 33 + c */
  }

  std::ostringstream hashString;
  hashString << hash;

  return hashString.str();
}

bool fileExists(const std::string& filename)
{
  std::ifstream i(filename.c_str());
  return i;
}

std::string canonicalizePath(const std::string& pathname)
{
  // We assume that pathname is already a path name, with no file component!

#if defined(linux)

  wordexp_t exp_result;

  wordexp(pathname.c_str(), &exp_result, 0);

  char * d = strdup(exp_result.we_wordv[0]);

  wordfree(&exp_result);

  char * rp = new char[PATH_MAX];
  std::memset(rp, 0, PATH_MAX);
  realpath(d, rp);

  std::string res(rp);

  free(d);
  delete rp;

  return res;

#elif defined(WIN32)

  return pathname;

#else

  return pathname;

#endif

}

std::string relativeToAbsolute(std::string pathname)
{
  /// This is a very crude way to check if the path is relative.
  /// We must replace this by a more portable "pathIsRelative()" check.
  if (!pathname.empty() && pathname[0] != PATH_SEPARATOR)
    pathname = Mineserver::get()->config()->config_path + PATH_SEPARATOR + pathname;

  pathname = canonicalizePath(pathname);

  return pathname;
}

std::string getHomeDir()
{

#if defined(linux)

  return canonicalizePath("~/.mineserver");

#elif defined(WIN32)

  return "%APPDATA%\Mineserver";

#else

  return "[ERROR: getHomeDir() not implemented]";

#endif

}

std::string pathOfExecutable()
{
  const size_t dest_len = 4096;
  char path[dest_len];
  std::memset(path, 0, dest_len);

#if defined(linux)

  if (readlink ("/proc/self/exe", path, dest_len) != -1)
  {
    dirname(path);
  }

  return std::string(path);

#elif defined(WIN32)

  if (0 == GetModuleFileName(NULL, path, dest_len))
  {
    return "";
  }

  return pathOfFile(path).first;

#else

  return "";

#endif

}

std::pair<std::string, std::string> pathOfFile(const std::string& filename)
{

#if defined(linux)

  char * a = strdup(filename.c_str());
  char * b = strdup(filename.c_str());
  char * d = dirname(a);

  std::string res_p(d), res_f(basename(b));

  free(a);
  free(b);

  return std::make_pair(canonicalizePath(res_p), res_f);

#elif defined(WIN32)

  const size_t dest_len = 4096;
  char path[dest_len], *pPart;
  std::memset(path, 0, dest_len);

  GetFullPathName(filename.c_str(), dest_len, path, &pPart);

  const size_t diff = pPart - buffer;

  if (diff > 0 && diff != size_t(-1))
    return std::make_pair(std::string(path, diff - 1), std::string(pPart));
  else
    return std::make_pair(std::string(path), "");

#else

  return std::make_pair("[ERROR IN PATH RECONSTRUCTION: Unknown platform, please implement.]", "");

#endif

}
<commit_msg>Fixed relative-to-absolute path conversion to allow for unix-style home directories.<commit_after>/*
   Copyright (c) 2011, The Mineserver Project
   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.
  * Neither the name of the The Mineserver Project nor the
    names of its contributors may be used to endorse or promote products
    derived from this software without specific prior written permission.

  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 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.
 */

#ifdef WIN32
#include <winsock2.h>
#else
#include <netinet/in.h>
#include <pwd.h>
#endif

#ifdef linux
#include <unistd.h>
#include <libgen.h>
#include <wordexp.h>
#include <climits>
#endif

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <ctime>
#include <cmath>
#include <cctype>

#include "tools.h"
#include "logger.h"
#include "mineserver.h"
#include "config.h"

void putSint64(uint8_t* buf, int64_t value)
{
  uint64_t nval = ntohll(value);
  memcpy(buf, &nval, 8);
}

void putSint32(uint8_t* buf, int32_t value)
{
  uint32_t nval = htonl(value);
  memcpy(buf, &nval, 4);
}

void putSint16(uint8_t* buf, int16_t value)
{
  short value2 = htons(value);
  memcpy(buf, &value2, 2);
}

void putFloat(uint8_t* buf, float value)
{
  uint32_t nval;
  memcpy(&nval, &value, 4);
  nval = htonl(nval);
  memcpy(buf, &nval, 4);
}

void putDouble(uint8_t* buf, double value)
{
  uint64_t nval;
  memcpy(&nval, &value, 8);
  nval = ntohll(nval);
  memcpy(buf, &nval, 8);
}

double getDouble(uint8_t* buf)
{
  double val;
  uint64_t ival;
  memcpy(&ival, buf, 8);
  ival = ntohll(ival);
  memcpy(&val, &ival, 8);
  return val;
}

float getFloat(uint8_t* buf)
{
  float val;
  int ival = ntohl(*reinterpret_cast<const int32_t*>(buf));
  memcpy(&val, &ival, 4);
  return val;
}

int64_t getSint64(uint8_t* buf)
{
  int64_t val;
  memcpy(&val, buf, 8);
  val = ntohll(val);
  return val;
}

int32_t getSint32(uint8_t* buf)
{
  int val = ntohl(*reinterpret_cast<const int32_t*>(buf));
  return val;
}

int32_t getSint16(uint8_t* buf)
{
  short val = ntohs(*reinterpret_cast<const int16_t*>(buf));

  return val;
}

std::string base36_encode(int value)
{
  std::string output;
  my_itoa((int)abs(value), output, 36);
  if (value < 0)
  {
    output.insert(output.begin(), '-');
  }

  return output;
}

void my_itoa(int value, std::string& buf, int base)
{
  std::string hexarray("0123456789abcdefghijklmnopqrstuvwxyz");
  int i = 30;
  buf = "";

  if (!value)
  {
    buf = "0";
  }

  for (; value && i; --i, value /= base)
  {
    buf.insert(buf.begin(), (char)hexarray[value % base]);
  }
}

std::string strToLower(std::string temp)
{
  const int len = temp.length();

  for (int i = 0; i != len; ++i)
  {
    temp[i] = std::tolower(temp[i]);
  }

  return temp;
}

std::string dtos(double n)
{
  std::ostringstream result;
  result << n;
  return result.str();
}

std::string hash(std::string value)
{
  // Hash the player's name along with a secret to generate a unique hash for this player
  // Uses the DJB2 algorithm
  unsigned long hash = 5381;
  int c;

  char* cvalue = const_cast<char*>(value.c_str());

  while ((c = *cvalue++))
  {
    hash = ((hash << 5) + hash) + c;  /* hash * 33 + c */
  }

  std::ostringstream hashString;
  hashString << hash;

  return hashString.str();
}

bool fileExists(const std::string& filename)
{
  std::ifstream i(filename.c_str());
  return i;
}

std::string canonicalizePath(const std::string& pathname)
{
  // We assume that pathname is already a path name, with no file component!

#if defined(linux)

  wordexp_t exp_result;

  wordexp(pathname.c_str(), &exp_result, 0);

  char * d = strdup(exp_result.we_wordv[0]);

  wordfree(&exp_result);

  char * rp = new char[PATH_MAX];
  std::memset(rp, 0, PATH_MAX);
  realpath(d, rp);

  std::string res(rp);

  free(d);
  delete rp;

  return res;

#elif defined(WIN32)

  return pathname;

#else

  return pathname;

#endif

}

std::string relativeToAbsolute(std::string pathname)
{
  /// This is a very crude way to check if the path is relative.
  /// We must replace this by a more portable "pathIsRelative()" check.
  if (!pathname.empty() && pathname[0] != PATH_SEPARATOR && pathname[0] != '~')
    pathname = Mineserver::get()->config()->config_path + PATH_SEPARATOR + pathname;

  pathname = canonicalizePath(pathname);

  return pathname;
}

std::string getHomeDir()
{

#if defined(linux)

  return canonicalizePath("~/.mineserver");

#elif defined(WIN32)

  return "%APPDATA%\Mineserver";

#else

  return "[ERROR: getHomeDir() not implemented]";

#endif

}

std::string pathOfExecutable()
{
  const size_t dest_len = 4096;
  char path[dest_len];
  std::memset(path, 0, dest_len);

#if defined(linux)

  if (readlink ("/proc/self/exe", path, dest_len) != -1)
  {
    dirname(path);
  }

  return std::string(path);

#elif defined(WIN32)

  if (0 == GetModuleFileName(NULL, path, dest_len))
  {
    return "";
  }

  return pathOfFile(path).first;

#else

  return "";

#endif

}

std::pair<std::string, std::string> pathOfFile(const std::string& filename)
{

#if defined(linux)

  char * a = strdup(filename.c_str());
  char * b = strdup(filename.c_str());
  char * d = dirname(a);

  std::string res_p(d), res_f(basename(b));

  free(a);
  free(b);

  return std::make_pair(canonicalizePath(res_p), res_f);

#elif defined(WIN32)

  const size_t dest_len = 4096;
  char path[dest_len], *pPart;
  std::memset(path, 0, dest_len);

  GetFullPathName(filename.c_str(), dest_len, path, &pPart);

  const size_t diff = pPart - buffer;

  if (diff > 0 && diff != size_t(-1))
    return std::make_pair(std::string(path, diff - 1), std::string(pPart));
  else
    return std::make_pair(std::string(path), "");

#else

  return std::make_pair("[ERROR IN PATH RECONSTRUCTION: Unknown platform, please implement.]", "");

#endif

}
<|endoftext|>


<commit_before>/***********************************************************************************
** MIT License                                                                    **
**                                                                                **
** Copyright (c) 2017 Victor DENIS (victordenis01@gmail.com)                      **
**                                                                                **
** Permission is hereby granted, free of charge, to any person obtaining a copy   **
** of this software and associated documentation files (the "Software"), to deal  **
** in the Software without restriction, including without limitation the rights   **
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      **
** copies of the Software, and to permit persons to whom the Software is          **
** furnished to do so, subject to the following conditions:                       **
**                                                                                **
** The above copyright notice and this permission notice shall be included in all **
** copies or substantial portions of the Software.                                **
**                                                                                **
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     **
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       **
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    **
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         **
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  **
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  **
** SOFTWARE.                                                                      **
***********************************************************************************/

#include "Bookmarks/AddBookmarkDialog.hpp"

#include <QTreeView>
#include <QHeaderView>

#include "Bookmarks/BookmarkManager.hpp"
#include "Bookmarks/AddBookmarkProxyModel.hpp"
#include "Bookmarks/BookmarksModel.hpp"
#include "Bookmarks/BookmarkNode.hpp"

#include "Application.hpp"

namespace Sn {

AddBookmarkDialog::AddBookmarkDialog(const QString& url, const QString& title, QWidget* parent,
									 BookmarksManager* bookmarksManager) :
	QDialog(parent),
	m_url(url),
	m_bookmarksManager(bookmarksManager)
{
	if (!m_bookmarksManager)
		m_bookmarksManager = Application::instance()->bookmarksManager();

	setWindowFlags(Qt::Sheet);
	setupUI();

	QTreeView* view{new QTreeView(this)};
	BookmarksModel* model = m_bookmarksManager->bookmarksModel();

	m_proxyModel = new AddBookmarkProxyModel(this);
	m_proxyModel->setSourceModel(model);

	view->setModel(m_proxyModel);
	view->expandAll();
	view->header()->setStretchLastSection(true);
	view->header()->hide();
	view->setItemsExpandable(false);
	view->setRootIsDecorated(false);
	view->setIndentation(10);
	view->show();

	m_location->setModel(m_proxyModel);
	m_location->setView(view);

	m_name->setText(title);

	connect(m_buttonBox, &QDialogButtonBox::accepted, this, &AddBookmarkDialog::accept);

}

void AddBookmarkDialog::accept()
{
	QModelIndex index{m_location->view()->currentIndex()};

	index = m_proxyModel->mapToSource(index);

	if (!index.isValid())
		index = m_bookmarksManager->bookmarksModel()->index(0, 0);

	BookmarkNode* parent{m_bookmarksManager->bookmarksModel()->node(index)};
	BookmarkNode* bookmark{new BookmarkNode(BookmarkNode::Bookmark)};

	bookmark->url = m_url;
	bookmark->title = m_name->text();

	m_bookmarksManager->addBookmark(parent, bookmark);

	QDialog::accept();
}

void AddBookmarkDialog::setupUI()
{
	resize(240, 168);

	m_layout = new QVBoxLayout(this);

	m_description = new QLabel(tr("Type a name for the bookmark and choose where to keep it"), this);
	m_description->setTextFormat(Qt::PlainText);
	m_description->setWordWrap(true);

	m_name = new QLineEdit(this);
	m_name->setPlaceholderText(tr("Name of bookmark"));

	m_location = new QComboBox(this);

	m_spacer = new QSpacerItem(20, 2, QSizePolicy::Minimum, QSizePolicy::Expanding);

	m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok, Qt::Horizontal, this);
	m_buttonBox->setCenterButtons(false);

	m_layout->addWidget(m_description);
	m_layout->addWidget(m_name);
	m_layout->addWidget(m_location);
	m_layout->addItem(m_spacer);
	m_layout->addWidget(m_buttonBox);
}

}<commit_msg>Core/Bookmarks: [Fix] cancel button issue on add bookmark dialog<commit_after>/***********************************************************************************
** MIT License                                                                    **
**                                                                                **
** Copyright (c) 2017 Victor DENIS (victordenis01@gmail.com)                      **
**                                                                                **
** Permission is hereby granted, free of charge, to any person obtaining a copy   **
** of this software and associated documentation files (the "Software"), to deal  **
** in the Software without restriction, including without limitation the rights   **
** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell      **
** copies of the Software, and to permit persons to whom the Software is          **
** furnished to do so, subject to the following conditions:                       **
**                                                                                **
** The above copyright notice and this permission notice shall be included in all **
** copies or substantial portions of the Software.                                **
**                                                                                **
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR     **
** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,       **
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE    **
** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER         **
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  **
** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  **
** SOFTWARE.                                                                      **
***********************************************************************************/

#include "Bookmarks/AddBookmarkDialog.hpp"

#include <QTreeView>
#include <QHeaderView>

#include "Bookmarks/BookmarkManager.hpp"
#include "Bookmarks/AddBookmarkProxyModel.hpp"
#include "Bookmarks/BookmarksModel.hpp"
#include "Bookmarks/BookmarkNode.hpp"

#include "Application.hpp"

namespace Sn {

AddBookmarkDialog::AddBookmarkDialog(const QString& url, const QString& title, QWidget* parent,
									 BookmarksManager* bookmarksManager) :
	QDialog(parent),
	m_url(url),
	m_bookmarksManager(bookmarksManager)
{
	if (!m_bookmarksManager)
		m_bookmarksManager = Application::instance()->bookmarksManager();

	setWindowFlags(Qt::Sheet);
	setupUI();

	QTreeView* view{new QTreeView(this)};
	BookmarksModel* model = m_bookmarksManager->bookmarksModel();

	m_proxyModel = new AddBookmarkProxyModel(this);
	m_proxyModel->setSourceModel(model);

	view->setModel(m_proxyModel);
	view->expandAll();
	view->header()->setStretchLastSection(true);
	view->header()->hide();
	view->setItemsExpandable(false);
	view->setRootIsDecorated(false);
	view->setIndentation(10);
	view->show();

	m_location->setModel(m_proxyModel);
	m_location->setView(view);

	m_name->setText(title);

	connect(m_buttonBox, &QDialogButtonBox::accepted, this, &AddBookmarkDialog::accept);
	connect(m_buttonBox, &QDialogButtonBox::rejected, this, &AddBookmarkDialog::reject);
}

void AddBookmarkDialog::accept()
{
	QModelIndex index{m_location->view()->currentIndex()};

	index = m_proxyModel->mapToSource(index);

	if (!index.isValid())
		index = m_bookmarksManager->bookmarksModel()->index(0, 0);

	BookmarkNode* parent{m_bookmarksManager->bookmarksModel()->node(index)};
	BookmarkNode* bookmark{new BookmarkNode(BookmarkNode::Bookmark)};

	bookmark->url = m_url;
	bookmark->title = m_name->text();

	m_bookmarksManager->addBookmark(parent, bookmark);

	QDialog::accept();
}

void AddBookmarkDialog::setupUI()
{
	resize(240, 168);

	m_layout = new QVBoxLayout(this);

	m_description = new QLabel(tr("Type a name for the bookmark and choose where to keep it"), this);
	m_description->setTextFormat(Qt::PlainText);
	m_description->setWordWrap(true);

	m_name = new QLineEdit(this);
	m_name->setPlaceholderText(tr("Name of bookmark"));

	m_location = new QComboBox(this);

	m_spacer = new QSpacerItem(20, 2, QSizePolicy::Minimum, QSizePolicy::Expanding);

	m_buttonBox = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok, Qt::Horizontal, this);
	m_buttonBox->setCenterButtons(false);

	m_layout->addWidget(m_description);
	m_layout->addWidget(m_name);
	m_layout->addWidget(m_location);
	m_layout->addItem(m_spacer);
	m_layout->addWidget(m_buttonBox);
}

}<|endoftext|>