Files
utun2/src/chat/group_avatar.h
T

75 lines
5.7 KiB
C

/* group_avatar — общая подписанная аватарка CHAT-канала, хранение в SQLite и обмен между участниками.
* После init: get читает текущую версию, set публикует картинку владельца, apply принимает проверяемую запись пира.
* Картинка до 16 КиБ передаётся router-блоками до 4 КиБ; данные публикуются после проверки подписи канала.
* Все вызовы/callbacks — uasync. group — канонический числовой ID1..INT64_MAX существующего CHAT-канала.
* Изменение приходит CHAT_EVT_CHANNEL_UPDATED после commit. Обработчик вправе отменить задачи группы;
* остановку сервиса/destroy выполняют вне callbacks. Ключи и содержимое картинки в лог не выводятся. */
#ifndef GROUP_AVATAR_H
#define GROUP_AVATAR_H
#include <stdint.h>
#include <stddef.h>
struct UTUN_INSTANCE;
struct sqlite3;
#ifdef __cplusplus
extern "C" {
#endif
// ── Схема хранения ──
/* Добавить отсутствующие поля channels одним savepoint; 0/-1, повтор допустим, БД заимствована.
* Внешняя транзакция остаётся у вызывающего. При отказе ROLLBACK TO не выполняет RELEASE частичной схемы;
* оставшуюся открытой транзакцию владелец должен откатить/закрыть. */
int group_avatar_schema_init(struct sqlite3* db);
#define GROUP_AVATAR_MAX 16384
#define GROUP_AVATAR_BLOCK 4096
struct group_avatar_record {
uint64_t version; // 0 — картинка ещё не публиковалась; иначе версия подписанной записи
uint16_t size; // байты картинки; size=0 при ненулевой версии — подписанное удаление
uint8_t hash[32], signature[64], data[GROUP_AVATAR_MAX]; // SHA256, подпись канала Ed25519 и готовая картинка
};
// ── Жизненный цикл модуля ──
// После chat_core_init создать контекст, router-service и таймер; 0/-1. Повтор допустим, ошибка снимает новые регистрации.
int group_avatar_init(struct UTUN_INSTANCE* inst);
// Остановить обмен и освободить контекст; сохранённая картинка остаётся в БД.
void group_avatar_destroy(struct UTUN_INSTANCE* inst);
// Отменить сборки/waiters/call_soon канала перед удалением группы; БД и router-сессия остаются у своих владельцев.
void group_avatar_cancel_group(struct UTUN_INSTANCE* inst, uint64_t group);
// ── Чтение и изменение аватарки ──
/* Скопировать запись и проверить SHA256/подпись:0 — прочитано (version=0, если не публиковалась), -1 — ошибка/нет канала.
* Проверяются bind/step/finalize и размеры полей; при любом отказе record не меняется. */
int group_avatar_get(struct UTUN_INSTANCE* inst, uint64_t group, struct group_avatar_record* record);
/* Проверить подпись и сохранить более новую запись:1 — committed,0 — прежняя/старая,-1 — ошибка.
* При одинаковой версии побеждает больший SHA256; запись заимствована. Проверка версии/запись защищены BEGIN IMMEDIATE.
* Внешняя транзакция запрещена даже для no-op; ошибка сохраняет прежнюю запись/события. Неудачный rollback
* с оставшейся транзакцией запрашивает stop/закрытие БД владельцем. Уведомление/STATE идут после commit. */
int group_avatar_apply(struct UTUN_INSTANCE* inst, uint64_t group, const struct group_avatar_record* record);
/* Подписать и сохранить картинку: нужен приватный Ed25519 ключ канала. size=0 — удаление; data копируется.
* 0 — committed или картинка прежняя, -1 — ошибка/нет прав. Членство/ключ/версия/запись читаются под одной блокировкой;
* внешняя транзакция запрещена, временный ключ очищается на всех выходах. Доставка пирам выполняется отдельно. */
int group_avatar_set(struct UTUN_INSTANCE* inst, uint64_t group, const uint8_t* data, size_t size);
// ── Команда интерфейса ──
struct group_avatar_gui_request {
struct UTUN_INSTANCE* inst; /* Заимствован до выполнения trampoline. */
uint64_t id, group; /* ID команды интерфейса и канала. */
uint16_t size; /* Размер data<=GROUP_AVATAR_MAX;0 — подписанное удаление. */
uint8_t data[GROUP_AVATAR_MAX]; /* Готовая картинка, скопированная интерфейсом. */
};
// arg — u_malloc/u_calloc group_avatar_gui_request, освобождается здесь; результат — CHAT_EVT_GROUP_AVATAR_RESULT.
void group_avatar_gui_set(void* arg);
#ifdef __cplusplus
}
#endif
#endif