diff --git a/src/chat/chat_attachment.c b/src/chat/chat_attachment.c index 9f968877..b0551ee3 100644 --- a/src/chat/chat_attachment.c +++ b/src/chat/chat_attachment.c @@ -988,7 +988,7 @@ fail: } /* Автозагрузка обычного участника запрашивает доступ; storage-реплика хранит только ciphertext. */ -int chat_attachment_downloaded(struct UTUN_INSTANCE* inst, const char* channel, const uint8_t message[32]) { +int chat_attachment_downloaded(struct UTUN_INSTANCE* inst, const char* channel, const uint8_t message[32], int verified_download) { if (!inst || !CC(inst) || CC(inst)->attachment_closing || !ak_channel(channel) || !message) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment keys: invalid downloaded state/arguments"); return -1; } @@ -999,7 +999,7 @@ int chat_attachment_downloaded(struct UTUN_INSTANCE* inst, const char* channel, const char* stage = "begin downloaded"; int rc = sqlite3_exec(db, "BEGIN IMMEDIATE /* attachment downloaded */", NULL, NULL, NULL); if (rc != SQLITE_OK) goto fail; - char sql[512]; snprintf(sql, sizeof(sql), "SELECT downloaded,wanted,NOT EXISTS(SELECT 1 FROM peers_%s" + char sql[512]; snprintf(sql, sizeof(sql), "SELECT downloaded,wanted,error,NOT EXISTS(SELECT 1 FROM peers_%s" " WHERE node_id=? AND source=0 AND deleted=0 AND length(adm_tags_sig)=64 AND json_extract(adm_tags,'$.storage')='yes')" " FROM attachment_keys WHERE channel=? AND message_id=?", channel); stage = "prepare download state"; rc = sqlite3_prepare_v2(db, sql, -1, &st, NULL); @@ -1012,20 +1012,23 @@ int chat_attachment_downloaded(struct UTUN_INSTANCE* inst, const char* channel, if (rc != SQLITE_OK) goto fail; stage = "read download state"; rc = sqlite3_step(st); if (rc != SQLITE_ROW) goto fail; - int downloaded = sqlite3_column_int(st, 0), wanted = sqlite3_column_int(st, 1), automatic = sqlite3_column_int(st, 2); + int downloaded = sqlite3_column_int(st, 0), wanted = sqlite3_column_int(st, 1), error = sqlite3_column_int(st, 2); + int automatic = sqlite3_column_int(st, 3), desired_error = verified_download ? 0 : error; if ((downloaded != 0 && downloaded != 1) || (wanted != 0 && wanted != 1)) { stage = "download state"; rc = SQLITE_MISMATCH; goto fail; } - int desired = wanted || automatic, changed_state = !downloaded || wanted != desired; + int desired = wanted || automatic, changed_state = !downloaded || wanted != desired || error != desired_error; stage = "finalize download state"; rc = sqlite3_finalize(st); st = NULL; if (rc != SQLITE_OK) goto fail; if (changed_state) { stage = "prepare downloaded"; - rc = sqlite3_prepare_v2(db, "UPDATE attachment_keys SET downloaded=1,wanted=? WHERE channel=? AND message_id=?", -1, &st, NULL); + rc = sqlite3_prepare_v2(db, "UPDATE attachment_keys SET downloaded=1,wanted=?,error=? WHERE channel=? AND message_id=?", -1, &st, NULL); if (rc != SQLITE_OK) goto fail; stage = "bind wanted"; rc = sqlite3_bind_int(st, 1, desired); if (rc != SQLITE_OK) goto fail; - stage = "bind channel"; rc = sqlite3_bind_text(st, 2, channel, -1, SQLITE_STATIC); + stage = "bind error"; rc = sqlite3_bind_int(st, 2, desired_error); if (rc != SQLITE_OK) goto fail; - stage = "bind message"; rc = sqlite3_bind_blob(st, 3, message, 32, SQLITE_STATIC); + stage = "bind channel"; rc = sqlite3_bind_text(st, 3, channel, -1, SQLITE_STATIC); + if (rc != SQLITE_OK) goto fail; + stage = "bind message"; rc = sqlite3_bind_blob(st, 4, message, 32, SQLITE_STATIC); if (rc != SQLITE_OK) goto fail; stage = "write downloaded"; rc = sqlite3_step(st); if (rc != SQLITE_DONE) goto fail; @@ -1037,7 +1040,8 @@ int chat_attachment_downloaded(struct UTUN_INSTANCE* inst, const char* channel, stage = "commit downloaded"; rc = sqlite3_exec(db, "COMMIT /* attachment downloaded */", NULL, NULL, NULL); if (rc != SQLITE_OK) goto fail; if (changed_state) { - DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "attachment keys: download state committed channel=%s wanted=%d automatic=%d", channel, desired, automatic); + DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "attachment keys: download state committed channel=%s wanted=%d automatic=%d verified=%d error=%d", + channel, desired, automatic, verified_download != 0, desired_error); ak_changed(inst, channel); } return 0; diff --git a/src/chat/chat_attachment.h b/src/chat/chat_attachment.h index 1803223f..9b66b333 100644 --- a/src/chat/chat_attachment.h +++ b/src/chat/chat_attachment.h @@ -66,10 +66,11 @@ int chat_attachment_observe(struct UTUN_INSTANCE* inst, const char* channel, con // ── Получение файла и выдача доступа ── -/* После проверенной загрузки/наличия сохранённого контейнера: своей транзакцией сохранить downloaded и policy wanted. +/* После загрузки/наличия сохранённого контейнера: своей транзакцией сохранить downloaded и policy wanted. * 0/-1; запись observe обязательна. Повтор неизменного состояния не даёт события. Криптографию проверит worker. + * verified_download!=0 — новая проверенная передача, также сбрасывает прежний error; 0 — найденный cache, error сохраняется. * Внешняя транзакция запрещена, SQL-отказ сохраняет прежние поля, событие — после commit. */ -int chat_attachment_downloaded(struct UTUN_INSTANCE* inst, const char* channel, const uint8_t message[32]); +int chat_attachment_downloaded(struct UTUN_INSTANCE* inst, const char* channel, const uint8_t message[32], int verified_download); /* Ручная загрузка, включая storage: одной транзакцией установить wanted, сбросить error и ready отсутствующего cache. * 0/-1; запись observe обязательна. Внешняя транзакция запрещена, отказ сохраняет прежние поля. */ diff --git a/src/chat/chat_msg.c b/src/chat/chat_msg.c index b36cbdae..de702754 100644 --- a/src/chat/chat_msg.c +++ b/src/chat/chat_msg.c @@ -778,7 +778,7 @@ static void md_download_done_cb(void* arg, int err) { struct chat_core_ctx* cc = CC(ctx->inst); if (!err) { if (!strcmp(ctx->content_type, ENCRYPTED_ATTACHMENT_TYPE)) { - if (chat_attachment_downloaded(ctx->inst, ctx->channel_id, ctx->message_id)) + if (chat_attachment_downloaded(ctx->inst, ctx->channel_id, ctx->message_id, 1)) media_queue_channel_backfill(ctx->inst, ctx->channel_id); } char attrs[1024]; @@ -1187,7 +1187,7 @@ void chat_core_attachment_download(struct UTUN_INSTANCE* inst, const char* chann channel_id, rc, finish, sqlite3_errmsg(cc->db)); u_free(body); return; } if (present) { - if (chat_attachment_downloaded(inst, channel_id, message)) media_queue_channel_backfill(inst, channel_id); + if (chat_attachment_downloaded(inst, channel_id, message, 0)) media_queue_channel_backfill(inst, channel_id); u_free(body); return; } } @@ -1297,7 +1297,7 @@ void chat_media_backfill(struct UTUN_INSTANCE* inst) { int registered = media_index_commit(cc->db, &result, cc->my_node_id, NULL, ch_id, path, media_base); if (!registered) total += result.num_blocks; else DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "media backfill registration failed ch=%s file=%s", ch_id, path); - if (encrypted_attachment && !registered && chat_attachment_downloaded(inst, ch_id, message)) + if (encrypted_attachment && !registered && chat_attachment_downloaded(inst, ch_id, message, 0)) media_queue_channel_backfill(inst, ch_id); u_free(body); @@ -1416,7 +1416,7 @@ static void bf_collect(struct chat_core_ctx* cc, const char* media_base, const c snprintf(full_path, sizeof(full_path), "%s/media/%s/%s", media_base, ch_id, final_name); int64_t actual = ma_file_size(full_path); if (actual < 0) actual = 0; - if (encrypted_attachment && actual > 0 && chat_attachment_downloaded(cc->inst, ch_id, message)) + if (encrypted_attachment && actual > 0 && chat_attachment_downloaded(cc->inst, ch_id, message, 0)) media_queue_channel_backfill(cc->inst, ch_id); if (l->count == l->cap) {