diff --git a/building/main.cpp b/building/main.cpp index 787db41..f75a8e2 100644 --- a/building/main.cpp +++ b/building/main.cpp @@ -25,7 +25,7 @@ struct CAWebChat { BuildUnitsArray runlevel_2; std::string build_type; - bool build_tests = false; + bool make_tests = false; std::vector warning_flags = {"-Wall", "-Wno-unused-variable", "-Werror=return-type","-pedantic", "-Wno-unused-but-set-variable", "-Wno-reorder"}; @@ -49,9 +49,11 @@ struct CAWebChat { return my_flag_collection; } - CAWebChat(const std::string& _build_type, bool _build_tests, const NormalCBuildSystemCommandMeaning& cmd) - : build_type(_build_type), build_tests(_build_tests) - { + explicit CAWebChat(const NormalCBuildSystemCommandMeaning& cmd){ + const char* BSCRIPT_TYPE = getenv("BSCRIPT_TYPE"); + const char* BSCRIPT_TESTS = getenv("BSCRIPT_TESTS"); + build_type = BSCRIPT_TYPE ? BSCRIPT_TYPE : "release"; + make_tests = (bool)BSCRIPT_TESTS; ASSERT(build_type == "release" || build_type == "debug", "Unknown build type"); std::vector ext_targets = { @@ -75,6 +77,7 @@ struct CAWebChat { "os_utils.cpp", "http_structures/client_request_parse.cpp", "http_structures/response_gen.cpp", + "http_structures/cookies.cpp", "connecting_assets/static_asset_manager.cpp", "running_mainloop.cpp", "form_data_structure/urlencoded_query.cpp", @@ -92,6 +95,7 @@ struct CAWebChat { "os_utils.h", "connecting_assets/static_asset_manager.h", "http_structures/client_request.h", + "http_structures/cookies.h", "http_structures/response_gen.h", "running_mainloop.h", "form_data_structure/urlencoded_query.h", @@ -127,27 +131,42 @@ struct CAWebChat { T.installation_dir = "nytl"; my_targets.push_back(T); } - { CTarget T{"iu9-ca-web-chat", "executable"}; + { CTarget T{"iu9_ca_web_chat_lib", "shared_library"}; T.additional_compilation_flags = getSomeRadFlags(); T.proj_deps = { - CTargetDependenceOnProjectsLibrary{"engine_engine_number_9"}, - CTargetDependenceOnProjectsLibrary{"new_york_transit_line"}, + CTargetDependenceOnProjectsLibrary{"engine_engine_number_9", {true, true}}, + CTargetDependenceOnProjectsLibrary{"new_york_transit_line", {true, true}}, }; T.external_deps = { - CTargetDependenceOnExternalLibrary{"sqlite3"} + CTargetDependenceOnExternalLibrary{"sqlite3", {true, true}} }; T.units = { - "main.cpp", "initialize.cpp", "run.cpp", - "str_fields_check.cpp", + "str_fields.cpp", "find_db.cpp", "sqlite3_wrapper.cpp", }; + for (std::string& u: T.units) + u = "web_chat/iu9_ca_web_chat_lib/" + u; + T.exported_headers = { + "actions.h" + }; + for (std::string& u: T.exported_headers) + u = "iu9_ca_web_chat_lib/" + u; + T.include_pr = "web_chat"; + T.installation_dir = "iu9cawebchat"; + my_targets.push_back(T); + } + { CTarget T{"iu9-ca-web-chat-service", "executable"}; + T.additional_compilation_flags = getSomeRadFlags(); + T.proj_deps = { + CTargetDependenceOnProjectsLibrary{"iu9_ca_web_chat_lib"}, + }; + T.units = {"service.cpp"}; for (std::string& u: T.units) u = "web_chat/iu9_ca_web_chat_service/" + u; T.include_pr = "web_chat"; - T.installation_dir = ""; my_targets.push_back(T); } { CTarget T{"iu9-ca-web-chat-admin-cli", "executable"}; @@ -177,9 +196,7 @@ int main(int argc, char** argv) { } NormalCBuildSystemCommandMeaning cmd; regular_bs_cli_cmd_interpret(args, cmd); - const char* BS_SCRIPT_TYPE = getenv("BSCRIPT_TYPE"); - const char* BS_SCRIPT_TESTS = getenv("BSCRIPT_TESTS"); - CAWebChat bs(BS_SCRIPT_TYPE ? BS_SCRIPT_TYPE : "release", (bool)BS_SCRIPT_TESTS, cmd); + CAWebChat bs(cmd); if (cmd.need_to_build) complete_tasks_of_build_units(bs.runlevel_1); umask(~0755); diff --git a/src/http_server/engine_engine_number_9/http_structures/cookies.cpp b/src/http_server/engine_engine_number_9/http_structures/cookies.cpp new file mode 100644 index 0000000..417c63c --- /dev/null +++ b/src/http_server/engine_engine_number_9/http_structures/cookies.cpp @@ -0,0 +1,83 @@ +#include "cookies.h" +#include "../baza_inter.h" + +namespace een9 { + bool isSPACE(char ch) { + return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; + } + + bool isToken(const std::string &str) { + for (char ch : str) { + if (!(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ('0' <= ch && ch <= '9') + || ch == '!' || ch == '#' || ch == '$' || ch == '%' || ch == '&' || ch == '\'' || ch == '*' + || ch == '+' || ch == '-' || ch == '.' || ch == '^' || ch == '_' || ch == '`' || ch == '|' + || ch == '~' )) + return false; + } // '*+-.^_`|~ + return !str.empty(); + } + + bool isCookieName(const std::string &str) { + return isToken(str); + } + + + bool isCookieValue(const std::string &str) { + for (char ch : str) { + if (ch <= 32 || ch == 0x22 || ch == 0x2C || ch == 0x3B || ch == 0x5C || ch >= 0x7F) + return false; + } + return !str.empty(); + } + + std::vector> parseCookieHeader(const std::string &hv) { + std::vector> result; + size_t pos = 0; + auto skip_ows = [&]() { + while (hv.size() > pos && isSPACE(hv[pos])) + pos++; + }; + auto read_to_space_or_ch = [&](char sch) -> std::string { + size_t S = pos; + while (hv.size() > pos && !isSPACE(hv[pos]) && hv[pos] != sch) + pos++; + return hv.substr(S, pos - S); + }; + auto isThis = [&](char ch) -> bool { + return pos < hv.size() && hv[pos] == ch; + }; + skip_ows(); + while (pos < hv.size()) { + std::string name_of_pechenye = read_to_space_or_ch('='); + ASSERT(isCookieName(name_of_pechenye), "Incorrect Cookie name"); + skip_ows(); + ASSERT(isThis('='), "Incorrect Cookie header line, missing ="); + pos++; + skip_ows(); + std::string value_of_pechenye; + if (isThis('"')) { + pos++; + value_of_pechenye = read_to_space_or_ch('"'); + ASSERT(isThis('"'), "Incorrect Cookie header line, missing \""); + pos++; + } else { + value_of_pechenye = read_to_space_or_ch('"');; + } + ASSERT(isCookieValue(value_of_pechenye), "Incorrect Cookie value"); + if (result.empty()) + result.emplace_back(); + result.back().first = std::move(name_of_pechenye); + result.back().second = std::move(value_of_pechenye); + skip_ows(); + } + return result; + } + + void set_cookie(const std::vector>& new_cookies, + std::vector>& res_header_lines) { + for (const std::pair& cookie : new_cookies) { + ASSERT_pl(isCookieName(cookie.first) && isCookieValue(cookie.second)); + res_header_lines.emplace_back("Set-Cookie", cookie.first + "=" + cookie.second + ";SameSite=Strict"); + } + } +} diff --git a/src/http_server/engine_engine_number_9/http_structures/cookies.h b/src/http_server/engine_engine_number_9/http_structures/cookies.h new file mode 100644 index 0000000..59f34f4 --- /dev/null +++ b/src/http_server/engine_engine_number_9/http_structures/cookies.h @@ -0,0 +1,22 @@ +#ifndef HTTP_STRUCTURES_COOKIES_H +#define HTTP_STRUCTURES_COOKIES_H + +#include +#include +#include "../baza.h" +#include + +namespace een9 { + bool isCookieName(const std::string& str); + + bool isCookieValue(const std::string& str); + + /* Throws een9::ServerError on failure */ + std::vector> parseCookieHeader(const std::string& hv); + + /* Can throw een9::ServerError (if check for a value failed). res_header_lines is mutated accordingle */ + void set_cookie(const std::vector>& new_cookies, + std::vector>& res_header_lines); +} + +#endif diff --git a/src/web_chat/iu9_ca_web_chat_lib/actions.h b/src/web_chat/iu9_ca_web_chat_lib/actions.h new file mode 100644 index 0000000..65ff137 --- /dev/null +++ b/src/web_chat/iu9_ca_web_chat_lib/actions.h @@ -0,0 +1,11 @@ +#ifndef IU9_CA_WEB_CHAT_ACTIONS_H +#define IU9_CA_WEB_CHAT_ACTIONS_H + +#include + +namespace iu9cawebchat { + void run_website(const json::JSON& config); + void initialize_website(const json::JSON& config, const std::string& root_pw); +} + +#endif diff --git a/src/web_chat/iu9_ca_web_chat_lib/find_db.cpp b/src/web_chat/iu9_ca_web_chat_lib/find_db.cpp new file mode 100644 index 0000000..56e39e4 --- /dev/null +++ b/src/web_chat/iu9_ca_web_chat_lib/find_db.cpp @@ -0,0 +1,16 @@ +#include "find_db.h" + +namespace iu9cawebchat{ + int find_db_sqlite_file_path(const json::JSON& config, std::string& res_path) { + const json::JSON& type = config["database"]["type"].g(); + if (!type.isString() && type.asString() == "sqlite3") + return -1; + const json::JSON& path = config["database"]["file"].g(); + if (!path.isString()) + return -1; + if (path.asString().empty() || path.asString()[0] == ':') + return -1; + res_path = path.asString(); + return 0; + } +} diff --git a/src/web_chat/iu9_ca_web_chat_service/find_db.h b/src/web_chat/iu9_ca_web_chat_lib/find_db.h similarity index 55% rename from src/web_chat/iu9_ca_web_chat_service/find_db.h rename to src/web_chat/iu9_ca_web_chat_lib/find_db.h index 99bf2a7..52d4f24 100644 --- a/src/web_chat/iu9_ca_web_chat_service/find_db.h +++ b/src/web_chat/iu9_ca_web_chat_lib/find_db.h @@ -3,6 +3,8 @@ #include -int find_db_sqlite_file_path(const json::JSON& config, std::string& res_path); +namespace iu9cawebchat { + int find_db_sqlite_file_path(const json::JSON& config, std::string& res_path); +} #endif diff --git a/src/web_chat/iu9_ca_web_chat_lib/initialize.cpp b/src/web_chat/iu9_ca_web_chat_lib/initialize.cpp new file mode 100644 index 0000000..c85b6ba --- /dev/null +++ b/src/web_chat/iu9_ca_web_chat_lib/initialize.cpp @@ -0,0 +1,78 @@ +#include "actions.h" +#include +#include "str_fields.h" +#include +#include +#include "find_db.h" +#include +#include +#include "sqlite3_wrapper.h" + +namespace iu9cawebchat { + void initialize_website(const json::JSON& config, const std::string& root_pw) { + printf("Initialization...\n"); + een9_ASSERT(check_password(root_pw), "Bad root password"); + std::string db_path; + int ret; + ret = find_db_sqlite_file_path(config, db_path); + een9_ASSERT(ret == 0, "Invalid settings[\"database\"] field"); + if (een9::isRegularFile(db_path)) { + // todo: plaese, don't do this + ret = unlink(db_path.c_str()); + een9_ASSERT_pl(ret == 0); + } + een9_ASSERT(!een9::isRegularFile(db_path), "Database file exists prior to initialization. " + "Can't preceed withut harming existing data"); + SqliteConnection conn(db_path.c_str()); + assert(sqlite3_errcode(conn.hand) == SQLITE_OK); + /* Role of memeber of chat: + * 1 - admin + * 2 - regular + * 3 - read-only member + * 4 - deleted (no longer a member) + * + * If user.id is 0, it is a root user + * If chat.lastMsgId is NULL, chat is empty + * If message.previous is NULL, this message is first in it's chat + */ + sqlite_single_statement(conn.hand, "PRAGMA foreign_keys = true"); + sqlite_single_statement(conn.hand, "BEGIN"); + sqlite_single_statement(conn.hand, "CREATE TABLE `nickname` (`it` TEXT PRIMARY KEY NOT NULL) WITHOUT ROWID"); + sqlite_single_statement(conn.hand, "CREATE TABLE `user` (" + "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL," + "`name` TEXT NOT NULL," + "`chatList_HistoryId` INTEGER NOT NULL," + "`password` TEXT NOT NULL" + ")"); + sqlite_single_statement(conn.hand, "CREATE TABLE `chat` (" + "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL," + "`name` TEXT NOT NULL," + "`it_HistoryId` INTEGER NOT NULL," + "`lastMsgId` INTEGER REFERENCES `message`" + ")"); + sqlite_single_statement(conn.hand, "CREATE TABLE `user_chat_membership` (" + "`userId` INTEGER REFERENCES `user` NOT NULL," + "`chatId` INTEGER REFERENCES `chat` NOT NULL," + "`user_chatList_IncHistoryId` INTEGER NOT NULL," + "`chat_IncHistoryId` INTEGER NOT NULL," + "`role` INTEGER NOT NULL" + ")"); + sqlite_single_statement(conn.hand, "CREATE TABLE `message` (" + "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," + "`chatId` INTEGER REFERENCES `chat` NOT NULL," + "`previous` INTEGER REFERENCES `message`," + "`senderUserId` INTEGER REFERENCES `user` NOT NULL," + "`exists` BOOLEAN NOT NULL," + "`isSystem` BOOLEAN NOT NULL," + "`text` TEXT NOT NULL," + "`chat_IncHistoryId` INTEGER NOT NULL" + ")"); + sqlite_single_statement(conn.hand, "INSERT INTO `nickname` VALUES (?1)", {}, {{1, "root"}}); + sqlite_single_statement(conn.hand, "INSERT INTO `user` (`id`, `nickname`, `name`, `chatList_HistoryId`, `password`) VALUES " + "(0, ?1, ?2, 0, ?3)", {}, + {{1, "root"}, {2, "Rootov Root Rootovich"}, {3, root_pw}}); + sqlite_single_statement(conn.hand, "END"); + } +} diff --git a/src/web_chat/iu9_ca_web_chat_lib/run.cpp b/src/web_chat/iu9_ca_web_chat_lib/run.cpp new file mode 100644 index 0000000..534548f --- /dev/null +++ b/src/web_chat/iu9_ca_web_chat_lib/run.cpp @@ -0,0 +1,140 @@ +#include "actions.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sqlite3_wrapper.h" +#include "str_fields.h" +#include "find_db.h" + +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"}} }, + een9::StaticAssetManagerRule{assets_dir + "/js", "/assets/js", {{".js", "text/js"}} }, + 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 templater; + std::unique_ptr db; + }; + std::vector worker_guest_data(slave_number); + for (int i = 0; i < slave_number; i++) { + worker_guest_data[i].templater = std::make_unique( + nytl::TemplaterSettings{nytl::TemplaterDetourRules{assets_dir + "/HypertextPages"}}); + worker_guest_data[i].templater->update(); + worker_guest_data[i].db = std::make_unique(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; + auto rteee = [&](const std::string& el_name, bool pass_phr) -> std::string { + std::string page = templater.render(el_name, + pass_phr ? std::vector{&config_presentation} : std::vector{}); + return een9::form_http_server_response_200("text/html", page); + }; + if (req.uri_path == "/" || req.uri_path == "/list-rooms") { + return rteee("list-rooms", true); + } + if (req.uri_path == "/chat") { + return rteee("chat", false); + } + if (req.uri_path == "/profile") { + return rteee("profile", false); + } + if (req.uri_path == "/registration") { + return rteee("registration", false); + } + /* 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", "

Not found!

"); + }; + + 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"); + sqlite_single_statement(wgd.db->hand, + "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& dest, const std::vector& source) { + size_t N = source.size(); + dest.resize(N); + for (size_t i = 0; i < N; i++) + een9::parse_socket_address(source[i].asString(), dest[i], sap); + }; + 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); + } +} diff --git a/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.cpp b/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.cpp new file mode 100644 index 0000000..ebbe4e3 --- /dev/null +++ b/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.cpp @@ -0,0 +1,94 @@ +#include "sqlite3_wrapper.h" +#include "str_fields.h" +#include +#include +#include + +namespace iu9cawebchat { + SqliteConnection::SqliteConnection(const std::string &file_path) { + int ret = sqlite3_open(file_path.c_str(), &hand); + if (ret != 0) { + een9_THROW(std::string("Can't open sqlite3 database ") + sqlite3_errstr(ret)); + } + } + + SqliteConnection::~SqliteConnection() { + if (sqlite3_close(hand) != 0) {abort();} + } + + void sqlite_single_statement(sqlite3* db_hand, const std::string& req_statement, + const std::vector>& int64_binds, + const std::vector>& text8_binds) { + sqlite3_stmt* stmt_obj = NULL; + int ret = sqlite3_prepare_v2(db_hand, req_statement.c_str(), -1, &stmt_obj, NULL); + if (ret != 0) { + int err_pos = sqlite3_error_offset(db_hand); + een9_THROW("Compilation of request\n" + req_statement + "\nfailed" + + ((err_pos >= 0) ? " with offset " + std::to_string(err_pos) : "")); + } + een9_ASSERT(ret == 0, "Can't compile request expression"); + assert(sqlite3_errcode(db_hand) == SQLITE_OK); + struct Guard1{sqlite3_stmt*& r; ~Guard1(){if (sqlite3_finalize(r) != 0) {abort();}}} guard1{stmt_obj}; + for (const std::pair& bv: int64_binds) { + ret = sqlite3_bind_int64(stmt_obj, bv.first, bv.second); + een9_ASSERT(ret, "Can't bind to parameter #" + std::to_string(bv.first)); + } + for (const std::pair& bv: text8_binds) { + een9_ASSERT(is_orthodox_string(bv.second), "Can't bind this string to parameter"); + een9_ASSERT(bv.second.size() + 1 < INT_MAX, "Ah, oh, senpai, your string is toooo huge"); + ret = sqlite3_bind_text(stmt_obj, bv.first, bv.second.c_str(), (int)bv.second.size(), SQLITE_STATIC); + } + while (true) { + ret = sqlite3_step(stmt_obj); + if (ret == SQLITE_DONE) + break; + if (ret != SQLITE_ROW) { + printf("sqlite_row error!!!\n"); + printf("%s\n", sqlite3_errmsg(db_hand)); + break; + } + int cc = sqlite3_column_count(stmt_obj); + std::vector types(cc); + for (int i = 0; i < cc; i++) { + types[i] = sqlite3_column_type(stmt_obj, i); + } + printf("Column: |"); + for (int i = 0; i < cc; i++) { + switch (types[i]) { +#define ccase(tname) case SQLITE_ ## tname: printf(" " #tname " |"); break; + ccase(INTEGER) + ccase(FLOAT) + ccase(BLOB) + ccase(NULL) + case SQLITE3_TEXT: + printf(" TEXT |"); break; + default: + een9_THROW("AAAAAA"); + } + } + printf("\n"); + printf("Values: | "); + for (int i = 0; i < cc; i++) { + if (types[i] == SQLITE_INTEGER) { + printf("%lld | ", sqlite3_column_int64(stmt_obj, i)); + } else if (types[i] == SQLITE_FLOAT) { + printf("%lf | ", sqlite3_column_double(stmt_obj, i)); + } else if (types[i] == SQLITE_BLOB) { + const void* blob = sqlite3_column_blob(stmt_obj, i); + een9_ASSERT(sqlite3_errcode(db_hand) == SQLITE_OK, "oom in sqlite3_column_blob"); + size_t sz = sqlite3_column_bytes(stmt_obj, i); + printf("Blob of size %lu | ", sz); + } else if (types[i] == SQLITE_NULL) { + printf("NULL | "); + } else { + const unsigned char* text = sqlite3_column_text(stmt_obj, i); + een9_ASSERT(text, "oom in sqlite3_column_text"); + printf("%s | ", (const char*)text); + // todo: THIS F. B.S. IS NOT SAFE + } + } + printf("\n"); + } + printf("Request steps are done\n"); + } +} diff --git a/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.h b/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.h new file mode 100644 index 0000000..12b2494 --- /dev/null +++ b/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.h @@ -0,0 +1,23 @@ +#ifndef IU9_CA_WEB_CHAT_SERVICE_SQLITE_WRAP_H +#define IU9_CA_WEB_CHAT_SERVICE_SQLITE_WRAP_H + +#include +#include +#include + +namespace iu9cawebchat { + struct SqliteConnection { + sqlite3* hand = NULL; + SqliteConnection() = default; + explicit SqliteConnection(const std::string& file_path); + SqliteConnection (const SqliteConnection&) = delete; + SqliteConnection& operator= (const SqliteConnection&) = delete; + ~SqliteConnection(); + }; + + void sqlite_single_statement(sqlite3* db_hand, const std::string& req_statement, + const std::vector>& int64_binds= {}, + const std::vector>& text8_binds = {}); +} + +#endif diff --git a/src/web_chat/iu9_ca_web_chat_lib/str_fields.cpp b/src/web_chat/iu9_ca_web_chat_lib/str_fields.cpp new file mode 100644 index 0000000..0c3aba5 --- /dev/null +++ b/src/web_chat/iu9_ca_web_chat_lib/str_fields.cpp @@ -0,0 +1,141 @@ +#include "str_fields.h" +#include +#include + +namespace iu9cawebchat { + bool isALPHA(char ch) { + return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'); + } + + bool isNUM(char ch) { + return '0' <= ch && ch <= '9'; + } + + bool isUNCHAR(char ch) { + return isALPHA(ch) || isNUM(ch) || ch == '-' || ch == '_'; + } + + bool isSPACE(char ch) { + return ch == ' ' || ch == '\r' || ch == '\t' || ch == '\n'; + } + + + bool is_orthodox_string(const std::string &str) { + for (char ch: str) + if (ch == 0) + return false; + return json::isUtf8String(str); + } + + bool check_password(const std::string &pwd) { + return is_orthodox_string(pwd) && pwd.size() >= 8; + } + + bool check_name(const std::string &name) { + return is_orthodox_string(name); + } + + bool check_nickname(const std::string &nickname) { + if (nickname.empty()) + return false; + for (char ch: nickname) { + if (!isUNCHAR(ch)) + return false; + } + return true; + } + + /* Yeah baby, it's base64 time!!! */ + + static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/'}; + + static uint8_t decoding_table[256] = { + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 62, 69, 69, 69, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 69, 69, 69, 69, 69, 69, + 69, 0, 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, 69, 69, 69, 69, 69, + 69, 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, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, + }; + + std::string base64_encode(const std::string& source) { + std::string result; + static const size_t szr2noes[3] = {0, 2, 1}; + size_t noes = szr2noes[source.size() % 3]; + size_t triplets = source.size() / 3; + result.reserve((triplets << 2) + noes); + size_t bt = 0; + for (size_t i = 0; i < triplets; i++) { + result.push_back(encoding_table[(uint8_t)source[bt] >> 2]); + result.push_back(encoding_table[(((uint8_t)source[bt] & 0x03) << 4) | ((uint8_t)source[bt + 1] >> 4)]); + result.push_back(encoding_table[(((uint8_t)source[bt + 1] & 0x0f) << 2) | ((uint8_t)source[bt + 2] >> 6)]); + result.push_back(encoding_table[(uint8_t)source[bt + 2] & 0x3f]); + bt += 3; + } + if (noes == 1) { + result.push_back(encoding_table[(uint8_t)source[bt] >> 2]); + result.push_back(encoding_table[(((uint8_t)source[bt] & 0x03) << 4) | ((uint8_t)source[bt + 1] >> 4)]); + + result.push_back(encoding_table[((uint8_t)source[bt + 1] & 0x0f) << 2]); + result.push_back('='); + } else if (noes == 2) { + result.push_back(encoding_table[(uint8_t)source[bt] >> 2]); + + result.push_back(encoding_table[((uint8_t)source[bt] & 0x03) << 4]); + result.push_back('='); + result.push_back('='); + } + return result; + } + + std::string base64_decode(const std::string& source) { +#define myMsg "Bad base64 string. Can't decode" + een9_ASSERT((source.size() & 0x3) == 0, myMsg); + if (source.empty()) + return ""; + size_t fm = (source.size() >> 2) * 3; + size_t noes = 0; + if (*(source.end() - 2) == '=') { + noes = 2; + een9_ASSERT(source.back() == '=', myMsg); + } else if (source.back() == '=') + noes = 1; + for (size_t i = 0; i + noes < source.size(); i++) + een9_ASSERT(decoding_table[(uint8_t)source[i]] < 64, myMsg); + std::string result; + static const size_t noes2ab[3] = {0, 2, 1}; + result.reserve(fm + noes2ab[noes]); + size_t naah = 0; + for (; naah < source.size(); naah += 4) { + result.push_back((char)((decoding_table[source[naah]] << 2) | (decoding_table[source[naah + 1]] >> 4))); + if (naah + 4 == source.size() && noes == 2) { + een9_ASSERT((decoding_table[source[naah + 1]] & 0x0f) == 0, myMsg); + break; + } + result.push_back((char)(((decoding_table[source[naah + 1]] & 0x0f) << 4) | (decoding_table[source[naah + 2]] >> 2))); + if (naah + 4 == source.size() && noes == 1) { + een9_ASSERT((decoding_table[source[naah + 2]] & 0x03) == 0, myMsg); + break; + } + result.push_back((char)(((decoding_table[source[naah + 2]] & 0x03) << 6) | decoding_table[source[naah + 3]])); + } + return result; + } +} diff --git a/src/web_chat/iu9_ca_web_chat_lib/str_fields.h b/src/web_chat/iu9_ca_web_chat_lib/str_fields.h new file mode 100644 index 0000000..c6cc2bd --- /dev/null +++ b/src/web_chat/iu9_ca_web_chat_lib/str_fields.h @@ -0,0 +1,25 @@ +#ifndef IU9_CA_WEB_CHAT_SRC_WEB_CHAT_STR_FIELDS_CHECK_H +#define IU9_CA_WEB_CHAT_SRC_WEB_CHAT_STR_FIELDS_CHECK_H + +#include +#include + +namespace iu9cawebchat { + bool isALPHA(char ch); + bool isNUM(char ch); + bool isUNCHAR(char ch); + bool isSPACE(char ch); + + bool is_orthodox_string(const std::string& str); + + bool check_password(const std::string& pwd); + bool check_name(const std::string& name); + bool check_nickname(const std::string& nickname); + + std::string base64_encode(const std::string& source); + + /* Кусаеца */ + std::string base64_decode(const std::string& source); +} + +#endif diff --git a/src/web_chat/iu9_ca_web_chat_service/actions.h b/src/web_chat/iu9_ca_web_chat_service/actions.h deleted file mode 100644 index edea856..0000000 --- a/src/web_chat/iu9_ca_web_chat_service/actions.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef IU9_CA_WEB_CHAT_ACTIONS_H -#define IU9_CA_WEB_CHAT_ACTIONS_H - -#include - -void run_website(const json::JSON& config); -void initialize_website(const json::JSON& config, const std::string& root_pw); - -#endif diff --git a/src/web_chat/iu9_ca_web_chat_service/find_db.cpp b/src/web_chat/iu9_ca_web_chat_service/find_db.cpp deleted file mode 100644 index 5df436f..0000000 --- a/src/web_chat/iu9_ca_web_chat_service/find_db.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "find_db.h" - -int find_db_sqlite_file_path(const json::JSON& config, std::string& res_path) { - const json::JSON& type = config["database"]["type"].g(); - if (!type.isString() && type.asString() == "sqlite3") - return -1; - const json::JSON& path = config["database"]["file"].g(); - if (!path.isString()) - return -1; - if (path.asString().empty() || path.asString()[0] == ':') - return -1; - res_path = path.asString(); - return 0; -} \ No newline at end of file diff --git a/src/web_chat/iu9_ca_web_chat_service/initialize.cpp b/src/web_chat/iu9_ca_web_chat_service/initialize.cpp deleted file mode 100644 index da05cae..0000000 --- a/src/web_chat/iu9_ca_web_chat_service/initialize.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "actions.h" -#include -#include "str_fields_check.h" -#include -#include -#include "find_db.h" -#include -#include -#include "sqlite3_wrapper.h" - -void initialize_website(const json::JSON& config, const std::string& root_pw) { - printf("Initialization...\n"); - een9_ASSERT(check_password(root_pw), "Bad root password"); - std::string db_path; - int ret; - ret = find_db_sqlite_file_path(config, db_path); - een9_ASSERT(ret == 0, "Invalid settings[\"database\"] field"); - if (een9::isRegularFile(db_path)) { - // todo: plaese, don't do this - ret = unlink(db_path.c_str()); - een9_ASSERT_pl(ret == 0); - } - een9_ASSERT(!een9::isRegularFile(db_path), "Database file exists prior to initialization. " - "Can't preceed withut harming existing data"); - SqliteConnection conn(db_path.c_str()); - assert(sqlite3_errcode(conn.hand) == SQLITE_OK); - /* Role of memeber of chat: - * 1 - admin - * 2 - regular - * 3 - read-only member - * 4 - deleted (no longer a member) - * - * If user.id is 0, it is a root user - * If chat.lastMsgId is NULL, chat is empty - * If message.previous is NULL, this message is first in it's chat - */ - sqlite_single_statement(conn.hand, "PRAGMA foreign_keys = true"); - sqlite_single_statement(conn.hand, "BEGIN"); - sqlite_single_statement(conn.hand, "CREATE TABLE `nickname` (`it` TEXT PRIMARY KEY NOT NULL) WITHOUT ROWID"); - sqlite_single_statement(conn.hand, "CREATE TABLE `user` (" - "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," - "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL," - "`name` TEXT NOT NULL," - "`chatList_HistoryId` INTEGER NOT NULL," - "`password` TEXT NOT NULL" - ")"); - sqlite_single_statement(conn.hand, "CREATE TABLE `chat` (" - "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," - "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL," - "`name` TEXT NOT NULL," - "`it_HistoryId` INTEGER NOT NULL," - "`lastMsgId` INTEGER REFERENCES `message`" - ")"); - sqlite_single_statement(conn.hand, "CREATE TABLE `user_chat_membership` (" - "`userId` INTEGER REFERENCES `user` NOT NULL," - "`chatId` INTEGER REFERENCES `chat` NOT NULL," - "`user_chatList_IncHistoryId` INTEGER NOT NULL," - "`chat_IncHistoryId` INTEGER NOT NULL," - "`role` INTEGER NOT NULL" - ")"); - sqlite_single_statement(conn.hand, "CREATE TABLE `message` (" - "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," - "`chatId` INTEGER REFERENCES `chat` NOT NULL," - "`previous` INTEGER REFERENCES `message`," - "`senderUserId` INTEGER REFERENCES `user` NOT NULL," - "`exists` BOOLEAN NOT NULL," - "`isSystem` BOOLEAN NOT NULL," - "`text` TEXT NOT NULL," - "`chat_IncHistoryId` INTEGER NOT NULL" - ")"); - sqlite_single_statement(conn.hand, "INSERT INTO `nickname` VALUES (?1)", {}, {{1, "root"}}); - sqlite_single_statement(conn.hand, "INSERT INTO `user` (`id`, `nickname`, `name`, `chatList_HistoryId`, `password`) VALUES " - "(0, ?1, ?2, 0, ?3)", {}, - {{1, "root"}, {2, "Rootov Root Rootovich"}, {3, root_pw}}); - sqlite_single_statement(conn.hand, "END"); -} diff --git a/src/web_chat/iu9_ca_web_chat_service/run.cpp b/src/web_chat/iu9_ca_web_chat_service/run.cpp deleted file mode 100644 index dbf6599..0000000 --- a/src/web_chat/iu9_ca_web_chat_service/run.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include "actions.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sqlite3_wrapper.h" -#include "str_fields_check.h" -#include "find_db.h" - -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"}} }, - een9::StaticAssetManagerRule{assets_dir + "/js", "/assets/js", {{".js", "text/js"}} }, - 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 templater; - std::unique_ptr db; - }; - std::vector worker_guest_data(slave_number); - for (int i = 0; i < slave_number; i++) { - worker_guest_data[i].templater = std::make_unique( - nytl::TemplaterSettings{nytl::TemplaterDetourRules{assets_dir + "/HypertextPages"}}); - worker_guest_data[i].templater->update(); - worker_guest_data[i].db = std::make_unique(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; - auto rteee = [&](const std::string& el_name, bool pass_phr) -> std::string { - std::string page = templater.render(el_name, - pass_phr ? std::vector{&config_presentation} : std::vector{}); - return een9::form_http_server_response_200("text/html", page); - }; - if (req.uri_path == "/" || req.uri_path == "/list-rooms") { - return rteee("list-rooms", true); - } - if (req.uri_path == "/chat") { - return rteee("chat", false); - } - if (req.uri_path == "/profile") { - return rteee("profile", false); - } - if (req.uri_path == "/registration") { - return rteee("registration", false); - } - /* 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", "

Not found!

"); - }; - - 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"); - sqlite_single_statement(wgd.db->hand, - "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& dest, const std::vector& source) { - size_t N = source.size(); - dest.resize(N); - for (size_t i = 0; i < N; i++) - een9::parse_socket_address(source[i].asString(), dest[i], sap); - }; - 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); -} \ No newline at end of file diff --git a/src/web_chat/iu9_ca_web_chat_service/main.cpp b/src/web_chat/iu9_ca_web_chat_service/service.cpp similarity index 90% rename from src/web_chat/iu9_ca_web_chat_service/main.cpp rename to src/web_chat/iu9_ca_web_chat_service/service.cpp index a067dfa..fea961a 100644 --- a/src/web_chat/iu9_ca_web_chat_service/main.cpp +++ b/src/web_chat/iu9_ca_web_chat_service/service.cpp @@ -2,7 +2,7 @@ #include #include #include -#include "actions.h" +#include #include void usage(char* argv0) { @@ -31,9 +31,9 @@ int main(int argc, char** argv){ een9_ASSERT(ROOT_PW, "No root password specified." "Assign desired root password value to environment variable ROOT_PW"); std::string root_pw = ROOT_PW; - initialize_website(config, root_pw); + iu9cawebchat::initialize_website(config, root_pw); } else if (cmd == "run") { - run_website(config); + iu9cawebchat::run_website(config); } else een9_THROW("unknown action (known are 'run', 'initialize')"); } catch (std::exception& e) { diff --git a/src/web_chat/iu9_ca_web_chat_service/sqlite3_wrapper.cpp b/src/web_chat/iu9_ca_web_chat_service/sqlite3_wrapper.cpp deleted file mode 100644 index 4a53bbf..0000000 --- a/src/web_chat/iu9_ca_web_chat_service/sqlite3_wrapper.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "sqlite3_wrapper.h" -#include "str_fields_check.h" -#include -#include -#include - -SqliteConnection::SqliteConnection(const std::string &file_path) { - int ret = sqlite3_open(file_path.c_str(), &hand); - if (ret != 0) { - een9_THROW(std::string("Can't open sqlite3 database ") + sqlite3_errstr(ret)); - } -} - -SqliteConnection::~SqliteConnection() { - if (sqlite3_close(hand) != 0) {abort();} -} - -void sqlite_single_statement(sqlite3* db_hand, const std::string& req_statement, - const std::vector>& int64_binds, - const std::vector>& text8_binds) { - sqlite3_stmt* stmt_obj = NULL; - int ret = sqlite3_prepare_v2(db_hand, req_statement.c_str(), -1, &stmt_obj, NULL); - if (ret != 0) { - int err_pos = sqlite3_error_offset(db_hand); - een9_THROW("Compilation of request\n" + req_statement + "\nfailed" + - ((err_pos >= 0) ? " with offset " + std::to_string(err_pos) : "")); - } - een9_ASSERT(ret == 0, "Can't compile request expression"); - assert(sqlite3_errcode(db_hand) == SQLITE_OK); - struct Guard1{sqlite3_stmt*& r; ~Guard1(){if (sqlite3_finalize(r) != 0) {abort();}}} guard1{stmt_obj}; - for (const std::pair& bv: int64_binds) { - ret = sqlite3_bind_int64(stmt_obj, bv.first, bv.second); - een9_ASSERT(ret, "Can't bind to parameter #" + std::to_string(bv.first)); - } - for (const std::pair& bv: text8_binds) { - een9_ASSERT(is_orthodox_string(bv.second), "Can't bind this string to parameter"); - een9_ASSERT(bv.second.size() + 1 < INT_MAX, "Ah, oh, senpai, your string is toooo huge"); - ret = sqlite3_bind_text(stmt_obj, bv.first, bv.second.c_str(), (int)bv.second.size(), SQLITE_STATIC); - } - while (true) { - ret = sqlite3_step(stmt_obj); - if (ret == SQLITE_DONE) - break; - if (ret != SQLITE_ROW) { - printf("sqlite_row error!!!\n"); - printf("%s\n", sqlite3_errmsg(db_hand)); - break; - } - int cc = sqlite3_column_count(stmt_obj); - std::vector types(cc); - for (int i = 0; i < cc; i++) { - types[i] = sqlite3_column_type(stmt_obj, i); - } - printf("Column: |"); - for (int i = 0; i < cc; i++) { - switch (types[i]) { -#define ccase(tname) case SQLITE_ ## tname: printf(" " #tname " |"); break; - ccase(INTEGER) - ccase(FLOAT) - ccase(BLOB) - ccase(NULL) - case SQLITE3_TEXT: - printf(" TEXT |"); break; - default: - een9_THROW("AAAAAA"); - } - } - printf("\n"); - printf("Values: | "); - for (int i = 0; i < cc; i++) { - if (types[i] == SQLITE_INTEGER) { - printf("%lld | ", sqlite3_column_int64(stmt_obj, i)); - } else if (types[i] == SQLITE_FLOAT) { - printf("%lf | ", sqlite3_column_double(stmt_obj, i)); - } else if (types[i] == SQLITE_BLOB) { - const void* blob = sqlite3_column_blob(stmt_obj, i); - een9_ASSERT(sqlite3_errcode(db_hand) == SQLITE_OK, "oom in sqlite3_column_blob"); - size_t sz = sqlite3_column_bytes(stmt_obj, i); - printf("Blob of size %lu | ", sz); - } else if (types[i] == SQLITE_NULL) { - printf("NULL | "); - } else { - const unsigned char* text = sqlite3_column_text(stmt_obj, i); - een9_ASSERT(text, "oom in sqlite3_column_text"); - printf("%s | ", (const char*)text); - // todo: THIS F. B.S. IS NOT SAFE - } - } - printf("\n"); - } - printf("Request steps are done\n"); -} diff --git a/src/web_chat/iu9_ca_web_chat_service/sqlite3_wrapper.h b/src/web_chat/iu9_ca_web_chat_service/sqlite3_wrapper.h deleted file mode 100644 index 6659088..0000000 --- a/src/web_chat/iu9_ca_web_chat_service/sqlite3_wrapper.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef IU9_CA_WEB_CHAT_SERVICE_SQLITE_WRAP_H -#define IU9_CA_WEB_CHAT_SERVICE_SQLITE_WRAP_H - -#include -#include -#include - -struct SqliteConnection { - sqlite3* hand = NULL; - SqliteConnection() = default; - explicit SqliteConnection(const std::string& file_path); - SqliteConnection (const SqliteConnection&) = delete; - SqliteConnection& operator= (const SqliteConnection&) = delete; - ~SqliteConnection(); -}; - -void sqlite_single_statement(sqlite3* db_hand, const std::string& req_statement, - const std::vector>& int64_binds= {}, - const std::vector>& text8_binds = {}); - -#endif diff --git a/src/web_chat/iu9_ca_web_chat_service/str_fields_check.cpp b/src/web_chat/iu9_ca_web_chat_service/str_fields_check.cpp deleted file mode 100644 index 24c1c38..0000000 --- a/src/web_chat/iu9_ca_web_chat_service/str_fields_check.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "str_fields_check.h" -#include - -bool isALPHA(char ch) { - return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z'); -} - -bool isNUM(char ch) { - return '0' <= ch && ch <= '9'; -} - -bool isUNCHAR(char ch) { - return isALPHA(ch) || isNUM(ch) || ch == '-' || ch == '_'; -} - -bool isSPACE(char ch) { - return ch == ' ' || ch == '\r' || ch == '\t' || ch == '\n'; -} - - -bool is_orthodox_string(const std::string &str) { - for (char ch: str) - if (ch == 0) - return false; - return json::isUtf8String(str); -} - -bool check_password(const std::string &pwd) { - return is_orthodox_string(pwd) && pwd.size() >= 8; -} - -bool check_name(const std::string &name) { - return is_orthodox_string(name); -} - -bool check_nickname(const std::string &nickname) { - if (nickname.empty()) - return false; - for (char ch: nickname) { - if (!isUNCHAR(ch)) - return false; - } - return true; -} diff --git a/src/web_chat/iu9_ca_web_chat_service/str_fields_check.h b/src/web_chat/iu9_ca_web_chat_service/str_fields_check.h deleted file mode 100644 index e7f951a..0000000 --- a/src/web_chat/iu9_ca_web_chat_service/str_fields_check.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef IU9_CA_WEB_CHAT_SRC_WEB_CHAT_STR_FIELDS_CHECK_H -#define IU9_CA_WEB_CHAT_SRC_WEB_CHAT_STR_FIELDS_CHECK_H - -#include - -bool isALPHA(char ch); -bool isNUM(char ch); -bool isUNCHAR(char ch); -bool isSPACE(char ch); - -bool is_orthodox_string(const std::string& str); - -bool check_password(const std::string& pwd); -bool check_name(const std::string& name); -bool check_nickname(const std::string& nickname); - -#endif diff --git a/src/web_chat/misc_tests/base64_test.cpp b/src/web_chat/misc_tests/base64_test.cpp new file mode 100644 index 0000000..c48f066 --- /dev/null +++ b/src/web_chat/misc_tests/base64_test.cpp @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +int main() { + auto test = [&](const std::string& octets) { + std::string res = base64_encode(octets); + std::string back = base64_decode(res); + if (octets != back) { + printf("Oshibka\n"); + abort(); + } + printf("Test passed\n"); + }; + std::mt19937 mt(123); + std::uniform_int_distribution dist(0, 255); + auto random_test = [&](int sz) { + std::vector octets(sz); + std::string s0(sz, 0); + printf("Test: "); + for (int i = 0; i < sz; i++) { + octets[i] = dist(mt); + printf("%3d ", octets[i]); + s0[i] = (char)(uint8_t)octets[i]; + } + printf("\n"); + std::string got1 = base64_encode(s0); + printf("Base64: %s\n", got1.c_str()); + std::string back2 = base64_decode(got1); + if (s0 != back2) { + printf("Test failed!\nBack: "); + for (size_t i = 0; i < back2.size(); i++) { + printf("%3d ", (int)(uint8_t)back2[i]); + } + printf("\n"); + abort(); + } + printf("Test passed\n"); + }; + test("//"); + test("/0"); + test("/ "); + test(" "); + test(""); + test("1"); + test("22"); + test("333"); + test("4444"); + test("55555"); + test("666666"); + test("7777777"); + test("88888888"); + test("999999999"); + test("1010101010"); + test("11111111111"); + for (int i = 0; i < 100; i++) { + for (int j = 0; j < 20; j++) { + random_test(j); + } + } + auto rb_test = [&](size_t sz) { + std::string octets(sz, 0); + for (int i = 0; i < sz; i++) + octets[i] = (char)(uint8_t)dist(mt); + try { + std::string gr = base64_decode(octets); + printf("Hoba, that was a good one\n"); + } catch (een9::ServerError& e) { + printf("Finished with error\n"); + } + }; + printf("Now it's time for robustness test\n"); + for (int j = 0; j < 16; j++) { + for (int i = 0; i < j * j * j / 100 + j * j / 5 + j * 100; i++) { + rb_test(i); + } + } + return 0; +}