implemented server database and api for client database
This commit is contained in:
@@ -22,28 +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_execCommand(ClientCLI* self, str command, bool* stop);
|
||||
static Result(void) ClientCLI_openUserDB(ClientCLI* self);
|
||||
static Result(ServerInfo*) ClientCLI_saveServerInfo(ClientCLI* self,
|
||||
str addr, str pk_base64, str name, str desc);
|
||||
static Result(ServerInfo*) ClientCLI_joinNewServer(ClientCLI* self);
|
||||
static Result(ServerInfo*) ClientCLI_selectServerFromCache(ClientCLI* self);
|
||||
static Result(void) ClientCLI_showServerInfo(ClientCLI* self, ServerInfo* server);
|
||||
static Result(void) ClientCLI_execCommand(ClientCLI* self, str command, bool* stop);
|
||||
static Result(SavedServer*) ClientCLI_joinNewServer(ClientCLI* self);
|
||||
static Result(SavedServer*) ClientCLI_selectServerFromCache(ClientCLI* self);
|
||||
static Result(void) ClientCLI_showSavedServer(ClientCLI* self, SavedServer* 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->db);
|
||||
List_ServerInfo_destroy(&self->servers.list);
|
||||
HashMap_destroy(&self->servers.addr_id_map);
|
||||
ClientQueries_free(self->queries);
|
||||
tsqlite_connection_close(self->db);
|
||||
List_SavedServer_destroyWithElements(&self->saved_servers, SavedServer_destroy);
|
||||
}
|
||||
|
||||
void ClientCLI_construct(ClientCLI* self){
|
||||
zeroStruct(self);
|
||||
self->saved_servers = List_SavedServer_alloc(0);
|
||||
}
|
||||
|
||||
Result(void) ClientCLI_run(ClientCLI* self) {
|
||||
@@ -137,6 +137,33 @@ static Result(void) ClientCLI_askUserNameAndPassword(str* username_out, str* pas
|
||||
Return RESULT_VOID;
|
||||
}
|
||||
|
||||
static Result(void) ClientCLI_openUserDB(ClientCLI* self){
|
||||
Deferral(8);
|
||||
|
||||
str username = Client_getUserName(self->client);
|
||||
// TODO: encrypt user database
|
||||
// Array(u8) user_data_key = Client_getUserDataKey(self->client);
|
||||
|
||||
// build database file path
|
||||
try(char* user_dir, p, path_getUserDir());
|
||||
Defer(free(user_dir));
|
||||
char* db_path = strcat_malloc(
|
||||
user_dir,
|
||||
path_seps".local"path_seps"tcp-chat-client"path_seps"user-db"path_seps,
|
||||
username.data, ".sqlite"
|
||||
);
|
||||
Defer(free(db_path));
|
||||
printf("loading database '%s'\n", db_path);
|
||||
|
||||
try(self->db, p, ClientDatabase_open(db_path));
|
||||
try(self->queries, p, ClientQueries_compile(self->db));
|
||||
|
||||
// load whole servers table to list
|
||||
try_void(SavedServer_getAll(self->queries, &self->saved_servers));
|
||||
|
||||
Return RESULT_VOID;
|
||||
}
|
||||
|
||||
static Result(void) ClientCLI_execCommand(ClientCLI* self, str command, bool* stop){
|
||||
Deferral(64);
|
||||
|
||||
@@ -190,229 +217,136 @@ static Result(void) ClientCLI_execCommand(ClientCLI* self, str command, bool* st
|
||||
|
||||
static Result(void) ClientCLI_joinNewServer(ClientCLI* self){
|
||||
Deferral(8);
|
||||
bool success = false;
|
||||
|
||||
// ask server address
|
||||
const u32 address_alloc_size = HOSTADDR_SIZE_MAX + 1;
|
||||
str address = str_construct((char*)malloc(address_alloc_size), address_alloc_size, true);
|
||||
Defer(if(!success) str_destroy(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);
|
||||
try_void(term_readLine(address.data, address.len));
|
||||
address.len = strlen(address.data);
|
||||
str_trim(&address, true);
|
||||
|
||||
// ask server public key
|
||||
const u32 server_pk_alloc_size = PUBLIC_KEY_BASE64_SIZE_MAX + 1;
|
||||
str server_pk = str_construct((char*)malloc(server_pk_alloc_size), server_pk_alloc_size, true);
|
||||
Defer(if(!success) str_destroy(server_pk));
|
||||
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);
|
||||
try_void(term_readLine(server_pk.data, server_pk.len));
|
||||
server_pk.len = strlen(server_pk.data);
|
||||
str_trim(&server_pk, true);
|
||||
|
||||
printf("Connecting to server...\n");
|
||||
try_void(Client_connect(self->client, server_addr_cstr, server_pk_cstr));
|
||||
try_void(Client_connect(self->client, address.data, server_pk.data));
|
||||
printf("Connection established\n");
|
||||
|
||||
str server_name = str_null;
|
||||
str server_description = str_null;
|
||||
try_void(Client_getServerName(self->client, &server_name));
|
||||
Defer(if(!success) str_destroy(server_name));
|
||||
str server_description = str_null;
|
||||
try_void(Client_getServerDescription(self->client, &server_description));
|
||||
try(ServerInfo* server, p, ClientCLI_saveServerInfo(self,
|
||||
server_addr_str, server_pk_str,
|
||||
server_name, server_description));
|
||||
Defer(if(!success) str_destroy(server_description));
|
||||
|
||||
try_void(ClientCLI_showServerInfo(self, server));
|
||||
SavedServer server = {
|
||||
.address = address,
|
||||
.pk_base64 = server_pk,
|
||||
.name = server_name,
|
||||
.description = server_description
|
||||
};
|
||||
try_void(SavedServer_save(self->queries, &server));
|
||||
List_SavedServer_pushMany(&self->saved_servers, &server, 1);
|
||||
|
||||
try_void(ClientCLI_showSavedServer(self, &server));
|
||||
|
||||
success = true;
|
||||
Return RESULT_VOID;
|
||||
}
|
||||
|
||||
static Result(void) ClientCLI_selectServerFromCache(ClientCLI* self){
|
||||
Deferral(8);
|
||||
bool success = false;
|
||||
|
||||
// Lock table until this function returns.
|
||||
// It may not change any data in table, but it uses associated cache structures.
|
||||
idb_lockTable(self->servers.table);
|
||||
Defer(idb_unlockTable(self->servers.table));
|
||||
|
||||
u32 server_count = self->servers.list.len;
|
||||
if(server_count == 0){
|
||||
printf("No servers found in cache\n");
|
||||
u32 servers_count = self->saved_servers.len;
|
||||
if(servers_count == 0){
|
||||
printf("No saved servers found\n");
|
||||
Return RESULT_VOID;
|
||||
}
|
||||
|
||||
for(u32 id = 0; id < server_count; id++){
|
||||
ServerInfo* server = self->servers.list.data + id;
|
||||
for(u32 i = 0; i < servers_count; i++){
|
||||
SavedServer* server = &self->saved_servers.data[i];
|
||||
printf("[%02u] "FMT_str" "FMT_str"\n",
|
||||
id, server->address_len, server->address, server->name_len, server->name);
|
||||
i, str_unwrap(server->address), str_unwrap(server->name));
|
||||
}
|
||||
|
||||
char buf[32];
|
||||
u32 id = -1;
|
||||
u32 selected_i = -1;
|
||||
while(true) {
|
||||
printf("Type 'q' to cancel\n");
|
||||
printf("Select server (number): ");
|
||||
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){
|
||||
if(sscanf(buf, FMT_u32, &selected_i) != 1){
|
||||
printf("ERROR: not a number\n");
|
||||
}
|
||||
else if(id >= server_count){
|
||||
printf("ERROR: not a server number: %u\n", id);
|
||||
else if(selected_i >= servers_count){
|
||||
printf("ERROR: not a server number\n");
|
||||
}
|
||||
else break;
|
||||
}
|
||||
ServerInfo* server = self->servers.list.data + id;
|
||||
SavedServer* selected_server = &self->saved_servers.data[selected_i];
|
||||
|
||||
printf("Connecting to '"FMT_str"'...\n", server->address_len, server->address);
|
||||
try_void(Client_connect(self->client, server->address, server->pk_base64));
|
||||
printf("Connecting to '"FMT_str"'...\n", str_unwrap(selected_server->address));
|
||||
try_void(Client_connect(self->client, selected_server->address.data, selected_server->pk_base64.data));
|
||||
printf("Connection established\n");
|
||||
|
||||
// update server name
|
||||
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))){
|
||||
str updated_server_name = str_null;
|
||||
try_void(Client_getServerName(self->client, &updated_server_name));
|
||||
Defer(if(!success) str_destroy(updated_server_name));
|
||||
if(!str_equals(updated_server_name, selected_server->name)){
|
||||
server_info_changed = true;
|
||||
if(name.len > SERVER_NAME_SIZE_MAX)
|
||||
name.len = SERVER_NAME_SIZE_MAX;
|
||||
server->name_len = name.len;
|
||||
memcpy(server->name, name.data, server->name_len);
|
||||
selected_server->name = updated_server_name;
|
||||
}
|
||||
// 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))){
|
||||
|
||||
// update server description
|
||||
str updated_server_description = str_null;
|
||||
try_void(Client_getServerDescription(self->client, &updated_server_description));
|
||||
Defer(if(!success) str_destroy(updated_server_description));
|
||||
if(!str_equals(updated_server_description, selected_server->description)){
|
||||
server_info_changed = true;
|
||||
if(desc.len > SERVER_DESC_SIZE_MAX)
|
||||
desc.len = SERVER_DESC_SIZE_MAX;
|
||||
server->desc_len = desc.len;
|
||||
memcpy(server->desc, desc.data, server->desc_len);
|
||||
selected_server->description = updated_server_description;
|
||||
}
|
||||
|
||||
if(server_info_changed){
|
||||
try_void(idb_updateRow(self->servers.table, id, server, false));
|
||||
try_void(SavedServer_save(self->queries, selected_server));
|
||||
}
|
||||
|
||||
try_void(ClientCLI_showServerInfo(self, server));
|
||||
try_void(ClientCLI_showSavedServer(self, selected_server));
|
||||
|
||||
success = true;
|
||||
Return RESULT_VOID;
|
||||
}
|
||||
|
||||
static Result(void) ClientCLI_showServerInfo(ClientCLI* self, ServerInfo* server){
|
||||
static Result(void) ClientCLI_showSavedServer(ClientCLI* self, SavedServer* 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("Server Name: "FMT_str"\n", str_unwrap(server->name));
|
||||
printf("Host Address: "FMT_str"\n", str_unwrap(server->address));
|
||||
printf("Description:\n"FMT_str"\n\n", str_unwrap(server->description));
|
||||
printf("Public Key:\n" FMT_str"\n\n", str_unwrap(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);
|
||||
|
||||
str username = Client_getUserName(self->client);
|
||||
Array(u8) user_data_key = Client_getUserDataKey(self->client);
|
||||
str user_db_path = str_from_cstr(strcat_malloc("client-db", path_seps, username.data));
|
||||
Defer(free(user_db_path.data));
|
||||
try(self->db, p, idb_open(user_db_path, user_data_key));
|
||||
|
||||
// Lock DB until this function returns.
|
||||
idb_lockDB(self->db);
|
||||
Defer(idb_unlockDB(self->db));
|
||||
|
||||
// Load servers table
|
||||
try(self->servers.table, p,
|
||||
idb_getOrCreateTable(self->db, str_null, STR("servers"), sizeof(ServerInfo), false)
|
||||
);
|
||||
|
||||
// Lock table until this function returns.
|
||||
idb_lockTable(self->servers.table);
|
||||
Defer(idb_unlockTable(self->servers.table));
|
||||
|
||||
// load whole servers table to list
|
||||
try_void(
|
||||
idb_createListFromTable(self->servers.table, (void*)&self->servers.list, false)
|
||||
);
|
||||
|
||||
// build address-id map
|
||||
try(i64 server_count, u,
|
||||
idb_getRowCount(self->servers.table, false)
|
||||
);
|
||||
HashMap_construct(&self->servers.addr_id_map, i64, NULL);
|
||||
for(i64 id = 0; id < server_count; id++){
|
||||
ServerInfo* server = self->servers.list.data + id;
|
||||
str key = str_construct(server->address, server->address_len, true);
|
||||
if(!HashMap_tryPush(&self->servers.addr_id_map, key, &id)){
|
||||
Return RESULT_ERROR_FMT(
|
||||
"duplicate server address '"FMT_str"'",
|
||||
key.len, key.data);
|
||||
}
|
||||
}
|
||||
|
||||
Return RESULT_VOID;
|
||||
}
|
||||
|
||||
static Result(ServerInfo*) ClientCLI_saveServerInfo(ClientCLI* self,
|
||||
str addr, str pk_base64, str name, str desc){
|
||||
Deferral(8);
|
||||
|
||||
// create new server info
|
||||
ServerInfo server;
|
||||
zeroStruct(&server);
|
||||
// address
|
||||
if(addr.len > HOSTADDR_SIZE_MAX)
|
||||
addr.len = HOSTADDR_SIZE_MAX;
|
||||
server.address_len = addr.len;
|
||||
memcpy(server.address, addr.data, server.address_len);
|
||||
// public key
|
||||
if(pk_base64.len > PUBLIC_KEY_BASE64_SIZE_MAX)
|
||||
pk_base64.len = PUBLIC_KEY_BASE64_SIZE_MAX;
|
||||
server.pk_base64_len = pk_base64.len;
|
||||
memcpy(server.pk_base64, pk_base64.data, server.pk_base64_len);
|
||||
// name
|
||||
if(name.len > SERVER_NAME_SIZE_MAX)
|
||||
name.len = SERVER_NAME_SIZE_MAX;
|
||||
server.name_len = name.len;
|
||||
memcpy(server.name, name.data, server.name_len);
|
||||
// description
|
||||
if(desc.len > SERVER_DESC_SIZE_MAX)
|
||||
desc.len = SERVER_DESC_SIZE_MAX;
|
||||
server.desc_len = desc.len;
|
||||
memcpy(server.desc, desc.data, server.desc_len);
|
||||
|
||||
// Lock table until this function returns.
|
||||
// It may not change any data in table, but it uses associated cache structures.
|
||||
idb_lockTable(self->servers.table);
|
||||
Defer(idb_unlockTable(self->servers.table));
|
||||
|
||||
// try find server id in cache
|
||||
ServerInfo* cached_row_ptr = NULL;
|
||||
i64* id_ptr = NULL;
|
||||
id_ptr = HashMap_tryGetPtr(&self->servers.addr_id_map, addr);
|
||||
if(id_ptr){
|
||||
// update existing server
|
||||
i64 id = *id_ptr;
|
||||
try_void(idb_updateRow(self->servers.table, id, &server, false));
|
||||
try_assert(id < self->servers.list.len);
|
||||
cached_row_ptr = self->servers.list.data + id;
|
||||
memcpy(cached_row_ptr, &server, sizeof(ServerInfo));
|
||||
}
|
||||
else {
|
||||
// push new server
|
||||
try(i64 id, u, idb_pushRow(self->servers.table, &server, false));
|
||||
try_assert(id == self->servers.list.len);
|
||||
List_ServerInfo_pushMany(&self->servers.list, &server, 1);
|
||||
cached_row_ptr = self->servers.list.data + 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);
|
||||
|
||||
@@ -420,6 +354,7 @@ static Result(void) ClientCLI_register(ClientCLI* self){
|
||||
try_void(Client_register(self->client, &user_id));
|
||||
printf("Registered successfully\n");
|
||||
printf("user_id: "FMT_i64"\n", user_id);
|
||||
try_assert(user_id > 0);
|
||||
// TODO: use user_id somewhere
|
||||
|
||||
Return RESULT_VOID;
|
||||
@@ -432,6 +367,7 @@ static Result(void) ClientCLI_login(ClientCLI* self){
|
||||
try_void(Client_login(self->client, &user_id, &landing_channel_id));
|
||||
printf("Authorized successfully\n");
|
||||
printf("user_id: "FMT_i64", landing_channel_id: "FMT_i64"\n", user_id, landing_channel_id);
|
||||
try_assert(user_id > 0);
|
||||
// TODO: use user_id, landing_channel_id somewhere
|
||||
|
||||
Return RESULT_VOID;
|
||||
|
||||
Reference in New Issue
Block a user