126 lines
4.9 KiB
C
126 lines
4.9 KiB
C
/*
|
|
* chat_core_priv.h — внутренний заголовок для под-модулей chat_core
|
|
*
|
|
* Предоставляет per-instance chat_core_ctx через CC(inst), общие SQL/сериализационные хелперы
|
|
* и внутренние операции частей chat_core/member_sync. Не включать извне chat/.
|
|
*/
|
|
|
|
#ifndef CHAT_CORE_PRIV_H
|
|
#define CHAT_CORE_PRIV_H
|
|
|
|
#include "chat_core.h"
|
|
|
|
#include "db_sync.h"
|
|
#include "../../lib/debug_config.h"
|
|
|
|
#include <sqlite3.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
|
|
#define CC_ID "chat_core"
|
|
|
|
/* ── Base64 encode/decode (URL-safe, used for msg "d" field prefix) ── */
|
|
|
|
static const char B64_ALPHABET[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
|
|
static inline int b64_decode_char(char c) {
|
|
if (c >= 'A' && c <= 'Z') return c - 'A';
|
|
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
|
|
if (c >= '0' && c <= '9') return c - '0' + 52;
|
|
if (c == '-') return 62;
|
|
if (c == '_') return 63;
|
|
return -1;
|
|
}
|
|
|
|
static inline size_t b64_encode(const uint8_t* src, size_t src_len, char* dst, size_t dst_cap) {
|
|
size_t out = 0;
|
|
for (size_t i = 0; i < src_len; i += 3) {
|
|
if (out + 4 > dst_cap) break;
|
|
uint32_t v = (uint32_t)src[i] << 16;
|
|
if (i + 1 < src_len) v |= (uint32_t)src[i + 1] << 8;
|
|
if (i + 2 < src_len) v |= (uint32_t)src[i + 2];
|
|
dst[out++] = B64_ALPHABET[(v >> 18) & 0x3F];
|
|
dst[out++] = B64_ALPHABET[(v >> 12) & 0x3F];
|
|
dst[out++] = (i + 1 < src_len) ? B64_ALPHABET[(v >> 6) & 0x3F] : '=';
|
|
dst[out++] = (i + 2 < src_len) ? B64_ALPHABET[v & 0x3F] : '=';
|
|
}
|
|
return out;
|
|
}
|
|
|
|
static inline size_t b64_decode(const char* src, size_t src_len, uint8_t* dst, size_t dst_cap) {
|
|
size_t out = 0;
|
|
int vals[4]; size_t vi = 0;
|
|
for (size_t i = 0; i < src_len && out < dst_cap; i++) {
|
|
if (src[i] == '=') break;
|
|
int v = b64_decode_char(src[i]);
|
|
if (v < 0) continue;
|
|
vals[vi++] = v;
|
|
if (vi == 4) {
|
|
dst[out++] = (uint8_t)((vals[0] << 2) | (vals[1] >> 4));
|
|
if (out < dst_cap) dst[out++] = (uint8_t)((vals[1] << 4) | (vals[2] >> 2));
|
|
if (out < dst_cap)
|
|
dst[out++] = (uint8_t)((vals[2] << 6) | vals[3]);
|
|
vi = 0;
|
|
}
|
|
}
|
|
if (vi == 3 && out < dst_cap) {
|
|
dst[out++] = (uint8_t)((vals[0] << 2) | (vals[1] >> 4));
|
|
if (out < dst_cap) dst[out++] = (uint8_t)((vals[1] << 4) | (vals[2] >> 2));
|
|
} else if (vi == 2 && out < dst_cap) {
|
|
dst[out++] = (uint8_t)((vals[0] << 2) | (vals[1] >> 4));
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/* ── Состояние одного экземпляра (создаётся в chat_core.c) ── */
|
|
|
|
struct ms_props_cbk; /* member_sync.c */
|
|
struct ms_apply_cbk; /* member_sync.c */
|
|
struct group_avatar;
|
|
|
|
struct chat_core_ctx {
|
|
struct UTUN_INSTANCE* inst;
|
|
sqlite3* db;
|
|
char db_path[512];
|
|
uint8_t shared_db;
|
|
uint64_t my_node_id;
|
|
uint8_t initialized;
|
|
/* per-instance sub-module state (was file-scope statics) */
|
|
struct ms_props_cbk* props_cbks; /* was member_sync.c g_props_cbks */
|
|
struct ms_apply_cbk* apply_cbks; /* was member_sync.c g_apply_cbks */
|
|
struct group_avatar* group_avatar;
|
|
uint64_t media_cache_bytes; /* was chat_msg.c g_media_cache_bytes */
|
|
void* media_backfill_timer;/* was chat_msg.c g_media_startup_backfill_timer */
|
|
int media_backfill_ran; /* was chat_msg.c g_media_startup_backfill_ran */
|
|
struct UTUN_INSTANCE* media_backfill_inst; /* was chat_msg.c g_media_startup_backfill_inst */
|
|
void* media_channel_backfill_timer; /* debounce per-channel докачки */
|
|
char media_channel_backfill[16][64]; /* pending ch_id */
|
|
int media_channel_backfill_pending;
|
|
};
|
|
|
|
/* Per-instance chat context. inst->chat_core обязателен после chat_core_init. */
|
|
#define CC(inst) ((inst)->chat_core)
|
|
|
|
/* ── Хелперы ── */
|
|
|
|
void sanitize_ch_id(const char* ch_id, char* out, size_t out_sz);
|
|
void msg_table_name(const char* ch_id, char* buf, size_t sz);
|
|
void peers_table_name(const char* ch_id, char* buf, size_t sz);
|
|
|
|
/* ── db_sync registry (chat_core.c) ── */
|
|
|
|
struct DB_SYNC_INSTANCE* si_find(struct UTUN_INSTANCE* inst, const char* ch_id);
|
|
void si_register(struct UTUN_INSTANCE* inst, struct DB_SYNC_INSTANCE* si, const char* ch_id);
|
|
|
|
/* ── db_sync callback (chat_msg.c) ── */
|
|
|
|
void on_msg_inserted(struct DB_SYNC_INSTANCE* si, const uint8_t message_id[32], uint64_t record_ts,
|
|
const char* data, size_t len, uint64_t author, int initial_sync, void* arg);
|
|
|
|
/* ── применить настройку group_autoconnect ко всем CHAT-группам (chat_channel.c) ── */
|
|
|
|
void chat_core_apply_group_autoconnect(struct UTUN_INSTANCE* inst);
|
|
|
|
#endif /* CHAT_CORE_PRIV_H */
|