Compare commits
2 Commits
3632ade86d
...
799e156f88
Author | SHA1 | Date | |
---|---|---|---|
799e156f88 | |||
a6f4bd6c88 |
@ -1,3 +1,4 @@
|
|||||||
|
{% ELDEF main JSON pres JSON userinfo %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="ru">
|
<html lang="ru">
|
||||||
<head>
|
<head>
|
||||||
@ -36,3 +37,4 @@
|
|||||||
<script src="/assets/js/chat.js"></script>
|
<script src="/assets/js/chat.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
{% ENDELDEF %}
|
@ -1,65 +0,0 @@
|
|||||||
<!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/list-rooms.css">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<h1 style="color: white;">Выберите Чат-Комнату</h1>
|
|
||||||
<ul class="room-list">
|
|
||||||
<!-- Здесь будет список комнат -->
|
|
||||||
</ul>
|
|
||||||
<button class="create-room-button" onclick="openCreateRoomModal()">Создать Комнату</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Модальное окно для создания комнаты -->
|
|
||||||
<div id="createRoomModal" class="modal">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<span class="close" onclick="closeCreateRoomModal()">×</span>
|
|
||||||
<h2>Создать Комнату</h2>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<input type="text" id="newRoomName" placeholder="Название комнаты">
|
|
||||||
<input type="password" id="newRoomNickname" placeholder="Никнейм комнаты">
|
|
||||||
</div>
|
|
||||||
<div id="error"></div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button class="join-button" onclick="createRoom()">Создать</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Модальное окно для добавления участников -->
|
|
||||||
<div class="overlay" id="add_members">
|
|
||||||
<div class="add-members">
|
|
||||||
<div class="add-members-header">
|
|
||||||
<span class="close" onclick="closeAdd()">×</span>
|
|
||||||
<h2>Добавить участников</h2>
|
|
||||||
</div>
|
|
||||||
<div class="add-members-body">
|
|
||||||
<input type="text" id="newMemberLogin" placeholder="Логин пользователя">
|
|
||||||
</div>
|
|
||||||
<div class="add-members-footer">
|
|
||||||
<button class="add-member-button" onclick="addMember()">Добавить</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="overlay" id="delete-chat">
|
|
||||||
<div class="delete-chat">
|
|
||||||
<div class="delete-chat-header">
|
|
||||||
<span class="delete-close" onclick="closeConfirm()">×</span>
|
|
||||||
<h2>Вы уверены, что хотите удалить чат?</h2>
|
|
||||||
</div>
|
|
||||||
<div class="delete-chat-body">
|
|
||||||
<button class="confirm" onclick="deleteChat()">Да</button>
|
|
||||||
<button class="cancel" onclick="closeConfirm()">Нет</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script src="/assets/js/list-rooms.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
68
assets/HypertextPages/list-rooms.nytl.html
Normal file
68
assets/HypertextPages/list-rooms.nytl.html
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
{% ELDEF main JSON pres JSON userinfo %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="{% WRITE pres.lang %}">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Список Чат-Комнат</title>
|
||||||
|
<link rel="stylesheet" href="/assets/css/list-rooms.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{% PUT pass-pres-userinfo pres userinfo %}
|
||||||
|
<div class="container">
|
||||||
|
<h1 style="color: white;">Выберите Чат-Комнату</h1>
|
||||||
|
<ul class="room-list">
|
||||||
|
<!-- Здесь будет список комнат -->
|
||||||
|
</ul>
|
||||||
|
<button class="create-room-button" onclick="openCreateRoomModal()">Создать Комнату</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Модальное окно для создания комнаты -->
|
||||||
|
<!--<div id="createRoomModal" class="modal">-->
|
||||||
|
<!-- <div class="modal-content">-->
|
||||||
|
<!-- <div class="modal-header">-->
|
||||||
|
<!-- <span class="close" onclick="closeCreateRoomModal()">×</span>-->
|
||||||
|
<!-- <h2>Создать Комнату</h2>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div class="modal-body">-->
|
||||||
|
<!-- <input type="text" id="newRoomName" placeholder="Название комнаты">-->
|
||||||
|
<!-- <input type="password" id="newRoomNickname" placeholder="Никнейм комнаты">-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div id="error"></div>-->
|
||||||
|
<!-- <div class="modal-footer">-->
|
||||||
|
<!-- <button class="join-button" onclick="createRoom()">Создать</button>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!--</div>-->
|
||||||
|
|
||||||
|
<!--<!– Модальное окно для добавления участников –>-->
|
||||||
|
<!--<div class="overlay" id="add_members">-->
|
||||||
|
<!-- <div class="add-members">-->
|
||||||
|
<!-- <div class="add-members-header">-->
|
||||||
|
<!-- <span class="close" onclick="closeAdd()">×</span>-->
|
||||||
|
<!-- <h2>Добавить участников</h2>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div class="add-members-body">-->
|
||||||
|
<!-- <input type="text" id="newMemberLogin" placeholder="Логин пользователя">-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div class="add-members-footer">-->
|
||||||
|
<!-- <button class="add-member-button" onclick="addMember()">Добавить</button>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!--</div>-->
|
||||||
|
<!--<div class="overlay" id="delete-chat">-->
|
||||||
|
<!-- <div class="delete-chat">-->
|
||||||
|
<!-- <div class="delete-chat-header">-->
|
||||||
|
<!-- <span class="delete-close" onclick="closeConfirm()">×</span>-->
|
||||||
|
<!-- <h2>Вы уверены, что хотите удалить чат?</h2>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div class="delete-chat-body">-->
|
||||||
|
<!-- <button class="confirm" onclick="deleteChat()">Да</button>-->
|
||||||
|
<!-- <button class="cancel" onclick="closeConfirm()">Нет</button>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!--</div>-->
|
||||||
|
<script src="/assets/js/list-rooms.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
{% ENDELDEF %}
|
@ -1,26 +1,27 @@
|
|||||||
{% ELDEF main JSON pres %}
|
{% ELDEF main JSON pres JSON userinfo %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{% WRITE pres.lang %}">
|
<html lang="{% WRITE pres.lang %}">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<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>
|
||||||
<div class="form-container">
|
{% PUT pass-pres-userinfo pres userinfo %}
|
||||||
<h1 class="hide-cursor no-select">{% WRITE pres.phr.decl.enter %}</h1>
|
<div class="form-container">
|
||||||
<form action="/" method="post">
|
<h1 class="hide-cursor no-select">{% WRITE pres.phr.decl.enter %}</h1>
|
||||||
<label for="username">{% WRITE pres.phr.decl.nickname %}</label>
|
<form action="/login" method="post" enctype="application/x-www-form-urlencoded">
|
||||||
<input type="text" name="username" id="username"><br>
|
<label for="nickname">{% WRITE pres.phr.decl.nickname %}</label>
|
||||||
<label for="password">{% WRITE pres.phr.decl.password %}</label>
|
<input type="text" name="nickname" id="nickname"><br>
|
||||||
<input type="password" name="password" id="password"><br>
|
<label for="password">{% WRITE pres.phr.decl.password %}</label>
|
||||||
<button type="submit" class="hide-cursor no-select">{% WRITE pres.phr.act.enter %}</button>
|
<input type="password" name="password" id="password"><br>
|
||||||
</form>
|
<button type="submit" class="hide-cursor no-select">{% WRITE pres.phr.act.enter %}</button>
|
||||||
</div>
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
{% ENDELDEF %}
|
{% ENDELDEF %}
|
||||||
|
6
assets/HypertextPages/pass-pres-userinfo.nytl.html
Normal file
6
assets/HypertextPages/pass-pres-userinfo.nytl.html
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{% ELDEF main JSON pres JSON userinfo %}
|
||||||
|
<script>
|
||||||
|
let pres = {% PUT jsinsert pres %};
|
||||||
|
let userinfo = {% PUT jsinsert userinfo %};
|
||||||
|
</script>
|
||||||
|
{% ENDELDEF %}
|
@ -1,3 +1,4 @@
|
|||||||
|
{% ELDEF main JSON pres JSON userinfo %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
@ -9,7 +10,7 @@
|
|||||||
<div class="main-container">
|
<div class="main-container">
|
||||||
<div class="profile-header">
|
<div class="profile-header">
|
||||||
<h1 style="color: white; text-align: center;">Профиль пользователя</h1>
|
<h1 style="color: white; text-align: center;">Профиль пользователя</h1>
|
||||||
<a class="return" href="chat.html">Назад</a>
|
<a class="return" href="chat.nytl.html">Назад</a>
|
||||||
</div>
|
</div>
|
||||||
<form>
|
<form>
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
@ -34,4 +35,5 @@
|
|||||||
<script src="/assets/js/list-rooms.js"> </script>
|
<script src="/assets/js/list-rooms.js"> </script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
{% ENDELDEF%}
|
@ -1,3 +1,4 @@
|
|||||||
|
{% ELDEF main JSON pres JSON userinfo %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="ru">
|
<html lang="ru">
|
||||||
<head>
|
<head>
|
||||||
@ -22,4 +23,5 @@
|
|||||||
|
|
||||||
<script src="assets/js/registration.js"></script>
|
<script src="assets/js/registration.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
{% ENDELDEF %}
|
@ -146,6 +146,11 @@ struct CAWebChat {
|
|||||||
"str_fields.cpp",
|
"str_fields.cpp",
|
||||||
"find_db.cpp",
|
"find_db.cpp",
|
||||||
"sqlite3_wrapper.cpp",
|
"sqlite3_wrapper.cpp",
|
||||||
|
"login_cookie.cpp",
|
||||||
|
"backend_logic/server_data_interact.cpp",
|
||||||
|
"backend_logic/when_login.cpp",
|
||||||
|
"backend_logic/when_internalapi_pollevents.cpp",
|
||||||
|
"backend_logic/when_internalapi_getchatlist.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;
|
||||||
|
@ -39,6 +39,6 @@
|
|||||||
"server": {
|
"server": {
|
||||||
"workers": 8,
|
"workers": 8,
|
||||||
"http-listen": ["127.0.0.1:1025"],
|
"http-listen": ["127.0.0.1:1025"],
|
||||||
"admin-command-listen": ["unix:/run/iu9/iu9cawebchat-ac.sock"]
|
"admin-command-listen": ["[::1]:1026"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ namespace een9 {
|
|||||||
if (ch <= 32 || ch == 0x22 || ch == 0x2C || ch == 0x3B || ch == 0x5C || ch >= 0x7F)
|
if (ch <= 32 || ch == 0x22 || ch == 0x2C || ch == 0x3B || ch == 0x5C || ch >= 0x7F)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return !str.empty();
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<std::string, std::string>> parseCookieHeader(const std::string &hv) {
|
std::vector<std::pair<std::string, std::string>> parseCookieHeader(const std::string &hv) {
|
||||||
@ -37,18 +37,25 @@ namespace een9 {
|
|||||||
while (hv.size() > pos && isSPACE(hv[pos]))
|
while (hv.size() > pos && isSPACE(hv[pos]))
|
||||||
pos++;
|
pos++;
|
||||||
};
|
};
|
||||||
auto read_to_space_or_ch = [&](char sch) -> std::string {
|
auto read_to_space_or_eq = [&]() -> std::string {
|
||||||
size_t S = pos;
|
size_t S = pos;
|
||||||
while (hv.size() > pos && !isSPACE(hv[pos]) && hv[pos] != sch)
|
while (hv.size() > pos && !isSPACE(hv[pos]) && hv[pos] != '=')
|
||||||
pos++;
|
pos++;
|
||||||
return hv.substr(S, pos - S);
|
return hv.substr(S, pos - S);
|
||||||
};
|
};
|
||||||
|
auto read_to_space_or_dq_or_semc = [&]() -> std::string {
|
||||||
|
size_t S = pos;
|
||||||
|
while (hv.size() > pos && !isSPACE(hv[pos]) && hv[pos] != '"' && hv[pos] != ';')
|
||||||
|
pos++;
|
||||||
|
return hv.substr(S, pos - S);
|
||||||
|
};
|
||||||
|
|
||||||
auto isThis = [&](char ch) -> bool {
|
auto isThis = [&](char ch) -> bool {
|
||||||
return pos < hv.size() && hv[pos] == ch;
|
return pos < hv.size() && hv[pos] == ch;
|
||||||
};
|
};
|
||||||
skip_ows();
|
skip_ows();
|
||||||
while (pos < hv.size()) {
|
while (pos < hv.size()) {
|
||||||
std::string name_of_pechenye = read_to_space_or_ch('=');
|
std::string name_of_pechenye = read_to_space_or_eq();
|
||||||
ASSERT(isCookieName(name_of_pechenye), "Incorrect Cookie name");
|
ASSERT(isCookieName(name_of_pechenye), "Incorrect Cookie name");
|
||||||
skip_ows();
|
skip_ows();
|
||||||
ASSERT(isThis('='), "Incorrect Cookie header line, missing =");
|
ASSERT(isThis('='), "Incorrect Cookie header line, missing =");
|
||||||
@ -57,11 +64,11 @@ namespace een9 {
|
|||||||
std::string value_of_pechenye;
|
std::string value_of_pechenye;
|
||||||
if (isThis('"')) {
|
if (isThis('"')) {
|
||||||
pos++;
|
pos++;
|
||||||
value_of_pechenye = read_to_space_or_ch('"');
|
value_of_pechenye = read_to_space_or_dq_or_semc();
|
||||||
ASSERT(isThis('"'), "Incorrect Cookie header line, missing \"");
|
ASSERT(isThis('"'), "Incorrect Cookie header line, missing \"");
|
||||||
pos++;
|
pos++;
|
||||||
} else {
|
} else {
|
||||||
value_of_pechenye = read_to_space_or_ch('"');;
|
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");
|
||||||
if (result.empty())
|
if (result.empty())
|
||||||
@ -73,11 +80,24 @@ namespace een9 {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, std::string>>
|
||||||
|
findAllClientCookies(const std::vector<std::pair<std::string, std::string>>& header) {
|
||||||
|
std::vector<std::pair<std::string, std::string>> result;
|
||||||
|
for (const std::pair<std::string, std::string>& line: header) {
|
||||||
|
if (line.first == "Cookie") {
|
||||||
|
std::vector<std::pair<std::string, std::string>> new_cookies = parseCookieHeader(line.second);
|
||||||
|
result.reserve(result.size() + new_cookies.size());
|
||||||
|
result.insert(result.end(), new_cookies.begin(), new_cookies.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void set_cookie(const std::vector<std::pair<std::string, std::string>>& new_cookies,
|
void set_cookie(const std::vector<std::pair<std::string, std::string>>& new_cookies,
|
||||||
std::vector<std::pair<std::string, std::string>>& res_header_lines) {
|
std::vector<std::pair<std::string, std::string>>& res_header_lines) {
|
||||||
for (const std::pair<std::string, std::string>& cookie : new_cookies) {
|
for (const std::pair<std::string, std::string>& cookie : new_cookies) {
|
||||||
ASSERT_pl(isCookieName(cookie.first) && isCookieValue(cookie.second));
|
ASSERT_pl(isCookieName(cookie.first) && isCookieValue(cookie.second));
|
||||||
res_header_lines.emplace_back("Set-Cookie", cookie.first + "=" + cookie.second + ";SameSite=Strict");
|
res_header_lines.emplace_back("Set-Cookie", cookie.first + "=\"" + cookie.second + "\";SameSite=Strict;Path=/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,10 @@ namespace een9 {
|
|||||||
/* Throws een9::ServerError on failure */
|
/* Throws een9::ServerError on failure */
|
||||||
std::vector<std::pair<std::string, std::string>> parseCookieHeader(const std::string& hv);
|
std::vector<std::pair<std::string, std::string>> parseCookieHeader(const std::string& hv);
|
||||||
|
|
||||||
|
/* Header is header. Throws een9::ServerError on failure. Concatenates output of een9::parseCookieHeader */
|
||||||
|
std::vector<std::pair<std::string, std::string>>
|
||||||
|
findAllClientCookies(const std::vector<std::pair<std::string, std::string>>& header);
|
||||||
|
|
||||||
/* Can throw een9::ServerError (if check for a value failed). res_header_lines is mutated accordingle */
|
/* Can throw een9::ServerError (if check for a value failed). res_header_lines is mutated accordingle */
|
||||||
void set_cookie(const std::vector<std::pair<std::string, std::string>>& new_cookies,
|
void set_cookie(const std::vector<std::pair<std::string, std::string>>& new_cookies,
|
||||||
std::vector<std::pair<std::string, std::string>>& res_header_lines);
|
std::vector<std::pair<std::string, std::string>>& res_header_lines);
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
|
|
||||||
|
|
||||||
namespace een9 {
|
namespace een9 {
|
||||||
std::string form_http_server_response_header(const char* code, const std::map<std::string, std::string>& headers) {
|
std::string form_http_server_response_header(const char* code,
|
||||||
|
const std::vector<std::pair<std::string, std::string>>& headers) {
|
||||||
assert(strlen(code) == 3);
|
assert(strlen(code) == 3);
|
||||||
std::string result = std::string("HTTP/1.0 ") + code + " " + (code[0] < '4' ? "OK" : "ERROR") + "\r\n";
|
std::string result = std::string("HTTP/1.0 ") + code + " " + (code[0] < '4' ? "OK" : "ERROR") + "\r\n";
|
||||||
for (auto& p: headers)
|
for (auto& p: headers)
|
||||||
@ -13,12 +14,13 @@ namespace een9 {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string form_http_server_reponse_header_only(const char* code, const std::map<std::string, std::string>& headers) {
|
std::string form_http_server_response_header_only(const char* code,
|
||||||
|
const std::vector<std::pair<std::string, std::string>>& headers) {
|
||||||
return form_http_server_response_header(code, headers) + "\r\n";
|
return form_http_server_response_header(code, headers) + "\r\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string form_http_server_response_with_body(const char* code,
|
std::string form_http_server_response_with_body(const char* code,
|
||||||
const std::map<std::string, std::string>& headers,
|
const std::vector<std::pair<std::string, std::string>>& headers,
|
||||||
const std::string& body)
|
const std::string& body)
|
||||||
{
|
{
|
||||||
std::string result = form_http_server_response_header(code, headers)
|
std::string result = form_http_server_response_header(code, headers)
|
||||||
@ -38,4 +40,15 @@ namespace een9 {
|
|||||||
{"Content-Type", Content_Type}
|
{"Content-Type", Content_Type}
|
||||||
}, body);
|
}, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string form_http_server_response_307(const std::string& Location) {
|
||||||
|
return form_http_server_response_header_only("307", {{"Location", Location}});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string form_http_server_response_307_spec_head(const std::string &Location,
|
||||||
|
const std::vector<std::pair<std::string, std::string>>& headers) {
|
||||||
|
std::vector<std::pair<std::string, std::string>> cp = headers;
|
||||||
|
cp.emplace_back("Location", Location);
|
||||||
|
return form_http_server_response_header_only("307", cp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,29 @@
|
|||||||
#ifndef ENGINE_ENGINE_NUMBER_9_HTTP_STRUCTURES_RESPONSE_GEN_H
|
#ifndef ENGINE_ENGINE_NUMBER_9_HTTP_STRUCTURES_RESPONSE_GEN_H
|
||||||
#define ENGINE_ENGINE_NUMBER_9_HTTP_STRUCTURES_RESPONSE_GEN_H
|
#define ENGINE_ENGINE_NUMBER_9_HTTP_STRUCTURES_RESPONSE_GEN_H
|
||||||
|
|
||||||
#include <map>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace een9 {
|
|
||||||
std::string form_http_server_response_header(const char* code, const std::map<std::string, std::string>& headers);
|
|
||||||
|
|
||||||
std::string form_http_server_reponse_header_only(const char* code, const std::map<std::string, std::string>& headers);
|
namespace een9 {
|
||||||
|
std::string form_http_server_response_header(const char* code,
|
||||||
|
const std::vector<std::pair<std::string, std::string>>& headers);
|
||||||
|
|
||||||
|
std::string form_http_server_reponse_header_only(const char* code,
|
||||||
|
const std::vector<std::pair<std::string, std::string>>& headers);
|
||||||
|
|
||||||
std::string form_http_server_response_with_body(const char* code,
|
std::string form_http_server_response_with_body(const char* code,
|
||||||
const std::map<std::string, std::string>& headers,
|
const std::vector<std::pair<std::string, std::string>>& headers,
|
||||||
const std::string& body);
|
const std::string& body);
|
||||||
|
|
||||||
std::string form_http_server_response_200(const std::string& Content_Type, const std::string& body);
|
std::string form_http_server_response_200(const std::string& Content_Type, const std::string& body);
|
||||||
|
|
||||||
std::string form_http_server_response_404(const std::string& Content_Type, const std::string& body);
|
std::string form_http_server_response_404(const std::string& Content_Type, const std::string& body);
|
||||||
|
|
||||||
|
std::string form_http_server_response_307(const std::string& Location);
|
||||||
|
|
||||||
|
std::string form_http_server_response_307_spec_head(const std::string &Location,
|
||||||
|
const std::vector<std::pair<std::string, std::string>>& headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -41,7 +41,6 @@ namespace een9 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void push_back(SlaveTask task) {
|
void push_back(SlaveTask task) {
|
||||||
/* Throws a goddamn execption. Because why not. Ofcourse everything has to throw an exception */
|
|
||||||
/* CLion says. Allocated memory is leaking. YOUR MOTHER IS LEAKING YOU FOOL!! MY CODE IS FINE!! */
|
/* CLion says. Allocated memory is leaking. YOUR MOTHER IS LEAKING YOU FOOL!! MY CODE IS FINE!! */
|
||||||
QElementHttpConnections* el = new QElementHttpConnections(std::move(task));
|
QElementHttpConnections* el = new QElementHttpConnections(std::move(task));
|
||||||
/* Exception does not leave queue in incorrect state */
|
/* Exception does not leave queue in incorrect state */
|
||||||
@ -66,6 +65,15 @@ namespace een9 {
|
|||||||
sz--;
|
sz--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~WorkersTaskQueue() {
|
||||||
|
QElementHttpConnections* cur = first;
|
||||||
|
while (cur) {
|
||||||
|
QElementHttpConnections* nxt = cur->nxt;
|
||||||
|
delete cur;
|
||||||
|
cur = nxt;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WorkersEnvCommon {
|
struct WorkersEnvCommon {
|
||||||
|
@ -34,7 +34,7 @@ namespace nytl {
|
|||||||
} else if (P.isDictionary() && what.isString()) {
|
} else if (P.isDictionary() && what.isString()) {
|
||||||
const std::map<std::string, json::JSON>& dict_p = P.asDictionary();
|
const std::map<std::string, json::JSON>& dict_p = P.asDictionary();
|
||||||
const std::string& key_w = what.asString();
|
const std::string& key_w = what.asString();
|
||||||
ASSERT(dict_p.count(key_w) == 1, "No such key exception");
|
ASSERT(dict_p.count(key_w) == 1, "No such key exception (" + key_w + ")");
|
||||||
result = LocalVarValue{true, "", &dict_p.at(key_w)};
|
result = LocalVarValue{true, "", &dict_p.at(key_w)};
|
||||||
} else
|
} else
|
||||||
THROW("Incorrect type of \"json[json]\" expression. Unallowed signature of [] operator");
|
THROW("Incorrect type of \"json[json]\" expression. Unallowed signature of [] operator");
|
||||||
@ -112,15 +112,9 @@ namespace nytl {
|
|||||||
for (size_t i = 0; i < m; i++) {
|
for (size_t i = 0; i < m; i++) {
|
||||||
result += text[i];
|
result += text[i];
|
||||||
if (text[i] == '\n') {
|
if (text[i] == '\n') {
|
||||||
// newlined_somewhere = true;
|
|
||||||
|
|
||||||
result.resize(result.size() + wsp_before_newlines, ' ');
|
result.resize(result.size() + wsp_before_newlines, ' ');
|
||||||
cur_line_width = wsp_before_newlines;
|
cur_line_width = wsp_before_newlines;
|
||||||
} else {
|
} else {
|
||||||
// if (cur_line_width == 0 && newlined_somewhere) {
|
|
||||||
// result.resize(result.size() + wsp_before_newlines, ' ');
|
|
||||||
// cur_line_width = wsp_before_newlines;
|
|
||||||
// }
|
|
||||||
cur_line_width++;
|
cur_line_width++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -246,7 +240,11 @@ namespace nytl {
|
|||||||
assert(passed_args.size() == 1);
|
assert(passed_args.size() == 1);
|
||||||
const json::JSON* X = passed_args[0].JSON_subval;
|
const json::JSON* X = passed_args[0].JSON_subval;
|
||||||
assert(X);
|
assert(X);
|
||||||
if (name == "jesc") {
|
if (name == "jsinsert") {
|
||||||
|
std::string pure_json = json::generate_str(*X, json::print_pretty);
|
||||||
|
rstrip(pure_json);
|
||||||
|
append(pure_json, result);
|
||||||
|
} else if (name == "jesc") {
|
||||||
std::string escaped_json = escape(json::generate_str(*X, json::print_pretty));
|
std::string escaped_json = escape(json::generate_str(*X, json::print_pretty));
|
||||||
rstrip(escaped_json);
|
rstrip(escaped_json);
|
||||||
append(escaped_json, result);
|
append(escaped_json, result);
|
||||||
|
@ -111,6 +111,7 @@ namespace nytl {
|
|||||||
|
|
||||||
void Templater::update() {
|
void Templater::update() {
|
||||||
elements = {
|
elements = {
|
||||||
|
{"jsinsert", Element{{json::JSON(true)}, true}},
|
||||||
{"jesc", Element{{json::JSON(true)}, true}},
|
{"jesc", Element{{json::JSON(true)}, true}},
|
||||||
{"jesccomp", Element{{json::JSON(true)}, true}},
|
{"jesccomp", Element{{json::JSON(true)}, true}},
|
||||||
/* str2text base element has a dedicated operator - WRITE */
|
/* str2text base element has a dedicated operator - WRITE */
|
||||||
|
@ -0,0 +1,114 @@
|
|||||||
|
#include "server_data_interact.h"
|
||||||
|
|
||||||
|
#include <engine_engine_number_9/baza_throw.h>
|
||||||
|
#include <engine_engine_number_9/http_structures/cookies.h>
|
||||||
|
|
||||||
|
namespace iu9cawebchat {
|
||||||
|
const char* stringify_user_chat_role(int64_t role) {
|
||||||
|
if (role == user_chat_role_admin)
|
||||||
|
return "admin";
|
||||||
|
if (role == user_chat_role_regular)
|
||||||
|
return "regular";
|
||||||
|
if (role == user_chat_role_read_only)
|
||||||
|
return "read-only";
|
||||||
|
return "not-a-member";
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t find_user_by_credentials (SqliteConnection& conn, const std::string& nickname, const std::string& password) {
|
||||||
|
SqliteStatement sql_req(conn,
|
||||||
|
"SELECT `id` FROM `user` WHERE `nickname` = ?1 AND `password` = ?2",
|
||||||
|
{}, {{1, nickname}, {2, password}});
|
||||||
|
fsql_integer_or_null id_col;
|
||||||
|
int status = sqlite_stmt_step(sql_req, {{0, &id_col}}, {});
|
||||||
|
if (status == SQLITE_ROW) {
|
||||||
|
een9_ASSERT_pl(id_col.exist & id_col.value >= 0);
|
||||||
|
return id_col.value;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string find_user_name (SqliteConnection& conn, int64_t uid) {
|
||||||
|
een9_ASSERT(uid >= 0, "Are you crazy?");
|
||||||
|
SqliteStatement sql_req(conn,
|
||||||
|
"SELECT `name` FROM `user` WHERE `id` = ?1",
|
||||||
|
{{1, uid}}, {});
|
||||||
|
fsql_text8_or_null name_col;
|
||||||
|
int status = sqlite_stmt_step(sql_req, {}, {{0, &name_col}});
|
||||||
|
if (status == SQLITE_ROW) {
|
||||||
|
een9_ASSERT_pl(name_col.exist);
|
||||||
|
return name_col.value;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
RowUser_Content lookup_user_content(SqliteConnection &conn, int64_t uid) {
|
||||||
|
een9_ASSERT(uid >= 0, "Are you crazy?");
|
||||||
|
SqliteStatement sql_req(conn,
|
||||||
|
"SELECT `nickname`, `name` FROM `user` WHERE `id` = ?1",
|
||||||
|
{{1, uid}}, {});
|
||||||
|
fsql_text8_or_null nickname_col;
|
||||||
|
fsql_text8_or_null name_col;
|
||||||
|
int status = sqlite_stmt_step(sql_req, {}, {{0, &nickname_col}, {1, &name_col}});
|
||||||
|
if (status == SQLITE_ROW) {
|
||||||
|
return {std::move(nickname_col.value), std::move(name_col.value)};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
RowChat_Content lookup_chat_content(SqliteConnection &conn, int64_t uid) {
|
||||||
|
een9_ASSERT(uid >= 0, "Are you crazy?");
|
||||||
|
SqliteStatement sql_req(conn,
|
||||||
|
"SELECT `nickname`, `name`, `lastMsgId` FROM `chat` WHERE `id` = ?1",
|
||||||
|
{{1, uid}}, {});
|
||||||
|
fsql_text8_or_null nickname_col;
|
||||||
|
fsql_text8_or_null name_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}});
|
||||||
|
if (status == SQLITE_ROW) {
|
||||||
|
return {std::move(nickname_col.value), std::move(name_col.value),
|
||||||
|
last_msg_id_col.exist ? last_msg_id_col.value : -1};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void initial_extraction_of_all_the_useful_info_from_cookies(
|
||||||
|
SqliteConnection& conn, const een9::ClientRequest& req,
|
||||||
|
std::vector<std::pair<std::string, std::string>> &ret_cookies,
|
||||||
|
std::vector<LoginCookie> &ret_login_cookies,
|
||||||
|
json::JSON &ret_userinfo,
|
||||||
|
int64_t& ret_logged_in_user
|
||||||
|
) {
|
||||||
|
ret_cookies = een9::findAllClientCookies(req.headers);
|
||||||
|
ret_login_cookies = select_login_cookies(ret_cookies);
|
||||||
|
ret_logged_in_user = -1; /* Negative means that user is not logged in */
|
||||||
|
if (!ret_login_cookies.empty()){
|
||||||
|
size_t oldest_ind = select_oldest_login_cookie(ret_login_cookies);
|
||||||
|
LoginCookie& tried = ret_login_cookies[oldest_ind];
|
||||||
|
ret_logged_in_user = find_user_by_credentials(conn, tried.nickname, tried.password);
|
||||||
|
if (ret_logged_in_user >= 0) {
|
||||||
|
ret_userinfo["uid"] = json::JSON(ret_logged_in_user);
|
||||||
|
ret_userinfo["nickname"] = json::JSON(tried.nickname);
|
||||||
|
ret_userinfo["name"] = json::JSON(find_user_name(conn, ret_logged_in_user));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t get_role_of_user_in_chat(SqliteConnection& conn, int64_t userId, int64_t chatId) {
|
||||||
|
SqliteStatement req(conn,
|
||||||
|
"SELECT `role` FROM `user_chat_membership` WHERE `userId` = ?1 AND `chatId` = ?2",
|
||||||
|
{{1, userId}, {2, chatId}}, {});
|
||||||
|
fsql_integer_or_null role;
|
||||||
|
int status = sqlite_stmt_step(req, {{0, &role}}, {});
|
||||||
|
if (status == SQLITE_ROW) {
|
||||||
|
return role.exist ? (int)role.value : user_chat_role_deleted;
|
||||||
|
}
|
||||||
|
return user_chat_role_deleted;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RTEE(const std::string& el_name,
|
||||||
|
const json::JSON& config_presentation, WorkerGuestData& wgd,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
#ifndef IU9_CA_WEB_CHAT_LIB_BACKEND_LOGIC_SERVER_DATA_INTERACT_H
|
||||||
|
#define IU9_CA_WEB_CHAT_LIB_BACKEND_LOGIC_SERVER_DATA_INTERACT_H
|
||||||
|
|
||||||
|
/* This folder covers all code that helps to interact with database and templater,
|
||||||
|
* or dictates the logic of how this web chat functions */
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <new_york_transit_line/templater.h>
|
||||||
|
#include "../sqlite3_wrapper.h"
|
||||||
|
#include "../login_cookie.h"
|
||||||
|
#include <engine_engine_number_9/http_structures/client_request.h>
|
||||||
|
#include <engine_engine_number_9/http_structures/response_gen.h>
|
||||||
|
#include <jsonincpp/string_representation.h>
|
||||||
|
|
||||||
|
namespace iu9cawebchat {
|
||||||
|
constexpr int64_t user_chat_role_admin = 1;
|
||||||
|
constexpr int64_t user_chat_role_regular = 2;
|
||||||
|
constexpr int64_t user_chat_role_read_only = 3;
|
||||||
|
constexpr int64_t user_chat_role_deleted = 4;
|
||||||
|
|
||||||
|
const char* stringify_user_chat_role(int64_t role);
|
||||||
|
|
||||||
|
struct WorkerGuestData {
|
||||||
|
/* Because templaters use libjsonincpp, they can't be READ by two thread simultaneously */
|
||||||
|
std::unique_ptr<nytl::Templater> templater;
|
||||||
|
std::unique_ptr<SqliteConnection> db;
|
||||||
|
int debug_trans_op; // todo: delete when debug is over
|
||||||
|
};
|
||||||
|
|
||||||
|
int64_t find_user_by_credentials (SqliteConnection& conn, const std::string& nickname, const std::string& password);
|
||||||
|
std::string find_user_name (SqliteConnection& conn, int64_t uid);
|
||||||
|
|
||||||
|
struct RowUser_Content {
|
||||||
|
std::string nickname;
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RowChat_Content {
|
||||||
|
std::string nickname;
|
||||||
|
std::string name;
|
||||||
|
int64_t lastMsgId; // Negative if it does not exist
|
||||||
|
};
|
||||||
|
|
||||||
|
RowUser_Content lookup_user_content(SqliteConnection& conn, int64_t uid);
|
||||||
|
RowChat_Content lookup_chat_content(SqliteConnection& conn, int64_t uid);
|
||||||
|
|
||||||
|
|
||||||
|
void initial_extraction_of_all_the_useful_info_from_cookies(
|
||||||
|
SqliteConnection& conn, const een9::ClientRequest& req,
|
||||||
|
std::vector<std::pair<std::string, std::string>>& ret_cookies,
|
||||||
|
std::vector<LoginCookie>& ret_login_cookies,
|
||||||
|
json::JSON& ret_userinfo,
|
||||||
|
int64_t& ret_logged_in_user
|
||||||
|
);
|
||||||
|
|
||||||
|
int64_t get_role_of_user_in_chat(SqliteConnection& conn, int64_t userId, int64_t chatId);
|
||||||
|
|
||||||
|
std::string RTEE(const std::string& el_name,
|
||||||
|
const json::JSON& config_presentation, WorkerGuestData& wgd,
|
||||||
|
const json::JSON& userinfo);
|
||||||
|
|
||||||
|
/* ========================== PAGES ================================== */
|
||||||
|
std::string when_page_login(WorkerGuestData& wgd, const json::JSON& config_presentation,
|
||||||
|
const een9::ClientRequest& req, const std::vector<LoginCookie>& login_cookies, const json::JSON& userinfo);
|
||||||
|
|
||||||
|
|
||||||
|
json::JSON internalapi_pollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
|
||||||
|
|
||||||
|
std::string when_internalapi_pollevents(WorkerGuestData& wgd,
|
||||||
|
const een9::ClientRequest& req, int64_t uid);
|
||||||
|
|
||||||
|
|
||||||
|
json::JSON internalapi_getChatList(SqliteConnection& conn, int64_t uid);
|
||||||
|
|
||||||
|
std::string when_internalapi_getchatlist(WorkerGuestData& wgd,
|
||||||
|
const een9::ClientRequest& req, int64_t uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,42 @@
|
|||||||
|
#include "server_data_interact.h"
|
||||||
|
#include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
|
||||||
|
#include <engine_engine_number_9/baza_throw.h>
|
||||||
|
#include "../str_fields.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string when_internalapi_getchatlist(WorkerGuestData& wgd,
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,142 @@
|
|||||||
|
#include "server_data_interact.h"
|
||||||
|
#include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
|
||||||
|
#include <engine_engine_number_9/baza_throw.h>
|
||||||
|
#include "../str_fields.h"
|
||||||
|
#include <engine_engine_number_9/http_structures/response_gen.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: 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string when_internalapi_pollevents(WorkerGuestData& wgd,
|
||||||
|
const een9::ClientRequest& req, int64_t uid) {
|
||||||
|
const json::JSON& Sent = json::parse_str_flawless(req.body);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
#include "server_data_interact.h"
|
||||||
|
#include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
|
||||||
|
#include <engine_engine_number_9/baza_throw.h>
|
||||||
|
#include "../str_fields.h"
|
||||||
|
|
||||||
|
namespace iu9cawebchat {
|
||||||
|
std::string when_page_login(WorkerGuestData& wgd, const json::JSON& config_presentation,
|
||||||
|
const een9::ClientRequest& req, const std::vector<LoginCookie>& login_cookies, const json::JSON& userinfo) {
|
||||||
|
if (req.method == "POST") {
|
||||||
|
std::vector<std::pair<std::string, std::string>> query = een9::split_html_query(req.body);
|
||||||
|
int64_t uid = -1;
|
||||||
|
std::string nickname;
|
||||||
|
std::string password;
|
||||||
|
try {
|
||||||
|
for (const std::pair<std::string, std::string>& cmp: query) {
|
||||||
|
if (cmp.first == "nickname")
|
||||||
|
nickname = cmp.second;
|
||||||
|
if (cmp.first == "password")
|
||||||
|
password = cmp.second;
|
||||||
|
}
|
||||||
|
een9_ASSERT(check_nickname(nickname), "/login/accpet-data rejected impossible nickname");
|
||||||
|
een9_ASSERT(check_password(password), "/login/accpet-data rejected impossible password");
|
||||||
|
uid = find_user_by_credentials(*wgd.db, nickname, password);
|
||||||
|
|
||||||
|
} catch(const std::exception& e){}
|
||||||
|
if (uid < 0) {
|
||||||
|
printf("Redirecting back to /login because of incorrect credentials\n");
|
||||||
|
/* todo: Here I need to tell somehow to user (through fancy red box, maybe), that login was incorrect */
|
||||||
|
return RTEE("login", config_presentation, wgd, userinfo);
|
||||||
|
}
|
||||||
|
std::vector<std::pair<std::string, std::string>> response_hlines;
|
||||||
|
LoginCookie new_login_cookie = create_login_cookie(nickname, password);
|
||||||
|
add_set_cookie_headers_to_login(login_cookies, response_hlines, new_login_cookie);
|
||||||
|
return een9::form_http_server_response_307_spec_head("/", response_hlines);
|
||||||
|
}
|
||||||
|
return RTEE("login", config_presentation, wgd, userinfo);
|
||||||
|
}
|
||||||
|
}
|
@ -35,44 +35,49 @@ namespace iu9cawebchat {
|
|||||||
* If chat.lastMsgId is NULL, chat is empty
|
* If chat.lastMsgId is NULL, chat is empty
|
||||||
* If message.previous is NULL, this message is first in it's chat
|
* If message.previous is NULL, this message is first in it's chat
|
||||||
*/
|
*/
|
||||||
sqlite_single_statement(conn.hand, "PRAGMA foreign_keys = true");
|
try {
|
||||||
sqlite_single_statement(conn.hand, "BEGIN");
|
sqlite_nooutput(conn, "PRAGMA foreign_keys = true");
|
||||||
sqlite_single_statement(conn.hand, "CREATE TABLE `nickname` (`it` TEXT PRIMARY KEY NOT NULL) WITHOUT ROWID");
|
sqlite_nooutput(conn, "BEGIN");
|
||||||
sqlite_single_statement(conn.hand, "CREATE TABLE `user` ("
|
sqlite_nooutput(conn, "CREATE TABLE `nickname` (`it` TEXT PRIMARY KEY NOT NULL) WITHOUT ROWID");
|
||||||
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
|
sqlite_nooutput(conn, "CREATE TABLE `user` ("
|
||||||
"`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL,"
|
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
|
||||||
"`name` TEXT NOT NULL,"
|
"`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL,"
|
||||||
"`chatList_HistoryId` INTEGER NOT NULL,"
|
"`name` TEXT NOT NULL,"
|
||||||
"`password` TEXT NOT NULL"
|
"`chatList_HistoryId` INTEGER NOT NULL,"
|
||||||
")");
|
"`password` TEXT NOT NULL"
|
||||||
sqlite_single_statement(conn.hand, "CREATE TABLE `chat` ("
|
")");
|
||||||
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
|
sqlite_nooutput(conn, "CREATE TABLE `chat` ("
|
||||||
"`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL,"
|
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
|
||||||
"`name` TEXT NOT NULL,"
|
"`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL,"
|
||||||
"`it_HistoryId` INTEGER NOT NULL,"
|
"`name` TEXT NOT NULL,"
|
||||||
"`lastMsgId` INTEGER REFERENCES `message`"
|
"`it_HistoryId` INTEGER NOT NULL,"
|
||||||
")");
|
"`lastMsgId` INTEGER REFERENCES `message`"
|
||||||
sqlite_single_statement(conn.hand, "CREATE TABLE `user_chat_membership` ("
|
")");
|
||||||
"`userId` INTEGER REFERENCES `user` NOT NULL,"
|
sqlite_nooutput(conn, "CREATE TABLE `user_chat_membership` ("
|
||||||
"`chatId` INTEGER REFERENCES `chat` NOT NULL,"
|
"`userId` INTEGER REFERENCES `user` NOT NULL,"
|
||||||
"`user_chatList_IncHistoryId` INTEGER NOT NULL,"
|
"`chatId` INTEGER REFERENCES `chat` NOT NULL,"
|
||||||
"`chat_IncHistoryId` INTEGER NOT NULL,"
|
"`user_chatList_IncHistoryId` INTEGER NOT NULL,"
|
||||||
"`role` INTEGER NOT NULL"
|
"`chat_IncHistoryId` INTEGER NOT NULL,"
|
||||||
")");
|
"`role` INTEGER NOT NULL"
|
||||||
sqlite_single_statement(conn.hand, "CREATE TABLE `message` ("
|
")");
|
||||||
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
|
sqlite_nooutput(conn, "CREATE TABLE `message` ("
|
||||||
"`chatId` INTEGER REFERENCES `chat` NOT NULL,"
|
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
|
||||||
"`previous` INTEGER REFERENCES `message`,"
|
"`chatId` INTEGER REFERENCES `chat` NOT NULL,"
|
||||||
"`senderUserId` INTEGER REFERENCES `user` NOT NULL,"
|
"`previous` INTEGER REFERENCES `message`,"
|
||||||
"`exists` BOOLEAN NOT NULL,"
|
"`senderUserId` INTEGER REFERENCES `user` NOT NULL,"
|
||||||
"`isSystem` BOOLEAN NOT NULL,"
|
"`exists` BOOLEAN NOT NULL,"
|
||||||
"`text` TEXT NOT NULL,"
|
"`isSystem` BOOLEAN NOT NULL,"
|
||||||
"`chat_IncHistoryId` INTEGER NOT NULL"
|
"`text` TEXT NOT NULL,"
|
||||||
")");
|
"`chat_IncHistoryId` INTEGER NOT NULL"
|
||||||
sqlite_single_statement(conn.hand, "INSERT INTO `nickname` VALUES (?1)", {}, {{1, "root"}});
|
")");
|
||||||
sqlite_single_statement(conn.hand, "INSERT INTO `user` (`id`, `nickname`, `name`, `chatList_HistoryId`, `password`) VALUES "
|
sqlite_nooutput(conn, "INSERT INTO `nickname` VALUES (?1)", {}, {{1, "root"}});
|
||||||
"(0, ?1, ?2, 0, ?3)", {},
|
sqlite_nooutput(conn, "INSERT INTO `user` (`id`, `nickname`, `name`, `chatList_HistoryId`, `password`) VALUES "
|
||||||
{{1, "root"}, {2, "Rootov Root Rootovich"}, {3, root_pw}});
|
"(0, ?1, ?2, 0, ?3)", {},
|
||||||
sqlite_single_statement(conn.hand, "END");
|
{{1, "root"}, {2, "Rootov Root Rootovich"}, {3, root_pw}});
|
||||||
|
sqlite_nooutput(conn, "END");
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
sqlite_nooutput(conn, "ROLLBACK", {}, {});
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
89
src/web_chat/iu9_ca_web_chat_lib/login_cookie.cpp
Normal file
89
src/web_chat/iu9_ca_web_chat_lib/login_cookie.cpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#include "login_cookie.h"
|
||||||
|
#include <jsonincpp/string_representation.h>
|
||||||
|
#include "str_fields.h"
|
||||||
|
#include <engine_engine_number_9/baza_throw.h>
|
||||||
|
#include <engine_engine_number_9/http_structures/cookies.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
namespace iu9cawebchat {
|
||||||
|
bool is_login_cookie(const std::pair<std::string, std::string>& any_cookie_encoded) {
|
||||||
|
return een9::beginsWith(any_cookie_encoded.first, "login_") && !any_cookie_encoded.second.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Should be verified with iu9cawebchat::is_login_cookie. Can throw std::exception anyway */
|
||||||
|
LoginCookie decode_login_cookie(const std::pair<std::string, std::string>& login_cookie_encoded) {
|
||||||
|
const std::string& ft = login_cookie_encoded.first;
|
||||||
|
size_t bg = strlen("login_");
|
||||||
|
een9_ASSERT_pl(ft.size() >= bg);
|
||||||
|
size_t s_ = bg;
|
||||||
|
while (s_ < ft.size() && ft[s_] != '_')
|
||||||
|
s_++;
|
||||||
|
een9_ASSERT_pl(s_ + 1 < ft.size())
|
||||||
|
uint64_t sec = std::stoull(ft.substr(bg, s_ - bg));
|
||||||
|
uint64_t nsec = std::stoull(ft.substr(s_ + 1, ft.size() - s_ - 1));
|
||||||
|
een9_ASSERT_pl(nsec < 1000000000);
|
||||||
|
const json::JSON cnt = json::parse_str_flawless(base64_decode(login_cookie_encoded.second));
|
||||||
|
std::string nickname = cnt[0].g().asString();
|
||||||
|
std::string password = cnt[1].g().asString();
|
||||||
|
return LoginCookie{{(time_t)sec, (time_t)nsec}, nickname, password};
|
||||||
|
}
|
||||||
|
|
||||||
|
LoginCookie create_login_cookie(const std::string& nickname, const std::string& password) {
|
||||||
|
ns_time moment;
|
||||||
|
int ret = clock_gettime(CLOCK_REALTIME, &moment);
|
||||||
|
een9_ASSERT_on_iret(ret, "Can't get time");
|
||||||
|
return {moment, nickname, password};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, std::string> encode_login_cookie(const LoginCookie& cookie) {
|
||||||
|
json::JSON cnt;
|
||||||
|
cnt[1].g() = cookie.password;
|
||||||
|
cnt[0].g() = cookie.nickname;
|
||||||
|
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))};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool login_cookie_age_less(const LoginCookie& A, const LoginCookie& B) {
|
||||||
|
if (A.login_time.tv_sec < B.login_time.tv_sec)
|
||||||
|
return true;
|
||||||
|
if (A.login_time.tv_sec > B.login_time.tv_sec)
|
||||||
|
return false;
|
||||||
|
return A.login_time.tv_nsec < B.login_time.tv_nsec;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<LoginCookie> select_login_cookies(const std::vector<std::pair<std::string, std::string>> &cookie_list) {
|
||||||
|
std::vector<LoginCookie> needed;
|
||||||
|
try {
|
||||||
|
for (const std::pair<std::string, std::string>& C: cookie_list)
|
||||||
|
if (is_login_cookie(C))
|
||||||
|
needed.push_back(decode_login_cookie(C));
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return needed;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t select_oldest_login_cookie(const std::vector<LoginCookie> &login_cokie_list) {
|
||||||
|
size_t oldest = 0;
|
||||||
|
for (size_t i = 1; i < login_cokie_list.size(); i++)
|
||||||
|
if (login_cookie_age_less(login_cokie_list[i], login_cokie_list[oldest]))
|
||||||
|
oldest = i;
|
||||||
|
return login_cokie_list.empty() ? 0 : oldest;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_set_cookie_headers_to_login(const std::vector<LoginCookie> &old_login_cookies,
|
||||||
|
std::vector<std::pair<std::string, std::string>> &response_headers, const LoginCookie& new_login_cookie) {
|
||||||
|
add_set_cookie_headers_to_logout(old_login_cookies, response_headers);
|
||||||
|
een9::set_cookie({encode_login_cookie(new_login_cookie)}, response_headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_set_cookie_headers_to_logout(const std::vector<LoginCookie> &old_login_cookies,
|
||||||
|
std::vector<std::pair<std::string, std::string>> &response_headers) {
|
||||||
|
std::vector<std::pair<std::string, std::string>> anti_cookies(old_login_cookies.size());
|
||||||
|
for (size_t i = 0; i < old_login_cookies.size(); i++) {
|
||||||
|
anti_cookies[i] = {"login_" + std::to_string(old_login_cookies[i].login_time.tv_sec) +
|
||||||
|
"_" + std::to_string(old_login_cookies[i].login_time.tv_nsec), ""};
|
||||||
|
}
|
||||||
|
een9::set_cookie(anti_cookies, response_headers);
|
||||||
|
}
|
||||||
|
}
|
34
src/web_chat/iu9_ca_web_chat_lib/login_cookie.h
Normal file
34
src/web_chat/iu9_ca_web_chat_lib/login_cookie.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#ifndef IU9_CA_WEB_CHAT_LIB_LOGIN_COOKIE_H
|
||||||
|
#define IU9_CA_WEB_CHAT_LIB_LOGIN_COOKIE_H
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace iu9cawebchat {
|
||||||
|
typedef struct timespec ns_time;
|
||||||
|
|
||||||
|
struct LoginCookie {
|
||||||
|
ns_time login_time;
|
||||||
|
std::string nickname;
|
||||||
|
std::string password;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool is_login_cookie(const std::pair<std::string, std::string>& any_cookie_encoded);
|
||||||
|
LoginCookie decode_login_cookie(const std::pair<std::string, std::string>& login_cookie_encoded);
|
||||||
|
LoginCookie create_login_cookie(const std::string& nickname, const std::string& password);
|
||||||
|
std::pair<std::string, std::string> encode_login_cookie(const LoginCookie& cookie);
|
||||||
|
|
||||||
|
/* (incorrect cookie set is treated as unloginned user ) */
|
||||||
|
std::vector<LoginCookie> select_login_cookies(const std::vector<std::pair<std::string, std::string>> &cookie_list);
|
||||||
|
size_t select_oldest_login_cookie(const std::vector<LoginCookie>& login_cokie_list);
|
||||||
|
|
||||||
|
/* Populates response headers */
|
||||||
|
void add_set_cookie_headers_to_login(const std::vector<LoginCookie>& old_login_cookies,
|
||||||
|
std::vector<std::pair<std::string, std::string>>& response_headers, const LoginCookie& new_login_cookie);
|
||||||
|
void add_set_cookie_headers_to_logout(const std::vector<LoginCookie>& old_login_cookies,
|
||||||
|
std::vector<std::pair<std::string, std::string>>& response_headers);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -1,18 +1,29 @@
|
|||||||
#include "actions.h"
|
#include "actions.h"
|
||||||
|
|
||||||
#include <engine_engine_number_9/baza_throw.h>
|
#include <engine_engine_number_9/baza_throw.h>
|
||||||
#include <engine_engine_number_9/running_mainloop.h>
|
#include <engine_engine_number_9/os_utils.h>
|
||||||
#include <engine_engine_number_9/http_structures/response_gen.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <engine_engine_number_9/connecting_assets/static_asset_manager.h>
|
#include <engine_engine_number_9/connecting_assets/static_asset_manager.h>
|
||||||
#include <assert.h>
|
|
||||||
#include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
|
|
||||||
#include <new_york_transit_line/templater.h>
|
|
||||||
#include <sqlite3.h>
|
|
||||||
#include <engine_engine_number_9/socket_address.h>
|
|
||||||
#include "sqlite3_wrapper.h"
|
|
||||||
#include "str_fields.h"
|
|
||||||
#include "find_db.h"
|
#include "find_db.h"
|
||||||
|
#include <engine_engine_number_9/running_mainloop.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include "str_fields.h"
|
||||||
|
|
||||||
|
// #include <engine_engine_number_9/baza_throw.h>
|
||||||
|
// #include <engine_engine_number_9/running_mainloop.h>
|
||||||
|
// #include <engine_engine_number_9/http_structures/response_gen.h>
|
||||||
|
// #include <engine_engine_number_9/connecting_assets/static_asset_manager.h>
|
||||||
|
// #include <assert.h>
|
||||||
|
// #include <engine_engine_number_9/form_data_structure/urlencoded_query.h>
|
||||||
|
// #include <new_york_transit_line/templater.h>
|
||||||
|
// #include <sqlite3.h>
|
||||||
|
// #include <engine_engine_number_9/socket_address.h>
|
||||||
|
// #include "sqlite3_wrapper.h"
|
||||||
|
// #include "str_fields.h"
|
||||||
|
// #include "find_db.h"
|
||||||
|
// #include "login_cookie.h"
|
||||||
|
// #include <engine_engine_number_9/http_structures/cookies.h>
|
||||||
|
// #include <jsonincpp/string_representation.h>
|
||||||
|
|
||||||
|
#include "backend_logic/server_data_interact.h"
|
||||||
|
|
||||||
namespace iu9cawebchat {
|
namespace iu9cawebchat {
|
||||||
bool termination = false;
|
bool termination = false;
|
||||||
@ -43,11 +54,6 @@ namespace iu9cawebchat {
|
|||||||
int ret = find_db_sqlite_file_path(config, sqlite_db_path);
|
int ret = find_db_sqlite_file_path(config, sqlite_db_path);
|
||||||
een9_ASSERT(ret == 0, "Can't find database file");
|
een9_ASSERT(ret == 0, "Can't find database file");
|
||||||
|
|
||||||
struct WorkerGuestData {
|
|
||||||
/* Because templaters use libjsonincpp, they can't be READ by two thread simultaneously */
|
|
||||||
std::unique_ptr<nytl::Templater> templater;
|
|
||||||
std::unique_ptr<SqliteConnection> db;
|
|
||||||
};
|
|
||||||
std::vector<WorkerGuestData> worker_guest_data(slave_number);
|
std::vector<WorkerGuestData> worker_guest_data(slave_number);
|
||||||
for (int i = 0; i < slave_number; i++) {
|
for (int i = 0; i < slave_number; i++) {
|
||||||
worker_guest_data[i].templater = std::make_unique<nytl::Templater>(
|
worker_guest_data[i].templater = std::make_unique<nytl::Templater>(
|
||||||
@ -61,32 +67,53 @@ namespace iu9cawebchat {
|
|||||||
(const een9::SlaveTask& task, const een9::ClientRequest& req, een9::worker_id_t worker_id) -> std::string {
|
(const een9::SlaveTask& task, const een9::ClientRequest& req, een9::worker_id_t worker_id) -> std::string {
|
||||||
een9_ASSERT_pl(0 <= worker_id && worker_id < worker_guest_data.size());
|
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];
|
||||||
nytl::Templater& templater = *wgd.templater;
|
|
||||||
een9::StaticAsset sa;
|
een9::StaticAsset sa;
|
||||||
int ret;
|
sqlite_nooutput(*wgd.db, "BEGIN", {}, {});
|
||||||
auto rteee = [&](const std::string& el_name, bool pass_phr) -> std::string {
|
struct guard {SqliteConnection& conn; bool rollback = false; ~guard() {
|
||||||
std::string page = templater.render(el_name,
|
if (rollback)
|
||||||
pass_phr ? std::vector<const json::JSON*>{&config_presentation} : std::vector<const json::JSON*>{});
|
sqlite_nooutput(conn, "ROLLBACK", {}, {});
|
||||||
return een9::form_http_server_response_200("text/html", page);
|
else
|
||||||
};
|
sqlite_nooutput(conn, "END", {}, {});
|
||||||
if (req.uri_path == "/" || req.uri_path == "/list-rooms") {
|
}} guard_{*wgd.db};
|
||||||
return rteee("list-rooms", false);
|
try {
|
||||||
}
|
std::vector<std::pair<std::string, std::string>> cookies;
|
||||||
if (req.uri_path == "/login") {
|
std::vector<LoginCookie> login_cookies;
|
||||||
return rteee("login", true);
|
json::JSON userinfo;
|
||||||
}
|
int64_t logged_in_user = -1;
|
||||||
if (req.uri_path == "/chat") {
|
initial_extraction_of_all_the_useful_info_from_cookies(*wgd.db, req, cookies, login_cookies, userinfo, logged_in_user);
|
||||||
return rteee("chat", false);
|
|
||||||
}
|
std::string result;
|
||||||
if (req.uri_path == "/profile") {
|
|
||||||
return rteee("profile", false);
|
if (req.uri_path == "/" || req.uri_path == "/list-rooms") {
|
||||||
}
|
if (logged_in_user < 0)
|
||||||
if (req.uri_path == "/registration") {
|
result = een9::form_http_server_response_307("/login");
|
||||||
return rteee("registration", false);
|
return RTEE("list-rooms", config_presentation, wgd, userinfo);
|
||||||
|
}
|
||||||
|
if (req.uri_path == "/login") {
|
||||||
|
return when_page_login(wgd, config_presentation, req, login_cookies, userinfo);
|
||||||
|
}
|
||||||
|
if (req.uri_path == "/chat") {
|
||||||
|
return RTEE("chat", config_presentation, wgd, userinfo);
|
||||||
|
}
|
||||||
|
if (req.uri_path == "/profile") {
|
||||||
|
return RTEE("profile", config_presentation, wgd, userinfo);
|
||||||
|
}
|
||||||
|
// if (req.uri_path == "/registration") {
|
||||||
|
// RTEE("registration", config_presentation, wgd, userinfo);
|
||||||
|
// }
|
||||||
|
if (req.uri_path == "/internalapi/pollEvents") {
|
||||||
|
return when_internalapi_pollevents(wgd, req, logged_in_user);
|
||||||
|
}
|
||||||
|
if (req.uri_path == "/internalapi/getChatList") {
|
||||||
|
return when_internalapi_getchatlist(wgd, req, logged_in_user);
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
guard_.rollback = true;
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
/* Trying to interpret request as asset lookup */
|
/* Trying to interpret request as asset lookup */
|
||||||
ret = samI.get_asset(req.uri_path, sa);
|
int rets = samI.get_asset(req.uri_path, sa);
|
||||||
if (ret >= 0) {
|
if (rets >= 0) {
|
||||||
return een9::form_http_server_response_200(sa.type, sa.content);
|
return een9::form_http_server_response_200(sa.type, sa.content);
|
||||||
}
|
}
|
||||||
return een9::form_http_server_response_404("text/html", "<h1> Not found! </h1>");
|
return een9::form_http_server_response_404("text/html", "<h1> Not found! </h1>");
|
||||||
@ -98,26 +125,26 @@ namespace iu9cawebchat {
|
|||||||
WorkerGuestData& wgd = worker_guest_data[worker_id];
|
WorkerGuestData& wgd = worker_guest_data[worker_id];
|
||||||
try {
|
try {
|
||||||
if (req == "hello") {
|
if (req == "hello") {
|
||||||
return ":0 omg! hiii!! Hewwou :3 !!!!";
|
return ":0 omg! hiii!! Hewwou :3 !!!!\n";
|
||||||
}
|
}
|
||||||
if (req == "8") {
|
if (req == "8") {
|
||||||
termination = true;
|
termination = true;
|
||||||
return "Bye";
|
return "Bye\n";
|
||||||
}
|
}
|
||||||
std::string updaterootpw_pref = "updaterootpw";
|
std::string updaterootpw_pref = "updaterootpw";
|
||||||
if (een9::beginsWith(req, "updaterootpw")) {
|
if (een9::beginsWith(req, "updaterootpw")) {
|
||||||
size_t nid = updaterootpw_pref.size();
|
size_t nid = updaterootpw_pref.size();
|
||||||
if (nid >= req.size() || !isSPACE(req[nid]))
|
if (nid >= req.size() || !isSPACE(req[nid]))
|
||||||
return "Bad command syntax. Missing whitespace";
|
return "Bad command syntax. Missing whitespace\n";
|
||||||
std::string new_password = req.substr(nid + 1);
|
std::string new_password = req.substr(nid + 1);
|
||||||
if (!check_password(new_password))
|
if (!check_password(new_password))
|
||||||
een9_THROW("Bad password");
|
een9_THROW("Bad password");
|
||||||
sqlite_single_statement(wgd.db->hand,
|
sqlite_nooutput(*wgd.db,
|
||||||
"UPDATE `user` SET `password` = ?1 WHERE `id` = 0 ",
|
"UPDATE `user` SET `password` = ?1 WHERE `id` = 0 ",
|
||||||
{}, {{1, new_password}});
|
{}, {{1, new_password}});
|
||||||
return "Successul update";
|
return "Successul update\n";
|
||||||
}
|
}
|
||||||
return "Incorrect command";
|
return "Incorrect command\n";
|
||||||
} catch (std::exception& e) {
|
} catch (std::exception& e) {
|
||||||
return std::string("Server error\n") + e.what();
|
return std::string("Server error\n") + e.what();
|
||||||
}
|
}
|
||||||
|
@ -13,45 +13,22 @@ namespace iu9cawebchat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SqliteConnection::~SqliteConnection() {
|
SqliteConnection::~SqliteConnection() {
|
||||||
if (sqlite3_close(hand) != 0) {abort();}
|
sqlite3_close_v2(hand);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sqlite_single_statement(sqlite3* db_hand, const std::string& req_statement,
|
void sqlite_nooutput(SqliteConnection& conn, const std::string& req_statement,
|
||||||
const std::vector<std::pair<int, int64_t>>& int64_binds,
|
const std::vector<std::pair<int, int64_t>>& int64_binds,
|
||||||
const std::vector<std::pair<int, std::string>>& text8_binds) {
|
const std::vector<std::pair<int, std::string>>& text8_binds) {
|
||||||
sqlite3_stmt* stmt_obj = NULL;
|
SqliteStatement stmt(conn, req_statement, int64_binds, text8_binds);
|
||||||
int ret = sqlite3_prepare_v2(db_hand, req_statement.c_str(), -1, &stmt_obj, NULL);
|
int ret;
|
||||||
if (ret != 0) {
|
|
||||||
int err_pos = sqlite3_error_offset(db_hand);
|
|
||||||
een9_THROW("Compilation of request\n" + req_statement + "\nfailed" +
|
|
||||||
((err_pos >= 0) ? " with offset " + std::to_string(err_pos) : ""));
|
|
||||||
}
|
|
||||||
een9_ASSERT(ret == 0, "Can't compile request expression");
|
|
||||||
assert(sqlite3_errcode(db_hand) == SQLITE_OK);
|
|
||||||
struct Guard1{sqlite3_stmt*& r; ~Guard1(){if (sqlite3_finalize(r) != 0) {abort();}}} guard1{stmt_obj};
|
|
||||||
for (const std::pair<int, int64_t>& bv: int64_binds) {
|
|
||||||
ret = sqlite3_bind_int64(stmt_obj, bv.first, bv.second);
|
|
||||||
een9_ASSERT(ret, "Can't bind to parameter #" + std::to_string(bv.first));
|
|
||||||
}
|
|
||||||
for (const std::pair<int, std::string>& bv: text8_binds) {
|
|
||||||
een9_ASSERT(is_orthodox_string(bv.second), "Can't bind this string to parameter");
|
|
||||||
een9_ASSERT(bv.second.size() + 1 < INT_MAX, "Ah, oh, senpai, your string is toooo huge");
|
|
||||||
ret = sqlite3_bind_text(stmt_obj, bv.first, bv.second.c_str(), (int)bv.second.size(), SQLITE_STATIC);
|
|
||||||
}
|
|
||||||
while (true) {
|
while (true) {
|
||||||
ret = sqlite3_step(stmt_obj);
|
ret = sqlite_stmt_step(stmt, {}, {});
|
||||||
if (ret == SQLITE_DONE)
|
if (ret != SQLITE_ROW)
|
||||||
break;
|
break;
|
||||||
if (ret != SQLITE_ROW) {
|
int cc = sqlite3_column_count(stmt.stmt_obj);
|
||||||
printf("sqlite_row error!!!\n");
|
|
||||||
printf("%s\n", sqlite3_errmsg(db_hand));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int cc = sqlite3_column_count(stmt_obj);
|
|
||||||
std::vector<int> types(cc);
|
std::vector<int> types(cc);
|
||||||
for (int i = 0; i < cc; i++) {
|
for (int i = 0; i < cc; i++)
|
||||||
types[i] = sqlite3_column_type(stmt_obj, i);
|
types[i] = sqlite3_column_type(stmt.stmt_obj, i);
|
||||||
}
|
|
||||||
printf("Column: |");
|
printf("Column: |");
|
||||||
for (int i = 0; i < cc; i++) {
|
for (int i = 0; i < cc; i++) {
|
||||||
switch (types[i]) {
|
switch (types[i]) {
|
||||||
@ -70,25 +47,94 @@ namespace iu9cawebchat {
|
|||||||
printf("Values: | ");
|
printf("Values: | ");
|
||||||
for (int i = 0; i < cc; i++) {
|
for (int i = 0; i < cc; i++) {
|
||||||
if (types[i] == SQLITE_INTEGER) {
|
if (types[i] == SQLITE_INTEGER) {
|
||||||
printf("%lld | ", sqlite3_column_int64(stmt_obj, i));
|
printf("%lld | ", sqlite3_column_int64(stmt.stmt_obj, i));
|
||||||
} else if (types[i] == SQLITE_FLOAT) {
|
} else if (types[i] == SQLITE_FLOAT) {
|
||||||
printf("%lf | ", sqlite3_column_double(stmt_obj, i));
|
printf("%lf | ", sqlite3_column_double(stmt.stmt_obj, i));
|
||||||
} else if (types[i] == SQLITE_BLOB) {
|
} else if (types[i] == SQLITE_BLOB) {
|
||||||
const void* blob = sqlite3_column_blob(stmt_obj, i);
|
const void* blob = sqlite3_column_blob(stmt.stmt_obj, i);
|
||||||
een9_ASSERT(sqlite3_errcode(db_hand) == SQLITE_OK, "oom in sqlite3_column_blob");
|
een9_ASSERT(sqlite3_errcode(conn.hand) == SQLITE_OK, "oom in sqlite3_column_blob");
|
||||||
size_t sz = sqlite3_column_bytes(stmt_obj, i);
|
size_t sz = sqlite3_column_bytes(stmt.stmt_obj, i);
|
||||||
printf("Blob of size %lu | ", sz);
|
printf("Blob of size %lu | ", sz);
|
||||||
} else if (types[i] == SQLITE_NULL) {
|
} else if (types[i] == SQLITE_NULL) {
|
||||||
printf("NULL | ");
|
printf("NULL | ");
|
||||||
} else {
|
} else {
|
||||||
const unsigned char* text = sqlite3_column_text(stmt_obj, i);
|
const unsigned char* text = sqlite3_column_text(stmt.stmt_obj, i);
|
||||||
een9_ASSERT(text, "oom in sqlite3_column_text");
|
een9_ASSERT(text, "oom in sqlite3_column_text");
|
||||||
printf("%s | ", (const char*)text);
|
printf("%s | ", (const char*)text);
|
||||||
// todo: THIS F. B.S. IS NOT SAFE
|
// todo: print only if string is safe to print
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
printf("Request steps are done\n");
|
printf("Request steps are done\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SqliteStatement::SqliteStatement(SqliteConnection &connection, const std::string &req_statement,
|
||||||
|
const std::vector<std::pair<int, int64_t>> &int64_binds,
|
||||||
|
const std::vector<std::pair<int, std::string>> &text8_binds): conn(connection) {
|
||||||
|
|
||||||
|
int ret = sqlite3_prepare_v2(connection.hand, req_statement.c_str(), -1, &stmt_obj, NULL);
|
||||||
|
if (ret != 0) {
|
||||||
|
int err_pos = sqlite3_error_offset(connection.hand);
|
||||||
|
een9_THROW("Compilation of request\n" + req_statement + "\nfailed" +
|
||||||
|
((err_pos >= 0) ? " with offset " + std::to_string(err_pos) : ""));
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
for (const std::pair<int, int64_t>& bv: int64_binds) {
|
||||||
|
ret = sqlite3_bind_int64(stmt_obj, bv.first, bv.second);
|
||||||
|
een9_ASSERT(ret == 0, "sqlite3_bind_int64");
|
||||||
|
}
|
||||||
|
for (const std::pair<int, std::string>& bv: text8_binds) {
|
||||||
|
een9_ASSERT(is_orthodox_string(bv.second), "Can't bind this string to parameter");
|
||||||
|
een9_ASSERT(bv.second.size() + 1 < INT_MAX, "Ah, oh, senpai, your string is toooo huge");
|
||||||
|
ret = sqlite3_bind_text(stmt_obj, bv.first, bv.second.c_str(), (int)bv.second.size(), SQLITE_TRANSIENT);
|
||||||
|
een9_ASSERT(ret == 0, "sqlite3_bind_text");
|
||||||
|
}
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
sqlite3_finalize(stmt_obj);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SqliteStatement::~SqliteStatement() {
|
||||||
|
sqlite3_finalize(stmt_obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
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_text8_or_null *>> &ret_of_text8_or_null) {
|
||||||
|
int ret = sqlite3_step(stmt.stmt_obj);
|
||||||
|
if (ret == SQLITE_DONE)
|
||||||
|
return ret;
|
||||||
|
if (ret != SQLITE_ROW)
|
||||||
|
een9_THROW(std::string("sqlite3_step ") + sqlite3_errstr(ret) + " :> " + sqlite3_errmsg(stmt.conn.hand));
|
||||||
|
int cc = sqlite3_column_count(stmt.stmt_obj);
|
||||||
|
for (auto& resp: ret_of_integer_or_null) {
|
||||||
|
if (resp.first >= cc)
|
||||||
|
een9_THROW("Not enough");
|
||||||
|
int type = sqlite3_column_type(stmt.stmt_obj, resp.first);
|
||||||
|
if (type == SQLITE_INTEGER) {
|
||||||
|
*resp.second = fsql_integer_or_null{true, (int64_t)sqlite3_column_int64(stmt.stmt_obj, resp.first)};
|
||||||
|
} else if (type == SQLITE_NULL) {
|
||||||
|
*resp.second = fsql_integer_or_null{false, 0};
|
||||||
|
} else
|
||||||
|
een9_THROW("sqlite3_column_type. Incorrect type");
|
||||||
|
}
|
||||||
|
for (auto& resp: ret_of_text8_or_null) {
|
||||||
|
if (resp.first >= cc)
|
||||||
|
een9_THROW("Not enough");
|
||||||
|
int type = sqlite3_column_type(stmt.stmt_obj, resp.first);
|
||||||
|
if (type == SQLITE_TEXT) {
|
||||||
|
/* Hm, yeah, I am reinterpret_casting between char and unsigned char again */
|
||||||
|
const unsigned char* text = sqlite3_column_text(stmt.stmt_obj, resp.first);
|
||||||
|
een9_ASSERT(text, "oom in sqlite3_column_text");
|
||||||
|
*resp.second = fsql_text8_or_null{true, reinterpret_cast<const char*>(text)};
|
||||||
|
} else if (type == SQLITE_NULL) {
|
||||||
|
*resp.second = fsql_text8_or_null{false, ""};
|
||||||
|
} else
|
||||||
|
een9_THROW("sqlite3_column_type. Incorrect type");
|
||||||
|
}
|
||||||
|
return SQLITE_ROW;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,35 @@ namespace iu9cawebchat {
|
|||||||
~SqliteConnection();
|
~SqliteConnection();
|
||||||
};
|
};
|
||||||
|
|
||||||
void sqlite_single_statement(sqlite3* db_hand, const std::string& req_statement,
|
void sqlite_nooutput(SqliteConnection& conn, const std::string& req_statement,
|
||||||
const std::vector<std::pair<int, int64_t>>& int64_binds= {},
|
const std::vector<std::pair<int, int64_t>>& int64_binds = {},
|
||||||
const std::vector<std::pair<int, std::string>>& text8_binds = {});
|
const std::vector<std::pair<int, std::string>>& text8_binds = {});
|
||||||
|
|
||||||
|
struct fsql_integer_or_null {
|
||||||
|
bool exist;
|
||||||
|
int64_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fsql_text8_or_null {
|
||||||
|
bool exist;
|
||||||
|
std::string value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SqliteStatement {
|
||||||
|
SqliteConnection& conn;
|
||||||
|
sqlite3_stmt* stmt_obj = NULL;
|
||||||
|
SqliteStatement(SqliteConnection& connection, const std::string& req_statement,
|
||||||
|
const std::vector<std::pair<int, int64_t>>& int64_binds = {},
|
||||||
|
const std::vector<std::pair<int, std::string>>& text8_binds = {});
|
||||||
|
SqliteStatement(SqliteStatement&) = delete;
|
||||||
|
SqliteStatement& operator=(SqliteStatement&) = delete;
|
||||||
|
|
||||||
|
~SqliteStatement();
|
||||||
|
};
|
||||||
|
|
||||||
|
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_text8_or_null*>>& ret_of_text8_or_null);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
31
src/web_chat/misc_tests/api_test0.cpp
Normal file
31
src/web_chat/misc_tests/api_test0.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include <iu9_ca_web_chat_lib/backend_logic/server_data_interact.h>
|
||||||
|
#include <iu9_ca_web_chat_lib/sqlite3_wrapper.h>
|
||||||
|
#include <jsonincpp/string_representation.h>
|
||||||
|
|
||||||
|
using namespace iu9cawebchat;
|
||||||
|
|
||||||
|
void test(SqliteConnection& conn, int64_t uid){
|
||||||
|
json::JSON Recv = internalapi_getChatList(conn, uid);
|
||||||
|
printf("%s\n", json::generate_str(Recv, json::print_pretty).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_polling(SqliteConnection& conn, int64_t uid, int64_t LocalHistoryId) {
|
||||||
|
json::JSON Sent;
|
||||||
|
Sent["scope"][0]["type"] = json::JSON("chatlist");
|
||||||
|
Sent["scope"][0]["LocalHistoryId"] = json::JSON(LocalHistoryId);
|
||||||
|
|
||||||
|
json::JSON Recv = internalapi_pollEvents(conn, uid, Sent);
|
||||||
|
printf("%s\n", json::generate_str(Recv, json::print_pretty).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
SqliteConnection conn("./iu9-ca-web-chat.db");
|
||||||
|
// test(conn, 0);
|
||||||
|
// test(conn, 1);
|
||||||
|
// test(conn, 2);
|
||||||
|
// printf("\n\n ===== Now testing polling of events ===== \n\n");
|
||||||
|
test_polling(conn, 1, 0);
|
||||||
|
test_polling(conn, 1, 1);
|
||||||
|
test_polling(conn, 1, 2);
|
||||||
|
return 0;
|
||||||
|
}
|
14
src/web_chat/misc_tests/find_credentials_test.cpp
Normal file
14
src/web_chat/misc_tests/find_credentials_test.cpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#include <iu9_ca_web_chat_lib/backend_logic/server_data_interact.h>
|
||||||
|
#include <iu9_ca_web_chat_lib/sqlite3_wrapper.h>
|
||||||
|
#include <jsonincpp/string_representation.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
using namespace iu9cawebchat;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
SqliteConnection conn("./iu9-ca-web-chat.db");
|
||||||
|
for (size_t i = 0; i < 100; i++) {
|
||||||
|
int64_t uid = find_user_by_credentials(conn, "root", "12345678");
|
||||||
|
assert(uid == 0);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user