diff --git a/src/dm/dm_media.c b/src/dm/dm_media.c index a0c31d88..61e0b7c9 100644 --- a/src/dm/dm_media.c +++ b/src/dm/dm_media.c @@ -52,6 +52,7 @@ struct dm_file_job { char input[1024], cipher[1024], plain[1024], temp[1024], output[1024]; int error; uint64_t generation; + uint64_t request_id; unsigned held; }; @@ -567,6 +568,7 @@ static void dm_file_done(void* arg, int error) { DEBUG_INFO(DEBUG_CATEGORY_DM, "dm_media: prepared conv=%s bytes=%llu name=%s", job->conv, (unsigned long long)job->size, job->info.name); chat_event_attachment(inst, 1, job->conv, job->id, 1); + chat_event_submitted(inst, job->request_id, 1); dm_job_free(job); return; } @@ -627,6 +629,7 @@ fail: DEBUG_WARN(DEBUG_CATEGORY_DM, "dm_media: file task failed/cancelled work=%d error=%d worker=%d closing=%d", job->work, error, job->error, media->closing); if (job->work == DM_FILE_PREPARE) { + chat_event_submitted(inst, job->request_id, 0); chat_event_attachment(inst, 1, job->conv, job->id, 2); dm_file_remove(job->plain); dm_file_remove(job->cipher); if (job->remove_source) dm_file_remove(job->input); @@ -718,7 +721,7 @@ static int dm_file_lookup(void* arg, uint64_t group, uint64_t peer, const uint8_ /* Путь исходника копируется; caller может освободить строку после возврата. */ int dm_send_file(struct UTUN_INSTANCE* inst, const char* conv, const char* path, const struct attachment_info* info, - int remove_source) { + int remove_source, uint64_t request_id) { struct dm_media_state* media = inst ? inst->dm_media : NULL; if (!media || media->closing || !inst->media_async || !conv || !path || attachment_validate(info) || strlen(path) >= 1024) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "dm_media: invalid file send arguments/state"); @@ -731,6 +734,7 @@ int dm_send_file(struct UTUN_INSTANCE* inst, const char* conv, const char* path, if (!job) { DEBUG_ERROR(DEBUG_CATEGORY_DM, "dm_media: preparation allocation failed"); return -1; } job->media = media; job->work = DM_FILE_PREPARE; job->author = inst->node_id; job->generation = dm_history_generation(inst, conv); + job->request_id = request_id; if (!dm_history_current(inst, conv, job->generation)) { u_free(job); return -1; } snprintf(job->conv, sizeof(job->conv), "%s", conv); job->info = *info; job->remove_source = remove_source; snprintf(job->input, sizeof(job->input), "%s", path); @@ -775,8 +779,9 @@ void dm_send_file_trampoline(void* arg) { if (!req) return; uint64_t generation = req->generation ? req->generation : dm_history_generation(req->inst, req->conv); int current = dm_history_current(req->inst, req->conv, generation); - if (!current || dm_send_file(req->inst, req->conv, req->path, &req->info, req->remove_source)) { + if (!current || dm_send_file(req->inst, req->conv, req->path, &req->info, req->remove_source, req->request_id)) { DEBUG_WARN(DEBUG_CATEGORY_DM, "DM files: send rejected conv=%s generation=%llu", req->conv, (unsigned long long)generation); + chat_event_submitted(req->inst, req->request_id, 0); const uint8_t rejected[16] = {0}; if (current) chat_event_attachment(req->inst, 1, req->conv, rejected, 2); if (req->remove_source) { diff --git a/src/dm/dm_media.h b/src/dm/dm_media.h index 505214a4..8851fa6d 100644 --- a/src/dm/dm_media.h +++ b/src/dm/dm_media.h @@ -56,13 +56,15 @@ void dm_media_destroy(struct UTUN_INSTANCE* inst); * готовность/ошибка подготовки сообщается CHAT_EVT_ATTACHMENT_PREPARATION, включая до возврата 0. * Беседа должна быть видимой; до 4 одновременных подготовок, plaintext до DM_MEDIA_MAX_SIZE. * remove_source=1 передаёт worker собственный временный файл приложения для удаления; при -1 его убирает caller. - * Успех подготовки означает commit file-сообщения, не доставку. Пользовательский исходник: remove_source=0. */ + * Успех подготовки означает commit file-сообщения, не доставку. Пользовательский исходник: remove_source=0. + * request_id!=0 получает CHAT_EVT_MSG_SUBMITTED после commit/ошибки; при возврате -1 событие посылает caller. */ int dm_send_file(struct UTUN_INSTANCE* inst, const char* conv, const char* path, const struct attachment_info* info, - int remove_source); + int remove_source, uint64_t request_id); struct dm_file_req { struct UTUN_INSTANCE* inst; /* Заимствованное ядро до callback. */ char conv[64]; /* Десятичный ID беседы с NUL. */ uint64_t generation; /* Ожидаемое поколение; 0=захватить при входе в uasync. */ + uint64_t request_id; /* Корреляция результата локального commit, 0=без подтверждения. */ char path[1024]; /* Путь исходника с NUL; копируется при принятии. */ struct attachment_info info; /* Проверенные метаданные вложения. */ int remove_source; /* Только принадлежащий приложению временный файл. */ diff --git a/src/media_async/attachment_send.c b/src/media_async/attachment_send.c index bc0483ec..e2dd623b 100644 --- a/src/media_async/attachment_send.c +++ b/src/media_async/attachment_send.c @@ -108,11 +108,13 @@ static void attachment_publish(struct attachment_send_req* req, int error) { if (file) { file->inst = req->inst; file->info = req->info; file->remove_source = 1; file->generation = req->dm_generation; + file->request_id = req->request_id; snprintf(file->conv, sizeof(file->conv), "%s", req->target); snprintf(file->path, sizeof(file->path), "%s", req->output); dm_send_file_trampoline(file); } else { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment: PM dispatch allocation failed"); + chat_event_submitted(req->inst, req->request_id, 0); chat_event_attachment(req->inst, req->is_dm, req->target, req->id, 2); if (remove(req->output)) DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "attachment: rejected output cleanup failed"); }