master #6
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -16,3 +16,4 @@ iu9-ca-web-chat.db | |||||||
| log/ | log/ | ||||||
| core | core | ||||||
| 
 | 
 | ||||||
|  | config/example.json | ||||||
							
								
								
									
										29
									
								
								assets/HypertextPages/chat-members.nytl.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								assets/HypertextPages/chat-members.nytl.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | {% ELDEF main JSON pres JSON userinfo JSON openedchat JSON initial_chatUpdResp %} | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="ru"> | ||||||
|  | <head> | ||||||
|  |     <meta charset="UTF-8"> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
|  |     <title>Веб-Чат Members</title> | ||||||
|  |     <link rel="stylesheet" href="/assets/css/chat.css"> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  | {% PUT chat.pass pres userinfo openedchat initial_chatUpdResp %} | ||||||
|  | <!--TODO: ADD SOMETHING WRITE SOMETHING AAAAAA --> | ||||||
|  | <div class="overlay" id="overlay"> | ||||||
|  |     <div class="members-list" id="members-list"> | ||||||
|  |         <div class="members-list-header"> | ||||||
|  |             <span class="close" onclick="closeMembersList()">×</span> | ||||||
|  |             <h2 class="all-members">Все участники</h2> | ||||||
|  |         </div> | ||||||
|  |         <div class="members-list-body"> | ||||||
|  |             <ul id="members-list-body"> | ||||||
|  |                 <!-- Список участников будет добавлен динамически --> | ||||||
|  |             </ul> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | </div> | ||||||
|  | <script src="/assets/js/chat-members.js"></script> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
|  | {% ENDELDEF %} | ||||||
| @ -1,4 +1,13 @@ | |||||||
| {% ELDEF main JSON pres JSON userinfo %} | {% ELDEF pass JSON pres JSON userinfo JSON openedchat JSON initial_chatUpdResp %} | ||||||
|  | <script> | ||||||
|  |     let pres = {% PUT jsinsert pres %}; | ||||||
|  |     let userinfo = {% PUT jsinsert userinfo %}; | ||||||
|  |     let openedchat = {% PUT jsinsert openedchat %}; | ||||||
|  |     let initial_chatUpdResp = {% PUT jsinsert initial_chatUpdResp %}; | ||||||
|  | </script> | ||||||
|  | {% ENDELDEF %} | ||||||
|  | 
 | ||||||
|  | {% ELDEF main JSON pres JSON userinfo JSON openedchat JSON initial_chatUpdResp %} | ||||||
| <!DOCTYPE html> | <!DOCTYPE html> | ||||||
| <html lang="ru"> | <html lang="ru"> | ||||||
| <head> | <head> | ||||||
| @ -8,6 +17,8 @@ | |||||||
|     <link rel="stylesheet" href="/assets/css/chat.css"> |     <link rel="stylesheet" href="/assets/css/chat.css"> | ||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
|  | {% PUT chat.pass pres userinfo openedchat initial_chatUpdResp %} | ||||||
|  | <!-- TODO AAAAA--> | ||||||
| <div class="chat-container"> | <div class="chat-container"> | ||||||
|     <div class="chat-header"> |     <div class="chat-header"> | ||||||
|         <span class="room-name">Веб чат</span> |         <span class="room-name">Веб чат</span> | ||||||
| @ -21,19 +32,6 @@ | |||||||
|         <button class="chat-send-button" onclick="sendMessage()">Отправить</button> |         <button class="chat-send-button" onclick="sendMessage()">Отправить</button> | ||||||
|     </div> |     </div> | ||||||
| </div> | </div> | ||||||
| <div class="overlay" id="overlay"> |  | ||||||
|     <div class="members-list" id="members-list"> |  | ||||||
|         <div class="members-list-header"> |  | ||||||
|             <span class="close" onclick="closeMembersList()">×</span> |  | ||||||
|             <h2 class="all-members">Все участники</h2> |  | ||||||
|         </div> |  | ||||||
|         <div class="members-list-body"> |  | ||||||
|             <ul id="members-list-body"> |  | ||||||
|                 <!-- Список участников будет добавлен динамически --> |  | ||||||
|             </ul> |  | ||||||
|         </div> |  | ||||||
|     </div> |  | ||||||
| </div> |  | ||||||
| <script src="/assets/js/chat.js"></script> | <script src="/assets/js/chat.js"></script> | ||||||
| </body> | </body> | ||||||
| </html> | </html> | ||||||
|  | |||||||
							
								
								
									
										42
									
								
								assets/HypertextPages/edit-profile.nytl.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								assets/HypertextPages/edit-profile.nytl.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | |||||||
|  | {% ELDEF main JSON pres JSON userprofile JSON errors %} | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  | <head> | ||||||
|  |     <meta charset="UTF-8"> | ||||||
|  |     <link rel="stylesheet" href="/assets/css/profile.css"> | ||||||
|  |     <title>Профиль</title> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  |     <div class="main-container"> | ||||||
|  |         {% FOR error IN errors %} | ||||||
|  |             <div> | ||||||
|  |                 <p>{% WRITE error.text %}</p> | ||||||
|  |             </div> | ||||||
|  |         {% ENDFOR %} | ||||||
|  |         <div class="profile-header"> | ||||||
|  |             <h1>Редактирование профиля</h1> | ||||||
|  |         </div> | ||||||
|  |         <form method="post" action="/user/{% WRITE userprofile.nickname %}"> | ||||||
|  |             <div class="columns"> | ||||||
|  |                 <div class="column"> | ||||||
|  |                     <p>{% WRITE userprofile.name %} ( {% WRITE userprofile.nickname %} )</p> | ||||||
|  |                     <label for="name"> Изменить имя </label> | ||||||
|  |                     <input type="text" name="name" id="name"> | ||||||
|  |                     <br> | ||||||
|  |                     <label for="password"> Изменить пароль </label> | ||||||
|  |                     <input type="password" name="password" id="password"> | ||||||
|  |                     <br> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="additional-info"> | ||||||
|  |                 <p>О себе</p> | ||||||
|  |                 <p>{% WRITE userprofile.bio %}</p><br> | ||||||
|  |                 <label for="bio">Изменить</label> | ||||||
|  |                 <textarea name="bio" id="bio"></textarea> | ||||||
|  |             </div> | ||||||
|  |             <button class="save" type="submit">Сохранить изменения</button> | ||||||
|  |         </form> | ||||||
|  |     </div> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
|  | {% ENDELDEF%} | ||||||
							
								
								
									
										10
									
								
								assets/HypertextPages/err-404.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								assets/HypertextPages/err-404.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  | <head> | ||||||
|  |     <meta charset="UTF-8"> | ||||||
|  |     <title>Not found</title> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  |     <h1>Page not found</h1> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
| @ -1,4 +1,4 @@ | |||||||
| {% ELDEF main JSON pres JSON userinfo %} | {% ELDEF main JSON pres JSON userinfo JSON initial_chatListUpdResp %} | ||||||
| <!DOCTYPE html> | <!DOCTYPE html> | ||||||
| <html lang="{% WRITE pres.lang %}"> | <html lang="{% WRITE pres.lang %}"> | ||||||
| <head> | <head> | ||||||
| @ -8,7 +8,11 @@ | |||||||
|     <link rel="stylesheet" href="/assets/css/list-rooms.css"> |     <link rel="stylesheet" href="/assets/css/list-rooms.css"> | ||||||
| </head> | </head> | ||||||
| <body> | <body> | ||||||
| {% PUT pass-pres-userinfo pres userinfo %} | <script> | ||||||
|  |     let pres = {% PUT jsinsert pres %}; | ||||||
|  |     let userinfo = {% PUT jsinsert userinfo %}; | ||||||
|  |     let initial_chatListUpdResp = {% PUT jsinsert initial_chatListUpdResp %}; | ||||||
|  | </script> | ||||||
| <div class="container"> | <div class="container"> | ||||||
|     <h1 style="color: white;">Выберите Чат-Комнату</h1> |     <h1 style="color: white;">Выберите Чат-Комнату</h1> | ||||||
|     <ul class="room-list"> |     <ul class="room-list"> | ||||||
|  | |||||||
| @ -9,10 +9,9 @@ | |||||||
| </head> | </head> | ||||||
| 
 | 
 | ||||||
| <body> | <body> | ||||||
| {% PUT pass-pres-userinfo pres userinfo %} |  | ||||||
| {% FOR msg IN errors %} | {% FOR msg IN errors %} | ||||||
|     <div class="error-msg"> |     <div class="error-msg"> | ||||||
|         {% WRITE msg %} |         {% WRITE msg.text %} | ||||||
|     </div> |     </div> | ||||||
| {% ENDFOR %} | {% ENDFOR %} | ||||||
| <div class="form-container"> | <div class="form-container"> | ||||||
|  | |||||||
| @ -1,6 +0,0 @@ | |||||||
| {% ELDEF main JSON pres JSON userinfo %} |  | ||||||
| <script> |  | ||||||
|     let pres = {% PUT jsinsert pres %}; |  | ||||||
|     let userinfo = {% PUT jsinsert userinfo %}; |  | ||||||
| </script> |  | ||||||
| {% ENDELDEF %} |  | ||||||
| @ -1,39 +0,0 @@ | |||||||
| {% ELDEF main JSON pres JSON userinfo %} |  | ||||||
| <!DOCTYPE html> |  | ||||||
| <html lang="en"> |  | ||||||
| <head> |  | ||||||
|     <meta charset="UTF-8"> |  | ||||||
|     <link rel="stylesheet" href="/assets/css/profile.css"> |  | ||||||
|     <title>Профиль</title> |  | ||||||
| </head> |  | ||||||
| <body> |  | ||||||
| <div class="main-container"> |  | ||||||
|     <div class="profile-header"> |  | ||||||
|         <h1 style="color: white; text-align: center;">Профиль пользователя</h1> |  | ||||||
|         <a class="return" href="chat.nytl.html">Назад</a> |  | ||||||
|     </div> |  | ||||||
|     <form> |  | ||||||
|         <div class="columns"> |  | ||||||
|             <div class="column"> |  | ||||||
|                 <img class="avatar" src="/assets/img/empty_avatar.png" id="avatar" height="200" width="200"><br> |  | ||||||
|                 <input type="file" id="fileInput" style="display:none"> |  | ||||||
|                 <button class="add" type="button" onclick="document.getElementById('fileInput').click();">Изменить фото</button><br> |  | ||||||
|             </div> |  | ||||||
|             <div class="column"> |  | ||||||
|                 <input type="text" name="username" placeholder = "Имя пользователя" value="Some Name" id="username"><br> |  | ||||||
|                 <input type="text" name="login" placeholder="Логин" value="some_login123" id="login" readonly><br> |  | ||||||
|             </div> |  | ||||||
|         </div> |  | ||||||
|         <h3 style="color:#007bb5;">О себе</h3> |  | ||||||
|         <div class="additional-info"> |  | ||||||
|             <textarea name="bio" placeholder="Напишите о себе..." id="bio"></textarea> |  | ||||||
|         </div> |  | ||||||
|         <button class="save" type="submit">Сохранить изменения</button> |  | ||||||
|     </form> |  | ||||||
| </div> |  | ||||||
| 
 |  | ||||||
| <script src="/assets/js/list-rooms.js"> </script> |  | ||||||
| 
 |  | ||||||
| </body> |  | ||||||
| </html> |  | ||||||
| {% ENDELDEF%} |  | ||||||
| @ -1,27 +0,0 @@ | |||||||
| {% ELDEF main JSON pres JSON userinfo %} |  | ||||||
| <!DOCTYPE html> |  | ||||||
| <html lang="ru"> |  | ||||||
| <head> |  | ||||||
|     <meta charset="UTF-8"> |  | ||||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> |  | ||||||
|     <title>Страница Регистрации</title> |  | ||||||
|     <link rel="stylesheet" href="/assets/css/registration.css"> |  | ||||||
| 
 |  | ||||||
| </head> |  | ||||||
| 
 |  | ||||||
| <body> |  | ||||||
| <div class="form-container"> |  | ||||||
|     <h1 class="hide-cursor no-select">Вход</h1> |  | ||||||
|     <form action="assets/html/list-rooms.html" method="post"> |  | ||||||
|         <input type="text" name="username" placeholder="Имя пользователя" id="username"><br> |  | ||||||
|         <input type="text" name="login" placeholder="Логин" id="login"><br> |  | ||||||
|         <input type="password" name="password" placeholder="Пароль" id="password"><br> |  | ||||||
|         <button type="submit" class="hide-cursor no-select">Зарегистрироваться</button> |  | ||||||
|         <div id="error"></div> |  | ||||||
|     </form> |  | ||||||
| </div> |  | ||||||
| 
 |  | ||||||
| <script src="assets/js/registration.js"></script> |  | ||||||
| </body> |  | ||||||
| </html> |  | ||||||
| {% ENDELDEF %} |  | ||||||
							
								
								
									
										26
									
								
								assets/HypertextPages/view-profile.nytl.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								assets/HypertextPages/view-profile.nytl.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | {% ELDEF main JSON pres JSON userprofile %} | ||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="en"> | ||||||
|  | <head> | ||||||
|  |     <meta charset="UTF-8"> | ||||||
|  |     <link rel="stylesheet" href="/assets/css/profile.css"> | ||||||
|  |     <title>Профиль</title> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  |     <div class="main-container"> | ||||||
|  |         <div class="profile-header"> | ||||||
|  |             <h1>Профиль пользователя</h1> | ||||||
|  |         </div> | ||||||
|  |         <div class="columns"> | ||||||
|  |             <div class="column"> | ||||||
|  |                 <p>{% WRITE userprofile.name %} ( {% WRITE userprofile.nickname %} )</p> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |         <div class="additional-info"> | ||||||
|  |             <p>О себе</p> | ||||||
|  |             <p>{% WRITE userprofile.bio %}</p><br> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
|  | {% ENDELDEF%} | ||||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 81 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/img/logo0.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/img/logo0.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 9.7 KiB | 
							
								
								
									
										0
									
								
								assets/js/chat-members.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								assets/js/chat-members.js
									
									
									
									
									
										Normal file
									
								
							| @ -1,10 +0,0 @@ | |||||||
| document.getElementById('fileInput').addEventListener('change', function(event) { |  | ||||||
|     const file = event.target.files[0]; |  | ||||||
|     if (file) { |  | ||||||
|         const reader = new FileReader(); |  | ||||||
|         reader.onload = function(e) { |  | ||||||
|             document.getElementById('avatar').src = e.target.result; |  | ||||||
|         }; |  | ||||||
|         reader.readAsDataURL(file); |  | ||||||
|     } |  | ||||||
| }); |  | ||||||
| @ -149,23 +149,8 @@ struct CAWebChat { | |||||||
|                 "login_cookie.cpp", |                 "login_cookie.cpp", | ||||||
|                 "backend_logic/server_data_interact.cpp", |                 "backend_logic/server_data_interact.cpp", | ||||||
|                 "backend_logic/client_server_interact.cpp", |                 "backend_logic/client_server_interact.cpp", | ||||||
|                 "backend_logic/when_list_rooms.cpp", | 
 | ||||||
|                 "backend_logic/when_login.cpp", |                 "backend_logic/polling.cpp", | ||||||
|                 "backend_logic/when_chat.cpp", |  | ||||||
|                 "backend_logic/when_user.cpp", |  | ||||||
|                 "backend_logic/api_pollevents.cpp", |  | ||||||
|                 "backend_logic/api_getchatlist.cpp", |  | ||||||
|                 "backend_logic/api_getchatinfo.cpp", |  | ||||||
|                 "backend_logic/api_getchatmemberlist.cpp", |  | ||||||
|                 "backend_logic/api_getuserinfo.cpp", |  | ||||||
|                 "backend_logic/api_getmessageinfo.cpp", |  | ||||||
|                 "backend_logic/api_getmessageneighbours.cpp", |  | ||||||
|                 "backend_logic/api_sendmessage.cpp", |  | ||||||
|                 "backend_logic/api_deletemessage.cpp", |  | ||||||
|                 "backend_logic/api_addmembertochat.cpp", |  | ||||||
|                 "backend_logic/api_removememberfromchat.cpp", |  | ||||||
|                 "backend_logic/api_createchat.cpp", |  | ||||||
|                 "backend_logic/api_removechat.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; | ||||||
|  | |||||||
							
								
								
									
										0
									
								
								config/default.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								config/default.json
									
									
									
									
									
										Normal file
									
								
							| @ -14,7 +14,9 @@ | |||||||
|         "name-of-room": "Название комнаты", |         "name-of-room": "Название комнаты", | ||||||
|         "create-room": "Создать комнату", |         "create-room": "Создать комнату", | ||||||
| 
 | 
 | ||||||
|         "incorrect-nickname-or-password": "Неправильный никнейм или пароль" |         "incorrect-nickname-or-password": "Неправильный никнейм или пароль", | ||||||
|  | 
 | ||||||
|  |         "incorrect-profile-data": "Неправильно заполнены поля профиля" | ||||||
|       }, |       }, | ||||||
|       "ask" : { |       "ask" : { | ||||||
|         "select-chat-room": "Выберете чат комнату" |         "select-chat-room": "Выберете чат комнату" | ||||||
| @ -29,7 +31,7 @@ | |||||||
|   }, |   }, | ||||||
|   "assets": "./assets", |   "assets": "./assets", | ||||||
|   "database": { |   "database": { | ||||||
|     "type": "sqlite", |     "type": "sqlite3", | ||||||
|     "file": "./iu9-ca-web-chat.db" |     "file": "./iu9-ca-web-chat.db" | ||||||
|   }, |   }, | ||||||
|   "limits": { |   "limits": { | ||||||
|  | |||||||
| @ -258,8 +258,8 @@ namespace een9 { | |||||||
|                 } |                 } | ||||||
|                 errno = 0; |                 errno = 0; | ||||||
|                 ret = poll(pollfds.data(), Nip, params.mainloop_recheck_interval_us); |                 ret = poll(pollfds.data(), Nip, params.mainloop_recheck_interval_us); | ||||||
|                 if (ret != 0) { |                 if (ret != 0 && errno != 0) { | ||||||
|                     printf("poll() error :> %d\n", errno); |                     printf("poll() error :> %s\n", een9::prettyprint_errno("").c_str()); | ||||||
|                 } |                 } | ||||||
|                 ASSERT_on_iret(ret, "poll()"); |                 ASSERT_on_iret(ret, "poll()"); | ||||||
|                 for (size_t i = 0; i < Nip; i++) { |                 for (size_t i = 0; i < Nip; i++) { | ||||||
|  | |||||||
| @ -18,7 +18,14 @@ int main(int argc, char** argv) { | |||||||
|     een9::readFile(config_file, config_text); |     een9::readFile(config_file, config_text); | ||||||
|     const json::JSON config = json::parse_str_flawless(config_text); |     const json::JSON config = json::parse_str_flawless(config_text); | ||||||
| 
 | 
 | ||||||
|     std::string answer2 = templater.render("login", {&config["presentation"].g()}); |     json::JSON userprofile; | ||||||
|  |     userprofile["uid"].asInteger() = json::Integer(0l); | ||||||
|  |     userprofile["name"].asString() = "radasdasdasdadsdasd"; | ||||||
|  |     userprofile["nickname"].asString() = "root"; | ||||||
|  |     userprofile["bio"].asString() = "Your mother"; | ||||||
|  |     json::JSON errors; | ||||||
|  |     errors = json::JSON(json::array); | ||||||
|  |     std::string answer2 = templater.render("edit-profile", {&config["presentation"], &userprofile, &errors}); | ||||||
|     printf("%s\n<a><f><t><e><r><><l><f>\n", answer2.c_str()); |     printf("%s\n<a><f><t><e><r><><l><f>\n", answer2.c_str()); | ||||||
| 
 | 
 | ||||||
|     return 0; |     return 0; | ||||||
|  | |||||||
| @ -178,7 +178,7 @@ namespace nytl { | |||||||
|                 } |                 } | ||||||
|                 ASSERT(nm == "EL", "Type of argument variable is either JSON or EL(...signature)") |                 ASSERT(nm == "EL", "Type of argument variable is either JSON or EL(...signature)") | ||||||
|                 skip(ctx, '('); |                 skip(ctx, '('); | ||||||
|                 result = json::JSON(json::array); |                 result.asArray(); | ||||||
|                 assert(result.isArray()); |                 assert(result.isArray()); | ||||||
|             } |             } | ||||||
|             skipWhitespace(ctx); |             skipWhitespace(ctx); | ||||||
| @ -220,16 +220,16 @@ namespace nytl { | |||||||
|                 ASSERT(!first.empty(), "Expression should start with 'root' name of global package or local variable"); |                 ASSERT(!first.empty(), "Expression should start with 'root' name of global package or local variable"); | ||||||
|                 ASSERT(first != "_", "_ ??? ARE YOU KIDDING???"); |                 ASSERT(first != "_", "_ ??? ARE YOU KIDDING???"); | ||||||
|                 if (local_var_names.count(first) == 1) { |                 if (local_var_names.count(first) == 1) { | ||||||
|                     result["V"] = json::JSON(json::Integer((int64_t)local_var_names.at(first))); |                     result["V"].asInteger() = json::Integer((int64_t)local_var_names.at(first)); | ||||||
|                 } else { |                 } else { | ||||||
|                     result["V"] = json::JSON(first); |                     result["V"].asString() = first; | ||||||
|                 } |                 } | ||||||
|                 result["C"] = json::JSON(json::array); |                 result["C"].asArray(); | ||||||
|             } else { |             } else { | ||||||
|                 skipWhitespace(ctx); |                 skipWhitespace(ctx); | ||||||
|                 skip(ctx, ']'); |                 skip(ctx, ']'); | ||||||
|             } |             } | ||||||
|             std::vector<json::JSON>& chain = result["C"].g().asArray(); |             std::vector<json::JSON>& chain = result["C"].asArray(); | ||||||
|             while (true) { |             while (true) { | ||||||
|                 if (peep(ctx) == '.') { |                 if (peep(ctx) == '.') { | ||||||
|                     skip(ctx, '.'); |                     skip(ctx, '.'); | ||||||
|  | |||||||
| @ -56,18 +56,18 @@ namespace nytl { | |||||||
|                 descend(*(temp_ret.JSON_subval)); |                 descend(*(temp_ret.JSON_subval)); | ||||||
|             } else { |             } else { | ||||||
|                 assert(expr.isDictionary()); |                 assert(expr.isDictionary()); | ||||||
|                 const json::JSON& val = expr["V"].g(); |                 const json::JSON& val = expr["V"]; | ||||||
|                 if (val.isInteger()) { |                 if (val.isInteger()) { | ||||||
|                     size_t lv_ind = val.asInteger().get_int(); |                     size_t lv_ind = val.asInteger().get_int(); | ||||||
|                     assert(lv_ind < local_vars.size()); |                     assert(lv_ind < local_vars.size()); | ||||||
|                     result = local_vars[lv_ind]; |                     result = local_vars[lv_ind]; | ||||||
|                 } else if (val.isString()) { |                 } else if (val.isString()) { | ||||||
|                     std::string cur_el_name_str = expr["V"].g().asString(); |                     std::string cur_el_name_str = expr["V"].asString(); | ||||||
|                     result = LocalVarValue{false, cur_el_name_str, NULL}; |                     result = LocalVarValue{false, cur_el_name_str, NULL}; | ||||||
|                 } else |                 } else | ||||||
|                     assert(false); |                     assert(false); | ||||||
|             } |             } | ||||||
|             const std::vector<json::JSON>& chain = expr["C"].g().asArray(); |             const std::vector<json::JSON>& chain = expr["C"].asArray(); | ||||||
|             while (true) { |             while (true) { | ||||||
|                 if (chain_el >= chain.size()) |                 if (chain_el >= chain.size()) | ||||||
|                     return NULL; |                     return NULL; | ||||||
| @ -211,7 +211,8 @@ namespace nytl { | |||||||
|     uptr<RFrame> RFrame_OverParts::toMe(bool returned, const global_elem_set_t &elem_ns, Ditch &result, |     uptr<RFrame> RFrame_OverParts::toMe(bool returned, const global_elem_set_t &elem_ns, Ditch &result, | ||||||
|         const std::function<std::string(std::string)> &escape) { |         const std::function<std::string(std::string)> &escape) { | ||||||
|         if (!returned) |         if (!returned) | ||||||
|             ASSERT(elem_ns.count(name) == 1, "No such element"); |             if (elem_ns.count(name) != 1) | ||||||
|  |                 THROW("No such element"); | ||||||
|         const Element& el = elem_ns.at(name); |         const Element& el = elem_ns.at(name); | ||||||
|         if (!returned) { |         if (!returned) { | ||||||
|             /* Continue to do checks */ |             /* Continue to do checks */ | ||||||
|  | |||||||
| @ -0,0 +1,30 @@ | |||||||
|  | #include "server_data_interact.h" | ||||||
|  | #include <engine_engine_number_9/baza_throw.h> | ||||||
|  | #include <engine_engine_number_9/baza.h> | ||||||
|  | #include "../str_fields.h" | ||||||
|  | 
 | ||||||
|  | namespace iu9cawebchat { | ||||||
|  |     std::string admin_control_procedure(SqliteConnection& conn, const std::string& req, bool& termination) { | ||||||
|  |         if (req == "hello") { | ||||||
|  |             return ":0 omg! hiii!! Hewwou :3 !!!!\n"; | ||||||
|  |         } | ||||||
|  |         if (req == "8") { | ||||||
|  |             termination = true; | ||||||
|  |             return "Bye\n"; | ||||||
|  |         } | ||||||
|  |         std::string updaterootpw_pref = "updaterootpw"; | ||||||
|  |         if (een9::beginsWith(req, "updaterootpw")) { | ||||||
|  |             size_t nid = updaterootpw_pref.size(); | ||||||
|  |             if (nid >= req.size() || !isSPACE(req[nid])) | ||||||
|  |                 return "Bad command syntax. Missing whitespace\n"; | ||||||
|  |             std::string new_password = req.substr(nid + 1); | ||||||
|  |             if (!check_password(new_password)) | ||||||
|  |                 een9_THROW("Bad password"); | ||||||
|  |             sqlite_nooutput(conn, | ||||||
|  |                 "UPDATE `user` SET `password` = ?1 WHERE `id` = 0 ", | ||||||
|  |                 {}, {{1, new_password}}); | ||||||
|  |             return "Successul update\n"; | ||||||
|  |         } | ||||||
|  |         return "Incorrect command\n"; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,15 +1,48 @@ | |||||||
| #include "server_data_interact.h" | #include "server_data_interact.h" | ||||||
| #include <engine_engine_number_9/baza_throw.h> | #include <engine_engine_number_9/baza_throw.h> | ||||||
|  | #include <assert.h> | ||||||
| 
 | 
 | ||||||
| namespace iu9cawebchat { | namespace iu9cawebchat { | ||||||
|  |     void make_her_a_member_of_the_midnight_crew(SqliteConnection& conn, int64_t chatId, int64_t alienUserId, int64_t role) { | ||||||
|  |         assert(role != user_chat_role_deleted); | ||||||
|  |         int64_t chat_HistoryId_BEFORE_EV = get_current_history_id_of_chat(conn, chatId); | ||||||
|  |         int64_t alien_chatlist_HistoryId_BEFORE_EV = get_current_history_id_of_user_chatList(conn, alienUserId); | ||||||
|  |         sqlite_nooutput(conn, | ||||||
|  |             "INSERT INTO `user_chat_membership` (`userId`, `chatId`, `user_chatList_IncHistoryId`," | ||||||
|  |             "`chat_IncHistoryId`, `role`) VALUES (?1, ?2, ?3, ?4, ?5)", | ||||||
|  |             {{1, alienUserId}, {2, chatId}, {3, alien_chatlist_HistoryId_BEFORE_EV + 1}, | ||||||
|  |             {4, chat_HistoryId_BEFORE_EV + 1}, {5, role}}, {}); | ||||||
|  | 
 | ||||||
|  |         sqlite_nooutput(conn, | ||||||
|  |             "UPDATE `chat` SET `it_HistoryId` = ?1 WHERE `id` = ?2", {{1, chat_HistoryId_BEFORE_EV + 1}, | ||||||
|  |             {2, chatId}}, {}); | ||||||
|  | 
 | ||||||
|  |         sqlite_nooutput(conn, | ||||||
|  |             "UPDATE `user` SET `chatList_HistoryId` = ?1 WHERE `id` = ?2", | ||||||
|  |             {{1, alien_chatlist_HistoryId_BEFORE_EV + 1}, {2, alienUserId}}, {}); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     json::JSON internalapi_addMemberToChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { |     json::JSON internalapi_addMemberToChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { | ||||||
|         int64_t chatId = Sent["chatId"].g().asInteger().get_int(); |         int64_t chatId = Sent["chatUpdReq"]["chatId"].asInteger().get_int(); | ||||||
|         int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); |         int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); | ||||||
|         if (my_role_here == user_chat_role_deleted) |         if (my_role_here != user_chat_role_admin) | ||||||
|             een9_THROW("Unauthorized user tries to access internalapi_getChatInfo"); |             een9_THROW("Non-admin user tries to access internalapi_getChatInfo"); | ||||||
|  | 
 | ||||||
|  |         std::string alien_nickname = Sent["nickname"].asString(); | ||||||
|  |         RowUser_Content alien; | ||||||
|  |         try { | ||||||
|  |             alien = lookup_user_content_by_nickname(conn, alien_nickname); | ||||||
|  |         } catch (std::exception& e) { | ||||||
|  |             return json::JSON(json::jdict{{"status", json::JSON(-1l)}}); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         int64_t aliens_old_role = get_role_of_user_in_chat(conn, alien.id, chatId); | ||||||
|  |         if (aliens_old_role == user_chat_role_deleted) { | ||||||
|  |             make_her_a_member_of_the_midnight_crew(conn, chatId, alien.id, user_chat_role_regular); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         json::JSON Recv; |         json::JSON Recv; | ||||||
|         Recv["status"] = json::JSON(0l); |         poll_update_chat(conn, Sent, Recv); | ||||||
|         // todo: WRITE THIS MORBID THING
 |  | ||||||
|         return Recv; |         return Recv; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,11 +1,27 @@ | |||||||
| #include "server_data_interact.h" | #include "server_data_interact.h" | ||||||
| #include <engine_engine_number_9/baza_throw.h> | #include <engine_engine_number_9/baza_throw.h> | ||||||
|  | #include "../str_fields.h" | ||||||
| 
 | 
 | ||||||
| namespace iu9cawebchat { | namespace iu9cawebchat { | ||||||
|     json::JSON internalapi_createChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { |     json::JSON internalapi_createChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { | ||||||
|  |         std::string new_chat_name = Sent["content"]["name"].asString(); | ||||||
|  |         std::string new_chat_nickname = Sent["content"]["nickname"].asString(); | ||||||
|  |         if (!check_nickname(new_chat_nickname) || !check_name(new_chat_name)) | ||||||
|  |             return json::JSON(json::jdict{{"status", json::JSON(-1l)}}); | ||||||
|  |         if (is_nickname_taken(conn, new_chat_nickname)) | ||||||
|  |             return json::JSON(json::jdict{{"status", json::JSON(-2l)}}); | ||||||
|  |         reserve_nickname(conn, new_chat_nickname); | ||||||
|  | 
 | ||||||
|  |         sqlite_nooutput(conn, | ||||||
|  |             "INSERT INTO `chat` (`nickname`, `name`, `it_HistoryId`, `lastMsgId`) VALUES (?1, ?2, 0, -1)"); | ||||||
|  | 
 | ||||||
|  |         int64_t CHAT_ID = sqlite_trsess_last_insert_rowid(conn); | ||||||
|  | 
 | ||||||
|  |         make_her_a_member_of_the_midnight_crew(conn, CHAT_ID, uid, user_chat_role_admin); | ||||||
|  | 
 | ||||||
|  |         // todo: send a message into chat
 | ||||||
|         json::JSON Recv; |         json::JSON Recv; | ||||||
|         Recv["status"] = json::JSON(0l); |         poll_update_chat_list(conn, uid, Sent, Recv); | ||||||
|         // todo: WRITE THIS MORBID THING
 |  | ||||||
|         return Recv; |         return Recv; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,13 +3,30 @@ | |||||||
| 
 | 
 | ||||||
| namespace iu9cawebchat { | namespace iu9cawebchat { | ||||||
|     json::JSON internalapi_deleteMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { |     json::JSON internalapi_deleteMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { | ||||||
|         int64_t chatId = Sent["chatId"].g().asInteger().get_int(); |         int64_t chatId = Sent["chatUpdReq"].asInteger().get_int(); | ||||||
|         int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); |         int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); | ||||||
|         if (my_role_here == user_chat_role_deleted) |         if (my_role_here == user_chat_role_deleted) | ||||||
|             een9_THROW("Unauthorized user tries to access internalapi_getChatInfo"); |             een9_THROW("Unauthorized user tries to access internalapi_getChatInfo"); | ||||||
|  |         if (my_role_here == user_chat_role_read_only) | ||||||
|  |             een9_THROW("read-only user can't send messages"); | ||||||
|  | 
 | ||||||
|  |         int64_t LocalHistoryId = Sent["chatUpdReq"]["LocalHistoryId"].asInteger().get_int(); | ||||||
|  |         int64_t msgId = Sent["id"].asInteger().get_int(); | ||||||
|  |         RowMessage_Content msgInQuestion = lookup_message_content(conn, chatId, msgId); | ||||||
|  |         if (!(!msgInQuestion.isSystem && (msgInQuestion.senderUserId == uid || my_role_here == user_chat_role_admin) )) | ||||||
|  |             een9_THROW("Can't delete: permission denied"); | ||||||
|  | 
 | ||||||
|  |         int64_t chat_HistoryId_BEFORE_EV = get_current_history_id_of_chat(conn, chatId); | ||||||
|  | 
 | ||||||
|  |         sqlite_nooutput(conn, | ||||||
|  |             "UPDATE `message` SET `exists` = 0, `text` = NULL, `chat_IncHistoryId` = ?1 WHERE `id` = ?2", | ||||||
|  |             {{1, chat_HistoryId_BEFORE_EV + 1}, {2, msgId}}); | ||||||
|  | 
 | ||||||
|  |         sqlite_nooutput(conn, "UPDATE `chat` SET `it_HistoryId` = ?1 WHERE `id` = ?2", | ||||||
|  |             {{1, chat_HistoryId_BEFORE_EV + 1}, {2, chatId}}, {}); | ||||||
|  | 
 | ||||||
|         json::JSON Recv; |         json::JSON Recv; | ||||||
|         Recv["status"] = json::JSON(0l); |         poll_update_chat(conn, Sent, Recv); | ||||||
|         // todo: WRITE THIS MORBID THING
 |  | ||||||
|         return Recv; |         return Recv; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,19 +0,0 @@ | |||||||
| #include "server_data_interact.h" |  | ||||||
| #include <engine_engine_number_9/baza_throw.h> |  | ||||||
| 
 |  | ||||||
| namespace iu9cawebchat { |  | ||||||
|     json::JSON internalapi_getChatInfo(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { |  | ||||||
|         int64_t chatId = Sent["id"].g().asInteger().get_int(); |  | ||||||
|         int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); |  | ||||||
|         if (my_role_here == user_chat_role_deleted) |  | ||||||
|             een9_THROW("Unauthorized user tries to access internalapi_getChatInfo"); |  | ||||||
|         json::JSON Recv; |  | ||||||
|         Recv["status"] = json::JSON(0l); |  | ||||||
|         RowChat_Content content = lookup_chat_content(conn, chatId); |  | ||||||
|         Recv["name"] = json::JSON(content.name); |  | ||||||
|         Recv["nickname"] = json::JSON(content.nickname); |  | ||||||
|         Recv["lastMsgId"] = json::JSON(content.lastMsgId); |  | ||||||
|         Recv["roleHere"] = json::JSON(stringify_user_chat_role(my_role_here)); |  | ||||||
|         return Recv; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,33 +0,0 @@ | |||||||
| #include "server_data_interact.h" |  | ||||||
| #include <engine_engine_number_9/baza_throw.h> |  | ||||||
| 
 |  | ||||||
| namespace iu9cawebchat { |  | ||||||
|     json::JSON internalapi_getChatList(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { |  | ||||||
|         json::JSON Recv; |  | ||||||
|         Recv["status"] = json::JSON(0l); |  | ||||||
|         Recv["chats"] = json::JSON(json::array); |  | ||||||
|         std::vector<json::JSON>& chats = Recv["chats"].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; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,36 +0,0 @@ | |||||||
| #include "server_data_interact.h" |  | ||||||
| #include <engine_engine_number_9/baza_throw.h> |  | ||||||
| 
 |  | ||||||
| namespace iu9cawebchat { |  | ||||||
|     json::JSON internalapi_getChatMemberList(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { |  | ||||||
|         int64_t chatId = Sent["id"].g().asInteger().get_int(); |  | ||||||
|         int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); |  | ||||||
|         if (my_role_here == user_chat_role_deleted) |  | ||||||
|             een9_THROW("Unauthorized user tries to access internalapi_getChatInfo"); |  | ||||||
|         json::JSON Recv; |  | ||||||
|         Recv["status"] = json::JSON(0l); |  | ||||||
|         Recv["members"] = json::JSON(json::array); |  | ||||||
|         std::vector<json::JSON>& members = Recv["members"].g().asArray(); |  | ||||||
|         SqliteStatement req(conn, |  | ||||||
|             "SELECT `user`.`id`, `user`.`nickname`, `user`.`name`, `user_chat_membership`.`role` FROM " |  | ||||||
|             "`user` RIGHT JOIN `user_chat_membership` ON `user`.`id` = `user_chat_membership`.`userId` " |  | ||||||
|             "WHERE `user_chat_membership`.`chatId` = ?1", |  | ||||||
|             {{1, chatId}}, {}); |  | ||||||
|         while (true) { |  | ||||||
|             fsql_integer_or_null this_user_id; |  | ||||||
|             fsql_text8_or_null this_user_nickname, this_user_name; |  | ||||||
|             fsql_integer_or_null this_users_role; |  | ||||||
|             int status = sqlite_stmt_step(req, {{0, &this_user_id}, {3, &this_users_role}}, |  | ||||||
|                 {{1, &this_user_nickname}, {2, &this_user_name}}); |  | ||||||
|             if (status != SQLITE_ROW) |  | ||||||
|                 break; |  | ||||||
|             members.emplace_back(); |  | ||||||
|             json::JSON& member = members.back(); |  | ||||||
|             member["id"] = json::JSON(this_user_id.value); |  | ||||||
|             member["content"]["nickname"] = json::JSON(this_user_nickname.value); |  | ||||||
|             member["content"]["name"] = json::JSON(this_user_name.value); |  | ||||||
|             member["content"]["role"] = json::JSON(this_users_role.value); |  | ||||||
|         } |  | ||||||
|         return Recv; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,20 +0,0 @@ | |||||||
| #include "server_data_interact.h" |  | ||||||
| #include <engine_engine_number_9/baza_throw.h> |  | ||||||
| 
 |  | ||||||
| namespace iu9cawebchat { |  | ||||||
|     json::JSON internalapi_getMessageInfo(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { |  | ||||||
|         int64_t chatId = Sent["chatId"].g().asInteger().get_int(); |  | ||||||
|         int64_t msgId = Sent["id"].g().asInteger().get_int(); |  | ||||||
|         if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted) |  | ||||||
|             een9_THROW("Authentication failure"); |  | ||||||
|         json::JSON Recv; |  | ||||||
|         Recv["status"] = json::JSON(0l); |  | ||||||
|         RowMessage_Content content = lookup_message_content(conn, chatId, msgId); |  | ||||||
|         Recv["text"] = json::JSON(content.text); |  | ||||||
|         Recv["isSystem"] = json::JSON(content.isSystem); |  | ||||||
|         Recv["sender"] = json::JSON(content.senderUserId); |  | ||||||
|         // todo: sync that addition with api documentation
 |  | ||||||
|         Recv["previous"] = json::JSON(content.previous); |  | ||||||
|         return Recv; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,52 +0,0 @@ | |||||||
| #include "server_data_interact.h" |  | ||||||
| #include <engine_engine_number_9/baza_throw.h> |  | ||||||
| 
 |  | ||||||
| namespace iu9cawebchat { |  | ||||||
|     /* This is literally the most dumb and useless query */ |  | ||||||
|     json::JSON internalapi_getMessageNeighbours(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { |  | ||||||
|         int64_t chatId = Sent["chatId"].g().asInteger().get_int(); |  | ||||||
|         if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted) |  | ||||||
|             een9_THROW("Authentication failure"); |  | ||||||
|         bool dir_forward = Sent["direction"].g().asString() == "forward"; |  | ||||||
|         int64_t amount = Sent["amount"].g().asInteger().get_int(); |  | ||||||
|         if (amount < 0) |  | ||||||
|             een9_THROW("Incorrect amount"); |  | ||||||
|         json::JSON Recv; |  | ||||||
|         Recv["status"] = json::JSON(0l); |  | ||||||
|         Recv["messages"] = json::JSON(json::array); |  | ||||||
|         std::vector<json::JSON>& messages = Recv["messages"].g().asArray(); |  | ||||||
|         if (dir_forward) { |  | ||||||
|             int64_t curMsg = Sent["id"].g().asInteger().get_int(); |  | ||||||
|             if (curMsg < 0) |  | ||||||
|                 een9_THROW("forward message lookup from the beginning of chat is not supported yet"); |  | ||||||
|             while (true) { |  | ||||||
|                 /* At this point, curMsg is non-negative */ |  | ||||||
|                 std::pair<int64_t, RowMessage_Content> nxt = lookup_message_content_rev_side(conn, chatId, curMsg); |  | ||||||
|                 if (nxt.first < 0) |  | ||||||
|                     break; |  | ||||||
|                 messages.emplace_back(); |  | ||||||
|                 json::JSON& message = messages.back(); |  | ||||||
|                 message["id"] = json::JSON(nxt.first); |  | ||||||
|                 message["previous"] = json::JSON(curMsg); |  | ||||||
|                 message["content"]["text"] = json::JSON(nxt.second.text); |  | ||||||
|                 message["content"]["isSystem"] = json::JSON(nxt.second.isSystem); |  | ||||||
|                 message["content"]["sender"] = json::JSON(nxt.second.senderUserId); |  | ||||||
|                 curMsg = nxt.first; |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             int64_t curMsg = Sent["previousMsgId"].g().asInteger().get_int(); |  | ||||||
|             while (curMsg >= 0) { |  | ||||||
|                 RowMessage_Content curRow = lookup_message_content(conn, chatId, curMsg); |  | ||||||
|                 messages.emplace_back(); |  | ||||||
|                 json::JSON& message = messages.back(); |  | ||||||
|                 message["id"] = json::JSON(curMsg); |  | ||||||
|                 message["previous"] = json::JSON(curRow.previous); |  | ||||||
|                 message["content"]["text"] = json::JSON(curRow.text); |  | ||||||
|                 message["content"]["isSystem"] = json::JSON(curRow.isSystem); |  | ||||||
|                 message["content"]["sender"] = json::JSON(curRow.senderUserId); |  | ||||||
|                 curMsg = curRow.previous; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return Recv; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,13 +0,0 @@ | |||||||
| #include "server_data_interact.h" |  | ||||||
| 
 |  | ||||||
| namespace iu9cawebchat { |  | ||||||
|     json::JSON internalapi_getUserInfo(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { |  | ||||||
|         int64_t otherUserId = Sent["id"].g().asInteger().get_int(); |  | ||||||
|         json::JSON Recv; |  | ||||||
|         Recv["status"] = json::JSON(0l); |  | ||||||
|         RowUser_Content content = lookup_user_content(conn, otherUserId); |  | ||||||
|         Recv["name"] = json::JSON(content.name); |  | ||||||
|         Recv["nickname"] = json::JSON(content.nickname); |  | ||||||
|         return Recv; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -0,0 +1,14 @@ | |||||||
|  | #include "server_data_interact.h" | ||||||
|  | #include <engine_engine_number_9/baza_throw.h> | ||||||
|  | 
 | ||||||
|  | namespace iu9cawebchat { | ||||||
|  |     json::JSON internalapi_leaveChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { | ||||||
|  |         int64_t chatId = Sent["chatId"].asInteger().get_int(); | ||||||
|  |         if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted) | ||||||
|  |             een9_THROW("Not a member"); | ||||||
|  |         kick_from_chat(conn, uid, chatId); | ||||||
|  |         json::JSON Recv; | ||||||
|  |         poll_update_chat_list(conn, uid, Sent, Recv); | ||||||
|  |         return Recv; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -1,151 +0,0 @@ | |||||||
| #include "server_data_interact.h" |  | ||||||
| #include <engine_engine_number_9/baza_throw.h> |  | ||||||
| 
 |  | ||||||
| namespace iu9cawebchat { |  | ||||||
|     int64_t get_current_history_id_of_chat(SqliteConnection& conn, int64_t chatId) { |  | ||||||
|         SqliteStatement req(conn, "SELECT `it_HistoryId` FROM `chat` WHERE `id` = ?1", {{1, chatId}}, {}); |  | ||||||
|         fsql_integer_or_null HistoryId; |  | ||||||
|         int status = sqlite_stmt_step(req, {{0, &HistoryId}}, {}); |  | ||||||
|         een9_ASSERT_pl(status == SQLITE_ROW); |  | ||||||
|         return HistoryId.value; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     int64_t get_current_history_id_of_user_chatList(SqliteConnection& conn, int64_t userId) { |  | ||||||
|         SqliteStatement req(conn, "SELECT `chatList_HistoryId` FROM `user` WHERE `id` = ?1", {{1, userId}}, {}); |  | ||||||
|         fsql_integer_or_null HistoryId; |  | ||||||
|         int status = sqlite_stmt_step(req, {{0, &HistoryId}}, {}); |  | ||||||
|         een9_ASSERT_pl(status == SQLITE_ROW); |  | ||||||
|         return HistoryId.value; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void internalapi_pollEvents_in_chat_collect_membership_events(SqliteConnection& conn, |  | ||||||
|         std::vector<json::JSON>& events, int64_t chatId, int64_t LocalHistoryId) { |  | ||||||
|         SqliteStatement membership_changes(conn, |  | ||||||
|                     "SELECT `userId`, `role`, FROM `user_chat_membership` WHERE `chatId` = ?1 " |  | ||||||
|                     "AND `chat_IncHistoryId` > ?2", {{1, chatId}, {2, LocalHistoryId}}, {}); |  | ||||||
|         fsql_integer_or_null ev_userId; |  | ||||||
|         fsql_integer_or_null ev_user_role; |  | ||||||
|         while (true) { |  | ||||||
|             int status = sqlite_stmt_step(membership_changes, |  | ||||||
|             {{0, &ev_userId}, {1, &ev_user_role}}, {}); |  | ||||||
|             if (status != SQLITE_ROW) |  | ||||||
|                 break; |  | ||||||
|             events.emplace_back(); |  | ||||||
|             json::JSON& event = events.back(); |  | ||||||
|             event["member"] = json::JSON(ev_userId.value); |  | ||||||
|             if (ev_user_role.value == user_chat_role_deleted) { |  | ||||||
|                 event["type"] = json::JSON("removedMember"); |  | ||||||
|             } else { |  | ||||||
|                 event["type"] = json::JSON("addedMember"); |  | ||||||
|                 RowUser_Content USER = lookup_user_content(conn, ev_userId.value); |  | ||||||
|                 event["content"]["name"] = json::JSON(USER.name); |  | ||||||
|                 event["content"]["nickname"] = json::JSON(USER.nickname); |  | ||||||
|                 event["content"]["role"] = json::JSON(stringify_user_chat_role(ev_user_role.value)); |  | ||||||
|             } |  | ||||||
|             events.push_back(event); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void internalapi_pollEvents_in_chat_collect_messages_events(SqliteConnection& conn, |  | ||||||
|         std::vector<json::JSON>& events, int64_t chatId, int64_t LocalHistoryId) { |  | ||||||
|         SqliteStatement messages_changes(conn, |  | ||||||
|             "SELECT `id`, `previous`, `senderUserId`, `exists`, `isSystem`, `text` FROM `messages` WHERE " |  | ||||||
|             "WHERE `chatId` = ?1 AND `chat_IncHistoryId` > ?2", {{1, chatId}, {2, LocalHistoryId}}, {}); |  | ||||||
|         fsql_integer_or_null ev_msgId, ev_previousMsgId, msgSenderUserId, msgExists, msgIsSystem; |  | ||||||
|         fsql_text8_or_null msgText; |  | ||||||
|         while (true) { |  | ||||||
|             int status = sqlite_stmt_step(messages_changes, |  | ||||||
|                 {{0, &ev_msgId}, {1, &ev_previousMsgId}, {2, &msgSenderUserId}, {3, &msgExists}, {4, &msgIsSystem}}, |  | ||||||
|                 {{5, &msgText}}); |  | ||||||
|             if (status != SQLITE_ROW) |  | ||||||
|                 break; |  | ||||||
|             events.emplace_back(); |  | ||||||
|             json::JSON& event = events.back(); |  | ||||||
|             event["type"] = json::JSON("newMessage"); |  | ||||||
|             event["id"] = json::JSON(ev_msgId.value); |  | ||||||
|             event["previous"] = json::JSON(ev_previousMsgId.value); |  | ||||||
|             event["content"]["sender"] = json::JSON(msgSenderUserId.value); |  | ||||||
|             event["content"]["isSystem"] = json::JSON((bool)msgIsSystem.value); |  | ||||||
|             event["content"]["text"] = json::JSON(msgText.value); |  | ||||||
|             events.push_back(event); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void internalapi_pollEvents_in_user_chatList_collect_events(SqliteConnection& conn, |  | ||||||
|         std::vector<json::JSON>& events, int64_t userId, int64_t LocalHistoryId) { |  | ||||||
|         SqliteStatement membership_changes(conn, |  | ||||||
|             "SELECT `chatId`, `role` FROM `user_chat_membership` WHERE `userId` = ?1 " |  | ||||||
|             "AND `user_chatList_IncHistoryId` > ?2", {{1, userId}, {2, LocalHistoryId}}); |  | ||||||
|         fsql_integer_or_null ev_chatId, usersRoleHere; |  | ||||||
|         while (true) { |  | ||||||
|             int status = sqlite_stmt_step(membership_changes, {{0, &ev_chatId}, {1, &usersRoleHere}}, {}); |  | ||||||
|             if (status != SQLITE_ROW) |  | ||||||
|                 break; |  | ||||||
|             events.emplace_back(); |  | ||||||
|             json::JSON& event = events.back(); |  | ||||||
|             event["id"] = json::JSON(ev_chatId.value); |  | ||||||
|             if (usersRoleHere.value == user_chat_role_deleted) { |  | ||||||
|                 event["type"] = json::JSON("removedChat"); |  | ||||||
|             } else { |  | ||||||
|                 event["type"] = json::JSON("addedChat"); |  | ||||||
|                 RowChat_Content CHAT = lookup_chat_content(conn, ev_chatId.value); |  | ||||||
|                 event["content"]["name"] = json::JSON(CHAT.name); |  | ||||||
|                 event["content"]["nickname"] = json::JSON(CHAT.nickname); |  | ||||||
|                 event["content"]["lastMsgId"] = json::JSON(CHAT.lastMsgId); |  | ||||||
|                 event["content"]["roleHere"] = json::JSON(stringify_user_chat_role(usersRoleHere.value)); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     int64_t event_polling_fill_chat_hist_entity_response(SqliteConnection& conn, json::JSON& hist_entity_response, |  | ||||||
|             int64_t uid, int64_t chatId, int64_t LocalHistoryId) { |  | ||||||
|         hist_entity_response["type"] = json::JSON("chat"); |  | ||||||
|         if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted) |  | ||||||
|             een9_THROW("internalapi/pollEvents: trying to access chat that user does not belong to"); |  | ||||||
|         hist_entity_response["chatId"] = json::JSON(chatId); |  | ||||||
|         int64_t NewHistoryId = get_current_history_id_of_chat(conn, chatId); |  | ||||||
|         hist_entity_response["HistoryId"] = json::JSON(NewHistoryId); |  | ||||||
| 
 |  | ||||||
|         hist_entity_response["events"] = json::JSON(json::array); |  | ||||||
|         std::vector<json::JSON>& events = hist_entity_response["events"].g().asArray(); |  | ||||||
|         /* 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); |  | ||||||
|         return NewHistoryId; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     int64_t event_polling_fill_chatlist_hist_entity_response(SqliteConnection& conn, json::JSON& hist_entity_response, |  | ||||||
|             int64_t uid, int64_t LocalHistoryId) { |  | ||||||
|         hist_entity_response["type"] = json::JSON("chatlist"); |  | ||||||
|         int64_t NewHistoryId = get_current_history_id_of_user_chatList(conn, uid); |  | ||||||
|         hist_entity_response["HistotyId"] = json::JSON(NewHistoryId); |  | ||||||
| 
 |  | ||||||
|         hist_entity_response["events"] = json::JSON(json::array); |  | ||||||
|         std::vector<json::JSON>& events = hist_entity_response["events"].g().asArray(); |  | ||||||
|         internalapi_pollEvents_in_user_chatList_collect_events(conn, events, uid, LocalHistoryId); |  | ||||||
|         return NewHistoryId; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     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(); |  | ||||||
|             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(); |  | ||||||
|                 event_polling_fill_chat_hist_entity_response(conn, hist_entity_response, uid, chatId, LocalHistoryId); |  | ||||||
|             } else if (hist_entity_request["type"].g().asString() == "chatlist") { |  | ||||||
|                 event_polling_fill_chatlist_hist_entity_response(conn, hist_entity_response, uid, LocalHistoryId); |  | ||||||
|             } else |  | ||||||
|                 een9_THROW("Bad request"); |  | ||||||
|         } |  | ||||||
|         return Recv; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -1,11 +0,0 @@ | |||||||
| #include "server_data_interact.h" |  | ||||||
| #include <engine_engine_number_9/baza_throw.h> |  | ||||||
| 
 |  | ||||||
| namespace iu9cawebchat { |  | ||||||
|     json::JSON internalapi_removeChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { |  | ||||||
|         json::JSON Recv; |  | ||||||
|         Recv["status"] = json::JSON(0l); |  | ||||||
|         // todo: WRITE THIS MORBID THING
 |  | ||||||
|         return Recv; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @ -2,14 +2,36 @@ | |||||||
| #include <engine_engine_number_9/baza_throw.h> | #include <engine_engine_number_9/baza_throw.h> | ||||||
| 
 | 
 | ||||||
| namespace iu9cawebchat { | namespace iu9cawebchat { | ||||||
|  |     void kick_from_chat(SqliteConnection& conn, int64_t alienUserId, int64_t chatId) { | ||||||
|  |         if (get_role_of_user_in_chat(conn, alienUserId, chatId) == user_chat_role_deleted) | ||||||
|  |             een9_THROW("Can't delete a deleted member"); | ||||||
|  |         int64_t chat_HistoryId_BEFORE_EV = get_current_history_id_of_chat(conn, chatId); | ||||||
|  |         int64_t alien_chatlist_HistoryId_BEFORE_EV = get_current_history_id_of_user_chatList(conn, alienUserId); | ||||||
|  | 
 | ||||||
|  |         sqlite_nooutput(conn, | ||||||
|  |         "UPDATE `user_chat_membership` SET `user_chatList_IncHistoryId` = ?3," | ||||||
|  |         "`chat_IncHistoryId` = ?4, `role` = ?5 WHERE `userId` = ?1 AND `chatId` = ?2", | ||||||
|  |             {{1, alienUserId}, {2, chatId}, {3, alien_chatlist_HistoryId_BEFORE_EV + 1}, | ||||||
|  |             {4, chat_HistoryId_BEFORE_EV + 1}, {5, user_chat_role_deleted}}, {}); | ||||||
|  | 
 | ||||||
|  |         sqlite_nooutput(conn, | ||||||
|  |             "UPDATE `chat` SET `it_HistoryId` = ?1 WHERE `id` = ?2", {{1, chat_HistoryId_BEFORE_EV + 1}, | ||||||
|  |             {2, chatId}}, {}); | ||||||
|  | 
 | ||||||
|  |         sqlite_nooutput(conn, | ||||||
|  |             "UPDATE `user` SET `chatList_HistoryId` = ?1 WHERE `id` = ?2", | ||||||
|  |             {{1, alien_chatlist_HistoryId_BEFORE_EV + 1}, {2, alienUserId}}, {}); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     json::JSON internalapi_removeMemberFromChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { |     json::JSON internalapi_removeMemberFromChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { | ||||||
|         int64_t chatId = Sent["chatId"].g().asInteger().get_int(); |         int64_t chatId = Sent["chatUpdReq"]["chatId"].asInteger().get_int(); | ||||||
|         int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); |         int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); | ||||||
|         if (my_role_here == user_chat_role_deleted) |         if (my_role_here != user_chat_role_deleted) | ||||||
|             een9_THROW("Unauthorized user tries to access internalapi_getChatInfo"); |             een9_THROW("Only admin can delete members of chat"); | ||||||
|  |         int64_t badAlienId = Sent["chatUpdReq"]["userId"].asInteger().get_int(); | ||||||
|  |         kick_from_chat(conn, badAlienId, chatId); | ||||||
|         json::JSON Recv; |         json::JSON Recv; | ||||||
|         Recv["status"] = json::JSON(0l); |         poll_update_chat(conn, Sent, Recv); | ||||||
|         // todo: WRITE THIS MORBID THING
 |  | ||||||
|         return Recv; |         return Recv; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -3,46 +3,39 @@ | |||||||
| #include "../str_fields.h" | #include "../str_fields.h" | ||||||
| 
 | 
 | ||||||
| namespace iu9cawebchat { | namespace iu9cawebchat { | ||||||
|     /* Throws excetion on failure
 |     /* No authorization check is performed
 | ||||||
|      * Chat's HistoryId will increment after this operation, incremented |      * Chat's HistoryId will increment after this operation | ||||||
|      * if adding system message, uid is ignored |      * if adding system message, uid is ignored | ||||||
|      */ |      */ | ||||||
|     int64_t insert_new_message(SqliteConnection& conn, int64_t uid, int64_t chatId, |     void insert_new_message(SqliteConnection& conn, int64_t uid, int64_t chatId, | ||||||
|             const std::string& text, bool isSystem) { |             const std::string& text, bool isSystem) { | ||||||
|         int64_t chat_HistoryId_BEFORE_MSG = get_current_history_id_of_chat(conn, chatId); |         int64_t chat_HistoryId_BEFORE_MSG = get_current_history_id_of_chat(conn, chatId); | ||||||
|         if (chat_HistoryId_BEFORE_MSG > INT64_MAX - 100) |         int64_t chat_lastMsgId = get_lastMsgId_of_chat(conn, chatId); | ||||||
|             een9_THROW("please no"); |  | ||||||
|         SqliteStatement req(conn, |         SqliteStatement req(conn, | ||||||
|             "INSERT INTO `message` (`chatId`, `previous`, `senderUserId`, `exists`, `isSystem`, `text`," |             "INSERT INTO `message` (`chatId`, `id`, `senderUserId`, `exists`, `isSystem`, `chat_IncHistoryId`, " | ||||||
|             "`chat_IncHistoryId`) VALUES (?1, ?2, ?3, 1, ?4, ?5, ?6)", |             "`text`) VALUES (?1, ?2, ?3 1, ?4, ?5, ?6)", | ||||||
|             {{1, chatId}, {4, (int64_t)isSystem}, {6, chat_HistoryId_BEFORE_MSG + 1}}, {{5, text}}); |             {{1, chatId}, {2, chat_lastMsgId + 1}, {4, (int64_t)isSystem}, {5, chat_HistoryId_BEFORE_MSG + 1}}, {{6, text}}); | ||||||
|         int64_t chat_cur_last_msg_id = get_lastMsgId_of_chat(conn, chatId); |  | ||||||
|         if (chat_cur_last_msg_id >= 0) |  | ||||||
|             sqlite_stmt_bind_int64(req, 2, chat_cur_last_msg_id); |  | ||||||
|         if (!isSystem) |         if (!isSystem) | ||||||
|             sqlite_stmt_bind_int64(req, 3, uid); |             sqlite_stmt_bind_int64(req, 3, uid); | ||||||
|         int64_t MSG_ID = sqlite_trsess_last_insert_rowid(conn); |  | ||||||
|         sqlite_nooutput(conn, "UPDATE `chat` SET `lastMsgId` = ?1, `it_HistoryId` = ?2 WHERE `id` = ?3", |         sqlite_nooutput(conn, "UPDATE `chat` SET `lastMsgId` = ?1, `it_HistoryId` = ?2 WHERE `id` = ?3", | ||||||
|             {{1, MSG_ID}, {2, chat_HistoryId_BEFORE_MSG + 1}, {3, chatId}}, {}); |             {{1, chat_lastMsgId + 1}, {2, chat_HistoryId_BEFORE_MSG + 1}, {3, chatId}}, {}); | ||||||
|         return MSG_ID; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     json::JSON internalapi_sendMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { |     json::JSON internalapi_sendMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { | ||||||
|         int64_t chatId = Sent["chatId"].g().asInteger().get_int(); |         int64_t chatId = Sent["chatUpdReq"].asInteger().get_int(); | ||||||
|         int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); |         int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); | ||||||
|         if (my_role_here == user_chat_role_deleted) |         if (my_role_here == user_chat_role_deleted) | ||||||
|             een9_THROW("Unauthorized user tries to access internalapi_getChatInfo"); |             een9_THROW("Unauthorized user tries to access internalapi_getChatInfo"); | ||||||
|         if (my_role_here == user_chat_role_read_only) |         if (my_role_here == user_chat_role_read_only) | ||||||
|             een9_THROW("read-only user can't send messages"); |             een9_THROW("read-only user can't send messages"); | ||||||
|         const int64_t LocalHistoryId = Sent["LocalHistoryId"].g().asInteger().get_int(); | 
 | ||||||
|         std::string text = Sent["content"]["text"].g().asString(); |         std::string text = Sent["content"]["text"].asString(); | ||||||
|         if (!is_orthodox_string(text) || text.empty()) |         if (!is_orthodox_string(text) || text.empty()) | ||||||
|             een9_THROW("Bad input text"); |             een9_THROW("Bad input text"); | ||||||
|         int64_t MSG_ID = insert_new_message(conn, uid, chatId, text, false); |         insert_new_message(conn, uid, chatId, text, false); | ||||||
|  | 
 | ||||||
|         json::JSON Recv; |         json::JSON Recv; | ||||||
|         Recv["status"] = json::JSON(0l); |         poll_update_chat(conn, Sent, Recv); | ||||||
|         json::JSON hist_ent_response = Recv["update"][0].g(); |  | ||||||
|         event_polling_fill_chat_hist_entity_response(conn, hist_ent_response, uid, chatId, LocalHistoryId); |  | ||||||
|         return Recv; |         return Recv; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -20,64 +20,45 @@ namespace iu9cawebchat { | |||||||
|             if (ret_logged_in_user >= 0) { |             if (ret_logged_in_user >= 0) { | ||||||
|                 ret_userinfo["uid"] = json::JSON(ret_logged_in_user); |                 ret_userinfo["uid"] = json::JSON(ret_logged_in_user); | ||||||
|                 ret_userinfo["nickname"] = json::JSON(tried.nickname); |                 ret_userinfo["nickname"] = json::JSON(tried.nickname); | ||||||
|                 ret_userinfo["name"] = json::JSON(find_user_name(conn, ret_logged_in_user)); |                 ret_userinfo["name"] = json::JSON(get_user_name(conn, ret_logged_in_user)); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string R200_pres_uinfo(const std::string& el_name, WorkerGuestData& wgd, |     std::string http_R200(const std::string &el_name, WorkerGuestData &wgd, | ||||||
|                      const json::JSON& config_presentation, |         const std::vector<const json::JSON *> &args) { | ||||||
|                      const json::JSON& userinfo) { |         std::string page = wgd.templater->render(el_name, args); | ||||||
| 
 |  | ||||||
|         std::string page = wgd.templater->render(el_name, {&config_presentation, &userinfo}); |  | ||||||
|         return een9::form_http_server_response_200("text/html", page); |         return een9::form_http_server_response_200("text/html", page); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string R200_pres_uinfo_msges(const std::string& el_name, WorkerGuestData& wgd, |     json::JSON jsonify_html_message_list(const std::vector<HtmlMsgBox>& messages) { | ||||||
|         const json::JSON& config_presentation, |         json::JSON jmessages(json::array); | ||||||
|         const json::JSON& userinfo, const std::vector<HtmlMsgBox>& messages) { |  | ||||||
| 
 |  | ||||||
|         json::JSON jmessages; |  | ||||||
|         // todo: optimize
 |  | ||||||
|         for (size_t i = 0; i < messages.size(); i++) { |         for (size_t i = 0; i < messages.size(); i++) { | ||||||
|             jmessages[i]["class"] = json::JSON(messages[i].class_); |             jmessages[i]["class"].asString() = messages[i].class_; | ||||||
|             jmessages[i]["text"] = json::JSON(messages[i].text); |             jmessages[i]["text"].asString() = messages[i].text; | ||||||
|         } |         } | ||||||
|         std::string page = wgd.templater->render(el_name, {&config_presentation, &userinfo, &jmessages}); |         return jmessages; | ||||||
|         return een9::form_http_server_response_200("text/html", page); |     } | ||||||
|  | 
 | ||||||
|  |     std::string page_E404(WorkerGuestData &wgd) { | ||||||
|  |         return een9::form_http_server_response_404("text/html", | ||||||
|  |             wgd.templater->render("err-404", {})); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* ========================= API =========================*/ |     /* ========================= API =========================*/ | ||||||
| 
 |  | ||||||
|     std::string when_internalapi(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid, |     std::string when_internalapi(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid, | ||||||
|         const std::function<json::JSON(SqliteConnection&, int64_t, const json::JSON&)>& F) { |                                  const std::function<json::JSON(SqliteConnection&, int64_t, const json::JSON&)>& F) { | ||||||
|         const json::JSON& Sent = json::parse_str_flawless(req.body); |         const json::JSON& Sent = json::parse_str_flawless(req.body); | ||||||
|         std::string result = json::generate_str(F(*wgd.db, uid, Sent), json::print_pretty); |         std::string result = json::generate_str(F(*wgd.db, uid, Sent), json::print_pretty); | ||||||
|         return een9::form_http_server_response_200("text/json", result); |         return een9::form_http_server_response_200("text/json", result); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string when_internalapi_pollevents(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid) { |     std::string when_internalapi_chatpollevents(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid) { | ||||||
|         return when_internalapi(wgd, req, uid, internalapi_pollEvents); |         return when_internalapi(wgd, req, uid, internalapi_chatPollEvents); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string when_internalapi_getchatlist(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid) { |     std::string when_internalapi_chatlistpollevents(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid) { | ||||||
|         return when_internalapi(wgd, req, uid, internalapi_getChatList); |         return when_internalapi(wgd, req, uid, internalapi_chatListPollEvents); | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     std::string when_internalapi_getchatinfo(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid) { |  | ||||||
|         return when_internalapi(wgd, req, uid, internalapi_getChatInfo); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     std::string when_internalapi_getchatmemberlist(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid) { |  | ||||||
|         return when_internalapi(wgd, req, uid, internalapi_getChatMemberList); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     std::string when_internalapi_getuserinfo(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid) { |  | ||||||
|         return when_internalapi(wgd, req, uid, internalapi_getUserInfo); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     std::string when_internalapi_getmessageinfo(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid) { |  | ||||||
|         return when_internalapi(wgd, req, uid, internalapi_getMessageInfo); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string when_internalapi_getmessageneighbours(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid) { |     std::string when_internalapi_getmessageneighbours(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid) { | ||||||
| @ -104,7 +85,7 @@ namespace iu9cawebchat { | |||||||
|         return when_internalapi(wgd, req, uid, internalapi_createChat); |         return when_internalapi(wgd, req, uid, internalapi_createChat); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string when_internalapi_removechat(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid) { |     std::string when_internalapi_leavechat(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid) { | ||||||
|         return when_internalapi(wgd, req, uid, internalapi_removeChat); |         return when_internalapi(wgd, req, uid, internalapi_leaveChat); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -27,18 +27,17 @@ namespace iu9cawebchat { | |||||||
|             int64_t& ret_logged_in_user |             int64_t& ret_logged_in_user | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|     std::string R200_pres_uinfo(const std::string& el_name, WorkerGuestData& wgd, |     std::string http_R200(const std::string& el_name, WorkerGuestData& wgd, | ||||||
|         const json::JSON& config_presentation, |         const std::vector<const json::JSON*>& args); | ||||||
|         const json::JSON& userinfo); |  | ||||||
| 
 | 
 | ||||||
|     struct HtmlMsgBox { |     struct HtmlMsgBox { | ||||||
|         std::string class_; |         std::string class_; | ||||||
|         std::string text; |         std::string text; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     std::string R200_pres_uinfo_msges(const std::string& el_name, WorkerGuestData& wgd, |     json::JSON jsonify_html_message_list(const std::vector<HtmlMsgBox>& messages); | ||||||
|         const json::JSON& config_presentation, | 
 | ||||||
|         const json::JSON& userinfo, const std::vector<HtmlMsgBox>& messages); |     std::string page_E404(WorkerGuestData& wgd); | ||||||
| 
 | 
 | ||||||
|     /*  ========================== PAGES ================================== */ |     /*  ========================== PAGES ================================== */ | ||||||
| 
 | 
 | ||||||
| @ -52,22 +51,13 @@ namespace iu9cawebchat { | |||||||
|                                const een9::ClientRequest& req, const json::JSON& userinfo); |                                const een9::ClientRequest& req, const json::JSON& userinfo); | ||||||
| 
 | 
 | ||||||
|     std::string when_page_user(WorkerGuestData& wgd, const json::JSON& config_presentation, |     std::string when_page_user(WorkerGuestData& wgd, const json::JSON& config_presentation, | ||||||
|             const een9::ClientRequest& req, const json::JSON& userinfo); |             const een9::ClientRequest& req, const std::vector<LoginCookie>& login_cookies, const json::JSON& userinfo); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     /* ========================  API  ============================== */ |     /* ========================  API  ============================== */ | ||||||
|  |     std::string when_internalapi_chatpollevents(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid); | ||||||
| 
 | 
 | ||||||
|     std::string when_internalapi_pollevents(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid); |     std::string when_internalapi_chatlistpollevents(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid); | ||||||
| 
 |  | ||||||
|     std::string when_internalapi_getchatlist(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid); |  | ||||||
| 
 |  | ||||||
|     std::string when_internalapi_getchatinfo(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid); |  | ||||||
| 
 |  | ||||||
|     std::string when_internalapi_getchatmemberlist(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid); |  | ||||||
| 
 |  | ||||||
|     std::string when_internalapi_getuserinfo(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid); |  | ||||||
| 
 |  | ||||||
|     std::string when_internalapi_getmessageinfo(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid); |  | ||||||
| 
 | 
 | ||||||
|     std::string when_internalapi_getmessageneighbours(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid); |     std::string when_internalapi_getmessageneighbours(WorkerGuestData& wgd, const een9::ClientRequest& req, int64_t uid); | ||||||
| 
 | 
 | ||||||
| @ -81,7 +71,7 @@ namespace iu9cawebchat { | |||||||
| 
 | 
 | ||||||
|     std::string when_internalapi_createchat(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid); |     std::string when_internalapi_createchat(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid); | ||||||
| 
 | 
 | ||||||
|     std::string when_internalapi_removechat(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid); |     std::string when_internalapi_leavechat(WorkerGuestData &wgd, const een9::ClientRequest &req, int64_t uid); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  | |||||||
							
								
								
									
										196
									
								
								src/web_chat/iu9_ca_web_chat_lib/backend_logic/polling.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								src/web_chat/iu9_ca_web_chat_lib/backend_logic/polling.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,196 @@ | |||||||
|  | #include "server_data_interact.h" | ||||||
|  | #include <assert.h> | ||||||
|  | #include <engine_engine_number_9/baza_throw.h> | ||||||
|  | 
 | ||||||
|  | namespace iu9cawebchat { | ||||||
|  |     json::JSON poll_update_chat_list_resp(SqliteConnection& conn, int64_t userId, int64_t LocalHistoryId) { | ||||||
|  |         json::JSON chatListUpdResp; | ||||||
|  |         SqliteStatement my_membership_changes(conn, | ||||||
|  |            "SELECT `chatId`, `role` FROM `user_chat_membership` WHERE `userId` = ?1 " | ||||||
|  |            "AND `user_chatList_IncHistoryId` > ?2", {{1, userId}, {2, LocalHistoryId}}); | ||||||
|  |         json::jarr myChats = chatListUpdResp["myChats"].asArray(); | ||||||
|  |         while (true) { | ||||||
|  |             fsql_integer_or_null ev_chatId, usersRoleHere; | ||||||
|  |             int status = sqlite_stmt_step(my_membership_changes, {{0, &ev_chatId}, {1, &usersRoleHere}}, {}); | ||||||
|  |             if (status != SQLITE_ROW) | ||||||
|  |                 break; | ||||||
|  |             myChats.emplace_back(); | ||||||
|  |             json::JSON& myMembershipSt = myChats.back(); | ||||||
|  |             myMembershipSt["chatId"].asInteger() = json::Integer(ev_chatId.value); | ||||||
|  |             myMembershipSt["myRoleHere"].asString() = stringify_user_chat_role(usersRoleHere.value); | ||||||
|  |             if (usersRoleHere.value != user_chat_role_deleted) { | ||||||
|  |                 RowChat_Content CHAT = lookup_chat_content(conn, ev_chatId.value); | ||||||
|  |                 myMembershipSt["chatName"].asString() = CHAT.name; | ||||||
|  |                 myMembershipSt["chatNickname"].asString() = CHAT.nickname; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         int64_t HistoryId = get_current_history_id_of_user_chatList(conn, userId); | ||||||
|  |         chatListUpdResp["HistoryId"].asInteger() = json::Integer(HistoryId); | ||||||
|  |         return chatListUpdResp; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void poll_update_chat_list(SqliteConnection& conn, int64_t userId, const json::JSON& Sent, json::JSON& Recv) { | ||||||
|  |         Recv["status"].asInteger() = json::Integer(0l); | ||||||
|  |         // todo: in libjsonincpp: get rid of Integer
 | ||||||
|  |         poll_update_chat_list_resp(conn, userId, Sent["chatListUpdReq"]["LocalHistoryId"].asInteger().get_int()); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     json::JSON make_messageSt_obj(int64_t id, int64_t senderUserId, bool exists, bool isSystem, const std::string& text) { | ||||||
|  |         json::JSON messageSt; | ||||||
|  |         messageSt["id"].asInteger() = json::Integer(id); | ||||||
|  |         if (!isSystem) | ||||||
|  |             messageSt["senderUserId"].asInteger() = json::Integer(senderUserId); | ||||||
|  |         messageSt["exists"] = json::JSON(exists); | ||||||
|  |         messageSt["isSystem"] = json::JSON(isSystem); | ||||||
|  |         if (exists) | ||||||
|  |             messageSt["text"].asString() = text; | ||||||
|  |         return messageSt; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     json::jarr poll_update_chat_resp_messages(SqliteConnection& conn, int64_t chatId, int64_t LocalHistoryId, | ||||||
|  |         int64_t QSEG_A, int64_t QSEG_B) { | ||||||
|  | 
 | ||||||
|  |         json::jarr messages; | ||||||
|  |         SqliteStatement messages_changes(conn, | ||||||
|  |             "SELECT `id`, `senderUserId`, `exists`, `isSystem`, `text` FROM `messages` " | ||||||
|  |             "WHERE `chatId` = ?1 AND ( `chat_IncHistoryId` > ?2 OR ( ?3 <= `id` AND `id` <= ?4 ) )", | ||||||
|  |             {{1, chatId}, {2, LocalHistoryId}, {3, QSEG_A}, {4, QSEG_B}}, {}); | ||||||
|  |         while (true) { | ||||||
|  |             fsql_integer_or_null msgId, msgSenderUserId, msgExists, msgIsSystem; | ||||||
|  |             fsql_text8_or_null msgText; | ||||||
|  |             int status = sqlite_stmt_step(messages_changes, | ||||||
|  |                 {{0, &msgId}, {1, &msgSenderUserId}, {2, &msgExists}, {3, &msgIsSystem}}, | ||||||
|  |                 {{4, &msgText}}); | ||||||
|  |             if (status != SQLITE_ROW) | ||||||
|  |                 break; | ||||||
|  |             messages.push_back(make_messageSt_obj(msgId.value, msgSenderUserId.value, msgExists.value, | ||||||
|  |                 msgIsSystem.value, msgText.value)); | ||||||
|  |         } | ||||||
|  |         return messages; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     json::jarr poll_update_chat_resp_members(SqliteConnection& conn, int64_t chatId, int64_t LocalHistoryId) { | ||||||
|  |         json::jarr members; | ||||||
|  | 
 | ||||||
|  |         SqliteStatement membership_changes(conn, | ||||||
|  |                    "SELECT `userId`, `role`, FROM `user_chat_membership` WHERE `chatId` = ?1 " | ||||||
|  |                    "AND `chat_IncHistoryId` > ?2", {{1, chatId}, {2, LocalHistoryId}}, {}); | ||||||
|  |         while (true) { | ||||||
|  |             fsql_integer_or_null alienUserId; | ||||||
|  |             fsql_integer_or_null alienRoleHere; | ||||||
|  |             int status = sqlite_stmt_step(membership_changes, | ||||||
|  |                 {{0, &alienUserId}, {1, &alienRoleHere}}, {}); | ||||||
|  |             if (status != SQLITE_ROW) | ||||||
|  |                 break; | ||||||
|  |             members.emplace_back(); | ||||||
|  |             json::JSON& memberSt = members.back(); | ||||||
|  |             memberSt["userId"].asInteger() = json::Integer(alienUserId.value); | ||||||
|  |             memberSt["roleHere"].asString() = stringify_user_chat_role(alienRoleHere.value); | ||||||
|  |             if (alienRoleHere.value != user_chat_role_deleted) { | ||||||
|  |                 RowUser_Content alien = lookup_user_content(conn, alienUserId.value); | ||||||
|  |                 memberSt["name"].asString() = alien.name; | ||||||
|  |                 memberSt["nickname"].asString() = alien.nickname; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return members; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     json::JSON poll_update_chat_ONE_MSG_resp(SqliteConnection& conn, int64_t chatId, int64_t selectedMsg) { | ||||||
|  |         json::JSON chatUpdResp; | ||||||
|  | 
 | ||||||
|  |         chatUpdResp["members"].asArray() = poll_update_chat_resp_members(conn, chatId, 0); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         json::jarr messages = chatUpdResp["messages"].asArray(); | ||||||
|  |         if (selectedMsg >= 0) { | ||||||
|  |             RowMessage_Content msg = lookup_message_content(conn, chatId, selectedMsg); | ||||||
|  |             messages.push_back(make_messageSt_obj(msg.id, msg.senderUserId, msg.exists, msg.isSystem, msg.text)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         int64_t lastMsgId = get_lastMsgId_of_chat(conn, chatId); | ||||||
|  |         chatUpdResp["lastMsgId"].asInteger() = json::Integer(lastMsgId); | ||||||
|  | 
 | ||||||
|  |         int64_t HistoryId = get_current_history_id_of_chat(conn, chatId); | ||||||
|  |         chatUpdResp["HistoryId"].asInteger() = json::Integer(HistoryId); | ||||||
|  |         return chatUpdResp; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     json::JSON poll_update_chat_important_segment_resp(SqliteConnection& conn, int64_t chatId, int64_t LocalHistoryId, | ||||||
|  |         int64_t QSEG_A, int64_t QSEG_B) { | ||||||
|  | 
 | ||||||
|  |         json::JSON chatUpdResp; | ||||||
|  | 
 | ||||||
|  |         chatUpdResp["members"].asArray() = poll_update_chat_resp_members(conn, chatId, LocalHistoryId); | ||||||
|  |         chatUpdResp["messages"].asArray() = poll_update_chat_resp_messages(conn, chatId, LocalHistoryId, QSEG_A, QSEG_B); | ||||||
|  | 
 | ||||||
|  |         int64_t lastMsgId = get_lastMsgId_of_chat(conn, chatId); | ||||||
|  |         chatUpdResp["lastMsgId"].asInteger() = json::Integer(lastMsgId); | ||||||
|  | 
 | ||||||
|  |         int64_t HistoryId = get_current_history_id_of_chat(conn, chatId); | ||||||
|  |         chatUpdResp["HistoryId"].asInteger() = json::Integer(HistoryId); | ||||||
|  |         return chatUpdResp; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* chat polling function MUST have one queer feature: it accepts a range of msgId, which are guaranteed to be
 | ||||||
|  |      * lookud up. */ | ||||||
|  |     void poll_update_chat_important_segment(SqliteConnection& conn, const json::JSON& Sent, json::JSON& Recv, | ||||||
|  |         int64_t QSEG_A, int64_t QSEG_B) { | ||||||
|  | 
 | ||||||
|  |         Recv["status"].asInteger() = json::Integer(0l); | ||||||
|  |         Recv["charUpdResp"] = poll_update_chat_important_segment_resp(conn, | ||||||
|  |             Sent["chatUpdReq"]["chatId"].asInteger().get_int(), | ||||||
|  |             Sent["chatUpdReq"]["LocalHistoryId"].asInteger().get_int(), QSEG_A, QSEG_B); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void poll_update_chat(SqliteConnection& conn, const json::JSON& Sent, json::JSON& Recv) { | ||||||
|  |         poll_update_chat_important_segment(conn, Sent, Recv, -1, -2); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     json::JSON internalapi_chatPollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { | ||||||
|  |         int64_t chatId = Sent["chatUpdReq"]["chatId"].asInteger().get_int(); | ||||||
|  |         if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted) | ||||||
|  |             een9_THROW("chatPollEvents: trying to access chat that user does not belong to"); | ||||||
|  |         json::JSON Recv; | ||||||
|  |         poll_update_chat(conn, Sent, Recv); | ||||||
|  |         return Recv; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     json::JSON internalapi_chatListPollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { | ||||||
|  |         json::JSON Recv; | ||||||
|  |         poll_update_chat_list(conn, uid, Sent, Recv); | ||||||
|  |         return Recv; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     /* Reznya */ | ||||||
|  |     json::JSON internalapi_getMessageNeighbours(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { | ||||||
|  |         int64_t chatId = Sent["chatId"].asInteger().get_int(); | ||||||
|  |         if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted) | ||||||
|  |             een9_THROW("Authentication failure"); | ||||||
|  |         int64_t lastMsgId = get_lastMsgId_of_chat(conn, chatId); | ||||||
|  |         bool dir_forward = Sent["direction"].asString() == "forward"; | ||||||
|  |         int64_t amount = Sent["amount"].asInteger().get_int(); | ||||||
|  |         int64_t K = Sent["msgId"].asInteger().get_int(); | ||||||
|  |         if (amount <= 0 || amount > 15) | ||||||
|  |             een9_THROW("Incorrect amount"); | ||||||
|  |         json::JSON Recv; | ||||||
|  |         int64_t qBeg = -1; | ||||||
|  |         int64_t qEnd = -2; | ||||||
|  |         if (lastMsgId >= 0) { | ||||||
|  |             if (K < 0) { | ||||||
|  |                 if (dir_forward) | ||||||
|  |                     een9_THROW("Can't go from the top of chat"); | ||||||
|  |                 qBeg = std::max(0l, lastMsgId - amount + 1); | ||||||
|  |                 qEnd = lastMsgId; | ||||||
|  |             } else if (dir_forward) { | ||||||
|  |                 qBeg = K + 1; | ||||||
|  |                 qEnd = K + amount; | ||||||
|  |             } else { | ||||||
|  |                 qBeg = K - amount; | ||||||
|  |                 qEnd = K - 1; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         poll_update_chat_important_segment(conn, Sent, Recv, qBeg, qEnd); | ||||||
|  |         return Recv; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -2,6 +2,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <engine_engine_number_9/baza_throw.h> | #include <engine_engine_number_9/baza_throw.h> | ||||||
| #include <engine_engine_number_9/http_structures/cookies.h> | #include <engine_engine_number_9/http_structures/cookies.h> | ||||||
|  | #include "../str_fields.h" | ||||||
| 
 | 
 | ||||||
| namespace iu9cawebchat { | namespace iu9cawebchat { | ||||||
|     const char* stringify_user_chat_role(int64_t role) { |     const char* stringify_user_chat_role(int64_t role) { | ||||||
| @ -27,7 +28,7 @@ namespace iu9cawebchat { | |||||||
|         return -1; |         return -1; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string find_user_name (SqliteConnection& conn, int64_t uid) { |     std::string get_user_name (SqliteConnection& conn, int64_t uid) { | ||||||
|         een9_ASSERT(uid >= 0, "Are you crazy?"); |         een9_ASSERT(uid >= 0, "Are you crazy?"); | ||||||
|         SqliteStatement sql_req(conn, |         SqliteStatement sql_req(conn, | ||||||
|            "SELECT `name` FROM `user` WHERE `id` = ?1", |            "SELECT `name` FROM `user` WHERE `id` = ?1", | ||||||
| @ -50,7 +51,20 @@ namespace iu9cawebchat { | |||||||
|         fsql_text8_or_null name_col; |         fsql_text8_or_null name_col; | ||||||
|         int status = sqlite_stmt_step(sql_req, {}, {{0, &nickname_col}, {1, &name_col}}); |         int status = sqlite_stmt_step(sql_req, {}, {{0, &nickname_col}, {1, &name_col}}); | ||||||
|         if (status == SQLITE_ROW) { |         if (status == SQLITE_ROW) { | ||||||
|             return {std::move(nickname_col.value), std::move(name_col.value)}; |             return {uid, std::move(nickname_col.value), std::move(name_col.value)}; | ||||||
|  |         } | ||||||
|  |         een9_THROW("No such user"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     RowUser_Content lookup_user_content_by_nickname(SqliteConnection& conn, const std::string& nickname) { | ||||||
|  |         SqliteStatement sql_req(conn, | ||||||
|  |            "SELECT `id`, `name` FROM `user` WHERE `nickname` = ?1", | ||||||
|  |            {}, {{1, nickname}}); | ||||||
|  |         fsql_integer_or_null id_col; | ||||||
|  |         fsql_text8_or_null name_col; | ||||||
|  |         int status = sqlite_stmt_step(sql_req, {{0, &id_col}}, {{1, &name_col}}); | ||||||
|  |         if (status == SQLITE_ROW) { | ||||||
|  |             return {id_col.value, nickname, std::move(name_col.value)}; | ||||||
|         } |         } | ||||||
|         een9_THROW("No such user"); |         een9_THROW("No such user"); | ||||||
|     } |     } | ||||||
| @ -65,7 +79,22 @@ namespace iu9cawebchat { | |||||||
|         fsql_integer_or_null last_msg_id_col; |         fsql_integer_or_null last_msg_id_col; | ||||||
|         int status = sqlite_stmt_step(sql_req, {{2, &last_msg_id_col}}, {{0, &nickname_col}, {1, &name_col}}); |         int status = sqlite_stmt_step(sql_req, {{2, &last_msg_id_col}}, {{0, &nickname_col}, {1, &name_col}}); | ||||||
|         if (status == SQLITE_ROW) { |         if (status == SQLITE_ROW) { | ||||||
|             return {std::move(nickname_col.value), std::move(name_col.value), |             return {chatId, std::move(nickname_col.value), std::move(name_col.value), | ||||||
|  |                 last_msg_id_col.exist ? last_msg_id_col.value : -1}; | ||||||
|  |         } | ||||||
|  |         een9_THROW("No such chat"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     RowChat_Content lookup_chat_content_by_nickname(SqliteConnection &conn, const std::string& nickname) { | ||||||
|  |         SqliteStatement sql_req(conn, | ||||||
|  |            "SELECT `id`, `name`, `lastMsgId` FROM `chat` WHERE `nickname` = ?1", | ||||||
|  |            {}, {{1, nickname}}); | ||||||
|  |         fsql_integer_or_null id_col; | ||||||
|  |         fsql_text8_or_null name_col; | ||||||
|  |         fsql_integer_or_null last_msg_id_col; | ||||||
|  |         int status = sqlite_stmt_step(sql_req, {{0, &id_col}, {2, &last_msg_id_col}}, {{1, &name_col}}); | ||||||
|  |         if (status == SQLITE_ROW) { | ||||||
|  |             return {id_col.value, nickname, std::move(name_col.value), | ||||||
|                 last_msg_id_col.exist ? last_msg_id_col.value : -1}; |                 last_msg_id_col.exist ? last_msg_id_col.value : -1}; | ||||||
|         } |         } | ||||||
|         een9_THROW("No such chat"); |         een9_THROW("No such chat"); | ||||||
| @ -73,39 +102,21 @@ namespace iu9cawebchat { | |||||||
| 
 | 
 | ||||||
|     RowMessage_Content lookup_message_content(SqliteConnection& conn, int64_t chatId, int64_t msgId) { |     RowMessage_Content lookup_message_content(SqliteConnection& conn, int64_t chatId, int64_t msgId) { | ||||||
|         SqliteStatement req(conn, |         SqliteStatement req(conn, | ||||||
|             "SELECT `previousId`, `senderUserId`, `exists`, `isSystem`, `text` FROM `message` WHERE " |             "SELECT `senderUserId`, `exists`, `isSystem`, `text` FROM `message` WHERE " | ||||||
|             "`chatId` = ?1 AND `id` = ?2", {{1, chatId}, {2, msgId}}, {}); |             "`chatId` = ?1 AND `id` = ?2", {{1, chatId}, {2, msgId}}, {}); | ||||||
|         fsql_integer_or_null previousId, senderUserId, exists, isSystem; |         fsql_integer_or_null senderUserId, exists, isSystem; | ||||||
|         fsql_text8_or_null msg_text; |         fsql_text8_or_null msg_text; | ||||||
|         int status = sqlite_stmt_step(req, {{0, &previousId}, {1, &senderUserId}, {2, &exists}, {3, &isSystem}}, |         int status = sqlite_stmt_step(req, {{0, &senderUserId}, {1, &exists}, {2, &isSystem}}, | ||||||
|             {{4, &msg_text}}); |             {{3, &msg_text}}); | ||||||
|         if (status == SQLITE_ROW) { |         if (status == SQLITE_ROW) { | ||||||
|             if (!(bool)exists.value) |             if (!(bool)exists.value) | ||||||
|                 een9_THROW("Message existed, but now it isn't"); |                 een9_THROW("Message existed, but now it does not"); | ||||||
|             return {(bool)isSystem.value, msg_text.value, senderUserId.exist ? senderUserId.value : -1, |             return {msgId, senderUserId.exist ? senderUserId.value : -1, (bool)exists.value, | ||||||
|             previousId.exist ? previousId.value : -1}; |                 (bool)isSystem.value, msg_text.value}; | ||||||
|         } |         } | ||||||
|         een9_THROW("No such message"); |         een9_THROW("No such message"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::pair<int64_t, RowMessage_Content> lookup_message_content_rev_side(SqliteConnection& conn, int64_t chatId, int64_t prevMsgId) { |  | ||||||
|         een9_ASSERT(prevMsgId >= 0, "V durku dobro pozhalovat"); |  | ||||||
|         SqliteStatement req(conn, |  | ||||||
|             "SELECT `id`, `senderUserId`, `exists`, `isSystem`, `text` FROM `message` WHERE " |  | ||||||
|             "`chatId` = ?1 AND `previous` = ?2", {{1, chatId}, {2, prevMsgId}}, {}); |  | ||||||
|         fsql_integer_or_null id, senderUserId, exists, isSystem; |  | ||||||
|         fsql_text8_or_null msg_text; |  | ||||||
|         int status = sqlite_stmt_step(req, {{0, &id}, {1, &senderUserId}, {2, &exists}, {3, &isSystem}}, |  | ||||||
|             {{4, &msg_text}}); |  | ||||||
|         if (status == SQLITE_ROW) { |  | ||||||
|             een9_ASSERT_pl(exists.value == 1); |  | ||||||
|             return {id.value, |  | ||||||
|                 {(bool)isSystem.value, msg_text.value, senderUserId.exist ? senderUserId.value : -1, prevMsgId}}; |  | ||||||
|         } |  | ||||||
|         return {-1, {}}; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     int64_t get_role_of_user_in_chat(SqliteConnection& conn, int64_t userId, int64_t chatId) { |     int64_t get_role_of_user_in_chat(SqliteConnection& conn, int64_t userId, int64_t chatId) { | ||||||
|         SqliteStatement req(conn, |         SqliteStatement req(conn, | ||||||
|             "SELECT `role` FROM `user_chat_membership` WHERE `userId` = ?1 AND `chatId` = ?2", |             "SELECT `role` FROM `user_chat_membership` WHERE `userId` = ?1 AND `chatId` = ?2", | ||||||
| @ -132,4 +143,103 @@ namespace iu9cawebchat { | |||||||
| 
 | 
 | ||||||
|     /* All the api calls processing is done in dedicated files.
 |     /* All the api calls processing is done in dedicated files.
 | ||||||
|      * All functions related to polling are defined in api_pollevents.cpp */ |      * All functions related to polling are defined in api_pollevents.cpp */ | ||||||
|  | 
 | ||||||
|  |     // todo: move everything to dedicated files
 | ||||||
|  |     int64_t get_current_history_id_of_chat(SqliteConnection& conn, int64_t chatId) { | ||||||
|  |         SqliteStatement req(conn, "SELECT `it_HistoryId` FROM `chat` WHERE `id` = ?1", {{1, chatId}}, {}); | ||||||
|  |         fsql_integer_or_null HistoryId; | ||||||
|  |         int status = sqlite_stmt_step(req, {{0, &HistoryId}}, {}); | ||||||
|  |         een9_ASSERT_pl(status == SQLITE_ROW); | ||||||
|  |         return HistoryId.value; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     int64_t get_current_history_id_of_user_chatList(SqliteConnection& conn, int64_t userId) { | ||||||
|  |         SqliteStatement req(conn, "SELECT `chatList_HistoryId` FROM `user` WHERE `id` = ?1", {{1, userId}}, {}); | ||||||
|  |         fsql_integer_or_null HistoryId; | ||||||
|  |         int status = sqlite_stmt_step(req, {{0, &HistoryId}}, {}); | ||||||
|  |         een9_ASSERT_pl(status == SQLITE_ROW); | ||||||
|  |         return HistoryId.value; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     // todo: extract useful clues from deprecated code.
 | ||||||
|  |     // todo: deprecated code goes here:
 | ||||||
|  |     /* !!! DEPRECATED FUNCTION */ | ||||||
|  |     json::JSON toremoveinternalapi_getChatList(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { | ||||||
|  |         json::JSON Recv; | ||||||
|  |         Recv["status"] = json::JSON(0l); | ||||||
|  |         Recv["chats"] = json::JSON(json::array); | ||||||
|  |         std::vector<json::JSON>& chats = Recv["chats"].asArray(); | ||||||
|  |         SqliteStatement req(conn, | ||||||
|  |             "SELECT `chat`.`id`, `chat`.`nickname`, `chat`.`name`, `chat`.`lastMsgId`, " | ||||||
|  |             "`user_chat_membership`.`role` FROM `chat` " | ||||||
|  |             "RIGHT JOIN `user_chat_membership` ON `chat`.`id` = `user_chat_membership`.`chatId` " | ||||||
|  |             "WHERE `user_chat_membership`.`userId` = ?1 ", {{1, uid}}, {}); | ||||||
|  |         while (true) { | ||||||
|  |             fsql_integer_or_null chat_id; | ||||||
|  |             fsql_text8_or_null chat_nickname, chat_name; | ||||||
|  |             fsql_integer_or_null chat_lastMsgId, role_here; | ||||||
|  |             int status = sqlite_stmt_step(req, {{0, &chat_id}, {3, &chat_lastMsgId}, {4, &role_here}}, | ||||||
|  |                 {{1, &chat_nickname}, {2, &chat_name}}); | ||||||
|  |             if (status != SQLITE_ROW) | ||||||
|  |                 break; | ||||||
|  |             chats.emplace_back(); | ||||||
|  |             json::JSON& chat = chats.back(); | ||||||
|  |             chat["id"] = json::JSON(chat_id.value); | ||||||
|  |             chat["content"]["nickname"] = json::JSON(chat_nickname.value); | ||||||
|  |             chat["content"]["name"] = json::JSON(chat_name.value); | ||||||
|  |             chat["content"]["lastMsgId"] = json::JSON(chat_lastMsgId.exist ? chat_lastMsgId.value : -1); | ||||||
|  |             chat["content"]["roleHere"] = json::JSON(stringify_user_chat_role(role_here.value)); | ||||||
|  |         } | ||||||
|  |         return Recv; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /* !!! DEPRECATED FUNCTION */ | ||||||
|  |     json::JSON toremoveinternalapi_getChatMemberList(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) { | ||||||
|  |         int64_t chatId = Sent["id"].asInteger().get_int(); | ||||||
|  |         int64_t my_role_here = get_role_of_user_in_chat(conn, uid, chatId); | ||||||
|  |         if (my_role_here == user_chat_role_deleted) | ||||||
|  |             een9_THROW("Unauthorized user tries to access internalapi_getChatInfo"); | ||||||
|  |         json::JSON Recv; | ||||||
|  |         Recv["status"] = json::JSON(0l); | ||||||
|  |         Recv["members"] = json::JSON(json::array); | ||||||
|  |         std::vector<json::JSON>& members = Recv["members"].asArray(); | ||||||
|  |         SqliteStatement req(conn, | ||||||
|  |             "SELECT `user`.`id`, `user`.`nickname`, `user`.`name`, `user_chat_membership`.`role` FROM " | ||||||
|  |             "`user` RIGHT JOIN `user_chat_membership` ON `user`.`id` = `user_chat_membership`.`userId` " | ||||||
|  |             "WHERE `user_chat_membership`.`chatId` = ?1", | ||||||
|  |             {{1, chatId}}, {}); | ||||||
|  |         while (true) { | ||||||
|  |             fsql_integer_or_null this_user_id; | ||||||
|  |             fsql_text8_or_null this_user_nickname, this_user_name; | ||||||
|  |             fsql_integer_or_null this_users_role; | ||||||
|  |             int status = sqlite_stmt_step(req, {{0, &this_user_id}, {3, &this_users_role}}, | ||||||
|  |                 {{1, &this_user_nickname}, {2, &this_user_name}}); | ||||||
|  |             if (status != SQLITE_ROW) | ||||||
|  |                 break; | ||||||
|  |             members.emplace_back(); | ||||||
|  |             json::JSON& member = members.back(); | ||||||
|  |             member["id"] = json::JSON(this_user_id.value); | ||||||
|  |             member["content"]["nickname"] = json::JSON(this_user_nickname.value); | ||||||
|  |             member["content"]["name"] = json::JSON(this_user_name.value); | ||||||
|  |             member["content"]["role"] = json::JSON(this_users_role.value); | ||||||
|  |         } | ||||||
|  |         return Recv; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool is_nickname_taken(SqliteConnection& conn, const std::string& nickname) { | ||||||
|  |         if (!check_nickname(nickname)) | ||||||
|  |             return true; | ||||||
|  |         SqliteStatement req(conn, "SELECT EXISTS(SELECT 1 FROM `nickname` WHERE `it` = ?1)", | ||||||
|  |             {}, {{1, nickname}}); | ||||||
|  |         fsql_integer_or_null r{true, 0}; | ||||||
|  |         int status = sqlite_stmt_step(req, {{0, &r}}, {}); | ||||||
|  |         return r.value; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void reserve_nickname(SqliteConnection& conn, const std::string& nickname) { | ||||||
|  |         if (!check_nickname(nickname)) | ||||||
|  |             een9_THROW("PRECAUTION! Trying to insert incorrect nickname into nickname table"); | ||||||
|  |         sqlite_nooutput(conn, "INSERT INTO `nickname` (`it`) VALUES (?1)", {}, {{1, nickname}}); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -16,62 +16,70 @@ namespace iu9cawebchat { | |||||||
|     const char* stringify_user_chat_role(int64_t role); |     const char* stringify_user_chat_role(int64_t role); | ||||||
| 
 | 
 | ||||||
|     int64_t find_user_by_credentials (SqliteConnection& conn, const std::string& nickname, const std::string& password); |     int64_t find_user_by_credentials (SqliteConnection& conn, const std::string& nickname, const std::string& password); | ||||||
|     std::string find_user_name (SqliteConnection& conn, int64_t uid); |     std::string get_user_name (SqliteConnection& conn, int64_t uid); | ||||||
| 
 | 
 | ||||||
|     struct RowUser_Content { |     struct RowUser_Content { | ||||||
|  |         int64_t id; | ||||||
|         std::string nickname; |         std::string nickname; | ||||||
|         std::string name; |         std::string name; | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     struct RowChat_Content { |     struct RowChat_Content { | ||||||
|  |         int64_t id; | ||||||
|         std::string nickname; |         std::string nickname; | ||||||
|         std::string name; |         std::string name; | ||||||
|         int64_t lastMsgId;  // Negative if it does not exist
 |         int64_t lastMsgId;  // Negative if it does not exist
 | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     RowUser_Content lookup_user_content(SqliteConnection& conn, int64_t uid); |     RowUser_Content lookup_user_content(SqliteConnection& conn, int64_t uid); | ||||||
|  |     RowUser_Content lookup_user_content_by_nickname(SqliteConnection& conn, const std::string& nickname); | ||||||
|     /* Does not make authorization check */ |     /* Does not make authorization check */ | ||||||
|     RowChat_Content lookup_chat_content(SqliteConnection& conn, int64_t chatId); |     RowChat_Content lookup_chat_content(SqliteConnection& conn, int64_t chatId); | ||||||
|  |     RowChat_Content lookup_chat_content_by_nickname(SqliteConnection &conn, const std::string& nickname); | ||||||
| 
 | 
 | ||||||
|     struct RowMessage_Content { |     struct RowMessage_Content { | ||||||
|  |         int64_t id; | ||||||
|  |         int64_t senderUserId; | ||||||
|  |         bool exists; | ||||||
|         bool isSystem; |         bool isSystem; | ||||||
|         std::string text; |         std::string text; | ||||||
|         int64_t senderUserId; |  | ||||||
|         int64_t previous = -1; |  | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     RowMessage_Content lookup_message_content(SqliteConnection& conn, int64_t chatId, int64_t msgId); |     RowMessage_Content lookup_message_content(SqliteConnection& conn, int64_t chatId, int64_t msgId); | ||||||
| 
 | 
 | ||||||
|     /* If prevMsgId is id of the last message in chat, and there is no message ahead of it, .first = -1 */ |  | ||||||
|     std::pair<int64_t, RowMessage_Content> lookup_message_content_rev_side(SqliteConnection& conn, int64_t chatId, int64_t prevMsgId); |  | ||||||
| 
 |  | ||||||
|     /* Does not make authorization check */ |     /* Does not make authorization check */ | ||||||
|     int64_t get_role_of_user_in_chat(SqliteConnection& conn, int64_t userId, int64_t chatId); |     int64_t get_role_of_user_in_chat(SqliteConnection& conn, int64_t userId, int64_t chatId); | ||||||
|     /* Does not make authorization check */ |     /* Does not make authorization check */ | ||||||
|     int64_t get_lastMsgId_of_chat(SqliteConnection& conn, int64_t chatId); |     int64_t get_lastMsgId_of_chat(SqliteConnection& conn, int64_t chatId); | ||||||
| 
 | 
 | ||||||
|     /* ============================= API ====================================*/ |  | ||||||
|     int64_t get_current_history_id_of_chat(SqliteConnection& conn, int64_t chatId); |     int64_t get_current_history_id_of_chat(SqliteConnection& conn, int64_t chatId); | ||||||
|     int64_t get_current_history_id_of_user_chatList(SqliteConnection& conn, int64_t userId); |     int64_t get_current_history_id_of_user_chatList(SqliteConnection& conn, int64_t userId); | ||||||
|     /* Returns HistoryId of chat (latest) */ | 
 | ||||||
|     int64_t event_polling_fill_chat_hist_entity_response(SqliteConnection& conn, json::JSON& hist_entity_response, |     json::JSON poll_update_chat_list_resp(SqliteConnection& conn, int64_t userId, int64_t LocalHistoryId); | ||||||
|             int64_t uid, int64_t chatId, int64_t LocalHistoryId); |     void poll_update_chat_list(SqliteConnection& conn, int64_t userId, const json::JSON& Sent, json::JSON& Recv); | ||||||
|     /* Returns HistoryId of chat list (latest) */ | 
 | ||||||
|     int64_t event_polling_fill_chatlist_hist_entity_response(SqliteConnection& conn, json::JSON& hist_entity_response, |     json::JSON poll_update_chat_ONE_MSG_resp(SqliteConnection& conn, int64_t chatId, int64_t selectedMsg); | ||||||
|             int64_t uid, int64_t LocalHistoryId); |     void poll_update_chat(SqliteConnection& conn, const json::JSON& Sent, json::JSON& Recv); | ||||||
|     json::JSON internalapi_pollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); | 
 | ||||||
|     json::JSON internalapi_getChatList(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); |     void make_her_a_member_of_the_midnight_crew(SqliteConnection& conn, int64_t chatId, int64_t alienUserId, int64_t role); | ||||||
|     json::JSON internalapi_getChatInfo(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); |     void kick_from_chat(SqliteConnection& conn, int64_t alienUserId, int64_t chatId); | ||||||
|     json::JSON internalapi_getChatMemberList(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); | 
 | ||||||
|     json::JSON internalapi_getUserInfo(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); |     bool is_nickname_taken(SqliteConnection& conn, const std::string& nickname); | ||||||
|     json::JSON internalapi_getMessageInfo(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); |     void reserve_nickname(SqliteConnection& conn, const std::string& nickname); | ||||||
|  | 
 | ||||||
|  |     /* ============================= API ==================================== */ | ||||||
|  |     json::JSON internalapi_chatPollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); | ||||||
|  |     json::JSON internalapi_chatListPollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); | ||||||
|     json::JSON internalapi_getMessageNeighbours(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); |     json::JSON internalapi_getMessageNeighbours(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); | ||||||
|     json::JSON internalapi_sendMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); |     json::JSON internalapi_sendMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); | ||||||
|     json::JSON internalapi_deleteMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); |     json::JSON internalapi_deleteMessage(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); | ||||||
|     json::JSON internalapi_addMemberToChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); |     json::JSON internalapi_addMemberToChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); | ||||||
|     json::JSON internalapi_removeMemberFromChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); |     json::JSON internalapi_removeMemberFromChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); | ||||||
|     json::JSON internalapi_createChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); |     json::JSON internalapi_createChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); | ||||||
|     json::JSON internalapi_removeChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); |     json::JSON internalapi_leaveChat(SqliteConnection& conn, int64_t uid, const json::JSON& Sent); | ||||||
|  | 
 | ||||||
|  |     /**/ | ||||||
|  |     std::string admin_control_procedure(SqliteConnection& conn, const std::string& req, bool& termination); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -1,8 +1,54 @@ | |||||||
| #include "client_server_interact.h" | #include "client_server_interact.h" | ||||||
| 
 | 
 | ||||||
|  | #include <engine_engine_number_9/baza_throw.h> | ||||||
|  | #include "../str_fields.h" | ||||||
|  | 
 | ||||||
| namespace iu9cawebchat { | namespace iu9cawebchat { | ||||||
|     std::string when_page_chat(WorkerGuestData& wgd, const json::JSON& config_presentation, |     std::string when_page_chat(WorkerGuestData& wgd, const json::JSON& config_presentation, | ||||||
|             const een9::ClientRequest& req, const json::JSON& userinfo) { |         const een9::ClientRequest& req, const json::JSON& userinfo) { | ||||||
|         return R200_pres_uinfo("chat", config_presentation, wgd, userinfo); | 
 | ||||||
|  |         std::vector<std::string> path_segs = {""}; | ||||||
|  |         path_segs.reserve(4); | ||||||
|  |         for (char ch: req.uri_path) { | ||||||
|  |             if (ch == '/') | ||||||
|  |                 path_segs.emplace_back(); | ||||||
|  |             else | ||||||
|  |                 path_segs.back() += ch; | ||||||
|  |         } | ||||||
|  |         // Parameter, passed from server to javascript
 | ||||||
|  |         std::string chat_nickname; | ||||||
|  |         int64_t selected_message_id = -1; | ||||||
|  |         if (path_segs.size() >= 2) { | ||||||
|  |             chat_nickname = std::move(path_segs[1]); | ||||||
|  |         } | ||||||
|  |         if (!check_nickname(chat_nickname)) | ||||||
|  |             return page_E404(wgd); | ||||||
|  |         if (path_segs.size() == 2) { | ||||||
|  |         } else if (path_segs.size() == 4) { | ||||||
|  |             if (path_segs[2] != "m") | ||||||
|  |                 return page_E404(wgd); | ||||||
|  |             selected_message_id = std::stoll(path_segs[3]); | ||||||
|  |             return page_E404(wgd); | ||||||
|  |         } else | ||||||
|  |             return page_E404(wgd); | ||||||
|  | 
 | ||||||
|  |         bool chat_members = (path_segs[0] == "chat-members"); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         // todo: do not throw id chat not found, catch exception and return 404
 | ||||||
|  |         json::JSON openedchat; | ||||||
|  |         RowChat_Content chatInfo = lookup_chat_content_by_nickname(*wgd.db, chat_nickname); | ||||||
|  |         if (get_role_of_user_in_chat(*wgd.db, userinfo["id"].asInteger().get_int(), chatInfo.id) == user_chat_role_deleted) { | ||||||
|  |             return page_E404(wgd); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         openedchat["name"] = json::JSON(chatInfo.name); | ||||||
|  |         openedchat["nickname"] = json::JSON(chatInfo.nickname); | ||||||
|  |         openedchat["id"] = json::JSON(chatInfo.id); | ||||||
|  |         openedchat["selectedMessageId"] = json::JSON(selected_message_id);  // -1 means that nothing was selected
 | ||||||
|  |         json::JSON initial_chatUpdResp = poll_update_chat_ONE_MSG_resp(*wgd.db, chatInfo.id, selected_message_id); | ||||||
|  |         if (chat_members) | ||||||
|  |             return http_R200("chat", wgd, {&config_presentation, &userinfo, &openedchat, &initial_chatUpdResp}); | ||||||
|  |         return http_R200("chat", wgd, {&config_presentation, &userinfo, &openedchat, &initial_chatUpdResp}); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,6 +6,8 @@ namespace iu9cawebchat { | |||||||
|         if (userinfo.isNull()) { |         if (userinfo.isNull()) { | ||||||
|             return een9::form_http_server_response_303("/login"); |             return een9::form_http_server_response_303("/login"); | ||||||
|         } |         } | ||||||
|         return R200_pres_uinfo("list-rooms", config_presentation, wgd, userinfo); |         json::JSON initial_chatListUpdResp = poll_update_chat_list_resp(*wgd.db, | ||||||
|  |             userinfo["uid"].asInteger().get_int(), 0); | ||||||
|  |         return http_R200("list-rooms", wgd, {&config_presentation, &userinfo, &initial_chatListUpdResp}); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -25,15 +25,18 @@ namespace iu9cawebchat { | |||||||
|             } catch(const std::exception& e){} |             } catch(const std::exception& e){} | ||||||
|             if (uid < 0) { |             if (uid < 0) { | ||||||
|                 printf("Redirecting back to /login because of incorrect credentials\n"); |                 printf("Redirecting back to /login because of incorrect credentials\n"); | ||||||
|                 // class will be successfully ignored
 |                 json::JSON msg_list = jsonify_html_message_list({{"", | ||||||
|                 return R200_pres_uinfo_msges("login", wgd, config_presentation, userinfo, {{"", |                     config_presentation["phr"]["decl"]["incorrect-nickname-or-password"].asString()}}); | ||||||
|                     config_presentation["phr"]["decl"]["incorrect-nickname-or-password"].g().asString()}}); |                 return http_R200("login", wgd, {&config_presentation, &userinfo, &msg_list}); | ||||||
|             } |             } | ||||||
|             std::vector<std::pair<std::string, std::string>> response_hlines; |             std::vector<std::pair<std::string, std::string>> response_hlines; | ||||||
|             LoginCookie new_login_cookie = create_login_cookie(nickname, password); |             LoginCookie new_login_cookie = create_login_cookie(nickname, password); | ||||||
|             add_set_cookie_headers_to_login(login_cookies, response_hlines, new_login_cookie); |             add_set_cookie_headers_to_login(login_cookies, response_hlines, new_login_cookie); | ||||||
|             return een9::form_http_server_response_303_spec_head("/", response_hlines); |             return een9::form_http_server_response_303_spec_head("/", response_hlines); | ||||||
|         } |         } | ||||||
|         return R200_pres_uinfo_msges("login", wgd, config_presentation, userinfo, {}); |         if (req.method != "GET") | ||||||
|  |             een9_THROW("Bad method"); | ||||||
|  |         json::JSON empty_msg_list = json::JSON(json::array); | ||||||
|  |         return http_R200("login", wgd, {&config_presentation, &userinfo, &empty_msg_list}); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,8 +1,111 @@ | |||||||
| #include "client_server_interact.h" | #include "client_server_interact.h" | ||||||
|  | #include <engine_engine_number_9/form_data_structure/urlencoded_query.h> | ||||||
|  | #include <engine_engine_number_9/baza_throw.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include "../str_fields.h" | ||||||
|  | #include "../login_cookie.h" | ||||||
| 
 | 
 | ||||||
| namespace iu9cawebchat { | namespace iu9cawebchat { | ||||||
|  |     std::string get_user_bio(SqliteConnection& conn, int64_t userId) { | ||||||
|  |         een9_ASSERT(userId >= 0, "Are you crazy?"); | ||||||
|  |         SqliteStatement sql_req(conn, "SELECT `bio` FROM `user` WHERE `id` = ?1", {{1, userId}}, {}); | ||||||
|  |         fsql_text8_or_null bio_col; | ||||||
|  |         int status = sqlite_stmt_step(sql_req, {}, {{0, &bio_col}}); | ||||||
|  |         if (status == SQLITE_ROW) | ||||||
|  |             return bio_col.value; | ||||||
|  |         een9_THROW("No such user"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     static const char* pr_path = "/user/"; | ||||||
|  | 
 | ||||||
|  |     bool is_page_of_certain_user(const std::string& path) { | ||||||
|  |         if (!een9::beginsWith(path, pr_path)) | ||||||
|  |             return false; | ||||||
|  |         std::string r = path.substr(strlen(pr_path)); | ||||||
|  |         return check_nickname(r); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::string obtain_nickname_in_user_page_path(const std::string& path) { | ||||||
|  |         return path.substr(strlen(pr_path)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     json::JSON user_row_to_userprofile_obj(SqliteConnection& conn, const RowUser_Content& alien) { | ||||||
|  |         return json::JSON(json::jdict{ | ||||||
|  |             {"name", json::JSON(alien.name)}, | ||||||
|  |             {"nickname", json::JSON(alien.nickname)}, | ||||||
|  |             {"bio", json::JSON(get_user_bio(conn, alien.id))} | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     std::string when_page_user(WorkerGuestData& wgd, const json::JSON& config_presentation, |     std::string when_page_user(WorkerGuestData& wgd, const json::JSON& config_presentation, | ||||||
|                 const een9::ClientRequest& req, const json::JSON& userinfo) { |                 const een9::ClientRequest& req, const std::vector<LoginCookie>& login_cookies, const json::JSON& userinfo) { | ||||||
|         return R200_pres_uinfo("profile", config_presentation, wgd, userinfo); |         SqliteConnection& conn = *wgd.db; | ||||||
|  |         if (!is_page_of_certain_user(req.uri_path)) | ||||||
|  |             return page_E404(wgd); | ||||||
|  |         std::string alien_nickname = obtain_nickname_in_user_page_path(req.uri_path); | ||||||
|  |         RowUser_Content alien; | ||||||
|  |         try { | ||||||
|  |             alien = lookup_user_content_by_nickname(conn, alien_nickname); | ||||||
|  |         } catch (const std::exception& e) { | ||||||
|  |             return page_E404(wgd); | ||||||
|  |         } | ||||||
|  |         // todo: in libjsonincpp: fix '999999 problem'
 | ||||||
|  |         int64_t myuid = userinfo["uid"].asInteger().get_int(); | ||||||
|  |         bool can_edit = (alien.id == myuid); | ||||||
|  |         if (req.method == "POST") { | ||||||
|  |             std::vector<std::pair<std::string, std::string>> response_hlines; | ||||||
|  |             try { | ||||||
|  |                 if (!can_edit) | ||||||
|  |                     een9_THROW("Unauthorized access"); | ||||||
|  |                 std::vector<std::pair<std::string, std::string>> query = een9::split_html_query(req.body); | ||||||
|  |                 // Profile update processing
 | ||||||
|  |                 std::string bio; | ||||||
|  |                 std::string name; | ||||||
|  |                 std::string password; | ||||||
|  |                 for (const std::pair<std::string, std::string>& p: query) { | ||||||
|  |                     if (p.first == "bio") | ||||||
|  |                         bio = p.second; | ||||||
|  |                     else if (p.first == "name") | ||||||
|  |                         name = p.second; | ||||||
|  |                     else if (p.first == "password") | ||||||
|  |                         password = p.second; | ||||||
|  |                 } | ||||||
|  |                 if (!bio.empty()) { | ||||||
|  |                     if (!is_orthodox_string(bio) || bio.size() > 100000) | ||||||
|  |                         een9_THROW("Incorrect `bio`"); | ||||||
|  |                     sqlite_nooutput(conn, "UPDATE `user` SET `bio` = ?1", {}, {{1, bio}}); | ||||||
|  |                 } | ||||||
|  |                 if (!name.empty()) { | ||||||
|  |                     if (!check_name(name)) | ||||||
|  |                         een9_THROW("Incorrect `name`"); | ||||||
|  |                     sqlite_nooutput(conn, "UPDATE `user` SET `name` = ?1", {}, {{1, name}}); | ||||||
|  |                 } | ||||||
|  |                 if (!password.empty()) { | ||||||
|  |                     if (!check_strong_password(password)) | ||||||
|  |                         een9_THROW("Incorrect `password`"); | ||||||
|  |                     sqlite_nooutput(conn, "UPDATE `user` SET `password` = ?1", {}, {{1, password}}); | ||||||
|  |                     if (alien.id == myuid) { | ||||||
|  |                         LoginCookie new_login_cookie = create_login_cookie(userinfo["nickname"].asString(), password); | ||||||
|  |                         add_set_cookie_headers_to_login(login_cookies, response_hlines, new_login_cookie); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } catch (const std::exception& e) { | ||||||
|  |                 printf("Redirecting back to /user/... because of incorrect credentials\n"); | ||||||
|  |                 json::JSON msg_list = jsonify_html_message_list({{"", | ||||||
|  |                     config_presentation["phr"]["decl"]["incorrect-profile-data"].asString()}}); | ||||||
|  |                 json::JSON alien_userprofile = user_row_to_userprofile_obj(conn, alien); | ||||||
|  |                 return http_R200("edit-profile", wgd, {&config_presentation, &alien_userprofile, &msg_list}); | ||||||
|  |             } | ||||||
|  |             return een9::form_http_server_response_303_spec_head("/user/" + alien_nickname, response_hlines); | ||||||
|  |         } | ||||||
|  |         if (req.method == "GET") { | ||||||
|  |             json::JSON alien_userprofile = user_row_to_userprofile_obj(conn, alien); | ||||||
|  |             if (can_edit) { | ||||||
|  |                 json::JSON empty_msg_list = jsonify_html_message_list({}); | ||||||
|  |                 return http_R200("edit-profile", wgd, {&config_presentation, &alien_userprofile, &empty_msg_list}); | ||||||
|  |             } | ||||||
|  |             return http_R200("view-profile", wgd, {&config_presentation, &alien_userprofile}); | ||||||
|  |         } | ||||||
|  |         een9_THROW("Bad method"); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,15 +2,17 @@ | |||||||
| 
 | 
 | ||||||
| namespace iu9cawebchat{ | namespace iu9cawebchat{ | ||||||
|     int find_db_sqlite_file_path(const json::JSON& config, std::string& res_path) { |     int find_db_sqlite_file_path(const json::JSON& config, std::string& res_path) { | ||||||
|         const json::JSON& type = config["database"]["type"].g(); |         try { | ||||||
|         if (!type.isString() && type.asString() == "sqlite3") |             const std::string& type = config["database"]["type"].asString(); | ||||||
|  |             if (type != "sqlite3") | ||||||
|  |                 return -1; | ||||||
|  |             const std::string& path = config["database"]["file"].asString(); | ||||||
|  |             if (path.empty() || path[0] == ':') | ||||||
|  |                 return -1; | ||||||
|  |             res_path = path; | ||||||
|  |         } catch (const json::misuse& e) { | ||||||
|             return -1; |             return -1; | ||||||
|         const json::JSON& path = config["database"]["file"].g(); |         } | ||||||
|         if (!path.isString()) |  | ||||||
|             return -1; |  | ||||||
|         if (path.asString().empty() || path.asString()[0] == ':') |  | ||||||
|             return -1; |  | ||||||
|         res_path = path.asString(); |  | ||||||
|         return 0; |         return 0; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -44,36 +44,38 @@ namespace iu9cawebchat { | |||||||
|                                              "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL," |                                              "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL," | ||||||
|                                              "`name` TEXT NOT NULL," |                                              "`name` TEXT NOT NULL," | ||||||
|                                              "`chatList_HistoryId` INTEGER NOT NULL," |                                              "`chatList_HistoryId` INTEGER NOT NULL," | ||||||
|                                              "`password` TEXT NOT NULL" |                                              "`password` TEXT NOT NULL," | ||||||
|  |                                              "`bio` TEXT NOT NULL" | ||||||
|                                              ")"); |                                              ")"); | ||||||
|             sqlite_nooutput(conn, "CREATE TABLE `chat` (" |             sqlite_nooutput(conn, "CREATE TABLE `chat` (" | ||||||
|                                              "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," |                                              "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," | ||||||
|                                              "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL," |                                              "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL," | ||||||
|                                              "`name` TEXT NOT NULL," |                                              "`name` TEXT NOT NULL," | ||||||
|                                              "`it_HistoryId` INTEGER NOT NULL," |                                              "`it_HistoryId` INTEGER NOT NULL," | ||||||
|                                              "`lastMsgId` INTEGER REFERENCES `message`" |                                              "`lastMsgId` INTEGER NOT NULL" | ||||||
|                                              ")"); |                                              ")"); | ||||||
|             sqlite_nooutput(conn, "CREATE TABLE `user_chat_membership` (" |             sqlite_nooutput(conn, "CREATE TABLE `user_chat_membership` (" | ||||||
|                                              "`userId` INTEGER REFERENCES `user` NOT NULL," |                                              "`userId` INTEGER REFERENCES `user` NOT NULL," | ||||||
|                                              "`chatId` INTEGER REFERENCES `chat` NOT NULL," |                                              "`chatId` INTEGER REFERENCES `chat` NOT NULL," | ||||||
|                                              "`user_chatList_IncHistoryId` INTEGER NOT NULL," |                                              "`user_chatList_IncHistoryId` INTEGER NOT NULL," | ||||||
|                                              "`chat_IncHistoryId` INTEGER NOT NULL," |                                              "`chat_IncHistoryId` INTEGER NOT NULL," | ||||||
|                                              "`role` INTEGER NOT NULL" |                                              "`role` INTEGER NOT NULL," | ||||||
|  |                                              "UNIQUE (`userId`, `chatId`)" | ||||||
|                                              ")"); |                                              ")"); | ||||||
|             sqlite_nooutput(conn, "CREATE TABLE `message` (" |             sqlite_nooutput(conn, "CREATE TABLE `message` (" | ||||||
|                                              "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," |  | ||||||
|                                              "`chatId` INTEGER REFERENCES `chat` NOT NULL," |                                              "`chatId` INTEGER REFERENCES `chat` NOT NULL," | ||||||
|                                              "`previous` INTEGER REFERENCES `message`," |                                              "`id` INTEGER NOT NULL," | ||||||
|                                              "`senderUserId` INTEGER REFERENCES `user`," |                                              "`senderUserId` INTEGER REFERENCES `user`," | ||||||
|                                              "`exists` BOOLEAN NOT NULL," |                                              "`exists` BOOLEAN NOT NULL," | ||||||
|                                              "`isSystem` BOOLEAN NOT NULL," |                                              "`isSystem` BOOLEAN NOT NULL," | ||||||
|                                              "`text` TEXT NOT NULL," |                                              "`text` TEXT," | ||||||
|                                              "`chat_IncHistoryId` INTEGER NOT NULL" |                                              "`chat_IncHistoryId` INTEGER NOT NULL," | ||||||
|  |                                              "PRIMARY KEY (`chatId`, `id`)" | ||||||
|                                              ")"); |                                              ")"); | ||||||
|             sqlite_nooutput(conn, "INSERT INTO `nickname` VALUES (?1)", {}, {{1, "root"}}); |             sqlite_nooutput(conn, "INSERT INTO `nickname` VALUES (?1)", {}, {{1, "root"}}); | ||||||
|             sqlite_nooutput(conn, "INSERT INTO `user` (`id`, `nickname`, `name`, `chatList_HistoryId`, `password`) VALUES " |             sqlite_nooutput(conn, "INSERT INTO `user` (`id`, `nickname`, `name`, `chatList_HistoryId`, `password`, " | ||||||
|                                              "(0, ?1, ?2, 0, ?3)", {}, |                                   "`bio` ) VALUES (0, ?1, ?2, 0, ?3, ?4)", {}, | ||||||
|                                              {{1, "root"}, {2, "Rootov Root Rootovich"}, {3, root_pw}}); |                     {{1, "root"}, {2, "Rootov Root Rootovich"}, {3, root_pw}, {4, "One admin to rule them all"}}); | ||||||
|             sqlite_nooutput(conn, "END"); |             sqlite_nooutput(conn, "END"); | ||||||
|         } catch (const std::exception& e) { |         } catch (const std::exception& e) { | ||||||
|             sqlite_nooutput(conn, "ROLLBACK", {}, {}); |             sqlite_nooutput(conn, "ROLLBACK", {}, {}); | ||||||
|  | |||||||
| @ -23,8 +23,8 @@ namespace iu9cawebchat { | |||||||
|         uint64_t nsec = std::stoull(ft.substr(s_ + 1, ft.size() - s_ - 1)); |         uint64_t nsec = std::stoull(ft.substr(s_ + 1, ft.size() - s_ - 1)); | ||||||
|         een9_ASSERT_pl(nsec < 1000000000); |         een9_ASSERT_pl(nsec < 1000000000); | ||||||
|         const json::JSON cnt = json::parse_str_flawless(base64_decode(login_cookie_encoded.second)); |         const json::JSON cnt = json::parse_str_flawless(base64_decode(login_cookie_encoded.second)); | ||||||
|         std::string nickname = cnt[0].g().asString(); |         std::string nickname = cnt[0].asString(); | ||||||
|         std::string password = cnt[1].g().asString(); |         std::string password = cnt[1].asString(); | ||||||
|         return LoginCookie{{(time_t)sec, (time_t)nsec}, nickname, password}; |         return LoginCookie{{(time_t)sec, (time_t)nsec}, nickname, password}; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -37,8 +37,8 @@ namespace iu9cawebchat { | |||||||
| 
 | 
 | ||||||
|     std::pair<std::string, std::string> encode_login_cookie(const LoginCookie& cookie) { |     std::pair<std::string, std::string> encode_login_cookie(const LoginCookie& cookie) { | ||||||
|             json::JSON cnt; |             json::JSON cnt; | ||||||
|             cnt[1].g() = cookie.password; |             cnt[1].asString() = cookie.password; | ||||||
|             cnt[0].g() = cookie.nickname; |             cnt[0].asString() = cookie.nickname; | ||||||
|         return {"login_" + std::to_string(cookie.login_time.tv_sec) + "_" + std::to_string(cookie.login_time.tv_nsec), |         return {"login_" + std::to_string(cookie.login_time.tv_sec) + "_" + std::to_string(cookie.login_time.tv_nsec), | ||||||
|             base64_encode(json::generate_str(cnt, json::print_compact))}; |             base64_encode(json::generate_str(cnt, json::print_compact))}; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -16,9 +16,25 @@ namespace iu9cawebchat { | |||||||
|         termination = true; |         termination = true; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     struct ONE_SQLITE_TRANSACTION_GUARD { | ||||||
|  |         SqliteConnection& conn; | ||||||
|  |         bool rollback = false; | ||||||
|  | 
 | ||||||
|  |         explicit ONE_SQLITE_TRANSACTION_GUARD(SqliteConnection& conn_) : conn(conn_) { | ||||||
|  |             sqlite_nooutput(conn, "BEGIN", {}, {}); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         ~ONE_SQLITE_TRANSACTION_GUARD() { | ||||||
|  |             if (rollback) | ||||||
|  |                 sqlite_nooutput(conn, "ROLLBACK", {}, {}); | ||||||
|  |             else | ||||||
|  |                 sqlite_nooutput(conn, "END", {}, {}); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|     void run_website(const json::JSON& config) { |     void run_website(const json::JSON& config) { | ||||||
|         een9_ASSERT(config["assets"].g().isString(), "config[\"assets\"] is not string"); |         een9_ASSERT(config["assets"].isString(), "config[\"assets\"] is not string"); | ||||||
|         std::string assets_dir = config["assets"].g().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"); | ||||||
| 
 | 
 | ||||||
|         een9::StaticAssetManagerSlaveModule samI; |         een9::StaticAssetManagerSlaveModule samI; | ||||||
| @ -30,8 +46,8 @@ namespace iu9cawebchat { | |||||||
|             } }, |             } }, | ||||||
|         }); |         }); | ||||||
| 
 | 
 | ||||||
|         const json::JSON& config_presentation = config["presentation"].g(); |         const json::JSON& config_presentation = config["presentation"]; | ||||||
|         int64_t slave_number = config["server"]["workers"].g().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"); | ||||||
| 
 | 
 | ||||||
|         std::string sqlite_db_path; |         std::string sqlite_db_path; | ||||||
| @ -52,13 +68,7 @@ namespace iu9cawebchat { | |||||||
|             een9_ASSERT_pl(0 <= worker_id && worker_id < worker_guest_data.size()); |             een9_ASSERT_pl(0 <= worker_id && worker_id < worker_guest_data.size()); | ||||||
|             WorkerGuestData& wgd = worker_guest_data[worker_id]; |             WorkerGuestData& wgd = worker_guest_data[worker_id]; | ||||||
|             een9::StaticAsset sa; |             een9::StaticAsset sa; | ||||||
|             sqlite_nooutput(*wgd.db, "BEGIN", {}, {}); |             ONE_SQLITE_TRANSACTION_GUARD conn_guard(*wgd.db); | ||||||
|             struct guard {SqliteConnection& conn; bool rollback = false; ~guard() { |  | ||||||
|                 if (rollback) |  | ||||||
|                     sqlite_nooutput(conn, "ROLLBACK", {}, {}); |  | ||||||
|                 else |  | ||||||
|                     sqlite_nooutput(conn, "END", {}, {}); |  | ||||||
|                 }} guard_{*wgd.db}; |  | ||||||
|             try { |             try { | ||||||
|                 std::vector<std::pair<std::string, std::string>> cookies; |                 std::vector<std::pair<std::string, std::string>> cookies; | ||||||
|                 std::vector<LoginCookie> login_cookies; |                 std::vector<LoginCookie> login_cookies; | ||||||
| @ -72,53 +82,42 @@ namespace iu9cawebchat { | |||||||
|                 if (req.uri_path == "/login") { |                 if (req.uri_path == "/login") { | ||||||
|                     return when_page_login(wgd, config_presentation, req, login_cookies, userinfo); |                     return when_page_login(wgd, config_presentation, req, login_cookies, userinfo); | ||||||
|                 } |                 } | ||||||
|                 if (een9::beginsWith(req.uri_path, "/chat")) { |                 // todo: split
 | ||||||
|  |                 if (een9::beginsWith(req.uri_path, "/chat/") || een9::beginsWith(req.uri_path, "/chat-members/")) { | ||||||
|                     return when_page_chat(wgd, config_presentation, req, userinfo); |                     return when_page_chat(wgd, config_presentation, req, userinfo); | ||||||
|                 } |                 } | ||||||
|                 if (req.uri_path == "/user") { |                 if (een9::beginsWith(req.uri_path, "/user/")) { | ||||||
|                     return when_page_user(wgd, config_presentation, req, userinfo); |                     return when_page_user(wgd, config_presentation, req, login_cookies, userinfo); | ||||||
|                 } |                 } | ||||||
|                 if (req.uri_path == "/internalapi/pollEvents") { |                 if (req.uri_path == "/api/chatPollEvents") { | ||||||
|                     return when_internalapi_pollevents(wgd, req, logged_in_user); |                     return when_internalapi_chatpollevents(wgd, req, logged_in_user); | ||||||
|                 } |                 } | ||||||
|                 if (req.uri_path == "/internalapi/getChatList") { |                 if (req.uri_path == "/api/chatListPollEvents") { | ||||||
|                     return  when_internalapi_getchatlist(wgd, req, logged_in_user); |                     return  when_internalapi_chatlistpollevents(wgd, req, logged_in_user); | ||||||
|                 } |                 } | ||||||
|                 if (req.uri_path == "/internalapi/getChatInfo") { |                 if (req.uri_path == "/api/getMessageNeighbours") { | ||||||
|                     return when_internalapi_getchatinfo(wgd, req, logged_in_user); |  | ||||||
|                 } |  | ||||||
|                 if (req.uri_path == "/internalapi/getChatMemberList") { |  | ||||||
|                     return when_internalapi_getchatmemberlist(wgd, req, logged_in_user); |  | ||||||
|                 } |  | ||||||
|                 if (req.uri_path == "/internalapi/getUserInfo") { |  | ||||||
|                     return when_internalapi_getuserinfo(wgd, req, logged_in_user); |  | ||||||
|                 } |  | ||||||
|                 if (req.uri_path == "/internalapi/getMessageInfo") { |  | ||||||
|                     return when_internalapi_getmessageinfo(wgd, req, logged_in_user); |  | ||||||
|                 } |  | ||||||
|                 if (req.uri_path == "/internalapi/getMessageNeighbours") { |  | ||||||
|                     return when_internalapi_getmessageneighbours(wgd, req, logged_in_user); |                     return when_internalapi_getmessageneighbours(wgd, req, logged_in_user); | ||||||
|                 } |                 } | ||||||
|                 if (req.uri_path == "/internalapi/sendMessage") { |                 if (req.uri_path == "/api/sendMessage") { | ||||||
|                     return when_internalapi_sendmessage(wgd, req, logged_in_user); |                     return when_internalapi_sendmessage(wgd, req, logged_in_user); | ||||||
|                 } |                 } | ||||||
|                 if (req.uri_path == "/internalapi/deleteMessage") { |                 if (req.uri_path == "/api/deleteMessage") { | ||||||
|                     return when_internalapi_deletemessage(wgd, req, logged_in_user); |                     return when_internalapi_deletemessage(wgd, req, logged_in_user); | ||||||
|                 } |                 } | ||||||
|                 if (req.uri_path == "/internalapi/addMemberToChat") { |                 if (req.uri_path == "/api/addMemberToChat") { | ||||||
|                     return when_internalapi_addmembertochat(wgd, req, logged_in_user); |                     return when_internalapi_addmembertochat(wgd, req, logged_in_user); | ||||||
|                 } |                 } | ||||||
|                 if (req.uri_path == "/internalapi/removeMemberFromChat") { |                 if (req.uri_path == "/api/removeMemberFromChat") { | ||||||
|                     return when_internalapi_removememberfromchat(wgd, req, logged_in_user); |                     return when_internalapi_removememberfromchat(wgd, req, logged_in_user); | ||||||
|                 } |                 } | ||||||
|                 if (req.uri_path == "/internalapi/createChat") { |                 if (req.uri_path == "/api/createChat") { | ||||||
|                     return when_internalapi_createchat(wgd, req, logged_in_user); |                     return when_internalapi_createchat(wgd, req, logged_in_user); | ||||||
|                 } |                 } | ||||||
|                 if (req.uri_path == "/internalapi/removeChat") { |                 if (req.uri_path == "/api/leaveChat") { | ||||||
|                     return when_internalapi_removechat(wgd, req, logged_in_user); |                     return when_internalapi_leavechat(wgd, req, logged_in_user); | ||||||
|                 } |                 } | ||||||
|             } catch (const std::exception& e) { |             } catch (const std::exception& e) { | ||||||
|                 guard_.rollback = true; |                 conn_guard.rollback = true; | ||||||
|                 throw; |                 throw; | ||||||
|             } |             } | ||||||
|             /* Trying to interpret request as asset lookup */ |             /* Trying to interpret request as asset lookup */ | ||||||
| @ -133,29 +132,11 @@ namespace iu9cawebchat { | |||||||
|         (const een9::SlaveTask& task, const std::string& req, een9::worker_id_t worker_id) -> std::string { |         (const een9::SlaveTask& task, const std::string& req, een9::worker_id_t worker_id) -> std::string { | ||||||
|             een9_ASSERT_pl(0 <= worker_id && worker_id < worker_guest_data.size()); |             een9_ASSERT_pl(0 <= worker_id && worker_id < worker_guest_data.size()); | ||||||
|             WorkerGuestData& wgd = worker_guest_data[worker_id]; |             WorkerGuestData& wgd = worker_guest_data[worker_id]; | ||||||
|  |             ONE_SQLITE_TRANSACTION_GUARD conn_guad(*wgd.db); | ||||||
|             try { |             try { | ||||||
|                 if (req == "hello") { |                 return admin_control_procedure(*wgd.db, req, termination); | ||||||
|                     return ":0 omg! hiii!! Hewwou :3 !!!!\n"; |  | ||||||
|                 } |  | ||||||
|                 if (req == "8") { |  | ||||||
|                     termination = true; |  | ||||||
|                     return "Bye\n"; |  | ||||||
|                 } |  | ||||||
|                 std::string updaterootpw_pref = "updaterootpw"; |  | ||||||
|                 if (een9::beginsWith(req, "updaterootpw")) { |  | ||||||
|                     size_t nid = updaterootpw_pref.size(); |  | ||||||
|                     if (nid >= req.size() || !isSPACE(req[nid])) |  | ||||||
|                         return "Bad command syntax. Missing whitespace\n"; |  | ||||||
|                     std::string new_password = req.substr(nid + 1); |  | ||||||
|                     if (!check_password(new_password)) |  | ||||||
|                         een9_THROW("Bad password"); |  | ||||||
|                     sqlite_nooutput(*wgd.db, |  | ||||||
|                         "UPDATE `user` SET `password` = ?1 WHERE `id` = 0 ", |  | ||||||
|                         {}, {{1, new_password}}); |  | ||||||
|                     return "Successul update\n"; |  | ||||||
|                 } |  | ||||||
|                 return "Incorrect command\n"; |  | ||||||
|             } catch (std::exception& e) { |             } catch (std::exception& e) { | ||||||
|  |                 conn_guad.rollback = true; | ||||||
|                 return std::string("Server error\n") + e.what(); |                 return std::string("Server error\n") + e.what(); | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
| @ -171,8 +152,8 @@ namespace iu9cawebchat { | |||||||
|                 een9_ASSERT(ret == 0, "Incorrect ear address: " + source[i].asString()); |                 een9_ASSERT(ret == 0, "Incorrect ear address: " + source[i].asString()); | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
|         translate_addr_list_conf(params.client_regular_listened, config["server"]["http-listen"].g().asArray()); |         translate_addr_list_conf(params.client_regular_listened, config["server"]["http-listen"].asArray()); | ||||||
|         translate_addr_list_conf(params.admin_control_listened, config["server"]["admin-command-listen"].g().asArray()); |         translate_addr_list_conf(params.admin_control_listened, config["server"]["admin-command-listen"].asArray()); | ||||||
| 
 | 
 | ||||||
|         signal(SIGINT, sigterm_action); |         signal(SIGINT, sigterm_action); | ||||||
|         signal(SIGTERM, sigterm_action); |         signal(SIGTERM, sigterm_action); | ||||||
|  | |||||||
| @ -28,11 +28,15 @@ namespace iu9cawebchat { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool check_password(const std::string &pwd) { |     bool check_password(const std::string &pwd) { | ||||||
|         return is_orthodox_string(pwd) && pwd.size() >= 8; |         return is_orthodox_string(pwd) && pwd.size() <= 150; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     bool check_strong_password(const std::string& pwd) { | ||||||
|  |         return check_password(pwd) && pwd.size() >= 8; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool check_name(const std::string &name) { |     bool check_name(const std::string &name) { | ||||||
|         return is_orthodox_string(name); |         return is_orthodox_string(name) && name.size() <= 150; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool check_nickname(const std::string &nickname) { |     bool check_nickname(const std::string &nickname) { | ||||||
| @ -42,7 +46,7 @@ namespace iu9cawebchat { | |||||||
|             if (!isUNCHAR(ch)) |             if (!isUNCHAR(ch)) | ||||||
|                 return false; |                 return false; | ||||||
|         } |         } | ||||||
|         return true; |         return nickname.size() <= 150; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /* Yeah baby, it's base64 time!!! */ |     /* Yeah baby, it's base64 time!!! */ | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ namespace iu9cawebchat { | |||||||
|     bool is_orthodox_string(const std::string& str); |     bool is_orthodox_string(const std::string& str); | ||||||
| 
 | 
 | ||||||
|     bool check_password(const std::string& pwd); |     bool check_password(const std::string& pwd); | ||||||
|  |     bool check_strong_password(const std::string& pwd); | ||||||
|     bool check_name(const std::string& name); |     bool check_name(const std::string& name); | ||||||
|     bool check_nickname(const std::string& nickname); |     bool check_nickname(const std::string& nickname); | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user