added methods for Client to send and receive messages
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
#include "tlibc/errors.h"
|
||||
#include "tlibc/time.h"
|
||||
#include "tlibc/magic.h"
|
||||
|
||||
/// requires tlibc and tlibtoml 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 logError(format, ...) log(LogSeverity_Error, format ,##__VA_ARGS__)
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Server //
|
||||
@@ -82,6 +85,7 @@ void Server_free(Server* server);
|
||||
|
||||
Result(void) Server_run(Server* server);
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Client //
|
||||
@@ -117,3 +121,15 @@ Result(void) Client_register(Client* self, i64* out_user_id);
|
||||
|
||||
/// Authorize on connected server
|
||||
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);
|
||||
|
||||
@@ -8,6 +8,7 @@ void ServerConnection_close(ServerConnection* self){
|
||||
EncryptedSocketTCP_destroy(&self->sock);
|
||||
Array_u8_destroy(&self->token);
|
||||
Array_u8_destroy(&self->session_key);
|
||||
MessageBlock_destroy(&self->received_message_block);
|
||||
free(self);
|
||||
}
|
||||
|
||||
@@ -73,6 +74,8 @@ Result(ServerConnection*) ServerConnection_open(Client* client, cstr server_addr
|
||||
PacketType_ServerHandshake));
|
||||
conn->session_id = server_handshake.session_id;
|
||||
|
||||
MessageBlock_alloc(&conn->received_message_block);
|
||||
|
||||
success = true;
|
||||
Return RESULT_VALUE(p, conn);
|
||||
}
|
||||
|
||||
@@ -80,7 +80,6 @@ Result(void) Client_register(Client* self, i64* out_user_id){
|
||||
PacketHeader req_head, res_head;
|
||||
RegisterRequest req;
|
||||
RegisterResponse res;
|
||||
// TODO: hash token with server public key
|
||||
try_void(RegisterRequest_tryConstruct(&req, &req_head, self->username, self->conn->token));
|
||||
try_void(sendRequest(&self->conn->sock, &req_head, &req));
|
||||
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;
|
||||
LoginRequest req;
|
||||
LoginResponse res;
|
||||
// TODO: hash token with server public key
|
||||
try_void(LoginRequest_tryConstruct(&req, &req_head, self->username, self->conn->token));
|
||||
try_void(sendRequest(&self->conn->sock, &req_head, &req));
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "cryptography/AES.h"
|
||||
#include "cryptography/RSA.h"
|
||||
#include "network/encrypted_sockets.h"
|
||||
#include "network/tcp-chat-protocol/v1.h"
|
||||
|
||||
typedef struct ServerConnection ServerConnection;
|
||||
|
||||
@@ -12,7 +13,6 @@ typedef struct Client {
|
||||
ServerConnection* conn;
|
||||
} Client;
|
||||
|
||||
|
||||
typedef struct ServerConnection {
|
||||
Client* client;
|
||||
EndpointIPv4 server_end;
|
||||
@@ -23,6 +23,7 @@ typedef struct ServerConnection {
|
||||
EncryptedSocketTCP sock;
|
||||
i64 session_id;
|
||||
i64 user_id;
|
||||
MessageBlock received_message_block;
|
||||
} ServerConnection;
|
||||
|
||||
/// @param server_addr_cstr
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "RSA.h"
|
||||
#include <assert.h>
|
||||
#include "bearssl_x509.h"
|
||||
#include "bearssl_pem.h"
|
||||
#include "tlibc/base64.h"
|
||||
|
||||
// https://crypto.stackexchange.com/questions/3110/impacts-of-not-using-rsa-exponent-of-65537
|
||||
|
||||
@@ -142,62 +142,67 @@ void RegisterResponse_construct(RegisterResponse *ptr, PacketHeader* header,
|
||||
ptr->user_id = user_id;
|
||||
}
|
||||
|
||||
Result(u32) MessageBlock_writeMessage(
|
||||
MessageMeta* msg, Array(u8) msg_content,
|
||||
MessageBlockMeta* block_meta, Array(u8)* block_free_part)
|
||||
Result(bool) MessageBlock_writeMessage(MessageBlock* block,
|
||||
const MessageMeta* msg_meta, const Array(u8) msg_content)
|
||||
{
|
||||
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;
|
||||
if(block_free_part->len < offset_increment){
|
||||
Return RESULT_VALUE(u, 0);
|
||||
// check msg_meta
|
||||
try_assert(msg_meta->magic.n == MESSAGE_MAGIC.n);
|
||||
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));
|
||||
block_free_part->data += sizeof(MessageMeta);
|
||||
block_free_part->len -= sizeof(MessageMeta);
|
||||
// write msg_meta
|
||||
memcpy(block->datum.data + block->offset, msg_meta, sizeof(MessageMeta));
|
||||
block->offset += sizeof(MessageMeta);
|
||||
|
||||
memcpy(block_free_part->data, msg_content.data, msg->data_size);
|
||||
block_free_part->data += msg->data_size;
|
||||
block_free_part->len -= msg->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;
|
||||
// write msg_content
|
||||
memcpy(block->datum.data + block->offset, msg_content.data, msg_meta->data_size);
|
||||
block->offset += msg_meta->data_size;
|
||||
|
||||
Return RESULT_VALUE(u, offset_increment);
|
||||
block->messages_count++;
|
||||
Return RESULT_VALUE(u, true);
|
||||
}
|
||||
|
||||
Result(u32) MessageBlock_readMessage(
|
||||
Array(u8)* block_unread_part,
|
||||
MessageMeta* msg, Array(u8) msg_content)
|
||||
Result(bool) MessageBlock_readMessage(MessageBlock* block,
|
||||
MessageMeta* msg_meta, Array(u8) msg_content)
|
||||
{
|
||||
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){
|
||||
Return RESULT_VALUE(u, 0);
|
||||
// check block
|
||||
if(block->messages_count == 0){
|
||||
Return RESULT_VALUE(u, false);
|
||||
}
|
||||
try_assert(block_unread_part->len >= msg->data_size);
|
||||
try_assert(msg->data_size >= MESSAGE_SIZE_MIN && msg->data_size <= MESSAGE_SIZE_MAX);
|
||||
try_assert(msg->id > 0);
|
||||
try_assert(msg->sender_id > 0);
|
||||
try_assert(msg->timestamp.d.year > 2024);
|
||||
try_assert(block->datum.len >= block->offset + sizeof(MessageMeta) + MESSAGE_SIZE_MIN);
|
||||
// check msg_content.len
|
||||
try_assert(msg_content.len >= MESSAGE_SIZE_MAX);
|
||||
|
||||
// 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);
|
||||
block_unread_part->data += msg->data_size;
|
||||
block_unread_part->len -= msg->data_size;
|
||||
// check msg_meta
|
||||
try_assert(msg_meta->magic.n == MESSAGE_MAGIC.n);
|
||||
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;
|
||||
Return RESULT_VALUE(u, offset_increment);
|
||||
// read msg_content
|
||||
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,
|
||||
@@ -219,19 +224,20 @@ void SendMessageResponse_construct(SendMessageResponse *ptr, PacketHeader *heade
|
||||
}
|
||||
|
||||
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);
|
||||
zeroStruct(ptr);
|
||||
ptr->channel_id = channel_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,
|
||||
MessageBlockMeta* block_meta)
|
||||
u32 messages_count, u32 data_size)
|
||||
{
|
||||
_PacketHeader_construct(GetMessageBlockResponse);
|
||||
zeroStruct(ptr);
|
||||
memcpy(&ptr->block_meta, block_meta, sizeof(MessageBlockMeta));
|
||||
ptr->messages_count = messages_count;
|
||||
ptr->data_size = data_size;
|
||||
}
|
||||
|
||||
@@ -129,30 +129,63 @@ typedef struct MessageMeta {
|
||||
i64 id;
|
||||
i64 sender_id;
|
||||
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 {
|
||||
i64 first_message_id;
|
||||
u32 message_count;
|
||||
u32 data_size;
|
||||
} ALIGN_PACKET_STRUCT MessageBlockMeta;
|
||||
#define MESSAGE_MAGIC ((Magic32){ .bytes = { 'M', 's', 'g', '1' } })
|
||||
|
||||
/// @brief write msg_meta and msg_meta->data_size bytes from msg_content to buffer
|
||||
/// @param block_meta set to {0} if block is empty yet
|
||||
/// @param block_free_part .data and .len are adjusted to point to free part
|
||||
/// @return amount of bytes written to block, may be 0 if msg_meta and msg_content don't fit
|
||||
Result(u32) MessageBlock_writeMessage(
|
||||
MessageMeta* msg_meta, Array(u8) msg_content,
|
||||
MessageBlockMeta* block_meta, Array(u8)* block_free_part);
|
||||
typedef struct MessageBlock {
|
||||
Array(u8) datum; // sequence(MessageMeta, byte[MessageMeta.data_size])
|
||||
u32 messages_count;
|
||||
u32 offset;
|
||||
} MessageBlock;
|
||||
|
||||
/// @brief read message meta and content from buffer
|
||||
/// @param block_unread_part .data and .len are adjusted to point to unread part
|
||||
/// @param msg_content .len must be >= MESSAGE_SIZE_MAX
|
||||
/// @return amount of bytes read from block, may be 0 if it doesn't start with MESSAGE_MAGIC
|
||||
Result(u32) MessageBlock_readMessage(
|
||||
Array(u8)* block_unread_part,
|
||||
static inline void MessageBlock_construct(MessageBlock* self, Array(u8) datum, u32 messages_count){
|
||||
self->datum = datum;
|
||||
self->messages_count = messages_count;
|
||||
self->offset = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -178,18 +211,19 @@ void SendMessageResponse_construct(SendMessageResponse* ptr, PacketHeader* heade
|
||||
typedef struct GetMessageBlockRequest {
|
||||
i64 channel_id;
|
||||
i64 first_message_id;
|
||||
u32 message_count;
|
||||
u32 messages_count;
|
||||
} ALIGN_PACKET_STRUCT GetMessageBlockRequest;
|
||||
|
||||
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 {
|
||||
MessageBlockMeta block_meta;
|
||||
u32 messages_count;
|
||||
u32 data_size;
|
||||
/* stream of size data_size : sequence (MessageMeta, byte[MessageMeta.data_size]) */
|
||||
} ALIGN_PACKET_STRUCT GetMessageBlockResponse;
|
||||
|
||||
void GetMessageBlockResponse_construct(GetMessageBlockResponse* ptr, PacketHeader* header,
|
||||
MessageBlockMeta* block_meta);
|
||||
u32 messages_count, u32 data_size);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ void ClientConnection_close(ClientConnection* conn){
|
||||
return;
|
||||
EncryptedSocketTCP_destroy(&conn->sock);
|
||||
Array_u8_destroy(&conn->session_key);
|
||||
Array_u8_destroy(&conn->message_block);
|
||||
MessageBlock_destroy(&conn->message_block);
|
||||
Array_u8_destroy(&conn->message_content);
|
||||
ServerQueries_free(conn->queries);
|
||||
tsqlite_connection_close(conn->db);
|
||||
@@ -26,7 +26,7 @@ Result(ClientConnection*) ClientConnection_accept(ConnectionHandlerArgs* args)
|
||||
conn->session_id = args->session_id;
|
||||
|
||||
// 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);
|
||||
|
||||
// database
|
||||
|
||||
@@ -63,14 +63,14 @@ Result(void) Channel_saveMessage(ServerQueries* q,
|
||||
|
||||
Result(void) Channel_loadMessageBlock(ServerQueries* q,
|
||||
i64 channel_id, i64 first_message_id, u32 count,
|
||||
MessageBlockMeta* block_meta, Array(u8) block_data)
|
||||
MessageBlock* block)
|
||||
{
|
||||
Deferral(4);
|
||||
try_assert(channel_id > 0);
|
||||
try_assert(block_data.len >= count * (sizeof(MessageMeta) + MESSAGE_SIZE_MAX));
|
||||
if(count == 0){
|
||||
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;
|
||||
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, "$count", count));
|
||||
|
||||
zeroStruct(block_meta);
|
||||
MessageMeta msg_meta = {0};
|
||||
Array(u8) msg_content;
|
||||
MessageBlock_reset(block);
|
||||
str tmp_str = str_null;
|
||||
while(true){
|
||||
try(bool has_result, i, tsqlite_statement_step(st));
|
||||
@@ -88,17 +86,24 @@ Result(void) Channel_loadMessageBlock(ServerQueries* q,
|
||||
break;
|
||||
|
||||
// id
|
||||
try(msg_meta.id, i, tsqlite_statement_getResult_i64(st));
|
||||
try(i64 message_id, i, tsqlite_statement_getResult_i64(st));
|
||||
// sender_id
|
||||
try(msg_meta.sender_id, i, tsqlite_statement_getResult_i64(st));
|
||||
try(i64 sender_id, i, tsqlite_statement_getResult_i64(st));
|
||||
// content
|
||||
Array(u8) msg_content;
|
||||
try_void(tsqlite_statement_getResult_blob(st, &msg_content));
|
||||
// timestamp
|
||||
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, ×tamp));
|
||||
|
||||
try(u32 write_n, u, MessageBlock_writeMessage(&msg_meta, msg_content, block_meta, &block_data));
|
||||
try_assert(write_n > 0);
|
||||
MessageMeta msg_meta = MessageMeta_construct(
|
||||
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;
|
||||
|
||||
@@ -22,12 +22,11 @@ Result(i64) Channel_saveMessage(ServerQueries* q,
|
||||
i64 channel_id, i64 sender_id, Array(u8) content,
|
||||
DateTime* out_timestamp_utc);
|
||||
|
||||
/// @brief try to find `count` messages starting from `first_message_id`
|
||||
/// @param out_meta writes here information about found messages, .count can be 0 if no messages found
|
||||
/// @param out_block .len must be >= count * (sizeof(MessageMeta) + MESSAGE_SIZE_MAX)
|
||||
/// @brief try to find count messages with id >= first_message_id
|
||||
/// @param dst_block writes messages here. messages_count can be 0 if no messages were found
|
||||
Result(void) Channel_loadMessageBlock(ServerQueries* q,
|
||||
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
|
||||
|
||||
@@ -21,8 +21,8 @@ declare_RequestHandler(GetMessageBlock)
|
||||
Return RESULT_VOID;
|
||||
}
|
||||
|
||||
// validate message_count
|
||||
if(req.message_count < 1 || req.message_count > MESSAGE_BLOCK_COUNT_MAX){
|
||||
// validate messages_count
|
||||
if(req.messages_count < 1 || req.messages_count > MESSAGE_BLOCK_COUNT_MAX){
|
||||
try_void(sendErrorMessage(log_ctx, conn, res_head,
|
||||
LogSeverity_Warn, STR("invalid message count in request") ));
|
||||
Return RESULT_VOID;
|
||||
@@ -36,21 +36,20 @@ declare_RequestHandler(GetMessageBlock)
|
||||
Return RESULT_VOID;
|
||||
}
|
||||
|
||||
// reset block meta
|
||||
zeroStruct(&conn->message_block_meta);
|
||||
// get message block from channel
|
||||
try_void(Channel_loadMessageBlock(conn->queries,
|
||||
req.channel_id, req.first_message_id, req.message_count,
|
||||
&conn->message_block_meta, conn->message_block));
|
||||
req.channel_id, req.first_message_id, req.messages_count,
|
||||
&conn->message_block));
|
||||
|
||||
// send response
|
||||
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));
|
||||
if(conn->message_block_meta.data_size != 0){
|
||||
if(conn->message_block.offset != 0){
|
||||
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))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <pthread.h>
|
||||
#include "tlibc/filesystem.h"
|
||||
#include "tlibc/time.h"
|
||||
#include "server/server_internal.h"
|
||||
#include "server/responses/responses.h"
|
||||
|
||||
@@ -38,9 +38,8 @@ typedef struct ClientConnection {
|
||||
i64 user_id; // 0 for unauthorized
|
||||
|
||||
/* buffers */
|
||||
MessageBlockMeta message_block_meta;
|
||||
Array(u8) message_block;
|
||||
Array(u8) message_content;
|
||||
MessageBlock message_block; // requested message block
|
||||
Array(u8) message_content; // sent message
|
||||
|
||||
/* database */
|
||||
tsqlite_connection* db;
|
||||
|
||||
Reference in New Issue
Block a user