summaryrefslogtreecommitdiffhomepage
path: root/click-fcgi.cpp
blob: 8b422ca0a1ded69f6eb6f8982c1a323c75d82108 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include "config.h"

#include <stdexcept>
#include <string>
#include <iostream>

#include <fcgiapp.h>

#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <fmt/format.h>

namespace pt = boost::property_tree;

using namespace std::string_literals;

namespace {

class PostData
{
public:
  PostData(FCGX_Request& request) {
    std::string result;
    std::string contentLengthString(FCGX_GetParam("CONTENT_LENGTH", request.envp));
    int contentLength = std::stoul(contentLengthString);

    result.resize(contentLength);

    unsigned int status = FCGX_GetStr(result.data(), result.size(), request.in);
    if (status != result.size()) {
      throw std::runtime_error(fmt::format("Read error: {}/{}", status, result.size()));
    }

    m_data = result;
  }

  std::string getData()
  {
    return m_data;
  }

  // path: xml path, e.g. data.value
  std::string getXMLElement(const std::string& path)
  {
    pt::ptree tree{};
    std::istringstream iss{m_data};
    pt::read_xml(iss, tree, pt::xml_parser::trim_whitespace);

    return tree.get<std::string>(path);
  }

private:
  std::string m_data;
};

std::string getCommand(FCGX_Request& request)
{
  std::string query = FCGX_GetParam("QUERY_STRING", request.envp);
  size_t pos = query.find("command=");
  if (pos != query.npos) {
    return query.substr(pos + 8);
  } else {
    return {};
  }
}

std::string to_xml()
{
  std::string result{"<data><status>ok</status><ui>"};

  result += "ui1</ui>";
  return result + "</data>";
}

} // namespace

int main(int argc, char* argv[]) {
  try {
    Config config{argc, argv};

    std::string ok_data{"<data><status>ok</status><message>OK</message></data>"};
    std::string error_data{"<data><status>error</status><message>General Error</message></data>"};

    int result = FCGX_Init();
    if (result != 0) {
      return 1; // error on init
    }

    FCGX_Request request;

    if (FCGX_InitRequest(&request, 0, 0) != 0) {
      return 1; // error on init
    }

    while (FCGX_Accept_r(&request) == 0) {
      std::string method = FCGX_GetParam("REQUEST_METHOD", request.envp);

      FCGX_PutS("Content-Type: text/xml\r\n\r\n", request.out);

      try {
        if (method == "POST") {
          PostData data{request};
          std::string command {getCommand(request)};
          if (command == "start") {
            FCGX_PutS(ok_data.c_str(), request.out);
          } else if (command == "stop") {
            FCGX_PutS(ok_data.c_str(), request.out);
          } else if (command == "getui") {
            FCGX_PutS(to_xml().c_str(), request.out);
          } else if (command == "setfile") {
            std::string filename = data.getXMLElement("data.value");
            FCGX_PutS(ok_data.c_str(), request.out);
          } else {
            FCGX_PutS(error_data.c_str(), request.out);
          }
        } else {
          throw std::runtime_error(fmt::format("Bad request method: POST expected, got {}", method).c_str());
        }
      } catch (const std::exception& ex) {
        FCGX_PutS(("<data><status>error</status><message>Error: "s + ex.what() + "</message></data>").c_str(), request.out);
      }
    }
  } catch (const std::exception& ex) {
    std::cerr << "Error: " << ex.what() << std::endl;
  }

  return 0;
}