diff --git a/assets/HypertextPages/list-rooms.nytl.html b/assets/HypertextPages/list-rooms.nytl.html
index 4a638ce..ddc4923 100644
--- a/assets/HypertextPages/list-rooms.nytl.html
+++ b/assets/HypertextPages/list-rooms.nytl.html
@@ -1,6 +1,6 @@
{% ELDEF main JSON pres JSON userinfo %}
-
+
@@ -8,7 +8,7 @@
-{% PUT pass-userinfo userinfo %}
+{% PUT pass-pres-userinfo pres userinfo %}
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{% ENDELDEF %}
\ No newline at end of file
diff --git a/assets/HypertextPages/login.nytl.html b/assets/HypertextPages/login.nytl.html
index 700e8b8..674f42b 100644
--- a/assets/HypertextPages/login.nytl.html
+++ b/assets/HypertextPages/login.nytl.html
@@ -1,27 +1,27 @@
{% ELDEF main JSON pres JSON userinfo %}
-
-
-
-
-
- {% WRITE pres.phr.decl.page-login %}
-
+
+
+
+
+
+ {% WRITE pres.phr.decl.page-login %}
+
-
+
-
- {% PUT pass-userinfo userinfo %}
-
-
{% WRITE pres.phr.decl.enter %}
-
-
+
+{% PUT pass-pres-userinfo pres userinfo %}
+
+
{% WRITE pres.phr.decl.enter %}
+
+
-
-
+
+
{% ENDELDEF %}
diff --git a/assets/HypertextPages/pass-pres-userinfo.nytl.html b/assets/HypertextPages/pass-pres-userinfo.nytl.html
new file mode 100644
index 0000000..8331b75
--- /dev/null
+++ b/assets/HypertextPages/pass-pres-userinfo.nytl.html
@@ -0,0 +1,6 @@
+{% ELDEF main JSON pres JSON userinfo %}
+
+{% ENDELDEF %}
diff --git a/assets/HypertextPages/pass-userinfo.nytl.html b/assets/HypertextPages/pass-userinfo.nytl.html
deleted file mode 100644
index 164366f..0000000
--- a/assets/HypertextPages/pass-userinfo.nytl.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{% ELDEF main JSON userinfo %}
-
-
-
-{% ENDELDEF %}
diff --git a/building/main.cpp b/building/main.cpp
index 9b03229..7f7cfc5 100644
--- a/building/main.cpp
+++ b/building/main.cpp
@@ -147,6 +147,10 @@ struct CAWebChat {
"find_db.cpp",
"sqlite3_wrapper.cpp",
"login_cookie.cpp",
+ "backend_logic/server_data_interact.cpp",
+ "backend_logic/when_login.cpp",
+ "backend_logic/when_internalapi_pollevents.cpp",
+ "backend_logic/when_internalapi_getchatlist.cpp",
};
for (std::string& u: T.units)
u = "web_chat/iu9_ca_web_chat_lib/" + u;
diff --git a/example/config.json b/example/config.json
index 2e99947..c88f13f 100644
--- a/example/config.json
+++ b/example/config.json
@@ -39,6 +39,6 @@
"server": {
"workers": 8,
"http-listen": ["127.0.0.1:1025"],
- "admin-command-listen": ["unix:/run/iu9/iu9cawebchat-ac.sock"]
+ "admin-command-listen": ["[::1]:1026"]
}
}
diff --git a/src/http_server/engine_engine_number_9/http_structures/response_gen.cpp b/src/http_server/engine_engine_number_9/http_structures/response_gen.cpp
index e194e3f..a1be045 100644
--- a/src/http_server/engine_engine_number_9/http_structures/response_gen.cpp
+++ b/src/http_server/engine_engine_number_9/http_structures/response_gen.cpp
@@ -14,7 +14,7 @@ namespace een9 {
return result;
}
- std::string form_http_server_reponse_header_only(const char* code,
+ std::string form_http_server_response_header_only(const char* code,
const std::vector>& headers) {
return form_http_server_response_header(code, headers) + "\r\n";
}
@@ -42,13 +42,13 @@ namespace een9 {
}
std::string form_http_server_response_307(const std::string& Location) {
- return form_http_server_reponse_header_only("307", {{"Location", Location}});
+ return form_http_server_response_header_only("307", {{"Location", Location}});
}
std::string form_http_server_response_307_spec_head(const std::string &Location,
const std::vector>& headers) {
std::vector> cp = headers;
cp.emplace_back("Location", Location);
- return form_http_server_reponse_header_only("307", cp);
+ return form_http_server_response_header_only("307", cp);
}
}
diff --git a/src/http_server/engine_engine_number_9/running_mainloop.cpp b/src/http_server/engine_engine_number_9/running_mainloop.cpp
index 1e3911f..0cb1355 100644
--- a/src/http_server/engine_engine_number_9/running_mainloop.cpp
+++ b/src/http_server/engine_engine_number_9/running_mainloop.cpp
@@ -41,7 +41,6 @@ namespace een9 {
}
void push_back(SlaveTask task) {
- /* Throws a goddamn execption. Because why not. Ofcourse everything has to throw an exception */
/* CLion says. Allocated memory is leaking. YOUR MOTHER IS LEAKING YOU FOOL!! MY CODE IS FINE!! */
QElementHttpConnections* el = new QElementHttpConnections(std::move(task));
/* Exception does not leave queue in incorrect state */
@@ -66,6 +65,15 @@ namespace een9 {
sz--;
}
}
+
+ ~WorkersTaskQueue() {
+ QElementHttpConnections* cur = first;
+ while (cur) {
+ QElementHttpConnections* nxt = cur->nxt;
+ delete cur;
+ cur = nxt;
+ }
+ }
};
struct WorkersEnvCommon {
diff --git a/src/http_server/new_york_transit_line/rendering.cpp b/src/http_server/new_york_transit_line/rendering.cpp
index 3be9d53..bbdddbc 100644
--- a/src/http_server/new_york_transit_line/rendering.cpp
+++ b/src/http_server/new_york_transit_line/rendering.cpp
@@ -34,7 +34,7 @@ namespace nytl {
} else if (P.isDictionary() && what.isString()) {
const std::map& dict_p = P.asDictionary();
const std::string& key_w = what.asString();
- ASSERT(dict_p.count(key_w) == 1, "No such key exception");
+ ASSERT(dict_p.count(key_w) == 1, "No such key exception (" + key_w + ")");
result = LocalVarValue{true, "", &dict_p.at(key_w)};
} else
THROW("Incorrect type of \"json[json]\" expression. Unallowed signature of [] operator");
@@ -112,15 +112,9 @@ namespace nytl {
for (size_t i = 0; i < m; i++) {
result += text[i];
if (text[i] == '\n') {
- // newlined_somewhere = true;
-
result.resize(result.size() + wsp_before_newlines, ' ');
cur_line_width = wsp_before_newlines;
} else {
- // if (cur_line_width == 0 && newlined_somewhere) {
- // result.resize(result.size() + wsp_before_newlines, ' ');
- // cur_line_width = wsp_before_newlines;
- // }
cur_line_width++;
}
}
@@ -246,7 +240,11 @@ namespace nytl {
assert(passed_args.size() == 1);
const json::JSON* X = passed_args[0].JSON_subval;
assert(X);
- if (name == "jesc") {
+ if (name == "jsinsert") {
+ std::string pure_json = json::generate_str(*X, json::print_pretty);
+ rstrip(pure_json);
+ append(pure_json, result);
+ } else if (name == "jesc") {
std::string escaped_json = escape(json::generate_str(*X, json::print_pretty));
rstrip(escaped_json);
append(escaped_json, result);
diff --git a/src/http_server/new_york_transit_line/templater.cpp b/src/http_server/new_york_transit_line/templater.cpp
index fa613af..b20b365 100644
--- a/src/http_server/new_york_transit_line/templater.cpp
+++ b/src/http_server/new_york_transit_line/templater.cpp
@@ -111,6 +111,7 @@ namespace nytl {
void Templater::update() {
elements = {
+ {"jsinsert", Element{{json::JSON(true)}, true}},
{"jesc", Element{{json::JSON(true)}, true}},
{"jesccomp", Element{{json::JSON(true)}, true}},
/* str2text base element has a dedicated operator - WRITE */
diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.cpp
new file mode 100644
index 0000000..f4a604f
--- /dev/null
+++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.cpp
@@ -0,0 +1,114 @@
+#include "server_data_interact.h"
+
+#include
+#include
+
+namespace iu9cawebchat {
+ const char* stringify_user_chat_role(int64_t role) {
+ if (role == user_chat_role_admin)
+ return "admin";
+ if (role == user_chat_role_regular)
+ return "regular";
+ if (role == user_chat_role_read_only)
+ return "read-only";
+ return "not-a-member";
+ }
+
+ int64_t find_user_by_credentials (SqliteConnection& conn, const std::string& nickname, const std::string& password) {
+ SqliteStatement sql_req(conn,
+ "SELECT `id` FROM `user` WHERE `nickname` = ?1 AND `password` = ?2",
+ {}, {{1, nickname}, {2, password}});
+ fsql_integer_or_null id_col;
+ int status = sqlite_stmt_step(sql_req, {{0, &id_col}}, {});
+ if (status == SQLITE_ROW) {
+ een9_ASSERT_pl(id_col.exist & id_col.value >= 0);
+ return id_col.value;
+ }
+ return -1;
+ }
+
+ std::string find_user_name (SqliteConnection& conn, int64_t uid) {
+ een9_ASSERT(uid >= 0, "Are you crazy?");
+ SqliteStatement sql_req(conn,
+ "SELECT `name` FROM `user` WHERE `id` = ?1",
+ {{1, uid}}, {});
+ fsql_text8_or_null name_col;
+ int status = sqlite_stmt_step(sql_req, {}, {{0, &name_col}});
+ if (status == SQLITE_ROW) {
+ een9_ASSERT_pl(name_col.exist);
+ return name_col.value;
+ }
+ return "";
+ }
+
+ RowUser_Content lookup_user_content(SqliteConnection &conn, int64_t uid) {
+ een9_ASSERT(uid >= 0, "Are you crazy?");
+ SqliteStatement sql_req(conn,
+ "SELECT `nickname`, `name` FROM `user` WHERE `id` = ?1",
+ {{1, uid}}, {});
+ fsql_text8_or_null nickname_col;
+ fsql_text8_or_null name_col;
+ int status = sqlite_stmt_step(sql_req, {}, {{0, &nickname_col}, {1, &name_col}});
+ if (status == SQLITE_ROW) {
+ return {std::move(nickname_col.value), std::move(name_col.value)};
+ }
+ return {};
+ }
+
+ RowChat_Content lookup_chat_content(SqliteConnection &conn, int64_t uid) {
+ een9_ASSERT(uid >= 0, "Are you crazy?");
+ SqliteStatement sql_req(conn,
+ "SELECT `nickname`, `name`, `lastMsgId` FROM `chat` WHERE `id` = ?1",
+ {{1, uid}}, {});
+ fsql_text8_or_null nickname_col;
+ fsql_text8_or_null name_col;
+ fsql_integer_or_null last_msg_id_col;
+ int status = sqlite_stmt_step(sql_req, {{2, &last_msg_id_col}}, {{0, &nickname_col}, {1, &name_col}});
+ if (status == SQLITE_ROW) {
+ return {std::move(nickname_col.value), std::move(name_col.value),
+ last_msg_id_col.exist ? last_msg_id_col.value : -1};
+ }
+ return {};
+ }
+
+ void initial_extraction_of_all_the_useful_info_from_cookies(
+ SqliteConnection& conn, const een9::ClientRequest& req,
+ std::vector> &ret_cookies,
+ std::vector &ret_login_cookies,
+ json::JSON &ret_userinfo,
+ int64_t& ret_logged_in_user
+ ) {
+ ret_cookies = een9::findAllClientCookies(req.headers);
+ ret_login_cookies = select_login_cookies(ret_cookies);
+ ret_logged_in_user = -1; /* Negative means that user is not logged in */
+ if (!ret_login_cookies.empty()){
+ size_t oldest_ind = select_oldest_login_cookie(ret_login_cookies);
+ LoginCookie& tried = ret_login_cookies[oldest_ind];
+ ret_logged_in_user = find_user_by_credentials(conn, tried.nickname, tried.password);
+ if (ret_logged_in_user >= 0) {
+ ret_userinfo["uid"] = json::JSON(ret_logged_in_user);
+ ret_userinfo["nickname"] = json::JSON(tried.nickname);
+ ret_userinfo["name"] = json::JSON(find_user_name(conn, ret_logged_in_user));
+ }
+ }
+ }
+
+ int64_t get_role_of_user_in_chat(SqliteConnection& conn, int64_t userId, int64_t chatId) {
+ SqliteStatement req(conn,
+ "SELECT `role` FROM `user_chat_membership` WHERE `userId` = ?1 AND `chatId` = ?2",
+ {{1, userId}, {2, chatId}}, {});
+ fsql_integer_or_null role;
+ int status = sqlite_stmt_step(req, {{0, &role}}, {});
+ if (status == SQLITE_ROW) {
+ return role.exist ? (int)role.value : user_chat_role_deleted;
+ }
+ return user_chat_role_deleted;
+ }
+
+ std::string RTEE(const std::string& el_name,
+ const json::JSON& config_presentation, WorkerGuestData& wgd,
+ const json::JSON& userinfo) {
+ std::string page = wgd.templater->render(el_name, {&config_presentation, &userinfo});
+ return een9::form_http_server_response_200("text/html", page);
+ }
+}
diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.h b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.h
new file mode 100644
index 0000000..e6361ac
--- /dev/null
+++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/server_data_interact.h
@@ -0,0 +1,79 @@
+#ifndef IU9_CA_WEB_CHAT_LIB_BACKEND_LOGIC_SERVER_DATA_INTERACT_H
+#define IU9_CA_WEB_CHAT_LIB_BACKEND_LOGIC_SERVER_DATA_INTERACT_H
+
+/* This folder covers all code that helps to interact with database and templater,
+* or dictates the logic of how this web chat functions */
+
+#include
+#include
+#include "../sqlite3_wrapper.h"
+#include "../login_cookie.h"
+#include
+#include
+#include
+
+namespace iu9cawebchat {
+ constexpr int64_t user_chat_role_admin = 1;
+ constexpr int64_t user_chat_role_regular = 2;
+ constexpr int64_t user_chat_role_read_only = 3;
+ constexpr int64_t user_chat_role_deleted = 4;
+
+ const char* stringify_user_chat_role(int64_t role);
+
+ struct WorkerGuestData {
+ /* Because templaters use libjsonincpp, they can't be READ by two thread simultaneously */
+ std::unique_ptr templater;
+ std::unique_ptr db;
+ int debug_trans_op; // todo: delete when debug is over
+ };
+
+ int64_t find_user_by_credentials (SqliteConnection& conn, const std::string& nickname, const std::string& password);
+ std::string find_user_name (SqliteConnection& conn, int64_t uid);
+
+ struct RowUser_Content {
+ std::string nickname;
+ std::string name;
+ };
+
+ struct RowChat_Content {
+ std::string nickname;
+ std::string name;
+ int64_t lastMsgId; // Negative if it does not exist
+ };
+
+ RowUser_Content lookup_user_content(SqliteConnection& conn, int64_t uid);
+ RowChat_Content lookup_chat_content(SqliteConnection& conn, int64_t uid);
+
+
+ void initial_extraction_of_all_the_useful_info_from_cookies(
+ SqliteConnection& conn, const een9::ClientRequest& req,
+ std::vector>& ret_cookies,
+ std::vector& ret_login_cookies,
+ json::JSON& ret_userinfo,
+ int64_t& ret_logged_in_user
+ );
+
+ int64_t get_role_of_user_in_chat(SqliteConnection& conn, int64_t userId, int64_t chatId);
+
+ std::string RTEE(const std::string& el_name,
+ const json::JSON& config_presentation, WorkerGuestData& wgd,
+ const json::JSON& userinfo);
+
+ /* ========================== PAGES ================================== */
+ std::string when_page_login(WorkerGuestData& wgd, const json::JSON& config_presentation,
+ const een9::ClientRequest& req, const std::vector& login_cookies, const json::JSON& userinfo);
+
+
+ json::JSON internalapi_pollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent);
+
+ std::string when_internalapi_pollevents(WorkerGuestData& wgd,
+ const een9::ClientRequest& req, int64_t uid);
+
+
+ json::JSON internalapi_getChatList(SqliteConnection& conn, int64_t uid);
+
+ std::string when_internalapi_getchatlist(WorkerGuestData& wgd,
+ const een9::ClientRequest& req, int64_t uid);
+}
+
+#endif
diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_internalapi_getchatlist.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_internalapi_getchatlist.cpp
new file mode 100644
index 0000000..ff441fe
--- /dev/null
+++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_internalapi_getchatlist.cpp
@@ -0,0 +1,42 @@
+#include "server_data_interact.h"
+#include
+#include
+#include "../str_fields.h"
+
+namespace iu9cawebchat {
+ json::JSON internalapi_getChatList(SqliteConnection& conn, int64_t uid) {
+ json::JSON Recv;
+ Recv["status"] = json::JSON(0l);
+ Recv["chats"] = json::JSON(json::array);
+ std::vector& chats = Recv["chats"].g().asArray();
+ SqliteStatement req(conn,
+ "SELECT `chat`.`id`, `chat`.`nickname`, `chat`.`name`, `chat`.`lastMsgId`, "
+ "`user_chat_membership`.`role` FROM `chat` "
+ "RIGHT JOIN `user_chat_membership` ON `chat`.`id` = `user_chat_membership`.`chatId` "
+ "WHERE `user_chat_membership`.`userId` = ?1 ", {{1, uid}}, {});
+ while (true) {
+ fsql_integer_or_null chat_id;
+ fsql_text8_or_null chat_nickname, chat_name;
+ fsql_integer_or_null chat_lastMsgId, role_here;
+ int status = sqlite_stmt_step(req, {{0, &chat_id}, {3, &chat_lastMsgId}, {4, &role_here}},
+ {{1, &chat_nickname}, {2, &chat_name}});
+ if (status != SQLITE_ROW)
+ break;
+ chats.emplace_back();
+ json::JSON& chat = chats.back();
+ chat["id"] = json::JSON(chat_id.value);
+ chat["content"]["nickname"] = json::JSON(chat_nickname.value);
+ chat["content"]["name"] = json::JSON(chat_name.value);
+ chat["content"]["lastMsgId"] = json::JSON(chat_lastMsgId.exist ? chat_lastMsgId.value : -1);
+ chat["content"]["roleHere"] = json::JSON(stringify_user_chat_role(role_here.value));
+ }
+ return Recv;
+ }
+
+ std::string when_internalapi_getchatlist(WorkerGuestData& wgd,
+ const een9::ClientRequest& req, int64_t uid) {
+ const json::JSON& Sent = json::parse_str_flawless(req.body);
+ std::string result = json::generate_str(internalapi_getChatList(*wgd.db, uid), json::print_pretty);
+ return een9::form_http_server_response_200("text/json", result);
+ }
+}
diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_internalapi_pollevents.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_internalapi_pollevents.cpp
new file mode 100644
index 0000000..b6af302
--- /dev/null
+++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_internalapi_pollevents.cpp
@@ -0,0 +1,142 @@
+#include "server_data_interact.h"
+#include
+#include
+#include "../str_fields.h"
+#include
+
+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& 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& 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& events, int64_t userId, int64_t LocalHistoryId) {
+ SqliteStatement membership_changes(conn,
+ "SELECT `chatId`, `role` FROM `user_chat_membership` WHERE `userId` = ?1 "
+ "AND `user_chatList_IncHistoryId` > ?2", {{1, userId}, {2, LocalHistoryId}});
+ fsql_integer_or_null ev_chatId, usersRoleHere;
+ while (true) {
+ int status = sqlite_stmt_step(membership_changes, {{0, &ev_chatId}, {1, &usersRoleHere}}, {});
+ if (status != SQLITE_ROW)
+ break;
+ events.emplace_back();
+ json::JSON& event = events.back();
+ event["id"] = json::JSON(ev_chatId.value);
+ if (usersRoleHere.value == user_chat_role_deleted) {
+ event["type"] = json::JSON("removedChat");
+ } else {
+ event["type"] = json::JSON("addedChat");
+ RowChat_Content CHAT = lookup_chat_content(conn, ev_chatId.value);
+ event["content"]["name"] = json::JSON(CHAT.name);
+ event["content"]["nickname"] = json::JSON(CHAT.nickname);
+ event["content"]["lastMsgId"] = json::JSON(CHAT.lastMsgId);
+ event["content"]["roleHere"] = json::JSON(stringify_user_chat_role(usersRoleHere.value));
+ }
+ }
+ }
+
+ json::JSON internalapi_pollEvents(SqliteConnection& conn, int64_t uid, const json::JSON& Sent) {
+ json::JSON Recv;
+ Recv["status"] = json::JSON(0l);
+ Recv["update"] = json::JSON(json::array);
+ const std::vector& req_scope = Sent["scope"].g().asArray();
+ std::vector& updated = Recv["update"].g().asArray();
+ for (const json::JSON& hist_entity_request: req_scope) {
+ updated.emplace_back();
+ json::JSON& hist_entity_response = updated.back();
+ hist_entity_response["type"] = hist_entity_request["type"].g();
+ hist_entity_response["events"] = json::JSON(json::array);
+ std::vector& events = hist_entity_response["events"].g().asArray();
+ const int64_t LocalHistoryId = hist_entity_request["LocalHistoryId"].g().asInteger().get_int();
+ if (hist_entity_request["type"].g().asString() == "chat") {
+ int64_t chatId = hist_entity_request["chatId"].g().asInteger().get_int();
+ if (get_role_of_user_in_chat(conn, uid, chatId) == user_chat_role_deleted)
+ een9_THROW("internalapi: trying to access chat that user does not belong to");
+ hist_entity_response["chatId"] = json::JSON(chatId);
+ hist_entity_response["HistoryId"] = json::JSON(get_current_history_id_of_chat(conn, chatId));
+ /* Two classes of 'real events' can happen to chat: membership table change, message table change */
+ /* Here, I collect membership changes (related to this chat) */
+ internalapi_pollEvents_in_chat_collect_membership_events(conn, events, chatId, LocalHistoryId);
+ /* Here, I collect message changes (related to this chat) */
+ internalapi_pollEvents_in_chat_collect_messages_events(conn, events, chatId, LocalHistoryId);
+ } else if (hist_entity_request["type"].g().asString() == "chatlist") {
+ hist_entity_response["HistotyId"] = json::JSON(get_current_history_id_of_user_chatList(conn, uid));
+ internalapi_pollEvents_in_user_chatList_collect_events(conn, events, uid, LocalHistoryId);
+ } else
+ een9_THROW("Bad request");
+ }
+ return Recv;
+ }
+
+ std::string when_internalapi_pollevents(WorkerGuestData& wgd,
+ const een9::ClientRequest& req, int64_t uid) {
+ const json::JSON& Sent = json::parse_str_flawless(req.body);
+ std::string result = json::generate_str(internalapi_pollEvents(*wgd.db, uid, Sent), json::print_pretty);
+ return een9::form_http_server_response_200("text/json", result);
+ }
+}
diff --git a/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_login.cpp b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_login.cpp
new file mode 100644
index 0000000..d69516f
--- /dev/null
+++ b/src/web_chat/iu9_ca_web_chat_lib/backend_logic/when_login.cpp
@@ -0,0 +1,38 @@
+#include "server_data_interact.h"
+#include
+#include
+#include "../str_fields.h"
+
+namespace iu9cawebchat {
+ std::string when_page_login(WorkerGuestData& wgd, const json::JSON& config_presentation,
+ const een9::ClientRequest& req, const std::vector& login_cookies, const json::JSON& userinfo) {
+ if (req.method == "POST") {
+ std::vector> query = een9::split_html_query(req.body);
+ int64_t uid = -1;
+ std::string nickname;
+ std::string password;
+ try {
+ for (const std::pair& cmp: query) {
+ if (cmp.first == "nickname")
+ nickname = cmp.second;
+ if (cmp.first == "password")
+ password = cmp.second;
+ }
+ een9_ASSERT(check_nickname(nickname), "/login/accpet-data rejected impossible nickname");
+ een9_ASSERT(check_password(password), "/login/accpet-data rejected impossible password");
+ uid = find_user_by_credentials(*wgd.db, nickname, password);
+
+ } catch(const std::exception& e){}
+ if (uid < 0) {
+ printf("Redirecting back to /login because of incorrect credentials\n");
+ /* todo: Here I need to tell somehow to user (through fancy red box, maybe), that login was incorrect */
+ return RTEE("login", config_presentation, wgd, userinfo);
+ }
+ std::vector> response_hlines;
+ LoginCookie new_login_cookie = create_login_cookie(nickname, password);
+ add_set_cookie_headers_to_login(login_cookies, response_hlines, new_login_cookie);
+ return een9::form_http_server_response_307_spec_head("/", response_hlines);
+ }
+ return RTEE("login", config_presentation, wgd, userinfo);
+ }
+}
diff --git a/src/web_chat/iu9_ca_web_chat_lib/initialize.cpp b/src/web_chat/iu9_ca_web_chat_lib/initialize.cpp
index 2c621b1..d5cd081 100644
--- a/src/web_chat/iu9_ca_web_chat_lib/initialize.cpp
+++ b/src/web_chat/iu9_ca_web_chat_lib/initialize.cpp
@@ -35,44 +35,49 @@ namespace iu9cawebchat {
* If chat.lastMsgId is NULL, chat is empty
* If message.previous is NULL, this message is first in it's chat
*/
- sqlite_nooutput(conn.hand, "PRAGMA foreign_keys = true");
- sqlite_nooutput(conn.hand, "BEGIN");
- sqlite_nooutput(conn.hand, "CREATE TABLE `nickname` (`it` TEXT PRIMARY KEY NOT NULL) WITHOUT ROWID");
- sqlite_nooutput(conn.hand, "CREATE TABLE `user` ("
- "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
- "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL,"
- "`name` TEXT NOT NULL,"
- "`chatList_HistoryId` INTEGER NOT NULL,"
- "`password` TEXT NOT NULL"
- ")");
- sqlite_nooutput(conn.hand, "CREATE TABLE `chat` ("
- "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
- "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL,"
- "`name` TEXT NOT NULL,"
- "`it_HistoryId` INTEGER NOT NULL,"
- "`lastMsgId` INTEGER REFERENCES `message`"
- ")");
- sqlite_nooutput(conn.hand, "CREATE TABLE `user_chat_membership` ("
- "`userId` INTEGER REFERENCES `user` NOT NULL,"
- "`chatId` INTEGER REFERENCES `chat` NOT NULL,"
- "`user_chatList_IncHistoryId` INTEGER NOT NULL,"
- "`chat_IncHistoryId` INTEGER NOT NULL,"
- "`role` INTEGER NOT NULL"
- ")");
- sqlite_nooutput(conn.hand, "CREATE TABLE `message` ("
- "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
- "`chatId` INTEGER REFERENCES `chat` NOT NULL,"
- "`previous` INTEGER REFERENCES `message`,"
- "`senderUserId` INTEGER REFERENCES `user` NOT NULL,"
- "`exists` BOOLEAN NOT NULL,"
- "`isSystem` BOOLEAN NOT NULL,"
- "`text` TEXT NOT NULL,"
- "`chat_IncHistoryId` INTEGER NOT NULL"
- ")");
- sqlite_nooutput(conn.hand, "INSERT INTO `nickname` VALUES (?1)", {}, {{1, "root"}});
- sqlite_nooutput(conn.hand, "INSERT INTO `user` (`id`, `nickname`, `name`, `chatList_HistoryId`, `password`) VALUES "
- "(0, ?1, ?2, 0, ?3)", {},
- {{1, "root"}, {2, "Rootov Root Rootovich"}, {3, root_pw}});
- sqlite_nooutput(conn.hand, "END");
+ try {
+ sqlite_nooutput(conn, "PRAGMA foreign_keys = true");
+ sqlite_nooutput(conn, "BEGIN");
+ sqlite_nooutput(conn, "CREATE TABLE `nickname` (`it` TEXT PRIMARY KEY NOT NULL) WITHOUT ROWID");
+ sqlite_nooutput(conn, "CREATE TABLE `user` ("
+ "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
+ "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL,"
+ "`name` TEXT NOT NULL,"
+ "`chatList_HistoryId` INTEGER NOT NULL,"
+ "`password` TEXT NOT NULL"
+ ")");
+ sqlite_nooutput(conn, "CREATE TABLE `chat` ("
+ "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
+ "`nickname` TEXT UNIQUE REFERENCES `nickname` NOT NULL,"
+ "`name` TEXT NOT NULL,"
+ "`it_HistoryId` INTEGER NOT NULL,"
+ "`lastMsgId` INTEGER REFERENCES `message`"
+ ")");
+ sqlite_nooutput(conn, "CREATE TABLE `user_chat_membership` ("
+ "`userId` INTEGER REFERENCES `user` NOT NULL,"
+ "`chatId` INTEGER REFERENCES `chat` NOT NULL,"
+ "`user_chatList_IncHistoryId` INTEGER NOT NULL,"
+ "`chat_IncHistoryId` INTEGER NOT NULL,"
+ "`role` INTEGER NOT NULL"
+ ")");
+ sqlite_nooutput(conn, "CREATE TABLE `message` ("
+ "`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
+ "`chatId` INTEGER REFERENCES `chat` NOT NULL,"
+ "`previous` INTEGER REFERENCES `message`,"
+ "`senderUserId` INTEGER REFERENCES `user` NOT NULL,"
+ "`exists` BOOLEAN NOT NULL,"
+ "`isSystem` BOOLEAN NOT NULL,"
+ "`text` TEXT NOT NULL,"
+ "`chat_IncHistoryId` INTEGER NOT NULL"
+ ")");
+ sqlite_nooutput(conn, "INSERT INTO `nickname` VALUES (?1)", {}, {{1, "root"}});
+ sqlite_nooutput(conn, "INSERT INTO `user` (`id`, `nickname`, `name`, `chatList_HistoryId`, `password`) VALUES "
+ "(0, ?1, ?2, 0, ?3)", {},
+ {{1, "root"}, {2, "Rootov Root Rootovich"}, {3, root_pw}});
+ sqlite_nooutput(conn, "END");
+ } catch (const std::exception& e) {
+ sqlite_nooutput(conn, "ROLLBACK", {}, {});
+ throw;
+ }
}
}
diff --git a/src/web_chat/iu9_ca_web_chat_lib/run.cpp b/src/web_chat/iu9_ca_web_chat_lib/run.cpp
index 082c7aa..3cff03a 100644
--- a/src/web_chat/iu9_ca_web_chat_lib/run.cpp
+++ b/src/web_chat/iu9_ca_web_chat_lib/run.cpp
@@ -1,20 +1,29 @@
#include "actions.h"
-
#include
-#include
-#include
-#include
+#include
#include
-#include
-#include
-#include
-#include
-#include
-#include "sqlite3_wrapper.h"
-#include "str_fields.h"
#include "find_db.h"
-#include "login_cookie.h"
-#include
+#include
+#include
+#include "str_fields.h"
+
+// #include
+// #include
+// #include
+// #include
+// #include
+// #include
+// #include
+// #include
+// #include
+// #include "sqlite3_wrapper.h"
+// #include "str_fields.h"
+// #include "find_db.h"
+// #include "login_cookie.h"
+// #include
+// #include
+
+#include "backend_logic/server_data_interact.h"
namespace iu9cawebchat {
bool termination = false;
@@ -45,11 +54,6 @@ namespace iu9cawebchat {
int ret = find_db_sqlite_file_path(config, sqlite_db_path);
een9_ASSERT(ret == 0, "Can't find database file");
- struct WorkerGuestData {
- /* Because templaters use libjsonincpp, they can't be READ by two thread simultaneously */
- std::unique_ptr templater;
- std::unique_ptr db;
- };
std::vector worker_guest_data(slave_number);
for (int i = 0; i < slave_number; i++) {
worker_guest_data[i].templater = std::make_unique(
@@ -63,101 +67,53 @@ namespace iu9cawebchat {
(const een9::SlaveTask& task, const een9::ClientRequest& req, een9::worker_id_t worker_id) -> std::string {
een9_ASSERT_pl(0 <= worker_id && worker_id < worker_guest_data.size());
WorkerGuestData& wgd = worker_guest_data[worker_id];
- nytl::Templater& templater = *wgd.templater;
een9::StaticAsset sa;
- int ret;
- auto find_user_by_credentials = [&](const std::string& nickname, const std::string& password) -> int64_t {
- SqliteStatement sql_req(*wgd.db,
- "SELECT `id` FROM `user` WHERE `nickname` = ?1 AND `password` = ?2",
- {}, {{1, nickname}, {2, password}});
- fsql_integer_or_null id_col;
- int status = sqlite_stmt_step(sql_req, {{0, &id_col}}, {});
- if (status == SQLITE_ROW) {
- een9_ASSERT_pl(id_col.exist & id_col.value >= 0);
- return id_col.value;
- }
- return -1;
- };
- auto find_user_name = [&](int64_t uid) -> std::string {
- een9_ASSERT(uid >= 0, "Are you crazy?");
- SqliteStatement sql_req(*wgd.db,
- "SELECT `name` FROM `user` WHERE `id` = ?1",
- {{1, uid}}, {});
- fsql_text8_or_null name_col;
- int status = sqlite_stmt_step(sql_req, {}, {{0, &name_col}});
- if (status == SQLITE_ROW) {
- een9_ASSERT_pl(name_col.exist);
- return name_col.value;
- }
- return "";
- };
+ sqlite_nooutput(*wgd.db, "BEGIN", {}, {});
+ struct guard {SqliteConnection& conn; bool rollback = false; ~guard() {
+ if (rollback)
+ sqlite_nooutput(conn, "ROLLBACK", {}, {});
+ else
+ sqlite_nooutput(conn, "END", {}, {});
+ }} guard_{*wgd.db};
+ try {
+ std::vector> cookies;
+ std::vector login_cookies;
+ json::JSON userinfo;
+ int64_t logged_in_user = -1;
+ initial_extraction_of_all_the_useful_info_from_cookies(*wgd.db, req, cookies, login_cookies, userinfo, logged_in_user);
- std::vector> cookies = een9::findAllClientCookies(req.headers);
- std::vector login_cookies = select_login_cookies(cookies);
- json::JSON userinfo;
- userinfo["uid"] = json::JSON("-1");
- userinfo["nickname"] = json::JSON("");
- userinfo["name"] = json::JSON("");
- int64_t logged_in_user = -1; /* Negative means that user is not logged in */
- if (!login_cookies.empty()){
- size_t oldest_ind = select_oldest_login_cookie(login_cookies);
- LoginCookie& tried = login_cookies[oldest_ind];
- logged_in_user = find_user_by_credentials(tried.nickname, tried.password);
- if (logged_in_user >= 0) {
- userinfo["uid"] = json::JSON(std::to_string(logged_in_user));
- userinfo["nickname"] = json::JSON(tried.nickname);
- userinfo["name"] = json::JSON(find_user_name(logged_in_user));
- }
- }
+ std::string result;
- auto RTEE = [&](const std::string& el_name) -> std::string {
- std::string page = templater.render(el_name, {&config_presentation, &userinfo});
- return een9::form_http_server_response_200("text/html", page);
- };
-
- if (req.uri_path == "/" || req.uri_path == "/list-rooms") {
- printf("DEBUG:::: %d\n", logged_in_user);
- if (logged_in_user < 0)
- return een9::form_http_server_response_307("/login");
- return RTEE("list-rooms");
- }
- if (req.uri_path == "/login") {
- if (req.method == "POST") {
- std::vector> query = een9::split_html_query(req.body);
- std::string nickname;
- std::string password;
- for (const std::pair& cmp: query) {
- if (cmp.first == "nickname")
- nickname = cmp.second;
- if (cmp.first == "password")
- password = cmp.second;
- }
- een9_ASSERT(check_nickname(nickname), "/login/accpet-data rejected impossible nickname");
- een9_ASSERT(check_password(password), "/login/accpet-data rejected impossible password");
- int64_t uid = find_user_by_credentials(nickname, password);
- if (uid < 0) {
- /* todo: Here I need to tell somehow to user (through fancy red box, maybe), that login was incorrect */
- return RTEE("login");
- }
- std::vector> response_hlines;
- LoginCookie new_login_cookie = create_login_cookie(nickname, password);
- add_set_cookie_headers_to_login(login_cookies, response_hlines, new_login_cookie);
- return een9::form_http_server_response_307_spec_head("/", response_hlines);
+ if (req.uri_path == "/" || req.uri_path == "/list-rooms") {
+ if (logged_in_user < 0)
+ result = een9::form_http_server_response_307("/login");
+ return RTEE("list-rooms", config_presentation, wgd, userinfo);
}
- return RTEE("login");
- }
- if (req.uri_path == "/chat") {
- return RTEE("chat");
- }
- if (req.uri_path == "/profile") {
- return RTEE("profile");
- }
- if (req.uri_path == "/registration") {
- return RTEE("registration");
+ if (req.uri_path == "/login") {
+ return when_page_login(wgd, config_presentation, req, login_cookies, userinfo);
+ }
+ if (req.uri_path == "/chat") {
+ return RTEE("chat", config_presentation, wgd, userinfo);
+ }
+ if (req.uri_path == "/profile") {
+ return RTEE("profile", config_presentation, wgd, userinfo);
+ }
+ // if (req.uri_path == "/registration") {
+ // RTEE("registration", config_presentation, wgd, userinfo);
+ // }
+ if (req.uri_path == "/internalapi/pollEvents") {
+ return when_internalapi_pollevents(wgd, req, logged_in_user);
+ }
+ if (req.uri_path == "/internalapi/getChatList") {
+ return when_internalapi_getchatlist(wgd, req, logged_in_user);
+ }
+ } catch (const std::exception& e) {
+ guard_.rollback = true;
+ throw;
}
/* Trying to interpret request as asset lookup */
- ret = samI.get_asset(req.uri_path, sa);
- if (ret >= 0) {
+ int rets = samI.get_asset(req.uri_path, sa);
+ if (rets >= 0) {
return een9::form_http_server_response_200(sa.type, sa.content);
}
return een9::form_http_server_response_404("text/html", " Not found!
");
@@ -169,26 +125,26 @@ namespace iu9cawebchat {
WorkerGuestData& wgd = worker_guest_data[worker_id];
try {
if (req == "hello") {
- return ":0 omg! hiii!! Hewwou :3 !!!!";
+ return ":0 omg! hiii!! Hewwou :3 !!!!\n";
}
if (req == "8") {
termination = true;
- return "Bye";
+ 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";
+ 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->hand,
+ sqlite_nooutput(*wgd.db,
"UPDATE `user` SET `password` = ?1 WHERE `id` = 0 ",
{}, {{1, new_password}});
- return "Successul update";
+ return "Successul update\n";
}
- return "Incorrect command";
+ return "Incorrect command\n";
} catch (std::exception& e) {
return std::string("Server error\n") + e.what();
}
diff --git a/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.cpp b/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.cpp
index f5a6190..55f150c 100644
--- a/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.cpp
+++ b/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.cpp
@@ -13,42 +13,22 @@ namespace iu9cawebchat {
}
SqliteConnection::~SqliteConnection() {
- if (sqlite3_close(hand) != 0) {abort();}
+ sqlite3_close_v2(hand);
}
- void sqlite_nooutput(sqlite3* db_hand, const std::string& req_statement,
+ void sqlite_nooutput(SqliteConnection& conn, const std::string& req_statement,
const std::vector>& int64_binds,
const std::vector>& text8_binds) {
- sqlite3_stmt* stmt_obj = NULL;
- int ret = sqlite3_prepare_v2(db_hand, req_statement.c_str(), -1, &stmt_obj, NULL);
- if (ret != 0) {
- int err_pos = sqlite3_error_offset(db_hand);
- een9_THROW("Compilation of request\n" + req_statement + "\nfailed" +
- ((err_pos >= 0) ? " with offset " + std::to_string(err_pos) : ""));
- }
- assert(sqlite3_errcode(db_hand) == SQLITE_OK);
- struct Guard1{sqlite3_stmt*& r; ~Guard1(){if (sqlite3_finalize(r) != 0) {abort();}}} guard1{stmt_obj};
- for (const std::pair& bv: int64_binds) {
- ret = sqlite3_bind_int64(stmt_obj, bv.first, bv.second);
- een9_ASSERT(ret == 0, "sqlite3_bind_int64");
- }
- for (const std::pair& bv: text8_binds) {
- een9_ASSERT(is_orthodox_string(bv.second), "Can't bind this string to parameter");
- een9_ASSERT(bv.second.size() + 1 < INT_MAX, "Ah, oh, senpai, your string is toooo huge");
- ret = sqlite3_bind_text(stmt_obj, bv.first, bv.second.c_str(), (int)bv.second.size(), SQLITE_STATIC);
- een9_ASSERT(ret == 0, "sqlite3_bind_text");
- }
+ SqliteStatement stmt(conn, req_statement, int64_binds, text8_binds);
+ int ret;
while (true) {
- ret = sqlite3_step(stmt_obj);
- if (ret == SQLITE_DONE)
- break;
+ ret = sqlite_stmt_step(stmt, {}, {});
if (ret != SQLITE_ROW)
- een9_THROW(std::string("sqlite_row ") + sqlite3_errstr(ret));
- int cc = sqlite3_column_count(stmt_obj);
+ break;
+ int cc = sqlite3_column_count(stmt.stmt_obj);
std::vector types(cc);
- for (int i = 0; i < cc; i++) {
- types[i] = sqlite3_column_type(stmt_obj, i);
- }
+ for (int i = 0; i < cc; i++)
+ types[i] = sqlite3_column_type(stmt.stmt_obj, i);
printf("Column: |");
for (int i = 0; i < cc; i++) {
switch (types[i]) {
@@ -67,18 +47,18 @@ namespace iu9cawebchat {
printf("Values: | ");
for (int i = 0; i < cc; i++) {
if (types[i] == SQLITE_INTEGER) {
- printf("%lld | ", sqlite3_column_int64(stmt_obj, i));
+ printf("%lld | ", sqlite3_column_int64(stmt.stmt_obj, i));
} else if (types[i] == SQLITE_FLOAT) {
- printf("%lf | ", sqlite3_column_double(stmt_obj, i));
+ printf("%lf | ", sqlite3_column_double(stmt.stmt_obj, i));
} else if (types[i] == SQLITE_BLOB) {
- const void* blob = sqlite3_column_blob(stmt_obj, i);
- een9_ASSERT(sqlite3_errcode(db_hand) == SQLITE_OK, "oom in sqlite3_column_blob");
- size_t sz = sqlite3_column_bytes(stmt_obj, i);
+ const void* blob = sqlite3_column_blob(stmt.stmt_obj, i);
+ een9_ASSERT(sqlite3_errcode(conn.hand) == SQLITE_OK, "oom in sqlite3_column_blob");
+ size_t sz = sqlite3_column_bytes(stmt.stmt_obj, i);
printf("Blob of size %lu | ", sz);
} else if (types[i] == SQLITE_NULL) {
printf("NULL | ");
} else {
- const unsigned char* text = sqlite3_column_text(stmt_obj, i);
+ const unsigned char* text = sqlite3_column_text(stmt.stmt_obj, i);
een9_ASSERT(text, "oom in sqlite3_column_text");
printf("%s | ", (const char*)text);
// todo: print only if string is safe to print
@@ -108,7 +88,7 @@ namespace iu9cawebchat {
for (const std::pair& bv: text8_binds) {
een9_ASSERT(is_orthodox_string(bv.second), "Can't bind this string to parameter");
een9_ASSERT(bv.second.size() + 1 < INT_MAX, "Ah, oh, senpai, your string is toooo huge");
- ret = sqlite3_bind_text(stmt_obj, bv.first, bv.second.c_str(), (int)bv.second.size(), SQLITE_STATIC);
+ ret = sqlite3_bind_text(stmt_obj, bv.first, bv.second.c_str(), (int)bv.second.size(), SQLITE_TRANSIENT);
een9_ASSERT(ret == 0, "sqlite3_bind_text");
}
} catch (const std::exception& e) {
@@ -127,7 +107,8 @@ namespace iu9cawebchat {
int ret = sqlite3_step(stmt.stmt_obj);
if (ret == SQLITE_DONE)
return ret;
- een9_ASSERT(ret == SQLITE_ROW, std::string("sqlite3_step ") + sqlite3_errstr(ret));
+ if (ret != SQLITE_ROW)
+ een9_THROW(std::string("sqlite3_step ") + sqlite3_errstr(ret) + " :> " + sqlite3_errmsg(stmt.conn.hand));
int cc = sqlite3_column_count(stmt.stmt_obj);
for (auto& resp: ret_of_integer_or_null) {
if (resp.first >= cc)
diff --git a/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.h b/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.h
index 620203f..2c880a8 100644
--- a/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.h
+++ b/src/web_chat/iu9_ca_web_chat_lib/sqlite3_wrapper.h
@@ -15,9 +15,9 @@ namespace iu9cawebchat {
~SqliteConnection();
};
- void sqlite_nooutput(sqlite3* db_hand, const std::string& req_statement,
- const std::vector>& int64_binds= {},
- const std::vector>& text8_binds = {});
+ void sqlite_nooutput(SqliteConnection& conn, const std::string& req_statement,
+ const std::vector>& int64_binds = {},
+ const std::vector>& text8_binds = {});
struct fsql_integer_or_null {
bool exist;
@@ -33,7 +33,7 @@ namespace iu9cawebchat {
SqliteConnection& conn;
sqlite3_stmt* stmt_obj = NULL;
SqliteStatement(SqliteConnection& connection, const std::string& req_statement,
- const std::vector>& int64_binds= {},
+ const std::vector>& int64_binds = {},
const std::vector>& text8_binds = {});
SqliteStatement(SqliteStatement&) = delete;
SqliteStatement& operator=(SqliteStatement&) = delete;
diff --git a/src/web_chat/misc_tests/api_test0.cpp b/src/web_chat/misc_tests/api_test0.cpp
new file mode 100644
index 0000000..e2d3f29
--- /dev/null
+++ b/src/web_chat/misc_tests/api_test0.cpp
@@ -0,0 +1,31 @@
+#include
+#include
+#include
+
+using namespace iu9cawebchat;
+
+void test(SqliteConnection& conn, int64_t uid){
+ json::JSON Recv = internalapi_getChatList(conn, uid);
+ printf("%s\n", json::generate_str(Recv, json::print_pretty).c_str());
+}
+
+void test_polling(SqliteConnection& conn, int64_t uid, int64_t LocalHistoryId) {
+ json::JSON Sent;
+ Sent["scope"][0]["type"] = json::JSON("chatlist");
+ Sent["scope"][0]["LocalHistoryId"] = json::JSON(LocalHistoryId);
+
+ json::JSON Recv = internalapi_pollEvents(conn, uid, Sent);
+ printf("%s\n", json::generate_str(Recv, json::print_pretty).c_str());
+}
+
+int main() {
+ SqliteConnection conn("./iu9-ca-web-chat.db");
+ // test(conn, 0);
+ // test(conn, 1);
+ // test(conn, 2);
+ // printf("\n\n ===== Now testing polling of events ===== \n\n");
+ test_polling(conn, 1, 0);
+ test_polling(conn, 1, 1);
+ test_polling(conn, 1, 2);
+ return 0;
+}
diff --git a/src/web_chat/misc_tests/find_credentials_test.cpp b/src/web_chat/misc_tests/find_credentials_test.cpp
new file mode 100644
index 0000000..9bf2239
--- /dev/null
+++ b/src/web_chat/misc_tests/find_credentials_test.cpp
@@ -0,0 +1,14 @@
+#include
+#include
+#include
+#include
+
+using namespace iu9cawebchat;
+
+int main() {
+ SqliteConnection conn("./iu9-ca-web-chat.db");
+ for (size_t i = 0; i < 100; i++) {
+ int64_t uid = find_user_by_credentials(conn, "root", "12345678");
+ assert(uid == 0);
+ }
+}
\ No newline at end of file