Return submitted message identity and encode JSON safely
This commit is contained in:
1 parent
d0aa60fe1a
commit
a4ca31f406
2 files changed
+45
-35
No files matched your search
@@ -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);
|
||||
|
||||
+41
-32
@@ -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) {
|
||||
|
||||
Reference in new issue
Block a user