|
|
|
|
@ -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) { |
|
|
|
|
|