Browse Source

Protect pending avatar uploads while bounding disk cache retention

master
evgeny 6 days ago
parent
commit
7582509792
  1. 4
      tools/chatgui/src/avatars/avatarcache.cpp
  2. 3
      tools/chatgui/src/avatars/avatarcache.h
  3. 10
      tools/chatgui/src/avatars/avatarmanager.cpp

4
tools/chatgui/src/avatars/avatarcache.cpp

@ -11,14 +11,14 @@ extern "C" {
#include "debug_config.h"
}
bool AvatarCache::initialize(const QString& directory) {
bool AvatarCache::initialize(const QString& directory, const QStringList& pinned) {
if (!QDir().mkpath(directory)) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "avatar: cannot create cache path=%s", qPrintable(directory)); return false;
}
const auto entries = QDir(directory).entryInfoList({QStringLiteral("*.part*"), QStringLiteral("*.download*")}, QDir::Files);
for (const auto& entry : entries) if (!QFile::remove(entry.absoluteFilePath()))
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "avatar: cannot remove interrupted cache file=%s", qPrintable(entry.fileName()));
prune(directory, {});
prune(directory, pinned);
return true;
}

3
tools/chatgui/src/avatars/avatarcache.h

@ -2,12 +2,13 @@
#pragma once
#include <QImage>
#include <QString>
#include <QStringList>
namespace AvatarCache {
constexpr int MaxBytes = 1024 * 1024;
constexpr qint64 DiskLimit = 100 * 1024 * 1024;
struct Result { QImage image; QString hash, error; };
bool initialize(const QString& directory);
bool initialize(const QString& directory, const QStringList& pinned = {});
Result load(const QString& path, const QString& hash);
Result prepare(const QString& source, const QString& directory);
Result publish(const QString& temporary, const QString& directory, const QString& hash);

10
tools/chatgui/src/avatars/avatarmanager.cpp

@ -52,8 +52,9 @@ void AvatarManager::initialize() {
const QString file = m_db->getUiState("avatar_default_file");
if (mf_avatar_file_valid(file.toLatin1().constData())) m_defaultHash = file.left(64);
const QString directory = m_directory;
m_pool.start(QRunnable::create([this, directory] {
bool ready = AvatarCache::initialize(directory);
const QStringList pinned = m_defaultHash.isEmpty() ? QStringList() : QStringList{m_defaultHash};
m_pool.start(QRunnable::create([this, directory, pinned] {
bool ready = AvatarCache::initialize(directory, pinned);
QMetaObject::invokeMethod(this, [this, ready] {
m_ready = ready;
if (!ready) { emit statusChanged(text("Cannot open avatar cache")); return; }
@ -207,7 +208,8 @@ void AvatarManager::clearDefault() {
void AvatarManager::scheduleOwn(quint64 group) {
if (!m_ready || m_preparing || !m_defaultServer || m_defaultHash.isEmpty() || !m_servers.value(group).contains(m_defaultServer) ||
!m_images.contains(m_defaultHash)) return;
m_missing.contains(m_defaultHash)) return;
if (!m_images.contains(m_defaultHash)) { loadCache(m_defaultHash); return; }
const QString context = key(group, m_db->myNodeId());
if (!m_refs.contains(context) || m_retry.value(context) > QDateTime::currentMSecsSinceEpoch()) return;
const Ref old = m_refs.value(context);
@ -297,7 +299,7 @@ void AvatarManager::onTransfer(const uint8_t* data, int length) {
if (job.upload) { publish(job); return; }
const Job snapshot = job; const QString directory = m_directory; job.stage = 101;
QStringList pinned; if (!m_defaultHash.isEmpty()) pinned << m_defaultHash;
for (const auto& ref : m_refs) if (m_wanted.contains(key(ref.group, ref.owner))) pinned << ref.hash;
for (const auto& active : m_jobs) if (active.upload) pinned << active.ref.hash;
m_pool.start(QRunnable::create([this, snapshot, directory, pinned] {
const auto result = AvatarCache::publish(snapshot.local, directory, snapshot.ref.hash); AvatarCache::prune(directory, pinned);
QMetaObject::invokeMethod(this, [this, snapshot, result] {

Loading…
Cancel
Save