<commit_before>#include "testing/testing.hpp"

#include "generator/feature_generator.hpp"
#include "generator/generator_tests/common.hpp"
#include "generator/popularity.hpp"

#include "indexer/classificator_loader.hpp"
#include "indexer/classificator.hpp"

#include "platform/platform.hpp"

#include "coding/string_utf8_multilang.hpp"

#include "base/geo_object_id.hpp"
#include "base/scope_guard.hpp"

#include <algorithm>
#include <cstdint>
#include <iterator>
#include <map>
#include <string>
#include <vector>

using namespace generator;
using namespace generator::popularity;

namespace generator_tests
{
class TestPopularityBuilder
{
public:
  TestPopularityBuilder()
  {
    classificator::Load();
  }

  void GetType() const
  {
    {
      FeatureBuilder1 fb;
      auto const type = m_cl.GetTypeByPath({"tourism", "museum"});
      auto const checkableType =  m_cl.GetReadableObjectName(type);
      fb.AddType(m_cl.GetTypeByPath({"building"}));
      fb.AddType(type);
      TEST_EQUAL(PopularityBuilder::GetType(fb), checkableType, ());
    }

    {
      FeatureBuilder1 fb;
      fb.AddType(m_cl.GetTypeByPath({"building"}));
      TEST_EQUAL(PopularityBuilder::GetType(fb), "", ());
    }

    {
      FeatureBuilder1 fb;
      TEST_EQUAL(PopularityBuilder::GetType(fb), "", ());
    }
  }

  static void GetFeatureName()
  {
    {
      FeatureBuilder1 fb;
      std::string checkableName = "Фича";
      fb.AddName("ru", checkableName);
      fb.AddName("en", "Feature");
      TEST_EQUAL(PopularityBuilder::GetFeatureName(fb), checkableName, ());
    }

    {
      FeatureBuilder1 fb;
      std::string checkableName = "Feature";
      fb.AddName("en", checkableName);
      fb.AddName("default", "Fonctionnalité");
      TEST_EQUAL(PopularityBuilder::GetFeatureName(fb), checkableName, ());
    }

    {
      FeatureBuilder1 fb;
      std::string checkableName = "Fonctionnalité";
      fb.AddName("default", checkableName);
      TEST_EQUAL(PopularityBuilder::GetFeatureName(fb), checkableName, ());
    }

    {
      FeatureBuilder1 fb;
      fb.AddName("fr", "Fonctionnalité");
      TEST_EQUAL(PopularityBuilder::GetFeatureName(fb), "", ());
    }

    {
      FeatureBuilder1 fb;
      TEST_EQUAL(PopularityBuilder::GetFeatureName(fb), "", ());
    }
  }

  void FindPointParent() const
  {
    auto const filtered = FilterPoint(m_testSet);
    std::map<std::string, FeatureBuilder1> pointMap;
    for (auto const & f : filtered)
      pointMap.emplace(f.GetName(), f);

    auto const filteredArea = FilterArea(m_testSet);
    auto geomPlaces = MakePopularityGeomPlaces(filteredArea);
    auto const nameToNode = GetPlacesMap(geomPlaces);
    auto const m = PopularityBuilder::GetAreaMap(geomPlaces);
    auto const tree = PopularityBuilder::MakeTree4d(geomPlaces);

    {
      auto const & ft = pointMap.at("1_3_4");
      auto const parent = PopularityBuilder::FindPointParent(ft.GetKeyPoint(), m, tree);
      TEST(parent, ());
      TEST_EQUAL(*parent, nameToNode.at("1_3")->GetData().GetId(), ());
    }

    {
      auto const & ft = pointMap.at("1_3_5");
      auto const parent = PopularityBuilder::FindPointParent(ft.GetKeyPoint(), m, tree);
      TEST(parent, ());
      TEST_EQUAL(*parent, nameToNode.at("1_3")->GetData().GetId(), ());
    }

    {
      m2::PointD bad{1000.0, 1000.0};
      auto const parent = PopularityBuilder::FindPointParent(bad, m, tree);
      TEST(!parent, ());
    }
  }

  void FindPopularityGeomPlaceParent() const
  {
    auto const filtered = FilterArea(m_testSet);
    auto geomPlaces = MakePopularityGeomPlaces(filtered);
    auto const nameToNode = GetPlacesMap(geomPlaces);
    auto const m = PopularityBuilder::GetAreaMap(geomPlaces);
    auto const tree = PopularityBuilder::MakeTree4d(geomPlaces);

    {
      auto const t = PopularityBuilder::FindPopularityGeomPlaceParent(nameToNode.at("1_2")->GetData(), m, tree);
      TEST(t, ());
      TEST_EQUAL(*t, nameToNode.at("1"), ());
    }

    {
      auto const t = PopularityBuilder::FindPopularityGeomPlaceParent(nameToNode.at("1_3")->GetData(), m, tree);
      TEST(t, ());
      TEST_EQUAL(*t, nameToNode.at("1"), ());
    }

    {
      auto const t = PopularityBuilder::FindPopularityGeomPlaceParent(nameToNode.at("1")->GetData(), m, tree);
      TEST(!t, ());
    }
  }

  void GetAreaMap() const
  {
    {
      auto const filtered = FilterArea(m_testSet);
      auto const geomPlaces = MakePopularityGeomPlaces(filtered);
      std::map<base::GeoObjectId, PopularityBuilder::Node::Ptr> checkableMap;
      for (auto const & n : geomPlaces)
        checkableMap.emplace(n->GetData().GetId(), n);

      auto const m = PopularityBuilder::GetAreaMap(geomPlaces);
      TEST_EQUAL(m.size(), checkableMap.size(), ());
      for (auto const & p : checkableMap)
      {
        TEST_EQUAL(m.count(p.first), 1, ());
        TEST_EQUAL(p.second, m.at(p.first), ());
      }
    }

    {
      auto const m = PopularityBuilder::GetAreaMap({});
      TEST(m.empty(), ());
    }
  }

  void MakeTree4d() const
  {
    {
      std::set<base::GeoObjectId> checkableIds;
      auto const filtered = FilterArea(m_testSet);
      auto const geomPlaces = MakePopularityGeomPlaces(filtered);
      for (auto const & node : geomPlaces)
        checkableIds.insert(node->GetData().GetId());

      auto const tree = PopularityBuilder::MakeTree4d(geomPlaces);
      std::set<base::GeoObjectId> ids;
      tree.ForEach([&](auto const & id) {
        ids.insert(id);
      });

      TEST_EQUAL(checkableIds, ids, ());
    }

    {
      std::set<base::GeoObjectId> checkableIds;
      auto const tree = PopularityBuilder::MakeTree4d({});
      std::set<base::GeoObjectId> ids;
      tree.ForEach([&](auto const & id) {
        ids.insert(id);
      });

      TEST_EQUAL(checkableIds, ids, ());
    }
  }

  void LinkGeomPlaces() const
  {
    auto const filtered = FilterArea(m_testSet);
    auto geomPlaces = MakePopularityGeomPlaces(filtered);
    auto const nameToNode = GetPlacesMap(geomPlaces);
    auto const m = PopularityBuilder::GetAreaMap(geomPlaces);
    auto const tree = PopularityBuilder::MakeTree4d(geomPlaces);
    PopularityBuilder::LinkGeomPlaces(m, tree, geomPlaces);

    TEST_EQUAL(nameToNode.size(), 3, ());
    TEST_EQUAL(nameToNode.at("1")->GetParent(), PopularityBuilder::Node::Ptr(), ());
    TEST_EQUAL(nameToNode.at("1_2")->GetParent(), nameToNode.at("1"), ());
    TEST_EQUAL(nameToNode.at("1_3")->GetParent(), nameToNode.at("1"), ());
  }

  static void MakeNodes()
  {
    std::vector<FeatureBuilder1> v = FilterArea(m_testSet);
    auto const nodes = PopularityBuilder::MakeNodes(v);
    TEST_EQUAL(nodes.size(), v.size(), ());
    for (size_t i = 0; i < v.size(); ++i)
      TEST_EQUAL(nodes[i]->GetData().GetId(), v[i].GetMostGenericOsmId() , ());
  }

  void Build() const
  {
    auto const filename = GetFileName();
    auto const type = m_cl.GetTypeByPath({"tourism", "museum"});
    auto const typeName = m_cl.GetReadableObjectName(type);
    auto const testSet = AddTypes(m_testSet, {type});

    {
      feature::FeaturesCollector collector(filename);
      for (auto const & feature : testSet)
        collector(feature);
    }

    PopularityBuilder builder(filename);
    auto const lines = builder.Build();

    std::map<std::string, PopularityLine> m;
    for (auto const & line : lines)
      m.emplace(line.m_name, line);

    SCOPE_GUARD(RemoveFile, [&] {
      Platform::RemoveFileIfExists(filename);
    });

    TEST_EQUAL(lines.size(), testSet.size(), ());
    TEST(!m.at("1").m_parent, ());
    TEST_EQUAL(m.at("1").m_type,  typeName, ());

    TEST(m.at("1_2").m_parent, ());
    TEST_EQUAL(*m.at("1_2").m_parent, m.at("1").m_id, ());
    TEST_EQUAL(m.at("1_2").m_type, typeName, ());

    TEST(m.at("1_3").m_parent, ());
    TEST_EQUAL(*m.at("1_3").m_parent, m.at("1").m_id, ());
    TEST_EQUAL(m.at("1_3").m_type, typeName, ());

    TEST(m.at("1_3_4").m_parent, ());
    TEST_EQUAL(*m.at("1_3_4").m_parent, m.at("1_3").m_id, ());
    TEST_EQUAL(m.at("1_3_4").m_type, typeName, ());

    TEST(m.at("1_3_5").m_parent, ());
    TEST_EQUAL(*m.at("1_3_5").m_parent, m.at("1_3").m_id, ());
    TEST_EQUAL(m.at("1_3_5").m_type, typeName, ());
  }

private:
  static std::vector<FeatureBuilder1> GetTestSet()
  {
    std::vector<FeatureBuilder1> v;
    v.reserve(5);

    {
      FeatureBuilder1 feature;
      feature.AddOsmId(base::GeoObjectId(1));
      auto const firstLast = m2::PointD{0.0, 0.0};
      feature.AddPoint(firstLast);
      feature.AddPoint(m2::PointD{0.0, 7.0});
      feature.AddPoint(m2::PointD{10.0, 7.0});
      feature.AddPoint(m2::PointD{10.0, 0.0});
      feature.AddPoint(firstLast);
      feature.SetArea();
      feature.AddName("default", "1");
      v.push_back(feature);
    }

    {
      FeatureBuilder1 feature;
      feature.AddOsmId(base::GeoObjectId(2));
      auto const firstLast = m2::PointD{2.0, 3.0};
      feature.AddPoint(firstLast);
      feature.AddPoint(m2::PointD{2.0, 5.0});
      feature.AddPoint(m2::PointD{4.0, 5.0});
      feature.AddPoint(m2::PointD{4.0, 3.0});
      feature.AddPoint(firstLast);
      feature.SetArea();
      feature.AddName("default", "1_2");
      v.push_back(feature);
    }

    {
      FeatureBuilder1 feature;
      feature.AddOsmId(base::GeoObjectId(3));
      auto const firstLast = m2::PointD{6.0, 0.0};
      feature.AddPoint(firstLast);
      feature.AddPoint(m2::PointD{6.0, 3.0});
      feature.AddPoint(m2::PointD{10.0, 3.0});
      feature.AddPoint(m2::PointD{10.0, 0.0});
      feature.AddPoint(firstLast);
      feature.SetArea();
      feature.AddName("default", "1_3");
      v.push_back(feature);
    }

    {
      FeatureBuilder1 feature;
      feature.AddOsmId(base::GeoObjectId(4));
      feature.SetCenter(m2::PointD{8.0, 2.0});
      feature.AddName("default", "1_3_4");
      v.push_back(feature);
    }

    {
      FeatureBuilder1 feature;
      feature.AddOsmId(base::GeoObjectId(5));
      feature.SetCenter(m2::PointD{7.0, 2.0});
      feature.AddName("default", "1_3_5");
      v.push_back(feature);
    }

    return v;
  }

  static std::vector<FeatureBuilder1> FilterArea(std::vector<FeatureBuilder1> const & v)
  {
    std::vector<FeatureBuilder1> filtered;
    std::copy_if(std::begin(v), std::end(v), std::back_inserter(filtered), [](auto const & feature) {
      return feature.IsArea() && feature.IsGeometryClosed();
    });

    return filtered;
  }

  static std::vector<FeatureBuilder1> FilterPoint(std::vector<FeatureBuilder1> const & v)
  {
    std::vector<FeatureBuilder1> filtered;
    std::copy_if(std::begin(v), std::end(v), std::back_inserter(filtered), [](auto const & feature) {
      return feature.IsPoint();
    });

    return filtered;
  }

  static std::map<std::string, PopularityBuilder::Node::Ptr>
  GetPlacesMap(PopularityBuilder::Node::PtrList const & geomPlaces)
  {
    std::map<std::string, PopularityBuilder::Node::Ptr> nameToNode;
    for (auto const & place : geomPlaces)
      nameToNode.emplace(place->GetData().GetFeature().GetName(), place);

    return nameToNode;
  }

  static PopularityBuilder::Node::PtrList MakePopularityGeomPlaces(std::vector<FeatureBuilder1> const & v)
  {
    PopularityBuilder::Node::PtrList nodes;
    nodes.reserve(v.size());
    std::transform(std::begin(v), std::end(v), std::back_inserter(nodes), [](FeatureBuilder1 const & f) {
      return std::make_shared<PopularityBuilder::Node>(PopularityGeomPlace(f));
    });

    return nodes;
  }

  static std::vector<FeatureBuilder1> AddTypes(std::vector<FeatureBuilder1> v, std::vector<uint32_t> const & types)
  {
    for (auto & feature : v)
    {
      for (auto const & type : types)
        feature.AddType(type);
    }

    return v;
  }

  static Classificator & m_cl;
  static std::vector<FeatureBuilder1> const m_testSet;
};

// static
Classificator & TestPopularityBuilder::m_cl = classif();
std::vector<FeatureBuilder1> const TestPopularityBuilder::m_testSet = TestPopularityBuilder::GetTestSet();
}  // namespace generator_tests

using namespace generator_tests;

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_GetType)
{
  TestPopularityBuilder::GetType();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_GetFeatureName)
{
  TestPopularityBuilder::GetFeatureName();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_FindPointParent)
{
  TestPopularityBuilder::FindPointParent();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_FindPopularityGeomPlaceParent)
{
  TestPopularityBuilder::FindPopularityGeomPlaceParent();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_GetAreaMap)
{
  TestPopularityBuilder::GetAreaMap();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_MakeTree4d)
{
  TestPopularityBuilder::MakeTree4d();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_LinkGeomPlaces)
{
  TestPopularityBuilder::LinkGeomPlaces();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_MakeNodes)
{
  TestPopularityBuilder::MakeNodes();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_Build)
{
  TestPopularityBuilder::Build();
}
<commit_msg>[generator] popularity tests fix. It was broken by 6527148af6.<commit_after>#include "testing/testing.hpp"

#include "generator/feature_generator.hpp"
#include "generator/generator_tests/common.hpp"
#include "generator/popularity.hpp"

#include "indexer/classificator_loader.hpp"
#include "indexer/classificator.hpp"

#include "platform/platform.hpp"

#include "coding/string_utf8_multilang.hpp"

#include "base/geo_object_id.hpp"
#include "base/scope_guard.hpp"

#include <algorithm>
#include <cstdint>
#include <iterator>
#include <map>
#include <string>
#include <vector>

using namespace generator;
using namespace generator::popularity;

namespace generator_tests
{
class TestPopularityBuilder
{
public:
  TestPopularityBuilder()
  {
    classificator::Load();
    m_testSet = TestPopularityBuilder::GetTestSet();
  }

  void GetType() const
  {
    {
      FeatureBuilder1 fb;
      auto const type = m_cl.GetTypeByPath({"tourism", "museum"});
      auto const checkableType =  m_cl.GetReadableObjectName(type);
      fb.AddType(m_cl.GetTypeByPath({"building"}));
      fb.AddType(type);
      TEST_EQUAL(PopularityBuilder::GetType(fb), checkableType, ());
    }

    {
      FeatureBuilder1 fb;
      fb.AddType(m_cl.GetTypeByPath({"building"}));
      TEST_EQUAL(PopularityBuilder::GetType(fb), "", ());
    }

    {
      FeatureBuilder1 fb;
      TEST_EQUAL(PopularityBuilder::GetType(fb), "", ());
    }
  }

  static void GetFeatureName()
  {
    {
      FeatureBuilder1 fb;
      std::string checkableName = "Фича";
      fb.AddName("ru", checkableName);
      fb.AddName("en", "Feature");
      TEST_EQUAL(PopularityBuilder::GetFeatureName(fb), checkableName, ());
    }

    {
      FeatureBuilder1 fb;
      std::string checkableName = "Feature";
      fb.AddName("en", checkableName);
      fb.AddName("default", "Fonctionnalité");
      TEST_EQUAL(PopularityBuilder::GetFeatureName(fb), checkableName, ());
    }

    {
      FeatureBuilder1 fb;
      std::string checkableName = "Fonctionnalité";
      fb.AddName("default", checkableName);
      TEST_EQUAL(PopularityBuilder::GetFeatureName(fb), checkableName, ());
    }

    {
      FeatureBuilder1 fb;
      fb.AddName("fr", "Fonctionnalité");
      TEST_EQUAL(PopularityBuilder::GetFeatureName(fb), "", ());
    }

    {
      FeatureBuilder1 fb;
      TEST_EQUAL(PopularityBuilder::GetFeatureName(fb), "", ());
    }
  }

  void FindPointParent() const
  {
    auto const filtered = FilterPoint(m_testSet);
    std::map<std::string, FeatureBuilder1> pointMap;
    for (auto const & f : filtered)
      pointMap.emplace(f.GetName(), f);

    auto const filteredArea = FilterArea(m_testSet);
    auto geomPlaces = MakePopularityGeomPlaces(filteredArea);
    auto const nameToNode = GetPlacesMap(geomPlaces);
    auto const m = PopularityBuilder::GetAreaMap(geomPlaces);
    auto const tree = PopularityBuilder::MakeTree4d(geomPlaces);

    {
      auto const & ft = pointMap.at("1_3_4");
      auto const parent = PopularityBuilder::FindPointParent(ft.GetKeyPoint(), m, tree);
      TEST(parent, ());
      TEST_EQUAL(*parent, nameToNode.at("1_3")->GetData().GetId(), ());
    }

    {
      auto const & ft = pointMap.at("1_3_5");
      auto const parent = PopularityBuilder::FindPointParent(ft.GetKeyPoint(), m, tree);
      TEST(parent, ());
      TEST_EQUAL(*parent, nameToNode.at("1_3")->GetData().GetId(), ());
    }

    {
      m2::PointD bad{1000.0, 1000.0};
      auto const parent = PopularityBuilder::FindPointParent(bad, m, tree);
      TEST(!parent, ());
    }
  }

  void FindPopularityGeomPlaceParent() const
  {
    auto const filtered = FilterArea(m_testSet);
    auto geomPlaces = MakePopularityGeomPlaces(filtered);
    auto const nameToNode = GetPlacesMap(geomPlaces);
    auto const m = PopularityBuilder::GetAreaMap(geomPlaces);
    auto const tree = PopularityBuilder::MakeTree4d(geomPlaces);

    {
      auto const t = PopularityBuilder::FindPopularityGeomPlaceParent(nameToNode.at("1_2")->GetData(), m, tree);
      TEST(t, ());
      TEST_EQUAL(*t, nameToNode.at("1"), ());
    }

    {
      auto const t = PopularityBuilder::FindPopularityGeomPlaceParent(nameToNode.at("1_3")->GetData(), m, tree);
      TEST(t, ());
      TEST_EQUAL(*t, nameToNode.at("1"), ());
    }

    {
      auto const t = PopularityBuilder::FindPopularityGeomPlaceParent(nameToNode.at("1")->GetData(), m, tree);
      TEST(!t, ());
    }
  }

  void GetAreaMap() const
  {
    {
      auto const filtered = FilterArea(m_testSet);
      auto const geomPlaces = MakePopularityGeomPlaces(filtered);
      std::map<base::GeoObjectId, PopularityBuilder::Node::Ptr> checkableMap;
      for (auto const & n : geomPlaces)
        checkableMap.emplace(n->GetData().GetId(), n);

      auto const m = PopularityBuilder::GetAreaMap(geomPlaces);
      TEST_EQUAL(m.size(), checkableMap.size(), ());
      for (auto const & p : checkableMap)
      {
        TEST_EQUAL(m.count(p.first), 1, ());
        TEST_EQUAL(p.second, m.at(p.first), ());
      }
    }

    {
      auto const m = PopularityBuilder::GetAreaMap({});
      TEST(m.empty(), ());
    }
  }

  void MakeTree4d() const
  {
    {
      std::set<base::GeoObjectId> checkableIds;
      auto const filtered = FilterArea(m_testSet);
      auto const geomPlaces = MakePopularityGeomPlaces(filtered);
      for (auto const & node : geomPlaces)
        checkableIds.insert(node->GetData().GetId());

      auto const tree = PopularityBuilder::MakeTree4d(geomPlaces);
      std::set<base::GeoObjectId> ids;
      tree.ForEach([&](auto const & id) {
        ids.insert(id);
      });

      TEST_EQUAL(checkableIds, ids, ());
    }

    {
      std::set<base::GeoObjectId> checkableIds;
      auto const tree = PopularityBuilder::MakeTree4d({});
      std::set<base::GeoObjectId> ids;
      tree.ForEach([&](auto const & id) {
        ids.insert(id);
      });

      TEST_EQUAL(checkableIds, ids, ());
    }
  }

  void LinkGeomPlaces() const
  {
    auto const filtered = FilterArea(m_testSet);
    auto geomPlaces = MakePopularityGeomPlaces(filtered);
    auto const nameToNode = GetPlacesMap(geomPlaces);
    auto const m = PopularityBuilder::GetAreaMap(geomPlaces);
    auto const tree = PopularityBuilder::MakeTree4d(geomPlaces);
    PopularityBuilder::LinkGeomPlaces(m, tree, geomPlaces);

    TEST_EQUAL(nameToNode.size(), 3, ());
    TEST_EQUAL(nameToNode.at("1")->GetParent(), PopularityBuilder::Node::Ptr(), ());
    TEST_EQUAL(nameToNode.at("1_2")->GetParent(), nameToNode.at("1"), ());
    TEST_EQUAL(nameToNode.at("1_3")->GetParent(), nameToNode.at("1"), ());
  }

  void MakeNodes()
  {
    std::vector<FeatureBuilder1> v = FilterArea(m_testSet);
    auto const nodes = PopularityBuilder::MakeNodes(v);
    TEST_EQUAL(nodes.size(), v.size(), ());
    for (size_t i = 0; i < v.size(); ++i)
      TEST_EQUAL(nodes[i]->GetData().GetId(), v[i].GetMostGenericOsmId() , ());
  }

  void Build() const
  {
    auto const filename = GetFileName();
    auto const type = m_cl.GetTypeByPath({"tourism", "museum"});
    auto const typeName = m_cl.GetReadableObjectName(type);

    {
      feature::FeaturesCollector collector(filename);
      for (auto const & feature : m_testSet)
        collector(feature);
    }

    PopularityBuilder builder(filename);
    auto const lines = builder.Build();

    std::map<std::string, PopularityLine> m;
    for (auto const & line : lines)
      m.emplace(line.m_name, line);

    SCOPE_GUARD(RemoveFile, [&] {
      Platform::RemoveFileIfExists(filename);
    });

    TEST_EQUAL(lines.size(), m_testSet.size(), ());
    TEST(!m.at("1").m_parent, ());
    TEST_EQUAL(m.at("1").m_type,  typeName, ());

    TEST(m.at("1_2").m_parent, ());
    TEST_EQUAL(*m.at("1_2").m_parent, m.at("1").m_id, ());
    TEST_EQUAL(m.at("1_2").m_type, typeName, ());

    TEST(m.at("1_3").m_parent, ());
    TEST_EQUAL(*m.at("1_3").m_parent, m.at("1").m_id, ());
    TEST_EQUAL(m.at("1_3").m_type, typeName, ());

    TEST(m.at("1_3_4").m_parent, ());
    TEST_EQUAL(*m.at("1_3_4").m_parent, m.at("1_3").m_id, ());
    TEST_EQUAL(m.at("1_3_4").m_type, typeName, ());

    TEST(m.at("1_3_5").m_parent, ());
    TEST_EQUAL(*m.at("1_3_5").m_parent, m.at("1_3").m_id, ());
    TEST_EQUAL(m.at("1_3_5").m_type, typeName, ());
  }

private:
  static std::vector<FeatureBuilder1> GetTestSet()
  {
    std::vector<FeatureBuilder1> v;
    v.reserve(5);

    {
      FeatureBuilder1 feature;
      feature.AddOsmId(base::GeoObjectId(1));
      auto const firstLast = m2::PointD{0.0, 0.0};
      feature.AddPoint(firstLast);
      feature.AddPoint(m2::PointD{0.0, 7.0});
      feature.AddPoint(m2::PointD{10.0, 7.0});
      feature.AddPoint(m2::PointD{10.0, 0.0});
      feature.AddPoint(firstLast);
      feature.SetArea();
      feature.AddType(classif().GetTypeByPath({"tourism", "museum"}));
      feature.AddName("default", "1");
      v.push_back(feature);
    }

    {
      FeatureBuilder1 feature;
      feature.AddOsmId(base::GeoObjectId(2));
      auto const firstLast = m2::PointD{2.0, 3.0};
      feature.AddPoint(firstLast);
      feature.AddPoint(m2::PointD{2.0, 5.0});
      feature.AddPoint(m2::PointD{4.0, 5.0});
      feature.AddPoint(m2::PointD{4.0, 3.0});
      feature.AddPoint(firstLast);
      feature.SetArea();
      feature.AddType(classif().GetTypeByPath({"tourism", "museum"}));
      feature.AddName("default", "1_2");
      v.push_back(feature);
    }

    {
      FeatureBuilder1 feature;
      feature.AddOsmId(base::GeoObjectId(3));
      auto const firstLast = m2::PointD{6.0, 0.0};
      feature.AddPoint(firstLast);
      feature.AddPoint(m2::PointD{6.0, 3.0});
      feature.AddPoint(m2::PointD{10.0, 3.0});
      feature.AddPoint(m2::PointD{10.0, 0.0});
      feature.AddPoint(firstLast);
      feature.SetArea();
      feature.AddType(classif().GetTypeByPath({"tourism", "museum"}));
      feature.AddName("default", "1_3");
      v.push_back(feature);
    }

    {
      FeatureBuilder1 feature;
      feature.AddOsmId(base::GeoObjectId(4));
      feature.SetCenter(m2::PointD{8.0, 2.0});
      feature.AddName("default", "1_3_4");
      feature.AddType(classif().GetTypeByPath({"tourism", "museum"}));
      v.push_back(feature);
    }

    {
      FeatureBuilder1 feature;
      feature.AddOsmId(base::GeoObjectId(5));
      feature.SetCenter(m2::PointD{7.0, 2.0});
      feature.AddName("default", "1_3_5");
      feature.AddType(classif().GetTypeByPath({"tourism", "museum"}));
      v.push_back(feature);
    }

    return v;
  }

  static std::vector<FeatureBuilder1> FilterArea(std::vector<FeatureBuilder1> const & v)
  {
    std::vector<FeatureBuilder1> filtered;
    std::copy_if(std::begin(v), std::end(v), std::back_inserter(filtered), [](auto const & feature) {
      return feature.IsArea() && feature.IsGeometryClosed();
    });

    return filtered;
  }

  static std::vector<FeatureBuilder1> FilterPoint(std::vector<FeatureBuilder1> const & v)
  {
    std::vector<FeatureBuilder1> filtered;
    std::copy_if(std::begin(v), std::end(v), std::back_inserter(filtered), [](auto const & feature) {
      return feature.IsPoint();
    });

    return filtered;
  }

  static std::map<std::string, PopularityBuilder::Node::Ptr>
  GetPlacesMap(PopularityBuilder::Node::PtrList const & geomPlaces)
  {
    std::map<std::string, PopularityBuilder::Node::Ptr> nameToNode;
    for (auto const & place : geomPlaces)
      nameToNode.emplace(place->GetData().GetFeature().GetName(), place);

    return nameToNode;
  }

  static PopularityBuilder::Node::PtrList MakePopularityGeomPlaces(std::vector<FeatureBuilder1> const & v)
  {
    PopularityBuilder::Node::PtrList nodes;
    nodes.reserve(v.size());
    std::transform(std::begin(v), std::end(v), std::back_inserter(nodes), [](FeatureBuilder1 const & f) {
      return std::make_shared<PopularityBuilder::Node>(PopularityGeomPlace(f));
    });

    return nodes;
  }

  static std::vector<FeatureBuilder1> AddTypes(std::vector<FeatureBuilder1> v, std::vector<uint32_t> const & types)
  {
    for (auto & feature : v)
    {
      for (auto const & type : types)
        feature.AddType(type);
    }

    return v;
  }

  Classificator const & m_cl = classif();
  std::vector<FeatureBuilder1> m_testSet;
};
}  // namespace generator_tests

using namespace generator_tests;

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_GetType)
{
  TestPopularityBuilder::GetType();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_GetFeatureName)
{
  TestPopularityBuilder::GetFeatureName();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_FindPointParent)
{
  TestPopularityBuilder::FindPointParent();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_FindPopularityGeomPlaceParent)
{
  TestPopularityBuilder::FindPopularityGeomPlaceParent();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_GetAreaMap)
{
  TestPopularityBuilder::GetAreaMap();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_MakeTree4d)
{
  TestPopularityBuilder::MakeTree4d();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_LinkGeomPlaces)
{
  TestPopularityBuilder::LinkGeomPlaces();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_MakeNodes)
{
  TestPopularityBuilder::MakeNodes();
}

UNIT_CLASS_TEST(TestPopularityBuilder, PopularityBuilder_Build)
{
  TestPopularityBuilder::Build();
}
<|endoftext|>


<commit_before>/*
 * Chord.cpp
 *
 *  Created on: Mar 17, 2015
 *      Author: jonno
 */

#include <iostream>
#include <cstdio>
#include <sha.h>
#include <memory>
#include <thread>
#include "Chord.h"
#include "Node.h"
#include "utils/csapp.h"
#include <sstream>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <ifaddrs.h>

using namespace std;

shared_ptr<Chord> Chord::myInstance = nullptr;

void Chord::Listen() {
	assert(myKey != 0);

	myListenFD = Open_listenfd(myListenPort);
	if (myListenFD < 0) {
		exit(-1);
	}

	int optval = 1;
	setsockopt(myListenFD, SOL_SOCKET, SO_REUSEADDR, (const void *) &optval, sizeof(int));

	cout << "Chord listening at " << myIPAddress << ":" << myListenPort << endl;

	socklen_t client_len;
	struct sockaddr_in client_addr;

	int newConnection;

	vector<thread> threads;

	while (true) {
		client_len = sizeof(client_addr);
		newConnection = Accept(myListenFD, (sockaddr*) &client_addr, &client_len);

		cout << "New Connection from " << inet_ntoa(client_addr.sin_addr) << ":" << dec << ntohs(client_addr.sin_port) << endl;

		threads.push_back(
				thread(&Chord::handleRequest, this, newConnection, client_addr)
				);
	}

}

Chord::Chord() {
	myListenPort = 0;
	myListenFD = 0;
	myIPAddress = Chord::getLocalIPAddress();
}

Chord::~Chord() {
	Close(myListenFD);
}

void Chord::JoinRing(std::string entry_ip, int entry_port) {
	Node entryNode = Node(entry_ip, entry_port);
	if (entryNode.Connect()) {
		// get the successor to connect to
		auto successor = entryNode.SearchSuccessor(myKey);

		cout << "Successor is " << successor->toString();
		auto pred = successor->getPredecessor();
		cout << "Successor's predecessor is " << pred->toString();

		//auto succ = successor->getSuccessor();

		// special case
//		if (pred->getKey() == successor->getKey()) {
//
//		}
//		else {
		cout << "Setting Pred's Succ and Succ's Pred" << endl;
			//pred->setSuccessor(NodeInfo.get());
			//successor->setPredecessor(NodeInfo.get());
		setSuccessor(1, successor);
		setPredecessor(1, pred);
//		}

	}
	else {
		cout << "Could not connect to Node." << endl;
		exit(-1);
	}
}

void Chord::handleRequest(int socket_fd, sockaddr_in sockaddr) {
	cout << "Processing connection." << endl;

	shared_ptr<RIOBuffered> connection(new RIOBuffered(socket_fd));
	char msg[256];
	sprintf(msg, "My ID is %x\n", myKey);
	connection->writeLine(&(Chord::WELCOME_MESSAGE));

	RIO::writep(socket_fd, (void*) msg, strlen(msg));

	string message = connection->readLine();
	stringstream parse(message);

	string command;
	parse >> command;

	if (command.compare("Node") == 0) {
		// Node identification message: Node Key IP:Port
		string message;
		getline(parse, message);

		shared_ptr<Node> node = Node::createFromInfo(message);
		if (node == nullptr) {
			string msg("You are using an invalid key.\n");
			RIO::writeString(socket_fd, &msg);
		}
		else {
			node->processCommunication(connection);
		}
	}
	else if (command.find("Query") == 0) {
		//Node n(socket_fd, "", "");
	}
	else {
		RIO::writeString(socket_fd, &(Chord::ERROR_GOODBYE_MESSAGE));
		cout << "Unknown client connected." << endl;
	}

	shutdown(socket_fd, 0);
	// don't care about double closing
	close(socket_fd);
}

chord_key Chord::hashKey(std::string value) {
	byte output[CryptoPP::SHA1::DIGESTSIZE];
	CryptoPP::SHA1().CalculateDigest(output, (byte*) value.c_str(), value.length());

	chord_key resultKey = 0;
	// 20 byte SHA key
	// turn it into a 4 byte key
	for (unsigned int i = 0; i < sizeof(output); i += 4) {
		chord_key current;
		memcpy(&current, (void*) (output + i), 4);
		resultKey = resultKey ^ current;
	}

	return resultKey;
}

std::shared_ptr<Chord> Chord::getInstance() {
	if (myInstance == nullptr) {
		myInstance = shared_ptr<Chord>(new Chord());
	}
	return myInstance;
}

void Chord::init(int port) {
	myListenPort = port;
	myKey = Chord::hashKey(myIPAddress + ":" + to_string(myListenPort));
	NodeInfo = shared_ptr<Node>(new Node(myIPAddress, myListenPort));
}

std::string Chord::getLocalIPAddress() {
	struct ifaddrs *ifaddr, *ifa;
	int n, s;
	int family;
	char host[NI_MAXHOST];

	if (getifaddrs(&ifaddr) == -1) {
		perror("getifaddrs");
		exit(EXIT_FAILURE);
	}

	for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
		if (ifa->ifa_addr == nullptr) {
			continue;
		}

		family = ifa->ifa_addr->sa_family;
		if (family == AF_INET) {
			if (strcmp(ifa->ifa_name, "lo") != 0) {
				s = getnameinfo(ifa->ifa_addr,
						(family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),
								host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
				if (s != 0) {
					printf("getnameinfo() failed: %s\n", gai_strerror(s));
					exit(EXIT_FAILURE);
				}
			}
		}

	}
	freeifaddrs(ifaddr);
	return string(host);
}

std::string Chord::toString() {
	stringstream result;
	result << hex << myKey << " " << myIPAddress << ":" << dec << myListenPort << endl;
	return result.str();
}

std::shared_ptr<Node> Chord::findSuccessor(chord_key key) {
	if (Successors.size() == 0) {
		// we have no successors - first node in circle?
		return NodeInfo;
	}
	else {
		shared_ptr<Node> n;
		n = findPredecessor(key);
		return n->getSuccessor();
	}

}

std::shared_ptr<Node> Chord::findPredecessor(chord_key key) {
	shared_ptr<Node> n = NodeInfo;
	// TODO: iterate over the finger table instead of the successor
	for (int i = Successors.size() - 1; i >= 0; --i) {
		// key in (current, mySuccessors[i])
		if (Chord::inRange(myKey, key, Successors[i]->getKey(), false)) {
			n = Successors[i];
			return n;
		}
	}
	return n;
}

void Chord::parseIPPort(std::string message, std::string* ip, int* port) {
	*port = std::stoi(message.substr(message.find(":") + 1));
	*ip = message.substr(0, message.find(":"));
}

std::tuple<int, int> Chord::getRange() {
	int lowerKey = myKey;
	if (Predecessors.size() > 0) {
		lowerKey = Predecessors[0]->getKey();
	}
	return tuple<int, int>(lowerKey, myKey);
}

bool Chord::inRange(chord_key lower, chord_key upper, chord_key key, bool inclusiveEnd) {
	// need to check if the key is within the ranger lower -> upper
	// if lower > upper, then it wraps around
	chord_key max = 0 - 1;

	// we don't wrap around the circle
	if (upper > lower) {
		bool part = inclusiveEnd ? key <= upper: key < upper;

		return lower < key && part;
	}
	else {
		if (lower > upper) {
			// lower less than key < max
			bool above = lower < key && key <= max;

			bool part = inclusiveEnd ? key <= upper: key < upper;
			bool wrapped = key >= 0 && part;

			return above || wrapped;
		}
		else {
			// equal each other, therefore, fits in here
			return true;
		}
	}
}

void Chord::setSuccessor(size_t index, std::shared_ptr<Node> node, bool setupOther) {
	// index starts at 1
	if (index >  NUM_SUCCESSORS) {
		// we don't support this
		cerr << "Tried to set Successor " << index << endl;
		return;
	}

	// set our pointer
	Successors.insert(Successors.begin() + index - 1, node);

	// trim the list to max size (don't want to store more)
	if (Successors.size() >= NUM_SUCCESSORS) {
		cerr << "Trimming" << endl;
		Successors.resize(NUM_SUCCESSORS);
	}

	// if we should set up a connection to the other side
	if (setupOther) {
		node->setPredecessor(NodeInfo.get(), index);
	}
}

void Chord::setPredecessor(size_t index, std::shared_ptr<Node> node, bool setupOther) {
	// index starts at 1
	if (index >  NUM_PREDECESSORS) {
		// we don't support this
		cerr << "Tried to set Predecessor " << index << endl;
		return;
	}

	// set our pointer
	Predecessors.insert(Predecessors.begin() + index - 1, node);

	// trim the list to max size (don't want to store more)
	if (Predecessors.size() >= NUM_SUCCESSORS) {
		cerr << "Trimming" << endl;
		Predecessors.resize(NUM_SUCCESSORS);
	}

	// if we should set up a connection to the other side
	if (setupOther) {
		node->setSuccessor(NodeInfo.get(), index);
	}
}
<commit_msg>Fixed join ring, added leavering, replaced silly case for search successor<commit_after>/*
 * Chord.cpp
 *
 *  Created on: Mar 17, 2015
 *      Author: jonno
 */

#include <iostream>
#include <cstdio>
#include <sha.h>
#include <memory>
#include <thread>
#include "Chord.h"
#include "Node.h"
#include "utils/csapp.h"
#include <sstream>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <ifaddrs.h>

using namespace std;

shared_ptr<Chord> Chord::myInstance = nullptr;

void Chord::Listen() {
	assert(myKey != 0);

	myListenFD = Open_listenfd(myListenPort);
	if (myListenFD < 0) {
		exit(-1);
	}

	int optval = 1;
	setsockopt(myListenFD, SOL_SOCKET, SO_REUSEADDR, (const void *) &optval, sizeof(int));

	cout << "Chord listening at " << myIPAddress << ":" << myListenPort << endl;

	socklen_t client_len;
	struct sockaddr_in client_addr;

	int newConnection;

	vector<thread> threads;

	while (true) {
		client_len = sizeof(client_addr);
		newConnection = Accept(myListenFD, (sockaddr*) &client_addr, &client_len);

		cout << "New Connection from " << inet_ntoa(client_addr.sin_addr) << ":" << dec << ntohs(client_addr.sin_port) << endl;

		threads.push_back(
				thread(&Chord::handleRequest, this, newConnection, client_addr)
				);
	}

}

Chord::Chord() {
	myListenPort = 0;
	myListenFD = 0;
	myIPAddress = Chord::getLocalIPAddress();
}

Chord::~Chord() {
	Close(myListenFD);
}

void Chord::JoinRing(std::string entry_ip, int entry_port) {
	Node entryNode = Node(entry_ip, entry_port);
	if (entryNode.Connect()) {
		// get the successor to connect to
		auto successor = entryNode.SearchSuccessor(myKey);

		cout << "Successor is " << successor->toString();
		auto pred = successor->getPredecessor();
		cout << "Successor's predecessor is " << pred->toString();

		cout << "Setting Pred's Succ and Succ's Pred" << endl;

		// this needs to happen all the time
		// works for one in the node, or infinite
		setSuccessor(1, successor);
		setPredecessor(1, pred);

		// if pred != succ, then we can do this
		if (successor->getKey() != pred->getKey()) {
			setSuccessor(2, successor->getSuccessor(1));
			setPredecessor(2, pred->getPredecessor(1));
		}
	}
	else {
		cout << "Could not connect to Node." << endl;
		exit(-1);
	}
}

void Chord::handleRequest(int socket_fd, sockaddr_in sockaddr) {
	cout << "Processing connection." << endl;

	shared_ptr<RIOBuffered> connection(new RIOBuffered(socket_fd));
	char msg[256];
	sprintf(msg, "My ID is %x\n", myKey);
	connection->writeLine(&(Chord::WELCOME_MESSAGE));

	RIO::writep(socket_fd, (void*) msg, strlen(msg));

	string message = connection->readLine();
	stringstream parse(message);

	string command;
	parse >> command;

	if (command.compare("Node") == 0) {
		// Node identification message: Node Key IP:Port
		string message;
		getline(parse, message);

		shared_ptr<Node> node = Node::createFromInfo(message);
		if (node == nullptr) {
			string msg("You are using an invalid key.\n");
			RIO::writeString(socket_fd, &msg);
		}
		else {
			node->processCommunication(connection);
		}
	}
	else if (command.find("Query") == 0) {
		//Node n(socket_fd, "", "");
	}
	else {
		RIO::writeString(socket_fd, &(Chord::ERROR_GOODBYE_MESSAGE));
		cout << "Unknown client connected." << endl;
	}

	shutdown(socket_fd, 0);
	// don't care about double closing
	close(socket_fd);
}

chord_key Chord::hashKey(std::string value) {
	byte output[CryptoPP::SHA1::DIGESTSIZE];
	CryptoPP::SHA1().CalculateDigest(output, (byte*) value.c_str(), value.length());

	chord_key resultKey = 0;
	// 20 byte SHA key
	// turn it into a 4 byte key
	for (unsigned int i = 0; i < sizeof(output); i += 4) {
		chord_key current;
		memcpy(&current, (void*) (output + i), 4);
		resultKey = resultKey ^ current;
	}

	return resultKey;
}

std::shared_ptr<Chord> Chord::getInstance() {
	if (myInstance == nullptr) {
		myInstance = shared_ptr<Chord>(new Chord());
	}
	return myInstance;
}

void Chord::init(int port) {
	myListenPort = port;
	myKey = Chord::hashKey(myIPAddress + ":" + to_string(myListenPort));
	NodeInfo = shared_ptr<Node>(new Node(myIPAddress, myListenPort));

	/// init the succ and pred
	Successors.insert(Successors.begin(), NUM_SUCCESSORS, NodeInfo);
	Predecessors.insert(Predecessors.begin(), NUM_PREDECESSORS, NodeInfo);
}

std::string Chord::getLocalIPAddress() {
	struct ifaddrs *ifaddr, *ifa;
	int n, s;
	int family;
	char host[NI_MAXHOST];

	if (getifaddrs(&ifaddr) == -1) {
		perror("getifaddrs");
		exit(EXIT_FAILURE);
	}

	for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
		if (ifa->ifa_addr == nullptr) {
			continue;
		}

		family = ifa->ifa_addr->sa_family;
		if (family == AF_INET) {
			if (strcmp(ifa->ifa_name, "lo") != 0) {
				s = getnameinfo(ifa->ifa_addr,
						(family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6),
								host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
				if (s != 0) {
					printf("getnameinfo() failed: %s\n", gai_strerror(s));
					exit(EXIT_FAILURE);
				}
			}
		}

	}
	freeifaddrs(ifaddr);
	return string(host);
}

std::string Chord::toString() {
	stringstream result;
	result << hex << myKey << " " << myIPAddress << ":" << dec << myListenPort << endl;
	return result.str();
}

std::shared_ptr<Node> Chord::findSuccessor(chord_key key) {
	shared_ptr<Node> n;
	n = findPredecessor(key);
	return n->getSuccessor();
}

std::shared_ptr<Node> Chord::findPredecessor(chord_key key) {
	shared_ptr<Node> n = NodeInfo;
	// TODO: iterate over the finger table instead of the successor
	for (int i = Successors.size() - 1; i >= 0; --i) {
		// key in (current, mySuccessors[i])
		if (Chord::inRange(myKey, key, Successors[i]->getKey(), false)) {
			n = Successors[i];
			return n;
		}
	}
	return n;
}

void Chord::parseIPPort(std::string message, std::string* ip, int* port) {
	*port = std::stoi(message.substr(message.find(":") + 1));
	*ip = message.substr(0, message.find(":"));
}

std::tuple<int, int> Chord::getRange() {
	int lowerKey = myKey;
	if (Predecessors.size() > 0) {
		lowerKey = Predecessors[0]->getKey();
	}
	return tuple<int, int>(lowerKey, myKey);
}

void Chord::LeaveRing() {
	cout << "Leaving Ring" << endl;

	for (int i = Successors.size() - 1; i >= 0; --i) {
		auto succ = Successors[i];
		auto pred = Predecessors[i];
		// since we're inserting, just insert it at the beginning position.
		succ->setPredecessor(pred.get(), 1);
		pred->setSuccessor(succ.get(), 1);
	}
	// we're down - quit
	exit(0);
}

bool Chord::inRange(chord_key lower, chord_key upper, chord_key key, bool inclusiveEnd) {
	// need to check if the key is within the ranger lower -> upper
	// if lower > upper, then it wraps around
	chord_key max = 0 - 1;

	// we don't wrap around the circle
	if (upper > lower) {
		bool part = inclusiveEnd ? key <= upper: key < upper;

		return lower < key && part;
	}
	else {
		if (lower > upper) {
			// lower less than key < max
			bool above = lower < key && key <= max;

			bool part = inclusiveEnd ? key <= upper: key < upper;
			bool wrapped = key >= 0 && part;

			return above || wrapped;
		}
		else {
			// equal each other, therefore, fits in here
			return true;
		}
	}
}

void Chord::setSuccessor(size_t index, std::shared_ptr<Node> node, bool setupOther) {
	// index starts at 1
	if (index >  NUM_SUCCESSORS) {
		// we don't support this
		cerr << "Tried to set Successor " << index << endl;
		return;
	}

	// set our pointer
	Successors.insert(Successors.begin() + index - 1, node);

	// trim the list to max size (don't want to store more)
	if (Successors.size() >= NUM_SUCCESSORS) {
		cerr << "Trimming" << endl;
		Successors.resize(NUM_SUCCESSORS);
	}

	// if we should set up a connection to the other side
	if (setupOther) {
		node->setPredecessor(NodeInfo.get(), index);
	}
}

void Chord::setPredecessor(size_t index, std::shared_ptr<Node> node, bool setupOther) {
	// index starts at 1
	if (index >  NUM_PREDECESSORS) {
		// we don't support this
		cerr << "Tried to set Predecessor " << index << endl;
		return;
	}

	// set our pointer
	Predecessors.insert(Predecessors.begin() + index - 1, node);

	// trim the list to max size (don't want to store more)
	if (Predecessors.size() >= NUM_SUCCESSORS) {
		cerr << "Trimming" << endl;
		Predecessors.resize(NUM_SUCCESSORS);
	}

	// if we should set up a connection to the other side
	if (setupOther) {
		node->setSuccessor(NodeInfo.get(), index);
	}
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 Damien Tardy-Panis
 *
 * This file is subject to the terms and conditions defined in
 * file 'LICENSE', which is part of this source code package.
 **/

#include "Comic.h"

#include <QDebug>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>

#include "ComicDatabaseResource.h"
#include "ComicFileResource.h"

const int Comic::_minFetchDelay = 1800; // 30 min
const int Comic::_timeout = 20000; // 20 sec

Comic::Comic(QObject *parent) :
    QObject(parent),
    m_currentReply(NULL),
    m_extractedStripUrl(QUrl()),
    m_stripUrl(QUrl()),
    m_favorite(false),
    m_newStrip(false),
    m_error(false),
    m_fetching(false),
    m_fetchTime(QDateTime()),
    m_fetchingProgress(0)
{
    m_networkManager = new QNetworkAccessManager(this);
    dbResource = ComicDatabaseResource::instance();
    fileResource = ComicFileResource::instance();

    m_timeoutTimer = new QTimer(this);
    m_timeoutTimer->setInterval(_timeout);
    m_timeoutTimer->setSingleShot(true);
    connect(m_timeoutTimer, SIGNAL(timeout()), this, SLOT(timeout()));
}

Comic::~Comic()
{
    delete m_currentReply;
    delete m_networkManager;
    delete m_timeoutTimer;
}

void Comic::load()
{
    dbResource->load(this);
}

void Comic::save()
{
    dbResource->save(this);
}

QString Comic::stripPath() const
{
     return fileResource->filePath(id());
}

void Comic::fetchStrip(QUrl stripUrl)
{
    if (!error() &&
        !fetchTime().isNull() &&
        QDateTime::currentDateTime().secsTo(fetchTime()) > -_minFetchDelay &&
        stripImageDownloaded())
        return;

    abortFetching();
    delete m_currentReply;
    m_currentReply = NULL;

    QUrl requestUrl = !stripUrl.isEmpty() ? stripUrl : stripSourceUrl();
    QNetworkRequest request(requestUrl);
    request.setHeader(QNetworkRequest::UserAgentHeader, "sailfishos/tardypad/dailycomics");
    m_currentReply = m_networkManager->get(request);

    m_timeoutTimer->start();
    emit fetchStarted();
    setFetching(true);
    setFetchingProgress(0);

    connect(m_currentReply, SIGNAL(finished()), this, SLOT(onFetchFinished()));
    connect(m_currentReply, SIGNAL(downloadProgress(qint64,qint64)), this, SIGNAL(downloadProgress(qint64,qint64)));
    connect(m_currentReply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(updateFetchingProgress(qint64,qint64)));
}

void Comic::abortFetching()
{
    setFetching(false);

    if (m_currentReply != NULL && m_currentReply->isRunning()) {
        m_currentReply->disconnect(this);
        m_currentReply->abort();
    }
}

void Comic::read()
{
    setNewStrip(false);
    save();
}

bool Comic::stripImageDownloaded()
{
    return fileResource->isDownloaded(id());
}

QUrl Comic::redirectedToUrl()
{
    QVariant redirectAttribute = m_currentReply->attribute(QNetworkRequest::RedirectionTargetAttribute);

    if (redirectAttribute.isValid()) {
        QUrl redirectUrl = redirectAttribute.toUrl();

        if (redirectUrl.isRelative())
            redirectUrl = m_currentReply->url().resolved(redirectUrl);

        return redirectUrl;
    }

    return QUrl();
}

void Comic::onFetchFinished()
{
    m_timeoutTimer->stop();

    if (m_currentReply->error() != QNetworkReply::NoError) {
        setFetching(false);
        setError(true);
        emit networkError();
        return;
    }

    QUrl redirectUrl = redirectedToUrl();

    if (!redirectUrl.isEmpty()) {
        fetchStrip(redirectUrl);
        return;
    }

    parse();
}

void Comic::parse()
{
    QByteArray data = m_currentReply->readAll();
    QUrl extractedStripUrl = extractStripUrl(data);

    if (!extractedStripUrl.isValid()) {
        setFetching(false);
        setError(true);
        emit parsingError();
        return;
    }

    setExtractedStripUrl(extractedStripUrl);

    if (extractedStripUrl != stripUrl()) {
        fetchStripImage(extractedStripUrl);
        setNewStrip(true);
    } else if (!stripImageDownloaded()) {
        fetchStripImage(extractedStripUrl);
    } else {
        setFetching(false);
        emit fetchFinished();
        setStripUrl(extractedStripUrl);
        setFetchTime(QDateTime::currentDateTime());
        save();
    }
}

void Comic::fetchStripImage(QUrl stripImageUrl)
{
    delete m_currentReply;
    m_currentReply = NULL;

    QNetworkRequest request(stripImageUrl);
    m_currentReply = m_networkManager->get(request);

    m_timeoutTimer->start();

    connect(m_currentReply, SIGNAL(finished()), this, SLOT(onFetchImageFinished()));
    connect(m_currentReply, SIGNAL(downloadProgress(qint64,qint64)), this, SIGNAL(downloadProgress(qint64,qint64)));
    connect(m_currentReply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(updateFetchingProgress(qint64,qint64)));
}

void Comic::onFetchImageFinished()
{
    m_timeoutTimer->stop();

    if (m_currentReply->error() != QNetworkReply::NoError) {
        setFetching(false);
        setError(true);
        emit networkError();
        return;
    }

    QUrl redirectUrl = redirectedToUrl();

    if (!redirectUrl.isEmpty()) {
        fetchStripImage(redirectUrl);
        return;
    }

    bool result = fileResource->save(id(), m_currentReply->readAll());

    if (!result) {
        setFetching(false);
        setError(true);
        emit savingError();
        return;
    }

    setFetching(false);
    setError(false);
    emit fetchFinished();
    setStripUrl(extractedStripUrl());
    setFetchTime(QDateTime::currentDateTime());
    save();
}

void Comic::timeout()
{
    abortFetching();
    setError(true);
    emit networkError();
}

void Comic::updateFetchingProgress(qint64 bytesReceived, qint64 bytesTotal)
{
    if (bytesTotal != 0)
        setFetchingProgress(bytesReceived / bytesTotal);
    else
        setFetchingProgress(0);
}
<commit_msg>fix comic status in case of same strip successful fetch after error<commit_after>/**
 * Copyright (c) 2015 Damien Tardy-Panis
 *
 * This file is subject to the terms and conditions defined in
 * file 'LICENSE', which is part of this source code package.
 **/

#include "Comic.h"

#include <QDebug>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>

#include "ComicDatabaseResource.h"
#include "ComicFileResource.h"

const int Comic::_minFetchDelay = 1800; // 30 min
const int Comic::_timeout = 20000; // 20 sec

Comic::Comic(QObject *parent) :
    QObject(parent),
    m_currentReply(NULL),
    m_extractedStripUrl(QUrl()),
    m_stripUrl(QUrl()),
    m_favorite(false),
    m_newStrip(false),
    m_error(false),
    m_fetching(false),
    m_fetchTime(QDateTime()),
    m_fetchingProgress(0)
{
    m_networkManager = new QNetworkAccessManager(this);
    dbResource = ComicDatabaseResource::instance();
    fileResource = ComicFileResource::instance();

    m_timeoutTimer = new QTimer(this);
    m_timeoutTimer->setInterval(_timeout);
    m_timeoutTimer->setSingleShot(true);
    connect(m_timeoutTimer, SIGNAL(timeout()), this, SLOT(timeout()));
}

Comic::~Comic()
{
    delete m_currentReply;
    delete m_networkManager;
    delete m_timeoutTimer;
}

void Comic::load()
{
    dbResource->load(this);
}

void Comic::save()
{
    dbResource->save(this);
}

QString Comic::stripPath() const
{
     return fileResource->filePath(id());
}

void Comic::fetchStrip(QUrl stripUrl)
{
    if (!error() &&
        !fetchTime().isNull() &&
        QDateTime::currentDateTime().secsTo(fetchTime()) > -_minFetchDelay &&
        stripImageDownloaded())
        return;

    abortFetching();
    delete m_currentReply;
    m_currentReply = NULL;

    QUrl requestUrl = !stripUrl.isEmpty() ? stripUrl : stripSourceUrl();
    QNetworkRequest request(requestUrl);
    request.setHeader(QNetworkRequest::UserAgentHeader, "sailfishos/tardypad/dailycomics");
    m_currentReply = m_networkManager->get(request);

    m_timeoutTimer->start();
    emit fetchStarted();
    setFetching(true);
    setFetchingProgress(0);

    connect(m_currentReply, SIGNAL(finished()), this, SLOT(onFetchFinished()));
    connect(m_currentReply, SIGNAL(downloadProgress(qint64,qint64)), this, SIGNAL(downloadProgress(qint64,qint64)));
    connect(m_currentReply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(updateFetchingProgress(qint64,qint64)));
}

void Comic::abortFetching()
{
    setFetching(false);

    if (m_currentReply != NULL && m_currentReply->isRunning()) {
        m_currentReply->disconnect(this);
        m_currentReply->abort();
    }
}

void Comic::read()
{
    setNewStrip(false);
    save();
}

bool Comic::stripImageDownloaded()
{
    return fileResource->isDownloaded(id());
}

QUrl Comic::redirectedToUrl()
{
    QVariant redirectAttribute = m_currentReply->attribute(QNetworkRequest::RedirectionTargetAttribute);

    if (redirectAttribute.isValid()) {
        QUrl redirectUrl = redirectAttribute.toUrl();

        if (redirectUrl.isRelative())
            redirectUrl = m_currentReply->url().resolved(redirectUrl);

        return redirectUrl;
    }

    return QUrl();
}

void Comic::onFetchFinished()
{
    m_timeoutTimer->stop();

    if (m_currentReply->error() != QNetworkReply::NoError) {
        setFetching(false);
        setError(true);
        emit networkError();
        return;
    }

    QUrl redirectUrl = redirectedToUrl();

    if (!redirectUrl.isEmpty()) {
        fetchStrip(redirectUrl);
        return;
    }

    parse();
}

void Comic::parse()
{
    QByteArray data = m_currentReply->readAll();
    QUrl extractedStripUrl = extractStripUrl(data);

    if (!extractedStripUrl.isValid()) {
        setFetching(false);
        setError(true);
        emit parsingError();
        return;
    }

    setExtractedStripUrl(extractedStripUrl);

    if (extractedStripUrl != stripUrl()) {
        fetchStripImage(extractedStripUrl);
        setNewStrip(true);
    } else if (!stripImageDownloaded()) {
        fetchStripImage(extractedStripUrl);
    } else {
        setFetching(false);
        setError(false);
        emit fetchFinished();
        setStripUrl(extractedStripUrl);
        setFetchTime(QDateTime::currentDateTime());
        save();
    }
}

void Comic::fetchStripImage(QUrl stripImageUrl)
{
    delete m_currentReply;
    m_currentReply = NULL;

    QNetworkRequest request(stripImageUrl);
    m_currentReply = m_networkManager->get(request);

    m_timeoutTimer->start();

    connect(m_currentReply, SIGNAL(finished()), this, SLOT(onFetchImageFinished()));
    connect(m_currentReply, SIGNAL(downloadProgress(qint64,qint64)), this, SIGNAL(downloadProgress(qint64,qint64)));
    connect(m_currentReply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(updateFetchingProgress(qint64,qint64)));
}

void Comic::onFetchImageFinished()
{
    m_timeoutTimer->stop();

    if (m_currentReply->error() != QNetworkReply::NoError) {
        setFetching(false);
        setError(true);
        emit networkError();
        return;
    }

    QUrl redirectUrl = redirectedToUrl();

    if (!redirectUrl.isEmpty()) {
        fetchStripImage(redirectUrl);
        return;
    }

    bool result = fileResource->save(id(), m_currentReply->readAll());

    if (!result) {
        setFetching(false);
        setError(true);
        emit savingError();
        return;
    }

    setFetching(false);
    setError(false);
    emit fetchFinished();
    setStripUrl(extractedStripUrl());
    setFetchTime(QDateTime::currentDateTime());
    save();
}

void Comic::timeout()
{
    abortFetching();
    setError(true);
    emit networkError();
}

void Comic::updateFetchingProgress(qint64 bytesReceived, qint64 bytesTotal)
{
    if (bytesTotal != 0)
        setFetchingProgress(bytesReceived / bytesTotal);
    else
        setFetchingProgress(0);
}
<|endoftext|>


<commit_before>//
// Created by coryan on 10/23/18.
//

#include "google/cloud/testing_util/capture_log_lines_backend.h"

namespace google {
namespace cloud {
inline namespace GOOGLE_CLOUD_CPP_NS {
namespace testing_util {

void CaptureLogLinesBackend::Process(LogRecord const& lr) {
  // Break the records in lines, it is easier to analyze them as such.
  std::istringstream is(lr.message);
  std::string line;
  while (std::getline(is, line)) {
    log_lines.emplace_back(line);
  }
}

void CaptureLogLinesBackend::ProcessWithOwnership(LogRecord lr) { Process(lr); }

}  // namespace testing_util
}  // namespace GOOGLE_CLOUD_CPP_NS
}  // namespace cloud
}  // namespace google
<commit_msg>chore: fix copyright boilerplate (#3078)<commit_after>// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "google/cloud/testing_util/capture_log_lines_backend.h"

namespace google {
namespace cloud {
inline namespace GOOGLE_CLOUD_CPP_NS {
namespace testing_util {

void CaptureLogLinesBackend::Process(LogRecord const& lr) {
  // Break the records in lines, it is easier to analyze them as such.
  std::istringstream is(lr.message);
  std::string line;
  while (std::getline(is, line)) {
    log_lines.emplace_back(line);
  }
}

void CaptureLogLinesBackend::ProcessWithOwnership(LogRecord lr) { Process(lr); }

}  // namespace testing_util
}  // namespace GOOGLE_CLOUD_CPP_NS
}  // namespace cloud
}  // namespace google
<|endoftext|>


<commit_before>// Copyright (c) 2021 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
#ifndef IOX_HOOFS_CXX_ATTRIBUTES_HPP
#define IOX_HOOFS_CXX_ATTRIBUTES_HPP

namespace iox
{
namespace cxx
{
/// @brief if a function has a return value which you do not want to use then you can wrap the function with that macro.
/// Purpose is to suppress the unused compiler warning by adding an attribute to the return value
/// @param[in] expr name of the function where the return value is not used.
/// @code
///     uint32_t foo();
///     IOX_DISCARD_RESULT(foo()); // suppress compiler warning for unused return value
/// @endcode
// NOLINTNEXTLINE
#define IOX_DISCARD_RESULT(expr) static_cast<void>(expr)

/// @brief IOX_NO_DISCARD adds the [[nodiscard]] keyword if it is available for the current compiler.
///        If additionally the keyword [[gnu::warn_unused]] is present it will be added as well.
/// @note
//    [[nodiscard]], [[gnu::warn_unused]] supported since gcc 4.8 (https://gcc.gnu.org/projects/cxx-status.html)
///   [[nodiscard]], [[gnu::warn_unused]] supported since clang 3.9 (https://clang.llvm.org/cxx_status.html)
///   activate keywords for gcc>=5 or clang>=4
#if defined(_WIN32)
// On WIN32 we are using C++17 which makes the keyword [[nodiscard]] available
// NOLINTNEXTLINE
#define IOX_NO_DISCARD [[nodiscard]]
#elif defined(__APPLE__) && defined(__clang__)
// On APPLE we are using C++17 which makes the keyword [[nodiscard]] available
// NOLINTNEXTLINE
#define IOX_NO_DISCARD [[nodiscard, gnu::warn_unused]]
#elif (defined(__clang__) && __clang_major__ >= 4)
// NOLINTNEXTLINE
#define IOX_NO_DISCARD [[gnu::warn_unused]]
#elif (defined(__GNUC__) && __GNUC__ >= 5)
// NOLINTNEXTLINE
#define IOX_NO_DISCARD [[nodiscard, gnu::warn_unused]]
#else
// on an unknown platform we use for now nothing since we do not know what is supported there
#define IOX_NO_DISCARD
#endif

/// @brief IOX_FALLTHROUGH adds the [[fallthrough]] keyword when it is available for the current compiler.
/// @note
//    [[fallthrough]] supported since gcc 7 (https://gcc.gnu.org/projects/cxx-status.html)
///   [[fallthrough]] supported since clang 3.9 (https://clang.llvm.org/cxx_status.html)
///   activate keywords for gcc>=7 or clang>=4
#if defined(_WIN32)
// On WIN32 we are using C++17 which makes the keyword [[fallthrough]] available
// NOLINTNEXTLINE
#define IOX_FALLTHROUGH [[fallthrough]]
#elif defined(__APPLE__) && defined(__clang__)
// On APPLE we are using C++17 which makes the keyword [[fallthrough]] available
// NOLINTNEXTLINE
#define IOX_FALLTHROUGH [[fallthrough]]
// with C++17 fallthrough was introduced and we can use it
#elif __cplusplus >= 201703L
// clang prints a warning therefore we exclude it here
// NOLINTNEXTLINE
#define IOX_FALLTHROUGH [[fallthrough]]
#else
// on an unknown platform we use for now nothing since we do not know what is supported there
#define IOX_FALLTHROUGH
#endif

/// @brief IOX_MAYBE_UNUSED adds the [[gnu::unused]] or [[maybe_unused]] attribute when it is available for the current
/// compiler.
/// @note
///   activate attribute for gcc or clang
#if defined(__GNUC__) || defined(__clang__)
// NOLINTNEXTLINE
#define IOX_MAYBE_UNUSED [[gnu::unused]]
#elif defined(_WIN32)
// On WIN32 we are using C++17 which makes the attribute [[maybe_unused]] available
// NOLINTNEXTLINE
#define IOX_MAYBE_UNUSED [[maybe_unused]]
// on an unknown platform we use for now nothing since we do not know what is supported there
#else
#define IOX_MAYBE_UNUSED
#endif

} // namespace cxx
} // namespace iox

#endif
<commit_msg>iox-#751 Using [[gnu::fallthrough]] attribute when not on C++17>= and on gcc-7><commit_after>// Copyright (c) 2021 by Apex.AI Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
#ifndef IOX_HOOFS_CXX_ATTRIBUTES_HPP
#define IOX_HOOFS_CXX_ATTRIBUTES_HPP

namespace iox
{
namespace cxx
{
/// @brief if a function has a return value which you do not want to use then you can wrap the function with that macro.
/// Purpose is to suppress the unused compiler warning by adding an attribute to the return value
/// @param[in] expr name of the function where the return value is not used.
/// @code
///     uint32_t foo();
///     IOX_DISCARD_RESULT(foo()); // suppress compiler warning for unused return value
/// @endcode
// NOLINTNEXTLINE
#define IOX_DISCARD_RESULT(expr) static_cast<void>(expr)

/// @brief IOX_NO_DISCARD adds the [[nodiscard]] keyword if it is available for the current compiler.
///        If additionally the keyword [[gnu::warn_unused]] is present it will be added as well.
/// @note
//    [[nodiscard]], [[gnu::warn_unused]] supported since gcc 4.8 (https://gcc.gnu.org/projects/cxx-status.html)
///   [[nodiscard]], [[gnu::warn_unused]] supported since clang 3.9 (https://clang.llvm.org/cxx_status.html)
///   activate keywords for gcc>=5 or clang>=4
#if defined(_WIN32)
// On WIN32 we are using C++17 which makes the keyword [[nodiscard]] available
// NOLINTNEXTLINE
#define IOX_NO_DISCARD [[nodiscard]]
#elif defined(__APPLE__) && defined(__clang__)
// On APPLE we are using C++17 which makes the keyword [[nodiscard]] available
// NOLINTNEXTLINE
#define IOX_NO_DISCARD [[nodiscard, gnu::warn_unused]]
#elif (defined(__clang__) && __clang_major__ >= 4)
// NOLINTNEXTLINE
#define IOX_NO_DISCARD [[gnu::warn_unused]]
#elif (defined(__GNUC__) && __GNUC__ >= 5)
// NOLINTNEXTLINE
#define IOX_NO_DISCARD [[nodiscard, gnu::warn_unused]]
#else
// on an unknown platform we use for now nothing since we do not know what is supported there
#define IOX_NO_DISCARD
#endif

/// @brief IOX_FALLTHROUGH adds the [[fallthrough]] keyword when it is available for the current compiler.
/// @note
//    [[fallthrough]] supported since gcc 7 (https://gcc.gnu.org/projects/cxx-status.html)
///   [[fallthrough]] supported since clang 3.9 (https://clang.llvm.org/cxx_status.html)
///   activate keywords for gcc>=7 or clang>=4
#if defined(_WIN32)
// On WIN32 we are using C++17 which makes the keyword [[fallthrough]] available
// NOLINTNEXTLINE
#define IOX_FALLTHROUGH [[fallthrough]]
#elif defined(__APPLE__) && defined(__clang__)
// On APPLE we are using C++17 which makes the keyword [[fallthrough]] available
// NOLINTNEXTLINE
#define IOX_FALLTHROUGH [[fallthrough]]
// with C++17 fallthrough was introduced and we can use it
#elif __cplusplus >= 201703L
// clang prints a warning therefore we exclude it here
// NOLINTNEXTLINE
#define IOX_FALLTHROUGH [[fallthrough]]
#elif (defined(__GNUC__) && __GNUC__ >= 7) && !defined(__clang__)
#define IOX_FALLTHROUGH [[gnu::fallthrough]]
#else
// on an unknown platform we use for now nothing since we do not know what is supported there
#define IOX_FALLTHROUGH
#endif

/// @brief IOX_MAYBE_UNUSED adds the [[gnu::unused]] or [[maybe_unused]] attribute when it is available for the current
/// compiler.
/// @note
///   activate attribute for gcc or clang
#if defined(__GNUC__) || defined(__clang__)
// NOLINTNEXTLINE
#define IOX_MAYBE_UNUSED [[gnu::unused]]
#elif defined(_WIN32)
// On WIN32 we are using C++17 which makes the attribute [[maybe_unused]] available
// NOLINTNEXTLINE
#define IOX_MAYBE_UNUSED [[maybe_unused]]
// on an unknown platform we use for now nothing since we do not know what is supported there
#else
#define IOX_MAYBE_UNUSED
#endif

} // namespace cxx
} // namespace iox

#endif
<|endoftext|>


<commit_before>#include <map>
#include <vector>
#include <string>
#include <time.h>
#include "NFComm/NFPluginModule/NFIPluginManager.h"
#include "NFComm/NFCore/NFQueue.h"
#include "NFComm/RapidXML/rapidxml.hpp"
#include "NFComm/RapidXML/rapidxml_iterators.hpp"
#include "NFComm/RapidXML/rapidxml_print.hpp"
#include "NFComm/RapidXML/rapidxml_utils.hpp"
#include "NFComm/NFPluginModule/NFPlatform.h"

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>

#if NF_PLATFORM == NF_PLATFORM_WIN
#include <io.h>
#include <windows.h>
#include <conio.h>
#else
#include <iconv.h>
#include <unistd.h>
#include <cstdio>
#include <dirent.h>
#include <sys/stat.h>
#endif

bool GetPluginNameList(std::string& strPlugin, std::vector<std::string>& pluginList, std::string& configPath)
{
	rapidxml::file<> fdoc(strPlugin.c_str());
	rapidxml::xml_document<>  doc;
	doc.parse<0>(fdoc.data());

	rapidxml::xml_node<>* pRoot = doc.first_node();
	for (rapidxml::xml_node<>* pPluginNode = pRoot->first_node("Plugin"); pPluginNode; pPluginNode = pPluginNode->next_sibling("Plugin"))
	{
		const char* strPluginName = pPluginNode->first_attribute("Name")->value();
		const char* strMain = pPluginNode->first_attribute("Main")->value();
		pluginList.push_back(std::string(strPluginName));
	}

	rapidxml::xml_node<>* pPluginAppNode = pRoot->first_node("APPID");
	if (!pPluginAppNode)
	{
		//NFASSERT(0, "There are no App ID", __FILE__, __FUNCTION__);
		return false;
	}

	const char* strAppID = pPluginAppNode->first_attribute("Name")->value();
	if (!strAppID)
	{
		//NFASSERT(0, "There are no App ID", __FILE__, __FUNCTION__);
		return false;
	}

	rapidxml::xml_node<>* pPluginConfigPathNode = pRoot->first_node("ConfigPath");
	if (!pPluginConfigPathNode)
	{
		//NFASSERT(0, "There are no ConfigPath", __FILE__, __FUNCTION__);
		return false;
	}

	if (NULL == pPluginConfigPathNode->first_attribute("Name"))
	{
		//NFASSERT(0, "There are no ConfigPath.Name", __FILE__, __FUNCTION__);
		return false;
	}

	configPath = pPluginConfigPathNode->first_attribute("Name")->value();

	return true;
}

int CopyFile(std::string& SourceFile, std::string& NewFile)
{
	ifstream in;
	ofstream out;
	in.open(SourceFile.c_str(), ios::binary);//Դļ
	if (in.fail())//Դļʧ
	{
		cout << "Error 1: Fail to open the source file." << endl;
		in.close();
		out.close();
		return 0;
	}
	out.open(NewFile.c_str(), ios::binary);//Ŀļ
	if (out.fail())//ļʧ
	{
		cout << "Error 2: Fail to create the new file." << endl;
		out.close();
		in.close();
		return 0;
	}
	else//ļ
	{
		out << in.rdbuf();
		out.close();
		in.close();
		return 1;
	}
}

std::vector<std::string> GetFileListInFolder(std::string folderPath, int depth)
{
	std::vector<std::string> result;
#if NF_PLATFORM == NF_PLATFORM_WIN
	_finddata_t FileInfo;
	std::string strfind = folderPath + "\\*";
	long long Handle = _findfirst(strfind.c_str(), &FileInfo);

	if (Handle == -1L)
	{
		std::cerr << "can not match the folder path" << std::endl;
		exit(-1);
	}
	do {
		//жǷĿ¼
		if (FileInfo.attrib & _A_SUBDIR)
		{
			//Ҫ
			if ((strcmp(FileInfo.name, ".") != 0) && (strcmp(FileInfo.name, "..") != 0))
			{
				std::string newPath = folderPath + "\\" + FileInfo.name;
				//dfsFolder(newPath, depth);
				auto newResult = GetFileListInFolder(newPath, depth);
				result.insert(result.begin(), newResult.begin(), newResult.end());
			}
		}
		else
		{

			std::string filename = (folderPath + "\\" + FileInfo.name);
			result.push_back(filename);
		}
	} while (_findnext(Handle, &FileInfo) == 0);

	_findclose(Handle);
#else
	DIR *dp;
	struct dirent *entry;
	struct stat statbuf;
	char absolutepath[512];

	if ((dp = opendir(folderPath.c_str())) == NULL) {
		fprintf(stderr, "Can`t open directory %s\n", folderPath.c_str());
		return result;
	}

	while ((entry = readdir(dp)) != NULL) {
		std::string strEntry = folderPath + entry->d_name;
		lstat(strEntry.c_str(), &statbuf);
		if (S_ISDIR(statbuf.st_mode)) {
			if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
			{
				continue;
			}
			std::string childpath = folderPath + "/" + entry->d_name;
			auto newResult = GetFileListInFolder(childpath, depth);
			result.insert(result.begin(), newResult.begin(), newResult.end());
		}
		else
		{
			sprintf(absolutepath, "%s/%s", folderPath.c_str(), entry->d_name);
			result.push_back(absolutepath);
		}
	}
	closedir(dp);
	sort(result.begin(), result.end());//
#endif
	return result;
}

void StringReplace(std::string &strBig, const std::string &strsrc, const std::string &strdst)
{
	std::string::size_type pos = 0;
	std::string::size_type srclen = strsrc.size();
	std::string::size_type dstlen = strdst.size();

	while ((pos = strBig.find(strsrc, pos)) != std::string::npos)
	{
		strBig.replace(pos, srclen, strdst);
		pos += dstlen;
	}
}

void printResult(int result, std::string& strName)
{
	if (result == 1)
	{
		printf("Copy file: %s success!\n", strName.c_str());
	}
	else
	{
		printf("Copy file: %s failed!\n", strName.c_str());
	}
}

int main()
{
	std::vector<std::string> fileList;
	std::vector<std::string> errorFileList;
#if NF_PLATFORM == NF_PLATFORM_WIN
	printf("NFAutoCopyDll is Running in Windows...\n");
#else
	printf("NFAutoCopyDll is Running in Linux...\n");
#endif
#ifdef NF_DEBUG_MODE
	fileList = GetFileListInFolder("../../Debug", 10);
	printf("Debug Mode, And fileList size == %d\n", fileList.size());
#else
	fileList = GetFileListInFolder("../../Release", 10);
	printf("Release Mode, And fileList size == %d\n", fileList.size());
#endif

	for (auto fileName : fileList)
	{
		if (fileName.find("Plugin.xml") != std::string::npos)
		{
			StringReplace(fileName, "\\", "/");
			StringReplace(fileName, "//", "/");
			printf("Reading xml file: %s\n", fileName.c_str());

			std::vector<std::string> pluginList;
			std::string configPath = "";
			GetPluginNameList(fileName, pluginList, configPath);
			if (pluginList.size() > 0 && configPath != "")
			{
#if NF_PLATFORM == NF_PLATFORM_WIN
				pluginList.push_back("libprotobuf");
				pluginList.push_back("NFMessageDefine");
#else
				pluginList.push_back("NFMessageDefine");
#endif
				pluginList.push_back("NFPluginLoader");
				configPath = "../" + configPath;
				configPath = fileName.substr(0, fileName.find_last_of("/")) + "/" + configPath;

				for (std::string name : pluginList)
				{
					int result = 0;
					if (name == "NFPluginLoader")
					{
						int result = 0;
#if NF_PLATFORM == NF_PLATFORM_WIN

#ifdef NF_DEBUG_MODE
						std::string src = configPath + "Comm/Debug/" + name;
						std::string des = fileName.substr(0, fileName.find_last_of("/")) + "/" + name;
						auto strSrc = src + "_d.exe";
						auto strDes = des + "_d.exe";
						auto strSrcPDB = src + "_d.pdb";
						auto strDesPDB = des + "_d.pdb";

						result = CopyFile(strSrc, strDes);
						if (result != 1)
						{
							errorFileList.push_back(strSrc);
						}
						printResult(result, strSrc);

						result = CopyFile(strSrcPDB, strDesPDB);
						if (result != 1)
						{
							errorFileList.push_back(strSrcPDB);
						}
						printResult(result, strSrcPDB);
#else
						std::string src = configPath + "Comm/Release/" + name;
						std::string des = fileName.substr(0, fileName.find_last_of("/")) + "/" + name;
						auto strSrc = src + ".exe";
						auto strDes = des + ".exe";
						int result = 0;
						result = CopyFile(strSrc, strDes);
						if (result != 1)
						{
							errorFileList.push_back(strSrc);
						}
						printResult(result, strSrc);
#endif

#else
						std::string src = configPath + "Comm/" + name;
						std::string des = fileName.substr(0, fileName.find_last_of("/")) + "/" + name;
						auto strSrc = src + "_d";
						auto strDes = des + "_d";
						result = CopyFile(strSrc, strDes);
						if (result != 1)
						{
							errorFileList.push_back(strSrc);
						}
						printResult(result, strSrc);
#endif

					}
					else
					{
#if NF_PLATFORM == NF_PLATFORM_WIN

#ifdef NF_DEBUG_MODE
						std::string src = configPath + "Comm/Debug/" + name;
						std::string des = fileName.substr(0, fileName.find_last_of("/")) + "/" + name;
						auto strSrc = src + "_d.dll";
						auto strDes = des + "_d.dll";
						auto strSrcPDB = src + "_d.pdb";
						auto strDesPDB = des + "_d.pdb";
						result = CopyFile(strSrc, strDes);
						if (result != 1)
						{
							errorFileList.push_back(strSrc);
						}
						printResult(result, strSrc);

						result = CopyFile(strSrcPDB, strDesPDB);
						if (result != 1)
						{
							errorFileList.push_back(strSrcPDB);
						}
						printResult(result, strSrcPDB);
#else
						std::string src = configPath + "Comm/Release/" + name;
						std::string des = fileName.substr(0, fileName.find_last_of("/")) + "/" + name;
						auto strSrc = src + ".dll";
						auto strDes = des + ".dll";
						result = CopyFile(strSrc, strDes);
						if (result != 1)
						{
							errorFileList.push_back(strSrc);
						}
						printResult(result, strSrc);
#endif

#else
						std::string src = configPath + "Comm/lib" + name;
						std::string des = fileName.substr(0, fileName.find_last_of("/")) + "/" + name;
						auto strSrc = src + "_d.so";
						auto strDes = des + "_d.so";
						result = CopyFile(strSrc, strDes);
						if (result != 1)
						{
							errorFileList.push_back(strSrc);
						}
						printResult(result, strSrc);
#endif
					}
				}
			}
		}
	}
	if (errorFileList.size() <= 0)
	{
		printf("File Copy Done with NO ERROR!\n");
	}
	else
	{
		printf("File Copy Done with %d errors as follow:\n", (int)errorFileList.size());
		for (auto errFile : errorFileList)
		{
			printf("\t%s\n", errFile.c_str());
		}
	}
#if NF_PLATFORM == NF_PLATFORM_WIN
	printf("Press any Key to Exit!");
	getch();
#else

#endif
	return 0;
}<commit_msg>fixed NFAutoCopyDll<commit_after>#include <map>
#include <vector>
#include <string>
#include <time.h>
#include "NFComm/NFPluginModule/NFIPluginManager.h"
#include "NFComm/NFCore/NFQueue.h"
#include "NFComm/RapidXML/rapidxml.hpp"
#include "NFComm/RapidXML/rapidxml_iterators.hpp"
#include "NFComm/RapidXML/rapidxml_print.hpp"
#include "NFComm/RapidXML/rapidxml_utils.hpp"
#include "NFComm/NFPluginModule/NFPlatform.h"

#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <errno.h>

#if NF_PLATFORM == NF_PLATFORM_WIN
#include <io.h>
#include <windows.h>
#include <conio.h>
#else
#include <iconv.h>
#include <unistd.h>
#include <cstdio>
#include <dirent.h>
#include <sys/stat.h>
#endif

bool GetPluginNameList(std::string& strPlugin, std::vector<std::string>& pluginList, std::string& configPath)
{
	rapidxml::file<> fdoc(strPlugin.c_str());
	rapidxml::xml_document<>  doc;
	doc.parse<0>(fdoc.data());

	rapidxml::xml_node<>* pRoot = doc.first_node();
	for (rapidxml::xml_node<>* pPluginNode = pRoot->first_node("Plugin"); pPluginNode; pPluginNode = pPluginNode->next_sibling("Plugin"))
	{
		const char* strPluginName = pPluginNode->first_attribute("Name")->value();
		const char* strMain = pPluginNode->first_attribute("Main")->value();
		pluginList.push_back(std::string(strPluginName));
	}

	rapidxml::xml_node<>* pPluginAppNode = pRoot->first_node("APPID");
	if (!pPluginAppNode)
	{
		//NFASSERT(0, "There are no App ID", __FILE__, __FUNCTION__);
		return false;
	}

	const char* strAppID = pPluginAppNode->first_attribute("Name")->value();
	if (!strAppID)
	{
		//NFASSERT(0, "There are no App ID", __FILE__, __FUNCTION__);
		return false;
	}

	rapidxml::xml_node<>* pPluginConfigPathNode = pRoot->first_node("ConfigPath");
	if (!pPluginConfigPathNode)
	{
		//NFASSERT(0, "There are no ConfigPath", __FILE__, __FUNCTION__);
		return false;
	}

	if (NULL == pPluginConfigPathNode->first_attribute("Name"))
	{
		//NFASSERT(0, "There are no ConfigPath.Name", __FILE__, __FUNCTION__);
		return false;
	}

	configPath = pPluginConfigPathNode->first_attribute("Name")->value();

	return true;
}

int CopyFile(std::string& SourceFile, std::string& NewFile)
{
	ifstream in;
	ofstream out;
	in.open(SourceFile.c_str(), ios::binary);//Դļ
	if (in.fail())//Դļʧ
	{
		cout << "Error 1: Fail to open the source file." << endl;
		in.close();
		out.close();
		return 0;
	}
	out.open(NewFile.c_str(), ios::binary);//Ŀļ
	if (out.fail())//ļʧ
	{
		cout << "Error 2: Fail to create the new file." << endl;
		out.close();
		in.close();
		return 0;
	}
	else//ļ
	{
		out << in.rdbuf();
		out.close();
		in.close();
		return 1;
	}
}

std::vector<std::string> GetFileListInFolder(std::string folderPath, int depth)
{
	std::vector<std::string> result;
#if NF_PLATFORM == NF_PLATFORM_WIN
	_finddata_t FileInfo;
	std::string strfind = folderPath + "\\*";
	long long Handle = _findfirst(strfind.c_str(), &FileInfo);

	if (Handle == -1L)
	{
		std::cerr << "can not match the folder path" << std::endl;
		exit(-1);
	}
	do {
		//жǷĿ¼
		if (FileInfo.attrib & _A_SUBDIR)
		{
			//Ҫ
			if ((strcmp(FileInfo.name, ".") != 0) && (strcmp(FileInfo.name, "..") != 0))
			{
				std::string newPath = folderPath + "\\" + FileInfo.name;
				//dfsFolder(newPath, depth);
				auto newResult = GetFileListInFolder(newPath, depth);
				result.insert(result.begin(), newResult.begin(), newResult.end());
			}
		}
		else
		{

			std::string filename = (folderPath + "\\" + FileInfo.name);
			result.push_back(filename);
		}
	} while (_findnext(Handle, &FileInfo) == 0);

	_findclose(Handle);
#else
	DIR *dp;
	struct dirent *entry;
	struct stat statbuf;
	char absolutepath[512];

	if ((dp = opendir(folderPath.c_str())) == NULL) {
		fprintf(stderr, "Can`t open directory %s\n", folderPath.c_str());
		return result;
	}

	while ((entry = readdir(dp)) != NULL) {
		std::string strEntry = folderPath + entry->d_name;
		lstat(strEntry.c_str(), &statbuf);
		if (S_ISDIR(statbuf.st_mode)) {
			if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
			{
				continue;
			}
			std::string childpath = folderPath + "/" + entry->d_name;
			auto newResult = GetFileListInFolder(childpath, depth);
			result.insert(result.begin(), newResult.begin(), newResult.end());
		}
		else
		{
			sprintf(absolutepath, "%s/%s", folderPath.c_str(), entry->d_name);
			result.push_back(absolutepath);
		}
	}
	closedir(dp);
	sort(result.begin(), result.end());//
#endif
	return result;
}

void StringReplace(std::string &strBig, const std::string &strsrc, const std::string &strdst)
{
	std::string::size_type pos = 0;
	std::string::size_type srclen = strsrc.size();
	std::string::size_type dstlen = strdst.size();

	while ((pos = strBig.find(strsrc, pos)) != std::string::npos)
	{
		strBig.replace(pos, srclen, strdst);
		pos += dstlen;
	}
}

void printResult(int result, std::string& strName)
{
	if (result == 1)
	{
		printf("Copy file: %s success!\n", strName.c_str());
	}
	else
	{
		printf("Copy file: %s failed!\n", strName.c_str());
	}
}

int main()
{
	std::vector<std::string> fileList;
	std::vector<std::string> errorFileList;
#if NF_PLATFORM == NF_PLATFORM_WIN
	printf("NFAutoCopyDll is Running in Windows...\n");
#else
	printf("NFAutoCopyDll is Running in Linux...\n");
#endif
#ifdef NF_DEBUG_MODE
	fileList = GetFileListInFolder("../../Debug/", 10);
	printf("Debug Mode, And fileList size == %d\n", fileList.size());
#else
	fileList = GetFileListInFolder("../../Release/", 10);
	printf("Release Mode, And fileList size == %d\n", fileList.size());
#endif

	for (auto fileName : fileList)
	{
		if (fileName.find("Plugin.xml") != std::string::npos)
		{
			StringReplace(fileName, "\\", "/");
			StringReplace(fileName, "//", "/");
			printf("Reading xml file: %s\n", fileName.c_str());

			std::vector<std::string> pluginList;
			std::string configPath = "";
			GetPluginNameList(fileName, pluginList, configPath);
			if (pluginList.size() > 0 && configPath != "")
			{
#if NF_PLATFORM == NF_PLATFORM_WIN
				pluginList.push_back("libprotobuf");
				pluginList.push_back("NFMessageDefine");
#else
				pluginList.push_back("NFMessageDefine");
#endif
				pluginList.push_back("NFPluginLoader");
				configPath = "../" + configPath;
				configPath = fileName.substr(0, fileName.find_last_of("/")) + "/" + configPath;

				for (std::string name : pluginList)
				{
					int result = 0;
					if (name == "NFPluginLoader")
					{
						int result = 0;
#if NF_PLATFORM == NF_PLATFORM_WIN

#ifdef NF_DEBUG_MODE
						std::string src = configPath + "Comm/Debug/" + name;
						std::string des = fileName.substr(0, fileName.find_last_of("/")) + "/" + name;
						auto strSrc = src + "_d.exe";
						auto strDes = des + "_d.exe";
						auto strSrcPDB = src + "_d.pdb";
						auto strDesPDB = des + "_d.pdb";

						result = CopyFile(strSrc, strDes);
						if (result != 1)
						{
							errorFileList.push_back(strSrc);
						}
						printResult(result, strSrc);

						result = CopyFile(strSrcPDB, strDesPDB);
						if (result != 1)
						{
							errorFileList.push_back(strSrcPDB);
						}
						printResult(result, strSrcPDB);
#else
						std::string src = configPath + "Comm/Release/" + name;
						std::string des = fileName.substr(0, fileName.find_last_of("/")) + "/" + name;
						auto strSrc = src + ".exe";
						auto strDes = des + ".exe";
						int result = 0;
						result = CopyFile(strSrc, strDes);
						if (result != 1)
						{
							errorFileList.push_back(strSrc);
						}
						printResult(result, strSrc);
#endif

#else
						std::string src = configPath + "Comm/" + name;
						std::string des = fileName.substr(0, fileName.find_last_of("/")) + "/" + name;
						auto strSrc = src + "_d";
						auto strDes = des + "_d";
						result = CopyFile(strSrc, strDes);
						if (result != 1)
						{
							errorFileList.push_back(strSrc);
						}
						printResult(result, strSrc);
#endif

					}
					else
					{
#if NF_PLATFORM == NF_PLATFORM_WIN

#ifdef NF_DEBUG_MODE
						std::string src = configPath + "Comm/Debug/" + name;
						std::string des = fileName.substr(0, fileName.find_last_of("/")) + "/" + name;
						auto strSrc = src + "_d.dll";
						auto strDes = des + "_d.dll";
						auto strSrcPDB = src + "_d.pdb";
						auto strDesPDB = des + "_d.pdb";
						result = CopyFile(strSrc, strDes);
						if (result != 1)
						{
							errorFileList.push_back(strSrc);
						}
						printResult(result, strSrc);

						result = CopyFile(strSrcPDB, strDesPDB);
						if (result != 1)
						{
							errorFileList.push_back(strSrcPDB);
						}
						printResult(result, strSrcPDB);
#else
						std::string src = configPath + "Comm/Release/" + name;
						std::string des = fileName.substr(0, fileName.find_last_of("/")) + "/" + name;
						auto strSrc = src + ".dll";
						auto strDes = des + ".dll";
						result = CopyFile(strSrc, strDes);
						if (result != 1)
						{
							errorFileList.push_back(strSrc);
						}
						printResult(result, strSrc);
#endif

#else
						std::string src = configPath + "Comm/lib" + name;
						std::string des = fileName.substr(0, fileName.find_last_of("/")) + "/" + name;
						auto strSrc = src + "_d.so";
						auto strDes = des + "_d.so";
						result = CopyFile(strSrc, strDes);
						if (result != 1)
						{
							errorFileList.push_back(strSrc);
						}
						printResult(result, strSrc);
#endif
					}
				}
			}
		}
	}
	if (errorFileList.size() <= 0)
	{
		printf("File Copy Done with NO ERROR!\n");
	}
	else
	{
		printf("File Copy Done with %d errors as follow:\n", (int)errorFileList.size());
		for (auto errFile : errorFileList)
		{
			printf("\t%s\n", errFile.c_str());
		}
	}
#if NF_PLATFORM == NF_PLATFORM_WIN
	printf("Press any Key to Exit!");
	getch();
#else

#endif
	return 0;
}<|endoftext|>


<commit_before>/*
	Flexisip, a flexible SIP proxy server with media capabilities.
    Copyright (C) 2010  Belledonne Communications SARL.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "agent.hh"
#include "registrardb.hh"

using namespace::std;

class Registrar : public Module {
	public:
		Registrar(Agent *ag) : Module(ag){
		}

		virtual void onDeclare(ConfigStruct *module_config){
			ConfigItemDescriptor items[]={
				{	StringList	,	"reg-domains",	"List of whitelist separated domain names to be managed by the registrar.","localhost"},
				config_item_end
			};
			module_config->addChildrenValues(items);
		}

		virtual void onLoad(Agent *agent, const ConfigStruct *module_config){
			list<string>::const_iterator it;
			mDomains=module_config->get<ConfigStringList>("reg-domains")->read();
			for (it=mDomains.begin();it!=mDomains.end();++it){
				LOGD("Found registrar domain: %s",(*it).c_str());
			}
		}

		virtual void onRequest(SipEvent *ev){
			sip_t *sip=ev->mSip;
			if (sip->sip_request->rq_method==sip_method_register){
				url_t *sipurl=sip->sip_from->a_url;
				if (sipurl->url_host && isManagedDomain(sipurl->url_host)){
					sip_expires_t *expires=sip->sip_expires;
					int delta=3600;
					char expires_str[16];

					if (expires){
						delta=expires->ex_delta;
						if (delta<30){
							delta=30;
						}
						if (delta > 3600*24)
							delta=3600*24;
					}
					snprintf(expires_str,sizeof(expires_str),"%i",delta);
					RegistrarDb::get()->addRecord(sip->sip_from,sip->sip_contact,delta);
					LOGD("Added record to registrar database.");
					/*we need to answer directly */
					nta_msg_treply(getAgent()->getSofiaAgent (),ev->mMsg,200,"Registration successful",
								   SIPTAG_CONTACT(sip->sip_contact), SIPTAG_SERVER_STR(getAgent()->getServerString()),
					               SIPTAG_EXPIRES_STR(expires_str),
								   TAG_END());
					ev->stopProcessing();
				}
			}else{
				/*see if we can route other requests */
				url_t *sipurl=sip->sip_request->rq_url;
				if (sipurl->url_host &&  isManagedDomain(sipurl->url_host)){
					const sip_contact_t *ct=RegistrarDb::get()->retrieveMostRecent(sipurl);
					/*sanity check on the contact address: might be '*' or whatever useless information*/
					if (ct && ct->m_url->url_host!=NULL && ct->m_url->url_host[0]!='\0'){
						LOGD("Registrar: found contact information in database, rewriting request uri");
						/*rewrite request-uri */
						sip->sip_request->rq_url[0]=*url_hdup(ev->getHome(),ct->m_url);
					}else{
						if (ct!=NULL){
							LOGW("Unrouted request because of incorrect address of record.");
						}
						if (sip->sip_request->rq_method!=sip_method_ack){
							LOGD("This user isn't registered.");
							nta_msg_treply(getAgent()->getSofiaAgent (),ev->mMsg,404,"User not found",SIPTAG_SERVER_STR(getAgent()->getServerString()),
						               TAG_END());
						}
						ev->stopProcessing();
					}
				}
			}
		}
		virtual void onResponse(SipEvent *ev){
		}

	private:
		bool isManagedDomain(const char *domain){
			return ModuleToolbox::matchesOneOf(domain,mDomains);
		}
		list<string> mDomains;
		static ModuleInfo<Registrar> sInfo;
};

ModuleInfo<Registrar> Registrar::sInfo("Registrar",
	"The Registrar module accepts REGISTERs for domains it manages, and store the address of record "
    "in order to route other requests destinated to the client who registered.");

<commit_msg>fix incorrect response to unREGISTER (expires was set to 30)<commit_after>/*
	Flexisip, a flexible SIP proxy server with media capabilities.
    Copyright (C) 2010  Belledonne Communications SARL.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "agent.hh"
#include "registrardb.hh"

using namespace::std;

class Registrar : public Module {
	public:
		Registrar(Agent *ag) : Module(ag){
		}

		virtual void onDeclare(ConfigStruct *module_config){
			ConfigItemDescriptor items[]={
				{	StringList	,	"reg-domains",	"List of whitelist separated domain names to be managed by the registrar.","localhost"},
				config_item_end
			};
			module_config->addChildrenValues(items);
		}

		virtual void onLoad(Agent *agent, const ConfigStruct *module_config){
			list<string>::const_iterator it;
			mDomains=module_config->get<ConfigStringList>("reg-domains")->read();
			for (it=mDomains.begin();it!=mDomains.end();++it){
				LOGD("Found registrar domain: %s",(*it).c_str());
			}
		}

		virtual void onRequest(SipEvent *ev){
			sip_t *sip=ev->mSip;
			if (sip->sip_request->rq_method==sip_method_register){
				url_t *sipurl=sip->sip_from->a_url;
				if (sipurl->url_host && isManagedDomain(sipurl->url_host)){
					sip_expires_t *expires=sip->sip_expires;
					int delta=3600;
					char expires_str[16];

					if (expires){
						delta=expires->ex_delta;
						if (delta>0  && delta<30){
							delta=30;
						}
						if (delta > 3600*24)
							delta=3600*24;
					}
					snprintf(expires_str,sizeof(expires_str),"%i",delta);
					RegistrarDb::get()->addRecord(sip->sip_from,sip->sip_contact,delta);
					LOGD("Added record to registrar database.");
					/*we need to answer directly */
					nta_msg_treply(getAgent()->getSofiaAgent (),ev->mMsg,200,"Registration successful",
								   SIPTAG_CONTACT(sip->sip_contact), SIPTAG_SERVER_STR(getAgent()->getServerString()),
					               SIPTAG_EXPIRES_STR(expires_str),
								   TAG_END());
					ev->stopProcessing();
				}
			}else{
				/*see if we can route other requests */
				url_t *sipurl=sip->sip_request->rq_url;
				if (sipurl->url_host &&  isManagedDomain(sipurl->url_host)){
					const sip_contact_t *ct=RegistrarDb::get()->retrieveMostRecent(sipurl);
					/*sanity check on the contact address: might be '*' or whatever useless information*/
					if (ct && ct->m_url->url_host!=NULL && ct->m_url->url_host[0]!='\0'){
						LOGD("Registrar: found contact information in database, rewriting request uri");
						/*rewrite request-uri */
						sip->sip_request->rq_url[0]=*url_hdup(ev->getHome(),ct->m_url);
					}else{
						if (ct!=NULL){
							LOGW("Unrouted request because of incorrect address of record.");
						}
						if (sip->sip_request->rq_method!=sip_method_ack){
							LOGD("This user isn't registered.");
							nta_msg_treply(getAgent()->getSofiaAgent (),ev->mMsg,404,"User not found",SIPTAG_SERVER_STR(getAgent()->getServerString()),
						               TAG_END());
						}
						ev->stopProcessing();
					}
				}
			}
		}
		virtual void onResponse(SipEvent *ev){
		}

	private:
		bool isManagedDomain(const char *domain){
			return ModuleToolbox::matchesOneOf(domain,mDomains);
		}
		list<string> mDomains;
		static ModuleInfo<Registrar> sInfo;
};

ModuleInfo<Registrar> Registrar::sInfo("Registrar",
	"The Registrar module accepts REGISTERs for domains it manages, and store the address of record "
    "in order to route other requests destinated to the client who registered.");

<|endoftext|>