implemented Login and Register requests

This commit is contained in:
2025-11-18 23:07:31 +05:00
parent 23c98e14df
commit 806f0359d0
19 changed files with 395 additions and 147 deletions

View File

@@ -1,5 +1,4 @@
#include "cli/ClientCLI/ClientCLI.h"
#include "cli/ClientCLI/db_tables.h"
#include "cli/term.h"
#include "tcp-chat/common_constants.h"
#include "tlibc/time.h"
@@ -23,22 +22,28 @@ static const str farewell_art = STR(
#define is_alias(LITERAL) str_equals(command, STR(LITERAL))
static Result(void) ClientCLI_askUserNameAndPassword(str* username_out, str* password_out);
static Result(void) ClientCLI_commandExec(ClientCLI* self, str command, bool* stop);
static Result(void) ClientCLI_execCommand(ClientCLI* self, str command, bool* stop);
static Result(void) ClientCLI_openUserDB(ClientCLI* self);
static Result(void) ClientCLI_saveServerInfo(ClientCLI* self,
cstr server_addr_cstr, cstr server_pk_base64,
str server_name, str server_description);
static Result(Server*) ClientCLI_saveServerInfo(ClientCLI* self,
str addr, str pk_base64, str name, str desc);
static Result(Server*) ClientCLI_joinNewServer(ClientCLI* self);
static Result(Server*) ClientCLI_selectServerFromCache(ClientCLI* self);
static Result(void) ClientCLI_showServerInfo(ClientCLI* self, Server* server);
static Result(void) ClientCLI_register(ClientCLI* self);
static Result(void) ClientCLI_login(ClientCLI* self);
void ClientCLI_destroy(ClientCLI* self){
if(!self)
return;
Client_free(self->client);
idb_close(self->user_db);
idb_close(self->db);
pthread_mutex_destroy(&self->servers_cache_mutex);
List_destroy(self->servers_cache_list);
HashMap_destroy(&self->servers_addr_id_map);
}
void ClientCLI_construct(ClientCLI* self){
self->client = NULL;
self->user_db = NULL;
self->db = NULL;
}
Result(void) ClientCLI_run(ClientCLI* self) {
@@ -46,7 +51,7 @@ Result(void) ClientCLI_run(ClientCLI* self) {
try_void(term_init());
term_clear();
printf("%s\n", greeting_art.data);
printf(FMT_str"\n", greeting_art.size, greeting_art.data);
// create Client
str username = str_null, password = str_null;
@@ -75,11 +80,11 @@ Result(void) ClientCLI_run(ClientCLI* self) {
if(command_input.size == 0)
continue;
ResultVar(void) com_result = ClientCLI_commandExec(self, command_input, &stop);
ResultVar(void) com_result = ClientCLI_execCommand(self, command_input, &stop);
if(com_result.error){
Error_addCallPos(com_result.error, ErrorCallPos_here());
str e_str = Error_toStr(com_result.error);
printf("%s\n", e_str.data);
printf(FMT_str"\n", e_str.size, e_str.data);
str_free(e_str);
Error_free(com_result.error);
}
@@ -105,6 +110,7 @@ static Result(void) ClientCLI_askUserNameAndPassword(str* username_out, str* pas
printf("ERROR: username length (in bytes) must be >= %i and <= %i\n",
USERNAME_SIZE_MIN, USERNAME_SIZE_MAX);
}
//TODO: validate username characters
else break;
}
@@ -131,11 +137,11 @@ static Result(void) ClientCLI_askUserNameAndPassword(str* username_out, str* pas
Return RESULT_VOID;
}
static Result(void) ClientCLI_commandExec(ClientCLI* self, str command, bool* stop){
static Result(void) ClientCLI_execCommand(ClientCLI* self, str command, bool* stop){
Deferral(64);
if(is_alias("q") || is_alias("quit") || is_alias("exit")){
printf("%s\n", farewell_art.data);
printf(FMT_str"\n", farewell_art.size, farewell_art.data);
*stop = true;
}
else if(is_alias("clear")){
@@ -143,55 +149,37 @@ static Result(void) ClientCLI_commandExec(ClientCLI* self, str command, bool* st
}
else if(is_alias("h") || is_alias("help")){
printf(
"COMMANDS:\n"
"h, help Show this message.\n"
"q, quit, exit Close the program.\n"
"clear Clear the screen.\n"
"j, join Join a server.\n"
"c, connect Connect to a server you joined.\n"
"COMMANDS:\n"
"Without connection:\n"
" h, help Show this message.\n"
" q, quit, exit Close the program.\n"
" clear Clear the screen.\n"
"Connection:\n"
" j, join Join a new server and select it.\n"
" s, select Select a server you joined before.\n"
"After connection:\n"
" r, register Create account on selected server\n"
" l, login Authorize on selected server\n"
"Authorized:\n"
);
}
else if (is_alias("j") || is_alias("join")){
// ask server address
printf("Enter server address (ip:port):\n");
char server_addr_cstr[HOSTADDR_SIZE_MAX + 1];
try_void(term_readLine(server_addr_cstr, sizeof(server_addr_cstr)));
str server_addr_str = str_from_cstr(server_addr_cstr);
str_trim(&server_addr_str, true);
// ask server public key
printf("Enter server public key (RSA-Public-<SIZE>:<DATA>):\n");
char server_pk_cstr[PUBLIC_KEY_BASE64_SIZE_MAX + 1];
try_void(term_readLine(server_pk_cstr, sizeof(server_pk_cstr)));
str server_pk_str = str_from_cstr(server_pk_cstr);
str_trim(&server_pk_str, true);
// connect to server
printf("connecting to server...\n");
try_void(Client_connect(self->client, server_addr_cstr, server_pk_cstr));
printf("connection established\n");
// show server info
str server_name = str_null;
str server_description = str_null;
try_void(Client_getServerName(self->client, &server_name));
try_void(Client_getServerName(self->client, &server_description));
printf("server name: %s\n", server_name.data);
printf("server description: %s\n", server_description.data);
try_void(ClientCLI_saveServerInfo(self, server_addr_cstr, server_pk_cstr,
server_name, server_description));
// TODO: ask in loop: log in / register
//TODO: call Client_runIO():
// ask address and key, connect to server
try_void(ClientCLI_joinNewServer(self));
}
else if(is_alias("s") || is_alias("select")){
// show scrollable list of servers, get selected one
try_void(ClientCLI_selectServerFromCache(self));
}
else if(is_alias("r") || is_alias("register")){
try_void(ClientCLI_register(self));
}
else if(is_alias("l") || is_alias("login")){
try_void(ClientCLI_login(self));
// TODO: call Client_runIO():
// function with infinite loop which sends and receives messages
// with navigation across server channels
}
else if(is_alias("c") || is_alias("connect")){
// TODO: read saved servers from database
// TODO: show scrollable list of servers, get selected one
// TODO: ask in loop: log in / register
}
else {
printf("ERROR: unknown command.\n"
"Use 'h' to see list of avaliable commands\n");
@@ -200,6 +188,127 @@ static Result(void) ClientCLI_commandExec(ClientCLI* self, str command, bool* st
Return RESULT_VOID;
}
static Result(void) ClientCLI_joinNewServer(ClientCLI* self){
Deferral(8);
// ask server address
printf("Enter server address (ip:port):\n");
char server_addr_cstr[HOSTADDR_SIZE_MAX + 1];
try_void(term_readLine(server_addr_cstr, sizeof(server_addr_cstr)));
str server_addr_str = str_from_cstr(server_addr_cstr);
str_trim(&server_addr_str, true);
// ask server public key
printf("Enter server public key (RSA-Public-<SIZE>:<DATA>):\n");
char server_pk_cstr[PUBLIC_KEY_BASE64_SIZE_MAX + 1];
try_void(term_readLine(server_pk_cstr, sizeof(server_pk_cstr)));
str server_pk_str = str_from_cstr(server_pk_cstr);
str_trim(&server_pk_str, true);
printf("Connecting to server...\n");
try_void(Client_connect(self->client, server_addr_cstr, server_pk_cstr));
printf("Connection established\n");
str server_name = str_null;
str server_description = str_null;
try_void(Client_getServerName(self->client, &server_name));
try_void(Client_getServerDescription(self->client, &server_description));
try(Server* server, p, ClientCLI_saveServerInfo(self,
server_addr_str, server_pk_str,
server_name, server_description));
try_void(ClientCLI_showServerInfo(self, server));
Return RESULT_VOID;
}
static Result(void) ClientCLI_selectServerFromCache(ClientCLI* self){
Deferral(8);
// lock servers cache
try_stderrcode(pthread_mutex_lock(&self->servers_cache_mutex));
Defer(pthread_mutex_unlock(&self->servers_cache_mutex));
u32 servers_count = List_len(self->servers_cache_list, Server);
if(servers_count == 0){
printf("No servers found in cache\n");
Return RESULT_VOID;
}
for(u32 id = 0; id < servers_count; id++){
Server* row = &List_index(self->servers_cache_list, Server, id);
printf("[%02u] "FMT_str"\n", id, row->name_len, row->name);
}
char buf[32];
u32 id = -1;
while(true) {
printf("Type 'q' to cancel\n");
printf("Select server (number): ");
try_void(term_readLine(buf, sizeof(buf)));
str input_line = str_from_cstr(buf);
str_trim(&input_line, true);
if(str_equals(input_line, STR("q"))){
Return RESULT_VOID;
}
if(sscanf(buf, FMT_u32, &id) != 1){
printf("ERROR: not a number\n");
}
else if(id >= servers_count){
printf("ERROR: not a server number: %u\n", id);
}
else break;
}
Server* server = &List_index(self->servers_cache_list, Server, id);
printf("Connecting to '"FMT_str"'...\n", server->address_len, server->address);
try_void(Client_connect(self->client, server->address, server->pk_base64));
printf("Connection established\n");
bool server_info_changed = false;
// update cached server name
str name = str_null;
try_void(Client_getServerName(self->client, &name));
if(!str_equals(name, str_construct(server->name, server->name_len, true))){
server_info_changed = true;
if(name.size > SERVER_NAME_SIZE_MAX)
name.size = SERVER_NAME_SIZE_MAX;
server->name_len = name.size;
memcpy(server->name, name.data, server->name_len);
}
// update cached server description
str desc = str_null;
try_void(Client_getServerDescription(self->client, &desc));
if(!str_equals(desc, str_construct(server->desc, server->desc_len, true))){
server_info_changed = true;
if(desc.size > SERVER_DESC_SIZE_MAX)
desc.size = SERVER_DESC_SIZE_MAX;
server->desc_len = desc.size;
memcpy(server->desc, desc.data, server->desc_len);
}
if(server_info_changed){
try_void(idb_updateRow(self->db_servers_table, id, server));
}
try_void(ClientCLI_showServerInfo(self, server));
Return RESULT_VOID;
}
static Result(void) ClientCLI_showServerInfo(ClientCLI* self, Server* server){
Deferral(8);
(void)self;
printf("Server Name: "FMT_str"\n", server->name_len, server->name);
printf("Host Address: "FMT_str"\n", server->address_len, server->address);
printf("Description:\n"FMT_str"\n\n", server->desc_len, server->desc);
printf("Public Key:\n" FMT_str"\n\n", server->pk_base64_len, server->pk_base64);
printf("Type 'register' if you don't have an account on the server.\n");
printf("Type 'login' to authorize on the server.\n");
Return RESULT_VOID;
}
static Result(void) ClientCLI_openUserDB(ClientCLI* self){
Deferral(8);
@@ -207,42 +316,105 @@ static Result(void) ClientCLI_openUserDB(ClientCLI* self){
Array(u8) user_data_key = Client_getUserDataKey(self->client);
str user_db_dir = str_from_cstr(strcat_malloc("client-db", path_seps, username.data));
Defer(free(user_db_dir.data));
try(self->user_db, p, idb_open(user_db_dir, user_data_key));
try(self->db, p, idb_open(user_db_dir, user_data_key));
// load servers table
pthread_mutex_init(&self->servers_cache_mutex, NULL);
try(self->db_servers_table, p, idb_getOrCreateTable(self->db, STR("servers"), sizeof(Server)));
// load whole table to list
try(u64 servers_count, u, idb_getRowCount(self->db_servers_table));
self->servers_cache_list = List_alloc(Server, servers_count);
try_void(idb_getRows(self->db_servers_table, 0, self->servers_cache_list.data, servers_count));
self->servers_cache_list.size = sizeof(Server) * servers_count;
// build address-id map
HashMap_construct(&self->servers_addr_id_map, u64, NULL);
for(u64 id = 0; id < servers_count; id++){
Server* row = &List_index(self->servers_cache_list, Server, id);
str key = str_construct(row->address, row->address_len, true);
if(!HashMap_tryPush(&self->servers_addr_id_map, key, &id)){
Return RESULT_ERROR_FMT("duplicate server address '"FMT_str"'", key.size, key.data);
}
}
Return RESULT_VOID;
}
static Result(void) ClientCLI_saveServerInfo(ClientCLI* self,
cstr server_addr_cstr, cstr server_pk_base64,
str server_name, str server_description){
static Result(Server*) ClientCLI_saveServerInfo(ClientCLI* self,
str addr, str pk_base64, str name, str desc){
Deferral(8);
ServerInfo si;
memset(&si, 0, sizeof(ServerInfo));
// create new server info
Server server;
memset(&server, 0, sizeof(Server));
// address
si.address_len = strlen(server_addr_cstr);
memcpy(si.address, server_addr_cstr, si.address_len);
si.address[si.address_len] = 0;
if(addr.size > HOSTADDR_SIZE_MAX)
addr.size = HOSTADDR_SIZE_MAX;
server.address_len = addr.size;
memcpy(server.address, addr.data, server.address_len);
// public key
si.pk_base64_len = strlen(server_pk_base64);
memcpy(si.pk_base64, server_addr_cstr, si.pk_base64_len);
si.pk_base64[si.pk_base64_len] = 0;
if(pk_base64.size > PUBLIC_KEY_BASE64_SIZE_MAX)
pk_base64.size = PUBLIC_KEY_BASE64_SIZE_MAX;
server.pk_base64_len = pk_base64.size;
memcpy(server.pk_base64, pk_base64.data, server.pk_base64_len);
// name
si.name_len = server_name.size;
memcpy(si.name, server_name.data, si.name_len);
si.name[si.name_len] = 0;
if(name.size > SERVER_NAME_SIZE_MAX)
name.size = SERVER_NAME_SIZE_MAX;
server.name_len = name.size;
memcpy(server.name, name.data, server.name_len);
// description
si.desc_len = server_name.size;
memcpy(si.desc, server_description.data, si.desc_len);
si.desc[si.desc_len] = 0;
if(desc.size > SERVER_DESC_SIZE_MAX)
desc.size = SERVER_DESC_SIZE_MAX;
server.desc_len = desc.size;
memcpy(server.desc, desc.data, server.desc_len);
// TODO: check server_address_id_cache_map
(void)self;
// TODO: save server info to user's db
// lock servers cache
try_stderrcode(pthread_mutex_lock(&self->servers_cache_mutex));
Defer(pthread_mutex_unlock(&self->servers_cache_mutex));
// try find server id in cache
Server* cached_row_ptr = NULL;
u64* id_ptr = NULL;
id_ptr = HashMap_tryGetPtr(&self->servers_addr_id_map, addr);
if(id_ptr){
// update existing server
u64 id = *id_ptr;
try_void(idb_updateRow(self->db_servers_table, id, &server));
try_assert(id < List_len(self->servers_cache_list, Server));
cached_row_ptr = &List_index(self->servers_cache_list, Server, id);
memcpy(cached_row_ptr, &server, sizeof(Server));
}
else {
// push new server
try(u64 id, u, idb_pushRow(self->db_servers_table, &server));
try_assert(id == List_len(self->servers_cache_list, Server));
List_pushMany(&self->servers_cache_list, Server, &server, 1);
cached_row_ptr = &List_index(self->servers_cache_list, Server, id);
try_assert(HashMap_tryPush(&self->servers_addr_id_map, addr, &id));
}
Return RESULT_VALUE(p, cached_row_ptr);
}
static Result(void) ClientCLI_register(ClientCLI* self){
Deferral(8);
u64 user_id = 0;
try_void(Client_register(self->client, &user_id));
printf("Registered successfully\n");
printf("user_id: "FMT_u64"\n", user_id);
// TODO: use user_id somewhere
Return RESULT_VOID;
}
static Result(void) ClientCLI_login(ClientCLI* self){
Deferral(8);
u64 user_id = 0, landing_channel_id = 0;
try_void(Client_login(self->client, &user_id, &landing_channel_id));
printf("Authorized successfully\n");
printf("user_id: "FMT_u64", landing_channel_id: "FMT_u64"\n", user_id, landing_channel_id);
// TODO: use user_id, landing_channel_id somewhere
Return RESULT_VOID;
}