Browse Source

Return submitted message identity and encode JSON safely

master
evgeny 6 days ago
parent
commit
a4ca31f406
  1. 7
      src/chat/chat_core.h
  2. 73
      src/chat/chat_msg.c

7
src/chat/chat_core.h

@ -66,13 +66,14 @@ struct chat_msg_submit {
int height; /* видео: высота */
uint8_t* data; /* содержимое сообщения, заимствовано обычным submit */
uint32_t data_len; /* размер data в байтах */
uint64_t timestamp; /* входное время; отправка назначает новый timestamp через db_sync */
uint64_t timestamp; /* успешная отправка возвращает назначенное время */
uint8_t message_id[32]; /* успешная обычная отправка возвращает общий ID */
uint64_t reply_to_ts; /* 0 = не ответ */
uint64_t reply_to_node_id; /* node_id автора исходного сообщения */
};
/* Подписать, сохранить и разослать сообщение. req/data остаются у вызывающего. */
void chat_core_submit_message(struct UTUN_INSTANCE* inst, struct chat_msg_submit* req);
/* Подписать, сохранить и разослать сообщение. req/data остаются у вызывающего. 0/-1. */
int chat_core_submit_message(struct UTUN_INSTANCE* inst, struct chat_msg_submit* req);
/* Отправка медиа-сообщения (вложение): src->dest (copy) или видео (transcode). */
void chat_core_submit_media_message(struct UTUN_INSTANCE* inst, struct chat_msg_submit* req);

73
src/chat/chat_msg.c

@ -6,6 +6,7 @@
#include "chat_core_priv.h"
#include "chat_event.h"
#include "chat_feedback.h"
#include "chat_setting.h"
#include "chat_whisper.h"
@ -71,45 +72,53 @@ static int msg_get_prev_chain_hash(struct chat_core_ctx* cc, const char* tbl, ui
sqlite3_finalize(st); return 0;
}
void chat_core_submit_message(struct UTUN_INSTANCE* inst, struct chat_msg_submit* req) {
struct chat_core_ctx* cc = CC(inst);
if (!cc || !cc->initialized) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: submit before init", CC_ID); return; }
if (!req) return;
DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "%s: SUBMIT ch=%s ct=%s len=%u",
CC_ID, req->channel_id, req->content_type, req->data_len);
int chat_core_submit_message(struct UTUN_INSTANCE* inst, struct chat_msg_submit* req) {
struct chat_core_ctx* cc = inst ? CC(inst) : NULL;
if (!cc || !cc->initialized || !req || (req->data_len && !req->data) || req->data_len > 4000) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: invalid message submission", CC_ID); return -1;
}
memset(req->message_id, 0, sizeof(req->message_id));
struct DB_SYNC_INSTANCE* si = si_find(inst, req->channel_id);
if (!si) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: no db_sync instance for ch=%s", CC_ID, req->channel_id); return; }
if (!si) { DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: no history for ch=%s", CC_ID, req->channel_id); return -1; }
char json[4096];
int off = snprintf(json, sizeof(json),
"{\"n\":%llu,\"ch\":\"%s\",\"ct\":\"%s\"",
(unsigned long long)cc->my_node_id, req->channel_id, req->content_type);
/* SQLite формирует JSON без обрезки и с экранированием текста/Unicode. */
const char* query = req->reply_to_ts
? "SELECT json_object('n',?1,'ch',?2,'ct',?3,'d',?4,'rt',?5,'rn',?6)"
: "SELECT json_object('n',?1,'ch',?2,'ct',?3,'d',?4)";
sqlite3_stmt* st = NULL;
if (sqlite3_prepare_v2(cc->db, query, -1, &st, NULL) != SQLITE_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: message JSON: %s", CC_ID, sqlite3_errmsg(cc->db)); return -1;
}
sqlite3_bind_int64(st, 1, (sqlite3_int64)cc->my_node_id);
sqlite3_bind_text(st, 2, req->channel_id, -1, SQLITE_STATIC);
sqlite3_bind_text(st, 3, req->content_type, -1, SQLITE_STATIC);
sqlite3_bind_text(st, 4, req->data ? (const char*)req->data : "", req->data_len, SQLITE_STATIC);
if (req->reply_to_ts) {
off += snprintf(json + off, sizeof(json) - off,
",\"rt\":%llu,\"rn\":%llu",
(unsigned long long)req->reply_to_ts, (unsigned long long)req->reply_to_node_id);
sqlite3_bind_int64(st, 5, (sqlite3_int64)req->reply_to_ts);
sqlite3_bind_int64(st, 6, (sqlite3_int64)req->reply_to_node_id);
}
off += snprintf(json + off, sizeof(json) - off,
",\"d\":\"%.*s\"}",
(int)req->data_len, (const char*)req->data);
int rc = sqlite3_step(st), bytes = rc == SQLITE_ROW ? sqlite3_column_bytes(st, 0) : 0;
char json[4001];
if (rc != SQLITE_ROW || bytes <= 0 || bytes >= (int)sizeof(json)) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: message JSON rejected ch=%s bytes=%d rc=%d: %s",
CC_ID, req->channel_id, bytes, rc, sqlite3_errmsg(cc->db)); sqlite3_finalize(st); return -1;
}
memcpy(json, sqlite3_column_text(st, 0), bytes); json[bytes] = 0; sqlite3_finalize(st);
uint64_t ts = db_sync_next_timestamp(si);
uint8_t sig_msg[8192]; size_t soff = 0;
memcpy(sig_msg + soff, &ts, 8); soff += 8;
size_t jl = strlen(json); memcpy(sig_msg + soff, json, jl); soff += jl;
uint8_t sig[64];
if (sc_ed25519_sign(inst->my_ed25519_privkey, sig_msg, soff, sig) != SC_OK) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: Ed25519 sign failed", CC_ID);
return;
uint8_t signed_body[sizeof(json) + 8], signature[64], message_id[32];
memcpy(signed_body, &ts, 8); memcpy(signed_body + 8, json, bytes);
if (sc_ed25519_sign(inst->my_ed25519_privkey, signed_body, bytes + 8, signature) != SC_OK
|| chat_message_id(db_sync_instance_group_id(si), ts, signature, message_id) < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: message signing failed ch=%s", CC_ID, req->channel_id); return -1;
}
int ret = db_sync_insert_signed(si, json, strlen(json), sig, 64, ts, NULL);
if (ret != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: db_sync_insert_signed failed ret=%d", CC_ID, ret);
return;
int result = db_sync_insert_signed(si, json, bytes, signature, 64, ts, NULL);
if (result != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_CHAT_SYNC, "%s: message insert failed ch=%s result=%d", CC_ID, req->channel_id, result); return -1;
}
req->timestamp = ts; memcpy(req->message_id, message_id, sizeof(message_id));
DEBUG_INFO(DEBUG_CATEGORY_CHAT_SYNC, "%s: message saved ch=%s ct=%s ts=%llu bytes=%d",
CC_ID, req->channel_id, req->content_type, (unsigned long long)ts, bytes);
return 0;
}
void chat_core_submit_trampoline(void* arg) {

Loading…
Cancel
Save