added methods for Client to send and receive messages

This commit is contained in:
2026-01-04 00:53:35 +05:00
parent 90e21bc5ae
commit 0132e71c88
13 changed files with 229 additions and 104 deletions

View File

@@ -1,5 +1,7 @@
#pragma once #pragma once
#include "tlibc/errors.h" #include "tlibc/errors.h"
#include "tlibc/time.h"
#include "tlibc/magic.h"
/// requires tlibc and tlibtoml init /// requires tlibc and tlibtoml init
Result(void) TcpChat_init(); Result(void) TcpChat_init();
@@ -63,6 +65,7 @@ typedef void (*LogFunction_t)(void* logger, cstr context, LogSeverity severity,
#define logWarn(format, ...) log(LogSeverity_Warn, format ,##__VA_ARGS__) #define logWarn(format, ...) log(LogSeverity_Warn, format ,##__VA_ARGS__)
#define logError(format, ...) log(LogSeverity_Error, format ,##__VA_ARGS__) #define logError(format, ...) log(LogSeverity_Error, format ,##__VA_ARGS__)
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// // // //
// Server // // Server //
@@ -82,6 +85,7 @@ void Server_free(Server* server);
Result(void) Server_run(Server* server); Result(void) Server_run(Server* server);
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// // // //
// Client // // Client //
@@ -117,3 +121,15 @@ Result(void) Client_register(Client* self, i64* out_user_id);
/// Authorize on connected server /// Authorize on connected server
Result(void) Client_login(Client* self, i64* out_user_id, i64* out_landing_channel_id); Result(void) Client_login(Client* self, i64* out_user_id, i64* out_landing_channel_id);
/// @param out_timestamp timestamp received from server
/// @return message id received from server
Result(i64) Client_sendMessage(Client* self, i64 channel_id, Array(u8) content, DateTime* out_timestamp);
/// Receive a bunch of messages from the server to a client internal buffer
/// @return number of messages received
Result(u32) Client_receiveMessageBlock(Client* self, i64 channel_id, i64 first_message_id, u32 messages_count);
/// Read message saved in client internal buffer.
/// @return number of bytes written in dst_content
Result(u32) Client_popMessage(Client* self, Array(u8) dst_content, i64* message_id, i64* sender_id, DateTime* timestamp_utc);

View File

@@ -8,6 +8,7 @@ void ServerConnection_close(ServerConnection* self){
EncryptedSocketTCP_destroy(&self->sock); EncryptedSocketTCP_destroy(&self->sock);
Array_u8_destroy(&self->token); Array_u8_destroy(&self->token);
Array_u8_destroy(&self->session_key); Array_u8_destroy(&self->session_key);
MessageBlock_destroy(&self->received_message_block);
free(self); free(self);
} }
@@ -73,6 +74,8 @@ Result(ServerConnection*) ServerConnection_open(Client* client, cstr server_addr
PacketType_ServerHandshake)); PacketType_ServerHandshake));
conn->session_id = server_handshake.session_id; conn->session_id = server_handshake.session_id;
MessageBlock_alloc(&conn->received_message_block);
success = true; success = true;
Return RESULT_VALUE(p, conn); Return RESULT_VALUE(p, conn);
} }

View File

@@ -80,7 +80,6 @@ Result(void) Client_register(Client* self, i64* out_user_id){
PacketHeader req_head, res_head; PacketHeader req_head, res_head;
RegisterRequest req; RegisterRequest req;
RegisterResponse res; RegisterResponse res;
// TODO: hash token with server public key
try_void(RegisterRequest_tryConstruct(&req, &req_head, self->username, self->conn->token)); try_void(RegisterRequest_tryConstruct(&req, &req_head, self->username, self->conn->token));
try_void(sendRequest(&self->conn->sock, &req_head, &req)); try_void(sendRequest(&self->conn->sock, &req_head, &req));
try_void(recvResponse(&self->conn->sock, &res_head, &res, PacketType_RegisterResponse)); try_void(recvResponse(&self->conn->sock, &res_head, &res, PacketType_RegisterResponse));
@@ -98,7 +97,6 @@ Result(void) Client_login(Client* self, i64* out_user_id, i64* out_landing_chann
PacketHeader req_head, res_head; PacketHeader req_head, res_head;
LoginRequest req; LoginRequest req;
LoginResponse res; LoginResponse res;
// TODO: hash token with server public key
try_void(LoginRequest_tryConstruct(&req, &req_head, self->username, self->conn->token)); try_void(LoginRequest_tryConstruct(&req, &req_head, self->username, self->conn->token));
try_void(sendRequest(&self->conn->sock, &req_head, &req)); try_void(sendRequest(&self->conn->sock, &req_head, &req));
try_void(recvResponse(&self->conn->sock, &res_head, &res, PacketType_LoginResponse)); try_void(recvResponse(&self->conn->sock, &res_head, &res, PacketType_LoginResponse));
@@ -108,3 +106,71 @@ Result(void) Client_login(Client* self, i64* out_user_id, i64* out_landing_chann
Return RESULT_VOID; Return RESULT_VOID;
} }
Result(i64) Client_sendMessage(Client* self, i64 channel_id, Array(u8) content, DateTime* out_timestamp){
Deferral(1);
try_assert(self != NULL);
try_assert(self->conn != NULL && "didn't connect to a server yet");
try_assert(content.len >= MESSAGE_SIZE_MIN && content.len <= MESSAGE_SIZE_MAX);
PacketHeader req_head, res_head;
SendMessageRequest req;
SendMessageResponse res;
SendMessageRequest_construct(&req, &req_head, channel_id, content.len);
try_void(sendRequest(&self->conn->sock, &req_head, &req));
try_void(recvResponse(&self->conn->sock, &res_head, &res, PacketType_SendMessageResponse));
*out_timestamp = res.timestamp;
Return RESULT_VALUE(i, res.message_id);
}
Result(u32) Client_receiveMessageBlock(Client* self, i64 channel_id, i64 first_message_id, u32 messages_count){
Deferral(1);
try_assert(self != NULL);
try_assert(self->conn != NULL && "didn't connect to a server yet");
PacketHeader req_head, res_head;
GetMessageBlockRequest req;
GetMessageBlockResponse res;
GetMessageBlockRequest_construct(&req, &req_head, channel_id, first_message_id, messages_count);
try_void(sendRequest(&self->conn->sock, &req_head, &req));
try_void(recvResponse(&self->conn->sock, &res_head, &res, PacketType_GetMessageBlockResponse));
self->conn->received_message_block.messages_count = res.messages_count;
self->conn->received_message_block.datum.len = res.data_size;
try_void(
EncryptedSocketTCP_recv(
&self->conn->sock,
self->conn->received_message_block.datum,
SocketRecvFlag_WholeBuffer
)
);
Return RESULT_VALUE(u, res.messages_count);
}
Result(u32) Client_popMessage(Client* self, Array(u8) dst_content,
i64* message_id, i64* sender_id, DateTime* timestamp_utc)
{
Deferral(1);
try_assert(self != NULL);
try_assert(self->conn != NULL && "didn't connect to a server yet");
try_assert(dst_content.len >= MESSAGE_SIZE_MAX);
MessageMeta msg_meta = {0};
try(bool read_success, u,
MessageBlock_readMessage(
&self->conn->received_message_block,
&msg_meta,
dst_content
)
);
if(!read_success){
Return RESULT_VALUE(u, 0);
}
*message_id = msg_meta.id;
*sender_id = msg_meta.sender_id;
*timestamp_utc = msg_meta.timestamp;
Return RESULT_VALUE(u, msg_meta.data_size);
}

View File

@@ -3,6 +3,7 @@
#include "cryptography/AES.h" #include "cryptography/AES.h"
#include "cryptography/RSA.h" #include "cryptography/RSA.h"
#include "network/encrypted_sockets.h" #include "network/encrypted_sockets.h"
#include "network/tcp-chat-protocol/v1.h"
typedef struct ServerConnection ServerConnection; typedef struct ServerConnection ServerConnection;
@@ -12,7 +13,6 @@ typedef struct Client {
ServerConnection* conn; ServerConnection* conn;
} Client; } Client;
typedef struct ServerConnection { typedef struct ServerConnection {
Client* client; Client* client;
EndpointIPv4 server_end; EndpointIPv4 server_end;
@@ -23,6 +23,7 @@ typedef struct ServerConnection {
EncryptedSocketTCP sock; EncryptedSocketTCP sock;
i64 session_id; i64 session_id;
i64 user_id; i64 user_id;
MessageBlock received_message_block;
} ServerConnection; } ServerConnection;
/// @param server_addr_cstr /// @param server_addr_cstr

View File

@@ -1,7 +1,5 @@
#include "RSA.h" #include "RSA.h"
#include <assert.h> #include <assert.h>
#include "bearssl_x509.h"
#include "bearssl_pem.h"
#include "tlibc/base64.h" #include "tlibc/base64.h"
// https://crypto.stackexchange.com/questions/3110/impacts-of-not-using-rsa-exponent-of-65537 // https://crypto.stackexchange.com/questions/3110/impacts-of-not-using-rsa-exponent-of-65537

View File

@@ -142,62 +142,67 @@ void RegisterResponse_construct(RegisterResponse *ptr, PacketHeader* header,
ptr->user_id = user_id; ptr->user_id = user_id;
} }
Result(u32) MessageBlock_writeMessage( Result(bool) MessageBlock_writeMessage(MessageBlock* block,
MessageMeta* msg, Array(u8) msg_content, const MessageMeta* msg_meta, const Array(u8) msg_content)
MessageBlockMeta* block_meta, Array(u8)* block_free_part)
{ {
Deferral(1); Deferral(1);
try_assert(msg->data_size >= MESSAGE_SIZE_MIN && msg->data_size <= MESSAGE_SIZE_MAX);
try_assert(msg->data_size <= msg_content.len);
u32 offset_increment = sizeof(MessageMeta) + msg->data_size; // check msg_meta
if(block_free_part->len < offset_increment){ try_assert(msg_meta->magic.n == MESSAGE_MAGIC.n);
Return RESULT_VALUE(u, 0); try_assert(msg_meta->data_size >= MESSAGE_SIZE_MIN && msg_meta->data_size <= MESSAGE_SIZE_MAX);
try_assert(msg_meta->data_size <= msg_content.len);
try_assert(msg_meta->id > 0);
try_assert(msg_meta->sender_id > 0);
try_assert(msg_meta->timestamp.d.year > 2024);
// check block->datum.len
if(block->datum.len < block->offset + sizeof(MessageMeta) + msg_meta->data_size){
Return RESULT_VALUE(u, false);
} }
memcpy(block_free_part->data, msg, sizeof(MessageMeta)); // write msg_meta
block_free_part->data += sizeof(MessageMeta); memcpy(block->datum.data + block->offset, msg_meta, sizeof(MessageMeta));
block_free_part->len -= sizeof(MessageMeta); block->offset += sizeof(MessageMeta);
memcpy(block_free_part->data, msg_content.data, msg->data_size); // write msg_content
block_free_part->data += msg->data_size; memcpy(block->datum.data + block->offset, msg_content.data, msg_meta->data_size);
block_free_part->len -= msg->data_size; block->offset += msg_meta->data_size;
if(block_meta->message_count == 0)
block_meta->first_message_id = msg->id;
block_meta->message_count++;
block_meta->data_size += offset_increment;
Return RESULT_VALUE(u, offset_increment); block->messages_count++;
Return RESULT_VALUE(u, true);
} }
Result(u32) MessageBlock_readMessage( Result(bool) MessageBlock_readMessage(MessageBlock* block,
Array(u8)* block_unread_part, MessageMeta* msg_meta, Array(u8) msg_content)
MessageMeta* msg, Array(u8) msg_content)
{ {
Deferral(1); Deferral(1);
try_assert(block_unread_part->len >= sizeof(MessageMeta) + MESSAGE_SIZE_MIN);
try_assert(msg_content.len >= MESSAGE_SIZE_MIN && msg_content.len <= MESSAGE_SIZE_MAX);
memcpy(msg, block_unread_part->data, sizeof(MessageMeta));
block_unread_part->data += sizeof(MessageMeta);
block_unread_part->len -= sizeof(MessageMeta);
if(msg->magic.n != MESSAGE_MAGIC.n){ // check block
Return RESULT_VALUE(u, 0); if(block->messages_count == 0){
Return RESULT_VALUE(u, false);
} }
try_assert(block_unread_part->len >= msg->data_size); try_assert(block->datum.len >= block->offset + sizeof(MessageMeta) + MESSAGE_SIZE_MIN);
try_assert(msg->data_size >= MESSAGE_SIZE_MIN && msg->data_size <= MESSAGE_SIZE_MAX); // check msg_content.len
try_assert(msg->id > 0); try_assert(msg_content.len >= MESSAGE_SIZE_MAX);
try_assert(msg->sender_id > 0);
try_assert(msg->timestamp.d.year > 2024); // read msg_meta
memcpy(msg_meta, block->datum.data + block->offset, sizeof(MessageMeta));
block->offset += sizeof(MessageMeta);
memcpy(msg_content.data, block_unread_part->data, msg->data_size); // check msg_meta
block_unread_part->data += msg->data_size; try_assert(msg_meta->magic.n == MESSAGE_MAGIC.n);
block_unread_part->len -= msg->data_size; try_assert(msg_meta->data_size >= MESSAGE_SIZE_MIN && msg_meta->data_size <= MESSAGE_SIZE_MAX);
try_assert(msg_meta->data_size <= msg_content.len);
try_assert(msg_meta->id > 0);
try_assert(msg_meta->sender_id > 0);
try_assert(msg_meta->timestamp.d.year > 2024);
try_assert(block->datum.len >= block->offset + msg_meta->data_size);
u32 offset_increment = sizeof(MessageMeta) + msg->data_size; // read msg_content
Return RESULT_VALUE(u, offset_increment); memcpy(msg_content.data, block->datum.data + block->offset, msg_meta->data_size);
block->offset += msg_meta->data_size;
block->messages_count--;
Return RESULT_VALUE(u, true);
} }
void SendMessageRequest_construct(SendMessageRequest *ptr, PacketHeader *header, void SendMessageRequest_construct(SendMessageRequest *ptr, PacketHeader *header,
@@ -219,19 +224,20 @@ void SendMessageResponse_construct(SendMessageResponse *ptr, PacketHeader *heade
} }
void GetMessageBlockRequest_construct(GetMessageBlockRequest *ptr, PacketHeader *header, void GetMessageBlockRequest_construct(GetMessageBlockRequest *ptr, PacketHeader *header,
i64 channel_id, i64 first_message_id, u32 message_count) i64 channel_id, i64 first_message_id, u32 messages_count)
{ {
_PacketHeader_construct(GetMessageBlockRequest); _PacketHeader_construct(GetMessageBlockRequest);
zeroStruct(ptr); zeroStruct(ptr);
ptr->channel_id = channel_id; ptr->channel_id = channel_id;
ptr->first_message_id = first_message_id; ptr->first_message_id = first_message_id;
ptr->message_count = message_count; ptr->messages_count = messages_count;
} }
void GetMessageBlockResponse_construct(GetMessageBlockResponse *ptr, PacketHeader *header, void GetMessageBlockResponse_construct(GetMessageBlockResponse *ptr, PacketHeader *header,
MessageBlockMeta* block_meta) u32 messages_count, u32 data_size)
{ {
_PacketHeader_construct(GetMessageBlockResponse); _PacketHeader_construct(GetMessageBlockResponse);
zeroStruct(ptr); zeroStruct(ptr);
memcpy(&ptr->block_meta, block_meta, sizeof(MessageBlockMeta)); ptr->messages_count = messages_count;
ptr->data_size = data_size;
} }

View File

@@ -129,30 +129,63 @@ typedef struct MessageMeta {
i64 id; i64 id;
i64 sender_id; i64 sender_id;
DateTime timestamp; /* UTC */ DateTime timestamp; /* UTC */
} ALIGN_PACKET_STRUCT MessageMeta; } ATTRIBUTE_ALIGNED(8) MessageMeta;
#define MESSAGE_MAGIC ((Magic32){ .bytes = { 'M', 's', 'g', 'S' } }) #define MessageMeta_construct(DATA_SIZE, MESSAGE_ID, SENDER_ID, TIMESTAMP) ((MessageMeta){ \
.magic = MESSAGE_MAGIC, \
.data_size = DATA_SIZE, \
.id = MESSAGE_ID, \
.sender_id = SENDER_ID, \
.timestamp = TIMESTAMP \
})
typedef struct MessageBlockMeta { #define MESSAGE_MAGIC ((Magic32){ .bytes = { 'M', 's', 'g', '1' } })
i64 first_message_id;
u32 message_count;
u32 data_size;
} ALIGN_PACKET_STRUCT MessageBlockMeta;
/// @brief write msg_meta and msg_meta->data_size bytes from msg_content to buffer typedef struct MessageBlock {
/// @param block_meta set to {0} if block is empty yet Array(u8) datum; // sequence(MessageMeta, byte[MessageMeta.data_size])
/// @param block_free_part .data and .len are adjusted to point to free part u32 messages_count;
/// @return amount of bytes written to block, may be 0 if msg_meta and msg_content don't fit u32 offset;
Result(u32) MessageBlock_writeMessage( } MessageBlock;
MessageMeta* msg_meta, Array(u8) msg_content,
MessageBlockMeta* block_meta, Array(u8)* block_free_part);
/// @brief read message meta and content from buffer static inline void MessageBlock_construct(MessageBlock* self, Array(u8) datum, u32 messages_count){
/// @param block_unread_part .data and .len are adjusted to point to unread part self->datum = datum;
/// @param msg_content .len must be >= MESSAGE_SIZE_MAX self->messages_count = messages_count;
/// @return amount of bytes read from block, may be 0 if it doesn't start with MESSAGE_MAGIC self->offset = 0;
Result(u32) MessageBlock_readMessage( }
Array(u8)* block_unread_part,
static inline void MessageBlock_alloc(MessageBlock* self){
self->datum = Array_u8_alloc(MESSAGE_BLOCK_COUNT_MAX * (sizeof(MessageMeta) + MESSAGE_SIZE_MAX));
Array_u8_memset(&self->datum, 0);
self->messages_count = 0;
self->offset = 0;
}
static inline void MessageBlock_reset(MessageBlock* self){
Array_u8_memset(&self->datum, 0);
self->messages_count = 0;
self->offset = 0;
}
static inline void MessageBlock_destroy(MessageBlock* self){
if(!self)
return;
Array_u8_destroy(&self->datum);
}
/// @brief write msg_meta and msg_meta->data_size bytes from msg_content to block and increase block.messages_count
/// @param block use MessageBlock_alloc() to create empty block
/// @param msg_meta use MessageMeta_construct() to create message metadata
/// @param msg_content array of size >= msg_meta.data_size
/// @return false if msg_meta and msg_content don't fit in block.datum
Result(bool) MessageBlock_writeMessage(MessageBlock* block,
const MessageMeta* msg_meta, const Array(u8) msg_content);
/// @brief read msg_meta and msg_content from block and decrease block.messages_count
/// @param block a block with correct .datum and .messages_count
/// @param msg_meta out meta copied from block_data
/// @param msg_content out content copied from block_data. Array of size >= MESSAGE_SIZE_MAX
/// @return false if there are no messages to read (block.messages_count == 0)
Result(bool) MessageBlock_readMessage(MessageBlock* block,
MessageMeta* msg_meta, Array(u8) msg_content); MessageMeta* msg_meta, Array(u8) msg_content);
@@ -178,18 +211,19 @@ void SendMessageResponse_construct(SendMessageResponse* ptr, PacketHeader* heade
typedef struct GetMessageBlockRequest { typedef struct GetMessageBlockRequest {
i64 channel_id; i64 channel_id;
i64 first_message_id; i64 first_message_id;
u32 message_count; u32 messages_count;
} ALIGN_PACKET_STRUCT GetMessageBlockRequest; } ALIGN_PACKET_STRUCT GetMessageBlockRequest;
void GetMessageBlockRequest_construct(GetMessageBlockRequest* ptr, PacketHeader* header, void GetMessageBlockRequest_construct(GetMessageBlockRequest* ptr, PacketHeader* header,
i64 channel_id, i64 first_message_id, u32 message_count); i64 channel_id, i64 first_message_id, u32 messages_count);
typedef struct GetMessageBlockResponse { typedef struct GetMessageBlockResponse {
MessageBlockMeta block_meta; u32 messages_count;
u32 data_size;
/* stream of size data_size : sequence (MessageMeta, byte[MessageMeta.data_size]) */ /* stream of size data_size : sequence (MessageMeta, byte[MessageMeta.data_size]) */
} ALIGN_PACKET_STRUCT GetMessageBlockResponse; } ALIGN_PACKET_STRUCT GetMessageBlockResponse;
void GetMessageBlockResponse_construct(GetMessageBlockResponse* ptr, PacketHeader* header, void GetMessageBlockResponse_construct(GetMessageBlockResponse* ptr, PacketHeader* header,
MessageBlockMeta* block_meta); u32 messages_count, u32 data_size);

View File

@@ -5,7 +5,7 @@ void ClientConnection_close(ClientConnection* conn){
return; return;
EncryptedSocketTCP_destroy(&conn->sock); EncryptedSocketTCP_destroy(&conn->sock);
Array_u8_destroy(&conn->session_key); Array_u8_destroy(&conn->session_key);
Array_u8_destroy(&conn->message_block); MessageBlock_destroy(&conn->message_block);
Array_u8_destroy(&conn->message_content); Array_u8_destroy(&conn->message_content);
ServerQueries_free(conn->queries); ServerQueries_free(conn->queries);
tsqlite_connection_close(conn->db); tsqlite_connection_close(conn->db);
@@ -26,7 +26,7 @@ Result(ClientConnection*) ClientConnection_accept(ConnectionHandlerArgs* args)
conn->session_id = args->session_id; conn->session_id = args->session_id;
// buffers // buffers
conn->message_block = Array_u8_alloc(MESSAGE_BLOCK_COUNT_MAX * (sizeof(MessageMeta) + MESSAGE_SIZE_MAX)); MessageBlock_alloc(&conn->message_block);
conn->message_content = Array_u8_alloc(MESSAGE_SIZE_MAX); conn->message_content = Array_u8_alloc(MESSAGE_SIZE_MAX);
// database // database

View File

@@ -63,14 +63,14 @@ Result(void) Channel_saveMessage(ServerQueries* q,
Result(void) Channel_loadMessageBlock(ServerQueries* q, Result(void) Channel_loadMessageBlock(ServerQueries* q,
i64 channel_id, i64 first_message_id, u32 count, i64 channel_id, i64 first_message_id, u32 count,
MessageBlockMeta* block_meta, Array(u8) block_data) MessageBlock* block)
{ {
Deferral(4); Deferral(4);
try_assert(channel_id > 0);
try_assert(block_data.len >= count * (sizeof(MessageMeta) + MESSAGE_SIZE_MAX));
if(count == 0){ if(count == 0){
Return RESULT_VOID; Return RESULT_VOID;
} }
try_assert(channel_id > 0);
try_assert(block->datum.len >= count * (sizeof(MessageMeta) + MESSAGE_SIZE_MAX));
tsqlite_statement* st = q->messages.get_block; tsqlite_statement* st = q->messages.get_block;
Defer(tsqlite_statement_reset(st)); Defer(tsqlite_statement_reset(st));
@@ -78,9 +78,7 @@ Result(void) Channel_loadMessageBlock(ServerQueries* q,
try_void(tsqlite_statement_bind_i64(st, "$first_message_id", first_message_id)); try_void(tsqlite_statement_bind_i64(st, "$first_message_id", first_message_id));
try_void(tsqlite_statement_bind_i64(st, "$count", count)); try_void(tsqlite_statement_bind_i64(st, "$count", count));
zeroStruct(block_meta); MessageBlock_reset(block);
MessageMeta msg_meta = {0};
Array(u8) msg_content;
str tmp_str = str_null; str tmp_str = str_null;
while(true){ while(true){
try(bool has_result, i, tsqlite_statement_step(st)); try(bool has_result, i, tsqlite_statement_step(st));
@@ -88,17 +86,24 @@ Result(void) Channel_loadMessageBlock(ServerQueries* q,
break; break;
// id // id
try(msg_meta.id, i, tsqlite_statement_getResult_i64(st)); try(i64 message_id, i, tsqlite_statement_getResult_i64(st));
// sender_id // sender_id
try(msg_meta.sender_id, i, tsqlite_statement_getResult_i64(st)); try(i64 sender_id, i, tsqlite_statement_getResult_i64(st));
// content // content
Array(u8) msg_content;
try_void(tsqlite_statement_getResult_blob(st, &msg_content)); try_void(tsqlite_statement_getResult_blob(st, &msg_content));
// timestamp // timestamp
try_void(tsqlite_statement_getResult_str(st, &tmp_str)); try_void(tsqlite_statement_getResult_str(st, &tmp_str));
try_void(DateTime_parse(tmp_str.data, &msg_meta.timestamp)); DateTime timestamp;
try_void(DateTime_parse(tmp_str.data, &timestamp));
try(u32 write_n, u, MessageBlock_writeMessage(&msg_meta, msg_content, block_meta, &block_data)); MessageMeta msg_meta = MessageMeta_construct(
try_assert(write_n > 0); msg_content.len,
message_id,
sender_id,
timestamp);
try(bool write_success, u, MessageBlock_writeMessage(block, &msg_meta, msg_content));
try_assert(write_success == true);
} }
Return RESULT_VOID; Return RESULT_VOID;

View File

@@ -22,12 +22,11 @@ Result(i64) Channel_saveMessage(ServerQueries* q,
i64 channel_id, i64 sender_id, Array(u8) content, i64 channel_id, i64 sender_id, Array(u8) content,
DateTime* out_timestamp_utc); DateTime* out_timestamp_utc);
/// @brief try to find `count` messages starting from `first_message_id` /// @brief try to find count messages with id >= first_message_id
/// @param out_meta writes here information about found messages, .count can be 0 if no messages found /// @param dst_block writes messages here. messages_count can be 0 if no messages were found
/// @param out_block .len must be >= count * (sizeof(MessageMeta) + MESSAGE_SIZE_MAX)
Result(void) Channel_loadMessageBlock(ServerQueries* q, Result(void) Channel_loadMessageBlock(ServerQueries* q,
i64 channel_id, i64 first_message_id, u32 count, i64 channel_id, i64 first_message_id, u32 count,
MessageBlockMeta* out_block_meta, Array(u8) out_block_data); MessageBlock* dst_block);
/// @return existing user id or 0 /// @return existing user id or 0

View File

@@ -21,8 +21,8 @@ declare_RequestHandler(GetMessageBlock)
Return RESULT_VOID; Return RESULT_VOID;
} }
// validate message_count // validate messages_count
if(req.message_count < 1 || req.message_count > MESSAGE_BLOCK_COUNT_MAX){ if(req.messages_count < 1 || req.messages_count > MESSAGE_BLOCK_COUNT_MAX){
try_void(sendErrorMessage(log_ctx, conn, res_head, try_void(sendErrorMessage(log_ctx, conn, res_head,
LogSeverity_Warn, STR("invalid message count in request") )); LogSeverity_Warn, STR("invalid message count in request") ));
Return RESULT_VOID; Return RESULT_VOID;
@@ -36,21 +36,20 @@ declare_RequestHandler(GetMessageBlock)
Return RESULT_VOID; Return RESULT_VOID;
} }
// reset block meta
zeroStruct(&conn->message_block_meta);
// get message block from channel // get message block from channel
try_void(Channel_loadMessageBlock(conn->queries, try_void(Channel_loadMessageBlock(conn->queries,
req.channel_id, req.first_message_id, req.message_count, req.channel_id, req.first_message_id, req.messages_count,
&conn->message_block_meta, conn->message_block)); &conn->message_block));
// send response // send response
GetMessageBlockResponse res; GetMessageBlockResponse res;
GetMessageBlockResponse_construct(&res, res_head, &conn->message_block_meta); GetMessageBlockResponse_construct(&res, res_head,
conn->message_block.messages_count, conn->message_block.offset);
try_void(EncryptedSocketTCP_sendStruct(&conn->sock, res_head)); try_void(EncryptedSocketTCP_sendStruct(&conn->sock, res_head));
try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res)); try_void(EncryptedSocketTCP_sendStruct(&conn->sock, &res));
if(conn->message_block_meta.data_size != 0){ if(conn->message_block.offset != 0){
try_void(EncryptedSocketTCP_send(&conn->sock, try_void(EncryptedSocketTCP_send(&conn->sock,
Array_u8_sliceTo(conn->message_block, conn->message_block_meta.data_size)) Array_u8_sliceTo(conn->message_block.datum, conn->message_block.offset))
); );
} }

View File

@@ -1,5 +1,4 @@
#include <pthread.h> #include <pthread.h>
#include "tlibc/filesystem.h"
#include "tlibc/time.h" #include "tlibc/time.h"
#include "server/server_internal.h" #include "server/server_internal.h"
#include "server/responses/responses.h" #include "server/responses/responses.h"

View File

@@ -38,9 +38,8 @@ typedef struct ClientConnection {
i64 user_id; // 0 for unauthorized i64 user_id; // 0 for unauthorized
/* buffers */ /* buffers */
MessageBlockMeta message_block_meta; MessageBlock message_block; // requested message block
Array(u8) message_block; Array(u8) message_content; // sent message
Array(u8) message_content;
/* database */ /* database */
tsqlite_connection* db; tsqlite_connection* db;