diff --git a/src/chat/chat_core_priv.h b/src/chat/chat_core_priv.h index 43f341ce..d6994436 100644 --- a/src/chat/chat_core_priv.h +++ b/src/chat/chat_core_priv.h @@ -76,6 +76,7 @@ static inline size_t b64_decode(const char* src, size_t src_len, uint8_t* dst, s /* ── Состояние одного экземпляра (создаётся в chat_core.c) ── */ struct ms_props_cbk; /* member_sync.c */ +struct media_index_result; struct ms_apply_cbk; /* member_sync.c */ struct group_avatar; @@ -119,6 +120,8 @@ void si_register(struct UTUN_INSTANCE* inst, struct DB_SYNC_INSTANCE* si, const 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); +/* Разобрать подписанный manifest канального медиа; result освобождается media_index_result_free. */ +int chat_msg_parse_media_body(const char* body, struct media_index_result* result); /* ── применить настройку group_autoconnect ко всем CHAT-группам (chat_channel.c) ── */ diff --git a/src/media_async/attachment_send.c b/src/media_async/attachment_send.c index f4627818..1898c327 100644 --- a/src/media_async/attachment_send.c +++ b/src/media_async/attachment_send.c @@ -10,6 +10,7 @@ #include "../utun_instance.h" #include "../chat/chat_core.h" #include "../chat/chat_event.h" +#include "../chat/chat_attachment.h" #include "../dm/dm_media.h" #include "../dm/dm_history.h" #include "../video/video.h" @@ -85,7 +86,13 @@ static void attachment_publish(struct attachment_send_req* req, int error) { chat_event_attachment(req->inst, req->is_dm, req->target, req->id, 1); DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "attachment: prepared target=%s pm=%d kind=%d duration=%u dimensions=%ux%u", req->target, req->is_dm, req->info.kind, req->info.duration_ms, req->info.width, req->info.height); - if (req->is_dm) { + if (req->encrypted && !req->is_dm) { + const char* type = req->content_type[0] ? req->content_type : attachment_content_type(req->info.kind); + if (chat_attachment_send(req->inst, req->target, req->output, &req->info, type, req->reply_to, req->request_id)) { + chat_event_submitted(req->inst, req->request_id, 0); + if (remove(req->output) && errno != ENOENT) DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "attachment: encrypted source cleanup failed"); + } + } else if (req->is_dm) { struct dm_file_req* file = u_calloc(1, sizeof(*file)); if (file) { file->inst = req->inst; file->info = req->info; file->remove_source = 1; @@ -164,7 +171,8 @@ void attachment_send_trampoline(void* arg) { for (unsigned i = 0; i < 16; i++) snprintf(hex + 2 * i, 3, "%02x", uuid[i]); memcpy(req->id, uuid, sizeof(req->id)); const char* base = req->inst->config->global.db_path; - int n = snprintf(req->output, sizeof(req->output), "%s/media/%s/%s.%s", base[0] ? base : ".", + int n = snprintf(req->output, sizeof(req->output), "%s/%s/%s/%s.%s", base[0] ? base : ".", + req->encrypted && !req->is_dm ? "encrypted-prepared" : "media", req->is_dm ? "pm-prepared" : req->target, hex, req->info.kind == ATTACHMENT_VOICE ? "opus" : "mp4"); if (n < 0 || (size_t)n >= sizeof(req->output)) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment: output path too long"); req->output[0] = 0; attachment_publish(req, -1); return; diff --git a/src/media_async/attachment_send.h b/src/media_async/attachment_send.h index a3e03a4c..b07fd826 100644 --- a/src/media_async/attachment_send.h +++ b/src/media_async/attachment_send.h @@ -17,6 +17,8 @@ struct attachment_send_req { struct UTUN_INSTANCE* inst; char target[64]; /* Десятичный channel_id либо conv_id без префикса dm:, зафиксированный до подготовки. */ int is_dm; + int encrypted; /* Отдельный случайный ключ группового вложения с ручной выдачей. */ + char content_type[32]; /* Необязательный исходный MIME (например image/png). */ uint64_t dm_generation; /* Захватывается до подготовки и сохраняется во всех её стадиях. */ unsigned dm_paths_held; uint8_t reply_to[32]; /* Оригинал канального ответа, зафиксированный до подготовки. */