application.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. #include "server/application.hpp"
  2. #include <ctime>
  3. using namespace binom;
  4. namespace {
  5. std::string getHostStr(TcpServer::Client& client) {
  6. uint32_t ip = client.getHost ();
  7. return std::to_string(int(reinterpret_cast<char*>(&ip)[0])) + '.' +
  8. std::to_string(int(reinterpret_cast<char*>(&ip)[1])) + '.' +
  9. std::to_string(int(reinterpret_cast<char*>(&ip)[2])) + '.' +
  10. std::to_string(int(reinterpret_cast<char*>(&ip)[3])) + ':' +
  11. std::to_string(client.getPort());
  12. }
  13. }
  14. bool strIsNumber(const std::string& s) {
  15. return !s.empty() && std::find_if(s.begin(),
  16. s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end();
  17. }
  18. int main(int argc, char* argv[]) {Application(argc, argv); return EXIT_SUCCESS;}
  19. Application::AppIniter Application::processArgs(int argc, char* argv[]) {
  20. #define ifeq(str_1, str_2) if(isstreq(str_1, str_2))
  21. #define elifeq(str_1, str_2) else if(isstreq(str_1, str_2))
  22. if(argc <= 1) return AppIniter{};
  23. enum class Token {
  24. flag,
  25. port,
  26. db_path,
  27. name
  28. }token = Token::flag;
  29. AppIniter app_init;
  30. for((--argc, ++argv); argc ;(--argc, ++argv)) {
  31. switch (token) {
  32. case Token::flag:
  33. ifeq(*argv, "--port") {
  34. token = Token::port;
  35. } elifeq(*argv, "-p") {
  36. token = Token::port;
  37. } elifeq(*argv, "--db-path") {
  38. token = Token::db_path;
  39. } elifeq(*argv, "-d") {
  40. token = Token::db_path;
  41. } elifeq(*argv, "--name") {
  42. token = Token::name;
  43. } elifeq(*argv, "-n") {
  44. token = Token::name;
  45. } elifeq(*argv, "--init-db") {
  46. std::string input;
  47. std::clog << "Do you rally want to initialize the database?\n"
  48. "Print \"yes\" if you want initialize the database\n"
  49. "Other answers will be perceived as no\n"
  50. "> ";
  51. std::getline(std::cin, input, '\n');
  52. app_init.init_db = !strcmp(input.c_str(), "yes");
  53. }
  54. continue;
  55. case Token::port: {
  56. if(strIsNumber(*argv))
  57. app_init.server_port = atol(*argv);
  58. token = Token::flag;
  59. }continue;
  60. case Token::db_path:
  61. app_init.db_path = *argv;
  62. token = Token::flag;
  63. continue;
  64. case Token::name:
  65. app_init.server_name = *argv;
  66. token = Token::flag;
  67. continue;
  68. }
  69. }
  70. return app_init;
  71. #undef ifeq
  72. #undef elifeq
  73. }
  74. void Application::dataHandler(DataBuffer& data, TcpServer::Client& client) {
  75. try {
  76. std::clog << getHostStr(client) << " (sent data of "<< data.size << " bytes)\n";
  77. char connection_uuid[6];
  78. *reinterpret_cast<uint32_t*>(connection_uuid) = client.getHost();
  79. *reinterpret_cast<uint16_t*>(connection_uuid + 4) = client.getPort();
  80. RequestHandler(db,
  81. server,
  82. server_key,
  83. client,
  84. Variable::deserialize(ByteArray(data.data_ptr, data.size)),
  85. db.getRoot()({"session", BufferArray(ValType::byte, connection_uuid, sizeof(connection_uuid))})
  86. );
  87. } catch(Exception& e) {
  88. std::cerr << getHostStr(client) << " (err): " << e.what() << '\n';
  89. ByteArray result = Variable(vobj{{"act", ui8(ServerAction::err)}}).serialize();
  90. client.sendData(result.begin(), result.length());
  91. }
  92. }
  93. void Application::connectHandler(TcpServer::Client& client) {
  94. std::clog << getHostStr(client) << " (connected)\n";
  95. FileNodeVisitor conn_data = db.getRoot()("session");
  96. char uid[6];
  97. *reinterpret_cast<uint32_t*>(uid) = client.getHost();
  98. *reinterpret_cast<uint16_t*>(uid + 4) = client.getPort();
  99. conn_data.insert(BufferArray(ValType::byte, uid, sizeof (uid)), vobj{{"state",ui8(RequestHandler::ClientState::connected)}});
  100. }
  101. void Application::disconnectHandler(TcpServer::Client& client) {
  102. std::clog << getHostStr(client) << " (disconnected)\n";
  103. char uid[6];
  104. *reinterpret_cast<uint32_t*>(uid) = client.getHost();
  105. *reinterpret_cast<uint16_t*>(uid + 4) = client.getPort();
  106. db.getRoot()("session").remove(BufferArray(ValType::byte, uid, sizeof (uid)));
  107. }
  108. Application::Application(int argc, char* argv[]) try : Application(processArgs(argc, argv)) {}
  109. catch(const Exception& e) {
  110. std::cerr << e.full() << '\n';
  111. std::exit(EXIT_FAILURE);
  112. } catch(const std::exception& e) {
  113. std::cerr << e.what() << '\n';
  114. std::exit(EXIT_FAILURE);
  115. } catch(...) {
  116. std::cerr << "Unexpected exception!\n";
  117. std::exit(EXIT_FAILURE);
  118. }
  119. Application::Application(Application::AppIniter app_initer)
  120. : db(app_initer.db_path,
  121. #include "server_db.hpp"
  122. , app_initer.init_db),
  123. server(app_initer.server_port,
  124. [this](DataBuffer data, TcpServer::Client& client){dataHandler(data, client);},
  125. [this](TcpServer::Client& client){connectHandler(client);},
  126. [this](TcpServer::Client& client){disconnectHandler(client);}),
  127. server_key(Security::genKey()) {
  128. // Server name init (will be used in the server federation system)
  129. if(FileNodeVisitor name = db.getRoot()({"server_data","name"}); app_initer.server_name) {
  130. if(name.getVariable().toBufferArray() != BufferArray(app_initer.server_name)) {
  131. name.setVariable(app_initer.server_name);
  132. std::clog << "Server human-readable name assigned\n"
  133. "current server name = " << name.getVariable().toBufferArray().toString() << '\n';
  134. }
  135. } else if(!name.getElementCount()) {
  136. name.setVariable(
  137. BufferArray(
  138. Security::encodeBase64(
  139. Security::getHashOf(
  140. Security::extractPublicKey(server_key),
  141. Security::HashMethod::shake128
  142. )
  143. )
  144. )
  145. );
  146. std::clog << "Server base64 name assigned\n"
  147. "current server name = " << name.getVariable().toBufferArray().toString() << "\n"
  148. "Use `librehubd -n <your-server-name>` to assign human-readable name\n";
  149. } else std::clog << "current server name = " << name.getVariable().toBufferArray().toString() << '\n';
  150. // Server start
  151. if(server.start() == TcpServer::status::up) {
  152. std::clog<<"Server up on port: "<< app_initer.server_port <<std::endl;
  153. server.joinLoop(); // Joing to the client handling loop
  154. } else {
  155. std::cerr<<"Server start error! Error code:"<< int(server.getStatus()) << std::endl;
  156. std::exit(-1);
  157. }
  158. }