2014-05-30 08:53:19 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
2018-01-03 10:23:14 +00:00
|
|
|
the official desktop application for the Telegram messaging service.
|
2014-05-30 08:53:19 +00:00
|
|
|
|
2018-01-03 10:23:14 +00:00
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
2014-05-30 08:53:19 +00:00
|
|
|
*/
|
2017-03-04 10:23:56 +00:00
|
|
|
#include "storage/file_download.h"
|
2016-03-23 18:43:12 +00:00
|
|
|
|
2017-09-26 11:49:16 +00:00
|
|
|
#include "data/data_document.h"
|
2018-08-27 11:35:58 +00:00
|
|
|
#include "data/data_session.h"
|
2019-03-22 14:19:43 +00:00
|
|
|
#include "data/data_file_origin.h"
|
2014-05-30 08:53:19 +00:00
|
|
|
#include "mainwidget.h"
|
2016-04-12 21:31:28 +00:00
|
|
|
#include "mainwindow.h"
|
2019-01-21 13:42:21 +00:00
|
|
|
#include "core/application.h"
|
2017-03-04 10:23:56 +00:00
|
|
|
#include "storage/localstorage.h"
|
2017-02-28 14:05:30 +00:00
|
|
|
#include "platform/platform_file_utilities.h"
|
2019-07-24 11:45:24 +00:00
|
|
|
#include "main/main_session.h"
|
2018-07-13 16:49:46 +00:00
|
|
|
#include "apiwrap.h"
|
2017-12-11 14:45:29 +00:00
|
|
|
#include "core/crash_reports.h"
|
2018-03-27 12:16:00 +00:00
|
|
|
#include "base/bytes.h"
|
|
|
|
#include "base/openssl_help.h"
|
2019-09-13 06:06:02 +00:00
|
|
|
#include "facades.h"
|
|
|
|
#include "app.h"
|
2017-03-04 19:36:59 +00:00
|
|
|
|
2018-08-28 21:09:55 +00:00
|
|
|
FileLoader::FileLoader(
|
|
|
|
const QString &toFile,
|
|
|
|
int32 size,
|
|
|
|
LocationType locationType,
|
|
|
|
LoadToCacheSetting toCache,
|
|
|
|
LoadFromCloudSetting fromCloud,
|
|
|
|
bool autoLoading,
|
|
|
|
uint8 cacheTag)
|
2019-12-04 12:15:58 +00:00
|
|
|
: _session(&Auth())
|
2017-03-04 19:36:59 +00:00
|
|
|
, _autoLoading(autoLoading)
|
2018-08-28 21:09:55 +00:00
|
|
|
, _cacheTag(cacheTag)
|
2017-08-15 15:59:40 +00:00
|
|
|
, _filename(toFile)
|
|
|
|
, _file(_filename)
|
2015-12-31 05:34:43 +00:00
|
|
|
, _toCache(toCache)
|
2015-12-24 19:26:28 +00:00
|
|
|
, _fromCloud(fromCloud)
|
|
|
|
, _size(size)
|
2016-10-12 19:34:25 +00:00
|
|
|
, _locationType(locationType) {
|
2017-08-15 15:59:40 +00:00
|
|
|
Expects(!_filename.isEmpty() || (_size <= Storage::kMaxFileInMemory));
|
2014-05-30 08:53:19 +00:00
|
|
|
}
|
|
|
|
|
2019-12-04 12:15:58 +00:00
|
|
|
FileLoader::~FileLoader() = default;
|
2019-12-02 07:28:33 +00:00
|
|
|
|
2019-07-24 11:45:24 +00:00
|
|
|
Main::Session &FileLoader::session() const {
|
2019-12-04 12:15:58 +00:00
|
|
|
return *_session;
|
2019-06-17 11:54:00 +00:00
|
|
|
}
|
|
|
|
|
2018-10-12 16:41:51 +00:00
|
|
|
void FileLoader::finishWithBytes(const QByteArray &data) {
|
|
|
|
_data = data;
|
|
|
|
_localStatus = LocalStatus::Loaded;
|
|
|
|
if (!_filename.isEmpty() && _toCache == LoadToCacheAsWell) {
|
|
|
|
if (!_fileIsOpen) _fileIsOpen = _file.open(QIODevice::WriteOnly);
|
|
|
|
if (!_fileIsOpen) {
|
|
|
|
cancel(true);
|
|
|
|
return;
|
|
|
|
}
|
2019-04-12 09:25:00 +00:00
|
|
|
_file.seek(0);
|
2018-10-12 16:41:51 +00:00
|
|
|
if (_file.write(_data) != qint64(_data.size())) {
|
|
|
|
cancel(true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_finished = true;
|
|
|
|
if (_fileIsOpen) {
|
|
|
|
_file.close();
|
|
|
|
_fileIsOpen = false;
|
|
|
|
Platform::File::PostprocessDownloaded(
|
|
|
|
QFileInfo(_file).absoluteFilePath());
|
|
|
|
}
|
2019-12-04 12:15:58 +00:00
|
|
|
Auth().downloaderTaskFinished().notify();
|
2018-10-12 16:41:51 +00:00
|
|
|
}
|
|
|
|
|
2016-04-10 14:53:01 +00:00
|
|
|
QByteArray FileLoader::imageFormat(const QSize &shrinkBox) const {
|
2015-09-29 18:44:31 +00:00
|
|
|
if (_imageFormat.isEmpty() && _locationType == UnknownFileLocation) {
|
2016-04-10 14:53:01 +00:00
|
|
|
readImage(shrinkBox);
|
2015-09-29 18:44:31 +00:00
|
|
|
}
|
|
|
|
return _imageFormat;
|
2014-05-30 08:53:19 +00:00
|
|
|
}
|
|
|
|
|
2018-10-12 16:41:51 +00:00
|
|
|
QImage FileLoader::imageData(const QSize &shrinkBox) const {
|
|
|
|
if (_imageData.isNull() && _locationType == UnknownFileLocation) {
|
2016-04-10 14:53:01 +00:00
|
|
|
readImage(shrinkBox);
|
2015-09-29 18:44:31 +00:00
|
|
|
}
|
2018-10-12 16:41:51 +00:00
|
|
|
return _imageData;
|
2014-05-30 08:53:19 +00:00
|
|
|
}
|
|
|
|
|
2016-04-10 14:53:01 +00:00
|
|
|
void FileLoader::readImage(const QSize &shrinkBox) const {
|
2017-03-04 11:28:21 +00:00
|
|
|
auto format = QByteArray();
|
|
|
|
auto image = App::readImage(_data, &format, false);
|
2016-04-10 14:53:01 +00:00
|
|
|
if (!image.isNull()) {
|
|
|
|
if (!shrinkBox.isEmpty() && (image.width() > shrinkBox.width() || image.height() > shrinkBox.height())) {
|
2018-10-12 16:41:51 +00:00
|
|
|
_imageData = image.scaled(shrinkBox, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
2016-07-13 17:34:57 +00:00
|
|
|
} else {
|
2018-10-12 16:41:51 +00:00
|
|
|
_imageData = std::move(image);
|
2016-04-10 14:53:01 +00:00
|
|
|
}
|
2015-09-29 18:44:31 +00:00
|
|
|
_imageFormat = format;
|
|
|
|
}
|
2014-05-30 08:53:19 +00:00
|
|
|
}
|
|
|
|
|
2019-01-04 11:09:48 +00:00
|
|
|
Data::FileOrigin FileLoader::fileOrigin() const {
|
|
|
|
return Data::FileOrigin();
|
|
|
|
}
|
|
|
|
|
2015-12-30 19:09:20 +00:00
|
|
|
float64 FileLoader::currentProgress() const {
|
2017-03-04 11:28:21 +00:00
|
|
|
if (_finished) return 1.;
|
2016-10-13 09:12:12 +00:00
|
|
|
if (!fullSize()) return 0.;
|
|
|
|
return snap(float64(currentOffset()) / fullSize(), 0., 1.);
|
2014-05-30 08:53:19 +00:00
|
|
|
}
|
|
|
|
|
2019-04-10 11:26:15 +00:00
|
|
|
int FileLoader::fullSize() const {
|
2015-12-24 19:26:28 +00:00
|
|
|
return _size;
|
2014-05-30 08:53:19 +00:00
|
|
|
}
|
|
|
|
|
2015-12-30 19:09:20 +00:00
|
|
|
bool FileLoader::setFileName(const QString &fileName) {
|
2017-08-15 15:59:40 +00:00
|
|
|
if (_toCache != LoadToCacheAsWell || !_filename.isEmpty()) {
|
|
|
|
return fileName.isEmpty() || (fileName == _filename);
|
2016-10-12 19:34:25 +00:00
|
|
|
}
|
2017-08-15 15:59:40 +00:00
|
|
|
_filename = fileName;
|
|
|
|
_file.setFileName(_filename);
|
2015-12-24 19:26:28 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-12-30 19:09:20 +00:00
|
|
|
void FileLoader::permitLoadFromCloud() {
|
2015-12-24 19:26:28 +00:00
|
|
|
_fromCloud = LoadFromCloudOrLocal;
|
2014-09-04 07:33:44 +00:00
|
|
|
}
|
|
|
|
|
2018-11-23 14:39:14 +00:00
|
|
|
void FileLoader::notifyAboutProgress() {
|
|
|
|
emit progress(this);
|
2015-12-30 19:09:20 +00:00
|
|
|
}
|
|
|
|
|
2018-08-27 11:35:58 +00:00
|
|
|
void FileLoader::localLoaded(
|
|
|
|
const StorageImageSaved &result,
|
|
|
|
const QByteArray &imageFormat,
|
2018-10-12 16:41:51 +00:00
|
|
|
const QImage &imageData) {
|
2019-01-17 08:18:23 +00:00
|
|
|
_localLoading = nullptr;
|
2017-03-04 11:28:21 +00:00
|
|
|
if (result.data.isEmpty()) {
|
2018-08-27 11:35:58 +00:00
|
|
|
_localStatus = LocalStatus::NotFound;
|
2019-04-12 11:52:39 +00:00
|
|
|
start();
|
2015-12-30 19:09:20 +00:00
|
|
|
return;
|
|
|
|
}
|
2018-10-12 16:41:51 +00:00
|
|
|
if (!imageData.isNull()) {
|
2015-12-30 19:09:20 +00:00
|
|
|
_imageFormat = imageFormat;
|
2018-10-12 16:41:51 +00:00
|
|
|
_imageData = imageData;
|
2015-12-30 19:09:20 +00:00
|
|
|
}
|
2018-10-12 16:41:51 +00:00
|
|
|
finishWithBytes(result.data);
|
2018-11-23 14:39:14 +00:00
|
|
|
notifyAboutProgress();
|
2015-12-30 19:09:20 +00:00
|
|
|
}
|
|
|
|
|
2019-04-12 11:52:39 +00:00
|
|
|
void FileLoader::start() {
|
2018-08-27 11:35:58 +00:00
|
|
|
if (_finished || tryLoadLocal()) {
|
|
|
|
return;
|
|
|
|
} else if (_fromCloud == LoadFromLocalOnly) {
|
2015-12-30 19:09:20 +00:00
|
|
|
cancel();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-08-15 15:59:40 +00:00
|
|
|
if (!_filename.isEmpty() && _toCache == LoadToFileOnly && !_fileIsOpen) {
|
2015-12-30 19:09:20 +00:00
|
|
|
_fileIsOpen = _file.open(QIODevice::WriteOnly);
|
|
|
|
if (!_fileIsOpen) {
|
|
|
|
return cancel(true);
|
|
|
|
}
|
|
|
|
}
|
2019-12-04 12:15:58 +00:00
|
|
|
startLoading();
|
2015-12-30 19:09:20 +00:00
|
|
|
}
|
|
|
|
|
2018-08-27 11:35:58 +00:00
|
|
|
void FileLoader::loadLocal(const Storage::Cache::Key &key) {
|
|
|
|
const auto readImage = (_locationType != AudioFileLocation);
|
2019-03-27 12:11:38 +00:00
|
|
|
auto done = [=, guard = _localLoading.make_guard()](
|
2018-08-28 20:49:16 +00:00
|
|
|
QByteArray &&value,
|
|
|
|
QImage &&image,
|
|
|
|
QByteArray &&format) mutable {
|
2019-02-17 11:52:57 +00:00
|
|
|
crl::on_main(std::move(guard), [
|
2018-08-27 11:35:58 +00:00
|
|
|
=,
|
|
|
|
value = std::move(value),
|
|
|
|
image = std::move(image),
|
2019-02-17 11:52:57 +00:00
|
|
|
format = std::move(format)
|
2018-08-27 11:35:58 +00:00
|
|
|
]() mutable {
|
|
|
|
localLoaded(
|
|
|
|
StorageImageSaved(std::move(value)),
|
|
|
|
format,
|
2018-10-12 16:41:51 +00:00
|
|
|
std::move(image));
|
2018-08-27 11:35:58 +00:00
|
|
|
});
|
|
|
|
};
|
2019-06-17 11:54:00 +00:00
|
|
|
session().data().cache().get(key, [=, callback = std::move(done)](
|
2018-08-28 20:49:16 +00:00
|
|
|
QByteArray &&value) mutable {
|
2018-08-27 11:35:58 +00:00
|
|
|
if (readImage) {
|
|
|
|
crl::async([
|
|
|
|
value = std::move(value),
|
|
|
|
done = std::move(callback)
|
|
|
|
]() mutable {
|
|
|
|
auto format = QByteArray();
|
|
|
|
auto image = App::readImage(value, &format, false);
|
|
|
|
if (!image.isNull()) {
|
2018-08-28 20:49:16 +00:00
|
|
|
done(
|
|
|
|
std::move(value),
|
|
|
|
std::move(image),
|
|
|
|
std::move(format));
|
2018-08-27 11:35:58 +00:00
|
|
|
} else {
|
2018-08-28 20:49:16 +00:00
|
|
|
done(std::move(value), {}, {});
|
2018-08-27 11:35:58 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
2018-08-28 20:49:16 +00:00
|
|
|
callback(std::move(value), {}, {});
|
2018-08-27 11:35:58 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FileLoader::tryLoadLocal() {
|
|
|
|
if (_localStatus == LocalStatus::NotFound
|
|
|
|
|| _localStatus == LocalStatus::Loaded) {
|
|
|
|
return false;
|
|
|
|
} else if (_localStatus == LocalStatus::Loading) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-09-13 12:22:54 +00:00
|
|
|
const auto weak = QPointer<FileLoader>(this);
|
2019-04-12 07:34:39 +00:00
|
|
|
if (_toCache == LoadToCacheAsWell) {
|
|
|
|
loadLocal(cacheKey());
|
2018-08-27 11:35:58 +00:00
|
|
|
emit progress(this);
|
|
|
|
}
|
2018-11-23 14:39:14 +00:00
|
|
|
if (!weak) {
|
|
|
|
return false;
|
|
|
|
} else if (_localStatus != LocalStatus::NotTried) {
|
2018-08-27 11:35:58 +00:00
|
|
|
return _finished;
|
2019-01-17 08:18:23 +00:00
|
|
|
} else if (_localLoading) {
|
2018-08-27 11:35:58 +00:00
|
|
|
_localStatus = LocalStatus::Loading;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
_localStatus = LocalStatus::NotFound;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-12-30 19:09:20 +00:00
|
|
|
void FileLoader::cancel() {
|
|
|
|
cancel(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void FileLoader::cancel(bool fail) {
|
2019-04-10 11:26:15 +00:00
|
|
|
const auto started = (currentOffset() > 0);
|
2019-12-05 08:32:33 +00:00
|
|
|
|
|
|
|
cancelHook();
|
|
|
|
|
2017-03-04 11:28:21 +00:00
|
|
|
_cancelled = true;
|
|
|
|
_finished = true;
|
2015-12-30 19:09:20 +00:00
|
|
|
if (_fileIsOpen) {
|
|
|
|
_file.close();
|
|
|
|
_fileIsOpen = false;
|
|
|
|
_file.remove();
|
|
|
|
}
|
|
|
|
_data = QByteArray();
|
2017-02-27 18:33:42 +00:00
|
|
|
|
2019-09-13 12:22:54 +00:00
|
|
|
const auto weak = QPointer<FileLoader>(this);
|
2015-12-30 19:09:20 +00:00
|
|
|
if (fail) {
|
|
|
|
emit failed(this, started);
|
|
|
|
} else {
|
|
|
|
emit progress(this);
|
|
|
|
}
|
2018-11-23 14:39:14 +00:00
|
|
|
if (weak) {
|
|
|
|
_filename = QString();
|
|
|
|
_file.setFileName(_filename);
|
|
|
|
}
|
2015-12-30 19:09:20 +00:00
|
|
|
}
|
|
|
|
|
2019-04-11 07:59:18 +00:00
|
|
|
int FileLoader::currentOffset() const {
|
|
|
|
return (_fileIsOpen ? _file.size() : _data.size()) - _skippedBytes;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FileLoader::writeResultPart(int offset, bytes::const_span buffer) {
|
|
|
|
Expects(!_finished);
|
|
|
|
|
2019-04-12 09:25:00 +00:00
|
|
|
if (buffer.empty()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (_fileIsOpen) {
|
|
|
|
auto fsize = _file.size();
|
|
|
|
if (offset < fsize) {
|
|
|
|
_skippedBytes -= buffer.size();
|
|
|
|
} else if (offset > fsize) {
|
|
|
|
_skippedBytes += offset - fsize;
|
|
|
|
}
|
|
|
|
_file.seek(offset);
|
|
|
|
if (_file.write(reinterpret_cast<const char*>(buffer.data()), buffer.size()) != qint64(buffer.size())) {
|
|
|
|
cancel(true);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
_data.reserve(offset + buffer.size());
|
|
|
|
if (offset > _data.size()) {
|
|
|
|
_skippedBytes += offset - _data.size();
|
|
|
|
_data.resize(offset);
|
|
|
|
}
|
|
|
|
if (offset == _data.size()) {
|
|
|
|
_data.append(reinterpret_cast<const char*>(buffer.data()), buffer.size());
|
|
|
|
} else {
|
|
|
|
_skippedBytes -= buffer.size();
|
|
|
|
if (int64(offset + buffer.size()) > _data.size()) {
|
|
|
|
_data.resize(offset + buffer.size());
|
|
|
|
}
|
|
|
|
const auto dst = bytes::make_detached_span(_data).subspan(
|
|
|
|
offset,
|
|
|
|
buffer.size());
|
|
|
|
bytes::copy(dst, buffer);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray FileLoader::readLoadedPartBack(int offset, int size) {
|
|
|
|
Expects(offset >= 0 && size > 0);
|
|
|
|
|
|
|
|
if (_fileIsOpen) {
|
|
|
|
if (_file.openMode() == QIODevice::WriteOnly) {
|
|
|
|
_file.close();
|
|
|
|
_fileIsOpen = _file.open(QIODevice::ReadWrite);
|
|
|
|
if (!_fileIsOpen) {
|
2019-04-11 07:59:18 +00:00
|
|
|
cancel(true);
|
2019-04-12 09:25:00 +00:00
|
|
|
return QByteArray();
|
2019-04-11 07:59:18 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-12 09:25:00 +00:00
|
|
|
if (!_file.seek(offset)) {
|
|
|
|
return QByteArray();
|
|
|
|
}
|
|
|
|
auto result = _file.read(size);
|
|
|
|
return (result.size() == size) ? result : QByteArray();
|
2019-04-11 07:59:18 +00:00
|
|
|
}
|
2019-04-12 09:25:00 +00:00
|
|
|
return (offset + size <= _data.size())
|
|
|
|
? _data.mid(offset, size)
|
|
|
|
: QByteArray();
|
2019-04-11 07:59:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool FileLoader::finalizeResult() {
|
|
|
|
Expects(!_finished);
|
|
|
|
|
|
|
|
if (!_filename.isEmpty() && (_toCache == LoadToCacheAsWell)) {
|
|
|
|
if (!_fileIsOpen) {
|
|
|
|
_fileIsOpen = _file.open(QIODevice::WriteOnly);
|
|
|
|
}
|
2019-04-12 09:25:00 +00:00
|
|
|
_file.seek(0);
|
2019-04-11 07:59:18 +00:00
|
|
|
if (!_fileIsOpen || _file.write(_data) != qint64(_data.size())) {
|
|
|
|
cancel(true);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_finished = true;
|
|
|
|
if (_fileIsOpen) {
|
|
|
|
_file.close();
|
|
|
|
_fileIsOpen = false;
|
|
|
|
Platform::File::PostprocessDownloaded(
|
|
|
|
QFileInfo(_file).absoluteFilePath());
|
|
|
|
}
|
|
|
|
if (_localStatus == LocalStatus::NotFound) {
|
|
|
|
if (const auto key = fileLocationKey()) {
|
2019-04-12 07:34:39 +00:00
|
|
|
if (!_filename.isEmpty()) {
|
|
|
|
Local::writeFileLocation(*key, FileLocation(_filename));
|
2019-04-11 07:59:18 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-12 07:34:39 +00:00
|
|
|
if ((_toCache == LoadToCacheAsWell)
|
|
|
|
&& (_data.size() <= Storage::kMaxFileInMemory)) {
|
2019-06-17 11:54:00 +00:00
|
|
|
session().data().cache().put(
|
2019-04-12 07:34:39 +00:00
|
|
|
cacheKey(),
|
|
|
|
Storage::Cache::Database::TaggedValue(
|
|
|
|
base::duplicate(_data),
|
|
|
|
_cacheTag));
|
|
|
|
}
|
2019-04-11 07:59:18 +00:00
|
|
|
}
|
2019-12-04 12:15:58 +00:00
|
|
|
Auth().downloaderTaskFinished().notify();
|
2017-12-08 13:13:13 +00:00
|
|
|
return true;
|
|
|
|
}
|