<commit_before>// TODO(carpenter): move this into test/unit/command
//                  it's here now because it won't compile there

#include <gtest/gtest.h>
#include <stan/lang/compiler.hpp>
#include <stan/command/stanc_helper.hpp>
#include <test/unit/util.hpp>
#include <fstream>
#include <sstream>

void expect_find(const std::string& str, const std::string& target) {
  EXPECT_TRUE(str.find(target) != std::string::npos)
      << str << " does not contain " << target << std::endl;
}

TEST(commandStancHelper, printVersion) {
  std::stringstream ss;
  print_version(&ss);
  expect_find(ss.str(), "stanc version 2.");
}

TEST(commandStancHelper, printStancHelp) {
  std::stringstream ss;
  print_stanc_help(&ss);
  expect_find(ss.str(), "USAGE:  stanc [options] <model_file>");
  expect_find(ss.str(), "OPTIONS:");
}

bool create_test_file(const std::string& path, const std::string& program) {
  std::string cmd_setup = "mkdir -p test/test-models";
  system(cmd_setup.c_str());
  std::string cmd = "echo ";
  cmd += "\"";
  cmd += program;
  cmd += "\"";
  cmd += " > ";
  cmd += path;
  int return_code = system(cmd.c_str());
  return return_code == 0;
}

bool create_test_file() {
  std::string program = "parameters { real y; } model { y ~ normal(0, 1); }";
  return create_test_file("test/test-models/temp-bar.stan", program);
}

TEST(commandStancHelper, deleteFile) {
  if (!create_test_file())
    return;
  std::stringstream ss;
  delete_file(&ss, "test/test-models/temp-bar.stan");
  // test there's no error message
  EXPECT_EQ(0, ss.str().size());
  // and then test the file stream can't be opened
  std::fstream fs;
  fs.open("test/test-models/temp-bar.stan", std::fstream::in);
  EXPECT_FALSE(fs.is_open());
  fs.close();
}

int run_helper(const std::string& path, std::ostream& out, std::ostream& err) {
  int argc = 2;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back(path.c_str());
  const char** argv = &argv_vec[0];
  return stanc_helper(argc, argv, &out, &err);
}

TEST(commandStancHelper, readOnlyOK) {
  std::stringstream out;
  std::stringstream err;
  int rc = run_helper("src/test/test-models/good/stanc_helper.stan", out, err);
  EXPECT_EQ(0, rc) << "out=" << out.str() << std::endl
                   << "err=" << err.str() << std::endl;
  expect_find(out.str(), "Model name=stanc_helper_model");
  expect_find(out.str(),
              "Input file=src/test/test-models/good/stanc_helper.stan");
  expect_find(out.str(), "Output file=stanc_helper_model.cpp");
  delete_file(&err, "stanc_helper_model.cpp");
  EXPECT_EQ(0, err.str().size()) << "error=" << err.str() << std::endl;
}

TEST(commandStancHelper, failRC) {
  std::stringstream out;
  std::stringstream err;
  int rc = run_helper("src/test/test-models/bad/stanc_helper.stan", out, err);

  // TODO(carpenter): This should be -2 but it's -3 so
  // I only tested that it's != 0 to contrast with earlier success
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, noSuchFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 2;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("src/test/test-models/good/nosuchfile.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_GT(err.str().size(), 10) << "error=" << err.str() << std::endl;
  expect_find(err.str(), "Failed to open model file");
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, readOnlyDirReadFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 4;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("--name=m1");
  argv_vec.push_back("--o=src/test/test-models/m1.cpp");
  argv_vec.push_back("src/test/test-models/bad/read_only/m1.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
  delete_file(&err, "src/test/test-models/m1.cpp");
  EXPECT_EQ(0, err.str().size()) << "error=" << err.str() << std::endl;
}

TEST(commandStancHelper, readOnlyDirWriteFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 4;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("--name=m1");
  argv_vec.push_back("--o=src/test/test-models/read_only/m1.cpp");
  argv_vec.push_back("src/test/test-models/bad/read_only/m1.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, readOnlyDirBadFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 2;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("src/test/test-models/bad/read_only/nosuchfile.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, includeSinglePathGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back("--include_paths=src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
}

TEST(commandStancHelper, includeMultPathSimpleGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back("--include_paths=foo,src/test/test-models/included/,baz");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
}

TEST(commandStancHelper, includeMultPathSingleQuoteGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "--include_paths='path,with,commas',src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
}

TEST(commandStancHelper, includeMultPathDoubleQuoteGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "--include_paths=\"path,with,commas\",src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_EQ(0, rc);
}

TEST(commandStancHelper, includeMultPathEscapedCommaGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "--include_paths=path\\,with\\,commas,src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_EQ(0, rc);
}

TEST(commandStancHelper, includeMultPathBad) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back("--include_paths=foo,baz");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, includeNoPathBad) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}
<commit_msg>fix ignoring return value<commit_after>// TODO(carpenter): move this into test/unit/command
//                  it's here now because it won't compile there

#include <gtest/gtest.h>
#include <stan/lang/compiler.hpp>
#include <stan/command/stanc_helper.hpp>
#include <test/unit/util.hpp>
#include <fstream>
#include <sstream>

void expect_find(const std::string& str, const std::string& target) {
  EXPECT_TRUE(str.find(target) != std::string::npos)
      << str << " does not contain " << target << std::endl;
}

TEST(commandStancHelper, printVersion) {
  std::stringstream ss;
  print_version(&ss);
  expect_find(ss.str(), "stanc version 2.");
}

TEST(commandStancHelper, printStancHelp) {
  std::stringstream ss;
  print_stanc_help(&ss);
  expect_find(ss.str(), "USAGE:  stanc [options] <model_file>");
  expect_find(ss.str(), "OPTIONS:");
}

bool create_test_file(const std::string& path, const std::string& program) {
  std::string cmd_setup = "mkdir -p test/test-models";
  int return_code = system(cmd_setup.c_str());
  std::string cmd = "echo ";
  cmd += "\"";
  cmd += program;
  cmd += "\"";
  cmd += " > ";
  cmd += path;
  return_code = system(cmd.c_str());
  return return_code == 0;
}

bool create_test_file() {
  std::string program = "parameters { real y; } model { y ~ normal(0, 1); }";
  return create_test_file("test/test-models/temp-bar.stan", program);
}

TEST(commandStancHelper, deleteFile) {
  if (!create_test_file())
    return;
  std::stringstream ss;
  delete_file(&ss, "test/test-models/temp-bar.stan");
  // test there's no error message
  EXPECT_EQ(0, ss.str().size());
  // and then test the file stream can't be opened
  std::fstream fs;
  fs.open("test/test-models/temp-bar.stan", std::fstream::in);
  EXPECT_FALSE(fs.is_open());
  fs.close();
}

int run_helper(const std::string& path, std::ostream& out, std::ostream& err) {
  int argc = 2;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back(path.c_str());
  const char** argv = &argv_vec[0];
  return stanc_helper(argc, argv, &out, &err);
}

TEST(commandStancHelper, readOnlyOK) {
  std::stringstream out;
  std::stringstream err;
  int rc = run_helper("src/test/test-models/good/stanc_helper.stan", out, err);
  EXPECT_EQ(0, rc) << "out=" << out.str() << std::endl
                   << "err=" << err.str() << std::endl;
  expect_find(out.str(), "Model name=stanc_helper_model");
  expect_find(out.str(),
              "Input file=src/test/test-models/good/stanc_helper.stan");
  expect_find(out.str(), "Output file=stanc_helper_model.cpp");
  delete_file(&err, "stanc_helper_model.cpp");
  EXPECT_EQ(0, err.str().size()) << "error=" << err.str() << std::endl;
}

TEST(commandStancHelper, failRC) {
  std::stringstream out;
  std::stringstream err;
  int rc = run_helper("src/test/test-models/bad/stanc_helper.stan", out, err);

  // TODO(carpenter): This should be -2 but it's -3 so
  // I only tested that it's != 0 to contrast with earlier success
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, noSuchFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 2;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("src/test/test-models/good/nosuchfile.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_GT(err.str().size(), 10) << "error=" << err.str() << std::endl;
  expect_find(err.str(), "Failed to open model file");
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, readOnlyDirReadFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 4;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("--name=m1");
  argv_vec.push_back("--o=src/test/test-models/m1.cpp");
  argv_vec.push_back("src/test/test-models/bad/read_only/m1.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
  delete_file(&err, "src/test/test-models/m1.cpp");
  EXPECT_EQ(0, err.str().size()) << "error=" << err.str() << std::endl;
}

TEST(commandStancHelper, readOnlyDirWriteFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 4;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("--name=m1");
  argv_vec.push_back("--o=src/test/test-models/read_only/m1.cpp");
  argv_vec.push_back("src/test/test-models/bad/read_only/m1.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, readOnlyDirBadFile) {
  std::stringstream out;
  std::stringstream err;
  int argc = 2;
  std::vector<const char*> argv_vec;
  argv_vec.push_back("main");
  argv_vec.push_back("src/test/test-models/bad/read_only/nosuchfile.stan");
  const char** argv = &argv_vec[0];
  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, includeSinglePathGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back("--include_paths=src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
}

TEST(commandStancHelper, includeMultPathSimpleGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back("--include_paths=foo,src/test/test-models/included/,baz");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
}

TEST(commandStancHelper, includeMultPathSingleQuoteGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "--include_paths='path,with,commas',src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc == 0);
}

TEST(commandStancHelper, includeMultPathDoubleQuoteGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "--include_paths=\"path,with,commas\",src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_EQ(0, rc);
}

TEST(commandStancHelper, includeMultPathEscapedCommaGood) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "--include_paths=path\\,with\\,commas,src/test/test-models/included/");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_EQ(0, rc);
}

TEST(commandStancHelper, includeMultPathBad) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back("--include_paths=foo,baz");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}

TEST(commandStancHelper, includeNoPathBad) {
  std::stringstream out;
  std::stringstream err;
  std::vector<const char*> argv_vec;

  argv_vec.push_back("main");
  argv_vec.push_back(
      "src/test/test-models/include_path_test/stanc_helper_with_include.stan");

  int argc = argv_vec.size();
  const char** argv = &argv_vec[0];

  int rc = stanc_helper(argc, argv, &out, &err);
  EXPECT_TRUE(rc != 0);
}
<|endoftext|>


<commit_before>#include <stdlib.h>
#include <vector>
#include <set>
#include <functional>

#include <opengm/graphicalmodel/graphicalmodel.hxx>
#include <opengm/operations/adder.hxx>
#include <opengm/operations/multiplier.hxx>
#include <opengm/operations/minimizer.hxx>
#include <opengm/operations/maximizer.hxx>
#include <opengm/inference/graphcut.hxx>

#include <opengm/unittests/blackboxtester.hxx>
#include <opengm/unittests/blackboxtests/blackboxtestgrid.hxx>
#include <opengm/unittests/blackboxtests/blackboxtestfull.hxx>
#include <opengm/unittests/blackboxtests/blackboxteststar.hxx>

#ifdef WITH_BOOST
#  ifdef NDEBUG
#    include <opengm/inference/auxiliary/minstcutboost.hxx>
#  else
#    define NDEBUG //Hot-fix to deal with floating point assert problem in boost
#    include <opengm/inference/auxiliary/minstcutboost.hxx>
#    undefine NDEBUG
#  endif
#endif
#ifdef WITH_MAXFLOW
#  include <opengm/inference/auxiliary/minstcutkolmogorov.hxx>
#endif
#ifdef WITH_MAXFLOW_IBFS
#  include <opengm/inference/auxiliary/minstcutibfs.hxx>
#endif

int main() {
   typedef opengm::GraphicalModel<float, opengm::Adder> GraphicalModelType;
   typedef opengm::GraphicalModel<float, opengm::Adder,
      opengm::ExplicitFunction<float,unsigned short, unsigned char>,
      opengm::DiscreteSpace<unsigned short, unsigned char> > SumGmType2;
   typedef opengm::BlackBoxTestGrid<SumGmType2> SumGridTest2;
   typedef opengm::BlackBoxTestFull<SumGmType2> SumFullTest2;
   typedef opengm::BlackBoxTestStar<SumGmType2> SumStarTest2;
   typedef opengm::BlackBoxTestGrid<GraphicalModelType> GridTest;
   typedef opengm::BlackBoxTestFull<GraphicalModelType> FullTest;
   typedef opengm::BlackBoxTestStar<GraphicalModelType> StarTest;

   opengm::InferenceBlackBoxTester<SumGmType2> minTester2;
   minTester2.addTest(new SumGridTest2(4, 4, 2, false, true, SumGridTest2::POTTS, opengm::OPTIMAL, 1));

   opengm::InferenceBlackBoxTester<GraphicalModelType> minTester;
   minTester.addTest(new GridTest(4, 4, 2, false, true, GridTest::POTTS, opengm::OPTIMAL, 10));
   minTester.addTest(new GridTest(3, 3, 2, false, true, GridTest::POTTS, opengm::OPTIMAL, 10));
   minTester.addTest(new GridTest(3, 3, 2, false, false,GridTest::POTTS, opengm::OPTIMAL, 10));
   minTester.addTest(new StarTest(5,    2, false, true, StarTest::POTTS, opengm::OPTIMAL, 10));
   minTester.addTest(new FullTest(5,    2, false, 3,    FullTest::POTTS, opengm::OPTIMAL, 10));

   opengm::InferenceBlackBoxTester<GraphicalModelType> maxTester;
   maxTester.addTest(new GridTest(4, 4, 2, false, true, GridTest::IPOTTS, opengm::OPTIMAL, 1));
   maxTester.addTest(new GridTest(3, 3, 2, false, true, GridTest::IPOTTS, opengm::OPTIMAL, 3));
   maxTester.addTest(new GridTest(3, 3, 2, false, false,GridTest::IPOTTS, opengm::OPTIMAL, 3));
   maxTester.addTest(new StarTest(5,    2, false, true, StarTest::IPOTTS, opengm::OPTIMAL, 3));
   maxTester.addTest(new FullTest(5,    2, false, 3,    FullTest::IPOTTS, opengm::OPTIMAL, 3));

   std::cout << "Test Graphcut ..." << std::endl;

#ifdef WITH_MAXFLOW_IBFS
   std::cout << "  * Test Min-Sum with IBFS (int)" << std::endl;
   {
      typedef opengm::external::MinSTCutIBFS<int, int> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Minimizer, MinStCutType> MinGraphCut;
      MinGraphCut::Parameter para(10000);
      minTester.test<MinGraphCut>(para);
   }
#endif

#ifdef WITH_MAXFLOW
   std::cout << "  * Test Min-Sum with Kolmogorov" << std::endl;
   {
      typedef opengm::external::MinSTCutKolmogorov<size_t, float> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Minimizer, MinStCutType> MinGraphCut;
      MinGraphCut::Parameter para;
      minTester.test<MinGraphCut>(para);
   }
   std::cout << "  * Test Min-Sum with Kolmogorov (float,uint16,uint8) " << std::endl;
   {
      typedef opengm::external::MinSTCutKolmogorov<size_t, float> MinStCutType;
      typedef opengm::GraphCut<SumGmType2, opengm::Minimizer, MinStCutType> MinGraphCut;
      MinGraphCut::Parameter para;
      minTester2.test<MinGraphCut>(para);
   }
#endif

#ifdef WITH_BOOST
   std::cout << "  * Test Min-Sum with BOOST-Push-Relabel" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, float, opengm::PUSH_RELABEL> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Minimizer, MinStCutType> MinGraphCut;
      MinGraphCut::Parameter para;
      minTester.test<MinGraphCut>(para);
   }
   std::cout << "  * Test Min-Sum with Interegr-BOOST-Push-Relabel" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, long, opengm::PUSH_RELABEL> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Minimizer, MinStCutType> MinGraphCut;
      MinGraphCut::Parameter para(1000000);
      minTester.test<MinGraphCut>(para);
   }
   std::cout << "  * Test Min-Sum with BOOST-Edmonds-Karp" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, float, opengm::EDMONDS_KARP> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Minimizer, MinStCutType> MinGraphCut;
      MinGraphCut::Parameter para;
      minTester.test<MinGraphCut>(para);
   }
   std::cout << "  * Test Min-Sum with BOOST-Kolmogorov" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, float, opengm::KOLMOGOROV> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Minimizer, MinStCutType> MinGraphCut;
      MinGraphCut::Parameter para;
      minTester.test<MinGraphCut>(para);
   }
#endif

#ifdef WITH_MAXFLOW_IBFS
   std::cout << "  * Test Max-Sum with IBFS (int)" << std::endl;
   {
      typedef opengm::external::MinSTCutIBFS<int, int> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Maximizer, MinStCutType> MaxGraphCut;
      MaxGraphCut::Parameter para(10000);
      maxTester.test<MaxGraphCut>(para);

   }
#endif

#ifdef WITH_MAXFLOW
   std::cout << "  * Test Max-Sum with Kolmogorov" << std::endl;
   {
      typedef opengm::external::MinSTCutKolmogorov<size_t, float> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Maximizer, MinStCutType> MaxGraphCut;
      MaxGraphCut::Parameter para;
      maxTester.test<MaxGraphCut>(para);

   }
#endif

#ifdef WITH_BOOST
   std::cout << "  * Test Max-Sum with BOOST-Push-Relabel" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, float, opengm::PUSH_RELABEL> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Maximizer, MinStCutType> MaxGraphCut;
      MaxGraphCut::Parameter para;
      maxTester.test<MaxGraphCut>(para);
   }
   //This test might fail due to overflow with int
   /*
   std::cout << "  * Test Max-Sum with Integer-BOOST-Push-Relabel" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, int, opengm::PUSH_RELABEL> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Maximizer, MinStCutType> MaxGraphCut;
      MaxGraphCut::Parameter para(1000000);
      maxTester.test<MaxGraphCut>(para);
   }
   */
   std::cout << "  * Test Max-Sum with BOOST-Edmonds-Karp" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, float, opengm::EDMONDS_KARP> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Maximizer, MinStCutType> MaxGraphCut;
      MaxGraphCut::Parameter para;
      maxTester.test<MaxGraphCut>(para);
   }
   std::cout << "  * Test Max-Sum with BOOST-Kolmogorov" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, float, opengm::KOLMOGOROV> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Maximizer, MinStCutType> MaxGraphCut;
      MaxGraphCut::Parameter para;
      maxTester.test<MaxGraphCut>(para);
   }
#endif

   return 0;
}
<commit_msg>s/undefine/undef in macro checks<commit_after>#include <stdlib.h>
#include <vector>
#include <set>
#include <functional>

#include <opengm/graphicalmodel/graphicalmodel.hxx>
#include <opengm/operations/adder.hxx>
#include <opengm/operations/multiplier.hxx>
#include <opengm/operations/minimizer.hxx>
#include <opengm/operations/maximizer.hxx>
#include <opengm/inference/graphcut.hxx>

#include <opengm/unittests/blackboxtester.hxx>
#include <opengm/unittests/blackboxtests/blackboxtestgrid.hxx>
#include <opengm/unittests/blackboxtests/blackboxtestfull.hxx>
#include <opengm/unittests/blackboxtests/blackboxteststar.hxx>

#ifdef WITH_BOOST
#  ifdef NDEBUG
#    include <opengm/inference/auxiliary/minstcutboost.hxx>
#  else
#    define NDEBUG //Hot-fix to deal with floating point assert problem in boost
#    include <opengm/inference/auxiliary/minstcutboost.hxx>
#    undef NDEBUG
#  endif
#endif
#ifdef WITH_MAXFLOW
#  include <opengm/inference/auxiliary/minstcutkolmogorov.hxx>
#endif
#ifdef WITH_MAXFLOW_IBFS
#  include <opengm/inference/auxiliary/minstcutibfs.hxx>
#endif

int main() {
   typedef opengm::GraphicalModel<float, opengm::Adder> GraphicalModelType;
   typedef opengm::GraphicalModel<float, opengm::Adder,
      opengm::ExplicitFunction<float,unsigned short, unsigned char>,
      opengm::DiscreteSpace<unsigned short, unsigned char> > SumGmType2;
   typedef opengm::BlackBoxTestGrid<SumGmType2> SumGridTest2;
   typedef opengm::BlackBoxTestFull<SumGmType2> SumFullTest2;
   typedef opengm::BlackBoxTestStar<SumGmType2> SumStarTest2;
   typedef opengm::BlackBoxTestGrid<GraphicalModelType> GridTest;
   typedef opengm::BlackBoxTestFull<GraphicalModelType> FullTest;
   typedef opengm::BlackBoxTestStar<GraphicalModelType> StarTest;

   opengm::InferenceBlackBoxTester<SumGmType2> minTester2;
   minTester2.addTest(new SumGridTest2(4, 4, 2, false, true, SumGridTest2::POTTS, opengm::OPTIMAL, 1));

   opengm::InferenceBlackBoxTester<GraphicalModelType> minTester;
   minTester.addTest(new GridTest(4, 4, 2, false, true, GridTest::POTTS, opengm::OPTIMAL, 10));
   minTester.addTest(new GridTest(3, 3, 2, false, true, GridTest::POTTS, opengm::OPTIMAL, 10));
   minTester.addTest(new GridTest(3, 3, 2, false, false,GridTest::POTTS, opengm::OPTIMAL, 10));
   minTester.addTest(new StarTest(5,    2, false, true, StarTest::POTTS, opengm::OPTIMAL, 10));
   minTester.addTest(new FullTest(5,    2, false, 3,    FullTest::POTTS, opengm::OPTIMAL, 10));

   opengm::InferenceBlackBoxTester<GraphicalModelType> maxTester;
   maxTester.addTest(new GridTest(4, 4, 2, false, true, GridTest::IPOTTS, opengm::OPTIMAL, 1));
   maxTester.addTest(new GridTest(3, 3, 2, false, true, GridTest::IPOTTS, opengm::OPTIMAL, 3));
   maxTester.addTest(new GridTest(3, 3, 2, false, false,GridTest::IPOTTS, opengm::OPTIMAL, 3));
   maxTester.addTest(new StarTest(5,    2, false, true, StarTest::IPOTTS, opengm::OPTIMAL, 3));
   maxTester.addTest(new FullTest(5,    2, false, 3,    FullTest::IPOTTS, opengm::OPTIMAL, 3));

   std::cout << "Test Graphcut ..." << std::endl;

#ifdef WITH_MAXFLOW_IBFS
   std::cout << "  * Test Min-Sum with IBFS (int)" << std::endl;
   {
      typedef opengm::external::MinSTCutIBFS<int, int> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Minimizer, MinStCutType> MinGraphCut;
      MinGraphCut::Parameter para(10000);
      minTester.test<MinGraphCut>(para);
   }
#endif

#ifdef WITH_MAXFLOW
   std::cout << "  * Test Min-Sum with Kolmogorov" << std::endl;
   {
      typedef opengm::external::MinSTCutKolmogorov<size_t, float> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Minimizer, MinStCutType> MinGraphCut;
      MinGraphCut::Parameter para;
      minTester.test<MinGraphCut>(para);
   }
   std::cout << "  * Test Min-Sum with Kolmogorov (float,uint16,uint8) " << std::endl;
   {
      typedef opengm::external::MinSTCutKolmogorov<size_t, float> MinStCutType;
      typedef opengm::GraphCut<SumGmType2, opengm::Minimizer, MinStCutType> MinGraphCut;
      MinGraphCut::Parameter para;
      minTester2.test<MinGraphCut>(para);
   }
#endif

#ifdef WITH_BOOST
   std::cout << "  * Test Min-Sum with BOOST-Push-Relabel" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, float, opengm::PUSH_RELABEL> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Minimizer, MinStCutType> MinGraphCut;
      MinGraphCut::Parameter para;
      minTester.test<MinGraphCut>(para);
   }
   std::cout << "  * Test Min-Sum with Interegr-BOOST-Push-Relabel" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, long, opengm::PUSH_RELABEL> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Minimizer, MinStCutType> MinGraphCut;
      MinGraphCut::Parameter para(1000000);
      minTester.test<MinGraphCut>(para);
   }
   std::cout << "  * Test Min-Sum with BOOST-Edmonds-Karp" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, float, opengm::EDMONDS_KARP> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Minimizer, MinStCutType> MinGraphCut;
      MinGraphCut::Parameter para;
      minTester.test<MinGraphCut>(para);
   }
   std::cout << "  * Test Min-Sum with BOOST-Kolmogorov" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, float, opengm::KOLMOGOROV> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Minimizer, MinStCutType> MinGraphCut;
      MinGraphCut::Parameter para;
      minTester.test<MinGraphCut>(para);
   }
#endif

#ifdef WITH_MAXFLOW_IBFS
   std::cout << "  * Test Max-Sum with IBFS (int)" << std::endl;
   {
      typedef opengm::external::MinSTCutIBFS<int, int> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Maximizer, MinStCutType> MaxGraphCut;
      MaxGraphCut::Parameter para(10000);
      maxTester.test<MaxGraphCut>(para);

   }
#endif

#ifdef WITH_MAXFLOW
   std::cout << "  * Test Max-Sum with Kolmogorov" << std::endl;
   {
      typedef opengm::external::MinSTCutKolmogorov<size_t, float> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Maximizer, MinStCutType> MaxGraphCut;
      MaxGraphCut::Parameter para;
      maxTester.test<MaxGraphCut>(para);

   }
#endif

#ifdef WITH_BOOST
   std::cout << "  * Test Max-Sum with BOOST-Push-Relabel" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, float, opengm::PUSH_RELABEL> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Maximizer, MinStCutType> MaxGraphCut;
      MaxGraphCut::Parameter para;
      maxTester.test<MaxGraphCut>(para);
   }
   //This test might fail due to overflow with int
   /*
   std::cout << "  * Test Max-Sum with Integer-BOOST-Push-Relabel" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, int, opengm::PUSH_RELABEL> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Maximizer, MinStCutType> MaxGraphCut;
      MaxGraphCut::Parameter para(1000000);
      maxTester.test<MaxGraphCut>(para);
   }
   */
   std::cout << "  * Test Max-Sum with BOOST-Edmonds-Karp" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, float, opengm::EDMONDS_KARP> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Maximizer, MinStCutType> MaxGraphCut;
      MaxGraphCut::Parameter para;
      maxTester.test<MaxGraphCut>(para);
   }
   std::cout << "  * Test Max-Sum with BOOST-Kolmogorov" << std::endl;
   {
      typedef opengm::MinSTCutBoost<size_t, float, opengm::KOLMOGOROV> MinStCutType;
      typedef opengm::GraphCut<GraphicalModelType, opengm::Maximizer, MinStCutType> MaxGraphCut;
      MaxGraphCut::Parameter para;
      maxTester.test<MaxGraphCut>(para);
   }
#endif

   return 0;
}
<|endoftext|>


<commit_before>/* -*- mode:linux -*- */
/**
 * \file main.cc
 *
 *
 *
 * \author Ethan Burns
 * \date 2008-10-08
 */

#include <iostream>
#include <vector>
#include <string.h>
#include <stdlib.h>

#include "state.h"
#include "a_star.h"
#include "cost_bound_dfs.h"
#include "ida_star.h"
#include "kbfs.h"
#include "h_zero.h"
#include "grid/grid_world.h"

using namespace std;

Search *get_search(int argc, char *argv[])
{
	if (argc > 1 && strcmp(argv[1], "astar") == 0) {
		return new AStar();
	} else if (argc > 1 && strcmp(argv[1], "idastar") == 0) {
		return new IDAStar();
	} else if (argc > 2 && strcmp(argv[1], "costbounddfs") == 0) {
		return new CostBoundDFS(atoi(argv[2]));
	} else if (argc > 1 && strcmp(argv[1], "kbfs") == 0) {
		return new KBFS();
	} else {
		cout << "Must supply a search algorithm:" << endl;
		cout << "\tastar, idastar, costbounddfs, kbfs" << endl;
		exit(EXIT_FAILURE);
	}
}

int main(int argc, char *argv[])
{
	vector<const State *> *path;
	GridWorld g(cin);
	GridWorld::ManhattanDist manhattan(&g);
	HZero hzero(&g);
	Search *search = get_search(argc, argv);

	g.set_heuristic(&manhattan);
//	g.set_heuristic(&hzero);
	path = search->search(g.initial_state());
//	g.print(cout, path);

	cout << "generated: " << search->get_generated() << endl;
	cout << "expanded: " << search->get_expanded() << endl;
	if (path) {
		cout << "cost: " << path->at(0)->get_g() << endl;
		for (unsigned int i = 0; i < path->size(); i += 1)
			delete path->at(i);
		delete path;
	} else {
		cout << "No Solution" << endl;
	}

	delete search;

#if ENABLE_IMAGES
	g.export_eps("output.eps");
#endif	// ENABLE_IMAGES

	return EXIT_SUCCESS;
}
<commit_msg>Check the command line arguments before creating the SearchDomain. This way we can report erroneous command line arguments before we attempt to read in a GridWorld.<commit_after>/* -*- mode:linux -*- */
/**
 * \file main.cc
 *
 *
 *
 * \author Ethan Burns
 * \date 2008-10-08
 */

#include <iostream>
#include <vector>
#include <string.h>
#include <stdlib.h>

#include "state.h"
#include "a_star.h"
#include "cost_bound_dfs.h"
#include "ida_star.h"
#include "kbfs.h"
#include "h_zero.h"
#include "grid/grid_world.h"

using namespace std;

Search *get_search(int argc, char *argv[])
{
	if (argc > 1 && strcmp(argv[1], "astar") == 0) {
		return new AStar();
	} else if (argc > 1 && strcmp(argv[1], "idastar") == 0) {
		return new IDAStar();
	} else if (argc > 2 && strcmp(argv[1], "costbounddfs") == 0) {
		return new CostBoundDFS(atoi(argv[2]));
	} else if (argc > 1 && strcmp(argv[1], "kbfs") == 0) {
		return new KBFS();
	} else {
		cout << "Must supply a search algorithm:" << endl;
		cout << "\tastar, idastar, costbounddfs, kbfs" << endl;
		exit(EXIT_FAILURE);
	}
}

int main(int argc, char *argv[])
{
	vector<const State *> *path;
	Search *search = get_search(argc, argv);
	GridWorld g(cin);
	GridWorld::ManhattanDist manhattan(&g);
	HZero hzero(&g);

	g.set_heuristic(&manhattan);
//	g.set_heuristic(&hzero);
	path = search->search(g.initial_state());
//	g.print(cout, path);

	cout << "generated: " << search->get_generated() << endl;
	cout << "expanded: " << search->get_expanded() << endl;
	if (path) {
		cout << "cost: " << path->at(0)->get_g() << endl;
		for (unsigned int i = 0; i < path->size(); i += 1)
			delete path->at(i);
		delete path;
	} else {
		cout << "No Solution" << endl;
	}

	delete search;

#if ENABLE_IMAGES
	g.export_eps("output.eps");
#endif	// ENABLE_IMAGES

	return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>/* This file is part of qjson
  *
  * Copyright (C) 2009 Till Adam <adam@kde.org>
  * Copyright (C) 2009 Flavio Castelli <flavio@castelli.name>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License version 2.1, as published by the Free Software Foundation.
  *
  *
  * This library 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
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with this library; see the file COPYING.LIB.  If not, write to
  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301, USA.
  */

#include "serializer.h"

#include <QtCore/QDataStream>
#include <QtCore/QStringList>
#include <QtCore/QVariant>

#include <cmath>

#ifdef _MSC_VER  // using MSVC compiler
#include <float.h>
#endif

using namespace QJson;

class Serializer::SerializerPrivate {
  public:
    SerializerPrivate() :
      specialNumbersAllowed(false),
      indentMode(QJson::IndentNone),
      doublePrecision(6) {
        errorMessage.clear();
    }
    QString errorMessage;
    bool specialNumbersAllowed;
    IndentMode indentMode;
    int doublePrecision;
    QByteArray buildIndent(int spaces);
    QByteArray serialize( const QVariant &v, bool *ok, int indentLevel = 0);
    QString sanitizeString( QString str );
    QByteArray join( const QList<QByteArray>& list, const QByteArray& sep );
};

QByteArray Serializer::SerializerPrivate::join( const QList<QByteArray>& list, const QByteArray& sep ) {
  QByteArray res;
  Q_FOREACH( const QByteArray& i, list ) {
    if ( !res.isEmpty() )
      res += sep;
    res += i;
  }
  return res;
}

QByteArray Serializer::SerializerPrivate::serialize( const QVariant &v, bool *ok, int indentLevel)
{
  QByteArray str;
  bool error = false;
  QByteArray indent;

  if ( ! v.isValid() ) { // invalid or null?
    str = "null";
  } else if (( v.type() == QVariant::List ) || ( v.type() == QVariant::StringList )){ // an array or a stringlist?
    const QVariantList list = v.toList();
    QList<QByteArray> values;
    Q_FOREACH( const QVariant& var, list )
    {
      indentLevel++;
      QByteArray serializedValue = serialize( var, ok, indentLevel);
      indentLevel--;
      if ( !*ok ) {
        break;
      }
      values << serializedValue;
    }

    if (indentMode == QJson::IndentMinimum) {
      QByteArray indent = buildIndent(indentLevel - 1);
      str = "[\n" + join( values, ",\n" ) + "\n" + indent + "]";
    }
    else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
      QByteArray indent = buildIndent(indentLevel);
      str = "[\n" + join( values, ",\n" ) + "\n" + indent + "]";
    }
    else if (indentMode == QJson::IndentCompact) {
      str = "[" + join( values, "," ) + "]";
    }
    else {
      str = "[ " + join( values, ", " ) + " ]";
    }

  } else if ( v.type() == QVariant::Map ) { // variant is a map?
    const QVariantMap vmap = v.toMap();
    QMapIterator<QString, QVariant> it( vmap );

    if (indentMode == QJson::IndentMinimum) {
      QByteArray indent = buildIndent(indentLevel);
      str = indent + "{ ";
    }
    else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
      QByteArray indent = buildIndent(indentLevel);
      QByteArray nextindent = buildIndent(indentLevel + 1);
      str = indent + "{\n" + nextindent;
    }
    else if (indentMode == QJson::IndentCompact) {
      str = "{";
    }
    else {
      str = "{ ";
    }

    QList<QByteArray> pairs;
    while ( it.hasNext() ) {
      it.next();
      indentLevel++;
      QByteArray serializedValue = serialize( it.value(), ok, indentLevel);
      indentLevel--;
      if ( !*ok ) {
        break;
      }
      QByteArray key   = sanitizeString( it.key() ).toUtf8();
      QByteArray value = serializedValue;
      if (indentMode == QJson::IndentCompact) {
        pairs << key + ":" + value;
      } else {
        pairs << key + " : " + value;
      }
    }

    if (indentMode == QJson::IndentFull) {
      QByteArray indent = buildIndent(indentLevel + 1);
      str += join( pairs, ",\n" + indent);
    }
    else if (indentMode == QJson::IndentCompact) {
      str += join( pairs, "," );
    }
    else {
      str += join( pairs, ", " );
    }

    if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
      QByteArray indent = buildIndent(indentLevel);
      str += "\n" + indent + "}";
    }
    else if (indentMode == QJson::IndentCompact) {
      str += "}";
    }
    else {
      str += " }";
    }

  } else if ( v.type() == QVariant::Hash ) { // variant is a hash?
    const QVariantHash vhash = v.toHash();
    QHashIterator<QString, QVariant> it( vhash );

    if (indentMode == QJson::IndentMinimum) {
      QByteArray indent = buildIndent(indentLevel);
      str = indent + "{ ";
    }
    else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
      QByteArray indent = buildIndent(indentLevel);
      QByteArray nextindent = buildIndent(indentLevel + 1);
      str = indent + "{\n" + nextindent;
    }
    else if (indentMode == QJson::IndentCompact) {
      str = "{";
    }
    else {
      str = "{ ";
    }

    QList<QByteArray> pairs;
    while ( it.hasNext() ) {
      it.next();
      indentLevel++;
      QByteArray serializedValue = serialize( it.value(), ok, indentLevel);
      indentLevel--;
      if ( !*ok ) {
        break;
      }
      QByteArray key   = sanitizeString( it.key() ).toUtf8();
      QByteArray value = serializedValue;
      if (indentMode == QJson::IndentCompact) {
        pairs << key + ":" + value;
      } else {
        pairs << key + " : " + value;
      }
    }

    if (indentMode == QJson::IndentFull) {
      QByteArray indent = buildIndent(indentLevel + 1);
      str += join( pairs, ",\n" + indent);
    }
    else if (indentMode == QJson::IndentCompact) {
      str += join( pairs, "," );
    }
    else {
      str += join( pairs, ", " );
    }

    if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
      QByteArray indent = buildIndent(indentLevel);
      str += "\n" + indent + "}";
    }
    else if (indentMode == QJson::IndentCompact) {
      str += "}";
    }
    else {
      str += " }";
    }

  } else if (( v.type() == QVariant::String ) ||  ( v.type() == QVariant::ByteArray )) { // a string or a byte array?
    str = sanitizeString( v.toString() ).toUtf8();
  } else if (( v.type() == QVariant::Double) || ((QMetaType::Type)v.type() == QMetaType::Float)) { // a double or a float?
    const double value = v.toDouble();
#if defined _WIN32 && !defined(Q_OS_SYMBIAN)
    const bool special = _isnan(value) || !_finite(value);
#elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID)
    const bool special = isnan(value) || isinf(value);
#else
    const bool special = std::isnan(value) || std::isinf(value);
#endif
    if (special) {
      if (specialNumbersAllowed) {
#if defined _WIN32 && !defined(Q_OS_SYMBIAN)
        if (_isnan(value)) {
#elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID)
        if (isnan(value)) {
#else
        if (std::isnan(value)) {
#endif
          str += "NaN";
        } else {
          if (value<0) {
            str += '-';
          }
          str += "Infinity";
        }
      } else {
        errorMessage += QLatin1String("Attempt to write NaN or infinity, which is not supported by json\n");
        *ok = false;
    }
    } else {
      str = QByteArray::number( value , 'g', doublePrecision);
      if( ! str.contains( "." ) && ! str.contains( "e" ) ) {
        str += ".0";
      }
    }
  } else if ( v.type() == QVariant::Bool ) { // boolean value?
    str = ( v.toBool() ? "true" : "false" );
  } else if ( v.type() == QVariant::ULongLong ) { // large unsigned number?
    str = QByteArray::number( v.value<qulonglong>() );
  } else if ( v.type() == QVariant::UInt ) { // unsigned int number?
    str = QByteArray::number( v.value<quint32>() );
  } else if ( v.canConvert<qlonglong>() ) { // any signed number?
    str = QByteArray::number( v.value<qlonglong>() );
  } else if ( v.canConvert<int>() ) { // unsigned short number?
    str = QByteArray::number( v.value<int>() );
  } else if ( v.canConvert<QString>() ){ // can value be converted to string?
    // this will catch QDate, QDateTime, QUrl, ...
    str = sanitizeString( v.toString() ).toUtf8();
    //TODO: catch other values like QImage, QRect, ...
  } else {
    *ok = false;
    errorMessage += QLatin1String("Cannot serialize ");
    errorMessage += v.toString();
    errorMessage += QLatin1String(" because type ");
    errorMessage += QLatin1String(v.typeName());
    errorMessage += QLatin1String(" is not supported by QJson\n");
  }
  if ( *ok )
  {
    return str;
  }
  else
    return QByteArray();
}

QByteArray Serializer::SerializerPrivate::buildIndent(int spaces)
{
   QByteArray indent;
   if (spaces < 0) {
     spaces = 0;
   }
   for (int i = 0; i < spaces; i++ ) {
     indent += " ";
   }
   return indent;
}

QString Serializer::SerializerPrivate::sanitizeString( QString str )
{
  str.replace( QLatin1String( "\\" ), QLatin1String( "\\\\" ) );

  // escape unicode chars
  QString result;
  const ushort* unicode = str.utf16();
  unsigned int i = 0;

  while ( unicode[ i ] ) {
    if ( unicode[ i ] < 128 ) {
      result.append( QChar( unicode[ i ] ) );
    }
    else {
      QString hexCode = QString::number( unicode[ i ], 16 ).rightJustified( 4,
                                                           QLatin1Char('0') );

      result.append( QLatin1String ("\\u") ).append( hexCode );
    }
    ++i;
  }
  str = result;

  str.replace( QLatin1String( "\"" ), QLatin1String( "\\\"" ) );
  str.replace( QLatin1String( "\b" ), QLatin1String( "\\b" ) );
  str.replace( QLatin1String( "\f" ), QLatin1String( "\\f" ) );
  str.replace( QLatin1String( "\n" ), QLatin1String( "\\n" ) );
  str.replace( QLatin1String( "\r" ), QLatin1String( "\\r" ) );
  str.replace( QLatin1String( "\t" ), QLatin1String( "\\t" ) );

  return QString( QLatin1String( "\"%1\"" ) ).arg( str );
}

Serializer::Serializer()
  : d( new SerializerPrivate )
{
}

Serializer::~Serializer() {
  delete d;
}

void Serializer::serialize( const QVariant& v, QIODevice* io, bool* ok)
{
  Q_ASSERT( io );
  *ok = true;

  if (!io->isOpen()) {
    if (!io->open(QIODevice::WriteOnly)) {
      d->errorMessage = QLatin1String("Error opening device");
      *ok = false;
      return;
    }
  }

  if (!io->isWritable()) {
    d->errorMessage = QLatin1String("Device is not readable");
    io->close();
    *ok = false;
    return;
  }

  const QByteArray str = serialize( v, ok);
  if (*ok && (io->write(str) != str.count())) {
    *ok = false;
    d->errorMessage = QLatin1String("Something went wrong while writing to IO device");
  }
}

QByteArray Serializer::serialize( const QVariant &v)
{
  bool ok;

  return serialize(v, &ok);
}

QByteArray Serializer::serialize( const QVariant &v, bool *ok)
{
  bool _ok = true;
  d->errorMessage.clear();

  if (ok) {
    *ok = true;
  } else {
    ok = &_ok;
  }

  return d->serialize(v, ok);
}

void QJson::Serializer::allowSpecialNumbers(bool allow) {
  d->specialNumbersAllowed = allow;
}

bool QJson::Serializer::specialNumbersAllowed() const {
  return d->specialNumbersAllowed;
}

void QJson::Serializer::setIndentMode(IndentMode mode) {
  d->indentMode = mode;
}

void QJson::Serializer::setDoublePrecision(int precision) {
  d->doublePrecision = precision;
}

IndentMode QJson::Serializer::indentMode() const {
  return d->indentMode;
}

QString QJson::Serializer::errorMessage() const {
  return d->errorMessage;
}

<commit_msg>Compile fix with -Wall -Werror -Wextra<commit_after>/* This file is part of qjson
  *
  * Copyright (C) 2009 Till Adam <adam@kde.org>
  * Copyright (C) 2009 Flavio Castelli <flavio@castelli.name>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License version 2.1, as published by the Free Software Foundation.
  *
  * This library 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
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public License
  * along with this library; see the file COPYING.LIB.  If not, write to
  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  * Boston, MA 02110-1301, USA.
  */

#include "serializer.h"

#include <QtCore/QDataStream>
#include <QtCore/QStringList>
#include <QtCore/QVariant>

#include <cmath>

#ifdef _MSC_VER  // using MSVC compiler
#include <float.h>
#endif

using namespace QJson;

class Serializer::SerializerPrivate {
  public:
    SerializerPrivate() :
      specialNumbersAllowed(false),
      indentMode(QJson::IndentNone),
      doublePrecision(6) {
        errorMessage.clear();
    }
    QString errorMessage;
    bool specialNumbersAllowed;
    IndentMode indentMode;
    int doublePrecision;
    QByteArray buildIndent(int spaces);
    QByteArray serialize( const QVariant &v, bool *ok, int indentLevel = 0);
    QString sanitizeString( QString str );
    QByteArray join( const QList<QByteArray>& list, const QByteArray& sep );
};

QByteArray Serializer::SerializerPrivate::join( const QList<QByteArray>& list, const QByteArray& sep ) {
  QByteArray res;
  Q_FOREACH( const QByteArray& i, list ) {
    if ( !res.isEmpty() )
      res += sep;
    res += i;
  }
  return res;
}

QByteArray Serializer::SerializerPrivate::serialize( const QVariant &v, bool *ok, int indentLevel)
{
  QByteArray str;

  if ( ! v.isValid() ) { // invalid or null?
    str = "null";
  } else if (( v.type() == QVariant::List ) || ( v.type() == QVariant::StringList )){ // an array or a stringlist?
    const QVariantList list = v.toList();
    QList<QByteArray> values;
    Q_FOREACH( const QVariant& var, list )
    {
      indentLevel++;
      QByteArray serializedValue = serialize( var, ok, indentLevel);
      indentLevel--;
      if ( !*ok ) {
        break;
      }
      values << serializedValue;
    }

    if (indentMode == QJson::IndentMinimum) {
      QByteArray indent = buildIndent(indentLevel - 1);
      str = "[\n" + join( values, ",\n" ) + "\n" + indent + "]";
    }
    else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
      QByteArray indent = buildIndent(indentLevel);
      str = "[\n" + join( values, ",\n" ) + "\n" + indent + "]";
    }
    else if (indentMode == QJson::IndentCompact) {
      str = "[" + join( values, "," ) + "]";
    }
    else {
      str = "[ " + join( values, ", " ) + " ]";
    }

  } else if ( v.type() == QVariant::Map ) { // variant is a map?
    const QVariantMap vmap = v.toMap();
    QMapIterator<QString, QVariant> it( vmap );

    if (indentMode == QJson::IndentMinimum) {
      QByteArray indent = buildIndent(indentLevel);
      str = indent + "{ ";
    }
    else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
      QByteArray indent = buildIndent(indentLevel);
      QByteArray nextindent = buildIndent(indentLevel + 1);
      str = indent + "{\n" + nextindent;
    }
    else if (indentMode == QJson::IndentCompact) {
      str = "{";
    }
    else {
      str = "{ ";
    }

    QList<QByteArray> pairs;
    while ( it.hasNext() ) {
      it.next();
      indentLevel++;
      QByteArray serializedValue = serialize( it.value(), ok, indentLevel);
      indentLevel--;
      if ( !*ok ) {
        break;
      }
      QByteArray key   = sanitizeString( it.key() ).toUtf8();
      QByteArray value = serializedValue;
      if (indentMode == QJson::IndentCompact) {
        pairs << key + ":" + value;
      } else {
        pairs << key + " : " + value;
      }
    }

    if (indentMode == QJson::IndentFull) {
      QByteArray indent = buildIndent(indentLevel + 1);
      str += join( pairs, ",\n" + indent);
    }
    else if (indentMode == QJson::IndentCompact) {
      str += join( pairs, "," );
    }
    else {
      str += join( pairs, ", " );
    }

    if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
      QByteArray indent = buildIndent(indentLevel);
      str += "\n" + indent + "}";
    }
    else if (indentMode == QJson::IndentCompact) {
      str += "}";
    }
    else {
      str += " }";
    }

  } else if ( v.type() == QVariant::Hash ) { // variant is a hash?
    const QVariantHash vhash = v.toHash();
    QHashIterator<QString, QVariant> it( vhash );

    if (indentMode == QJson::IndentMinimum) {
      QByteArray indent = buildIndent(indentLevel);
      str = indent + "{ ";
    }
    else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
      QByteArray indent = buildIndent(indentLevel);
      QByteArray nextindent = buildIndent(indentLevel + 1);
      str = indent + "{\n" + nextindent;
    }
    else if (indentMode == QJson::IndentCompact) {
      str = "{";
    }
    else {
      str = "{ ";
    }

    QList<QByteArray> pairs;
    while ( it.hasNext() ) {
      it.next();
      indentLevel++;
      QByteArray serializedValue = serialize( it.value(), ok, indentLevel);
      indentLevel--;
      if ( !*ok ) {
        break;
      }
      QByteArray key   = sanitizeString( it.key() ).toUtf8();
      QByteArray value = serializedValue;
      if (indentMode == QJson::IndentCompact) {
        pairs << key + ":" + value;
      } else {
        pairs << key + " : " + value;
      }
    }

    if (indentMode == QJson::IndentFull) {
      QByteArray indent = buildIndent(indentLevel + 1);
      str += join( pairs, ",\n" + indent);
    }
    else if (indentMode == QJson::IndentCompact) {
      str += join( pairs, "," );
    }
    else {
      str += join( pairs, ", " );
    }

    if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
      QByteArray indent = buildIndent(indentLevel);
      str += "\n" + indent + "}";
    }
    else if (indentMode == QJson::IndentCompact) {
      str += "}";
    }
    else {
      str += " }";
    }

  } else if (( v.type() == QVariant::String ) ||  ( v.type() == QVariant::ByteArray )) { // a string or a byte array?
    str = sanitizeString( v.toString() ).toUtf8();
  } else if (( v.type() == QVariant::Double) || ((QMetaType::Type)v.type() == QMetaType::Float)) { // a double or a float?
    const double value = v.toDouble();
#if defined _WIN32 && !defined(Q_OS_SYMBIAN)
    const bool special = _isnan(value) || !_finite(value);
#elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID)
    const bool special = isnan(value) || isinf(value);
#else
    const bool special = std::isnan(value) || std::isinf(value);
#endif
    if (special) {
      if (specialNumbersAllowed) {
#if defined _WIN32 && !defined(Q_OS_SYMBIAN)
        if (_isnan(value)) {
#elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID)
        if (isnan(value)) {
#else
        if (std::isnan(value)) {
#endif
          str += "NaN";
        } else {
          if (value<0) {
            str += '-';
          }
          str += "Infinity";
        }
      } else {
        errorMessage += QLatin1String("Attempt to write NaN or infinity, which is not supported by json\n");
        *ok = false;
    }
    } else {
      str = QByteArray::number( value , 'g', doublePrecision);
      if( ! str.contains( "." ) && ! str.contains( "e" ) ) {
        str += ".0";
      }
    }
  } else if ( v.type() == QVariant::Bool ) { // boolean value?
    str = ( v.toBool() ? "true" : "false" );
  } else if ( v.type() == QVariant::ULongLong ) { // large unsigned number?
    str = QByteArray::number( v.value<qulonglong>() );
  } else if ( v.type() == QVariant::UInt ) { // unsigned int number?
    str = QByteArray::number( v.value<quint32>() );
  } else if ( v.canConvert<qlonglong>() ) { // any signed number?
    str = QByteArray::number( v.value<qlonglong>() );
  } else if ( v.canConvert<int>() ) { // unsigned short number?
    str = QByteArray::number( v.value<int>() );
  } else if ( v.canConvert<QString>() ){ // can value be converted to string?
    // this will catch QDate, QDateTime, QUrl, ...
    str = sanitizeString( v.toString() ).toUtf8();
    //TODO: catch other values like QImage, QRect, ...
  } else {
    *ok = false;
    errorMessage += QLatin1String("Cannot serialize ");
    errorMessage += v.toString();
    errorMessage += QLatin1String(" because type ");
    errorMessage += QLatin1String(v.typeName());
    errorMessage += QLatin1String(" is not supported by QJson\n");
  }
  if ( *ok )
  {
    return str;
  }
  else
    return QByteArray();
}

QByteArray Serializer::SerializerPrivate::buildIndent(int spaces)
{
   QByteArray indent;
   if (spaces < 0) {
     spaces = 0;
   }
   for (int i = 0; i < spaces; i++ ) {
     indent += " ";
   }
   return indent;
}

QString Serializer::SerializerPrivate::sanitizeString( QString str )
{
  str.replace( QLatin1String( "\\" ), QLatin1String( "\\\\" ) );

  // escape unicode chars
  QString result;
  const ushort* unicode = str.utf16();
  unsigned int i = 0;

  while ( unicode[ i ] ) {
    if ( unicode[ i ] < 128 ) {
      result.append( QChar( unicode[ i ] ) );
    }
    else {
      QString hexCode = QString::number( unicode[ i ], 16 ).rightJustified( 4,
                                                           QLatin1Char('0') );

      result.append( QLatin1String ("\\u") ).append( hexCode );
    }
    ++i;
  }
  str = result;

  str.replace( QLatin1String( "\"" ), QLatin1String( "\\\"" ) );
  str.replace( QLatin1String( "\b" ), QLatin1String( "\\b" ) );
  str.replace( QLatin1String( "\f" ), QLatin1String( "\\f" ) );
  str.replace( QLatin1String( "\n" ), QLatin1String( "\\n" ) );
  str.replace( QLatin1String( "\r" ), QLatin1String( "\\r" ) );
  str.replace( QLatin1String( "\t" ), QLatin1String( "\\t" ) );

  return QString( QLatin1String( "\"%1\"" ) ).arg( str );
}

Serializer::Serializer()
  : d( new SerializerPrivate )
{
}

Serializer::~Serializer() {
  delete d;
}

void Serializer::serialize( const QVariant& v, QIODevice* io, bool* ok)
{
  Q_ASSERT( io );
  *ok = true;

  if (!io->isOpen()) {
    if (!io->open(QIODevice::WriteOnly)) {
      d->errorMessage = QLatin1String("Error opening device");
      *ok = false;
      return;
    }
  }

  if (!io->isWritable()) {
    d->errorMessage = QLatin1String("Device is not readable");
    io->close();
    *ok = false;
    return;
  }

  const QByteArray str = serialize( v, ok);
  if (*ok && (io->write(str) != str.count())) {
    *ok = false;
    d->errorMessage = QLatin1String("Something went wrong while writing to IO device");
  }
}

QByteArray Serializer::serialize( const QVariant &v)
{
  bool ok;

  return serialize(v, &ok);
}

QByteArray Serializer::serialize( const QVariant &v, bool *ok)
{
  bool _ok = true;
  d->errorMessage.clear();

  if (ok) {
    *ok = true;
  } else {
    ok = &_ok;
  }

  return d->serialize(v, ok);
}

void QJson::Serializer::allowSpecialNumbers(bool allow) {
  d->specialNumbersAllowed = allow;
}

bool QJson::Serializer::specialNumbersAllowed() const {
  return d->specialNumbersAllowed;
}

void QJson::Serializer::setIndentMode(IndentMode mode) {
  d->indentMode = mode;
}

void QJson::Serializer::setDoublePrecision(int precision) {
  d->doublePrecision = precision;
}

IndentMode QJson::Serializer::indentMode() const {
  return d->indentMode;
}

QString QJson::Serializer::errorMessage() const {
  return d->errorMessage;
}

<|endoftext|>


<commit_before>#include <string.h>
#include <stdio.h>

#include "httpServer.h"
#include "logging.h"

HttpServer::HttpServer(int portNr)
{
    listenSocket.listen(portNr);
    selector.add(listenSocket);
}

HttpServer::~HttpServer()
{
    listenSocket.close();
    for(unsigned int n=0; n<connections.size(); n++)
        delete connections[n];
    for(unsigned int n=0; n<handlers.size(); n++)
        delete handlers[n];
}

void HttpServer::update(float delta)
{
    if (selector.wait(sf::microseconds(1)))
    {
        if (selector.isReady(listenSocket))
        {
            HttpServerConnection* connection = new HttpServerConnection(this);
            if (listenSocket.accept(connection->socket) == sf::Socket::Done)
            {
                connections.push_back(connection);
                selector.add(connection->socket);
            }else{
                delete connection;
            }
        }
        for(unsigned int n=0; n<connections.size(); n++)
        {
            if (selector.isReady(connections[n]->socket))
            {
                if (!connections[n]->read())
                {
                    selector.remove(connections[n]->socket);
                    delete connections[n];
                    connections.erase(connections.begin() + n);
                }
            }
        }
    }
}

HttpServerConnection::HttpServerConnection(HttpServer* server)
: server(server)
{
    recvBufferCount = 0;
    status = METHOD;
}

bool HttpServerConnection::read()
{
    char buffer[1024];
    size_t size;
    if (socket.receive(buffer, sizeof(buffer), size) != sf::Socket::Done)
        return false;
    if (recvBufferCount + size > recvBufferSize)
        size = recvBufferSize - recvBufferCount;
    if (size < 1)
        return false;
    memcpy(recvBuffer + recvBufferCount, buffer, size);
    recvBufferCount += size;

    while(true)
    {
        char* ptr = (char*)memchr(recvBuffer, '\n', recvBufferCount);
        if (!ptr)
            break;
        *ptr = '\0';
        string line(recvBuffer);
        ptr++;
        size_t len = ptr - recvBuffer;
        recvBufferCount -= len;
        memmove(recvBuffer, ptr, recvBufferCount);
        if (line.endswith("\r"))
            line = line.substr(0, -1);
        if (!handleLine(line))
            return false;
    }
    return true;
}

string HttpServerConnection::UriDecode(const string & sSrc)
{
   // Note from RFC1630: "Sequences which start with a percent
   // sign but are not followed by two hexadecimal characters
   // (0-9, A-F) are reserved for future extension"

   const unsigned char * pSrc = (const unsigned char *)sSrc.c_str();
   const int SRC_LEN = sSrc.length();
   const unsigned char * const SRC_END = pSrc + SRC_LEN;
   // const char DEC2HEX[16 + 1] = "0123456789ABCDEF";
   // last decodable '%'
   const unsigned char * const SRC_LAST_DEC = SRC_END - 2;

   char * const pStart = new char[SRC_LEN];
   char * pEnd = pStart;

   while (pSrc < SRC_LAST_DEC)
   {
      if (*pSrc == '%')
      {
         char dec1, dec2;
         if (-1 != (dec1 = HEX2DEC[*(pSrc + 1)])
            && -1 != (dec2 = HEX2DEC[*(pSrc + 2)]))
         {
            *pEnd++ = (dec1 << 4) + dec2;
            pSrc += 3;
            continue;
         }
      }

      *pEnd++ = *pSrc++;
   }

   // the last 2- chars
   while (pSrc < SRC_END)
      *pEnd++ = *pSrc++;

   std::string sResult(pStart, pEnd);
   delete [] pStart;
   return (string) sResult;
}

void HttpServerConnection::parseUri(const string & sSrc)
{
    string uri = UriDecode(sSrc);
    std::size_t found = uri.find('?');
    if (found==std::string::npos)
    {
        request.path = uri;
        return;
    }
    else
    {
        std::vector<string> parts = uri.split("?", 1);
        request.path = parts[0];

        std::vector<string> parameters = parts[1].split("&");
        for (unsigned int n=0; n<parameters.size(); n++)
        {
            string param = parameters[n];
            std::size_t found = param.find('=');
            if (found==std::string::npos)
            {
                request.parameters[param] = sFALSE;
                LOG(DEBUG) << "HTTP Parameter: " << param;
            }
            else
            {
                if (param.endswith('='))
                {
                    request.parameters[param.substr(0, param.length()-1)] = sFALSE;
                    LOG(DEBUG) << "HTTP Parameter: " << param.substr(0, param.length()-1);
                }
                else
                {
                    std::vector<string> items = param.split("=", 1);
                    request.parameters[items[0]] = items[1];
                    LOG(DEBUG) << "HTTP Parameter: " << items[0] << " = " << items[1];
                }
            }
        }
    }
    LOG(DEBUG) << "HTTP Path: " << request.path;
}

bool HttpServerConnection::handleLine(string line)
{
    switch(status)
    {
    case METHOD:{
        std::vector<string> parts = line.split();
        if (parts.size() != 3)
            return false;
        request.method = parts[0];
        parseUri(parts[1]);
        //request.path = parts[1];
        status = HEADERS;
        }break;
    case HEADERS:
        if (line.length() == 0)
        {
            request.post_data = "";
            if (request.method == "POST")
            {
                if (request.headers.find("content-length") != request.headers.end())
                {
                    unsigned int body_length = request.headers["content-length"].toInt();
                    if (body_length > recvBufferSize)
                        return false;
                    while (body_length > recvBufferCount)
                    {
                        size_t received;
                        if (socket.receive(recvBuffer + recvBufferCount, body_length - recvBufferCount, received) != sf::Socket::Done)
                            return false;
                        recvBufferCount += received;
                    }
                    request.post_data = string(recvBuffer, body_length);
                    recvBufferCount -= body_length;
                    memmove(recvBuffer, recvBuffer + body_length, recvBufferCount);
                }
            }
            status = METHOD;
            LOG(DEBUG) << "HTTP request: " << request.path;
#ifdef DEBUG
            for (std::map<string, string>::iterator iter = request.headers.begin(); iter != request.headers.end(); iter++)
            {
                string key=iter->first;
                string value=iter->second;
                LOG(DEBUG) << "HTTP header: (" << key << ", " << value << ")";
            }
#endif // DEBUG
            handleRequest();
        }else{
            std::vector<string> parts = line.split(":", 1);
            if (parts.size() != 2)
                LOG(WARNING) << "Invalid HTTP header: " << line;
            else
                request.headers[parts[0].strip().lower()] = parts[1];
        }
        break;
    }
    return true;
}

void HttpServerConnection::handleRequest()
{
    reply_code = 200;
    headers_send = false;

    for(unsigned int n=0; n<server->handlers.size(); n++)
    {
        if (server->handlers[n]->handleRequest(request, this))
            break;
        if (headers_send)
            break;
    }

    if (!headers_send)
    {
        reply_code = 404;
        string replyData = "File not found";
        sendData(replyData.c_str(), replyData.size());
    }
    string end_chunk = "0\r\n\r\n";
    socket.send(end_chunk.c_str(), end_chunk.size());
    request.parameters.clear();
}

void HttpServerConnection::sendHeaders()
{
    string reply = string("HTTP/1.1 ") + string(reply_code) + " OK\r\n";
    reply += "Content-type: text/html\r\n";
    reply += "Connection: Keep-Alive\r\n";
    reply += "Transfer-Encoding: chunked\r\n";
    reply += "\r\n";
    socket.send(reply.c_str(), reply.size());
    headers_send = true;
}

void HttpServerConnection::sendData(const char* data, size_t data_length)
{
    if (!headers_send)
        sendHeaders();
    if (data_length < 1)
        return;
    string chunk_len_string = string::hex(data_length) + "\r\n";
    socket.send(chunk_len_string.c_str(), chunk_len_string.size());
    socket.send(data, data_length);
    socket.send("\r\n", 2);
}

const char HttpServerConnection::HEX2DEC[256] =
{
    /*       0  1  2  3   4  5  6  7   8  9  A  B   C  D  E  F */
    /* 0 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 1 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 2 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 3 */  0, 1, 2, 3,  4, 5, 6, 7,  8, 9,-1,-1, -1,-1,-1,-1,

    /* 4 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 5 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 6 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 7 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,

    /* 8 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 9 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* A */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* B */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,

    /* C */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* D */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* E */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* F */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
};

bool HttpRequestFileHandler::handleRequest(HttpRequest& request, HttpServerConnection* connection)
{
    string replyData = "";
    FILE* f = NULL;
    if (request.path == "/")
        request.path = "/index.html";
    if (request.path.find("..") != -1)
        return false;

    string fullPath = base_path + request.path;
    f = fopen(fullPath.c_str(), "rb");
    if (!f)
        return false;

    while(true)
    {
        char buffer[1024];
        size_t n = fread(buffer, 1, sizeof(buffer), f);
        if (n < 1)
            break;
        connection->sendData(buffer, n);
    }
    fclose(f);
    return true;
}
<commit_msg>Added documentation<commit_after>#include <string.h>
#include <stdio.h>

#include "httpServer.h"
#include "logging.h"

HttpServer::HttpServer(int portNr)
{
    listenSocket.listen(portNr);
    selector.add(listenSocket);
}

HttpServer::~HttpServer()
{
    listenSocket.close();
    for(unsigned int n=0; n<connections.size(); n++)
        delete connections[n];
    for(unsigned int n=0; n<handlers.size(); n++)
        delete handlers[n];
}

void HttpServer::update(float delta)
{
    if (selector.wait(sf::microseconds(1)))
    {
        if (selector.isReady(listenSocket))
        {
            HttpServerConnection* connection = new HttpServerConnection(this);
            if (listenSocket.accept(connection->socket) == sf::Socket::Done)
            {
                connections.push_back(connection);
                selector.add(connection->socket);
            }else{
                delete connection;
            }
        }
        for(unsigned int n=0; n<connections.size(); n++)
        {
            if (selector.isReady(connections[n]->socket))
            {
                if (!connections[n]->read())
                {
                    selector.remove(connections[n]->socket);
                    delete connections[n];
                    connections.erase(connections.begin() + n);
                }
            }
        }
    }
}

HttpServerConnection::HttpServerConnection(HttpServer* server)
: server(server)
{
    recvBufferCount = 0;
    status = METHOD;
}

bool HttpServerConnection::read()
{
    char buffer[1024];
    size_t size;
    if (socket.receive(buffer, sizeof(buffer), size) != sf::Socket::Done)
        return false;
    if (recvBufferCount + size > recvBufferSize)
        size = recvBufferSize - recvBufferCount;
    if (size < 1)
        return false;
    memcpy(recvBuffer + recvBufferCount, buffer, size);
    recvBufferCount += size;

    while(true)
    {
        char* ptr = (char*)memchr(recvBuffer, '\n', recvBufferCount);
        if (!ptr)
            break;
        *ptr = '\0';
        string line(recvBuffer);
        ptr++;
        size_t len = ptr - recvBuffer;
        recvBufferCount -= len;
        memmove(recvBuffer, ptr, recvBufferCount);
        if (line.endswith("\r"))
            line = line.substr(0, -1);
        if (!handleLine(line))
            return false;
    }
    return true;
}

/** \brief Decode a percent-encoded URI
 * Uri decoding according to RFC1630, RFC1738, RFC2396
 * Credits: Jin Qing
 * \param sSrc const string&   Percent-encoded URI
 * \return string              Decoded URI-string
 *
 */
string HttpServerConnection::UriDecode(const string & sSrc)
{
   // Note from RFC1630: "Sequences which start with a percent
   // sign but are not followed by two hexadecimal characters
   // (0-9, A-F) are reserved for future extension"

   const unsigned char * pSrc = (const unsigned char *)sSrc.c_str();
   const int SRC_LEN = sSrc.length();
   const unsigned char * const SRC_END = pSrc + SRC_LEN;
   // last decodable '%'
   const unsigned char * const SRC_LAST_DEC = SRC_END - 2;

   char * const pStart = new char[SRC_LEN];
   char * pEnd = pStart;

   while (pSrc < SRC_LAST_DEC)
   {
      if (*pSrc == '%')
      {
         char dec1, dec2;
         if (-1 != (dec1 = HEX2DEC[*(pSrc + 1)])
            && -1 != (dec2 = HEX2DEC[*(pSrc + 2)]))
         {
            *pEnd++ = (dec1 << 4) + dec2;
            pSrc += 3;
            continue;
         }
      }

      *pEnd++ = *pSrc++;
   }

   // the last 2- chars
   while (pSrc < SRC_END)
      *pEnd++ = *pSrc++;

   std::string sResult(pStart, pEnd);
   delete [] pStart;
   return (string) sResult;
}

/**< Map to convert between character encodings */
const char HttpServerConnection::HEX2DEC[256] =
{
    /*       0  1  2  3   4  5  6  7   8  9  A  B   C  D  E  F */
    /* 0 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 1 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 2 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 3 */  0, 1, 2, 3,  4, 5, 6, 7,  8, 9,-1,-1, -1,-1,-1,-1,

    /* 4 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 5 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 6 */ -1,10,11,12, 13,14,15,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 7 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,

    /* 8 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* 9 */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* A */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* B */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,

    /* C */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* D */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* E */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
    /* F */ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1
};

/** \brief Parse a URL, splitting it in its part and optional parameters
 *
 * \param sSrc const string&  URL
 * \return void
 *
 */
void HttpServerConnection::parseUri(const string & sSrc)
{
    string uri = UriDecode(sSrc);
    std::size_t found = uri.find('?');
    if (found==std::string::npos)
    {
        request.path = uri;
        return;
    }
    else
    {
        std::vector<string> parts = uri.split("?", 1);
        request.path = parts[0];

        std::vector<string> parameters = parts[1].split("&");
        for (unsigned int n=0; n<parameters.size(); n++)
        {
            string param = parameters[n];
            std::size_t found = param.find('=');
            if (found==std::string::npos)
            {
                request.parameters[param] = sFALSE;
                LOG(DEBUG) << "HTTP Parameter: " << param;
            }
            else
            {
                if (param.endswith('='))
                {
                    request.parameters[param.substr(0, param.length()-1)] = sFALSE;
                    LOG(DEBUG) << "HTTP Parameter: " << param.substr(0, param.length()-1);
                }
                else
                {
                    std::vector<string> items = param.split("=", 1);
                    request.parameters[items[0]] = items[1];
                    LOG(DEBUG) << "HTTP Parameter: " << items[0] << " = " << items[1];
                }
            }
        }
    }
    LOG(DEBUG) << "HTTP Path: " << request.path;
}

bool HttpServerConnection::handleLine(string line)
{
    switch(status)
    {
    case METHOD:{
        std::vector<string> parts = line.split();
        if (parts.size() != 3)
            return false;
        request.method = parts[0];
        parseUri(parts[1]);
        //request.path = parts[1];
        status = HEADERS;
        }break;
    case HEADERS:
        if (line.length() == 0)
        {
            request.post_data = "";
            if (request.method == "POST")
            {
                if (request.headers.find("content-length") != request.headers.end())
                {
                    unsigned int body_length = request.headers["content-length"].toInt();
                    if (body_length > recvBufferSize)
                        return false;
                    while (body_length > recvBufferCount)
                    {
                        size_t received;
                        if (socket.receive(recvBuffer + recvBufferCount, body_length - recvBufferCount, received) != sf::Socket::Done)
                            return false;
                        recvBufferCount += received;
                    }
                    request.post_data = string(recvBuffer, body_length);
                    recvBufferCount -= body_length;
                    memmove(recvBuffer, recvBuffer + body_length, recvBufferCount);
                }
            }
            status = METHOD;
#ifdef DEBUG
            for (std::map<string, string>::iterator iter = request.headers.begin(); iter != request.headers.end(); iter++)
            {
                string key=iter->first;
                string value=iter->second;
                LOG(DEBUG) << "HTTP header: (" << key << ", " << value << ")";
            }
#endif // DEBUG
            handleRequest();
        }else{
            std::vector<string> parts = line.split(":", 1);
            if (parts.size() != 2)
                LOG(WARNING) << "Invalid HTTP header: " << line;
            else
                request.headers[parts[0].strip().lower()] = parts[1];
        }
        break;
    }
    return true;
}

void HttpServerConnection::handleRequest()
{
    reply_code = 200;
    headers_send = false;

    for(unsigned int n=0; n<server->handlers.size(); n++)
    {
        if (server->handlers[n]->handleRequest(request, this))
            break;
        if (headers_send)
            break;
    }

    if (!headers_send)
    {
        reply_code = 404;
        string replyData = "File not found";
        sendData(replyData.c_str(), replyData.size());
    }
    string end_chunk = "0\r\n\r\n";
    socket.send(end_chunk.c_str(), end_chunk.size());
    request.parameters.clear();
}

void HttpServerConnection::sendHeaders()
{
    string reply = string("HTTP/1.1 ") + string(reply_code) + " OK\r\n";
    reply += "Content-type: text/html\r\n";
    reply += "Connection: Keep-Alive\r\n";
    reply += "Transfer-Encoding: chunked\r\n";
    reply += "\r\n";
    socket.send(reply.c_str(), reply.size());
    headers_send = true;
}

void HttpServerConnection::sendData(const char* data, size_t data_length)
{
    if (!headers_send)
        sendHeaders();
    if (data_length < 1)
        return;
    string chunk_len_string = string::hex(data_length) + "\r\n";
    socket.send(chunk_len_string.c_str(), chunk_len_string.size());
    socket.send(data, data_length);
    socket.send("\r\n", 2);
}

bool HttpRequestFileHandler::handleRequest(HttpRequest& request, HttpServerConnection* connection)
{
    string replyData = "";
    FILE* f = NULL;
    if (request.path == "/")
        request.path = "/index.html";
    if (request.path.find("..") != -1)
        return false;

    string fullPath = base_path + request.path;
    f = fopen(fullPath.c_str(), "rb");
    if (!f)
        return false;

    while(true)
    {
        char buffer[1024];
        size_t n = fread(buffer, 1, sizeof(buffer), f);
        if (n < 1)
            break;
        connection->sendData(buffer, n);
    }
    fclose(f);
    return true;
}
<|endoftext|>


<commit_before>/*
 * SSL and TLS filter.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "ssl_filter.hxx"
#include "ssl_factory.hxx"
#include "ssl_config.hxx"
#include "ssl_quark.hxx"
#include "thread_socket_filter.hxx"
#include "pool.hxx"
#include "gerrno.h"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"

#include <openssl/ssl.h>
#include <openssl/err.h>

#include <assert.h>
#include <string.h>

struct ssl_filter {
    /**
     * Buffers which can be accessed from within the thread without
     * holding locks.  These will be copied to/from the accordding
     * #thread_socket_filter buffers.
     */
    SliceFifoBuffer decrypted_input, plain_output;

    /**
     * Memory BIOs for passing data to/from OpenSSL.
     */
    BIO *encrypted_input, *encrypted_output;

    SSL *ssl;

    bool handshaking;

    char *peer_subject, *peer_issuer_subject;
};

static void
ssl_set_error(GError **error_r)
{
    if (error_r == NULL)
        return;

    unsigned long error = ERR_get_error();
    char buffer[120];
    g_set_error(error_r, ssl_quark(), 0, "%s",
                ERR_error_string(error, buffer));
}

/**
 * Move data from #src to #dest.
 */
static void
Move(ForeignFifoBuffer<uint8_t> &dest, ForeignFifoBuffer<uint8_t> &src)
{
    auto r = src.Read();
    if (r.IsEmpty())
        return;

    auto w = dest.Write();
    if (w.IsEmpty())
        return;

    size_t nbytes = std::min(r.size, w.size);

    memcpy(w.data, r.data, nbytes);
    dest.Append(nbytes);
    src.Consume(nbytes);
}

/**
 * Move data from #src to #dest.
 */
static void
Move(BIO *dest, ForeignFifoBuffer<uint8_t> &src)
{
    auto r = src.Read();
    if (r.IsEmpty())
        return;

    int nbytes = BIO_write(dest, r.data, r.size);
    if (nbytes > 0)
        src.Consume(nbytes);
}

static void
Move(ForeignFifoBuffer<uint8_t> &dest, BIO *src)
{
    while (true) {
        auto w = dest.Write();
        if (w.IsEmpty())
            return;

        int nbytes = BIO_read(src, w.data, w.size);
        if (nbytes <= 0)
            return;

        dest.Append(nbytes);
    }
}

static char *
format_name(X509_NAME *name)
{
    if (name == NULL)
        return NULL;

    BIO *bio = BIO_new(BIO_s_mem());
    if (bio == NULL)
        return NULL;

    X509_NAME_print_ex(bio, name, 0,
                       ASN1_STRFLGS_UTF8_CONVERT | XN_FLAG_SEP_COMMA_PLUS);
    char buffer[1024];
    int length = BIO_read(bio, buffer, sizeof(buffer) - 1);
    BIO_free(bio);

    return strndup(buffer, length);
}

static char *
format_subject_name(X509 *cert)
{
    return format_name(X509_get_subject_name(cert));
}

static char *
format_issuer_subject_name(X509 *cert)
{
    return format_name(X509_get_issuer_name(cert));
}

gcc_const
static bool
is_ssl_error(SSL *ssl, int ret)
{
    switch (SSL_get_error(ssl, ret)) {
    case SSL_ERROR_NONE:
    case SSL_ERROR_WANT_READ:
    case SSL_ERROR_WANT_WRITE:
    case SSL_ERROR_WANT_CONNECT:
    case SSL_ERROR_WANT_ACCEPT:
        return false;

    default:
        return true;
    }
}

static bool
check_ssl_error(SSL *ssl, int result, GError **error_r)
{
    if (is_ssl_error(ssl, result)) {
        ssl_set_error(error_r);
        return false;
    } else
        return true;
}

/**
 * @return false on error
 */
static bool
ssl_decrypt(SSL *ssl, ForeignFifoBuffer<uint8_t> &buffer, GError **error_r)
{
    /* SSL_read() must be called repeatedly until there is no more
       data (or until the buffer is full) */

    while (true) {
        auto w = buffer.Write();
        if (w.IsEmpty())
            return 0;

        int result = SSL_read(ssl, w.data, w.size);
        if (result <= 0)
            return result == 0 || check_ssl_error(ssl, result, error_r);

        buffer.Append(result);
    }
}

/**
 * @return false on error
 */
static bool
ssl_encrypt(SSL *ssl, ForeignFifoBuffer<uint8_t> &buffer, GError **error_r)
{
    auto r = buffer.Read();
    if (r.IsEmpty())
        return true;

    int result = SSL_write(ssl, r.data, r.size);
    if (result < 0 && !check_ssl_error(ssl, result, error_r))
        return false;

    if (result > 0)
        buffer.Consume(result);

    return true;
}

/*
 * thread_socket_filter_handler
 *
 */

static bool
ssl_thread_socket_filter_run(ThreadSocketFilter &f, GError **error_r,
                             void *ctx)
{
    struct ssl_filter *ssl = (struct ssl_filter *)ctx;

    /* copy input (and output to make room for more output) */

    pthread_mutex_lock(&f.mutex);
    Move(f.decrypted_input, ssl->decrypted_input);
    Move(ssl->plain_output, f.plain_output);
    Move(ssl->encrypted_input, f.encrypted_input);
    Move(f.encrypted_output, ssl->encrypted_output);
    pthread_mutex_unlock(&f.mutex);

    /* let OpenSSL work */

    ERR_clear_error();

    if (gcc_unlikely(ssl->handshaking)) {
        int result = SSL_do_handshake(ssl->ssl);
        if (result == 1) {
            ssl->handshaking = false;

            X509 *cert = SSL_get_peer_certificate(ssl->ssl);
            if (cert != nullptr) {
                ssl->peer_subject = format_subject_name(cert);
                ssl->peer_issuer_subject = format_issuer_subject_name(cert);
                X509_free(cert);
            }
        } else if (result == 0) {
            ssl_set_error(error_r);
            return false;
        } else if (!check_ssl_error(ssl->ssl, result, error_r))
            return false;
    }

    if (gcc_likely(!ssl->handshaking) &&
        (!ssl_encrypt(ssl->ssl, ssl->plain_output, error_r) ||
         !ssl_decrypt(ssl->ssl, ssl->decrypted_input, error_r)))
        return false;

    /* copy output */

    pthread_mutex_lock(&f.mutex);
    Move(f.decrypted_input, ssl->decrypted_input);
    Move(f.encrypted_output, ssl->encrypted_output);
    f.drained = ssl->plain_output.IsEmpty() &&
        BIO_eof(ssl->encrypted_output);
    pthread_mutex_unlock(&f.mutex);

    return true;
}

static void
ssl_thread_socket_filter_destroy(gcc_unused ThreadSocketFilter &f, void *ctx)
{
    struct ssl_filter *ssl = (struct ssl_filter *)ctx;

    if (ssl->ssl != NULL)
        SSL_free(ssl->ssl);

    ssl->decrypted_input.Free(fb_pool_get());
    ssl->plain_output.Free(fb_pool_get());

    free(ssl->peer_subject);
    free(ssl->peer_issuer_subject);
}

const struct ThreadSocketFilterHandler ssl_thread_socket_filter = {
    ssl_thread_socket_filter_run,
    ssl_thread_socket_filter_destroy,
};

/*
 * constructor
 *
 */

struct ssl_filter *
ssl_filter_new(struct pool *pool, ssl_factory &factory,
               GError **error_r)
{
    assert(pool != NULL);

    ssl_filter *ssl = NewFromPool<ssl_filter>(*pool);

    ssl->ssl = ssl_factory_make(factory);
    if (ssl->ssl == NULL) {
        g_set_error(error_r, ssl_quark(), 0, "SSL_new() failed");
        return NULL;
    }

    ssl->decrypted_input.Allocate(fb_pool_get());
    ssl->plain_output.Allocate(fb_pool_get());
    ssl->encrypted_input = BIO_new(BIO_s_mem());
    ssl->encrypted_output = BIO_new(BIO_s_mem());

    SSL_set_bio(ssl->ssl, ssl->encrypted_input, ssl->encrypted_output);

    ssl->peer_subject = NULL;
    ssl->peer_issuer_subject = NULL;
    ssl->handshaking = true;

    return ssl;
}

const char *
ssl_filter_get_peer_subject(struct ssl_filter *ssl)
{
    assert(ssl != NULL);

    return ssl->peer_subject;
}

const char *
ssl_filter_get_peer_issuer_subject(struct ssl_filter *ssl)
{
    assert(ssl != NULL);

    return ssl->peer_issuer_subject;
}
<commit_msg>ssl_filter: fix comment typo<commit_after>/*
 * SSL and TLS filter.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "ssl_filter.hxx"
#include "ssl_factory.hxx"
#include "ssl_config.hxx"
#include "ssl_quark.hxx"
#include "thread_socket_filter.hxx"
#include "pool.hxx"
#include "gerrno.h"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"

#include <openssl/ssl.h>
#include <openssl/err.h>

#include <assert.h>
#include <string.h>

struct ssl_filter {
    /**
     * Buffers which can be accessed from within the thread without
     * holding locks.  These will be copied to/from the according
     * #thread_socket_filter buffers.
     */
    SliceFifoBuffer decrypted_input, plain_output;

    /**
     * Memory BIOs for passing data to/from OpenSSL.
     */
    BIO *encrypted_input, *encrypted_output;

    SSL *ssl;

    bool handshaking;

    char *peer_subject, *peer_issuer_subject;
};

static void
ssl_set_error(GError **error_r)
{
    if (error_r == NULL)
        return;

    unsigned long error = ERR_get_error();
    char buffer[120];
    g_set_error(error_r, ssl_quark(), 0, "%s",
                ERR_error_string(error, buffer));
}

/**
 * Move data from #src to #dest.
 */
static void
Move(ForeignFifoBuffer<uint8_t> &dest, ForeignFifoBuffer<uint8_t> &src)
{
    auto r = src.Read();
    if (r.IsEmpty())
        return;

    auto w = dest.Write();
    if (w.IsEmpty())
        return;

    size_t nbytes = std::min(r.size, w.size);

    memcpy(w.data, r.data, nbytes);
    dest.Append(nbytes);
    src.Consume(nbytes);
}

/**
 * Move data from #src to #dest.
 */
static void
Move(BIO *dest, ForeignFifoBuffer<uint8_t> &src)
{
    auto r = src.Read();
    if (r.IsEmpty())
        return;

    int nbytes = BIO_write(dest, r.data, r.size);
    if (nbytes > 0)
        src.Consume(nbytes);
}

static void
Move(ForeignFifoBuffer<uint8_t> &dest, BIO *src)
{
    while (true) {
        auto w = dest.Write();
        if (w.IsEmpty())
            return;

        int nbytes = BIO_read(src, w.data, w.size);
        if (nbytes <= 0)
            return;

        dest.Append(nbytes);
    }
}

static char *
format_name(X509_NAME *name)
{
    if (name == NULL)
        return NULL;

    BIO *bio = BIO_new(BIO_s_mem());
    if (bio == NULL)
        return NULL;

    X509_NAME_print_ex(bio, name, 0,
                       ASN1_STRFLGS_UTF8_CONVERT | XN_FLAG_SEP_COMMA_PLUS);
    char buffer[1024];
    int length = BIO_read(bio, buffer, sizeof(buffer) - 1);
    BIO_free(bio);

    return strndup(buffer, length);
}

static char *
format_subject_name(X509 *cert)
{
    return format_name(X509_get_subject_name(cert));
}

static char *
format_issuer_subject_name(X509 *cert)
{
    return format_name(X509_get_issuer_name(cert));
}

gcc_const
static bool
is_ssl_error(SSL *ssl, int ret)
{
    switch (SSL_get_error(ssl, ret)) {
    case SSL_ERROR_NONE:
    case SSL_ERROR_WANT_READ:
    case SSL_ERROR_WANT_WRITE:
    case SSL_ERROR_WANT_CONNECT:
    case SSL_ERROR_WANT_ACCEPT:
        return false;

    default:
        return true;
    }
}

static bool
check_ssl_error(SSL *ssl, int result, GError **error_r)
{
    if (is_ssl_error(ssl, result)) {
        ssl_set_error(error_r);
        return false;
    } else
        return true;
}

/**
 * @return false on error
 */
static bool
ssl_decrypt(SSL *ssl, ForeignFifoBuffer<uint8_t> &buffer, GError **error_r)
{
    /* SSL_read() must be called repeatedly until there is no more
       data (or until the buffer is full) */

    while (true) {
        auto w = buffer.Write();
        if (w.IsEmpty())
            return 0;

        int result = SSL_read(ssl, w.data, w.size);
        if (result <= 0)
            return result == 0 || check_ssl_error(ssl, result, error_r);

        buffer.Append(result);
    }
}

/**
 * @return false on error
 */
static bool
ssl_encrypt(SSL *ssl, ForeignFifoBuffer<uint8_t> &buffer, GError **error_r)
{
    auto r = buffer.Read();
    if (r.IsEmpty())
        return true;

    int result = SSL_write(ssl, r.data, r.size);
    if (result < 0 && !check_ssl_error(ssl, result, error_r))
        return false;

    if (result > 0)
        buffer.Consume(result);

    return true;
}

/*
 * thread_socket_filter_handler
 *
 */

static bool
ssl_thread_socket_filter_run(ThreadSocketFilter &f, GError **error_r,
                             void *ctx)
{
    struct ssl_filter *ssl = (struct ssl_filter *)ctx;

    /* copy input (and output to make room for more output) */

    pthread_mutex_lock(&f.mutex);
    Move(f.decrypted_input, ssl->decrypted_input);
    Move(ssl->plain_output, f.plain_output);
    Move(ssl->encrypted_input, f.encrypted_input);
    Move(f.encrypted_output, ssl->encrypted_output);
    pthread_mutex_unlock(&f.mutex);

    /* let OpenSSL work */

    ERR_clear_error();

    if (gcc_unlikely(ssl->handshaking)) {
        int result = SSL_do_handshake(ssl->ssl);
        if (result == 1) {
            ssl->handshaking = false;

            X509 *cert = SSL_get_peer_certificate(ssl->ssl);
            if (cert != nullptr) {
                ssl->peer_subject = format_subject_name(cert);
                ssl->peer_issuer_subject = format_issuer_subject_name(cert);
                X509_free(cert);
            }
        } else if (result == 0) {
            ssl_set_error(error_r);
            return false;
        } else if (!check_ssl_error(ssl->ssl, result, error_r))
            return false;
    }

    if (gcc_likely(!ssl->handshaking) &&
        (!ssl_encrypt(ssl->ssl, ssl->plain_output, error_r) ||
         !ssl_decrypt(ssl->ssl, ssl->decrypted_input, error_r)))
        return false;

    /* copy output */

    pthread_mutex_lock(&f.mutex);
    Move(f.decrypted_input, ssl->decrypted_input);
    Move(f.encrypted_output, ssl->encrypted_output);
    f.drained = ssl->plain_output.IsEmpty() &&
        BIO_eof(ssl->encrypted_output);
    pthread_mutex_unlock(&f.mutex);

    return true;
}

static void
ssl_thread_socket_filter_destroy(gcc_unused ThreadSocketFilter &f, void *ctx)
{
    struct ssl_filter *ssl = (struct ssl_filter *)ctx;

    if (ssl->ssl != NULL)
        SSL_free(ssl->ssl);

    ssl->decrypted_input.Free(fb_pool_get());
    ssl->plain_output.Free(fb_pool_get());

    free(ssl->peer_subject);
    free(ssl->peer_issuer_subject);
}

const struct ThreadSocketFilterHandler ssl_thread_socket_filter = {
    ssl_thread_socket_filter_run,
    ssl_thread_socket_filter_destroy,
};

/*
 * constructor
 *
 */

struct ssl_filter *
ssl_filter_new(struct pool *pool, ssl_factory &factory,
               GError **error_r)
{
    assert(pool != NULL);

    ssl_filter *ssl = NewFromPool<ssl_filter>(*pool);

    ssl->ssl = ssl_factory_make(factory);
    if (ssl->ssl == NULL) {
        g_set_error(error_r, ssl_quark(), 0, "SSL_new() failed");
        return NULL;
    }

    ssl->decrypted_input.Allocate(fb_pool_get());
    ssl->plain_output.Allocate(fb_pool_get());
    ssl->encrypted_input = BIO_new(BIO_s_mem());
    ssl->encrypted_output = BIO_new(BIO_s_mem());

    SSL_set_bio(ssl->ssl, ssl->encrypted_input, ssl->encrypted_output);

    ssl->peer_subject = NULL;
    ssl->peer_issuer_subject = NULL;
    ssl->handshaking = true;

    return ssl;
}

const char *
ssl_filter_get_peer_subject(struct ssl_filter *ssl)
{
    assert(ssl != NULL);

    return ssl->peer_subject;
}

const char *
ssl_filter_get_peer_issuer_subject(struct ssl_filter *ssl)
{
    assert(ssl != NULL);

    return ssl->peer_issuer_subject;
}
<|endoftext|>