mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 10:11:41 -05:00
Follow secure file upload/download progress.
This commit is contained in:
parent
083b520eee
commit
c2aa9c571c
6 changed files with 334 additions and 81 deletions
|
@ -27,6 +27,11 @@ public:
|
||||||
const QString &description);
|
const QString &description);
|
||||||
|
|
||||||
void setImage(const QImage &image);
|
void setImage(const QImage &image);
|
||||||
|
void setDescription(const QString &description);
|
||||||
|
|
||||||
|
rpl::producer<> deleteClicks() const {
|
||||||
|
return _delete->clicks();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int resizeGetHeight(int newWidth) override;
|
int resizeGetHeight(int newWidth) override;
|
||||||
|
@ -54,13 +59,11 @@ ScanButton::ScanButton(
|
||||||
, _title(
|
, _title(
|
||||||
st::semiboldTextStyle,
|
st::semiboldTextStyle,
|
||||||
title,
|
title,
|
||||||
Ui::NameTextOptions(),
|
Ui::NameTextOptions())
|
||||||
st::boxWideWidth / 2)
|
|
||||||
, _description(
|
, _description(
|
||||||
st::defaultTextStyle,
|
st::defaultTextStyle,
|
||||||
description,
|
description,
|
||||||
Ui::NameTextOptions(),
|
Ui::NameTextOptions())
|
||||||
st::boxWideWidth / 2)
|
|
||||||
, _delete(this, st::passportRowCheckbox) {
|
, _delete(this, st::passportRowCheckbox) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,10 +72,18 @@ void ScanButton::setImage(const QImage &image) {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScanButton::setDescription(const QString &description) {
|
||||||
|
_description.setText(
|
||||||
|
st::defaultTextStyle,
|
||||||
|
description,
|
||||||
|
Ui::NameTextOptions());
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
int ScanButton::resizeGetHeight(int newWidth) {
|
int ScanButton::resizeGetHeight(int newWidth) {
|
||||||
const auto availableWidth = countAvailableWidth(newWidth);
|
const auto availableWidth = countAvailableWidth(newWidth);
|
||||||
_titleHeight = _title.countHeight(availableWidth);
|
_titleHeight = st::semiboldFont->height;
|
||||||
_descriptionHeight = _description.countHeight(availableWidth);
|
_descriptionHeight = st::normalFont->height;
|
||||||
const auto result = st::passportRowPadding.top()
|
const auto result = st::passportRowPadding.top()
|
||||||
+ _titleHeight
|
+ _titleHeight
|
||||||
+ st::passportRowSkip
|
+ st::passportRowSkip
|
||||||
|
@ -122,10 +133,10 @@ void ScanButton::paintEvent(QPaintEvent *e) {
|
||||||
left += size + st::passportRowPadding.left();
|
left += size + st::passportRowPadding.left();
|
||||||
availableWidth -= size + st::passportRowPadding.left();
|
availableWidth -= size + st::passportRowPadding.left();
|
||||||
|
|
||||||
_title.drawLeft(p, left, top, availableWidth, width());
|
_title.drawLeftElided(p, left, top, availableWidth, width());
|
||||||
top += _titleHeight + st::passportRowSkip;
|
top += _titleHeight + st::passportRowSkip;
|
||||||
|
|
||||||
_description.drawLeft(p, left, top, availableWidth, width());
|
_description.drawLeftElided(p, left, top, availableWidth, width());
|
||||||
top += _descriptionHeight + st::passportRowPadding.bottom();
|
top += _descriptionHeight + st::passportRowPadding.bottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,21 +166,18 @@ void IdentityBox::prepare() {
|
||||||
setTitle(langFactory(lng_passport_identity_title));
|
setTitle(langFactory(lng_passport_identity_title));
|
||||||
|
|
||||||
auto index = 0;
|
auto index = 0;
|
||||||
auto height = st::contactPadding.top();
|
|
||||||
for (const auto &scan : _files) {
|
for (const auto &scan : _files) {
|
||||||
_scans.push_back(object_ptr<ScanButton>(this, QString("Scan %1").arg(++index), scan.date));
|
_scans.push_back(object_ptr<ScanButton>(
|
||||||
|
this,
|
||||||
|
QString("Scan %1").arg(++index), // #TODO langs
|
||||||
|
scan.status));
|
||||||
_scans.back()->setImage(scan.thumb);
|
_scans.back()->setImage(scan.thumb);
|
||||||
_scans.back()->resizeToWidth(st::boxWideWidth);
|
_scans.back()->resizeToWidth(st::boxWideWidth);
|
||||||
height += _scans.back()->height();
|
_scans.back()->deleteClicks(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
_controller->deleteScan(_fieldIndex, index - 1);
|
||||||
|
}, lifetime());
|
||||||
}
|
}
|
||||||
height += st::contactPadding.top()
|
|
||||||
+ _uploadScan->height()
|
|
||||||
+ st::contactSkip
|
|
||||||
+ _name->height()
|
|
||||||
+ st::contactSkip
|
|
||||||
+ _surname->height()
|
|
||||||
+ st::contactPadding.bottom()
|
|
||||||
+ st::boxPadding.bottom();
|
|
||||||
|
|
||||||
addButton(langFactory(lng_settings_save), [=] {
|
addButton(langFactory(lng_settings_save), [=] {
|
||||||
save();
|
save();
|
||||||
|
@ -185,7 +193,23 @@ void IdentityBox::prepare() {
|
||||||
_uploadScan->addClickHandler([=] {
|
_uploadScan->addClickHandler([=] {
|
||||||
chooseScan();
|
chooseScan();
|
||||||
});
|
});
|
||||||
setDimensions(st::boxWideWidth, height);
|
setDimensions(st::boxWideWidth, countHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
int IdentityBox::countHeight() const {
|
||||||
|
auto height = st::contactPadding.top();
|
||||||
|
for (const auto &scan : _scans) {
|
||||||
|
height += scan->height();
|
||||||
|
}
|
||||||
|
height += st::contactPadding.top()
|
||||||
|
+ _uploadScan->height()
|
||||||
|
+ st::contactSkip
|
||||||
|
+ _name->height()
|
||||||
|
+ st::contactSkip
|
||||||
|
+ _surname->height()
|
||||||
|
+ st::contactPadding.bottom()
|
||||||
|
+ st::boxPadding.bottom();
|
||||||
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IdentityBox::updateScan(ScanInfo &&info) {
|
void IdentityBox::updateScan(ScanInfo &&info) {
|
||||||
|
@ -194,8 +218,21 @@ void IdentityBox::updateScan(ScanInfo &&info) {
|
||||||
});
|
});
|
||||||
if (i != _files.end()) {
|
if (i != _files.end()) {
|
||||||
*i = info;
|
*i = info;
|
||||||
|
_scans[i - _files.begin()]->setDescription(i->status);
|
||||||
_scans[i - _files.begin()]->setImage(i->thumb);
|
_scans[i - _files.begin()]->setImage(i->thumb);
|
||||||
|
} else {
|
||||||
|
_files.push_back(std::move(info));
|
||||||
|
_scans.push_back(object_ptr<ScanButton>(
|
||||||
|
this,
|
||||||
|
QString("Scan %1").arg(_files.size()),
|
||||||
|
_files.back().status));
|
||||||
|
_scans.back()->setImage(_files.back().thumb);
|
||||||
|
_scans.back()->resizeToWidth(st::boxWideWidth);
|
||||||
|
_scans.back()->show();
|
||||||
|
updateControlsPosition();
|
||||||
|
setDimensions(st::boxWideWidth, countHeight());
|
||||||
}
|
}
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IdentityBox::setInnerFocus() {
|
void IdentityBox::setInnerFocus() {
|
||||||
|
@ -211,6 +248,10 @@ void IdentityBox::resizeEvent(QResizeEvent *e) {
|
||||||
_name->height());
|
_name->height());
|
||||||
_surname->resize(_name->width(), _surname->height());
|
_surname->resize(_name->width(), _surname->height());
|
||||||
|
|
||||||
|
updateControlsPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
void IdentityBox::updateControlsPosition() {
|
||||||
auto top = st::contactPadding.top();
|
auto top = st::contactPadding.top();
|
||||||
for (const auto &scan : _scans) {
|
for (const auto &scan : _scans) {
|
||||||
scan->moveToLeft(0, top);
|
scan->moveToLeft(0, top);
|
||||||
|
@ -253,7 +294,6 @@ void IdentityBox::encryptScan(const QString &path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void IdentityBox::encryptScanContent(QByteArray &&content) {
|
void IdentityBox::encryptScanContent(QByteArray &&content) {
|
||||||
_uploadScan->hide();
|
|
||||||
_controller->uploadScan(_fieldIndex, std::move(content));
|
_controller->uploadScan(_fieldIndex, std::move(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,8 @@ private:
|
||||||
void encryptScan(const QString &path);
|
void encryptScan(const QString &path);
|
||||||
void encryptScanContent(QByteArray &&content);
|
void encryptScanContent(QByteArray &&content);
|
||||||
void updateScan(ScanInfo &&info);
|
void updateScan(ScanInfo &&info);
|
||||||
|
int countHeight() const;
|
||||||
|
void updateControlsPosition();
|
||||||
void save();
|
void save();
|
||||||
|
|
||||||
not_null<FormController*> _controller;
|
not_null<FormController*> _controller;
|
||||||
|
|
|
@ -20,6 +20,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "storage/file_download.h"
|
#include "storage/file_download.h"
|
||||||
|
|
||||||
namespace Passport {
|
namespace Passport {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
QImage ReadImage(bytes::const_span buffer) {
|
||||||
|
return App::readImage(QByteArray::fromRawData(
|
||||||
|
reinterpret_cast<const char*>(buffer.data()),
|
||||||
|
buffer.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
FormRequest::FormRequest(
|
FormRequest::FormRequest(
|
||||||
UserId botId,
|
UserId botId,
|
||||||
|
@ -203,18 +212,45 @@ QString FormController::passwordHint() const {
|
||||||
return _password.hint;
|
return _password.hint;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::uploadScan(int index, QByteArray &&content) {
|
void FormController::uploadScan(int fieldIndex, QByteArray &&content) {
|
||||||
Expects(_editBox != nullptr);
|
Expects(_editBox != nullptr);
|
||||||
Expects(index >= 0 && index < _form.fields.size());
|
Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size());
|
||||||
|
|
||||||
auto &field = _form.fields[index];
|
auto &field = _form.fields[fieldIndex];
|
||||||
if (field.secret.empty()) {
|
if (field.secret.empty()) {
|
||||||
field.secret = GenerateSecretBytes();
|
field.secret = GenerateSecretBytes();
|
||||||
}
|
}
|
||||||
|
auto fileIndex = int(field.filesInEdit.size());
|
||||||
|
field.filesInEdit.emplace_back(
|
||||||
|
File(),
|
||||||
|
nullptr);
|
||||||
|
const auto fileId = rand_value<uint64>();
|
||||||
|
auto &file = field.filesInEdit.back();
|
||||||
|
file.fields.size = content.size();
|
||||||
|
file.fields.id = fileId;
|
||||||
|
file.fields.dcId = MTP::maindc();
|
||||||
|
file.fields.image = ReadImage(bytes::make_span(content));
|
||||||
|
file.fields.downloadOffset = file.fields.size;
|
||||||
|
|
||||||
|
_scanUpdated.fire(collectScanInfo(file));
|
||||||
|
|
||||||
|
encryptScan(fieldIndex, fileIndex, std::move(content));
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormController::encryptScan(
|
||||||
|
int fieldIndex,
|
||||||
|
int fileIndex,
|
||||||
|
QByteArray &&content) {
|
||||||
|
Expects(_editBox != nullptr);
|
||||||
|
Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size());
|
||||||
|
Expects(fileIndex >= 0
|
||||||
|
&& fileIndex < _form.fields[fieldIndex].filesInEdit.size());
|
||||||
|
|
||||||
|
auto &field = _form.fields[fieldIndex];
|
||||||
const auto weak = _editBox;
|
const auto weak = _editBox;
|
||||||
crl::async([
|
crl::async([
|
||||||
=,
|
=,
|
||||||
|
fileId = field.filesInEdit[fileIndex].fields.id,
|
||||||
bytes = std::move(content),
|
bytes = std::move(content),
|
||||||
valueSecret = field.secret
|
valueSecret = field.secret
|
||||||
] {
|
] {
|
||||||
|
@ -222,7 +258,7 @@ void FormController::uploadScan(int index, QByteArray &&content) {
|
||||||
bytes::make_span(bytes),
|
bytes::make_span(bytes),
|
||||||
valueSecret);
|
valueSecret);
|
||||||
auto result = UploadedScan();
|
auto result = UploadedScan();
|
||||||
result.fileId = rand_value<uint64>();
|
result.fileId = fileId;
|
||||||
result.hash = std::move(data.hash);
|
result.hash = std::move(data.hash);
|
||||||
result.bytes = std::move(data.bytes);
|
result.bytes = std::move(data.bytes);
|
||||||
result.md5checksum.resize(32);
|
result.md5checksum.resize(32);
|
||||||
|
@ -232,38 +268,60 @@ void FormController::uploadScan(int index, QByteArray &&content) {
|
||||||
result.md5checksum.data());
|
result.md5checksum.data());
|
||||||
crl::on_main([=, encrypted = std::move(result)]() mutable {
|
crl::on_main([=, encrypted = std::move(result)]() mutable {
|
||||||
if (weak) {
|
if (weak) {
|
||||||
uploadEncryptedScan(index, std::move(encrypted));
|
uploadEncryptedScan(
|
||||||
|
fieldIndex,
|
||||||
|
fileIndex,
|
||||||
|
std::move(encrypted));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FormController::deleteScan(
|
||||||
|
int fieldIndex,
|
||||||
|
int fileIndex) {
|
||||||
|
Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size());
|
||||||
|
Expects(fileIndex >= 0
|
||||||
|
&& fileIndex < _form.fields[fieldIndex].filesInEdit.size());
|
||||||
|
|
||||||
|
auto &file = _form.fields[fieldIndex].filesInEdit[fileIndex];
|
||||||
|
file.deleted = !file.deleted;
|
||||||
|
_scanUpdated.fire(collectScanInfo(file));
|
||||||
|
}
|
||||||
|
|
||||||
void FormController::subscribeToUploader() {
|
void FormController::subscribeToUploader() {
|
||||||
if (_uploaderSubscriptions) {
|
if (_uploaderSubscriptions) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using namespace Storage;
|
||||||
Auth().uploader().secureReady(
|
Auth().uploader().secureReady(
|
||||||
) | rpl::start_with_next([=](const Storage::UploadedSecure &data) {
|
) | rpl::start_with_next([=](const UploadSecureDone &data) {
|
||||||
scanUploaded(data);
|
scanUploadDone(data);
|
||||||
}, _uploaderSubscriptions);
|
}, _uploaderSubscriptions);
|
||||||
Auth().uploader().secureProgress(
|
Auth().uploader().secureProgress(
|
||||||
) | rpl::start_with_next([=](const FullMsgId &fullId) {
|
) | rpl::start_with_next([=](const UploadSecureProgress &data) {
|
||||||
|
scanUploadProgress(data);
|
||||||
}, _uploaderSubscriptions);
|
}, _uploaderSubscriptions);
|
||||||
Auth().uploader().secureFailed(
|
Auth().uploader().secureFailed(
|
||||||
) | rpl::start_with_next([=](const FullMsgId &fullId) {
|
) | rpl::start_with_next([=](const FullMsgId &fullId) {
|
||||||
|
scanUploadFail(fullId);
|
||||||
}, _uploaderSubscriptions);
|
}, _uploaderSubscriptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::uploadEncryptedScan(int index, UploadedScan &&data) {
|
void FormController::uploadEncryptedScan(
|
||||||
|
int fieldIndex,
|
||||||
|
int fileIndex,
|
||||||
|
UploadedScan &&data) {
|
||||||
Expects(_editBox != nullptr);
|
Expects(_editBox != nullptr);
|
||||||
Expects(index >= 0 && index < _form.fields.size());
|
Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size());
|
||||||
|
Expects(fileIndex >= 0
|
||||||
|
&& fileIndex < _form.fields[fieldIndex].filesInEdit.size());
|
||||||
|
|
||||||
subscribeToUploader();
|
subscribeToUploader();
|
||||||
|
|
||||||
_form.fields[index].filesInEdit.emplace_back(
|
auto &file = _form.fields[fieldIndex].filesInEdit[fileIndex];
|
||||||
File(),
|
file.uploaded = std::make_unique<UploadedScan>(std::move(data));
|
||||||
std::make_unique<UploadedScan>(std::move(data)));
|
|
||||||
auto &file = _form.fields[index].filesInEdit.back();
|
|
||||||
|
|
||||||
auto uploaded = std::make_shared<FileLoadResult>(
|
auto uploaded = std::make_shared<FileLoadResult>(
|
||||||
TaskId(),
|
TaskId(),
|
||||||
|
@ -282,17 +340,37 @@ void FormController::uploadEncryptedScan(int index, UploadedScan &&data) {
|
||||||
Auth().uploader().upload(file.uploaded->fullId, std::move(uploaded));
|
Auth().uploader().upload(file.uploaded->fullId, std::move(uploaded));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::scanUploaded(const Storage::UploadedSecure &data) {
|
void FormController::scanUploadDone(const Storage::UploadSecureDone &data) {
|
||||||
if (const auto edit = findEditFile(data.fullId)) {
|
if (const auto file = findEditFile(data.fullId)) {
|
||||||
Assert(edit->uploaded != nullptr);
|
Assert(file->uploaded != nullptr);
|
||||||
|
Assert(file->uploaded->fileId == data.fileId);
|
||||||
|
|
||||||
edit->fields.id = edit->uploaded->fileId = data.fileId;
|
file->uploaded->partsCount = data.partsCount;
|
||||||
edit->fields.size = edit->uploaded->bytes.size();
|
file->fields.fileHash = std::move(file->uploaded->hash);
|
||||||
edit->fields.dcId = MTP::maindc();
|
file->uploaded->fullId = FullMsgId();
|
||||||
edit->uploaded->partsCount = data.partsCount;
|
|
||||||
edit->fields.bytes = std::move(edit->uploaded->bytes);
|
_scanUpdated.fire(collectScanInfo(*file));
|
||||||
edit->fields.fileHash = std::move(edit->uploaded->hash);
|
}
|
||||||
edit->uploaded->fullId = FullMsgId();
|
}
|
||||||
|
|
||||||
|
void FormController::scanUploadProgress(
|
||||||
|
const Storage::UploadSecureProgress &data) {
|
||||||
|
if (const auto file = findEditFile(data.fullId)) {
|
||||||
|
Assert(file->uploaded != nullptr);
|
||||||
|
|
||||||
|
file->uploaded->offset = data.offset;
|
||||||
|
|
||||||
|
_scanUpdated.fire(collectScanInfo(*file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormController::scanUploadFail(const FullMsgId &fullId) {
|
||||||
|
if (const auto file = findEditFile(fullId)) {
|
||||||
|
Assert(file->uploaded != nullptr);
|
||||||
|
|
||||||
|
file->uploaded->offset = -1;
|
||||||
|
|
||||||
|
_scanUpdated.fire(collectScanInfo(*file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,11 +454,17 @@ void FormController::editField(int index) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::loadFiles(const std::vector<File> &files) {
|
void FormController::loadFiles(std::vector<File> &files) {
|
||||||
for (const auto &file : files) {
|
for (auto &file : files) {
|
||||||
|
if (!file.image.isNull()) {
|
||||||
|
file.downloadOffset = file.size;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const auto key = FileKey{ file.id, file.dcId };
|
const auto key = FileKey{ file.id, file.dcId };
|
||||||
const auto i = _fileLoaders.find(key);
|
const auto i = _fileLoaders.find(key);
|
||||||
if (i == _fileLoaders.end()) {
|
if (i == _fileLoaders.end()) {
|
||||||
|
file.downloadOffset = 0;
|
||||||
const auto [i, ok] = _fileLoaders.emplace(
|
const auto [i, ok] = _fileLoaders.emplace(
|
||||||
key,
|
key,
|
||||||
std::make_unique<mtpFileLoader>(
|
std::make_unique<mtpFileLoader>(
|
||||||
|
@ -397,35 +481,91 @@ void FormController::loadFiles(const std::vector<File> &files) {
|
||||||
const auto loader = i->second.get();
|
const auto loader = i->second.get();
|
||||||
loader->connect(loader, &mtpFileLoader::progress, [=] {
|
loader->connect(loader, &mtpFileLoader::progress, [=] {
|
||||||
if (loader->finished()) {
|
if (loader->finished()) {
|
||||||
fileLoaded(key, loader->bytes());
|
fileLoadDone(key, loader->bytes());
|
||||||
|
} else {
|
||||||
|
fileLoadProgress(key, loader->currentOffset());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
loader->connect(loader, &mtpFileLoader::failed, [=] {
|
loader->connect(loader, &mtpFileLoader::failed, [=] {
|
||||||
|
fileLoadFail(key);
|
||||||
});
|
});
|
||||||
loader->start();
|
loader->start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::fileLoaded(FileKey key, const QByteArray &bytes) {
|
void FormController::fileLoadDone(FileKey key, const QByteArray &bytes) {
|
||||||
if (const auto [field, file] = findFile(key); file != nullptr) {
|
if (const auto [field, file] = findFile(key); file != nullptr) {
|
||||||
const auto decrypted = DecryptData(
|
const auto decrypted = DecryptData(
|
||||||
bytes::make_span(bytes),
|
bytes::make_span(bytes),
|
||||||
file->fileHash,
|
file->fileHash,
|
||||||
field->secret);
|
field->secret);
|
||||||
auto image = App::readImage(QByteArray::fromRawData(
|
file->downloadOffset = file->size;
|
||||||
|
file->image = App::readImage(QByteArray::fromRawData(
|
||||||
reinterpret_cast<const char*>(decrypted.data()),
|
reinterpret_cast<const char*>(decrypted.data()),
|
||||||
decrypted.size()));
|
decrypted.size()));
|
||||||
if (!image.isNull()) {
|
if (const auto fileInEdit = findEditFile(key)) {
|
||||||
_scanUpdated.fire({
|
fileInEdit->fields.image = file->image;
|
||||||
FileKey{ file->id, file->dcId },
|
fileInEdit->fields.downloadOffset = file->downloadOffset;
|
||||||
QString("loaded"),
|
_scanUpdated.fire(collectScanInfo(*fileInEdit));
|
||||||
std::move(image),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FormController::fileLoadProgress(FileKey key, int offset) {
|
||||||
|
if (const auto [field, file] = findFile(key); file != nullptr) {
|
||||||
|
file->downloadOffset = offset;
|
||||||
|
if (const auto fileInEdit = findEditFile(key)) {
|
||||||
|
fileInEdit->fields.downloadOffset = file->downloadOffset;
|
||||||
|
_scanUpdated.fire(collectScanInfo(*fileInEdit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormController::fileLoadFail(FileKey key) {
|
||||||
|
if (const auto [field, file] = findFile(key); file != nullptr) {
|
||||||
|
file->downloadOffset = -1;
|
||||||
|
if (const auto fileInEdit = findEditFile(key)) {
|
||||||
|
fileInEdit->fields.downloadOffset = file->downloadOffset;
|
||||||
|
_scanUpdated.fire(collectScanInfo(*fileInEdit));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanInfo FormController::collectScanInfo(const EditFile &file) const {
|
||||||
|
const auto status = [&] {
|
||||||
|
if (file.deleted) {
|
||||||
|
return QString("deleted");
|
||||||
|
} else if (file.fields.accessHash) {
|
||||||
|
if (file.fields.downloadOffset < 0) {
|
||||||
|
return QString("download failed");
|
||||||
|
} else if (file.fields.downloadOffset < file.fields.size) {
|
||||||
|
return QString("downloading %1 / %2"
|
||||||
|
).arg(file.fields.downloadOffset
|
||||||
|
).arg(file.fields.size);
|
||||||
|
} else {
|
||||||
|
return QString("download ready");
|
||||||
|
}
|
||||||
|
} else if (file.uploaded) {
|
||||||
|
if (file.uploaded->offset < 0) {
|
||||||
|
return QString("upload failed");
|
||||||
|
} else if (file.uploaded->fullId) {
|
||||||
|
return QString("uploading %1 / %2"
|
||||||
|
).arg(file.uploaded->offset
|
||||||
|
).arg(file.uploaded->bytes.size());
|
||||||
|
} else {
|
||||||
|
return QString("upload ready");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return QString("preparing");
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
return {
|
||||||
|
FileKey{ file.fields.id, file.fields.dcId },
|
||||||
|
status,
|
||||||
|
file.fields.image };
|
||||||
|
}
|
||||||
|
|
||||||
IdentityData FormController::fieldDataIdentity(const Field &field) const {
|
IdentityData FormController::fieldDataIdentity(const Field &field) const {
|
||||||
const auto &map = field.parsedData;
|
const auto &map = field.parsedData;
|
||||||
auto result = IdentityData();
|
auto result = IdentityData();
|
||||||
|
@ -442,10 +582,7 @@ std::vector<ScanInfo> FormController::fieldFilesIdentity(
|
||||||
const Field &field) const {
|
const Field &field) const {
|
||||||
auto result = std::vector<ScanInfo>();
|
auto result = std::vector<ScanInfo>();
|
||||||
for (const auto &file : field.filesInEdit) {
|
for (const auto &file : field.filesInEdit) {
|
||||||
result.push_back({
|
result.push_back(collectScanInfo(file));
|
||||||
FileKey{ file.fields.id, file.fields.dcId },
|
|
||||||
langDateTime(QDateTime::currentDateTime()),
|
|
||||||
QImage() });
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -481,7 +618,9 @@ void FormController::saveIdentity(int index) {
|
||||||
auto inputFiles = QVector<MTPInputSecureFile>();
|
auto inputFiles = QVector<MTPInputSecureFile>();
|
||||||
inputFiles.reserve(field.filesInEdit.size());
|
inputFiles.reserve(field.filesInEdit.size());
|
||||||
for (const auto &file : field.filesInEdit) {
|
for (const auto &file : field.filesInEdit) {
|
||||||
if (const auto uploaded = file.uploaded.get()) {
|
if (file.deleted) {
|
||||||
|
continue;
|
||||||
|
} else if (const auto uploaded = file.uploaded.get()) {
|
||||||
inputFiles.push_back(MTP_inputSecureFileUploaded(
|
inputFiles.push_back(MTP_inputSecureFileUploaded(
|
||||||
MTP_long(file.fields.id),
|
MTP_long(file.fields.id),
|
||||||
MTP_int(uploaded->partsCount),
|
MTP_int(uploaded->partsCount),
|
||||||
|
@ -501,7 +640,9 @@ void FormController::saveIdentity(int index) {
|
||||||
field.secret);
|
field.secret);
|
||||||
const auto fileHashes = ranges::view::all(
|
const auto fileHashes = ranges::view::all(
|
||||||
field.filesInEdit
|
field.filesInEdit
|
||||||
) | ranges::view::transform([](const EditFile &file) {
|
) | ranges::view::filter([](const EditFile &file) {
|
||||||
|
return !file.deleted;
|
||||||
|
}) | ranges::view::transform([](const EditFile &file) {
|
||||||
return bytes::make_span(file.fields.fileHash);
|
return bytes::make_span(file.fields.fileHash);
|
||||||
});
|
});
|
||||||
const auto valueHash = openssl::Sha256(bytes::concatenate(
|
const auto valueHash = openssl::Sha256(bytes::concatenate(
|
||||||
|
@ -523,6 +664,13 @@ void FormController::saveIdentity(int index) {
|
||||||
MTP_bytes(valueHash)),
|
MTP_bytes(valueHash)),
|
||||||
MTP_long(CountSecureSecretHash(_secret))
|
MTP_long(CountSecureSecretHash(_secret))
|
||||||
)).done([=](const MTPSecureValueSaved &result) {
|
)).done([=](const MTPSecureValueSaved &result) {
|
||||||
|
Expects(result.type() == mtpc_secureValueSaved);
|
||||||
|
|
||||||
|
const auto &data = result.c_secureValueSaved();
|
||||||
|
_form.fields[index].files = parseFiles(
|
||||||
|
data.vfiles.v,
|
||||||
|
base::take(_form.fields[index].filesInEdit));
|
||||||
|
|
||||||
Ui::show(Box<InformBox>("Saved"), LayerOption::KeepOther);
|
Ui::show(Box<InformBox>("Saved"), LayerOption::KeepOther);
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
Ui::show(Box<InformBox>("Error saving value."));
|
Ui::show(Box<InformBox>("Error saving value."));
|
||||||
|
@ -610,10 +758,22 @@ auto FormController::parseEncryptedField(
|
||||||
const auto &fields = data.vdata.c_secureData();
|
const auto &fields = data.vdata.c_secureData();
|
||||||
result.originalData = fields.vdata.v;
|
result.originalData = fields.vdata.v;
|
||||||
result.dataHash = bytes::make_vector(fields.vdata_hash.v);
|
result.dataHash = bytes::make_vector(fields.vdata_hash.v);
|
||||||
for (const auto &file : data.vfiles.v) {
|
result.files = parseFiles(data.vfiles.v);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto FormController::parseFiles(
|
||||||
|
const QVector<MTPSecureFile> &data,
|
||||||
|
const std::vector<EditFile> &editData) const
|
||||||
|
-> std::vector<File> {
|
||||||
|
auto result = std::vector<File>();
|
||||||
|
result.reserve(data.size());
|
||||||
|
|
||||||
|
auto index = 0;
|
||||||
|
for (const auto &file : data) {
|
||||||
switch (file.type()) {
|
switch (file.type()) {
|
||||||
case mtpc_secureFileEmpty: {
|
case mtpc_secureFileEmpty: {
|
||||||
result.files.push_back(File());
|
|
||||||
} break;
|
} break;
|
||||||
case mtpc_secureFile: {
|
case mtpc_secureFile: {
|
||||||
const auto &fields = file.c_secureFile();
|
const auto &fields = file.c_secureFile();
|
||||||
|
@ -623,10 +783,20 @@ auto FormController::parseEncryptedField(
|
||||||
normal.size = fields.vsize.v;
|
normal.size = fields.vsize.v;
|
||||||
normal.dcId = fields.vdc_id.v;
|
normal.dcId = fields.vdc_id.v;
|
||||||
normal.fileHash = bytes::make_vector(fields.vfile_hash.v);
|
normal.fileHash = bytes::make_vector(fields.vfile_hash.v);
|
||||||
result.files.push_back(std::move(normal));
|
const auto i = ranges::find(
|
||||||
|
editData,
|
||||||
|
normal.fileHash,
|
||||||
|
[](const EditFile &file) { return file.fields.fileHash; });
|
||||||
|
if (i != editData.end()) {
|
||||||
|
normal.image = i->fields.image;
|
||||||
|
normal.downloadOffset = i->fields.downloadOffset;
|
||||||
|
}
|
||||||
|
result.push_back(std::move(normal));
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
++index;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -705,6 +875,17 @@ auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto FormController::findEditFile(const FileKey &key) -> EditFile* {
|
||||||
|
for (auto &field : _form.fields) {
|
||||||
|
for (auto &file : field.filesInEdit) {
|
||||||
|
if (file.fields.dcId == key.dcId && file.fields.id == key.id) {
|
||||||
|
return &file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
auto FormController::findFile(const FileKey &key)
|
auto FormController::findFile(const FileKey &key)
|
||||||
-> std::pair<Field*, File*> {
|
-> std::pair<Field*, File*> {
|
||||||
for (auto &field : _form.fields) {
|
for (auto &field : _form.fields) {
|
||||||
|
|
|
@ -13,7 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
class BoxContent;
|
class BoxContent;
|
||||||
|
|
||||||
namespace Storage {
|
namespace Storage {
|
||||||
struct UploadedSecure;
|
struct UploadSecureDone;
|
||||||
|
struct UploadSecureProgress;
|
||||||
} // namespace Storage
|
} // namespace Storage
|
||||||
|
|
||||||
namespace Window {
|
namespace Window {
|
||||||
|
@ -65,7 +66,7 @@ struct FileKey {
|
||||||
|
|
||||||
struct ScanInfo {
|
struct ScanInfo {
|
||||||
FileKey key;
|
FileKey key;
|
||||||
QString date;
|
QString status;
|
||||||
QImage thumb;
|
QImage thumb;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -82,7 +83,8 @@ public:
|
||||||
rpl::producer<QString> passwordError() const;
|
rpl::producer<QString> passwordError() const;
|
||||||
QString passwordHint() const;
|
QString passwordHint() const;
|
||||||
|
|
||||||
void uploadScan(int index, QByteArray &&content);
|
void uploadScan(int fieldIndex, QByteArray &&content);
|
||||||
|
void deleteScan(int fieldIndex, int fileIndex);
|
||||||
|
|
||||||
rpl::producer<> secretReadyEvents() const;
|
rpl::producer<> secretReadyEvents() const;
|
||||||
|
|
||||||
|
@ -111,6 +113,8 @@ private:
|
||||||
QByteArray md5checksum;
|
QByteArray md5checksum;
|
||||||
bytes::vector hash;
|
bytes::vector hash;
|
||||||
bytes::vector bytes;
|
bytes::vector bytes;
|
||||||
|
|
||||||
|
int offset = 0;
|
||||||
};
|
};
|
||||||
struct File {
|
struct File {
|
||||||
uint64 id = 0;
|
uint64 id = 0;
|
||||||
|
@ -118,7 +122,9 @@ private:
|
||||||
int32 size = 0;
|
int32 size = 0;
|
||||||
int32 dcId = 0;
|
int32 dcId = 0;
|
||||||
bytes::vector fileHash;
|
bytes::vector fileHash;
|
||||||
bytes::vector bytes;
|
|
||||||
|
int downloadOffset = 0;
|
||||||
|
QImage image;
|
||||||
};
|
};
|
||||||
struct EditFile {
|
struct EditFile {
|
||||||
EditFile(
|
EditFile(
|
||||||
|
@ -127,6 +133,7 @@ private:
|
||||||
|
|
||||||
File fields;
|
File fields;
|
||||||
std::unique_ptr<UploadedScan> uploaded;
|
std::unique_ptr<UploadedScan> uploaded;
|
||||||
|
bool deleted = false;
|
||||||
};
|
};
|
||||||
struct Verification {
|
struct Verification {
|
||||||
TimeId date;
|
TimeId date;
|
||||||
|
@ -170,6 +177,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
EditFile *findEditFile(const FullMsgId &fullId);
|
EditFile *findEditFile(const FullMsgId &fullId);
|
||||||
|
EditFile *findEditFile(const FileKey &key);
|
||||||
std::pair<Field*, File*> findFile(const FileKey &key);
|
std::pair<Field*, File*> findFile(const FileKey &key);
|
||||||
|
|
||||||
void requestForm();
|
void requestForm();
|
||||||
|
@ -190,6 +198,9 @@ private:
|
||||||
const QByteArray &value,
|
const QByteArray &value,
|
||||||
const DataType &data) const;
|
const DataType &data) const;
|
||||||
Verification parseVerified(const MTPSecureValueVerified &data) const;
|
Verification parseVerified(const MTPSecureValueVerified &data) const;
|
||||||
|
std::vector<File> parseFiles(
|
||||||
|
const QVector<MTPSecureFile> &data,
|
||||||
|
const std::vector<EditFile> &editData = {}) const;
|
||||||
|
|
||||||
void passwordDone(const MTPaccount_Password &result);
|
void passwordDone(const MTPaccount_Password &result);
|
||||||
void passwordFail(const RPCError &error);
|
void passwordFail(const RPCError &error);
|
||||||
|
@ -209,8 +220,10 @@ private:
|
||||||
std::vector<ScanInfo> fieldFilesIdentity(const Field &field) const;
|
std::vector<ScanInfo> fieldFilesIdentity(const Field &field) const;
|
||||||
void saveIdentity(int index);
|
void saveIdentity(int index);
|
||||||
|
|
||||||
void loadFiles(const std::vector<File> &files);
|
void loadFiles(std::vector<File> &files);
|
||||||
void fileLoaded(FileKey key, const QByteArray &bytes);
|
void fileLoadDone(FileKey key, const QByteArray &bytes);
|
||||||
|
void fileLoadProgress(FileKey key, int offset);
|
||||||
|
void fileLoadFail(FileKey key);
|
||||||
void generateSecret(bytes::const_span password);
|
void generateSecret(bytes::const_span password);
|
||||||
|
|
||||||
template <typename FileHashes>
|
template <typename FileHashes>
|
||||||
|
@ -219,8 +232,15 @@ private:
|
||||||
bytes::const_span valueHash);
|
bytes::const_span valueHash);
|
||||||
|
|
||||||
void subscribeToUploader();
|
void subscribeToUploader();
|
||||||
void uploadEncryptedScan(int index, UploadedScan &&data);
|
void encryptScan(
|
||||||
void scanUploaded(const Storage::UploadedSecure &data);
|
int fieldIndex,
|
||||||
|
int fileIndex,
|
||||||
|
QByteArray &&content);
|
||||||
|
void uploadEncryptedScan(int fieldIndex, int fileIndex, UploadedScan &&data);
|
||||||
|
void scanUploadDone(const Storage::UploadSecureDone &data);
|
||||||
|
void scanUploadProgress(const Storage::UploadSecureProgress &data);
|
||||||
|
void scanUploadFail(const FullMsgId &fullId);
|
||||||
|
ScanInfo collectScanInfo(const EditFile &file) const;
|
||||||
|
|
||||||
not_null<Window::Controller*> _controller;
|
not_null<Window::Controller*> _controller;
|
||||||
FormRequest _request;
|
FormRequest _request;
|
||||||
|
|
|
@ -29,8 +29,8 @@ struct Uploader::File {
|
||||||
|
|
||||||
std::shared_ptr<FileLoadResult> file;
|
std::shared_ptr<FileLoadResult> file;
|
||||||
SendMediaReady media;
|
SendMediaReady media;
|
||||||
int32 partsCount;
|
int32 partsCount = 0;
|
||||||
mutable int32 fileSentSize;
|
mutable int32 fileSentSize = 0;
|
||||||
|
|
||||||
uint64 id() const;
|
uint64 id() const;
|
||||||
SendMediaType type() const;
|
SendMediaType type() const;
|
||||||
|
@ -483,7 +483,11 @@ void Uploader::partLoaded(const MTPBool &result, mtpRequestId requestId) {
|
||||||
}
|
}
|
||||||
_documentProgress.fire_copy(fullId);
|
_documentProgress.fire_copy(fullId);
|
||||||
} else if (file.type() == SendMediaType::Secure) {
|
} else if (file.type() == SendMediaType::Secure) {
|
||||||
_secureProgress.fire_copy(fullId);
|
file.fileSentSize += sentPartSize;
|
||||||
|
_secureProgress.fire_copy({
|
||||||
|
fullId,
|
||||||
|
file.fileSentSize,
|
||||||
|
file.file->partssize });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,13 @@ struct UploadedThumbDocument {
|
||||||
MTPInputFile thumb;
|
MTPInputFile thumb;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UploadedSecure {
|
struct UploadSecureProgress {
|
||||||
|
FullMsgId fullId;
|
||||||
|
int offset = 0;
|
||||||
|
int size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UploadSecureDone {
|
||||||
FullMsgId fullId;
|
FullMsgId fullId;
|
||||||
uint64 fileId = 0;
|
uint64 fileId = 0;
|
||||||
int partsCount = 0;
|
int partsCount = 0;
|
||||||
|
@ -65,7 +71,7 @@ public:
|
||||||
rpl::producer<UploadedThumbDocument> thumbDocumentReady() const {
|
rpl::producer<UploadedThumbDocument> thumbDocumentReady() const {
|
||||||
return _thumbDocumentReady.events();
|
return _thumbDocumentReady.events();
|
||||||
}
|
}
|
||||||
rpl::producer<UploadedSecure> secureReady() const {
|
rpl::producer<UploadSecureDone> secureReady() const {
|
||||||
return _secureReady.events();
|
return _secureReady.events();
|
||||||
}
|
}
|
||||||
rpl::producer<FullMsgId> photoProgress() const {
|
rpl::producer<FullMsgId> photoProgress() const {
|
||||||
|
@ -74,7 +80,7 @@ public:
|
||||||
rpl::producer<FullMsgId> documentProgress() const {
|
rpl::producer<FullMsgId> documentProgress() const {
|
||||||
return _documentProgress.events();
|
return _documentProgress.events();
|
||||||
}
|
}
|
||||||
rpl::producer<FullMsgId> secureProgress() const {
|
rpl::producer<UploadSecureProgress> secureProgress() const {
|
||||||
return _secureProgress.events();
|
return _secureProgress.events();
|
||||||
}
|
}
|
||||||
rpl::producer<FullMsgId> photoFailed() const {
|
rpl::producer<FullMsgId> photoFailed() const {
|
||||||
|
@ -117,10 +123,10 @@ private:
|
||||||
rpl::event_stream<UploadedPhoto> _photoReady;
|
rpl::event_stream<UploadedPhoto> _photoReady;
|
||||||
rpl::event_stream<UploadedDocument> _documentReady;
|
rpl::event_stream<UploadedDocument> _documentReady;
|
||||||
rpl::event_stream<UploadedThumbDocument> _thumbDocumentReady;
|
rpl::event_stream<UploadedThumbDocument> _thumbDocumentReady;
|
||||||
rpl::event_stream<UploadedSecure> _secureReady;
|
rpl::event_stream<UploadSecureDone> _secureReady;
|
||||||
rpl::event_stream<FullMsgId> _photoProgress;
|
rpl::event_stream<FullMsgId> _photoProgress;
|
||||||
rpl::event_stream<FullMsgId> _documentProgress;
|
rpl::event_stream<FullMsgId> _documentProgress;
|
||||||
rpl::event_stream<FullMsgId> _secureProgress;
|
rpl::event_stream<UploadSecureProgress> _secureProgress;
|
||||||
rpl::event_stream<FullMsgId> _photoFailed;
|
rpl::event_stream<FullMsgId> _photoFailed;
|
||||||
rpl::event_stream<FullMsgId> _documentFailed;
|
rpl::event_stream<FullMsgId> _documentFailed;
|
||||||
rpl::event_stream<FullMsgId> _secureFailed;
|
rpl::event_stream<FullMsgId> _secureFailed;
|
||||||
|
|
Loading…
Add table
Reference in a new issue