tdesktop/Telegram/SourceFiles/storage/storage_cloud_blob.cpp

152 lines
3.7 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "storage/storage_cloud_blob.h"
#include "base/zlib_help.h"
#include "lang/lang_keys.h"
#include "ui/text/format_values.h"
#include "main/main_account.h"
#include "main/main_session.h"
namespace Storage::CloudBlob {
namespace {
QByteArray ReadFinalFile(const QString &path) {
constexpr auto kMaxZipSize = 10 * 1024 * 1024;
auto file = QFile(path);
if (file.size() > kMaxZipSize || !file.open(QIODevice::ReadOnly)) {
return QByteArray();
}
return file.readAll();
}
bool ExtractZipFile(zlib::FileToRead &zip, const QString path) {
constexpr auto kMaxSize = 25 * 1024 * 1024;
const auto content = zip.readCurrentFileContent(kMaxSize);
if (content.isEmpty() || zip.error() != UNZ_OK) {
return false;
}
auto file = QFile(path);
return file.open(QIODevice::WriteOnly)
&& (file.write(content) == content.size());
}
} // namespace
bool UnpackBlob(
const QString &path,
const QString &folder,
Fn<bool(const QString &)> checkNameCallback) {
const auto bytes = ReadFinalFile(path);
if (bytes.isEmpty()) {
return false;
}
auto zip = zlib::FileToRead(bytes);
if (zip.goToFirstFile() != UNZ_OK) {
return false;
}
do {
const auto name = zip.getCurrentFileName();
const auto path = folder + '/' + name;
if (checkNameCallback(name) && !ExtractZipFile(zip, path)) {
return false;
}
const auto jump = zip.goToNextFile();
if (jump == UNZ_END_OF_LIST_OF_FILE) {
break;
} else if (jump != UNZ_OK) {
return false;
}
} while (true);
return true;
}
QString StateDescription(const BlobState &state, tr::phrase<> activeText) {
return v::match(state, [](const Available &data) {
return tr::lng_emoji_set_download(
tr::now,
lt_size,
Ui::FormatSizeText(data.size));
}, [](const Ready &data) -> QString {
return tr::lng_emoji_set_ready(tr::now);
}, [&](const Active &data) -> QString {
return activeText(tr::now);
}, [](const Loading &data) {
const auto percent = (data.size > 0)
? std::clamp((data.already * 100) / float64(data.size), 0., 100.)
: 0.;
return tr::lng_emoji_set_loading(
tr::now,
lt_percent,
QString::number(int(std::round(percent))) + '%',
lt_progress,
Ui::FormatDownloadText(data.already, data.size));
}, [](const Failed &data) {
return tr::lng_attach_failed(tr::now);
});
}
BlobLoader::BlobLoader(
QObject *parent,
not_null<Main::Session*> session,
int id,
MTP::DedicatedLoader::Location location,
const QString &folder,
int size)
: QObject(parent)
, _folder(folder)
, _id(id)
, _state(Loading{ 0, size })
, _mtproto(session.get()) {
const auto ready = [=](std::unique_ptr<MTP::DedicatedLoader> loader) {
if (loader) {
setImplementation(std::move(loader));
} else {
fail();
}
};
MTP::StartDedicatedLoader(&_mtproto, location, _folder, ready);
}
int BlobLoader::id() const {
return _id;
}
rpl::producer<BlobState> BlobLoader::state() const {
return _state.value();
}
void BlobLoader::setImplementation(
std::unique_ptr<MTP::DedicatedLoader> loader) {
_implementation = std::move(loader);
_state = _implementation->progress(
) | rpl::map([](const Loading &state) {
return BlobState(state);
});
_implementation->failed(
) | rpl::start_with_next([=] {
fail();
}, _implementation->lifetime());
_implementation->ready(
) | rpl::start_with_next([=](const QString &filepath) {
unpack(filepath);
}, _implementation->lifetime());
QDir(_folder).removeRecursively();
_implementation->start();
}
void BlobLoader::fail() {
_state = Failed();
}
} // namespace Storage::CloudBlob