Added base64 encoding/decoding, added cookie creation and parsing, divided main service into library (entry = actions.h, module: iu9cawechat::) and small executable

This commit is contained in:
Андреев Григорий 2024-08-23 15:37:29 +03:00
parent 0df109643a
commit 69d054ed04
22 changed files with 750 additions and 429 deletions

View File

@ -25,7 +25,7 @@ struct CAWebChat {
BuildUnitsArray runlevel_2; BuildUnitsArray runlevel_2;
std::string build_type; std::string build_type;
bool build_tests = false; bool make_tests = false;
std::vector<std::string> warning_flags = {"-Wall", "-Wno-unused-variable", "-Werror=return-type","-pedantic", std::vector<std::string> warning_flags = {"-Wall", "-Wno-unused-variable", "-Werror=return-type","-pedantic",
"-Wno-unused-but-set-variable", "-Wno-reorder"}; "-Wno-unused-but-set-variable", "-Wno-reorder"};
@ -49,9 +49,11 @@ struct CAWebChat {
return my_flag_collection; return my_flag_collection;
} }
CAWebChat(const std::string& _build_type, bool _build_tests, const NormalCBuildSystemCommandMeaning& cmd) explicit CAWebChat(const NormalCBuildSystemCommandMeaning& cmd){
: build_type(_build_type), build_tests(_build_tests) 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"); ASSERT(build_type == "release" || build_type == "debug", "Unknown build type");
std::vector<ExternalLibraryTarget> ext_targets = { std::vector<ExternalLibraryTarget> ext_targets = {
@ -75,6 +77,7 @@ struct CAWebChat {
"os_utils.cpp", "os_utils.cpp",
"http_structures/client_request_parse.cpp", "http_structures/client_request_parse.cpp",
"http_structures/response_gen.cpp", "http_structures/response_gen.cpp",
"http_structures/cookies.cpp",
"connecting_assets/static_asset_manager.cpp", "connecting_assets/static_asset_manager.cpp",
"running_mainloop.cpp", "running_mainloop.cpp",
"form_data_structure/urlencoded_query.cpp", "form_data_structure/urlencoded_query.cpp",
@ -92,6 +95,7 @@ struct CAWebChat {
"os_utils.h", "os_utils.h",
"connecting_assets/static_asset_manager.h", "connecting_assets/static_asset_manager.h",
"http_structures/client_request.h", "http_structures/client_request.h",
"http_structures/cookies.h",
"http_structures/response_gen.h", "http_structures/response_gen.h",
"running_mainloop.h", "running_mainloop.h",
"form_data_structure/urlencoded_query.h", "form_data_structure/urlencoded_query.h",
@ -127,27 +131,42 @@ struct CAWebChat {
T.installation_dir = "nytl"; T.installation_dir = "nytl";
my_targets.push_back(T); 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.additional_compilation_flags = getSomeRadFlags();
T.proj_deps = { T.proj_deps = {
CTargetDependenceOnProjectsLibrary{"engine_engine_number_9"}, CTargetDependenceOnProjectsLibrary{"engine_engine_number_9", {true, true}},
CTargetDependenceOnProjectsLibrary{"new_york_transit_line"}, CTargetDependenceOnProjectsLibrary{"new_york_transit_line", {true, true}},
}; };
T.external_deps = { T.external_deps = {
CTargetDependenceOnExternalLibrary{"sqlite3"} CTargetDependenceOnExternalLibrary{"sqlite3", {true, true}}
}; };
T.units = { T.units = {
"main.cpp",
"initialize.cpp", "initialize.cpp",
"run.cpp", "run.cpp",
"str_fields_check.cpp", "str_fields.cpp",
"find_db.cpp", "find_db.cpp",
"sqlite3_wrapper.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) for (std::string& u: T.units)
u = "web_chat/iu9_ca_web_chat_service/" + u; u = "web_chat/iu9_ca_web_chat_service/" + u;
T.include_pr = "web_chat"; T.include_pr = "web_chat";
T.installation_dir = "";
my_targets.push_back(T); my_targets.push_back(T);
} }
{ CTarget T{"iu9-ca-web-chat-admin-cli", "executable"}; { CTarget T{"iu9-ca-web-chat-admin-cli", "executable"};
@ -177,9 +196,7 @@ int main(int argc, char** argv) {
} }
NormalCBuildSystemCommandMeaning cmd; NormalCBuildSystemCommandMeaning cmd;
regular_bs_cli_cmd_interpret(args, cmd); regular_bs_cli_cmd_interpret(args, cmd);
const char* BS_SCRIPT_TYPE = getenv("BSCRIPT_TYPE"); CAWebChat bs(cmd);
const char* BS_SCRIPT_TESTS = getenv("BSCRIPT_TESTS");
CAWebChat bs(BS_SCRIPT_TYPE ? BS_SCRIPT_TYPE : "release", (bool)BS_SCRIPT_TESTS, cmd);
if (cmd.need_to_build) if (cmd.need_to_build)
complete_tasks_of_build_units(bs.runlevel_1); complete_tasks_of_build_units(bs.runlevel_1);
umask(~0755); umask(~0755);

View File

@ -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<std::pair<std::string, std::string>> parseCookieHeader(const std::string &hv) {
std::vector<std::pair<std::string, std::string>> 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<std::pair<std::string, std::string>>& new_cookies,
std::vector<std::pair<std::string, std::string>>& res_header_lines) {
for (const std::pair<std::string, std::string>& cookie : new_cookies) {
ASSERT_pl(isCookieName(cookie.first) && isCookieValue(cookie.second));
res_header_lines.emplace_back("Set-Cookie", cookie.first + "=" + cookie.second + ";SameSite=Strict");
}
}
}

View File

@ -0,0 +1,22 @@
#ifndef HTTP_STRUCTURES_COOKIES_H
#define HTTP_STRUCTURES_COOKIES_H
#include <string>
#include <vector>
#include "../baza.h"
#include <map>
namespace een9 {
bool isCookieName(const std::string& str);
bool isCookieValue(const std::string& str);
/* Throws een9::ServerError on failure */
std::vector<std::pair<std::string, std::string>> 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<std::pair<std::string, std::string>>& new_cookies,
std::vector<std::pair<std::string, std::string>>& res_header_lines);
}
#endif

View File

@ -0,0 +1,11 @@
#ifndef IU9_CA_WEB_CHAT_ACTIONS_H
#define IU9_CA_WEB_CHAT_ACTIONS_H
#include <jsonincpp/jsonobj.h>
namespace iu9cawebchat {
void run_website(const json::JSON& config);
void initialize_website(const json::JSON& config, const std::string& root_pw);
}
#endif

View File

@ -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;
}
}

View File

@ -3,6 +3,8 @@
#include <jsonincpp/jsonobj.h> #include <jsonincpp/jsonobj.h>
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 #endif

View File

@ -0,0 +1,78 @@
#include "actions.h"
#include <engine_engine_number_9/baza_throw.h>
#include "str_fields.h"
#include <sqlite3.h>
#include <engine_engine_number_9/os_utils.h>
#include "find_db.h"
#include <unistd.h>
#include <assert.h>
#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");
}
}

View File

@ -0,0 +1,140 @@
#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"
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<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;
auto rteee = [&](const std::string& el_name, bool pass_phr) -> std::string {
std::string page = templater.render(el_name,
pass_phr ? std::vector<const json::JSON*>{&config_presentation} : std::vector<const json::JSON*>{});
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", "<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");
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<een9::SocketAddress>& dest, const std::vector<json::JSON>& 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);
}
}

View File

@ -0,0 +1,94 @@
#include "sqlite3_wrapper.h"
#include "str_fields.h"
#include <engine_engine_number_9/baza_throw.h>
#include <assert.h>
#include <limits.h>
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<std::pair<int, int64_t>>& int64_binds,
const std::vector<std::pair<int, std::string>>& 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<int, int64_t>& 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<int, std::string>& 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<int> 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");
}
}

View File

@ -0,0 +1,23 @@
#ifndef IU9_CA_WEB_CHAT_SERVICE_SQLITE_WRAP_H
#define IU9_CA_WEB_CHAT_SERVICE_SQLITE_WRAP_H
#include <sqlite3.h>
#include <vector>
#include <string>
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<std::pair<int, int64_t>>& int64_binds= {},
const std::vector<std::pair<int, std::string>>& text8_binds = {});
}
#endif

View File

@ -0,0 +1,141 @@
#include "str_fields.h"
#include <jsonincpp/utf8.h>
#include <engine_engine_number_9/baza_throw.h>
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;
}
}

View File

@ -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 <string>
#include <stdint.h>
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

View File

@ -1,9 +0,0 @@
#ifndef IU9_CA_WEB_CHAT_ACTIONS_H
#define IU9_CA_WEB_CHAT_ACTIONS_H
#include <jsonincpp/jsonobj.h>
void run_website(const json::JSON& config);
void initialize_website(const json::JSON& config, const std::string& root_pw);
#endif

View File

@ -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;
}

View File

@ -1,76 +0,0 @@
#include "actions.h"
#include <engine_engine_number_9/baza_throw.h>
#include "str_fields_check.h"
#include <sqlite3.h>
#include <engine_engine_number_9/os_utils.h>
#include "find_db.h"
#include <unistd.h>
#include <assert.h>
#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");
}

View File

@ -1,138 +0,0 @@
#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_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<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;
auto rteee = [&](const std::string& el_name, bool pass_phr) -> std::string {
std::string page = templater.render(el_name,
pass_phr ? std::vector<const json::JSON*>{&config_presentation} : std::vector<const json::JSON*>{});
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", "<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");
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<een9::SocketAddress>& dest, const std::vector<json::JSON>& 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);
}

View File

@ -2,7 +2,7 @@
#include <engine_engine_number_9/os_utils.h> #include <engine_engine_number_9/os_utils.h>
#include <assert.h> #include <assert.h>
#include <jsonincpp/string_representation.h> #include <jsonincpp/string_representation.h>
#include "actions.h" #include <iu9_ca_web_chat_lib/actions.h>
#include <stdexcept> #include <stdexcept>
void usage(char* argv0) { void usage(char* argv0) {
@ -31,9 +31,9 @@ int main(int argc, char** argv){
een9_ASSERT(ROOT_PW, "No root password specified." een9_ASSERT(ROOT_PW, "No root password specified."
"Assign desired root password value to environment variable ROOT_PW"); "Assign desired root password value to environment variable ROOT_PW");
std::string root_pw = ROOT_PW; std::string root_pw = ROOT_PW;
initialize_website(config, root_pw); iu9cawebchat::initialize_website(config, root_pw);
} else if (cmd == "run") { } else if (cmd == "run") {
run_website(config); iu9cawebchat::run_website(config);
} else } else
een9_THROW("unknown action (known are 'run', 'initialize')"); een9_THROW("unknown action (known are 'run', 'initialize')");
} catch (std::exception& e) { } catch (std::exception& e) {

View File

@ -1,92 +0,0 @@
#include "sqlite3_wrapper.h"
#include "str_fields_check.h"
#include <engine_engine_number_9/baza_throw.h>
#include <assert.h>
#include <limits.h>
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<std::pair<int, int64_t>>& int64_binds,
const std::vector<std::pair<int, std::string>>& 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<int, int64_t>& 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<int, std::string>& 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<int> 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");
}

View File

@ -1,21 +0,0 @@
#ifndef IU9_CA_WEB_CHAT_SERVICE_SQLITE_WRAP_H
#define IU9_CA_WEB_CHAT_SERVICE_SQLITE_WRAP_H
#include <sqlite3.h>
#include <vector>
#include <string>
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<std::pair<int, int64_t>>& int64_binds= {},
const std::vector<std::pair<int, std::string>>& text8_binds = {});
#endif

View File

@ -1,44 +0,0 @@
#include "str_fields_check.h"
#include <jsonincpp/utf8.h>
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;
}

View File

@ -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 <string>
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

View File

@ -0,0 +1,80 @@
#include <iu9_ca_web_chat_lib/str_fields.h>
#include <random>
#include <stdexcept>
#include <engine_engine_number_9/baza.h>
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<int> dist(0, 255);
auto random_test = [&](int sz) {
std::vector<int> 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;
}