2024-08-23 12:37:29 +00:00
|
|
|
#include "actions.h"
|
|
|
|
|
|
|
|
#include <engine_engine_number_9/baza_throw.h>
|
|
|
|
#include <engine_engine_number_9/running_mainloop.h>
|
|
|
|
#include <engine_engine_number_9/http_structures/response_gen.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <engine_engine_number_9/connecting_assets/static_asset_manager.h>
|
|
|
|
#include <assert.h>
|
|
|
|
#include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
|
|
|
|
#include <new_york_transit_line/templater.h>
|
|
|
|
#include <sqlite3.h>
|
|
|
|
#include <engine_engine_number_9/socket_address.h>
|
|
|
|
#include "sqlite3_wrapper.h"
|
|
|
|
#include "str_fields.h"
|
|
|
|
#include "find_db.h"
|
2024-08-23 22:43:07 +00:00
|
|
|
#include "login_cookie.h"
|
|
|
|
#include <engine_engine_number_9/http_structures/cookies.h>
|
2024-08-23 12:37:29 +00:00
|
|
|
|
|
|
|
namespace iu9cawebchat {
|
|
|
|
bool termination = false;
|
|
|
|
|
|
|
|
void sigterm_action(int) {
|
|
|
|
termination = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void run_website(const json::JSON& config) {
|
|
|
|
een9_ASSERT(config["assets"].g().isString(), "config[\"assets\"] is not string");
|
|
|
|
std::string assets_dir = config["assets"].g().asString();
|
|
|
|
een9_ASSERT(een9::isDirectory(assets_dir), "\"" + assets_dir + "\" is not a directory");
|
|
|
|
|
|
|
|
een9::StaticAssetManagerSlaveModule samI;
|
|
|
|
samI.update({
|
|
|
|
een9::StaticAssetManagerRule{assets_dir + "/css", "/assets/css", {{".css", "text/css"}} },
|
2024-08-23 13:54:52 +00:00
|
|
|
een9::StaticAssetManagerRule{assets_dir + "/js", "/assets/js", {{".js", "text/javascript"}} },
|
2024-08-23 12:37:29 +00:00
|
|
|
een9::StaticAssetManagerRule{assets_dir + "/img", "/assets/img", {
|
|
|
|
{".jpg", "image/jpg"}, {".png", "image/png"}, {".svg", "image/svg+xml"}
|
|
|
|
} },
|
|
|
|
});
|
|
|
|
|
|
|
|
const json::JSON& config_presentation = config["presentation"].g();
|
|
|
|
int64_t slave_number = config["server"]["workers"].g().asInteger().get_int();
|
|
|
|
een9_ASSERT(slave_number > 0 && slave_number <= 200, "E");
|
|
|
|
|
|
|
|
std::string sqlite_db_path;
|
|
|
|
int ret = find_db_sqlite_file_path(config, sqlite_db_path);
|
|
|
|
een9_ASSERT(ret == 0, "Can't find database file");
|
|
|
|
|
|
|
|
struct WorkerGuestData {
|
|
|
|
/* Because templaters use libjsonincpp, they can't be READ by two thread simultaneously */
|
|
|
|
std::unique_ptr<nytl::Templater> templater;
|
|
|
|
std::unique_ptr<SqliteConnection> db;
|
|
|
|
};
|
|
|
|
std::vector<WorkerGuestData> worker_guest_data(slave_number);
|
|
|
|
for (int i = 0; i < slave_number; i++) {
|
|
|
|
worker_guest_data[i].templater = std::make_unique<nytl::Templater>(
|
|
|
|
nytl::TemplaterSettings{nytl::TemplaterDetourRules{assets_dir + "/HypertextPages"}});
|
|
|
|
worker_guest_data[i].templater->update();
|
|
|
|
worker_guest_data[i].db = std::make_unique<SqliteConnection>(sqlite_db_path);
|
|
|
|
}
|
|
|
|
|
|
|
|
een9::MainloopParameters params;
|
|
|
|
params.guest_core = [&samI, &worker_guest_data, config_presentation]
|
|
|
|
(const een9::SlaveTask& task, const een9::ClientRequest& req, een9::worker_id_t worker_id) -> std::string {
|
|
|
|
een9_ASSERT_pl(0 <= worker_id && worker_id < worker_guest_data.size());
|
|
|
|
WorkerGuestData& wgd = worker_guest_data[worker_id];
|
|
|
|
nytl::Templater& templater = *wgd.templater;
|
|
|
|
een9::StaticAsset sa;
|
|
|
|
int ret;
|
2024-08-23 22:43:07 +00:00
|
|
|
auto find_user_by_credentials = [&](const std::string& nickname, const std::string& password) -> int64_t {
|
|
|
|
SqliteStatement sql_req(*wgd.db,
|
|
|
|
"SELECT `id` FROM `user` WHERE `nickname` = ?1 AND `password` = ?2",
|
|
|
|
{}, {{1, nickname}, {2, password}});
|
|
|
|
fsql_integer_or_null id_col;
|
|
|
|
int status = sqlite_stmt_step(sql_req, {{0, &id_col}}, {});
|
|
|
|
if (status == SQLITE_ROW) {
|
|
|
|
een9_ASSERT_pl(id_col.exist & id_col.value >= 0);
|
|
|
|
return id_col.value;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
};
|
|
|
|
auto find_user_name = [&](int64_t uid) -> std::string {
|
|
|
|
een9_ASSERT(uid >= 0, "Are you crazy?");
|
|
|
|
SqliteStatement sql_req(*wgd.db,
|
|
|
|
"SELECT `name` FROM `user` WHERE `id` = ?1",
|
|
|
|
{{1, uid}}, {});
|
|
|
|
fsql_text8_or_null name_col;
|
|
|
|
int status = sqlite_stmt_step(sql_req, {}, {{0, &name_col}});
|
|
|
|
if (status == SQLITE_ROW) {
|
|
|
|
een9_ASSERT_pl(name_col.exist);
|
|
|
|
return name_col.value;
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<std::pair<std::string, std::string>> cookies = een9::findAllClientCookies(req.headers);
|
|
|
|
std::vector<LoginCookie> login_cookies = select_login_cookies(cookies);
|
|
|
|
json::JSON userinfo;
|
|
|
|
userinfo["uid"] = json::JSON("-1");
|
|
|
|
userinfo["nickname"] = json::JSON("");
|
|
|
|
userinfo["name"] = json::JSON("");
|
|
|
|
int64_t logged_in_user = -1; /* Negative means that user is not logged in */
|
|
|
|
if (!login_cookies.empty()){
|
|
|
|
size_t oldest_ind = select_oldest_login_cookie(login_cookies);
|
|
|
|
LoginCookie& tried = login_cookies[oldest_ind];
|
|
|
|
logged_in_user = find_user_by_credentials(tried.nickname, tried.password);
|
|
|
|
if (logged_in_user >= 0) {
|
|
|
|
userinfo["uid"] = json::JSON(std::to_string(logged_in_user));
|
|
|
|
userinfo["nickname"] = json::JSON(tried.nickname);
|
|
|
|
userinfo["name"] = json::JSON(find_user_name(logged_in_user));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
auto RTEE = [&](const std::string& el_name) -> std::string {
|
|
|
|
std::string page = templater.render(el_name, {&config_presentation, &userinfo});
|
2024-08-23 12:37:29 +00:00
|
|
|
return een9::form_http_server_response_200("text/html", page);
|
|
|
|
};
|
2024-08-23 22:43:07 +00:00
|
|
|
|
2024-08-23 12:37:29 +00:00
|
|
|
if (req.uri_path == "/" || req.uri_path == "/list-rooms") {
|
2024-08-23 22:43:07 +00:00
|
|
|
printf("DEBUG:::: %d\n", logged_in_user);
|
|
|
|
if (logged_in_user < 0)
|
|
|
|
return een9::form_http_server_response_307("/login");
|
|
|
|
return RTEE("list-rooms");
|
2024-08-23 12:37:29 +00:00
|
|
|
}
|
2024-08-23 15:29:21 +00:00
|
|
|
if (req.uri_path == "/login") {
|
2024-08-23 22:43:07 +00:00
|
|
|
if (req.method == "POST") {
|
|
|
|
std::vector<std::pair<std::string, std::string>> query = een9::split_html_query(req.body);
|
|
|
|
std::string nickname;
|
|
|
|
std::string password;
|
|
|
|
for (const std::pair<std::string, std::string>& cmp: query) {
|
|
|
|
if (cmp.first == "nickname")
|
|
|
|
nickname = cmp.second;
|
|
|
|
if (cmp.first == "password")
|
|
|
|
password = cmp.second;
|
|
|
|
}
|
|
|
|
een9_ASSERT(check_nickname(nickname), "/login/accpet-data rejected impossible nickname");
|
|
|
|
een9_ASSERT(check_password(password), "/login/accpet-data rejected impossible password");
|
|
|
|
int64_t uid = find_user_by_credentials(nickname, password);
|
|
|
|
if (uid < 0) {
|
|
|
|
/* todo: Here I need to tell somehow to user (through fancy red box, maybe), that login was incorrect */
|
|
|
|
return RTEE("login");
|
|
|
|
}
|
|
|
|
std::vector<std::pair<std::string, std::string>> response_hlines;
|
|
|
|
LoginCookie new_login_cookie = create_login_cookie(nickname, password);
|
|
|
|
add_set_cookie_headers_to_login(login_cookies, response_hlines, new_login_cookie);
|
|
|
|
return een9::form_http_server_response_307_spec_head("/", response_hlines);
|
|
|
|
}
|
|
|
|
return RTEE("login");
|
2024-08-23 15:29:21 +00:00
|
|
|
}
|
2024-08-23 12:37:29 +00:00
|
|
|
if (req.uri_path == "/chat") {
|
2024-08-23 22:43:07 +00:00
|
|
|
return RTEE("chat");
|
2024-08-23 12:37:29 +00:00
|
|
|
}
|
|
|
|
if (req.uri_path == "/profile") {
|
2024-08-23 22:43:07 +00:00
|
|
|
return RTEE("profile");
|
2024-08-23 12:37:29 +00:00
|
|
|
}
|
|
|
|
if (req.uri_path == "/registration") {
|
2024-08-23 22:43:07 +00:00
|
|
|
return RTEE("registration");
|
2024-08-23 12:37:29 +00:00
|
|
|
}
|
|
|
|
/* Trying to interpret request as asset lookup */
|
|
|
|
ret = samI.get_asset(req.uri_path, sa);
|
|
|
|
if (ret >= 0) {
|
|
|
|
return een9::form_http_server_response_200(sa.type, sa.content);
|
|
|
|
}
|
|
|
|
return een9::form_http_server_response_404("text/html", "<h1> Not found! </h1>");
|
|
|
|
};
|
|
|
|
|
|
|
|
params.guest_core_admin_control = [&worker_guest_data]
|
|
|
|
(const een9::SlaveTask& task, const std::string& req, een9::worker_id_t worker_id) -> std::string {
|
|
|
|
een9_ASSERT_pl(0 <= worker_id && worker_id < worker_guest_data.size());
|
|
|
|
WorkerGuestData& wgd = worker_guest_data[worker_id];
|
|
|
|
try {
|
|
|
|
if (req == "hello") {
|
|
|
|
return ":0 omg! hiii!! Hewwou :3 !!!!";
|
|
|
|
}
|
|
|
|
if (req == "8") {
|
|
|
|
termination = true;
|
|
|
|
return "Bye";
|
|
|
|
}
|
|
|
|
std::string updaterootpw_pref = "updaterootpw";
|
|
|
|
if (een9::beginsWith(req, "updaterootpw")) {
|
|
|
|
size_t nid = updaterootpw_pref.size();
|
|
|
|
if (nid >= req.size() || !isSPACE(req[nid]))
|
|
|
|
return "Bad command syntax. Missing whitespace";
|
|
|
|
std::string new_password = req.substr(nid + 1);
|
|
|
|
if (!check_password(new_password))
|
|
|
|
een9_THROW("Bad password");
|
2024-08-23 22:43:07 +00:00
|
|
|
sqlite_nooutput(wgd.db->hand,
|
2024-08-23 12:37:29 +00:00
|
|
|
"UPDATE `user` SET `password` = ?1 WHERE `id` = 0 ",
|
|
|
|
{}, {{1, new_password}});
|
|
|
|
return "Successul update";
|
|
|
|
}
|
|
|
|
return "Incorrect command";
|
|
|
|
} catch (std::exception& e) {
|
|
|
|
return std::string("Server error\n") + e.what();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
params.slave_number = slave_number;
|
|
|
|
|
|
|
|
een9::SocketAddressParser sap;
|
|
|
|
auto translate_addr_list_conf = [&sap](std::vector<een9::SocketAddress>& dest, const std::vector<json::JSON>& source) {
|
|
|
|
size_t N = source.size();
|
|
|
|
dest.resize(N);
|
2024-08-23 15:29:21 +00:00
|
|
|
for (size_t i = 0; i < N; i++) {
|
|
|
|
int ret = een9::parse_socket_address(source[i].asString(), dest[i], sap);
|
|
|
|
een9_ASSERT(ret == 0, "Incorrect ear address: " + source[i].asString());
|
|
|
|
}
|
2024-08-23 12:37:29 +00:00
|
|
|
};
|
|
|
|
translate_addr_list_conf(params.client_regular_listened, config["server"]["http-listen"].g().asArray());
|
|
|
|
translate_addr_list_conf(params.admin_control_listened, config["server"]["admin-command-listen"].g().asArray());
|
|
|
|
|
|
|
|
signal(SIGINT, sigterm_action);
|
|
|
|
signal(SIGTERM, sigterm_action);
|
|
|
|
|
|
|
|
een9::electric_boogaloo(params, termination);
|
|
|
|
}
|
|
|
|
}
|