master #6
@ -34,11 +34,11 @@
|
|||||||
<a href="/list-rooms" id="go-to-chat-list" class="panel-thing">
|
<a href="/list-rooms" id="go-to-chat-list" class="panel-thing">
|
||||||
<img alt="Go to list of chats" src="/assets/img/list-rooms.svg" width="32px">
|
<img alt="Go to list of chats" src="/assets/img/list-rooms.svg" width="32px">
|
||||||
</a>
|
</a>
|
||||||
<a href="/user/{% WRITE userinfo.nickname %}" id="go-to-my-profile" class="panel-thing">
|
<a href="/user/{% W userinfo.nickname %}" id="go-to-my-profile" class="panel-thing">
|
||||||
<img alt="Go to my profile" src="/assets/img/user.svg" width="32px">
|
<img alt="Go to my profile" src="/assets/img/user.svg" width="32px">
|
||||||
</a>
|
</a>
|
||||||
<p class="panel-thing panel-header-txt"> Members list of {% WRITE openedchat.name %} ({% WRITE openedchat.nickname %})</p>
|
<p class="panel-thing panel-header-txt"> Members list of {% W openedchat.name %} ({% W openedchat.nickname %})</p>
|
||||||
<a href="/chat/{% WRITE openedchat.nickname %}" id="go-to-chat-settings" class="panel-thing">
|
<a href="/chat/{% W openedchat.nickname %}" id="go-to-chat-settings" class="panel-thing">
|
||||||
<img alt="Back to chat" src="/assets/img/return.svg" width="32px">
|
<img alt="Back to chat" src="/assets/img/return.svg" width="32px">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,11 +36,11 @@
|
|||||||
<a href="/list-rooms" id="go-to-chat-list" class="panel-thing">
|
<a href="/list-rooms" id="go-to-chat-list" class="panel-thing">
|
||||||
<img alt="Go to list of chats" src="/assets/img/list-rooms.svg" width="32px">
|
<img alt="Go to list of chats" src="/assets/img/list-rooms.svg" width="32px">
|
||||||
</a>
|
</a>
|
||||||
<a href="/user/{% WRITE userinfo.nickname %}" id="go-to-my-profile" class="panel-thing">
|
<a href="/user/{% W userinfo.nickname %}" id="go-to-my-profile" class="panel-thing">
|
||||||
<img alt="Go to my profile" src="/assets/img/user.svg" width="32px">
|
<img alt="Go to my profile" src="/assets/img/user.svg" width="32px">
|
||||||
</a>
|
</a>
|
||||||
<p class="panel-thing panel-header-txt"> {% WRITE openedchat.name %} ({% WRITE openedchat.nickname %})</p>
|
<p class="panel-thing panel-header-txt"> {% W openedchat.name %} ({% W openedchat.nickname %})</p>
|
||||||
<a href="/chat-members/{% WRITE openedchat.nickname %}" id="go-to-chat-settings" class="panel-thing">
|
<a href="/chat-members/{% W openedchat.nickname %}" id="go-to-chat-settings" class="panel-thing">
|
||||||
<img alt="Settings of chat. List of members" src="/assets/img/settings-iron.svg" width="32px">
|
<img alt="Settings of chat. List of members" src="/assets/img/settings-iron.svg" width="32px">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -16,28 +16,28 @@
|
|||||||
<a href="/list-rooms" id="go-to-chat-list" class="panel-thing">
|
<a href="/list-rooms" id="go-to-chat-list" class="panel-thing">
|
||||||
<img alt="Go to list of chats" src="/assets/img/list-rooms.svg" width="32px">
|
<img alt="Go to list of chats" src="/assets/img/list-rooms.svg" width="32px">
|
||||||
</a>
|
</a>
|
||||||
<a href="/user/{% WRITE userinfo.nickname %}" id="go-to-my-profile" class="panel-thing">
|
<a href="/user/{% W userinfo.nickname %}" id="go-to-my-profile" class="panel-thing">
|
||||||
<img alt="Go to my profile" src="/assets/img/user.svg" width="32px">
|
<img alt="Go to my profile" src="/assets/img/user.svg" width="32px">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% FOR error IN errors %}
|
{% FOR error IN errors %}
|
||||||
<div class="server-notif-error-msg-box">
|
<div class="server-notif-error-msg-box">
|
||||||
{% WRITE error.text %}
|
{% W error.text %}
|
||||||
</div>
|
</div>
|
||||||
{% ENDFOR %}
|
{% ENDFOR %}
|
||||||
|
|
||||||
<div class="profile-container">
|
<div class="profile-container">
|
||||||
<h2 class="profile-name-text">{% WRITE alienprofile.name %}</h2>
|
<h2 class="profile-name-text">{% W alienprofile.name %}</h2>
|
||||||
<h3 class="profile-nickname-text">Nickname: {% WRITE alienprofile.nickname %}</h3>
|
<h3 class="profile-nickname-text">Nickname: {% W alienprofile.nickname %}</h3>
|
||||||
<p class="profile-bio-text">
|
<p class="profile-bio-text">
|
||||||
{% WRITE alienprofile.bio %}
|
{% W alienprofile.bio %}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="profile-container resp-container">
|
<div class="profile-container resp-container">
|
||||||
<h1 class="wide-centered-header">Change user attributes</h1>
|
<h1 class="wide-centered-header">Change user attributes</h1>
|
||||||
<form action = "/user/{% WRITE alienprofile.nickname %}" method="post" enctype="application/x-www-form-urlencoded">
|
<form action = "/user/{% W alienprofile.nickname %}" method="post" enctype="application/x-www-form-urlencoded">
|
||||||
<table class="logins-input-table">
|
<table class="logins-input-table">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="logins-input-td1">
|
<td class="logins-input-td1">
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{% ELDEF main JSON pres JSON userinfo JSON initial_chatListUpdResp %}
|
{% ELDEF main JSON pres JSON userinfo JSON initial_chatListUpdResp %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="{% WRITE pres.lang %}">
|
<html lang="{% W 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">
|
||||||
@ -51,7 +51,7 @@
|
|||||||
x
|
x
|
||||||
<div class="document-container resp-container">
|
<div class="document-container resp-container">
|
||||||
<div id="navigation-panel" class="panel">
|
<div id="navigation-panel" class="panel">
|
||||||
<a href="/user/{% WRITE userinfo.nickname %}" id="go-to-my-profile" class="panel-thing">
|
<a href="/user/{% W userinfo.nickname %}" id="go-to-my-profile" class="panel-thing">
|
||||||
<img alt="Go to my profile" src="/assets/img/user.svg" width="32px">
|
<img alt="Go to my profile" src="/assets/img/user.svg" width="32px">
|
||||||
</a>
|
</a>
|
||||||
<p class="panel-thing panel-header-txt">
|
<p class="panel-thing panel-header-txt">
|
||||||
|
@ -1,39 +1,41 @@
|
|||||||
{% ELDEF main JSON pres JSON userinfo JSON errors %}
|
{% ELDEF main JSON pres JSON userinfo JSON errors %}
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="{% W 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">
|
||||||
<link rel="icon" type="image/png" href="/assets/img/favicon.png">
|
<link rel="icon" type="image/png" href="/assets/img/favicon.png">
|
||||||
<link rel="stylesheet" href="/assets/css/common.css">
|
<link rel="stylesheet" href="/assets/css/common.css">
|
||||||
<link rel="stylesheet" href="/assets/css/login.css">
|
<link rel="stylesheet" href="/assets/css/login.css">
|
||||||
<title>Login Page</title>
|
<title>{% W pres.loginP.header %}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% FOR error IN errors %}
|
{% FOR error IN errors %}
|
||||||
<div class="server-notif-error-msg-box">
|
<div class="server-notif-error-msg-box">
|
||||||
{% WRITE error.text %}
|
{% W error.text %}
|
||||||
</div>
|
</div>
|
||||||
{% ENDFOR %}
|
{% ENDFOR %}
|
||||||
|
|
||||||
<div class="form-container">
|
<div class="form-container">
|
||||||
<h1 class="wide-centered-header">Login</h1>
|
<h1 class="wide-centered-header">{% W pres.loginP.header %}</h1>
|
||||||
<form action="/login" method="post" enctype="application/x-www-form-urlencoded">
|
<form action="/login" method="post" enctype="application/x-www-form-urlencoded">
|
||||||
<table class="logins-input-table">
|
<table class="logins-input-table">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="logins-input-td1"><label for="input-nickname">Enter user nickname:</label></td>
|
<td class="logins-input-td1"><label for="input-nickname">{% W pres.loginP.directive-nickname %}</label></td>
|
||||||
<td class="logins-input-td2">
|
<td class="logins-input-td2">
|
||||||
<input name="nickname" id="input-nickname" type="text" placeholder="Nickname" class="one-line-input" required>
|
<input type="text" name="nickname" id="input-nickname"
|
||||||
|
placeholder="{% W pres.loginP.placeholder-nickname %}" class="one-line-input" required>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="logins-input-td1"><label for="input-password">Enter password:</label></td>
|
<td class="logins-input-td1"><label for="input-password">{% W pres.loginP.directive-password %}</label></td>
|
||||||
<td class="logins-input-td2">
|
<td class="logins-input-td2">
|
||||||
<input name="password" id="input-password" type="password" placeholder="Password" class="one-line-input" required>
|
<input name="password" id="input-password" type="password"
|
||||||
|
placeholder="{% W pres.loginP.placeholder-password %}" class="one-line-input" required>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<button class="action-button centered-block-el" type="submit">Login</button>
|
<button class="action-button centered-block-el" type="submit">{% W pres.loginP.act %}</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -16,16 +16,16 @@
|
|||||||
<a href="/list-rooms" id="go-to-chat-list" class="panel-thing">
|
<a href="/list-rooms" id="go-to-chat-list" class="panel-thing">
|
||||||
<img alt="Go to list of chats" src="/assets/img/list-rooms.svg" width="32px">
|
<img alt="Go to list of chats" src="/assets/img/list-rooms.svg" width="32px">
|
||||||
</a>
|
</a>
|
||||||
<a href="/user/{% WRITE userinfo.nickname %}" id="go-to-my-profile" class="panel-thing">
|
<a href="/user/{% W userinfo.nickname %}" id="go-to-my-profile" class="panel-thing">
|
||||||
<img alt="Go to my profile" src="/assets/img/user.svg" width="32px">
|
<img alt="Go to my profile" src="/assets/img/user.svg" width="32px">
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="profile-container">
|
<div class="profile-container">
|
||||||
<h2 class="profile-name-text">{% WRITE alienprofile.name %}</h2>
|
<h2 class="profile-name-text">{% W alienprofile.name %}</h2>
|
||||||
<h3 class="profile-nickname-text">Nickname: {% WRITE alienprofile.nickname %}</h3>
|
<h3 class="profile-nickname-text">Nickname: {% W alienprofile.nickname %}</h3>
|
||||||
<p class="profile-bio-text">
|
<p class="profile-bio-text">
|
||||||
{% WRITE alienprofile.bio %}
|
{% W alienprofile.bio %}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
11
assets/lang/en-US.lang.json
Normal file
11
assets/lang/en-US.lang.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"lang": "en",
|
||||||
|
"loginP": {
|
||||||
|
"header": "Login",
|
||||||
|
"directive-nickname": "Enter your nickname:",
|
||||||
|
"placeholder-nickname": "Nickname",
|
||||||
|
"directive-password": "Enter password:",
|
||||||
|
"placeholder-password": "Password",
|
||||||
|
"act": "Login"
|
||||||
|
}
|
||||||
|
}
|
11
assets/lang/ru-RU.lang.json
Normal file
11
assets/lang/ru-RU.lang.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"lang": "ru",
|
||||||
|
"loginP": {
|
||||||
|
"header": "Вход",
|
||||||
|
"directive-nickname": "Введите свой никнейм:",
|
||||||
|
"placeholder-nickname": "",
|
||||||
|
"directive-password": "Введите пароль:",
|
||||||
|
"placeholder-password": "",
|
||||||
|
"act": "Войти"
|
||||||
|
}
|
||||||
|
}
|
@ -78,6 +78,7 @@ struct CAWebChat {
|
|||||||
"http_structures/client_request_parse.cpp",
|
"http_structures/client_request_parse.cpp",
|
||||||
"http_structures/response_gen.cpp",
|
"http_structures/response_gen.cpp",
|
||||||
"http_structures/cookies.cpp",
|
"http_structures/cookies.cpp",
|
||||||
|
"http_structures/accept_language.cpp",
|
||||||
"connecting_assets/static_asset_manager.cpp",
|
"connecting_assets/static_asset_manager.cpp",
|
||||||
"running_mainloop.cpp",
|
"running_mainloop.cpp",
|
||||||
"form_data_structure/urlencoded_query.cpp",
|
"form_data_structure/urlencoded_query.cpp",
|
||||||
@ -97,6 +98,7 @@ struct CAWebChat {
|
|||||||
"http_structures/client_request.h",
|
"http_structures/client_request.h",
|
||||||
"http_structures/cookies.h",
|
"http_structures/cookies.h",
|
||||||
"http_structures/response_gen.h",
|
"http_structures/response_gen.h",
|
||||||
|
"http_structures/accept_language.h",
|
||||||
"running_mainloop.h",
|
"running_mainloop.h",
|
||||||
"form_data_structure/urlencoded_query.h",
|
"form_data_structure/urlencoded_query.h",
|
||||||
"socket_address.h",
|
"socket_address.h",
|
||||||
@ -141,6 +143,7 @@ struct CAWebChat {
|
|||||||
CTargetDependenceOnExternalLibrary{"sqlite3", {true, true}}
|
CTargetDependenceOnExternalLibrary{"sqlite3", {true, true}}
|
||||||
};
|
};
|
||||||
T.units = {
|
T.units = {
|
||||||
|
"localizator.cpp",
|
||||||
"initialize.cpp",
|
"initialize.cpp",
|
||||||
"run.cpp",
|
"run.cpp",
|
||||||
"str_fields.cpp",
|
"str_fields.cpp",
|
||||||
|
@ -1,33 +1,9 @@
|
|||||||
{
|
{
|
||||||
"presentation": {
|
"lang": {
|
||||||
"lang": "ru",
|
"whitelist": ["*"],
|
||||||
"instance-identity": {
|
"force-order": [
|
||||||
"top-title": "Вэб чат от ИУ9"
|
"ru"
|
||||||
},
|
]
|
||||||
"phr": {
|
|
||||||
"decl": {
|
|
||||||
"enter": "Вход",
|
|
||||||
"nickname": "Никнейм",
|
|
||||||
"password": "Пароль",
|
|
||||||
"page-login": "Вход",
|
|
||||||
"list-of-chat-rooms": "Список Чат-Коsмнат",
|
|
||||||
"name-of-room": "Название комнаты",
|
|
||||||
"create-room": "Создать комнату",
|
|
||||||
|
|
||||||
"incorrect-nickname-or-password": "Неправильный никнейм или пароль",
|
|
||||||
|
|
||||||
"incorrect-profile-data": "Неправильно заполнены поля профиля"
|
|
||||||
},
|
|
||||||
"ask" : {
|
|
||||||
"select-chat-room": "Выберете чат комнату"
|
|
||||||
},
|
|
||||||
"act": {
|
|
||||||
"enter": "Войти",
|
|
||||||
"create-room": "Создать комнату",
|
|
||||||
"confirm": "Подтвердить",
|
|
||||||
"create": "Создать"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"assets": "./assets",
|
"assets": "./assets",
|
||||||
"database": {
|
"database": {
|
||||||
@ -41,7 +17,7 @@
|
|||||||
"storage-size-limit": 100000000000
|
"storage-size-limit": 100000000000
|
||||||
},
|
},
|
||||||
"server": {
|
"server": {
|
||||||
"workers": 8,
|
"workers": 16,
|
||||||
"http-listen": ["127.0.0.1:1025"],
|
"http-listen": ["127.0.0.1:1025"],
|
||||||
"admin-command-listen": ["[::1]:1026"]
|
"admin-command-listen": ["[::1]:1026"]
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
#include "accept_language.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include "grammar.h"
|
||||||
|
#include "../baza_inter.h"
|
||||||
|
|
||||||
|
namespace een9 {
|
||||||
|
bool AcceptLanguageSpec(char ch) {
|
||||||
|
return ch == ',' || ch == ';' || ch == '=';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* todo: This is one of many places in een9, where bad alloc does not interrupt request,
|
||||||
|
* todo: completely changing response instead. (see cookies and login cookies lol)
|
||||||
|
* todo: I have to do something about it. Maybe add more exception types */
|
||||||
|
std::vector<std::string> parse_header_Accept_Language(const std::string &AcceptLanguage) {
|
||||||
|
size_t n = AcceptLanguage.size();
|
||||||
|
struct LR {
|
||||||
|
std::string lr;
|
||||||
|
float q = 1;
|
||||||
|
};
|
||||||
|
size_t i = 0;
|
||||||
|
auto skipOWS = [&]() {
|
||||||
|
while (i < n && isSPACE(AcceptLanguage[i]))
|
||||||
|
i++;
|
||||||
|
};
|
||||||
|
auto isThis = [&](char ch) {
|
||||||
|
skipOWS();
|
||||||
|
return i >= n ? false : AcceptLanguage[i] == ch;
|
||||||
|
};
|
||||||
|
auto readTkn = [&]() -> std::string {
|
||||||
|
skipOWS();
|
||||||
|
if (i >= n)
|
||||||
|
return "";
|
||||||
|
size_t bg = i;
|
||||||
|
while (i < n && !AcceptLanguageSpec(AcceptLanguage[i]) && !isSPACE(AcceptLanguage[i]))
|
||||||
|
i++;
|
||||||
|
return AcceptLanguage.substr(bg, i - bg);
|
||||||
|
};
|
||||||
|
std::vector<LR> lrs;
|
||||||
|
#define myMsg "Bad Accept-Language"
|
||||||
|
while (i < n) {
|
||||||
|
skipOWS();
|
||||||
|
if (i >= n)
|
||||||
|
break;
|
||||||
|
if (!lrs.empty()) {
|
||||||
|
if (isThis(','))
|
||||||
|
i++;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lrs.emplace_back();
|
||||||
|
lrs.back().lr = readTkn();
|
||||||
|
LR lr{readTkn(), 0};
|
||||||
|
if (isThis(';')) {
|
||||||
|
i++;
|
||||||
|
if (readTkn() != "q")
|
||||||
|
THROW(myMsg);
|
||||||
|
if (!isThis('='))
|
||||||
|
THROW(myMsg);
|
||||||
|
i++;
|
||||||
|
lrs.back().q = std::stof(readTkn());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::sort(lrs.begin(), lrs.end(), [](const LR& A, const LR& B) {
|
||||||
|
return A.q > B.q;
|
||||||
|
});
|
||||||
|
std::vector<std::string> result;
|
||||||
|
result.reserve(lrs.size());
|
||||||
|
for (const LR& lr: lrs)
|
||||||
|
result.push_back(lr.lr == "*" ? "" : lr.lr);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
#ifndef ENGINE_ENGINE_NUMBER_9_HTTP_STRUCTURES_ACCEPT_LANGUAGE_H
|
||||||
|
#define ENGINE_ENGINE_NUMBER_9_HTTP_STRUCTURES_ACCEPT_LANGUAGE_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace een9 {
|
||||||
|
/* Returns language ranges, sorted by priority (reverse)
|
||||||
|
* throws std::exception if header is incorrect! But it is not guaranteed. Maybe it won't */
|
||||||
|
std::vector<std::string> parse_header_Accept_Language(const std::string& AcceptLanguage);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -1,14 +1,20 @@
|
|||||||
#include "cookies.h"
|
#include "cookies.h"
|
||||||
#include "../baza_inter.h"
|
#include "../baza_inter.h"
|
||||||
|
|
||||||
|
#include "grammar.h"
|
||||||
|
|
||||||
namespace een9 {
|
namespace een9 {
|
||||||
bool isSPACE(char ch) {
|
bool isSPACE(char ch) {
|
||||||
return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
|
return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isALPHANUM(char ch) {
|
||||||
|
return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ('0' <= ch && ch <= '9');
|
||||||
|
}
|
||||||
|
|
||||||
bool isToken(const std::string &str) {
|
bool isToken(const std::string &str) {
|
||||||
for (char ch : str) {
|
for (char ch : str) {
|
||||||
if (!(('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || ('0' <= ch && ch <= '9')
|
if (!(isALPHANUM(ch)
|
||||||
|| ch == '!' || ch == '#' || ch == '$' || ch == '%' || ch == '&' || ch == '\'' || ch == '*'
|
|| ch == '!' || ch == '#' || ch == '$' || ch == '%' || ch == '&' || ch == '\'' || ch == '*'
|
||||||
|| ch == '+' || ch == '-' || ch == '.' || ch == '^' || ch == '_' || ch == '`' || ch == '|'
|
|| ch == '+' || ch == '-' || ch == '.' || ch == '^' || ch == '_' || ch == '`' || ch == '|'
|
||||||
|| ch == '~' ))
|
|| ch == '~' ))
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
#ifndef ENGINE_ENGINE_NUMBER_9_HTTP_STRUCTURES_GRAMMAR_H
|
||||||
|
#define ENGINE_ENGINE_NUMBER_9_HTTP_STRUCTURES_GRAMMAR_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace een9 {
|
||||||
|
bool isSPACE(char ch);
|
||||||
|
bool isALPHANUM(char ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
35
src/http_server/misc_tests/accept_language_test.cpp
Normal file
35
src/http_server/misc_tests/accept_language_test.cpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#include <engine_engine_number_9/baza_inter.h>
|
||||||
|
#include <engine_engine_number_9/http_structures/accept_language.h>
|
||||||
|
|
||||||
|
using namespace een9;
|
||||||
|
|
||||||
|
void test(const std::string& al, const std::vector<std::string>& rls) {
|
||||||
|
std::vector<std::string> got = parse_header_Accept_Language(al);
|
||||||
|
if (got != rls) {
|
||||||
|
printf("Test failed: wrong answer\n");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
printf("Test passed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void btest(const std::string& al) {
|
||||||
|
try {
|
||||||
|
parse_header_Accept_Language(al);
|
||||||
|
} catch (std::exception& e) {}
|
||||||
|
printf("...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
test("RU-RU, uk-EN; q = 12.22", {"uk-EN", "RU-RU"});
|
||||||
|
test(" RU-RU ,uk-EN; q = 12.22 ", {"uk-EN", "RU-RU"});
|
||||||
|
test(" AAA; q=0.1, BBB-bb ; q=3, *; q=3", {"BBB-bb", "", "AAA"});
|
||||||
|
test(" AAA; q=0.1, BBB-bb ; q=2.5, *; q=4.5", {"", "BBB-bb", "AAA"});
|
||||||
|
test("ABB, AAA; q=0.1,AAB, BBB-bb ; q=2.5, *; q=4.5", {"", "BBB-bb", "ABB", "AAB", "AAA"});
|
||||||
|
test("", {});
|
||||||
|
test(" ", {});
|
||||||
|
btest(";;;;");
|
||||||
|
btest(";;==;;");
|
||||||
|
btest("-;");
|
||||||
|
btest("-==");
|
||||||
|
return 0;
|
||||||
|
}
|
@ -453,11 +453,11 @@ namespace nytl {
|
|||||||
P.passed_arguments = {parse_expression(ctx, local_var_names)};
|
P.passed_arguments = {parse_expression(ctx, local_var_names)};
|
||||||
skip_magic_block_end(ctx, syntax);
|
skip_magic_block_end(ctx, syntax);
|
||||||
};
|
};
|
||||||
if (op == "WRITE") {
|
if (op == "WRITE" || op == "W") {
|
||||||
mediocre_operator("str2text");
|
mediocre_operator("str2text");
|
||||||
goto ya_e_ya_h_i_ya_g_d_o;;
|
goto ya_e_ya_h_i_ya_g_d_o;;
|
||||||
}
|
}
|
||||||
if (op == "ROUGHINSERT") {
|
if (op == "ROUGHINSERT" || op == "RI") {
|
||||||
mediocre_operator("str2code");
|
mediocre_operator("str2code");
|
||||||
goto ya_e_ya_h_i_ya_g_d_o;;
|
goto ya_e_ya_h_i_ya_g_d_o;;
|
||||||
}
|
}
|
||||||
|
@ -11,12 +11,14 @@
|
|||||||
#include <jsonincpp/jsonobj.h>
|
#include <jsonincpp/jsonobj.h>
|
||||||
#include <new_york_transit_line/templater.h>
|
#include <new_york_transit_line/templater.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include "../localizator.h"
|
||||||
|
|
||||||
namespace iu9cawebchat {
|
namespace iu9cawebchat {
|
||||||
struct WorkerGuestData {
|
struct WorkerGuestData {
|
||||||
/* Because templaters use libjsonincpp, they can't be READ by two thread simultaneously */
|
/* Because templaters use libjsonincpp, they can't be READ by two thread simultaneously */
|
||||||
std::unique_ptr<nytl::Templater> templater;
|
std::unique_ptr<nytl::Templater> templater;
|
||||||
std::unique_ptr<SqliteConnection> db;
|
std::unique_ptr<SqliteConnection> db;
|
||||||
|
std::unique_ptr<Localizator> locales;
|
||||||
};
|
};
|
||||||
|
|
||||||
void initial_extraction_of_all_the_useful_info_from_cookies(
|
void initial_extraction_of_all_the_useful_info_from_cookies(
|
||||||
|
154
src/web_chat/iu9_ca_web_chat_lib/localizator.cpp
Normal file
154
src/web_chat/iu9_ca_web_chat_lib/localizator.cpp
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
#include "localizator.h"
|
||||||
|
|
||||||
|
#include <jsonincpp/string_representation.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <engine_engine_number_9/os_utils.h>
|
||||||
|
#include <engine_engine_number_9/baza_throw.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace iu9cawebchat {
|
||||||
|
std::string languageRangeSimpler(const std::string& a) {
|
||||||
|
return a == "*" ? "" : a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// I won't use iterators. c plus plus IS a scripting language and I do not want to mess with iterators
|
||||||
|
std::vector<std::string> languageRangeGetPrefixes(const std::string& lr) {
|
||||||
|
if (lr.empty())
|
||||||
|
return {""};
|
||||||
|
std::vector<std::string> result = {"", ""};
|
||||||
|
for (size_t i = 0; i < lr.size(); i++) {
|
||||||
|
if (lr[i] == '-')
|
||||||
|
result.push_back(result.back());
|
||||||
|
result.back() += lr[i];
|
||||||
|
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isInWhitelist(const std::string& lr, const std::vector<std::string>& whitelist) {
|
||||||
|
for (const std::string& prefix : languageRangeGetPrefixes(lr))
|
||||||
|
for (const std::string& nicePrefix: whitelist)
|
||||||
|
if (prefix == nicePrefix)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<LanguageFile> collect_lang_dir_content(const std::string& lang_dir,
|
||||||
|
const std::vector<std::string>& whitelist) {
|
||||||
|
|
||||||
|
std::vector<LanguageFile> result;
|
||||||
|
errno = 0;
|
||||||
|
DIR* D = opendir(lang_dir.c_str());
|
||||||
|
struct Guard1{ DIR*& D; ~Guard1(){ closedir(D); } } g1{D};
|
||||||
|
if (!D)
|
||||||
|
een9_THROW_on_errno("opendir (" + lang_dir + ")");
|
||||||
|
while (true) {
|
||||||
|
errno = 0;
|
||||||
|
struct dirent* Dent = readdir(D);
|
||||||
|
if (Dent == NULL) {
|
||||||
|
if (errno == 0)
|
||||||
|
break;
|
||||||
|
een9_THROW_on_errno("dirent");
|
||||||
|
}
|
||||||
|
std::string entry = Dent->d_name;
|
||||||
|
if (entry == "." || entry == "..")
|
||||||
|
continue;
|
||||||
|
std::string filename = lang_dir + "/" + entry;
|
||||||
|
struct stat info;
|
||||||
|
int ret = stat(filename.c_str(), &info);
|
||||||
|
een9_ASSERT_on_iret(ret, "stat(" + filename + ")");
|
||||||
|
if (!S_ISREG(info.st_mode))
|
||||||
|
continue;
|
||||||
|
const std::string postfix = ".lang.json";
|
||||||
|
if (!een9::endsWith(entry, postfix))
|
||||||
|
continue;
|
||||||
|
std::string lang_antirange = entry.substr(0, entry.size() - postfix.size());
|
||||||
|
if (!isInWhitelist(lang_antirange, whitelist))
|
||||||
|
continue;
|
||||||
|
std::string content;
|
||||||
|
een9::readFile(filename, content);
|
||||||
|
|
||||||
|
result.emplace_back();
|
||||||
|
result.back().languagerange = languageRangeSimpler(lang_antirange);
|
||||||
|
result.back().content = json::parse_str_flawless(content);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Localizator::Localizator(const LocalizatorSettings &settings) : settings(settings) {
|
||||||
|
/* First - length of the longest prefix that was found so far (in force_order)
|
||||||
|
* Second - index in force_order that was assigned to this thingy
|
||||||
|
*/
|
||||||
|
|
||||||
|
files = collect_lang_dir_content(settings.lang_dir, settings.whitelist);
|
||||||
|
size_t n = files.size();
|
||||||
|
#define redundantFileMsg "Redundant localization file"
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
for (size_t j = i + 1; j < n; j++) {
|
||||||
|
std::string A = files[i].languagerange;
|
||||||
|
std::string B = files[j].languagerange;
|
||||||
|
for (std::string& pa: languageRangeGetPrefixes(A))
|
||||||
|
if (pa == B)
|
||||||
|
een9_THROW(redundantFileMsg);
|
||||||
|
for (std::string& pb: languageRangeGetPrefixes(B))
|
||||||
|
if (pb == A)
|
||||||
|
een9_THROW(redundantFileMsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::map<std::string, std::vector<std::size_t>> pref_to_files;
|
||||||
|
for (size_t k = 0; k < n; k++) {
|
||||||
|
for (const std::string& prefix: languageRangeGetPrefixes(files[k].languagerange)) {
|
||||||
|
pref_to_files[prefix].push_back(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<std::pair<size_t, size_t>> assignment;
|
||||||
|
constexpr size_t inf_bad_order = 999999999;
|
||||||
|
assignment.assign(n, {0, inf_bad_order});
|
||||||
|
if (settings.force_order.size() >= inf_bad_order - 2)
|
||||||
|
een9_THROW("o_O");
|
||||||
|
for (ssize_t i = 0; i < settings.force_order.size(); i++) {
|
||||||
|
const std::string& ip = settings.force_order[i];
|
||||||
|
if (pref_to_files.count(ip) != 1)
|
||||||
|
een9_THROW("force-order list contains entries that match no files (" + ip + ")");
|
||||||
|
for (size_t k: pref_to_files.at(ip)) {
|
||||||
|
if (assignment[k].first <= ip.size()) {
|
||||||
|
assignment[k].first = ip.size();
|
||||||
|
assignment[k].second = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto& p: pref_to_files) {
|
||||||
|
const std::vector<size_t>& candidates = p.second;
|
||||||
|
assert(!candidates.empty());
|
||||||
|
size_t bestSoFar = candidates[0];
|
||||||
|
size_t f = inf_bad_order;
|
||||||
|
for (size_t k: candidates) {
|
||||||
|
if (assignment[k].second <= f) {
|
||||||
|
f = assignment[k].second;
|
||||||
|
bestSoFar = k;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prefix_to_file[p.first] = bestSoFar;
|
||||||
|
}
|
||||||
|
if (prefix_to_file.count("") != 1)
|
||||||
|
een9_THROW("No locales were provided");
|
||||||
|
// todo: remove DEBUG
|
||||||
|
// for (size_t k = 0; k < n; k++) {
|
||||||
|
// printf("%s has priority %lu\n", files[k].languagerange.c_str(), assignment[k].second);
|
||||||
|
// }
|
||||||
|
// printf("==============\n");
|
||||||
|
// for (const auto& p : prefix_to_file) {
|
||||||
|
// printf("%s -> %s\n", p.first.c_str(), files[p.second].languagerange.c_str());
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
const LanguageFile& Localizator::get_right_locale(const std::vector<std::string> &preferred_langs) {
|
||||||
|
for (const std::string& lr: preferred_langs) {
|
||||||
|
if (prefix_to_file.count(lr) == 1)
|
||||||
|
return files[prefix_to_file.at(lr)];
|
||||||
|
}
|
||||||
|
return files[prefix_to_file.at("")];
|
||||||
|
}
|
||||||
|
}
|
36
src/web_chat/iu9_ca_web_chat_lib/localizator.h
Normal file
36
src/web_chat/iu9_ca_web_chat_lib/localizator.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef IU9_CA_WEB_CHAT_LIB_LOCALIZATOR_H
|
||||||
|
#define IU9_CA_WEB_CHAT_LIB_LOCALIZATOR_H
|
||||||
|
|
||||||
|
#include <jsonincpp/jsonobj.h>
|
||||||
|
|
||||||
|
namespace iu9cawebchat {
|
||||||
|
/* '*' -> ''; X -> X */
|
||||||
|
std::string languageRangeSimpler(const std::string& a);
|
||||||
|
|
||||||
|
struct LocalizatorSettings {
|
||||||
|
std::string lang_dir;
|
||||||
|
std::vector<std::string> whitelist;
|
||||||
|
std::vector<std::string> force_order;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* There is no need to put http Content-Language response value into json file. When is is in the name */
|
||||||
|
struct LanguageFile {
|
||||||
|
std::string languagerange;
|
||||||
|
json::JSON content;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Localizator uses libjsonincpp internally, and thus can't be read by two treads simultaneously */
|
||||||
|
struct Localizator {
|
||||||
|
LocalizatorSettings settings;
|
||||||
|
std::vector<LanguageFile> files;
|
||||||
|
std::map<std::string, size_t> prefix_to_file;
|
||||||
|
|
||||||
|
/* Throws std::exception if something goes wrong */
|
||||||
|
explicit Localizator(const LocalizatorSettings& settings);
|
||||||
|
|
||||||
|
/* Returns a reference to object inside Localizator */
|
||||||
|
const LanguageFile& get_right_locale(const std::vector<std::string>& preferred_langs);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -5,6 +5,7 @@
|
|||||||
#include <engine_engine_number_9/connecting_assets/static_asset_manager.h>
|
#include <engine_engine_number_9/connecting_assets/static_asset_manager.h>
|
||||||
#include "find_db.h"
|
#include "find_db.h"
|
||||||
#include <engine_engine_number_9/running_mainloop.h>
|
#include <engine_engine_number_9/running_mainloop.h>
|
||||||
|
#include <engine_engine_number_9/http_structures/accept_language.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include "str_fields.h"
|
#include "str_fields.h"
|
||||||
#include "backend_logic/client_server_interact.h"
|
#include "backend_logic/client_server_interact.h"
|
||||||
@ -32,11 +33,23 @@ namespace iu9cawebchat {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
LocalizatorSettings make_localizator_settings(const std::string& assets_dir, const json::JSON& config) {
|
||||||
|
std::vector<std::string> whitelist;
|
||||||
|
for (const json::JSON& entry: config["lang"]["whitelist"].asArray())
|
||||||
|
whitelist.push_back(languageRangeSimpler(entry.asString()));
|
||||||
|
std::vector<std::string> force_order;
|
||||||
|
for (const json::JSON& entry: config["lang"]["force-order"].asArray())
|
||||||
|
force_order.push_back(languageRangeSimpler(entry.asString()));
|
||||||
|
return LocalizatorSettings{assets_dir + "/lang", whitelist, force_order};
|
||||||
|
}
|
||||||
|
|
||||||
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"].isString(), "config[\"assets\"] is not string");
|
||||||
const std::string& assets_dir = config["assets"].asString();
|
const std::string& assets_dir = config["assets"].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");
|
||||||
|
|
||||||
|
LocalizatorSettings localizator_settings = make_localizator_settings(assets_dir, config);
|
||||||
|
|
||||||
een9::StaticAssetManagerSlaveModule samI;
|
een9::StaticAssetManagerSlaveModule samI;
|
||||||
samI.update({
|
samI.update({
|
||||||
een9::StaticAssetManagerRule{assets_dir + "/css", "/assets/css", {{".css", "text/css"}} },
|
een9::StaticAssetManagerRule{assets_dir + "/css", "/assets/css", {{".css", "text/css"}} },
|
||||||
@ -47,7 +60,6 @@ namespace iu9cawebchat {
|
|||||||
} },
|
} },
|
||||||
});
|
});
|
||||||
|
|
||||||
const json::JSON& config_presentation = config["presentation"];
|
|
||||||
int64_t slave_number = config["server"]["workers"].asInteger().get_int();
|
int64_t slave_number = config["server"]["workers"].asInteger().get_int();
|
||||||
een9_ASSERT(slave_number > 0 && slave_number <= 200, "E");
|
een9_ASSERT(slave_number > 0 && slave_number <= 200, "E");
|
||||||
|
|
||||||
@ -61,15 +73,24 @@ namespace iu9cawebchat {
|
|||||||
nytl::TemplaterSettings{nytl::TemplaterDetourRules{assets_dir + "/HypertextPages"}});
|
nytl::TemplaterSettings{nytl::TemplaterDetourRules{assets_dir + "/HypertextPages"}});
|
||||||
worker_guest_data[i].templater->update();
|
worker_guest_data[i].templater->update();
|
||||||
worker_guest_data[i].db = std::make_unique<SqliteConnection>(sqlite_db_path);
|
worker_guest_data[i].db = std::make_unique<SqliteConnection>(sqlite_db_path);
|
||||||
|
worker_guest_data[i].locales = std::make_unique<Localizator>(localizator_settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
een9::MainloopParameters params;
|
een9::MainloopParameters params;
|
||||||
params.guest_core = [&samI, &worker_guest_data, config_presentation]
|
params.guest_core = [&samI, &worker_guest_data]
|
||||||
(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];
|
||||||
een9::StaticAsset sa;
|
een9::StaticAsset sa;
|
||||||
ONE_SQLITE_TRANSACTION_GUARD conn_guard(*wgd.db);
|
ONE_SQLITE_TRANSACTION_GUARD conn_guard(*wgd.db);
|
||||||
|
std::string AcceptLanguage;
|
||||||
|
for (const std::pair<std::string, std::string>& p: req.headers) {
|
||||||
|
if (p.first == "Accept-Language")
|
||||||
|
AcceptLanguage = p.second;
|
||||||
|
}
|
||||||
|
std::vector<std::string> AcceptLanguageB = een9::parse_header_Accept_Language(AcceptLanguage);
|
||||||
|
const LanguageFile& locale = wgd.locales->get_right_locale(AcceptLanguageB);
|
||||||
|
const json::JSON& pres = locale.content;
|
||||||
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;
|
||||||
@ -78,17 +99,17 @@ namespace iu9cawebchat {
|
|||||||
initial_extraction_of_all_the_useful_info_from_cookies(*wgd.db, req, cookies, login_cookies, userinfo, logged_in_user);
|
initial_extraction_of_all_the_useful_info_from_cookies(*wgd.db, req, cookies, login_cookies, userinfo, logged_in_user);
|
||||||
|
|
||||||
if (req.uri_path == "/" || req.uri_path == "/list-rooms") {
|
if (req.uri_path == "/" || req.uri_path == "/list-rooms") {
|
||||||
return when_page_list_rooms(wgd, config_presentation, req, userinfo);
|
return when_page_list_rooms(wgd, pres, req, userinfo);
|
||||||
}
|
}
|
||||||
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, pres, req, login_cookies, userinfo);
|
||||||
}
|
}
|
||||||
// todo: split
|
// todo: split
|
||||||
if (een9::beginsWith(req.uri_path, "/chat/") || een9::beginsWith(req.uri_path, "/chat-members/")) {
|
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, pres, req, userinfo);
|
||||||
}
|
}
|
||||||
if (een9::beginsWith(req.uri_path, "/user/")) {
|
if (een9::beginsWith(req.uri_path, "/user/")) {
|
||||||
return when_page_user(wgd, config_presentation, req, login_cookies, userinfo);
|
return when_page_user(wgd, pres, req, login_cookies, userinfo);
|
||||||
}
|
}
|
||||||
if (req.uri_path == "/api/chatPollEvents") {
|
if (req.uri_path == "/api/chatPollEvents") {
|
||||||
return when_internalapi_chatpollevents(wgd, req, logged_in_user);
|
return when_internalapi_chatpollevents(wgd, req, logged_in_user);
|
||||||
|
Loading…
Reference in New Issue
Block a user