Compare commits

..

No commits in common. "925229bbda50735aca5fcdd0c2aeebf95a9cf24d" and "2ad4f5b52d511c4a4d14d941393de2fbc57a3c25" have entirely different histories.

55 changed files with 577 additions and 1127 deletions

1
.gitignore vendored
View File

@ -16,4 +16,3 @@ iu9-ca-web-chat.db
log/ log/
core core
config/example.json

View File

@ -1,29 +0,0 @@
{% ELDEF main JSON pres JSON userinfo JSON openedchat JSON initial_chatUpdResp %}
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Веб-Чат Members</title>
<link rel="stylesheet" href="/assets/css/chat.css">
</head>
<body>
{% PUT chat.pass pres userinfo openedchat initial_chatUpdResp %}
<!--TODO: ADD SOMETHING WRITE SOMETHING AAAAAA -->
<div class="overlay" id="overlay">
<div class="members-list" id="members-list">
<div class="members-list-header">
<span class="close" onclick="closeMembersList()">&times;</span>
<h2 class="all-members">Все участники</h2>
</div>
<div class="members-list-body">
<ul id="members-list-body">
<!-- Список участников будет добавлен динамически -->
</ul>
</div>
</div>
</div>
<script src="/assets/js/chat-members.js"></script>
</body>
</html>
{% ENDELDEF %}

View File

@ -1,13 +1,4 @@
{% ELDEF pass JSON pres JSON userinfo JSON openedchat JSON initial_chatUpdResp %} {% ELDEF main JSON pres JSON userinfo %}
<script>
let pres = {% PUT jsinsert pres %};
let userinfo = {% PUT jsinsert userinfo %};
let openedchat = {% PUT jsinsert openedchat %};
let initial_chatUpdResp = {% PUT jsinsert initial_chatUpdResp %};
</script>
{% ENDELDEF %}
{% ELDEF main JSON pres JSON userinfo JSON openedchat JSON initial_chatUpdResp %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="ru"> <html lang="ru">
<head> <head>
@ -17,8 +8,6 @@
<link rel="stylesheet" href="/assets/css/chat.css"> <link rel="stylesheet" href="/assets/css/chat.css">
</head> </head>
<body> <body>
{% PUT chat.pass pres userinfo openedchat initial_chatUpdResp %}
<!-- TODO AAAAA-->
<div class="chat-container"> <div class="chat-container">
<div class="chat-header"> <div class="chat-header">
<span class="room-name">Веб чат</span> <span class="room-name">Веб чат</span>
@ -32,6 +21,19 @@
<button class="chat-send-button" onclick="sendMessage()">Отправить</button> <button class="chat-send-button" onclick="sendMessage()">Отправить</button>
</div> </div>
</div> </div>
<div class="overlay" id="overlay">
<div class="members-list" id="members-list">
<div class="members-list-header">
<span class="close" onclick="closeMembersList()">&times;</span>
<h2 class="all-members">Все участники</h2>
</div>
<div class="members-list-body">
<ul id="members-list-body">
<!-- Список участников будет добавлен динамически -->
</ul>
</div>
</div>
</div>
<script src="/assets/js/chat.js"></script> <script src="/assets/js/chat.js"></script>
</body> </body>
</html> </html>

View File

@ -1,42 +0,0 @@
{% ELDEF main JSON pres JSON userprofile JSON errors %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/assets/css/profile.css">
<title>Профиль</title>
</head>
<body>
<div class="main-container">
{% FOR error IN errors %}
<div>
<p>{% WRITE error.text %}</p>
</div>
{% ENDFOR %}
<div class="profile-header">
<h1>Редактирование профиля</h1>
</div>
<form method="post" action="/user/{% WRITE userprofile.nickname %}">
<div class="columns">
<div class="column">
<p>{% WRITE userprofile.name %} ( {% WRITE userprofile.nickname %} )</p>
<label for="name"> Изменить имя </label>
<input type="text" name="name" id="name">
<br>
<label for="password"> Изменить пароль </label>
<input type="password" name="password" id="password">
<br>
</div>
</div>
<div class="additional-info">
<p>О себе</p>
<p>{% WRITE userprofile.bio %}</p><br>
<label for="bio">Изменить</label>
<textarea name="bio" id="bio"></textarea>
</div>
<button class="save" type="submit">Сохранить изменения</button>
</form>
</div>
</body>
</html>
{% ENDELDEF%}

View File

@ -1,10 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Not found</title>
</head>
<body>
<h1>Page not found</h1>
</body>
</html>

View File

@ -1,4 +1,4 @@
{% ELDEF main JSON pres JSON userinfo JSON initial_chatListUpdResp %} {% ELDEF main JSON pres JSON userinfo %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{% WRITE pres.lang %}"> <html lang="{% WRITE pres.lang %}">
<head> <head>
@ -8,11 +8,7 @@
<link rel="stylesheet" href="/assets/css/list-rooms.css"> <link rel="stylesheet" href="/assets/css/list-rooms.css">
</head> </head>
<body> <body>
<script> {% PUT pass-pres-userinfo pres userinfo %}
let pres = {% PUT jsinsert pres %};
let userinfo = {% PUT jsinsert userinfo %};
let initial_chatListUpdResp = {% PUT jsinsert initial_chatListUpdResp %};
</script>
<div class="container"> <div class="container">
<h1 style="color: white;">Выберите Чат-Комнату</h1> <h1 style="color: white;">Выберите Чат-Комнату</h1>
<ul class="room-list"> <ul class="room-list">

View File

@ -1,4 +1,4 @@
{% ELDEF main JSON pres JSON userinfo JSON errors %} {% ELDEF main JSON pres JSON userinfo %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{% WRITE pres.lang %}"> <html lang="{% WRITE pres.lang %}">
<head> <head>
@ -6,14 +6,11 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% WRITE pres.phr.decl.page-login %}</title> <title>{% WRITE pres.phr.decl.page-login %}</title>
<link rel="stylesheet" href="/assets/css/login.css"> <link rel="stylesheet" href="/assets/css/login.css">
</head> </head>
<body> <body>
{% FOR msg IN errors %} {% PUT pass-pres-userinfo pres userinfo %}
<div class="error-msg">
{% WRITE msg.text %}
</div>
{% ENDFOR %}
<div class="form-container"> <div class="form-container">
<h1 class="hide-cursor no-select">{% WRITE pres.phr.decl.enter %}</h1> <h1 class="hide-cursor no-select">{% WRITE pres.phr.decl.enter %}</h1>
<form action="/login" method="post" enctype="application/x-www-form-urlencoded"> <form action="/login" method="post" enctype="application/x-www-form-urlencoded">

View File

@ -0,0 +1,6 @@
{% ELDEF main JSON pres JSON userinfo %}
<script>
let pres = {% PUT jsinsert pres %};
let userinfo = {% PUT jsinsert userinfo %};
</script>
{% ENDELDEF %}

View File

@ -0,0 +1,39 @@
{% ELDEF main JSON pres JSON userinfo %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/assets/css/profile.css">
<title>Профиль</title>
</head>
<body>
<div class="main-container">
<div class="profile-header">
<h1 style="color: white; text-align: center;">Профиль пользователя</h1>
<a class="return" href="chat.nytl.html">Назад</a>
</div>
<form>
<div class="columns">
<div class="column">
<img class="avatar" src="/assets/img/empty_avatar.png" id="avatar" height="200" width="200"><br>
<input type="file" id="fileInput" style="display:none">
<button class="add" type="button" onclick="document.getElementById('fileInput').click();">Изменить фото</button><br>
</div>
<div class="column">
<input type="text" name="username" placeholder = "Имя пользователя" value="Some Name" id="username"><br>
<input type="text" name="login" placeholder="Логин" value="some_login123" id="login" readonly><br>
</div>
</div>
<h3 style="color:#007bb5;">О себе</h3>
<div class="additional-info">
<textarea name="bio" placeholder="Напишите о себе..." id="bio"></textarea>
</div>
<button class="save" type="submit">Сохранить изменения</button>
</form>
</div>
<script src="/assets/js/list-rooms.js"> </script>
</body>
</html>
{% ENDELDEF%}

View File

@ -0,0 +1,27 @@
{% ELDEF main JSON pres JSON userinfo %}
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Страница Регистрации</title>
<link rel="stylesheet" href="/assets/css/registration.css">
</head>
<body>
<div class="form-container">
<h1 class="hide-cursor no-select">Вход</h1>
<form action="assets/html/list-rooms.html" method="post">
<input type="text" name="username" placeholder="Имя пользователя" id="username"><br>
<input type="text" name="login" placeholder="Логин" id="login"><br>
<input type="password" name="password" placeholder="Пароль" id="password"><br>
<button type="submit" class="hide-cursor no-select">Зарегистрироваться</button>
<div id="error"></div>
</form>
</div>
<script src="assets/js/registration.js"></script>
</body>
</html>
{% ENDELDEF %}

View File

@ -1,26 +0,0 @@
{% ELDEF main JSON pres JSON userprofile %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="/assets/css/profile.css">
<title>Профиль</title>
</head>
<body>
<div class="main-container">
<div class="profile-header">
<h1>Профиль пользователя</h1>
</div>
<div class="columns">
<div class="column">
<p>{% WRITE userprofile.name %} ( {% WRITE userprofile.nickname %} )</p>
</div>
</div>
<div class="additional-info">
<p>О себе</p>
<p>{% WRITE userprofile.bio %}</p><br>
</div>
</div>
</body>
</html>
{% ENDELDEF%}

View File

@ -1,5 +1,4 @@
/* I have no idea what is going on here */ dy {
body {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -51,9 +50,28 @@ button {
transition: background-color 0.3s; transition: background-color 0.3s;
} }
.error-msg { button:hover,
color: red; button:focus-visible {
background-color: #ffc0c0; background-color: #007bb5;
border-color: red; }
border-radius: 5px;
.hide-cursor::placeholder {
color: #000;
}
.hide-cursor {
caret-color: transparent;
}
.no-select {
-webkit-user-select: none; /* Для Safari */
-moz-user-select: none; /* Для Firefox */
user-select: none; /* Для всех остальных браузеров */
}
div {
color: red;
font-size: 15px;
margin-top: 10px;
display: none;
} }

BIN
assets/img/empty_avatar.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

10
assets/js/profile.js Normal file
View File

@ -0,0 +1,10 @@
document.getElementById('fileInput').addEventListener('change', function(event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = function(e) {
document.getElementById('avatar').src = e.target.result;
};
reader.readAsDataURL(file);
}
});

View File

@ -149,8 +149,16 @@ struct CAWebChat {
"login_cookie.cpp", "login_cookie.cpp",
"backend_logic/server_data_interact.cpp", "backend_logic/server_data_interact.cpp",
"backend_logic/client_server_interact.cpp", "backend_logic/client_server_interact.cpp",
"backend_logic/when_list_rooms.cpp",
"backend_logic/polling.cpp", "backend_logic/when_login.cpp",
"backend_logic/when_chat.cpp",
"backend_logic/when_user.cpp",
"backend_logic/when_api_pollevents.cpp",
"backend_logic/when_api_getchatlist.cpp",
"backend_logic/when_api_getchatinfo.cpp",
"backend_logic/when_api_getchatmemberlist.cpp",
"backend_logic/when_api_getuserinfo.cpp",
"backend_logic/when_api_getmessageinfo.cpp",
}; };
for (std::string& u: T.units) for (std::string& u: T.units)
u = "web_chat/iu9_ca_web_chat_lib/" + u; u = "web_chat/iu9_ca_web_chat_lib/" + u;

View File

View File

@ -12,11 +12,7 @@
"page-login": "Вход", "page-login": "Вход",
"list-of-chat-rooms": "Список Чат-Коsмнат", "list-of-chat-rooms": "Список Чат-Коsмнат",
"name-of-room": "Название комнаты", "name-of-room": "Название комнаты",
"create-room": "Создать комнату", "create-room": "Создать комнату"
"incorrect-nickname-or-password": "Неправильный никнейм или пароль",
"incorrect-profile-data": "Неправильно заполнены поля профиля"
}, },
"ask" : { "ask" : {
"select-chat-room": "Выберете чат комнату" "select-chat-room": "Выберете чат комнату"
@ -31,7 +27,7 @@
}, },
"assets": "./assets", "assets": "./assets",
"database": { "database": {
"type": "sqlite3", "type": "sqlite",
"file": "./iu9-ca-web-chat.db" "file": "./iu9-ca-web-chat.db"
}, },
"limits": { "limits": {

View File

@ -111,7 +111,7 @@ namespace een9 {
status = -1; status = -1;
return status; return status;
} }
res.body.reserve(std::min(100000ul, body_size)); res.body.reserve(body_size);
} }
} }
if (!res.has_body) { if (!res.has_body) {

View File

@ -43,7 +43,7 @@ namespace een9 {
pos++; pos++;
return hv.substr(S, pos - S); return hv.substr(S, pos - S);
}; };
auto read_to_space_or_semc = [&]() -> std::string { auto read_to_space_or_dq_or_semc = [&]() -> std::string {
size_t S = pos; size_t S = pos;
while (hv.size() > pos && !isSPACE(hv[pos]) && hv[pos] != '"' && hv[pos] != ';') while (hv.size() > pos && !isSPACE(hv[pos]) && hv[pos] != '"' && hv[pos] != ';')
pos++; pos++;
@ -68,7 +68,16 @@ namespace een9 {
THROW("Incorrect Cookie header line, missing ="); THROW("Incorrect Cookie header line, missing =");
pos++; pos++;
skip_ows(); skip_ows();
std::string value_of_pechenye = read_to_space_or_semc(); std::string value_of_pechenye;
if (isThis('"')) {
pos++;
value_of_pechenye = read_to_space_or_dq_or_semc();
if (!isThis('"'))
THROW("Incorrect Cookie header line, missing \"");
pos++;
} else {
value_of_pechenye = read_to_space_or_dq_or_semc();
}
// ASSERT(isCookieValue(value_of_pechenye), "Incorrect Cookie value"); // ASSERT(isCookieValue(value_of_pechenye), "Incorrect Cookie value");
result.emplace_back(name_of_pechenye, value_of_pechenye); result.emplace_back(name_of_pechenye, value_of_pechenye);
skip_ows(); skip_ows();

View File

@ -258,8 +258,8 @@ namespace een9 {
} }
errno = 0; errno = 0;
ret = poll(pollfds.data(), Nip, params.mainloop_recheck_interval_us); ret = poll(pollfds.data(), Nip, params.mainloop_recheck_interval_us);
if (ret != 0 && errno != 0) { if (ret != 0) {
printf("poll() error :> %s\n", een9::prettyprint_errno("").c_str()); printf("poll() error :> %d\n", errno);
} }
ASSERT_on_iret(ret, "poll()"); ASSERT_on_iret(ret, "poll()");
for (size_t i = 0; i < Nip; i++) { for (size_t i = 0; i < Nip; i++) {

View File

@ -18,14 +18,7 @@ int main(int argc, char** argv) {
een9::readFile(config_file, config_text); een9::readFile(config_file, config_text);
const json::JSON config = json::parse_str_flawless(config_text); const json::JSON config = json::parse_str_flawless(config_text);
json::JSON userprofile; std::string answer2 = templater.render("login", {&config["presentation"].g()});
userprofile["uid"].asInteger() = json::Integer(0l);
userprofile["name"].asString() = "radasdasdasdadsdasd";
userprofile["nickname"].asString() = "root";
userprofile["bio"].asString() = "Your mother";
json::JSON errors;
errors = json::JSON(json::array);
std::string answer2 = templater.render("edit-profile", {&config["presentation"], &userprofile, &errors});
printf("%s\n<a><f><t><e><r><><l><f>\n", answer2.c_str()); printf("%s\n<a><f><t><e><r><><l><f>\n", answer2.c_str());
return 0; return 0;

View File

@ -178,7 +178,7 @@ namespace nytl {
} }
ASSERT(nm == "EL", "Type of argument variable is either JSON or EL(...signature)") ASSERT(nm == "EL", "Type of argument variable is either JSON or EL(...signature)")
skip(ctx, '('); skip(ctx, '(');
result.asArray(); result = json::JSON(json::array);
assert(result.isArray()); assert(result.isArray());
} }
skipWhitespace(ctx); skipWhitespace(ctx);
@ -220,16 +220,16 @@ namespace nytl {
ASSERT(!first.empty(), "Expression should start with 'root' name of global package or local variable"); ASSERT(!first.empty(), "Expression should start with 'root' name of global package or local variable");
ASSERT(first != "_", "_ ??? ARE YOU KIDDING???"); ASSERT(first != "_", "_ ??? ARE YOU KIDDING???");
if (local_var_names.count(first) == 1) { if (local_var_names.count(first) == 1) {
result["V"].asInteger() = json::Integer((int64_t)local_var_names.at(first)); result["V"] = json::JSON(json::Integer((int64_t)local_var_names.at(first)));
} else { } else {
result["V"].asString() = first; result["V"] = json::JSON(first);
} }
result["C"].asArray(); result["C"] = json::JSON(json::array);
} else { } else {
skipWhitespace(ctx); skipWhitespace(ctx);
skip(ctx, ']'); skip(ctx, ']');
} }
std::vector<json::JSON>& chain = result["C"].asArray(); std::vector<json::JSON>& chain = result["C"].g().asArray();
while (true) { while (true) {
if (peep(ctx) == '.') { if (peep(ctx) == '.') {
skip(ctx, '.'); skip(ctx, '.');

View File

@ -56,18 +56,18 @@ namespace nytl {
descend(*(temp_ret.JSON_subval)); descend(*(temp_ret.JSON_subval));
} else { } else {
assert(expr.isDictionary()); assert(expr.isDictionary());
const json::JSON& val = expr["V"]; const json::JSON& val = expr["V"].g();
if (val.isInteger()) { if (val.isInteger()) {
size_t lv_ind = val.asInteger().get_int(); size_t lv_ind = val.asInteger().get_int();
assert(lv_ind < local_vars.size()); assert(lv_ind < local_vars.size());
result = local_vars[lv_ind]; result = local_vars[lv_ind];
} else if (val.isString()) { } else if (val.isString()) {
std::string cur_el_name_str = expr["V"].asString(); std::string cur_el_name_str = expr["V"].g().asString();
result = LocalVarValue{false, cur_el_name_str, NULL}; result = LocalVarValue{false, cur_el_name_str, NULL};
} else } else
assert(false); assert(false);
} }
const std::vector<json::JSON>& chain = expr["C"].asArray(); const std::vector<json::JSON>& chain = expr["C"].g().asArray();
while (true) { while (true) {
if (chain_el >= chain.size()) if (chain_el >= chain.size())
return NULL; return NULL;
@ -211,8 +211,7 @@ namespace nytl {
uptr<RFrame> RFrame_OverParts::toMe(bool returned, const global_elem_set_t &elem_ns, Ditch &result, uptr<RFrame> RFrame_OverParts::toMe(bool returned, const global_elem_set_t &elem_ns, Ditch &result,
const std::function<std::string(std::string)> &escape) { const std::function<std::string(std::string)> &escape) {
if (!returned) if (!returned)
if (elem_ns.count(name) != 1) ASSERT(elem_ns.count(name) == 1, "No such element");
THROW("No such element");
const Element& el = elem_ns.at(name); const Element& el = elem_ns.at(name);
if (!returned) { if (!returned) {
/* Continue to do checks */ /* Continue to do checks */

View File

@ -1,30 +0,0 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/baza_throw.h>
#include <engine_engine_number_9/baza.h>
#include "../str_fields.h"
namespace iu9cawebchat {
std::string admin_control_procedure(SqliteConnection& conn, const std::string& req, bool& termination) {
if (req == "hello") {
return ":0 omg! hiii!! Hewwou :3 !!!!\n";
}
if (req == "8") {
termination = true;
return "Bye\n";
}
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\n";
std::string new_password = req.substr(nid + 1);
if (!check_password(new_password))
een9_THROW("Bad password");
sqlite_nooutput(conn,
"UPDATE `user` SET `password` = ?1 WHERE `id` = 0 ",
{}, {{1, new_password}});
return "Successul update\n";
}
return "Incorrect command\n";
}
}

View File

@ -1,48 +0,0 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/baza_throw.h>
#include <assert.h>
namespace iu9cawebchat {
void make_her_a_member_of_the_midnight_crew(SqliteConnection& conn, int64_t chatId, int64_t alienUserId, int64_t role) {
assert(role != user_chat_role_deleted);
int64_t chat_HistoryId_BEFORE_EV = get_current_history_id_of_chat(conn, chatId);
int64_t alien_chatlist_HistoryId_BEFORE_EV = get_current_history_id_of_user_chatList(conn, alienUserId);
sqlite_nooutput(conn,
"INSERT INTO `user_chat_membership` (`userId`, `chatId`, `user_chatList_IncHistoryId`,"
"`chat_IncHistoryId`, `role`) VALUES (?1, ?2, ?3, ?4, ?5)",
{{1, alienUserId}, {2, chatId}, {3, alien_chatlist_HistoryId_BEFORE_EV + 1},
{4, chat_HistoryId_BEFORE_EV + 1}, {5, role}}, {});
sqlite_nooutput(conn,
"UPDATE `chat` SET `it_HistoryId` = ?1 WHERE `id` = ?2", {{1, chat_HistoryId_BEFORE_EV + 1},
{2, chatId}}, {});
sqlite_nooutput(conn,
"UPDATE `user` SET `chatList_HistoryId` = ?1 WHERE `id` = ?2",
{{1, alien_chatlist_HistoryId_BEFORE_EV + 1}, {2, alienUserId}}, {});
}
json::JSON internalapi_addMemberToChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
int64_t chatId = Sent["chatUpdReq"]["chatId"].asInteger().get_int();
int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId);
if (my_role_here != user_chat_role_admin)
een9_THROW("Non-admin user tries to access internalapi_getChatInfo");
std::string alien_nickname = Sent["nickname"].asString();
RowUser_Content alien;
try {
alien = lookup_user_content_by_nickname(conn, alien_nickname);
} catch (std::exception& e) {
return json::JSON(json::jdict{{"status", json::JSON(-1l)}});
}
int64_t aliens_old_role = get_role_of_user_in_chat(conn, alien.id, chatId);
if (aliens_old_role == user_chat_role_deleted) {
make_her_a_member_of_the_midnight_crew(conn, chatId, alien.id, user_chat_role_regular);
}
json::JSON Recv;
poll_update_chat(conn, Sent, Recv);
return Recv;
}
}

View File

@ -1,27 +0,0 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/baza_throw.h>
#include "../str_fields.h"
namespace iu9cawebchat {
json::JSON internalapi_createChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
std::string new_chat_name = Sent["content"]["name"].asString();
std::string new_chat_nickname = Sent["content"]["nickname"].asString();
if (!check_nickname(new_chat_nickname) || !check_name(new_chat_name))
return json::JSON(json::jdict{{"status", json::JSON(-1l)}});
if (is_nickname_taken(conn, new_chat_nickname))
return json::JSON(json::jdict{{"status", json::JSON(-2l)}});
reserve_nickname(conn, new_chat_nickname);
sqlite_nooutput(conn,
"INSERT INTO `chat` (`nickname`, `name`, `it_HistoryId`, `lastMsgId`) VALUES (?1, ?2, 0, -1)");
int64_t CHAT_ID = sqlite_trsess_last_insert_rowid(conn);
make_her_a_member_of_the_midnight_crew(conn, CHAT_ID, uid, user_chat_role_admin);
// todo: send a message into chat
json::JSON Recv;
poll_update_chat_list(conn, uid, Sent, Recv);
return Recv;
}
}

View File

@ -1,32 +0,0 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/baza_throw.h>
namespace iu9cawebchat {
json::JSON internalapi_deleteMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
int64_t chatId = Sent["chatUpdReq"].asInteger().get_int();
int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId);
if (my_role_here == user_chat_role_deleted)
een9_THROW("Unauthorized user tries to access internalapi_getChatInfo");
if (my_role_here == user_chat_role_read_only)
een9_THROW("read-only user can't send messages");
int64_t LocalHistoryId = Sent["chatUpdReq"]["LocalHistoryId"].asInteger().get_int();
int64_t msgId = Sent["id"].asInteger().get_int();
RowMessage_Content msgInQuestion = lookup_message_content(conn, chatId, msgId);
if (!(!msgInQuestion.isSystem && (msgInQuestion.senderUserId == uid || my_role_here == user_chat_role_admin) ))
een9_THROW("Can't delete: permission denied");
int64_t chat_HistoryId_BEFORE_EV = get_current_history_id_of_chat(conn, chatId);
sqlite_nooutput(conn,
"UPDATE `message` SET `exists` = 0, `text` = NULL, `chat_IncHistoryId` = ?1 WHERE `id` = ?2",
{{1, chat_HistoryId_BEFORE_EV + 1}, {2, msgId}});
sqlite_nooutput(conn, "UPDATE `chat` SET `it_HistoryId` = ?1 WHERE `id` = ?2",
{{1, chat_HistoryId_BEFORE_EV + 1}, {2, chatId}}, {});
json::JSON Recv;
poll_update_chat(conn, Sent, Recv);
return Recv;
}
}

View File

@ -1,14 +0,0 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/baza_throw.h>
namespace iu9cawebchat {
json::JSON internalapi_leaveChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
int64_t chatId = Sent["chatId"].asInteger().get_int();
if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted)
een9_THROW("Not a member");
kick_from_chat(conn, uid, chatId);
json::JSON Recv;
poll_update_chat_list(conn, uid, Sent, Recv);
return Recv;
}
}

View File

@ -1,37 +0,0 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/baza_throw.h>
namespace iu9cawebchat {
void kick_from_chat(SqliteConnection& conn, int64_t alienUserId, int64_t chatId) {
if (get_role_of_user_in_chat(conn, alienUserId, chatId) == user_chat_role_deleted)
een9_THROW("Can't delete a deleted member");
int64_t chat_HistoryId_BEFORE_EV = get_current_history_id_of_chat(conn, chatId);
int64_t alien_chatlist_HistoryId_BEFORE_EV = get_current_history_id_of_user_chatList(conn, alienUserId);
sqlite_nooutput(conn,
"UPDATE `user_chat_membership` SET `user_chatList_IncHistoryId` = ?3,"
"`chat_IncHistoryId` = ?4, `role` = ?5 WHERE `userId` = ?1 AND `chatId` = ?2",
{{1, alienUserId}, {2, chatId}, {3, alien_chatlist_HistoryId_BEFORE_EV + 1},
{4, chat_HistoryId_BEFORE_EV + 1}, {5, user_chat_role_deleted}}, {});
sqlite_nooutput(conn,
"UPDATE `chat` SET `it_HistoryId` = ?1 WHERE `id` = ?2", {{1, chat_HistoryId_BEFORE_EV + 1},
{2, chatId}}, {});
sqlite_nooutput(conn,
"UPDATE `user` SET `chatList_HistoryId` = ?1 WHERE `id` = ?2",
{{1, alien_chatlist_HistoryId_BEFORE_EV + 1}, {2, alienUserId}}, {});
}
json::JSON internalapi_removeMemberFromChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
int64_t chatId = Sent["chatUpdReq"]["chatId"].asInteger().get_int();
int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId);
if (my_role_here != user_chat_role_deleted)
een9_THROW("Only admin can delete members of chat");
int64_t badAlienId = Sent["chatUpdReq"]["userId"].asInteger().get_int();
kick_from_chat(conn, badAlienId, chatId);
json::JSON Recv;
poll_update_chat(conn, Sent, Recv);
return Recv;
}
}

View File

@ -1,41 +0,0 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/baza_throw.h>
#include "../str_fields.h"
namespace iu9cawebchat {
/* No authorization check is performed
* Chat's HistoryId will increment after this operation
* if adding system message, uid is ignored
*/
void insert_new_message(SqliteConnection& conn, int64_t uid, int64_t chatId,
const std::string& text, bool isSystem) {
int64_t chat_HistoryId_BEFORE_MSG = get_current_history_id_of_chat(conn, chatId);
int64_t chat_lastMsgId = get_lastMsgId_of_chat(conn, chatId);
SqliteStatement req(conn,
"INSERT INTO `message` (`chatId`, `id`, `senderUserId`, `exists`, `isSystem`, `chat_IncHistoryId`, "
"`text`) VALUES (?1, ?2, ?3 1, ?4, ?5, ?6)",
{{1, chatId}, {2, chat_lastMsgId + 1}, {4, (int64_t)isSystem}, {5, chat_HistoryId_BEFORE_MSG + 1}}, {{6, text}});
if (!isSystem)
sqlite_stmt_bind_int64(req, 3, uid);
sqlite_nooutput(conn, "UPDATE `chat` SET `lastMsgId` = ?1, `it_HistoryId` = ?2 WHERE `id` = ?3",
{{1, chat_lastMsgId + 1}, {2, chat_HistoryId_BEFORE_MSG + 1}, {3, chatId}}, {});
}
json::JSON internalapi_sendMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
int64_t chatId = Sent["chatUpdReq"].asInteger().get_int();
int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId);
if (my_role_here == user_chat_role_deleted)
een9_THROW("Unauthorized user tries to access internalapi_getChatInfo");
if (my_role_here == user_chat_role_read_only)
een9_THROW("read-only user can't send messages");
std::string text = Sent["content"]["text"].asString();
if (!is_orthodox_string(text) || text.empty())
een9_THROW("Bad input text");
insert_new_message(conn, uid, chatId, text, false);
json::JSON Recv;
poll_update_chat(conn, Sent, Recv);
return Recv;
}
}

View File

@ -1,6 +1,5 @@
#include "client_server_interact.h" #include "client_server_interact.h"
#include <engine_engine_number_9/http_structures/cookies.h> #include <engine_engine_number_9/http_structures/cookies.h>
#include <functional>
namespace iu9cawebchat { namespace iu9cawebchat {
void initial_extraction_of_all_the_useful_info_from_cookies( void initial_extraction_of_all_the_useful_info_from_cookies(
@ -20,72 +19,60 @@ namespace iu9cawebchat {
if (ret_logged_in_user >= 0) { if (ret_logged_in_user >= 0) {
ret_userinfo["uid"] = json::JSON(ret_logged_in_user); ret_userinfo["uid"] = json::JSON(ret_logged_in_user);
ret_userinfo["nickname"] = json::JSON(tried.nickname); ret_userinfo["nickname"] = json::JSON(tried.nickname);
ret_userinfo["name"] = json::JSON(get_user_name(conn, ret_logged_in_user)); ret_userinfo["name"] = json::JSON(find_user_name(conn, ret_logged_in_user));
} }
} }
} }
std::string http_R200(const std::string &el_name, WorkerGuestData &wgd, std::string RTEE(const std::string& el_name,
const std::vector<const json::JSON *> &args) { const json::JSON& config_presentation, WorkerGuestData& wgd,
std::string page = wgd.templater->render(el_name, args); const json::JSON& userinfo) {
std::string page = wgd.templater->render(el_name, {&config_presentation, &userinfo});
return een9::form_http_server_response_200("text/html", page); return een9::form_http_server_response_200("text/html", page);
} }
json::JSON jsonify_html_message_list(const std::vector<HtmlMsgBox>& messages) {
json::JSON jmessages(json::array);
for (size_t i = 0; i < messages.size(); i++) {
jmessages[i]["class"].asString() = messages[i].class_;
jmessages[i]["text"].asString() = messages[i].text;
}
return jmessages;
}
std::string page_E404(WorkerGuestData &wgd) {
return een9::form_http_server_response_404("text/html",
wgd.templater->render("err-404", {}));
}
/* ========================= API =========================*/ /* ========================= API =========================*/
std::string when_internalapi(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid,
const std::function<json::JSON(SqliteConnection&, int64_t, const json::JSON&)>& F) {
std::string when_internalapi_pollevents(WorkerGuestData& wgd,
const een9::ClientRequest& req, int64_t uid) {
const json::JSON& Sent = json::parse_str_flawless(req.body); const json::JSON& Sent = json::parse_str_flawless(req.body);
std::string result = json::generate_str(F(*wgd.db, uid, Sent), json::print_pretty); std::string result = json::generate_str(internalapi_pollEvents(*wgd.db, uid, Sent), json::print_pretty);
return een9::form_http_server_response_200("text/json", result); return een9::form_http_server_response_200("text/json", result);
} }
std::string when_internalapi_chatpollevents(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid) { std::string when_internalapi_getchatlist(WorkerGuestData& wgd,
return when_internalapi(wgd, req, uid, internalapi_chatPollEvents); const een9::ClientRequest& req, int64_t uid) {
const json::JSON& Sent = json::parse_str_flawless(req.body);
std::string result = json::generate_str(internalapi_getChatList(*wgd.db, uid), json::print_pretty);
return een9::form_http_server_response_200("text/json", result);
} }
std::string when_internalapi_chatlistpollevents(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid) { std::string when_internalapi_getchatinfo(WorkerGuestData& wgd,
return when_internalapi(wgd, req, uid, internalapi_chatListPollEvents); const een9::ClientRequest& req, int64_t uid) {
const json::JSON& Sent = json::parse_str_flawless(req.body);
std::string result = json::generate_str(internalapi_getChatInfo(*wgd.db, uid, Sent), json::print_pretty);
return een9::form_http_server_response_200("text/json", result);
} }
std::string when_internalapi_getmessageneighbours(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid) { std::string when_internalapi_getchatmemberlist(WorkerGuestData& wgd,
return when_internalapi(wgd, req, uid, internalapi_getMessageNeighbours); const een9::ClientRequest& req, int64_t uid) {
const json::JSON& Sent = json::parse_str_flawless(req.body);
std::string result = json::generate_str(internalapi_getChatMemberList(*wgd.db, uid, Sent), json::print_pretty);
return een9::form_http_server_response_200("text/json", result);
} }
std::string when_internalapi_sendmessage(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid) { std::string when_internalapi_getuserinfo(WorkerGuestData& wgd,
return when_internalapi(wgd, req, uid, internalapi_sendMessage); const een9::ClientRequest& req, int64_t uid) {
const json::JSON& Sent = json::parse_str_flawless(req.body);
std::string result = json::generate_str(internalapi_getUserInfo(*wgd.db, uid, Sent), json::print_pretty);
return een9::form_http_server_response_200("text/json", result);
} }
std::string when_internalapi_deletemessage(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid) { std::string when_internalapi_getmessageinfo(WorkerGuestData& wgd,
return when_internalapi(wgd, req, uid, internalapi_deleteMessage); const een9::ClientRequest& req, int64_t uid) {
} const json::JSON& Sent = json::parse_str_flawless(req.body);
std::string result = json::generate_str(internalapi_getMessageInfo(*wgd.db, uid, Sent), json::print_pretty);
std::string when_internalapi_addmembertochat(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid) { return een9::form_http_server_response_200("text/json", result);
return when_internalapi(wgd, req, uid, internalapi_addMemberToChat);
}
std::string when_internalapi_removememberfromchat(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid) {
return when_internalapi(wgd, req, uid, internalapi_removeMemberFromChat);
}
std::string when_internalapi_createchat(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid) {
return when_internalapi(wgd, req, uid, internalapi_createChat);
}
std::string when_internalapi_leavechat(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid) {
return when_internalapi(wgd, req, uid, internalapi_leaveChat);
} }
} }

View File

@ -27,17 +27,9 @@ namespace iu9cawebchat {
int64_t& ret_logged_in_user int64_t& ret_logged_in_user
); );
std::string http_R200(const std::string& el_name, WorkerGuestData& wgd, std::string RTEE(const std::string& el_name,
const std::vector<const json::JSON*>& args); const json::JSON& config_presentation, WorkerGuestData& wgd,
const json::JSON& userinfo);
struct HtmlMsgBox {
std::string class_;
std::string text;
};
json::JSON jsonify_html_message_list(const std::vector<HtmlMsgBox>& messages);
std::string page_E404(WorkerGuestData& wgd);
/* ========================== PAGES ================================== */ /* ========================== PAGES ================================== */
@ -51,27 +43,28 @@ namespace iu9cawebchat {
const een9::ClientRequest& req, const json::JSON& userinfo); const een9::ClientRequest& req, const json::JSON& userinfo);
std::string when_page_user(WorkerGuestData& wgd, const json::JSON& config_presentation, std::string when_page_user(WorkerGuestData& wgd, const json::JSON& config_presentation,
const een9::ClientRequest& req, const std::vector<LoginCookie>& login_cookies, const json::JSON& userinfo); const een9::ClientRequest& req, const json::JSON& userinfo);
/* ======================== API ============================== */ /* ======================== API ============================== */
std::string when_internalapi_chatpollevents(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid);
std::string when_internalapi_chatlistpollevents(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid); std::string when_internalapi_pollevents(WorkerGuestData& wgd,
const een9::ClientRequest& req, int64_t uid);
std::string when_internalapi_getmessageneighbours(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid); std::string when_internalapi_getchatlist(WorkerGuestData& wgd,
const een9::ClientRequest& req, int64_t uid);
std::string when_internalapi_sendmessage(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid); std::string when_internalapi_getchatinfo(WorkerGuestData& wgd,
const een9::ClientRequest& req, int64_t uid);
std::string when_internalapi_deletemessage(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid); std::string when_internalapi_getchatmemberlist(WorkerGuestData& wgd,
const een9::ClientRequest& req, int64_t uid);
std::string when_internalapi_addmembertochat(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid); std::string when_internalapi_getuserinfo(WorkerGuestData& wgd,
const een9::ClientRequest& req, int64_t uid);
std::string when_internalapi_removememberfromchat(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid); std::string when_internalapi_getmessageinfo(WorkerGuestData& wgd,
const een9::ClientRequest& req, int64_t uid);
std::string when_internalapi_createchat(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid);
std::string when_internalapi_leavechat(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid);
} }
#endif #endif

View File

@ -1,196 +0,0 @@
#include "server_data_interact.h"
#include <assert.h>
#include <engine_engine_number_9/baza_throw.h>
namespace iu9cawebchat {
json::JSON poll_update_chat_list_resp(SqliteConnection& conn, int64_t userId, int64_t LocalHistoryId) {
json::JSON chatListUpdResp;
SqliteStatement my_membership_changes(conn,
"SELECT `chatId`, `role` FROM `user_chat_membership` WHERE `userId` = ?1 "
"AND `user_chatList_IncHistoryId` > ?2", {{1, userId}, {2, LocalHistoryId}});
json::jarr myChats = chatListUpdResp["myChats"].asArray();
while (true) {
fsql_integer_or_null ev_chatId, usersRoleHere;
int status = sqlite_stmt_step(my_membership_changes, {{0, &ev_chatId}, {1, &usersRoleHere}}, {});
if (status != SQLITE_ROW)
break;
myChats.emplace_back();
json::JSON& myMembershipSt = myChats.back();
myMembershipSt["chatId"].asInteger() = json::Integer(ev_chatId.value);
myMembershipSt["myRoleHere"].asString() = stringify_user_chat_role(usersRoleHere.value);
if (usersRoleHere.value != user_chat_role_deleted) {
RowChat_Content CHAT = lookup_chat_content(conn, ev_chatId.value);
myMembershipSt["chatName"].asString() = CHAT.name;
myMembershipSt["chatNickname"].asString() = CHAT.nickname;
}
}
int64_t HistoryId = get_current_history_id_of_user_chatList(conn, userId);
chatListUpdResp["HistoryId"].asInteger() = json::Integer(HistoryId);
return chatListUpdResp;
}
void poll_update_chat_list(SqliteConnection& conn, int64_t userId, const json::JSON& Sent, json::JSON& Recv) {
Recv["status"].asInteger() = json::Integer(0l);
// todo: in libjsonincpp: get rid of Integer
poll_update_chat_list_resp(conn, userId, Sent["chatListUpdReq"]["LocalHistoryId"].asInteger().get_int());
}
json::JSON make_messageSt_obj(int64_t id, int64_t senderUserId, bool exists, bool isSystem, const std::string& text) {
json::JSON messageSt;
messageSt["id"].asInteger() = json::Integer(id);
if (!isSystem)
messageSt["senderUserId"].asInteger() = json::Integer(senderUserId);
messageSt["exists"] = json::JSON(exists);
messageSt["isSystem"] = json::JSON(isSystem);
if (exists)
messageSt["text"].asString() = text;
return messageSt;
}
json::jarr poll_update_chat_resp_messages(SqliteConnection& conn, int64_t chatId, int64_t LocalHistoryId,
int64_t QSEG_A, int64_t QSEG_B) {
json::jarr messages;
SqliteStatement messages_changes(conn,
"SELECT `id`, `senderUserId`, `exists`, `isSystem`, `text` FROM `messages` "
"WHERE `chatId` = ?1 AND ( `chat_IncHistoryId` > ?2 OR ( ?3 <= `id` AND `id` <= ?4 ) )",
{{1, chatId}, {2, LocalHistoryId}, {3, QSEG_A}, {4, QSEG_B}}, {});
while (true) {
fsql_integer_or_null msgId, msgSenderUserId, msgExists, msgIsSystem;
fsql_text8_or_null msgText;
int status = sqlite_stmt_step(messages_changes,
{{0, &msgId}, {1, &msgSenderUserId}, {2, &msgExists}, {3, &msgIsSystem}},
{{4, &msgText}});
if (status != SQLITE_ROW)
break;
messages.push_back(make_messageSt_obj(msgId.value, msgSenderUserId.value, msgExists.value,
msgIsSystem.value, msgText.value));
}
return messages;
}
json::jarr poll_update_chat_resp_members(SqliteConnection& conn, int64_t chatId, int64_t LocalHistoryId) {
json::jarr members;
SqliteStatement membership_changes(conn,
"SELECT `userId`, `role`, FROM `user_chat_membership` WHERE `chatId` = ?1 "
"AND `chat_IncHistoryId` > ?2", {{1, chatId}, {2, LocalHistoryId}}, {});
while (true) {
fsql_integer_or_null alienUserId;
fsql_integer_or_null alienRoleHere;
int status = sqlite_stmt_step(membership_changes,
{{0, &alienUserId}, {1, &alienRoleHere}}, {});
if (status != SQLITE_ROW)
break;
members.emplace_back();
json::JSON& memberSt = members.back();
memberSt["userId"].asInteger() = json::Integer(alienUserId.value);
memberSt["roleHere"].asString() = stringify_user_chat_role(alienRoleHere.value);
if (alienRoleHere.value != user_chat_role_deleted) {
RowUser_Content alien = lookup_user_content(conn, alienUserId.value);
memberSt["name"].asString() = alien.name;
memberSt["nickname"].asString() = alien.nickname;
}
}
return members;
}
json::JSON poll_update_chat_ONE_MSG_resp(SqliteConnection& conn, int64_t chatId, int64_t selectedMsg) {
json::JSON chatUpdResp;
chatUpdResp["members"].asArray() = poll_update_chat_resp_members(conn, chatId, 0);
json::jarr messages = chatUpdResp["messages"].asArray();
if (selectedMsg >= 0) {
RowMessage_Content msg = lookup_message_content(conn, chatId, selectedMsg);
messages.push_back(make_messageSt_obj(msg.id, msg.senderUserId, msg.exists, msg.isSystem, msg.text));
}
int64_t lastMsgId = get_lastMsgId_of_chat(conn, chatId);
chatUpdResp["lastMsgId"].asInteger() = json::Integer(lastMsgId);
int64_t HistoryId = get_current_history_id_of_chat(conn, chatId);
chatUpdResp["HistoryId"].asInteger() = json::Integer(HistoryId);
return chatUpdResp;
}
json::JSON poll_update_chat_important_segment_resp(SqliteConnection& conn, int64_t chatId, int64_t LocalHistoryId,
int64_t QSEG_A, int64_t QSEG_B) {
json::JSON chatUpdResp;
chatUpdResp["members"].asArray() = poll_update_chat_resp_members(conn, chatId, LocalHistoryId);
chatUpdResp["messages"].asArray() = poll_update_chat_resp_messages(conn, chatId, LocalHistoryId, QSEG_A, QSEG_B);
int64_t lastMsgId = get_lastMsgId_of_chat(conn, chatId);
chatUpdResp["lastMsgId"].asInteger() = json::Integer(lastMsgId);
int64_t HistoryId = get_current_history_id_of_chat(conn, chatId);
chatUpdResp["HistoryId"].asInteger() = json::Integer(HistoryId);
return chatUpdResp;
}
/* chat polling function MUST have one queer feature: it accepts a range of msgId, which are guaranteed to be
* lookud up. */
void poll_update_chat_important_segment(SqliteConnection& conn, const json::JSON& Sent, json::JSON& Recv,
int64_t QSEG_A, int64_t QSEG_B) {
Recv["status"].asInteger() = json::Integer(0l);
Recv["charUpdResp"] = poll_update_chat_important_segment_resp(conn,
Sent["chatUpdReq"]["chatId"].asInteger().get_int(),
Sent["chatUpdReq"]["LocalHistoryId"].asInteger().get_int(), QSEG_A, QSEG_B);
}
void poll_update_chat(SqliteConnection& conn, const json::JSON& Sent, json::JSON& Recv) {
poll_update_chat_important_segment(conn, Sent, Recv, -1, -2);
}
json::JSON internalapi_chatPollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
int64_t chatId = Sent["chatUpdReq"]["chatId"].asInteger().get_int();
if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted)
een9_THROW("chatPollEvents: trying to access chat that user does not belong to");
json::JSON Recv;
poll_update_chat(conn, Sent, Recv);
return Recv;
}
json::JSON internalapi_chatListPollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
json::JSON Recv;
poll_update_chat_list(conn, uid, Sent, Recv);
return Recv;
}
/* Reznya */
json::JSON internalapi_getMessageNeighbours(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
int64_t chatId = Sent["chatId"].asInteger().get_int();
if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted)
een9_THROW("Authentication failure");
int64_t lastMsgId = get_lastMsgId_of_chat(conn, chatId);
bool dir_forward = Sent["direction"].asString() == "forward";
int64_t amount = Sent["amount"].asInteger().get_int();
int64_t K = Sent["msgId"].asInteger().get_int();
if (amount <= 0 || amount > 15)
een9_THROW("Incorrect amount");
json::JSON Recv;
int64_t qBeg = -1;
int64_t qEnd = -2;
if (lastMsgId >= 0) {
if (K < 0) {
if (dir_forward)
een9_THROW("Can't go from the top of chat");
qBeg = std::max(0l, lastMsgId - amount + 1);
qEnd = lastMsgId;
} else if (dir_forward) {
qBeg = K + 1;
qEnd = K + amount;
} else {
qBeg = K - amount;
qEnd = K - 1;
}
}
poll_update_chat_important_segment(conn, Sent, Recv, qBeg, qEnd);
return Recv;
}
}

View File

@ -2,7 +2,6 @@
#include <engine_engine_number_9/baza_throw.h> #include <engine_engine_number_9/baza_throw.h>
#include <engine_engine_number_9/http_structures/cookies.h> #include <engine_engine_number_9/http_structures/cookies.h>
#include "../str_fields.h"
namespace iu9cawebchat { namespace iu9cawebchat {
const char* stringify_user_chat_role(int64_t role) { const char* stringify_user_chat_role(int64_t role) {
@ -28,7 +27,7 @@ namespace iu9cawebchat {
return -1; return -1;
} }
std::string get_user_name (SqliteConnection& conn, int64_t uid) { std::string find_user_name (SqliteConnection& conn, int64_t uid) {
een9_ASSERT(uid >= 0, "Are you crazy?"); een9_ASSERT(uid >= 0, "Are you crazy?");
SqliteStatement sql_req(conn, SqliteStatement sql_req(conn,
"SELECT `name` FROM `user` WHERE `id` = ?1", "SELECT `name` FROM `user` WHERE `id` = ?1",
@ -51,20 +50,7 @@ namespace iu9cawebchat {
fsql_text8_or_null name_col; fsql_text8_or_null name_col;
int status = sqlite_stmt_step(sql_req, {}, {{0, &nickname_col}, {1, &name_col}}); int status = sqlite_stmt_step(sql_req, {}, {{0, &nickname_col}, {1, &name_col}});
if (status == SQLITE_ROW) { if (status == SQLITE_ROW) {
return {uid, std::move(nickname_col.value), std::move(name_col.value)}; return {std::move(nickname_col.value), std::move(name_col.value)};
}
een9_THROW("No such user");
}
RowUser_Content lookup_user_content_by_nickname(SqliteConnection& conn, const std::string& nickname) {
SqliteStatement sql_req(conn,
"SELECT `id`, `name` FROM `user` WHERE `nickname` = ?1",
{}, {{1, nickname}});
fsql_integer_or_null id_col;
fsql_text8_or_null name_col;
int status = sqlite_stmt_step(sql_req, {{0, &id_col}}, {{1, &name_col}});
if (status == SQLITE_ROW) {
return {id_col.value, nickname, std::move(name_col.value)};
} }
een9_THROW("No such user"); een9_THROW("No such user");
} }
@ -79,22 +65,7 @@ namespace iu9cawebchat {
fsql_integer_or_null last_msg_id_col; fsql_integer_or_null last_msg_id_col;
int status = sqlite_stmt_step(sql_req, {{2, &last_msg_id_col}}, {{0, &nickname_col}, {1, &name_col}}); int status = sqlite_stmt_step(sql_req, {{2, &last_msg_id_col}}, {{0, &nickname_col}, {1, &name_col}});
if (status == SQLITE_ROW) { if (status == SQLITE_ROW) {
return {chatId, std::move(nickname_col.value), std::move(name_col.value), return {std::move(nickname_col.value), std::move(name_col.value),
last_msg_id_col.exist ? last_msg_id_col.value : -1};
}
een9_THROW("No such chat");
}
RowChat_Content lookup_chat_content_by_nickname(SqliteConnection &conn, const std::string& nickname) {
SqliteStatement sql_req(conn,
"SELECT `id`, `name`, `lastMsgId` FROM `chat` WHERE `nickname` = ?1",
{}, {{1, nickname}});
fsql_integer_or_null id_col;
fsql_text8_or_null name_col;
fsql_integer_or_null last_msg_id_col;
int status = sqlite_stmt_step(sql_req, {{0, &id_col}, {2, &last_msg_id_col}}, {{1, &name_col}});
if (status == SQLITE_ROW) {
return {id_col.value, nickname, std::move(name_col.value),
last_msg_id_col.exist ? last_msg_id_col.value : -1}; last_msg_id_col.exist ? last_msg_id_col.value : -1};
} }
een9_THROW("No such chat"); een9_THROW("No such chat");
@ -102,17 +73,15 @@ namespace iu9cawebchat {
RowMessage_Content lookup_message_content(SqliteConnection& conn, int64_t chatId, int64_t msgId) { RowMessage_Content lookup_message_content(SqliteConnection& conn, int64_t chatId, int64_t msgId) {
SqliteStatement req(conn, SqliteStatement req(conn,
"SELECT `senderUserId`, `exists`, `isSystem`, `text` FROM `message` WHERE " "SELECT `previousId`, `senderUserId`, `exists`, `isSystem`, `text` FROM `message` WHERE "
"`chatId` = ?1 AND `id` = ?2", {{1, chatId}, {2, msgId}}, {}); "`chatId` = ?1 AND `id` = ?2", {{1, chatId}, {2, msgId}}, {});
fsql_integer_or_null senderUserId, exists, isSystem; fsql_integer_or_null previousId, senderUserId, exists, isSystem;
fsql_text8_or_null msg_text; fsql_text8_or_null msg_text;
int status = sqlite_stmt_step(req, {{0, &senderUserId}, {1, &exists}, {2, &isSystem}}, int status = sqlite_stmt_step(req, {{0, &previousId}, {1, &senderUserId}, {2, &exists}, {3, &isSystem}},
{{3, &msg_text}}); {{4, &msg_text}});
if (status == SQLITE_ROW) { if (status == SQLITE_ROW) {
if (!(bool)exists.value) return {(bool)isSystem.value, msg_text.value, senderUserId.exist ? senderUserId.value : -1,
een9_THROW("Message existed, but now it does not"); previousId.exist ? previousId.value : -1};
return {msgId, senderUserId.exist ? senderUserId.value : -1, (bool)exists.value,
(bool)isSystem.value, msg_text.value};
} }
een9_THROW("No such message"); een9_THROW("No such message");
} }
@ -129,117 +98,5 @@ namespace iu9cawebchat {
return user_chat_role_deleted; return user_chat_role_deleted;
} }
int64_t get_lastMsgId_of_chat(SqliteConnection &conn, int64_t chatId) { /* All the api calls processing is done in dedicated files */
een9_ASSERT(chatId >= 0, "Are you crazy?");
SqliteStatement sql_req(conn,
"SELECT `lastMsgId` FROM `chat` WHERE `id` = ?1", {{1, chatId}}, {});
fsql_integer_or_null last_msg_id_col;
int status = sqlite_stmt_step(sql_req, {{0, &last_msg_id_col}}, {});
if (status == SQLITE_ROW) {
return last_msg_id_col.exist ? last_msg_id_col.value : -1;
}
een9_THROW("No such chat");
}
/* All the api calls processing is done in dedicated files.
* All functions related to polling are defined in api_pollevents.cpp */
// todo: move everything to dedicated files
int64_t get_current_history_id_of_chat(SqliteConnection& conn, int64_t chatId) {
SqliteStatement req(conn, "SELECT `it_HistoryId` FROM `chat` WHERE `id` = ?1", {{1, chatId}}, {});
fsql_integer_or_null HistoryId;
int status = sqlite_stmt_step(req, {{0, &HistoryId}}, {});
een9_ASSERT_pl(status == SQLITE_ROW);
return HistoryId.value;
}
int64_t get_current_history_id_of_user_chatList(SqliteConnection& conn, int64_t userId) {
SqliteStatement req(conn, "SELECT `chatList_HistoryId` FROM `user` WHERE `id` = ?1", {{1, userId}}, {});
fsql_integer_or_null HistoryId;
int status = sqlite_stmt_step(req, {{0, &HistoryId}}, {});
een9_ASSERT_pl(status == SQLITE_ROW);
return HistoryId.value;
}
// todo: extract useful clues from deprecated code.
// todo: deprecated code goes here:
/* !!! DEPRECATED FUNCTION */
json::JSON toremoveinternalapi_getChatList(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
json::JSON Recv;
Recv["status"] = json::JSON(0l);
Recv["chats"] = json::JSON(json::array);
std::vector<json::JSON>& chats = Recv["chats"].asArray();
SqliteStatement req(conn,
"SELECT `chat`.`id`, `chat`.`nickname`, `chat`.`name`, `chat`.`lastMsgId`, "
"`user_chat_membership`.`role` FROM `chat` "
"RIGHT JOIN `user_chat_membership` ON `chat`.`id` = `user_chat_membership`.`chatId` "
"WHERE `user_chat_membership`.`userId` = ?1 ", {{1, uid}}, {});
while (true) {
fsql_integer_or_null chat_id;
fsql_text8_or_null chat_nickname, chat_name;
fsql_integer_or_null chat_lastMsgId, role_here;
int status = sqlite_stmt_step(req, {{0, &chat_id}, {3, &chat_lastMsgId}, {4, &role_here}},
{{1, &chat_nickname}, {2, &chat_name}});
if (status != SQLITE_ROW)
break;
chats.emplace_back();
json::JSON& chat = chats.back();
chat["id"] = json::JSON(chat_id.value);
chat["content"]["nickname"] = json::JSON(chat_nickname.value);
chat["content"]["name"] = json::JSON(chat_name.value);
chat["content"]["lastMsgId"] = json::JSON(chat_lastMsgId.exist ? chat_lastMsgId.value : -1);
chat["content"]["roleHere"] = json::JSON(stringify_user_chat_role(role_here.value));
}
return Recv;
}
/* !!! DEPRECATED FUNCTION */
json::JSON toremoveinternalapi_getChatMemberList(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
int64_t chatId = Sent["id"].asInteger().get_int();
int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId);
if (my_role_here == user_chat_role_deleted)
een9_THROW("Unauthorized user tries to access internalapi_getChatInfo");
json::JSON Recv;
Recv["status"] = json::JSON(0l);
Recv["members"] = json::JSON(json::array);
std::vector<json::JSON>& members = Recv["members"].asArray();
SqliteStatement req(conn,
"SELECT `user`.`id`, `user`.`nickname`, `user`.`name`, `user_chat_membership`.`role` FROM "
"`user` RIGHT JOIN `user_chat_membership` ON `user`.`id` = `user_chat_membership`.`userId` "
"WHERE `user_chat_membership`.`chatId` = ?1",
{{1, chatId}}, {});
while (true) {
fsql_integer_or_null this_user_id;
fsql_text8_or_null this_user_nickname, this_user_name;
fsql_integer_or_null this_users_role;
int status = sqlite_stmt_step(req, {{0, &this_user_id}, {3, &this_users_role}},
{{1, &this_user_nickname}, {2, &this_user_name}});
if (status != SQLITE_ROW)
break;
members.emplace_back();
json::JSON& member = members.back();
member["id"] = json::JSON(this_user_id.value);
member["content"]["nickname"] = json::JSON(this_user_nickname.value);
member["content"]["name"] = json::JSON(this_user_name.value);
member["content"]["role"] = json::JSON(this_users_role.value);
}
return Recv;
}
bool is_nickname_taken(SqliteConnection& conn, const std::string& nickname) {
if (!check_nickname(nickname))
return true;
SqliteStatement req(conn, "SELECT EXISTS(SELECT 1 FROM `nickname` WHERE `it` = ?1)",
{}, {{1, nickname}});
fsql_integer_or_null r{true, 0};
int status = sqlite_stmt_step(req, {{0, &r}}, {});
return r.value;
}
void reserve_nickname(SqliteConnection& conn, const std::string& nickname) {
if (!check_nickname(nickname))
een9_THROW("PRECAUTION! Trying to insert incorrect nickname into nickname table");
sqlite_nooutput(conn, "INSERT INTO `nickname` (`it`) VALUES (?1)", {}, {{1, nickname}});
}
} }

View File

@ -16,70 +16,41 @@ namespace iu9cawebchat {
const char* stringify_user_chat_role(int64_t role); const char* stringify_user_chat_role(int64_t role);
int64_t find_user_by_credentials (SqliteConnection& conn, const std::string& nickname, const std::string& password); int64_t find_user_by_credentials (SqliteConnection& conn, const std::string& nickname, const std::string& password);
std::string get_user_name (SqliteConnection& conn, int64_t uid); std::string find_user_name (SqliteConnection& conn, int64_t uid);
struct RowUser_Content { struct RowUser_Content {
int64_t id;
std::string nickname; std::string nickname;
std::string name; std::string name;
}; };
struct RowChat_Content { struct RowChat_Content {
int64_t id;
std::string nickname; std::string nickname;
std::string name; std::string name;
int64_t lastMsgId; // Negative if it does not exist int64_t lastMsgId; // Negative if it does not exist
}; };
RowUser_Content lookup_user_content(SqliteConnection& conn, int64_t uid); RowUser_Content lookup_user_content(SqliteConnection& conn, int64_t uid);
RowUser_Content lookup_user_content_by_nickname(SqliteConnection& conn, const std::string& nickname);
/* Does not make authorization check */
RowChat_Content lookup_chat_content(SqliteConnection& conn, int64_t chatId); RowChat_Content lookup_chat_content(SqliteConnection& conn, int64_t chatId);
RowChat_Content lookup_chat_content_by_nickname(SqliteConnection &conn, const std::string& nickname);
struct RowMessage_Content { struct RowMessage_Content {
int64_t id;
int64_t senderUserId;
bool exists;
bool isSystem; bool isSystem;
std::string text; std::string text;
int64_t senderUserId;
int64_t previous = -1;
}; };
RowMessage_Content lookup_message_content(SqliteConnection& conn, int64_t chatId, int64_t msgId); RowMessage_Content lookup_message_content(SqliteConnection& conn, int64_t chatId, int64_t msgId);
/* Does not make authorization check */
int64_t get_role_of_user_in_chat(SqliteConnection& conn, int64_t userId, int64_t chatId); int64_t get_role_of_user_in_chat(SqliteConnection& conn, int64_t userId, int64_t chatId);
/* Does not make authorization check */
int64_t get_lastMsgId_of_chat(SqliteConnection& conn, int64_t chatId);
int64_t get_current_history_id_of_chat(SqliteConnection& conn, int64_t chatId); /* ============================= API ====================================*/
int64_t get_current_history_id_of_user_chatList(SqliteConnection& conn, int64_t userId); json::JSON internalapi_pollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
json::JSON internalapi_getChatList(SqliteConnection& conn, int64_t uid);
json::JSON poll_update_chat_list_resp(SqliteConnection& conn, int64_t userId, int64_t LocalHistoryId); json::JSON internalapi_getChatInfo(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
void poll_update_chat_list(SqliteConnection& conn, int64_t userId, const json::JSON& Sent, json::JSON& Recv); json::JSON internalapi_getChatMemberList(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
json::JSON internalapi_getUserInfo(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
json::JSON poll_update_chat_ONE_MSG_resp(SqliteConnection& conn, int64_t chatId, int64_t selectedMsg); json::JSON internalapi_getMessageInfo(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
void poll_update_chat(SqliteConnection& conn, const json::JSON& Sent, json::JSON& Recv); // todo: complete the list
void make_her_a_member_of_the_midnight_crew(SqliteConnection& conn, int64_t chatId, int64_t alienUserId, int64_t role);
void kick_from_chat(SqliteConnection& conn, int64_t alienUserId, int64_t chatId);
bool is_nickname_taken(SqliteConnection& conn, const std::string& nickname);
void reserve_nickname(SqliteConnection& conn, const std::string& nickname);
/* ============================= API ==================================== */
json::JSON internalapi_chatPollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
json::JSON internalapi_chatListPollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
json::JSON internalapi_getMessageNeighbours(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
json::JSON internalapi_sendMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
json::JSON internalapi_deleteMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
json::JSON internalapi_addMemberToChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
json::JSON internalapi_removeMemberFromChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
json::JSON internalapi_createChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
json::JSON internalapi_leaveChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
/**/
std::string admin_control_procedure(SqliteConnection& conn, const std::string& req, bool& termination);
} }
#endif #endif

View File

@ -0,0 +1,19 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/baza_throw.h>
namespace iu9cawebchat {
json::JSON internalapi_getChatInfo(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
int64_t chatId = Sent["id"].g().asInteger().get_int();
int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId);
if (my_role_here == user_chat_role_deleted)
een9_THROW("Unauthorized user tries to access internalapi_getChatInfo");
json::JSON Recv;
Recv["status"] = json::JSON(0l);
RowChat_Content content = lookup_chat_content(conn, chatId);
Recv["name"] = json::JSON(content.name);
Recv["nickname"] = json::JSON(content.nickname);
Recv["lastMsgId"] = json::JSON(content.lastMsgId);
Recv["roleHere"] = json::JSON(stringify_user_chat_role(my_role_here));
return Recv;
}
}

View File

@ -0,0 +1,33 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/baza_throw.h>
namespace iu9cawebchat {
json::JSON internalapi_getChatList(SqliteConnection& conn, int64_t uid) {
json::JSON Recv;
Recv["status"] = json::JSON(0l);
Recv["chats"] = json::JSON(json::array);
std::vector<json::JSON>& chats = Recv["chats"].g().asArray();
SqliteStatement req(conn,
"SELECT `chat`.`id`, `chat`.`nickname`, `chat`.`name`, `chat`.`lastMsgId`, "
"`user_chat_membership`.`role` FROM `chat` "
"RIGHT JOIN `user_chat_membership` ON `chat`.`id` = `user_chat_membership`.`chatId` "
"WHERE `user_chat_membership`.`userId` = ?1 ", {{1, uid}}, {});
while (true) {
fsql_integer_or_null chat_id;
fsql_text8_or_null chat_nickname, chat_name;
fsql_integer_or_null chat_lastMsgId, role_here;
int status = sqlite_stmt_step(req, {{0, &chat_id}, {3, &chat_lastMsgId}, {4, &role_here}},
{{1, &chat_nickname}, {2, &chat_name}});
if (status != SQLITE_ROW)
break;
chats.emplace_back();
json::JSON& chat = chats.back();
chat["id"] = json::JSON(chat_id.value);
chat["content"]["nickname"] = json::JSON(chat_nickname.value);
chat["content"]["name"] = json::JSON(chat_name.value);
chat["content"]["lastMsgId"] = json::JSON(chat_lastMsgId.exist ? chat_lastMsgId.value : -1);
chat["content"]["roleHere"] = json::JSON(stringify_user_chat_role(role_here.value));
}
return Recv;
}
}

View File

@ -0,0 +1,36 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/baza_throw.h>
namespace iu9cawebchat {
json::JSON internalapi_getChatMemberList(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
int64_t chatId = Sent["id"].g().asInteger().get_int();
int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId);
if (my_role_here == user_chat_role_deleted)
een9_THROW("Unauthorized user tries to access internalapi_getChatInfo");
json::JSON Recv;
Recv["status"] = json::JSON(0l);
Recv["members"] = json::JSON(json::array);
std::vector<json::JSON>& members = Recv["members"].g().asArray();
SqliteStatement req(conn,
"SELECT `user`.`id`, `user`.`nickname`, `user`.`name`, `user_chat_membership`.`role` FROM "
"`user` RIGHT JOIN `user_chat_membership` ON `user`.`id` = `user_chat_membership`.`userId` "
"WHERE `user_chat_membership`.`chatId` = ?1",
{{1, chatId}}, {});
while (true) {
fsql_integer_or_null this_user_id;
fsql_text8_or_null this_user_nickname, this_user_name;
fsql_integer_or_null this_users_role;
int status = sqlite_stmt_step(req, {{0, &this_user_id}, {3, &this_users_role}},
{{1, &this_user_nickname}, {2, &this_user_name}});
if (status != SQLITE_ROW)
break;
members.emplace_back();
json::JSON& member = members.back();
member["id"] = json::JSON(this_user_id.value);
member["content"]["nickname"] = json::JSON(this_user_nickname.value);
member["content"]["name"] = json::JSON(this_user_name.value);
member["content"]["role"] = json::JSON(this_users_role.value);
}
return Recv;
}
}

View File

@ -0,0 +1,21 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/baza_throw.h>
namespace iu9cawebchat {
/* This is literally the most dumb and useless query */
json::JSON internalapi_getMessageInfo(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
int64_t chatId = Sent["chatId"].g().asInteger().get_int();
int64_t msgId = Sent["id"].g().asInteger().get_int();
if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted)
een9_THROW("Authentication failure");
json::JSON Recv;
Recv["status"] = json::JSON(0l);
RowMessage_Content content = lookup_message_content(conn, chatId, msgId);
Recv["text"] = json::JSON(content.text);
Recv["isSystem"] = json::JSON(content.isSystem);
Recv["sender"] = json::JSON(content.senderUserId);
// todo: sync that addition with api documentation
Recv["previous"] = json::JSON(content.previous);
return Recv;
}
}

View File

@ -0,0 +1,13 @@
#include "server_data_interact.h"
namespace iu9cawebchat {
json::JSON internalapi_getUserInfo(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
int64_t otherUserId = Sent["id"].g().asInteger().get_int();
json::JSON Recv;
Recv["status"] = json::JSON(0l);
RowUser_Content content = lookup_user_content(conn, otherUserId);
Recv["name"] = json::JSON(content.name);
Recv["nickname"] = json::JSON(content.nickname);
return Recv;
}
}

View File

@ -0,0 +1,132 @@
#include "server_data_interact.h"
#include <engine_engine_number_9/baza_throw.h>
namespace iu9cawebchat {
int64_t get_current_history_id_of_chat(SqliteConnection& conn, int64_t chatId) {
SqliteStatement req(conn, "SELECT `it_HistoryId` FROM `chat` WHERE `id` = ?1", {{1, chatId}}, {});
fsql_integer_or_null HistoryId;
int status = sqlite_stmt_step(req, {{0, &HistoryId}}, {});
een9_ASSERT_pl(status == SQLITE_ROW);
return HistoryId.value;
}
int64_t get_current_history_id_of_user_chatList(SqliteConnection& conn, int64_t userId) {
SqliteStatement req(conn, "SELECT `chatList_HistoryId` FROM `user` WHERE `id` = ?1", {{1, userId}}, {});
fsql_integer_or_null HistoryId;
int status = sqlite_stmt_step(req, {{0, &HistoryId}}, {});
een9_ASSERT_pl(status == SQLITE_ROW);
return HistoryId.value;
}
void internalapi_pollEvents_in_chat_collect_membership_events(SqliteConnection& conn,
std::vector<json::JSON>& events, int64_t chatId, int64_t LocalHistoryId) {
SqliteStatement membership_changes(conn,
"SELECT `userId`, `role`, FROM `user_chat_membership` WHERE `chatId` = ?1 "
"AND `chat_IncHistoryId` > ?2", {{1, chatId}, {2, LocalHistoryId}}, {});
fsql_integer_or_null ev_userId;
fsql_integer_or_null ev_user_role;
while (true) {
int status = sqlite_stmt_step(membership_changes,
{{0, &ev_userId}, {1, &ev_user_role}}, {});
if (status != SQLITE_ROW)
break;
events.emplace_back();
json::JSON& event = events.back();
event["member"] = json::JSON(ev_userId.value);
if (ev_user_role.value == user_chat_role_deleted) {
event["type"] = json::JSON("removedMember");
} else {
event["type"] = json::JSON("addedMember");
RowUser_Content USER = lookup_user_content(conn, ev_userId.value);
event["content"]["name"] = json::JSON(USER.name);
event["content"]["nickname"] = json::JSON(USER.nickname);
event["content"]["role"] = json::JSON(stringify_user_chat_role(ev_user_role.value));
}
events.push_back(event);
}
}
void internalapi_pollEvents_in_chat_collect_messages_events(SqliteConnection& conn,
std::vector<json::JSON>& events, int64_t chatId, int64_t LocalHistoryId) {
SqliteStatement messages_changes(conn,
"SELECT `id`, `previous`, `senderUserId`, `exists`, `isSystem`, `text` FROM `messages` WHERE "
"WHERE `chatId` = ?1 AND `chat_IncHistoryId` > ?2", {{1, chatId}, {2, LocalHistoryId}}, {});
fsql_integer_or_null ev_msgId, ev_previousMsgId, msgSenderUserId, msgExists, msgIsSystem;
fsql_text8_or_null msgText;
while (true) {
int status = sqlite_stmt_step(messages_changes,
{{0, &ev_msgId}, {1, &ev_previousMsgId}, {2, &msgSenderUserId}, {3, &msgExists}, {4, &msgIsSystem}},
{{5, &msgText}});
if (status != SQLITE_ROW)
break;
events.emplace_back();
json::JSON& event = events.back();
event["type"] = json::JSON("newMessage");
event["id"] = json::JSON(ev_msgId.value);
event["previous"] = json::JSON(ev_previousMsgId.value);
event["content"]["sender"] = json::JSON(msgSenderUserId.value);
event["content"]["isSystem"] = json::JSON((bool)msgIsSystem.value);
event["content"]["text"] = json::JSON(msgText.value);
events.push_back(event);
}
}
void internalapi_pollEvents_in_user_chatList_collect_events(SqliteConnection& conn,
std::vector<json::JSON>& events, int64_t userId, int64_t LocalHistoryId) {
SqliteStatement membership_changes(conn,
"SELECT `chatId`, `role` FROM `user_chat_membership` WHERE `userId` = ?1 "
"AND `user_chatList_IncHistoryId` > ?2", {{1, userId}, {2, LocalHistoryId}});
fsql_integer_or_null ev_chatId, usersRoleHere;
while (true) {
int status = sqlite_stmt_step(membership_changes, {{0, &ev_chatId}, {1, &usersRoleHere}}, {});
if (status != SQLITE_ROW)
break;
events.emplace_back();
json::JSON& event = events.back();
event["id"] = json::JSON(ev_chatId.value);
if (usersRoleHere.value == user_chat_role_deleted) {
event["type"] = json::JSON("removedChat");
} else {
event["type"] = json::JSON("addedChat");
RowChat_Content CHAT = lookup_chat_content(conn, ev_chatId.value);
event["content"]["name"] = json::JSON(CHAT.name);
event["content"]["nickname"] = json::JSON(CHAT.nickname);
event["content"]["lastMsgId"] = json::JSON(CHAT.lastMsgId);
event["content"]["roleHere"] = json::JSON(stringify_user_chat_role(usersRoleHere.value));
}
}
}
json::JSON internalapi_pollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
json::JSON Recv;
Recv["status"] = json::JSON(0l);
Recv["update"] = json::JSON(json::array);
const std::vector<json::JSON>& req_scope = Sent["scope"].g().asArray();
std::vector<json::JSON>& updated = Recv["update"].g().asArray();
for (const json::JSON& hist_entity_request: req_scope) {
updated.emplace_back();
json::JSON& hist_entity_response = updated.back();
hist_entity_response["type"] = hist_entity_request["type"].g();
hist_entity_response["events"] = json::JSON(json::array);
std::vector<json::JSON>& events = hist_entity_response["events"].g().asArray();
const int64_t LocalHistoryId = hist_entity_request["LocalHistoryId"].g().asInteger().get_int();
if (hist_entity_request["type"].g().asString() == "chat") {
int64_t chatId = hist_entity_request["chatId"].g().asInteger().get_int();
if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted)
een9_THROW("internalapi/pollEvents: trying to access chat that user does not belong to");
hist_entity_response["chatId"] = json::JSON(chatId);
hist_entity_response["HistoryId"] = json::JSON(get_current_history_id_of_chat(conn, chatId));
/* Two classes of 'real events' can happen to chat: membership table change, message table change */
/* Here, I collect membership changes (related to this chat) */
internalapi_pollEvents_in_chat_collect_membership_events(conn, events, chatId, LocalHistoryId);
/* Here, I collect message changes (related to this chat) */
internalapi_pollEvents_in_chat_collect_messages_events(conn, events, chatId, LocalHistoryId);
} else if (hist_entity_request["type"].g().asString() == "chatlist") {
hist_entity_response["HistotyId"] = json::JSON(get_current_history_id_of_user_chatList(conn, uid));
internalapi_pollEvents_in_user_chatList_collect_events(conn, events, uid, LocalHistoryId);
} else
een9_THROW("Bad request");
}
return Recv;
}
}

View File

@ -1,54 +1,8 @@
#include "client_server_interact.h" #include "client_server_interact.h"
#include <engine_engine_number_9/baza_throw.h>
#include "../str_fields.h"
namespace iu9cawebchat { namespace iu9cawebchat {
std::string when_page_chat(WorkerGuestData& wgd, const json::JSON& config_presentation, std::string when_page_chat(WorkerGuestData& wgd, const json::JSON& config_presentation,
const een9::ClientRequest& req, const json::JSON& userinfo) { const een9::ClientRequest& req, const json::JSON& userinfo) {
return RTEE("chat", config_presentation, wgd, userinfo);
std::vector<std::string> path_segs = {""};
path_segs.reserve(4);
for (char ch: req.uri_path) {
if (ch == '/')
path_segs.emplace_back();
else
path_segs.back() += ch;
}
// Parameter, passed from server to javascript
std::string chat_nickname;
int64_t selected_message_id = -1;
if (path_segs.size() >= 2) {
chat_nickname = std::move(path_segs[1]);
}
if (!check_nickname(chat_nickname))
return page_E404(wgd);
if (path_segs.size() == 2) {
} else if (path_segs.size() == 4) {
if (path_segs[2] != "m")
return page_E404(wgd);
selected_message_id = std::stoll(path_segs[3]);
return page_E404(wgd);
} else
return page_E404(wgd);
bool chat_members = (path_segs[0] == "chat-members");
// todo: do not throw id chat not found, catch exception and return 404
json::JSON openedchat;
RowChat_Content chatInfo = lookup_chat_content_by_nickname(*wgd.db, chat_nickname);
if (get_role_of_user_in_chat(*wgd.db, userinfo["id"].asInteger().get_int(), chatInfo.id) == user_chat_role_deleted) {
return page_E404(wgd);
}
openedchat["name"] = json::JSON(chatInfo.name);
openedchat["nickname"] = json::JSON(chatInfo.nickname);
openedchat["id"] = json::JSON(chatInfo.id);
openedchat["selectedMessageId"] = json::JSON(selected_message_id); // -1 means that nothing was selected
json::JSON initial_chatUpdResp = poll_update_chat_ONE_MSG_resp(*wgd.db, chatInfo.id, selected_message_id);
if (chat_members)
return http_R200("chat", wgd, {&config_presentation, &userinfo, &openedchat, &initial_chatUpdResp});
return http_R200("chat", wgd, {&config_presentation, &userinfo, &openedchat, &initial_chatUpdResp});
} }
} }

View File

@ -6,8 +6,6 @@ namespace iu9cawebchat {
if (userinfo.isNull()) { if (userinfo.isNull()) {
return een9::form_http_server_response_303("/login"); return een9::form_http_server_response_303("/login");
} }
json::JSON initial_chatListUpdResp = poll_update_chat_list_resp(*wgd.db, return RTEE("list-rooms", config_presentation, wgd, userinfo);
userinfo["uid"].asInteger().get_int(), 0);
return http_R200("list-rooms", wgd, {&config_presentation, &userinfo, &initial_chatListUpdResp});
} }
} }

View File

@ -25,18 +25,14 @@ namespace iu9cawebchat {
} catch(const std::exception& e){} } catch(const std::exception& e){}
if (uid < 0) { if (uid < 0) {
printf("Redirecting back to /login because of incorrect credentials\n"); printf("Redirecting back to /login because of incorrect credentials\n");
json::JSON msg_list = jsonify_html_message_list({{"", /* todo: Here I need to tell somehow to user (through fancy red box, maybe), that login was incorrect */
config_presentation["phr"]["decl"]["incorrect-nickname-or-password"].asString()}}); return RTEE("login", config_presentation, wgd, userinfo);
return http_R200("login", wgd, {&config_presentation, &userinfo, &msg_list});
} }
std::vector<std::pair<std::string, std::string>> response_hlines; std::vector<std::pair<std::string, std::string>> response_hlines;
LoginCookie new_login_cookie = create_login_cookie(nickname, password); LoginCookie new_login_cookie = create_login_cookie(nickname, password);
add_set_cookie_headers_to_login(login_cookies, response_hlines, new_login_cookie); add_set_cookie_headers_to_login(login_cookies, response_hlines, new_login_cookie);
return een9::form_http_server_response_303_spec_head("/", response_hlines); return een9::form_http_server_response_303_spec_head("/", response_hlines);
} }
if (req.method != "GET") return RTEE("login", config_presentation, wgd, userinfo);
een9_THROW("Bad method");
json::JSON empty_msg_list = json::JSON(json::array);
return http_R200("login", wgd, {&config_presentation, &userinfo, &empty_msg_list});
} }
} }

View File

@ -1,111 +1,8 @@
#include "client_server_interact.h" #include "client_server_interact.h"
#include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
#include <engine_engine_number_9/baza_throw.h>
#include <string.h>
#include "../str_fields.h"
#include "../login_cookie.h"
namespace iu9cawebchat { namespace iu9cawebchat {
std::string get_user_bio(SqliteConnection& conn, int64_t userId) {
een9_ASSERT(userId >= 0, "Are you crazy?");
SqliteStatement sql_req(conn, "SELECT `bio` FROM `user` WHERE `id` = ?1", {{1, userId}}, {});
fsql_text8_or_null bio_col;
int status = sqlite_stmt_step(sql_req, {}, {{0, &bio_col}});
if (status == SQLITE_ROW)
return bio_col.value;
een9_THROW("No such user");
}
static const char* pr_path = "/user/";
bool is_page_of_certain_user(const std::string& path) {
if (!een9::beginsWith(path, pr_path))
return false;
std::string r = path.substr(strlen(pr_path));
return check_nickname(r);
}
std::string obtain_nickname_in_user_page_path(const std::string& path) {
return path.substr(strlen(pr_path));
}
json::JSON user_row_to_userprofile_obj(SqliteConnection& conn, const RowUser_Content& alien) {
return json::JSON(json::jdict{
{"name", json::JSON(alien.name)},
{"nickname", json::JSON(alien.nickname)},
{"bio", json::JSON(get_user_bio(conn, alien.id))}
});
}
std::string when_page_user(WorkerGuestData& wgd, const json::JSON& config_presentation, std::string when_page_user(WorkerGuestData& wgd, const json::JSON& config_presentation,
const een9::ClientRequest& req, const std::vector<LoginCookie>& login_cookies, const json::JSON& userinfo) { const een9::ClientRequest& req, const json::JSON& userinfo) {
SqliteConnection& conn = *wgd.db; return RTEE("profile", config_presentation, wgd, userinfo);
if (!is_page_of_certain_user(req.uri_path))
return page_E404(wgd);
std::string alien_nickname = obtain_nickname_in_user_page_path(req.uri_path);
RowUser_Content alien;
try {
alien = lookup_user_content_by_nickname(conn, alien_nickname);
} catch (const std::exception& e) {
return page_E404(wgd);
}
// todo: in libjsonincpp: fix '999999 problem'
int64_t myuid = userinfo["uid"].asInteger().get_int();
bool can_edit = (alien.id == myuid);
if (req.method == "POST") {
std::vector<std::pair<std::string, std::string>> response_hlines;
try {
if (!can_edit)
een9_THROW("Unauthorized access");
std::vector<std::pair<std::string, std::string>> query = een9::split_html_query(req.body);
// Profile update processing
std::string bio;
std::string name;
std::string password;
for (const std::pair<std::string, std::string>& p: query) {
if (p.first == "bio")
bio = p.second;
else if (p.first == "name")
name = p.second;
else if (p.first == "password")
password = p.second;
}
if (!bio.empty()) {
if (!is_orthodox_string(bio) || bio.size() > 100000)
een9_THROW("Incorrect `bio`");
sqlite_nooutput(conn, "UPDATE `user` SET `bio` = ?1", {}, {{1, bio}});
}
if (!name.empty()) {
if (!check_name(name))
een9_THROW("Incorrect `name`");
sqlite_nooutput(conn, "UPDATE `user` SET `name` = ?1", {}, {{1, name}});
}
if (!password.empty()) {
if (!check_strong_password(password))
een9_THROW("Incorrect `password`");
sqlite_nooutput(conn, "UPDATE `user` SET `password` = ?1", {}, {{1, password}});
if (alien.id == myuid) {
LoginCookie new_login_cookie = create_login_cookie(userinfo["nickname"].asString(), password);
add_set_cookie_headers_to_login(login_cookies, response_hlines, new_login_cookie);
}
}
} catch (const std::exception& e) {
printf("Redirecting back to /user/... because of incorrect credentials\n");
json::JSON msg_list = jsonify_html_message_list({{"",
config_presentation["phr"]["decl"]["incorrect-profile-data"].asString()}});
json::JSON alien_userprofile = user_row_to_userprofile_obj(conn, alien);
return http_R200("edit-profile", wgd, {&config_presentation, &alien_userprofile, &msg_list});
}
return een9::form_http_server_response_303_spec_head("/user/" + alien_nickname, response_hlines);
}
if (req.method == "GET") {
json::JSON alien_userprofile = user_row_to_userprofile_obj(conn, alien);
if (can_edit) {
json::JSON empty_msg_list = jsonify_html_message_list({});
return http_R200("edit-profile", wgd, {&config_presentation, &alien_userprofile, &empty_msg_list});
}
return http_R200("view-profile", wgd, {&config_presentation, &alien_userprofile});
}
een9_THROW("Bad method");
} }
} }

View File

@ -2,17 +2,15 @@
namespace iu9cawebchat{ namespace iu9cawebchat{
int find_db_sqlite_file_path(const json::JSON& config, std::string& res_path) { int find_db_sqlite_file_path(const json::JSON& config, std::string& res_path) {
try { const json::JSON& type = config["database"]["type"].g();
const std::string& type = config["database"]["type"].asString(); if (!type.isString() && type.asString() == "sqlite3")
if (type != "sqlite3")
return -1;
const std::string& path = config["database"]["file"].asString();
if (path.empty() || path[0] == ':')
return -1;
res_path = path;
} catch (const json::misuse& e) {
return -1; 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; return 0;
} }
} }

View File

@ -44,38 +44,36 @@ namespace iu9cawebchat {
"`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL," "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL,"
"`name` TEXT NOT NULL," "`name` TEXT NOT NULL,"
"`chatList_HistoryId` INTEGER NOT NULL," "`chatList_HistoryId` INTEGER NOT NULL,"
"`password` TEXT NOT NULL," "`password` TEXT NOT NULL"
"`bio` TEXT NOT NULL"
")"); ")");
sqlite_nooutput(conn, "CREATE TABLE `chat` (" sqlite_nooutput(conn, "CREATE TABLE `chat` ("
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
"`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL," "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL,"
"`name` TEXT NOT NULL," "`name` TEXT NOT NULL,"
"`it_HistoryId` INTEGER NOT NULL," "`it_HistoryId` INTEGER NOT NULL,"
"`lastMsgId` INTEGER NOT NULL" "`lastMsgId` INTEGER REFERENCES `message`"
")"); ")");
sqlite_nooutput(conn, "CREATE TABLE `user_chat_membership` (" sqlite_nooutput(conn, "CREATE TABLE `user_chat_membership` ("
"`userId` INTEGER REFERENCES `user` NOT NULL," "`userId` INTEGER REFERENCES `user` NOT NULL,"
"`chatId` INTEGER REFERENCES `chat` NOT NULL," "`chatId` INTEGER REFERENCES `chat` NOT NULL,"
"`user_chatList_IncHistoryId` INTEGER NOT NULL," "`user_chatList_IncHistoryId` INTEGER NOT NULL,"
"`chat_IncHistoryId` INTEGER NOT NULL," "`chat_IncHistoryId` INTEGER NOT NULL,"
"`role` INTEGER NOT NULL," "`role` INTEGER NOT NULL"
"UNIQUE (`userId`, `chatId`)"
")"); ")");
sqlite_nooutput(conn, "CREATE TABLE `message` (" sqlite_nooutput(conn, "CREATE TABLE `message` ("
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
"`chatId` INTEGER REFERENCES `chat` NOT NULL," "`chatId` INTEGER REFERENCES `chat` NOT NULL,"
"`id` INTEGER NOT NULL," "`previous` INTEGER REFERENCES `message`,"
"`senderUserId` INTEGER REFERENCES `user`," "`senderUserId` INTEGER REFERENCES `user` NOT NULL,"
"`exists` BOOLEAN NOT NULL," "`exists` BOOLEAN NOT NULL,"
"`isSystem` BOOLEAN NOT NULL," "`isSystem` BOOLEAN NOT NULL,"
"`text` TEXT," "`text` TEXT NOT NULL,"
"`chat_IncHistoryId` INTEGER NOT NULL," "`chat_IncHistoryId` INTEGER NOT NULL"
"PRIMARY KEY (`chatId`, `id`)"
")"); ")");
sqlite_nooutput(conn, "INSERT INTO `nickname` VALUES (?1)", {}, {{1, "root"}}); sqlite_nooutput(conn, "INSERT INTO `nickname` VALUES (?1)", {}, {{1, "root"}});
sqlite_nooutput(conn, "INSERT INTO `user` (`id`, `nickname`, `name`, `chatList_HistoryId`, `password`, " sqlite_nooutput(conn, "INSERT INTO `user` (`id`, `nickname`, `name`, `chatList_HistoryId`, `password`) VALUES "
"`bio` ) VALUES (0, ?1, ?2, 0, ?3, ?4)", {}, "(0, ?1, ?2, 0, ?3)", {},
{{1, "root"}, {2, "Rootov Root Rootovich"}, {3, root_pw}, {4, "One admin to rule them all"}}); {{1, "root"}, {2, "Rootov Root Rootovich"}, {3, root_pw}});
sqlite_nooutput(conn, "END"); sqlite_nooutput(conn, "END");
} catch (const std::exception& e) { } catch (const std::exception& e) {
sqlite_nooutput(conn, "ROLLBACK", {}, {}); sqlite_nooutput(conn, "ROLLBACK", {}, {});

View File

@ -23,8 +23,8 @@ namespace iu9cawebchat {
uint64_t nsec = std::stoull(ft.substr(s_ + 1, ft.size() - s_ - 1)); uint64_t nsec = std::stoull(ft.substr(s_ + 1, ft.size() - s_ - 1));
een9_ASSERT_pl(nsec < 1000000000); een9_ASSERT_pl(nsec < 1000000000);
const json::JSON cnt = json::parse_str_flawless(base64_decode(login_cookie_encoded.second)); const json::JSON cnt = json::parse_str_flawless(base64_decode(login_cookie_encoded.second));
std::string nickname = cnt[0].asString(); std::string nickname = cnt[0].g().asString();
std::string password = cnt[1].asString(); std::string password = cnt[1].g().asString();
return LoginCookie{{(time_t)sec, (time_t)nsec}, nickname, password}; return LoginCookie{{(time_t)sec, (time_t)nsec}, nickname, password};
} }
@ -37,8 +37,8 @@ namespace iu9cawebchat {
std::pair<std::string, std::string> encode_login_cookie(const LoginCookie& cookie) { std::pair<std::string, std::string> encode_login_cookie(const LoginCookie& cookie) {
json::JSON cnt; json::JSON cnt;
cnt[1].asString() = cookie.password; cnt[1].g() = cookie.password;
cnt[0].asString() = cookie.nickname; cnt[0].g() = cookie.nickname;
return {"login_" + std::to_string(cookie.login_time.tv_sec) + "_" + std::to_string(cookie.login_time.tv_nsec), return {"login_" + std::to_string(cookie.login_time.tv_sec) + "_" + std::to_string(cookie.login_time.tv_nsec),
base64_encode(json::generate_str(cnt, json::print_compact))}; base64_encode(json::generate_str(cnt, json::print_compact))};
} }

View File

@ -16,25 +16,9 @@ namespace iu9cawebchat {
termination = true; termination = true;
} }
struct ONE_SQLITE_TRANSACTION_GUARD {
SqliteConnection& conn;
bool rollback = false;
explicit ONE_SQLITE_TRANSACTION_GUARD(SqliteConnection& conn_) : conn(conn_) {
sqlite_nooutput(conn, "BEGIN", {}, {});
}
~ONE_SQLITE_TRANSACTION_GUARD() {
if (rollback)
sqlite_nooutput(conn, "ROLLBACK", {}, {});
else
sqlite_nooutput(conn, "END", {}, {});
}
};
void run_website(const json::JSON& config) { void run_website(const json::JSON& config) {
een9_ASSERT(config["assets"].isString(), "config[\"assets\"] is not string"); een9_ASSERT(config["assets"].g().isString(), "config[\"assets\"] is not string");
const std::string& assets_dir = config["assets"].asString(); std::string assets_dir = config["assets"].g().asString();
een9_ASSERT(een9::isDirectory(assets_dir), "\"" + assets_dir + "\" is not a directory"); een9_ASSERT(een9::isDirectory(assets_dir), "\"" + assets_dir + "\" is not a directory");
een9::StaticAssetManagerSlaveModule samI; een9::StaticAssetManagerSlaveModule samI;
@ -46,8 +30,8 @@ namespace iu9cawebchat {
} }, } },
}); });
const json::JSON& config_presentation = config["presentation"]; const json::JSON& config_presentation = config["presentation"].g();
int64_t slave_number = config["server"]["workers"].asInteger().get_int(); int64_t slave_number = config["server"]["workers"].g().asInteger().get_int();
een9_ASSERT(slave_number > 0 && slave_number <= 200, "E"); een9_ASSERT(slave_number > 0 && slave_number <= 200, "E");
std::string sqlite_db_path; std::string sqlite_db_path;
@ -68,7 +52,13 @@ namespace iu9cawebchat {
een9_ASSERT_pl(0 <= worker_id && worker_id < worker_guest_data.size()); een9_ASSERT_pl(0 <= worker_id && worker_id < worker_guest_data.size());
WorkerGuestData& wgd = worker_guest_data[worker_id]; WorkerGuestData& wgd = worker_guest_data[worker_id];
een9::StaticAsset sa; een9::StaticAsset sa;
ONE_SQLITE_TRANSACTION_GUARD conn_guard(*wgd.db); sqlite_nooutput(*wgd.db, "BEGIN", {}, {});
struct guard {SqliteConnection& conn; bool rollback = false; ~guard() {
if (rollback)
sqlite_nooutput(conn, "ROLLBACK", {}, {});
else
sqlite_nooutput(conn, "END", {}, {});
}} guard_{*wgd.db};
try { try {
std::vector<std::pair<std::string, std::string>> cookies; std::vector<std::pair<std::string, std::string>> cookies;
std::vector<LoginCookie> login_cookies; std::vector<LoginCookie> login_cookies;
@ -82,42 +72,33 @@ namespace iu9cawebchat {
if (req.uri_path == "/login") { if (req.uri_path == "/login") {
return when_page_login(wgd, config_presentation, req, login_cookies, userinfo); return when_page_login(wgd, config_presentation, req, login_cookies, userinfo);
} }
// todo: split if (een9::beginsWith(req.uri_path, "/chat")) {
if (een9::beginsWith(req.uri_path, "/chat/") || een9::beginsWith(req.uri_path, "/chat-members/")) {
return when_page_chat(wgd, config_presentation, req, userinfo); return when_page_chat(wgd, config_presentation, req, userinfo);
} }
if (een9::beginsWith(req.uri_path, "/user/")) { if (req.uri_path == "/user") {
return when_page_user(wgd, config_presentation, req, login_cookies, userinfo); return when_page_user(wgd, config_presentation, req, userinfo);
} }
if (req.uri_path == "/api/chatPollEvents") { if (req.uri_path == "/internalapi/pollEvents") {
return when_internalapi_chatpollevents(wgd, req, logged_in_user); return when_internalapi_pollevents(wgd, req, logged_in_user);
} }
if (req.uri_path == "/api/chatListPollEvents") { if (req.uri_path == "/internalapi/getChatList") {
return when_internalapi_chatlistpollevents(wgd, req, logged_in_user); return when_internalapi_getchatlist(wgd, req, logged_in_user);
} }
if (req.uri_path == "/api/getMessageNeighbours") { if (req.uri_path == "/internalapi/getChatInfo") {
return when_internalapi_getmessageneighbours(wgd, req, logged_in_user); return when_internalapi_getchatinfo(wgd, req, logged_in_user);
} }
if (req.uri_path == "/api/sendMessage") { if (req.uri_path == "/internalapi/getChatMemberList") {
return when_internalapi_sendmessage(wgd, req, logged_in_user); return when_internalapi_getchatmemberlist(wgd, req, logged_in_user);
} }
if (req.uri_path == "/api/deleteMessage") { if (req.uri_path == "/internalapi/getUserInfo") {
return when_internalapi_deletemessage(wgd, req, logged_in_user); return when_internalapi_getuserinfo(wgd, req, logged_in_user);
} }
if (req.uri_path == "/api/addMemberToChat") { if (req.uri_path == "/internalapi/getMessageInfo") {
return when_internalapi_addmembertochat(wgd, req, logged_in_user); return when_internalapi_getmessageinfo(wgd, req, logged_in_user);
}
if (req.uri_path == "/api/removeMemberFromChat") {
return when_internalapi_removememberfromchat(wgd, req, logged_in_user);
}
if (req.uri_path == "/api/createChat") {
return when_internalapi_createchat(wgd, req, logged_in_user);
}
if (req.uri_path == "/api/leaveChat") {
return when_internalapi_leavechat(wgd, req, logged_in_user);
} }
// todo: write all the other interfaces
} catch (const std::exception& e) { } catch (const std::exception& e) {
conn_guard.rollback = true; guard_.rollback = true;
throw; throw;
} }
/* Trying to interpret request as asset lookup */ /* Trying to interpret request as asset lookup */
@ -132,11 +113,29 @@ namespace iu9cawebchat {
(const een9::SlaveTask& task, const std::string& req, een9::worker_id_t worker_id) -> std::string { (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()); een9_ASSERT_pl(0 <= worker_id && worker_id < worker_guest_data.size());
WorkerGuestData& wgd = worker_guest_data[worker_id]; WorkerGuestData& wgd = worker_guest_data[worker_id];
ONE_SQLITE_TRANSACTION_GUARD conn_guad(*wgd.db);
try { try {
return admin_control_procedure(*wgd.db, req, termination); if (req == "hello") {
return ":0 omg! hiii!! Hewwou :3 !!!!\n";
}
if (req == "8") {
termination = true;
return "Bye\n";
}
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\n";
std::string new_password = req.substr(nid + 1);
if (!check_password(new_password))
een9_THROW("Bad password");
sqlite_nooutput(*wgd.db,
"UPDATE `user` SET `password` = ?1 WHERE `id` = 0 ",
{}, {{1, new_password}});
return "Successul update\n";
}
return "Incorrect command\n";
} catch (std::exception& e) { } catch (std::exception& e) {
conn_guad.rollback = true;
return std::string("Server error\n") + e.what(); return std::string("Server error\n") + e.what();
} }
}; };
@ -152,8 +151,8 @@ namespace iu9cawebchat {
een9_ASSERT(ret == 0, "Incorrect ear address: " + source[i].asString()); een9_ASSERT(ret == 0, "Incorrect ear address: " + source[i].asString());
} }
}; };
translate_addr_list_conf(params.client_regular_listened, config["server"]["http-listen"].asArray()); 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"].asArray()); translate_addr_list_conf(params.admin_control_listened, config["server"]["admin-command-listen"].g().asArray());
signal(SIGINT, sigterm_action); signal(SIGINT, sigterm_action);
signal(SIGTERM, sigterm_action); signal(SIGTERM, sigterm_action);

View File

@ -101,14 +101,9 @@ namespace iu9cawebchat {
sqlite3_finalize(stmt_obj); sqlite3_finalize(stmt_obj);
} }
void sqlite_stmt_bind_int64(SqliteStatement &stmt, int paramId, int64_t value) {
int ret = sqlite3_bind_int64(stmt.stmt_obj, paramId, value);
een9_ASSERT(ret == 0, "sqlite3_bind_int64");
}
int sqlite_stmt_step(SqliteStatement &stmt, int sqlite_stmt_step(SqliteStatement &stmt,
const std::vector<std::pair<int, fsql_integer_or_null *>> &ret_of_integer_or_null, const std::vector<std::pair<int, fsql_integer_or_null *>> &ret_of_integer_or_null,
const std::vector<std::pair<int, fsql_text8_or_null *>> &ret_of_text8_or_null) { const std::vector<std::pair<int, fsql_text8_or_null *>> &ret_of_text8_or_null) {
int ret = sqlite3_step(stmt.stmt_obj); int ret = sqlite3_step(stmt.stmt_obj);
if (ret == SQLITE_DONE) if (ret == SQLITE_DONE)
return ret; return ret;
@ -142,9 +137,4 @@ namespace iu9cawebchat {
} }
return SQLITE_ROW; return SQLITE_ROW;
} }
int64_t sqlite_trsess_last_insert_rowid(SqliteConnection& conn) {
int64_t res = sqlite3_last_insert_rowid(conn.hand);
return res;
}
} }

View File

@ -41,13 +41,9 @@ namespace iu9cawebchat {
~SqliteStatement(); ~SqliteStatement();
}; };
void sqlite_stmt_bind_int64(SqliteStatement& stmt, int paramId, int64_t value);
int sqlite_stmt_step(SqliteStatement& stmt, int sqlite_stmt_step(SqliteStatement& stmt,
const std::vector<std::pair<int, fsql_integer_or_null*>>& ret_of_integer_or_null, const std::vector<std::pair<int, fsql_integer_or_null*>>& ret_of_integer_or_null,
const std::vector<std::pair<int, fsql_text8_or_null*>>& ret_of_text8_or_null); const std::vector<std::pair<int, fsql_text8_or_null*>>& ret_of_text8_or_null);
int64_t sqlite_trsess_last_insert_rowid(SqliteConnection& conn);
} }
#endif #endif

View File

@ -28,15 +28,11 @@ namespace iu9cawebchat {
} }
bool check_password(const std::string &pwd) { bool check_password(const std::string &pwd) {
return is_orthodox_string(pwd) && pwd.size() <= 150; return is_orthodox_string(pwd) && pwd.size() >= 8;
}
bool check_strong_password(const std::string& pwd) {
return check_password(pwd) && pwd.size() >= 8;
} }
bool check_name(const std::string &name) { bool check_name(const std::string &name) {
return is_orthodox_string(name) && name.size() <= 150; return is_orthodox_string(name);
} }
bool check_nickname(const std::string &nickname) { bool check_nickname(const std::string &nickname) {
@ -46,7 +42,7 @@ namespace iu9cawebchat {
if (!isUNCHAR(ch)) if (!isUNCHAR(ch))
return false; return false;
} }
return nickname.size() <= 150; return true;
} }
/* Yeah baby, it's base64 time!!! */ /* Yeah baby, it's base64 time!!! */

View File

@ -13,7 +13,6 @@ namespace iu9cawebchat {
bool is_orthodox_string(const std::string& str); bool is_orthodox_string(const std::string& str);
bool check_password(const std::string& pwd); bool check_password(const std::string& pwd);
bool check_strong_password(const std::string& pwd);
bool check_name(const std::string& name); bool check_name(const std::string& name);
bool check_nickname(const std::string& nickname); bool check_nickname(const std::string& nickname);