mirror of https://github.com/procxx/kepka.git
Add simple files and contacts export.
Also move all API calls in export to Export::ApiWrap.
This commit is contained in:
parent
0a1a5ed70e
commit
cec8114b99
|
@ -160,12 +160,17 @@ class optional : public optional_variant<Type> {
|
||||||
public:
|
public:
|
||||||
using parent::parent;
|
using parent::parent;
|
||||||
|
|
||||||
Type &operator*() {
|
Type &operator*() & {
|
||||||
Expects(parent::template is<Type>());
|
Expects(parent::template is<Type>());
|
||||||
|
|
||||||
return parent::template get_unchecked<Type>();
|
return parent::template get_unchecked<Type>();
|
||||||
}
|
}
|
||||||
const Type &operator*() const {
|
Type &&operator*() && {
|
||||||
|
Expects(parent::template is<Type>());
|
||||||
|
|
||||||
|
return std::move(parent::template get_unchecked<Type>());
|
||||||
|
}
|
||||||
|
const Type &operator*() const & {
|
||||||
Expects(parent::template is<Type>());
|
Expects(parent::template is<Type>());
|
||||||
|
|
||||||
return parent::template get_unchecked<Type>();
|
return parent::template get_unchecked<Type>();
|
||||||
|
|
|
@ -18,53 +18,183 @@ Utf8String ParseString(const MTPstring &data) {
|
||||||
return data.v;
|
return data.v;
|
||||||
}
|
}
|
||||||
|
|
||||||
PersonalInfo ParsePersonalInfo(const MTPUserFull &data) {
|
FileLocation ParseLocation(const MTPFileLocation &data) {
|
||||||
Expects(data.type() == mtpc_userFull);
|
switch (data.type()) {
|
||||||
|
case mtpc_fileLocation: {
|
||||||
|
const auto &location = data.c_fileLocation();
|
||||||
|
return {
|
||||||
|
location.vdc_id.v,
|
||||||
|
MTP_inputFileLocation(
|
||||||
|
location.vvolume_id,
|
||||||
|
location.vlocal_id,
|
||||||
|
location.vsecret)
|
||||||
|
};
|
||||||
|
} break;
|
||||||
|
case mtpc_fileLocationUnavailable: {
|
||||||
|
const auto &location = data.c_fileLocationUnavailable();
|
||||||
|
return {
|
||||||
|
0,
|
||||||
|
MTP_inputFileLocation(
|
||||||
|
location.vvolume_id,
|
||||||
|
location.vlocal_id,
|
||||||
|
location.vsecret)
|
||||||
|
};
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
Unexpected("Type in ParseLocation.");
|
||||||
|
}
|
||||||
|
|
||||||
const auto &fields = data.c_userFull();
|
File ParseMaxImage(
|
||||||
const auto &small = fields.vuser.c_user();
|
const MTPVector<MTPPhotoSize> &data,
|
||||||
auto result = PersonalInfo();
|
const QString &suggestedPath) {
|
||||||
if (small.has_first_name()) {
|
auto result = File();
|
||||||
result.firstName = ParseString(small.vfirst_name);
|
result.suggestedPath = suggestedPath;
|
||||||
}
|
|
||||||
if (small.has_last_name()) {
|
auto maxArea = int64(0);
|
||||||
result.lastName = ParseString(small.vlast_name);
|
for (const auto &size : data.v) {
|
||||||
}
|
switch (size.type()) {
|
||||||
if (small.has_phone()) {
|
case mtpc_photoSize: {
|
||||||
result.phoneNumber = ParseString(small.vphone);
|
const auto &fields = size.c_photoSize();
|
||||||
}
|
const auto area = fields.vw.v * int64(fields.vh.v);
|
||||||
if (small.has_username()) {
|
if (area > maxArea) {
|
||||||
result.username = ParseString(small.vusername);
|
result.location = ParseLocation(fields.vlocation);
|
||||||
}
|
result.size = fields.vsize.v;
|
||||||
if (fields.has_about()) {
|
result.content = QByteArray();
|
||||||
result.bio = ParseString(fields.vabout);
|
maxArea = area;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case mtpc_photoCachedSize: {
|
||||||
|
const auto &fields = size.c_photoCachedSize();
|
||||||
|
const auto area = fields.vw.v * int64(fields.vh.v);
|
||||||
|
if (area > maxArea) {
|
||||||
|
result.location = ParseLocation(fields.vlocation);
|
||||||
|
result.size = fields.vbytes.v.size();
|
||||||
|
result.content = fields.vbytes.v;
|
||||||
|
maxArea = area;
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Photo ParsePhoto(const MTPPhoto &data, const QString &suggestedPath) {
|
||||||
|
auto result = Photo();
|
||||||
|
switch (data.type()) {
|
||||||
|
case mtpc_photo: {
|
||||||
|
const auto &photo = data.c_photo();
|
||||||
|
result.id = photo.vid.v;
|
||||||
|
result.date = QDateTime::fromTime_t(photo.vdate.v);
|
||||||
|
result.image = ParseMaxImage(photo.vsizes, suggestedPath);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case mtpc_photoEmpty: {
|
||||||
|
const auto &photo = data.c_photoEmpty();
|
||||||
|
result.id = photo.vid.v;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: Unexpected("Photo type in ParsePhoto.");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Utf8String FormatDateTime(
|
||||||
|
const int32 date,
|
||||||
|
QChar dateSeparator,
|
||||||
|
QChar timeSeparator,
|
||||||
|
QChar separator) {
|
||||||
|
const auto value = QDateTime::fromTime_t(date);
|
||||||
|
return (QString("%1") + dateSeparator + "%2" + dateSeparator + "%3"
|
||||||
|
+ separator + "%4" + timeSeparator + "%5" + timeSeparator + "%6"
|
||||||
|
).arg(value.date().year()
|
||||||
|
).arg(value.date().month(), 2, 10, QChar('0')
|
||||||
|
).arg(value.date().day(), 2, 10, QChar('0')
|
||||||
|
).arg(value.time().hour(), 2, 10, QChar('0')
|
||||||
|
).arg(value.time().minute(), 2, 10, QChar('0')
|
||||||
|
).arg(value.time().second(), 2, 10, QChar('0')
|
||||||
|
).toUtf8();
|
||||||
|
}
|
||||||
|
|
||||||
UserpicsSlice ParseUserpicsSlice(const MTPVector<MTPPhoto> &data) {
|
UserpicsSlice ParseUserpicsSlice(const MTPVector<MTPPhoto> &data) {
|
||||||
const auto &list = data.v;
|
const auto &list = data.v;
|
||||||
auto result = UserpicsSlice();
|
auto result = UserpicsSlice();
|
||||||
result.list.reserve(list.size());
|
result.list.reserve(list.size());
|
||||||
for (const auto &photo : list) {
|
for (const auto &photo : list) {
|
||||||
switch (photo.type()) {
|
const auto suggestedPath = "PersonalPhotos/Photo_"
|
||||||
case mtpc_photo: {
|
+ (photo.type() == mtpc_photo
|
||||||
const auto &fields = photo.c_photo();
|
? QString::fromUtf8(
|
||||||
auto userpic = Userpic();
|
FormatDateTime(photo.c_photo().vdate.v, '_', '_', '_'))
|
||||||
userpic.id = fields.vid.v;
|
: "Empty")
|
||||||
userpic.date = QDateTime::fromTime_t(fields.vdate.v);
|
+ ".jpg";
|
||||||
userpic.image = File{ "(not saved)" };
|
result.list.push_back(ParsePhoto(photo, suggestedPath));
|
||||||
result.list.push_back(std::move(userpic));
|
}
|
||||||
} break;
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
case mtpc_photoEmpty: {
|
User ParseUser(const MTPUser &data) {
|
||||||
const auto &fields = photo.c_photoEmpty();
|
auto result = User();
|
||||||
auto userpic = Userpic();
|
switch (data.type()) {
|
||||||
userpic.id = fields.vid.v;
|
case mtpc_user: {
|
||||||
result.list.push_back(std::move(userpic));
|
const auto &fields = data.c_user();
|
||||||
} break;
|
result.id = fields.vid.v;
|
||||||
|
if (fields.has_first_name()) {
|
||||||
|
result.firstName = ParseString(fields.vfirst_name);
|
||||||
|
}
|
||||||
|
if (fields.has_last_name()) {
|
||||||
|
result.lastName = ParseString(fields.vlast_name);
|
||||||
|
}
|
||||||
|
if (fields.has_phone()) {
|
||||||
|
result.phoneNumber = ParseString(fields.vphone);
|
||||||
|
}
|
||||||
|
if (fields.has_username()) {
|
||||||
|
result.username = ParseString(fields.vusername);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
default: Unexpected("Photo type in ParseUserpicsSlice.");
|
case mtpc_userEmpty: {
|
||||||
|
const auto &fields = data.c_userEmpty();
|
||||||
|
result.id = fields.vid.v;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: Unexpected("Type in ParseUser.");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<int, User> ParseUsersList(const MTPVector<MTPUser> &data) {
|
||||||
|
auto result = std::map<int, User>();
|
||||||
|
for (const auto &user : data.v) {
|
||||||
|
auto parsed = ParseUser(user);
|
||||||
|
result.emplace(parsed.id, std::move(parsed));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
PersonalInfo ParsePersonalInfo(const MTPUserFull &data) {
|
||||||
|
Expects(data.type() == mtpc_userFull);
|
||||||
|
|
||||||
|
const auto &fields = data.c_userFull();
|
||||||
|
auto result = PersonalInfo();
|
||||||
|
result.user = ParseUser(fields.vuser);
|
||||||
|
if (fields.has_about()) {
|
||||||
|
result.bio = ParseString(fields.vabout);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactsList ParseContactsList(const MTPcontacts_Contacts &data) {
|
||||||
|
Expects(data.type() == mtpc_contacts_contacts);
|
||||||
|
|
||||||
|
auto result = ContactsList();
|
||||||
|
const auto &contacts = data.c_contacts_contacts();
|
||||||
|
const auto map = ParseUsersList(contacts.vusers);
|
||||||
|
for (const auto &contact : contacts.vcontacts.v) {
|
||||||
|
const auto userId = contact.c_contact().vuser_id.v;
|
||||||
|
if (const auto i = map.find(userId); i != end(map)) {
|
||||||
|
result.list.push_back(i->second);
|
||||||
|
} else {
|
||||||
|
result.list.push_back(User());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "scheme.h"
|
#include "scheme.h"
|
||||||
|
#include "base/optional.h"
|
||||||
|
|
||||||
#include <QtCore/QDateTime>
|
#include <QtCore/QDateTime>
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
|
@ -28,46 +29,64 @@ inline auto NumberToString(Type value)
|
||||||
return QByteArray(result.data(), int(result.size()));
|
return QByteArray(result.data(), int(result.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PersonalInfo {
|
struct UserpicsInfo {
|
||||||
|
int count = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FileLocation {
|
||||||
|
int dcId = 0;
|
||||||
|
MTPInputFileLocation data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct File {
|
||||||
|
FileLocation location;
|
||||||
|
int size = 0;
|
||||||
|
QByteArray content;
|
||||||
|
|
||||||
|
QString suggestedPath;
|
||||||
|
|
||||||
|
QString relativePath;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Photo {
|
||||||
|
uint64 id = 0;
|
||||||
|
QDateTime date;
|
||||||
|
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
File image;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UserpicsSlice {
|
||||||
|
std::vector<Photo> list;
|
||||||
|
};
|
||||||
|
|
||||||
|
UserpicsSlice ParseUserpicsSlice(const MTPVector<MTPPhoto> &data);
|
||||||
|
|
||||||
|
struct User {
|
||||||
|
int id = 0;
|
||||||
Utf8String firstName;
|
Utf8String firstName;
|
||||||
Utf8String lastName;
|
Utf8String lastName;
|
||||||
Utf8String phoneNumber;
|
Utf8String phoneNumber;
|
||||||
Utf8String username;
|
Utf8String username;
|
||||||
|
};
|
||||||
|
|
||||||
|
User ParseUser(const MTPUser &user);
|
||||||
|
std::map<int, User> ParseUsersList(const MTPVector<MTPUser> &data);
|
||||||
|
|
||||||
|
struct PersonalInfo {
|
||||||
|
User user;
|
||||||
Utf8String bio;
|
Utf8String bio;
|
||||||
};
|
};
|
||||||
|
|
||||||
PersonalInfo ParsePersonalInfo(const MTPUserFull &data);
|
PersonalInfo ParsePersonalInfo(const MTPUserFull &data);
|
||||||
|
|
||||||
struct UserpicsInfo {
|
|
||||||
int count = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct File {
|
|
||||||
QString relativePath;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Userpic {
|
|
||||||
uint64 id = 0;
|
|
||||||
QDateTime date;
|
|
||||||
File image;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct UserpicsSlice {
|
|
||||||
std::vector<Userpic> list;
|
|
||||||
};
|
|
||||||
|
|
||||||
UserpicsSlice ParseUserpicsSlice(const MTPVector<MTPPhoto> &data);
|
|
||||||
|
|
||||||
struct Contact {
|
|
||||||
Utf8String firstName;
|
|
||||||
Utf8String lastName;
|
|
||||||
Utf8String phoneNumber;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ContactsList {
|
struct ContactsList {
|
||||||
std::vector<Contact> list;
|
std::vector<User> list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ContactsList ParseContactsList(const MTPcontacts_Contacts &data);
|
||||||
|
|
||||||
struct Session {
|
struct Session {
|
||||||
Utf8String platform;
|
Utf8String platform;
|
||||||
Utf8String deviceModel;
|
Utf8String deviceModel;
|
||||||
|
@ -108,6 +127,11 @@ struct MessagesSlice {
|
||||||
};
|
};
|
||||||
|
|
||||||
Utf8String FormatPhoneNumber(const Utf8String &phoneNumber);
|
Utf8String FormatPhoneNumber(const Utf8String &phoneNumber);
|
||||||
|
Utf8String FormatDateTime(
|
||||||
|
const int32 date,
|
||||||
|
QChar dateSeparator = QChar('.'),
|
||||||
|
QChar timeSeparator = QChar(':'),
|
||||||
|
QChar separator = QChar(' '));
|
||||||
|
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
} // namespace Export
|
} // namespace Export
|
||||||
|
|
|
@ -0,0 +1,364 @@
|
||||||
|
/*
|
||||||
|
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 "export/export_api_wrap.h"
|
||||||
|
|
||||||
|
#include "export/data/export_data_types.h"
|
||||||
|
#include "export/output/export_output_file.h"
|
||||||
|
#include "mtproto/rpc_sender.h"
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
namespace Export {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kUserpicsSliceLimit = 2;
|
||||||
|
constexpr auto kFileChunkSize = 128 * 1024;
|
||||||
|
constexpr auto kFileRequestsCount = 2;
|
||||||
|
constexpr auto kFileNextRequestDelay = TimeMs(20);
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
struct ApiWrap::UserpicsProcess {
|
||||||
|
FnMut<void(Data::UserpicsInfo&&)> start;
|
||||||
|
Fn<void(Data::UserpicsSlice&&)> handleSlice;
|
||||||
|
FnMut<void()> finish;
|
||||||
|
|
||||||
|
base::optional<Data::UserpicsSlice> slice;
|
||||||
|
bool lastSlice = false;
|
||||||
|
int loading = -1;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ApiWrap::FileProcess {
|
||||||
|
FileProcess(const QString &path);
|
||||||
|
|
||||||
|
Output::File file;
|
||||||
|
QString relativePath;
|
||||||
|
|
||||||
|
FnMut<void(const QString &relativePath)> done;
|
||||||
|
|
||||||
|
Data::FileLocation location;
|
||||||
|
int offset = 0;
|
||||||
|
int size = 0;
|
||||||
|
|
||||||
|
struct Request {
|
||||||
|
int offset = 0;
|
||||||
|
QByteArray bytes;
|
||||||
|
};
|
||||||
|
std::deque<Request> requests;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
ApiWrap::FileProcess::FileProcess(const QString &path) : file(path) {
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Request>
|
||||||
|
auto ApiWrap::mainRequest(Request &&request) {
|
||||||
|
return std::move(_mtp.request(
|
||||||
|
std::move(request)
|
||||||
|
).fail([=](RPCError &&result) {
|
||||||
|
error(std::move(result));
|
||||||
|
}).toDC(MTP::ShiftDcId(0, MTP::kExportDcShift)));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ApiWrap::fileRequest(const Data::FileLocation &location, int offset) {
|
||||||
|
Expects(location.dcId != 0);
|
||||||
|
|
||||||
|
return std::move(_mtp.request(MTPupload_GetFile(
|
||||||
|
location.data,
|
||||||
|
MTP_int(offset),
|
||||||
|
MTP_int(kFileChunkSize)
|
||||||
|
)).fail([=](RPCError &&result) {
|
||||||
|
error(std::move(result));
|
||||||
|
}).toDC(MTP::ShiftDcId(location.dcId, MTP::kExportDcShift)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiWrap::ApiWrap(Fn<void(FnMut<void()>)> runner)
|
||||||
|
: _mtp(std::move(runner)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::setFilesBaseFolder(const QString &folder) {
|
||||||
|
Expects(folder.endsWith('/'));
|
||||||
|
|
||||||
|
_filesFolder = folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<RPCError> ApiWrap::errors() const {
|
||||||
|
return _errors.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::requestPersonalInfo(FnMut<void(Data::PersonalInfo&&)> done) {
|
||||||
|
mainRequest(MTPusers_GetFullUser(
|
||||||
|
_user
|
||||||
|
)).done([=, done = std::move(done)](const MTPUserFull &result) mutable {
|
||||||
|
Expects(result.type() == mtpc_userFull);
|
||||||
|
|
||||||
|
const auto &full = result.c_userFull();
|
||||||
|
if (full.vuser.type() == mtpc_user) {
|
||||||
|
done(Data::ParsePersonalInfo(result));
|
||||||
|
} else {
|
||||||
|
error("Bad user type.");
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::requestUserpics(
|
||||||
|
FnMut<void(Data::UserpicsInfo&&)> start,
|
||||||
|
Fn<void(Data::UserpicsSlice&&)> slice,
|
||||||
|
FnMut<void()> finish) {
|
||||||
|
_userpicsProcess = std::make_unique<UserpicsProcess>();
|
||||||
|
_userpicsProcess->start = std::move(start);
|
||||||
|
_userpicsProcess->handleSlice = std::move(slice);
|
||||||
|
_userpicsProcess->finish = std::move(finish);
|
||||||
|
|
||||||
|
mainRequest(MTPphotos_GetUserPhotos(
|
||||||
|
_user,
|
||||||
|
MTP_int(0),
|
||||||
|
MTP_long(0),
|
||||||
|
MTP_int(kUserpicsSliceLimit)
|
||||||
|
)).done([=](const MTPphotos_Photos &result) mutable {
|
||||||
|
Expects(_userpicsProcess != nullptr);
|
||||||
|
|
||||||
|
_userpicsProcess->start([&] {
|
||||||
|
auto info = Data::UserpicsInfo();
|
||||||
|
switch (result.type()) {
|
||||||
|
case mtpc_photos_photos: {
|
||||||
|
const auto &data = result.c_photos_photos();
|
||||||
|
info.count = data.vphotos.v.size();
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case mtpc_photos_photosSlice: {
|
||||||
|
const auto &data = result.c_photos_photosSlice();
|
||||||
|
info.count = data.vcount.v;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: Unexpected("Photos type in Controller::exportUserpics.");
|
||||||
|
}
|
||||||
|
return info;
|
||||||
|
}());
|
||||||
|
|
||||||
|
handleUserpicsSlice(result);
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::handleUserpicsSlice(const MTPphotos_Photos &result) {
|
||||||
|
Expects(_userpicsProcess != nullptr);
|
||||||
|
|
||||||
|
switch (result.type()) {
|
||||||
|
case mtpc_photos_photos: {
|
||||||
|
const auto &data = result.c_photos_photos();
|
||||||
|
_userpicsProcess->lastSlice = true;
|
||||||
|
loadUserpicsFiles(Data::ParseUserpicsSlice(data.vphotos));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case mtpc_photos_photosSlice: {
|
||||||
|
const auto &data = result.c_photos_photosSlice();
|
||||||
|
loadUserpicsFiles(Data::ParseUserpicsSlice(data.vphotos));
|
||||||
|
} break;
|
||||||
|
|
||||||
|
default: Unexpected("Photos type in Controller::exportUserpicsSlice.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::loadUserpicsFiles(Data::UserpicsSlice &&slice) {
|
||||||
|
Expects(_userpicsProcess != nullptr);
|
||||||
|
Expects(!_userpicsProcess->slice.has_value());
|
||||||
|
|
||||||
|
if (slice.list.empty()) {
|
||||||
|
_userpicsProcess->lastSlice = true;
|
||||||
|
}
|
||||||
|
_userpicsProcess->slice = std::move(slice);
|
||||||
|
_userpicsProcess->loading = -1;
|
||||||
|
loadNextUserpic();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::loadNextUserpic() {
|
||||||
|
Expects(_userpicsProcess != nullptr);
|
||||||
|
Expects(_userpicsProcess->slice.has_value());
|
||||||
|
|
||||||
|
const auto &list = _userpicsProcess->slice->list;
|
||||||
|
++_userpicsProcess->loading;
|
||||||
|
if (_userpicsProcess->loading < list.size()) {
|
||||||
|
loadFile(
|
||||||
|
list[_userpicsProcess->loading].image,
|
||||||
|
[=](const QString &path) { loadUserpicDone(path); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto lastUserpicId = list.empty()
|
||||||
|
? base::none
|
||||||
|
: base::make_optional(list.back().id);
|
||||||
|
|
||||||
|
_userpicsProcess->handleSlice(*base::take(_userpicsProcess->slice));
|
||||||
|
|
||||||
|
if (_userpicsProcess->lastSlice) {
|
||||||
|
finishUserpics();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(lastUserpicId.has_value());
|
||||||
|
mainRequest(MTPphotos_GetUserPhotos(
|
||||||
|
_user,
|
||||||
|
MTP_int(0),
|
||||||
|
MTP_long(*lastUserpicId),
|
||||||
|
MTP_int(kUserpicsSliceLimit)
|
||||||
|
)).done([=](const MTPphotos_Photos &result) {
|
||||||
|
handleUserpicsSlice(result);
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::loadUserpicDone(const QString &relativePath) {
|
||||||
|
Expects(_userpicsProcess != nullptr);
|
||||||
|
Expects(_userpicsProcess->slice.has_value());
|
||||||
|
Expects((_userpicsProcess->loading >= 0)
|
||||||
|
&& (_userpicsProcess->loading
|
||||||
|
< _userpicsProcess->slice->list.size()));
|
||||||
|
|
||||||
|
const auto index = _userpicsProcess->loading;
|
||||||
|
_userpicsProcess->slice->list[index].image.relativePath = relativePath;
|
||||||
|
loadNextUserpic();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::finishUserpics() {
|
||||||
|
Expects(_userpicsProcess != nullptr);
|
||||||
|
|
||||||
|
base::take(_userpicsProcess)->finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::requestContacts(FnMut<void(Data::ContactsList&&)> done) {
|
||||||
|
const auto hash = 0;
|
||||||
|
mainRequest(MTPcontacts_GetContacts(
|
||||||
|
MTP_int(hash)
|
||||||
|
)).done([=, done = std::move(done)](
|
||||||
|
const MTPcontacts_Contacts &result) mutable {
|
||||||
|
if (result.type() == mtpc_contacts_contacts) {
|
||||||
|
done(Data::ParseContactsList(result));
|
||||||
|
} else {
|
||||||
|
error("Bad contacts type.");
|
||||||
|
}
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::loadFile(const Data::File &file, FnMut<void(QString)> done) {
|
||||||
|
Expects(_fileProcess == nullptr);
|
||||||
|
|
||||||
|
if (!file.relativePath.isEmpty()) {
|
||||||
|
done(file.relativePath);
|
||||||
|
}
|
||||||
|
using namespace Output;
|
||||||
|
const auto relativePath = File::PrepareRelativePath(
|
||||||
|
_filesFolder,
|
||||||
|
file.suggestedPath);
|
||||||
|
_fileProcess = std::make_unique<FileProcess>(
|
||||||
|
_filesFolder + relativePath);
|
||||||
|
_fileProcess->relativePath = relativePath;
|
||||||
|
_fileProcess->location = file.location;
|
||||||
|
_fileProcess->done = std::move(done);
|
||||||
|
|
||||||
|
if (!file.content.isEmpty()) {
|
||||||
|
auto &output = _fileProcess->file;
|
||||||
|
if (output.writeBlock(file.content) == File::Result::Success) {
|
||||||
|
_fileProcess->done(relativePath);
|
||||||
|
} else {
|
||||||
|
error(QString("Could not open '%1'.").arg(relativePath));
|
||||||
|
}
|
||||||
|
} else if (!file.location.dcId) {
|
||||||
|
_fileProcess->done(QString());
|
||||||
|
} else {
|
||||||
|
loadFilePart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::loadFilePart() {
|
||||||
|
if (!_fileProcess
|
||||||
|
|| _fileProcess->requests.size() >= kFileRequestsCount
|
||||||
|
|| (_fileProcess->size > 0
|
||||||
|
&& _fileProcess->offset >= _fileProcess->size)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto offset = _fileProcess->offset;
|
||||||
|
_fileProcess->requests.push_back({ offset });
|
||||||
|
fileRequest(
|
||||||
|
_fileProcess->location,
|
||||||
|
_fileProcess->offset
|
||||||
|
).done([=](const MTPupload_File &result) {
|
||||||
|
filePartDone(offset, result);
|
||||||
|
}).send();
|
||||||
|
_fileProcess->offset += kFileChunkSize;
|
||||||
|
|
||||||
|
if (_fileProcess->size > 0
|
||||||
|
&& _fileProcess->requests.size() < kFileRequestsCount) {
|
||||||
|
//const auto runner = _runner;
|
||||||
|
//crl::on_main([=] {
|
||||||
|
// QTimer::singleShot(kFileNextRequestDelay, [=] {
|
||||||
|
// runner([=] {
|
||||||
|
// loadFilePart();
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
//});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::filePartDone(int offset, const MTPupload_File &result) {
|
||||||
|
Expects(_fileProcess != nullptr);
|
||||||
|
Expects(!_fileProcess->requests.empty());
|
||||||
|
|
||||||
|
if (result.type() == mtpc_upload_fileCdnRedirect) {
|
||||||
|
error("Cdn redirect is not supported.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto &data = result.c_upload_file();
|
||||||
|
if (data.vbytes.v.isEmpty()) {
|
||||||
|
if (_fileProcess->size > 0) {
|
||||||
|
error("Empty bytes received in file part.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
using Request = FileProcess::Request;
|
||||||
|
auto &requests = _fileProcess->requests;
|
||||||
|
const auto i = ranges::find(
|
||||||
|
requests,
|
||||||
|
offset,
|
||||||
|
[](const Request &request) { return request.offset; });
|
||||||
|
Assert(i != end(requests));
|
||||||
|
|
||||||
|
i->bytes = data.vbytes.v;
|
||||||
|
|
||||||
|
auto &file = _fileProcess->file;
|
||||||
|
while (!requests.empty() && !requests.front().bytes.isEmpty()) {
|
||||||
|
const auto &bytes = requests.front().bytes;
|
||||||
|
if (file.writeBlock(bytes) != Output::File::Result::Success) {
|
||||||
|
error(QString("Could not write bytes to '%1'."
|
||||||
|
).arg(_fileProcess->relativePath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
requests.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!requests.empty()
|
||||||
|
|| !_fileProcess->size
|
||||||
|
|| _fileProcess->size > _fileProcess->offset) {
|
||||||
|
loadFilePart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto process = base::take(_fileProcess);
|
||||||
|
process->done(process->relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::error(RPCError &&error) {
|
||||||
|
_errors.fire(std::move(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiWrap::error(const QString &text) {
|
||||||
|
error(MTP_rpc_error(MTP_int(0), MTP_string("API_ERROR: " + text)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiWrap::~ApiWrap() = default;
|
||||||
|
|
||||||
|
} // namespace Export
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "mtproto/concurrent_sender.h"
|
||||||
|
|
||||||
|
namespace Export {
|
||||||
|
namespace Data {
|
||||||
|
struct File;
|
||||||
|
struct FileLocation;
|
||||||
|
struct PersonalInfo;
|
||||||
|
struct UserpicsInfo;
|
||||||
|
struct UserpicsSlice;
|
||||||
|
struct ContactsList;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
|
class ApiWrap {
|
||||||
|
public:
|
||||||
|
ApiWrap(Fn<void(FnMut<void()>)> runner);
|
||||||
|
|
||||||
|
void setFilesBaseFolder(const QString &folder);
|
||||||
|
|
||||||
|
rpl::producer<RPCError> errors() const;
|
||||||
|
|
||||||
|
void requestPersonalInfo(FnMut<void(Data::PersonalInfo&&)> done);
|
||||||
|
|
||||||
|
void requestUserpics(
|
||||||
|
FnMut<void(Data::UserpicsInfo&&)> start,
|
||||||
|
Fn<void(Data::UserpicsSlice&&)> slice,
|
||||||
|
FnMut<void()> finish);
|
||||||
|
|
||||||
|
void requestContacts(FnMut<void(Data::ContactsList&&)> done);
|
||||||
|
|
||||||
|
~ApiWrap();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void handleUserpicsSlice(const MTPphotos_Photos &result);
|
||||||
|
void loadUserpicsFiles(Data::UserpicsSlice &&slice);
|
||||||
|
void loadNextUserpic();
|
||||||
|
void loadUserpicDone(const QString &relativePath);
|
||||||
|
void finishUserpics();
|
||||||
|
|
||||||
|
void loadFile(const Data::File &file, FnMut<void(QString)> done);
|
||||||
|
void loadFilePart();
|
||||||
|
void filePartDone(int offset, const MTPupload_File &result);
|
||||||
|
|
||||||
|
template <typename Request>
|
||||||
|
[[nodiscard]] auto mainRequest(Request &&request);
|
||||||
|
|
||||||
|
[[nodiscard]] auto fileRequest(
|
||||||
|
const Data::FileLocation &location,
|
||||||
|
int offset);
|
||||||
|
|
||||||
|
void error(RPCError &&error);
|
||||||
|
void error(const QString &text);
|
||||||
|
|
||||||
|
MTP::ConcurrentSender _mtp;
|
||||||
|
QString _filesFolder;
|
||||||
|
MTPInputUser _user = MTP_inputUserSelf();
|
||||||
|
|
||||||
|
struct UserpicsProcess;
|
||||||
|
std::unique_ptr<UserpicsProcess> _userpicsProcess;
|
||||||
|
|
||||||
|
struct FileProcess;
|
||||||
|
std::unique_ptr<FileProcess> _fileProcess;
|
||||||
|
|
||||||
|
rpl::event_stream<RPCError> _errors;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Export
|
|
@ -7,18 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "export/export_controller.h"
|
#include "export/export_controller.h"
|
||||||
|
|
||||||
|
#include "export/export_api_wrap.h"
|
||||||
#include "export/export_settings.h"
|
#include "export/export_settings.h"
|
||||||
#include "export/data/export_data_types.h"
|
#include "export/data/export_data_types.h"
|
||||||
#include "export/output/export_output_abstract.h"
|
#include "export/output/export_output_abstract.h"
|
||||||
#include "mtproto/rpc_sender.h"
|
|
||||||
#include "mtproto/concurrent_sender.h"
|
|
||||||
|
|
||||||
namespace Export {
|
namespace Export {
|
||||||
namespace {
|
|
||||||
|
|
||||||
constexpr auto kUserpicsSliceLimit = 100;
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
class Controller {
|
class Controller {
|
||||||
public:
|
public:
|
||||||
|
@ -40,8 +34,6 @@ private:
|
||||||
using Step = ProcessingState::Step;
|
using Step = ProcessingState::Step;
|
||||||
|
|
||||||
void setState(State &&state);
|
void setState(State &&state);
|
||||||
void apiError(const RPCError &error);
|
|
||||||
void apiError(const QString &error);
|
|
||||||
void ioError(const QString &path);
|
void ioError(const QString &path);
|
||||||
void setFinishedState();
|
void setFinishedState();
|
||||||
|
|
||||||
|
@ -56,11 +48,9 @@ private:
|
||||||
void exportSessions();
|
void exportSessions();
|
||||||
void exportChats();
|
void exportChats();
|
||||||
|
|
||||||
void exportUserpicsSlice(const MTPphotos_Photos &result);
|
|
||||||
|
|
||||||
bool normalizePath();
|
bool normalizePath();
|
||||||
|
|
||||||
MTP::ConcurrentSender _mtp;
|
ApiWrap _api;
|
||||||
Settings _settings;
|
Settings _settings;
|
||||||
|
|
||||||
// rpl::variable<State> fails to compile in MSVC :(
|
// rpl::variable<State> fails to compile in MSVC :(
|
||||||
|
@ -73,14 +63,23 @@ private:
|
||||||
std::vector<ProcessingState::Step> _steps;
|
std::vector<ProcessingState::Step> _steps;
|
||||||
int _stepIndex = -1;
|
int _stepIndex = -1;
|
||||||
|
|
||||||
MTPInputUser _user = MTP_inputUserSelf();
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Controller::Controller(crl::weak_on_queue<Controller> weak)
|
Controller::Controller(crl::weak_on_queue<Controller> weak)
|
||||||
: _mtp(weak)
|
: _api(weak.runner())
|
||||||
, _state(PasswordCheckState{}) {
|
, _state(PasswordCheckState{}) {
|
||||||
requestPasswordState();
|
_api.errors(
|
||||||
|
) | rpl::start_with_next([=](RPCError &&error) {
|
||||||
|
setState(ErrorState{ ErrorState::Type::API, std::move(error) });
|
||||||
|
}, _lifetime);
|
||||||
|
|
||||||
|
//requestPasswordState();
|
||||||
|
auto state = PasswordCheckState();
|
||||||
|
state.checked = false;
|
||||||
|
state.requesting = false;
|
||||||
|
setState(std::move(state));
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<State> Controller::state() const {
|
rpl::producer<State> Controller::state() const {
|
||||||
|
@ -99,14 +98,6 @@ void Controller::setState(State &&state) {
|
||||||
_stateChanges.fire_copy(_state);
|
_stateChanges.fire_copy(_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::apiError(const RPCError &error) {
|
|
||||||
setState(ErrorState{ ErrorState::Type::API, error });
|
|
||||||
}
|
|
||||||
|
|
||||||
void Controller::apiError(const QString &error) {
|
|
||||||
apiError(MTP_rpc_error(MTP_int(0), MTP_string("API_ERROR: " + error)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Controller::ioError(const QString &path) {
|
void Controller::ioError(const QString &path) {
|
||||||
setState(ErrorState{ ErrorState::Type::IO, base::none, path });
|
setState(ErrorState{ ErrorState::Type::IO, base::none, path });
|
||||||
}
|
}
|
||||||
|
@ -124,7 +115,7 @@ rpl::producer<PasswordUpdate> Controller::passwordUpdate() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::reloadPasswordState() {
|
void Controller::reloadPasswordState() {
|
||||||
_mtp.request(base::take(_passwordRequestId)).cancel();
|
//_mtp.request(base::take(_passwordRequestId)).cancel();
|
||||||
requestPasswordState();
|
requestPasswordState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,13 +123,13 @@ void Controller::requestPasswordState() {
|
||||||
if (_passwordRequestId) {
|
if (_passwordRequestId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_passwordRequestId = _mtp.request(MTPaccount_GetPassword(
|
//_passwordRequestId = _mtp.request(MTPaccount_GetPassword(
|
||||||
)).done([=](const MTPaccount_Password &result) {
|
//)).done([=](const MTPaccount_Password &result) {
|
||||||
_passwordRequestId = 0;
|
// _passwordRequestId = 0;
|
||||||
passwordStateDone(result);
|
// passwordStateDone(result);
|
||||||
}).fail([=](const RPCError &error) {
|
//}).fail([=](const RPCError &error) {
|
||||||
apiError(error);
|
// apiError(error);
|
||||||
}).send();
|
//}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::passwordStateDone(const MTPaccount_Password &result) {
|
void Controller::passwordStateDone(const MTPaccount_Password &result) {
|
||||||
|
@ -156,6 +147,9 @@ void Controller::cancelUnconfirmedPassword() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::startExport(const Settings &settings) {
|
void Controller::startExport(const Settings &settings) {
|
||||||
|
if (!_settings.path.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_settings = base::duplicate(settings);
|
_settings = base::duplicate(settings);
|
||||||
|
|
||||||
if (!normalizePath()) {
|
if (!normalizePath()) {
|
||||||
|
@ -163,6 +157,7 @@ void Controller::startExport(const Settings &settings) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_writer = Output::CreateWriter(_settings.format);
|
_writer = Output::CreateWriter(_settings.format);
|
||||||
|
_api.setFilesBaseFolder(_settings.path);
|
||||||
fillExportSteps();
|
fillExportSteps();
|
||||||
exportNext();
|
exportNext();
|
||||||
}
|
}
|
||||||
|
@ -182,7 +177,7 @@ bool Controller::normalizePath() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
const auto date = QDate::currentDate();
|
const auto date = QDate::currentDate();
|
||||||
const auto base = QString("DataExport.%1.%2.%3"
|
const auto base = QString("DataExport_%1_%2_%3"
|
||||||
).arg(date.day(), 2, 10, QChar('0')
|
).arg(date.day(), 2, 10, QChar('0')
|
||||||
).arg(date.month(), 2, 10, QChar('0')
|
).arg(date.month(), 2, 10, QChar('0')
|
||||||
).arg(date.year());
|
).arg(date.year());
|
||||||
|
@ -241,101 +236,28 @@ void Controller::exportNext() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::exportPersonalInfo() {
|
void Controller::exportPersonalInfo() {
|
||||||
if (!(_settings.types & Settings::Type::PersonalInfo)) {
|
_api.requestPersonalInfo([=](Data::PersonalInfo &&result) {
|
||||||
exportUserpics();
|
_writer->writePersonal(result);
|
||||||
return;
|
exportNext();
|
||||||
}
|
});
|
||||||
_mtp.request(MTPusers_GetFullUser(
|
|
||||||
_user
|
|
||||||
)).done([=](const MTPUserFull &result) {
|
|
||||||
Expects(result.type() == mtpc_userFull);
|
|
||||||
|
|
||||||
const auto &full = result.c_userFull();
|
|
||||||
if (full.vuser.type() == mtpc_user) {
|
|
||||||
_writer->writePersonal(Data::ParsePersonalInfo(result));
|
|
||||||
exportNext();
|
|
||||||
} else {
|
|
||||||
apiError("Bad user type.");
|
|
||||||
}
|
|
||||||
}).fail([=](const RPCError &error) {
|
|
||||||
apiError(error);
|
|
||||||
}).send();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::exportUserpics() {
|
void Controller::exportUserpics() {
|
||||||
_mtp.request(MTPphotos_GetUserPhotos(
|
_api.requestUserpics([=](Data::UserpicsInfo &&start) {
|
||||||
_user,
|
_writer->writeUserpicsStart(start);
|
||||||
MTP_int(0),
|
}, [=](Data::UserpicsSlice &&slice) {
|
||||||
MTP_long(0),
|
_writer->writeUserpicsSlice(slice);
|
||||||
MTP_int(kUserpicsSliceLimit)
|
}, [=] {
|
||||||
)).done([=](const MTPphotos_Photos &result) {
|
|
||||||
_writer->writeUserpicsStart([&] {
|
|
||||||
auto info = Data::UserpicsInfo();
|
|
||||||
switch (result.type()) {
|
|
||||||
case mtpc_photos_photos: {
|
|
||||||
const auto &data = result.c_photos_photos();
|
|
||||||
info.count = data.vphotos.v.size();
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case mtpc_photos_photosSlice: {
|
|
||||||
const auto &data = result.c_photos_photosSlice();
|
|
||||||
info.count = data.vcount.v;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
default: Unexpected("Photos type in Controller::exportUserpics.");
|
|
||||||
}
|
|
||||||
return info;
|
|
||||||
}());
|
|
||||||
|
|
||||||
exportUserpicsSlice(result);
|
|
||||||
}).fail([=](const RPCError &error) {
|
|
||||||
apiError(error);
|
|
||||||
}).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Controller::exportUserpicsSlice(const MTPphotos_Photos &result) {
|
|
||||||
const auto finish = [&] {
|
|
||||||
_writer->writeUserpicsEnd();
|
_writer->writeUserpicsEnd();
|
||||||
exportNext();
|
exportNext();
|
||||||
};
|
});
|
||||||
switch (result.type()) {
|
|
||||||
case mtpc_photos_photos: {
|
|
||||||
const auto &data = result.c_photos_photos();
|
|
||||||
|
|
||||||
_writer->writeUserpicsSlice(
|
|
||||||
Data::ParseUserpicsSlice(data.vphotos));
|
|
||||||
|
|
||||||
finish();
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case mtpc_photos_photosSlice: {
|
|
||||||
const auto &data = result.c_photos_photosSlice();
|
|
||||||
|
|
||||||
const auto slice = Data::ParseUserpicsSlice(data.vphotos);
|
|
||||||
_writer->writeUserpicsSlice(slice);
|
|
||||||
|
|
||||||
if (slice.list.empty()) {
|
|
||||||
finish();
|
|
||||||
} else {
|
|
||||||
_mtp.request(MTPphotos_GetUserPhotos(
|
|
||||||
_user,
|
|
||||||
MTP_int(0),
|
|
||||||
MTP_long(slice.list.back().id),
|
|
||||||
MTP_int(kUserpicsSliceLimit)
|
|
||||||
)).done([=](const MTPphotos_Photos &result) {
|
|
||||||
exportUserpicsSlice(result);
|
|
||||||
}).fail([=](const RPCError &error) {
|
|
||||||
apiError(error);
|
|
||||||
}).send();
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
default: Unexpected("Photos type in Controller::exportUserpicsSlice.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::exportContacts() {
|
void Controller::exportContacts() {
|
||||||
exportNext();
|
_api.requestContacts([=](Data::ContactsList &&result) {
|
||||||
|
_writer->writeContactsList(result);
|
||||||
|
exportNext();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Controller::exportSessions() {
|
void Controller::exportSessions() {
|
||||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <QtCore/QFile>
|
#include <QtCore/QFile>
|
||||||
|
#include <QtCore/QFileInfo>
|
||||||
#include <QtCore/QDir>
|
#include <QtCore/QDir>
|
||||||
#include <QtCore/QTextStream>
|
#include <QtCore/QTextStream>
|
||||||
#include <QtCore/QDateTime>
|
#include <QtCore/QDateTime>
|
||||||
|
@ -14,6 +15,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include <rpl/rpl.h>
|
#include <rpl/rpl.h>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
|
#include <range/v3/all.hpp>
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include "platform/win/windows_range_v3_helpers.h"
|
||||||
|
#endif // Q_OS_WIN
|
||||||
|
|
||||||
#include "scheme.h"
|
#include "scheme.h"
|
||||||
#include "logs.h"
|
#include "logs.h"
|
||||||
|
|
|
@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "export/output/export_output_file.h"
|
#include "export/output/export_output_file.h"
|
||||||
|
|
||||||
|
#include <QtCore/QFileInfo>
|
||||||
|
#include <QtCore/QDir>
|
||||||
#include <gsl/gsl_util>
|
#include <gsl/gsl_util>
|
||||||
|
|
||||||
namespace Export {
|
namespace Export {
|
||||||
|
@ -46,10 +48,45 @@ File::Result File::reopen() {
|
||||||
} else if (_offset > 0) {
|
} else if (_offset > 0) {
|
||||||
return Result::FatalError;
|
return Result::FatalError;
|
||||||
}
|
}
|
||||||
return _file->open(QIODevice::Append)
|
if (_file->open(QIODevice::Append)) {
|
||||||
|
return Result::Success;
|
||||||
|
}
|
||||||
|
const auto info = QFileInfo(_path);
|
||||||
|
const auto dir = info.absoluteDir();
|
||||||
|
return (!dir.exists()
|
||||||
|
&& dir.mkpath(dir.absolutePath())
|
||||||
|
&& _file->open(QIODevice::Append))
|
||||||
? Result::Success
|
? Result::Success
|
||||||
: Result::Error;
|
: Result::Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString File::PrepareRelativePath(
|
||||||
|
const QString &folder,
|
||||||
|
const QString &suggested) {
|
||||||
|
if (!QFile::exists(folder + suggested)) {
|
||||||
|
return suggested;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not lastIndexOf('.') so that "file.tar.xz" won't be messed up.
|
||||||
|
const auto position = suggested.indexOf('.');
|
||||||
|
const auto base = suggested.midRef(0, position);
|
||||||
|
const auto extension = (position >= 0)
|
||||||
|
? suggested.midRef(position)
|
||||||
|
: QStringRef();
|
||||||
|
const auto relativePart = [&](int attempt) {
|
||||||
|
auto result = QString(" (%1)").arg(attempt);
|
||||||
|
result.prepend(base);
|
||||||
|
result.append(extension);
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
auto attempt = 0;
|
||||||
|
while (true) {
|
||||||
|
const auto relativePath = relativePart(++attempt);
|
||||||
|
if (!QFile::exists(folder + relativePath)) {
|
||||||
|
return relativePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Output
|
} // namespace Output
|
||||||
} // namespace File
|
} // namespace File
|
||||||
|
|
|
@ -27,6 +27,10 @@ public:
|
||||||
};
|
};
|
||||||
Result writeBlock(const QByteArray &block);
|
Result writeBlock(const QByteArray &block);
|
||||||
|
|
||||||
|
static QString PrepareRelativePath(
|
||||||
|
const QString &folder,
|
||||||
|
const QString &suggested);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Result reopen();
|
Result reopen();
|
||||||
Result writeBlockAttempt(const QByteArray &block);
|
Result writeBlockAttempt(const QByteArray &block);
|
||||||
|
|
|
@ -35,6 +35,32 @@ void SerializeMultiline(
|
||||||
} while (newline > 0);
|
} while (newline > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray JoinList(
|
||||||
|
const QByteArray &separator,
|
||||||
|
const std::vector<QByteArray> &list) {
|
||||||
|
if (list.empty()) {
|
||||||
|
return QByteArray();
|
||||||
|
} else if (list.size() == 1) {
|
||||||
|
return list[0];
|
||||||
|
}
|
||||||
|
auto size = (list.size() - 1) * separator.size();
|
||||||
|
for (const auto &value : list) {
|
||||||
|
size += value.size();
|
||||||
|
}
|
||||||
|
auto result = QByteArray();
|
||||||
|
result.reserve(size);
|
||||||
|
auto counter = 0;
|
||||||
|
while (true) {
|
||||||
|
result.append(list[counter]);
|
||||||
|
if (++counter == list.size()) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
result.append(separator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray SerializeKeyValue(
|
QByteArray SerializeKeyValue(
|
||||||
std::vector<std::pair<QByteArray, QByteArray>> &&values) {
|
std::vector<std::pair<QByteArray, QByteArray>> &&values) {
|
||||||
auto result = QByteArray();
|
auto result = QByteArray();
|
||||||
|
@ -74,10 +100,10 @@ bool TextWriter::writePersonal(const Data::PersonalInfo &data) {
|
||||||
+ kLineBreak
|
+ kLineBreak
|
||||||
+ kLineBreak
|
+ kLineBreak
|
||||||
+ SerializeKeyValue({
|
+ SerializeKeyValue({
|
||||||
{ "First name", data.firstName },
|
{ "First name", data.user.firstName },
|
||||||
{ "Last name", data.lastName },
|
{ "Last name", data.user.lastName },
|
||||||
{ "Phone number", Data::FormatPhoneNumber(data.phoneNumber) },
|
{ "Phone number", Data::FormatPhoneNumber(data.user.phoneNumber) },
|
||||||
{ "Username", FormatUsername(data.username) },
|
{ "Username", FormatUsername(data.user.username) },
|
||||||
{ "Bio", data.bio },
|
{ "Bio", data.bio },
|
||||||
})
|
})
|
||||||
+ kLineBreak;
|
+ kLineBreak;
|
||||||
|
@ -101,8 +127,17 @@ bool TextWriter::writeUserpicsStart(const Data::UserpicsInfo &data) {
|
||||||
bool TextWriter::writeUserpicsSlice(const Data::UserpicsSlice &data) {
|
bool TextWriter::writeUserpicsSlice(const Data::UserpicsSlice &data) {
|
||||||
auto lines = QByteArray();
|
auto lines = QByteArray();
|
||||||
for (const auto &userpic : data.list) {
|
for (const auto &userpic : data.list) {
|
||||||
lines.append(userpic.date.toString().toUtf8()).append(": ");
|
if (!userpic.date.isValid()) {
|
||||||
lines.append(userpic.image.relativePath.toUtf8());
|
lines.append("(empty photo)");
|
||||||
|
} else {
|
||||||
|
lines.append(Data::FormatDateTime(userpic.date.toTime_t()));
|
||||||
|
lines.append(" - ");
|
||||||
|
if (userpic.image.relativePath.isEmpty()) {
|
||||||
|
lines.append("(file unavailable)");
|
||||||
|
} else {
|
||||||
|
lines.append(userpic.image.relativePath.toUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
lines.append(kLineBreak);
|
lines.append(kLineBreak);
|
||||||
}
|
}
|
||||||
return _result->writeBlock(lines) == File::Result::Success;
|
return _result->writeBlock(lines) == File::Result::Success;
|
||||||
|
@ -115,7 +150,52 @@ bool TextWriter::writeUserpicsEnd() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextWriter::writeContactsList(const Data::ContactsList &data) {
|
bool TextWriter::writeContactsList(const Data::ContactsList &data) {
|
||||||
return true;
|
if (data.list.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get sorted by name indices.
|
||||||
|
const auto names = ranges::view::all(
|
||||||
|
data.list
|
||||||
|
) | ranges::view::transform([](const Data::User &user) {
|
||||||
|
return (QString::fromUtf8(user.firstName)
|
||||||
|
+ ' '
|
||||||
|
+ QString::fromUtf8(user.lastName)).toLower();
|
||||||
|
}) | ranges::to_vector;
|
||||||
|
|
||||||
|
auto indices = ranges::view::ints(0, int(data.list.size()))
|
||||||
|
| ranges::to_vector;
|
||||||
|
ranges::sort(indices, [&](int i, int j) {
|
||||||
|
return names[i] < names[j];
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto header = "Contacts "
|
||||||
|
"(" + Data::NumberToString(data.list.size()) + ")"
|
||||||
|
+ kLineBreak
|
||||||
|
+ kLineBreak;
|
||||||
|
auto list = std::vector<QByteArray>();
|
||||||
|
list.reserve(data.list.size());
|
||||||
|
for (const auto &index : indices) {
|
||||||
|
const auto &contact = data.list[index];
|
||||||
|
if (!contact.id) {
|
||||||
|
list.push_back("(user unavailable)");
|
||||||
|
} else if (contact.firstName.isEmpty()
|
||||||
|
&& contact.lastName.isEmpty()
|
||||||
|
&& contact.phoneNumber.isEmpty()) {
|
||||||
|
list.push_back("(empty user)" + kLineBreak);
|
||||||
|
} else {
|
||||||
|
list.push_back(SerializeKeyValue({
|
||||||
|
{ "First name", contact.firstName },
|
||||||
|
{ "Last name", contact.lastName },
|
||||||
|
{
|
||||||
|
"Phone number",
|
||||||
|
Data::FormatPhoneNumber(contact.phoneNumber)
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto full = header + JoinList(kLineBreak, list) + kLineBreak;
|
||||||
|
return _result->writeBlock(full) == File::Result::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextWriter::writeSessionsList(const Data::SessionsList &data) {
|
bool TextWriter::writeSessionsList(const Data::SessionsList &data) {
|
||||||
|
|
|
@ -93,6 +93,7 @@ void SettingsWidget::refreshButtons(not_null<Ui::RpWidget*> container) {
|
||||||
st::defaultBoxButton)
|
st::defaultBoxButton)
|
||||||
: nullptr;
|
: nullptr;
|
||||||
if (start) {
|
if (start) {
|
||||||
|
start->show();
|
||||||
_startClicks = start->clicks();
|
_startClicks = start->clicks();
|
||||||
|
|
||||||
container->sizeValue(
|
container->sizeValue(
|
||||||
|
@ -107,6 +108,7 @@ void SettingsWidget::refreshButtons(not_null<Ui::RpWidget*> container) {
|
||||||
container.get(),
|
container.get(),
|
||||||
langFactory(lng_cancel),
|
langFactory(lng_cancel),
|
||||||
st::defaultBoxButton);
|
st::defaultBoxButton);
|
||||||
|
cancel->show();
|
||||||
_cancelClicks = cancel->clicks();
|
_cancelClicks = cancel->clicks();
|
||||||
|
|
||||||
rpl::combine(
|
rpl::combine(
|
||||||
|
|
|
@ -16,7 +16,7 @@ class ConcurrentSender::RPCDoneHandler : public RPCAbstractDoneHandler {
|
||||||
public:
|
public:
|
||||||
RPCDoneHandler(
|
RPCDoneHandler(
|
||||||
not_null<ConcurrentSender*> sender,
|
not_null<ConcurrentSender*> sender,
|
||||||
Fn<void(FnMut<void()>)> run);
|
Fn<void(FnMut<void()>)> runner);
|
||||||
|
|
||||||
void operator()(
|
void operator()(
|
||||||
mtpRequestId requestId,
|
mtpRequestId requestId,
|
||||||
|
@ -25,7 +25,7 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
base::weak_ptr<ConcurrentSender> _weak;
|
base::weak_ptr<ConcurrentSender> _weak;
|
||||||
Fn<void(FnMut<void()>)> _run;
|
Fn<void(FnMut<void()>)> _runner;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ class ConcurrentSender::RPCFailHandler : public RPCAbstractFailHandler {
|
||||||
public:
|
public:
|
||||||
RPCFailHandler(
|
RPCFailHandler(
|
||||||
not_null<ConcurrentSender*> sender,
|
not_null<ConcurrentSender*> sender,
|
||||||
Fn<void(FnMut<void()>)> run,
|
Fn<void(FnMut<void()>)> runner,
|
||||||
FailSkipPolicy skipPolicy);
|
FailSkipPolicy skipPolicy);
|
||||||
|
|
||||||
bool operator()(
|
bool operator()(
|
||||||
|
@ -42,16 +42,16 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
base::weak_ptr<ConcurrentSender> _weak;
|
base::weak_ptr<ConcurrentSender> _weak;
|
||||||
Fn<void(FnMut<void()>)> _run;
|
Fn<void(FnMut<void()>)> _runner;
|
||||||
FailSkipPolicy _skipPolicy = FailSkipPolicy::Simple;
|
FailSkipPolicy _skipPolicy = FailSkipPolicy::Simple;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ConcurrentSender::RPCDoneHandler::RPCDoneHandler(
|
ConcurrentSender::RPCDoneHandler::RPCDoneHandler(
|
||||||
not_null<ConcurrentSender*> sender,
|
not_null<ConcurrentSender*> sender,
|
||||||
Fn<void(FnMut<void()>)> run)
|
Fn<void(FnMut<void()>)> runner)
|
||||||
: _weak(sender)
|
: _weak(sender)
|
||||||
, _run(std::move(run)) {
|
, _runner(std::move(runner)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConcurrentSender::RPCDoneHandler::operator()(
|
void ConcurrentSender::RPCDoneHandler::operator()(
|
||||||
|
@ -61,7 +61,7 @@ void ConcurrentSender::RPCDoneHandler::operator()(
|
||||||
auto response = gsl::make_span(
|
auto response = gsl::make_span(
|
||||||
from,
|
from,
|
||||||
end - from);
|
end - from);
|
||||||
_run([=, weak = _weak, moved = bytes::make_vector(response)]() mutable {
|
_runner([=, weak = _weak, moved = bytes::make_vector(response)]() mutable {
|
||||||
if (const auto strong = weak.get()) {
|
if (const auto strong = weak.get()) {
|
||||||
strong->senderRequestDone(requestId, std::move(moved));
|
strong->senderRequestDone(requestId, std::move(moved));
|
||||||
}
|
}
|
||||||
|
@ -70,10 +70,10 @@ void ConcurrentSender::RPCDoneHandler::operator()(
|
||||||
|
|
||||||
ConcurrentSender::RPCFailHandler::RPCFailHandler(
|
ConcurrentSender::RPCFailHandler::RPCFailHandler(
|
||||||
not_null<ConcurrentSender*> sender,
|
not_null<ConcurrentSender*> sender,
|
||||||
Fn<void(FnMut<void()>)> run,
|
Fn<void(FnMut<void()>)> runner,
|
||||||
FailSkipPolicy skipPolicy)
|
FailSkipPolicy skipPolicy)
|
||||||
: _weak(sender)
|
: _weak(sender)
|
||||||
, _run(std::move(run))
|
, _runner(std::move(runner))
|
||||||
, _skipPolicy(skipPolicy) {
|
, _skipPolicy(skipPolicy) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ bool ConcurrentSender::RPCFailHandler::operator()(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_run([=, weak = _weak, error = error]() mutable {
|
_runner([=, weak = _weak, error = error]() mutable {
|
||||||
if (const auto strong = weak.get()) {
|
if (const auto strong = weak.get()) {
|
||||||
strong->senderRequestFail(requestId, std::move(error));
|
strong->senderRequestFail(requestId, std::move(error));
|
||||||
}
|
}
|
||||||
|
@ -142,10 +142,10 @@ mtpRequestId ConcurrentSender::RequestBuilder::send() {
|
||||||
_sender->with_instance([
|
_sender->with_instance([
|
||||||
=,
|
=,
|
||||||
request = std::move(_serialized),
|
request = std::move(_serialized),
|
||||||
done = std::make_shared<RPCDoneHandler>(_sender, _sender->_run),
|
done = std::make_shared<RPCDoneHandler>(_sender, _sender->_runner),
|
||||||
fail = std::make_shared<RPCFailHandler>(
|
fail = std::make_shared<RPCFailHandler>(
|
||||||
_sender,
|
_sender,
|
||||||
_sender->_run,
|
_sender->_runner,
|
||||||
_failSkipPolicy)
|
_failSkipPolicy)
|
||||||
](not_null<Instance*> instance) mutable {
|
](not_null<Instance*> instance) mutable {
|
||||||
instance->sendSerialized(
|
instance->sendSerialized(
|
||||||
|
@ -160,8 +160,8 @@ mtpRequestId ConcurrentSender::RequestBuilder::send() {
|
||||||
return requestId;
|
return requestId;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConcurrentSender::ConcurrentSender(Fn<void(FnMut<void()>)> run)
|
ConcurrentSender::ConcurrentSender(Fn<void(FnMut<void()>)> runner)
|
||||||
: _run(run) {
|
: _runner(runner) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ConcurrentSender::~ConcurrentSender() {
|
ConcurrentSender::~ConcurrentSender() {
|
||||||
|
|
|
@ -8,7 +8,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <rpl/details/callable.h>
|
#include <rpl/details/callable.h>
|
||||||
#include <crl/crl_object_on_queue.h>
|
|
||||||
#include "base/bytes.h"
|
#include "base/bytes.h"
|
||||||
#include "base/weak_ptr.h"
|
#include "base/weak_ptr.h"
|
||||||
#include "base/flat_map.h"
|
#include "base/flat_map.h"
|
||||||
|
@ -81,10 +80,7 @@ class ConcurrentSender : public base::has_weak_ptr {
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ConcurrentSender(Fn<void(FnMut<void()>)> run);
|
ConcurrentSender(Fn<void(FnMut<void()>)> runner);
|
||||||
|
|
||||||
template <typename Type>
|
|
||||||
ConcurrentSender(const crl::weak_on_queue<Type> &weak);
|
|
||||||
|
|
||||||
template <typename Request>
|
template <typename Request>
|
||||||
class SpecificRequestBuilder : public RequestBuilder {
|
class SpecificRequestBuilder : public RequestBuilder {
|
||||||
|
@ -107,21 +103,21 @@ public:
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
// Allow code completion to show response type.
|
// Allow code completion to show response type.
|
||||||
[[nodiscard]] SpecificRequestBuilder &done(Fn<void()> &&handler);
|
[[nodiscard]] SpecificRequestBuilder &done(FnMut<void()> &&handler);
|
||||||
[[nodiscard]] SpecificRequestBuilder &done(Fn<void(
|
[[nodiscard]] SpecificRequestBuilder &done(FnMut<void(
|
||||||
mtpRequestId)> &&handler);
|
mtpRequestId)> &&handler);
|
||||||
[[nodiscard]] SpecificRequestBuilder &done(Fn<void(
|
[[nodiscard]] SpecificRequestBuilder &done(FnMut<void(
|
||||||
mtpRequestId,
|
mtpRequestId,
|
||||||
Response &&)> &&handler);
|
Response &&)> &&handler);
|
||||||
[[nodiscard]] SpecificRequestBuilder &done(Fn<void(
|
[[nodiscard]] SpecificRequestBuilder &done(FnMut<void(
|
||||||
Response &&)> &&handler);
|
Response &&)> &&handler);
|
||||||
[[nodiscard]] SpecificRequestBuilder &fail(Fn<void()> &&handler);
|
[[nodiscard]] SpecificRequestBuilder &fail(FnMut<void()> &&handler);
|
||||||
[[nodiscard]] SpecificRequestBuilder &fail(Fn<void(
|
[[nodiscard]] SpecificRequestBuilder &fail(FnMut<void(
|
||||||
mtpRequestId)> &&handler);
|
mtpRequestId)> &&handler);
|
||||||
[[nodiscard]] SpecificRequestBuilder &fail(Fn<void(
|
[[nodiscard]] SpecificRequestBuilder &fail(FnMut<void(
|
||||||
mtpRequestId,
|
mtpRequestId,
|
||||||
RPCError &&)> &&handler);
|
RPCError &&)> &&handler);
|
||||||
[[nodiscard]] SpecificRequestBuilder &fail(Fn<void(
|
[[nodiscard]] SpecificRequestBuilder &fail(FnMut<void(
|
||||||
RPCError &&)> &&handler);
|
RPCError &&)> &&handler);
|
||||||
#else // _DEBUG
|
#else // _DEBUG
|
||||||
template <typename Handler>
|
template <typename Handler>
|
||||||
|
@ -194,20 +190,11 @@ private:
|
||||||
void senderRequestCancel(mtpRequestId requestId);
|
void senderRequestCancel(mtpRequestId requestId);
|
||||||
void senderRequestCancelAll();
|
void senderRequestCancelAll();
|
||||||
|
|
||||||
const Fn<void(FnMut<void()>)> _run;
|
const Fn<void(FnMut<void()>)> _runner;
|
||||||
base::flat_map<mtpRequestId, Handlers> _requests;
|
base::flat_map<mtpRequestId, Handlers> _requests;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Type>
|
|
||||||
ConcurrentSender::ConcurrentSender(const crl::weak_on_queue<Type> &weak)
|
|
||||||
: ConcurrentSender([=](FnMut<void()> method) {
|
|
||||||
weak.with([method = std::move(method)](Type&) mutable {
|
|
||||||
std::move(method)();
|
|
||||||
});
|
|
||||||
}) {
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Response, typename InvokeFullDone>
|
template <typename Response, typename InvokeFullDone>
|
||||||
void ConcurrentSender::RequestBuilder::setDoneHandler(
|
void ConcurrentSender::RequestBuilder::setDoneHandler(
|
||||||
InvokeFullDone &&invoke) noexcept {
|
InvokeFullDone &&invoke) noexcept {
|
||||||
|
@ -256,7 +243,7 @@ template <typename Request>
|
||||||
// Allow code completion to show response type.
|
// Allow code completion to show response type.
|
||||||
template <typename Request>
|
template <typename Request>
|
||||||
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::done(
|
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::done(
|
||||||
Fn<void(Response &&)> &&handler)
|
FnMut<void(Response &&)> &&handler)
|
||||||
-> SpecificRequestBuilder & {
|
-> SpecificRequestBuilder & {
|
||||||
setDoneHandler<Response>([handler = std::move(handler)](
|
setDoneHandler<Response>([handler = std::move(handler)](
|
||||||
mtpRequestId requestId,
|
mtpRequestId requestId,
|
||||||
|
@ -268,7 +255,7 @@ template <typename Request>
|
||||||
|
|
||||||
template <typename Request>
|
template <typename Request>
|
||||||
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::done(
|
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::done(
|
||||||
Fn<void(mtpRequestId, Response &&)> &&handler)
|
FnMut<void(mtpRequestId, Response &&)> &&handler)
|
||||||
-> SpecificRequestBuilder & {
|
-> SpecificRequestBuilder & {
|
||||||
setDoneHandler<Response>(std::move(handler));
|
setDoneHandler<Response>(std::move(handler));
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -276,7 +263,7 @@ template <typename Request>
|
||||||
|
|
||||||
template <typename Request>
|
template <typename Request>
|
||||||
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::done(
|
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::done(
|
||||||
Fn<void(mtpRequestId)> &&handler) -> SpecificRequestBuilder & {
|
FnMut<void(mtpRequestId)> &&handler) -> SpecificRequestBuilder & {
|
||||||
setDoneHandler<Response>([handler = std::move(handler)](
|
setDoneHandler<Response>([handler = std::move(handler)](
|
||||||
mtpRequestId requestId,
|
mtpRequestId requestId,
|
||||||
Response &&result) mutable {
|
Response &&result) mutable {
|
||||||
|
@ -287,7 +274,7 @@ template <typename Request>
|
||||||
|
|
||||||
template <typename Request>
|
template <typename Request>
|
||||||
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::done(
|
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::done(
|
||||||
Fn<void()> &&handler) -> SpecificRequestBuilder & {
|
FnMut<void()> &&handler) -> SpecificRequestBuilder & {
|
||||||
setDoneHandler<Response>([handler = std::move(handler)](
|
setDoneHandler<Response>([handler = std::move(handler)](
|
||||||
mtpRequestId requestId,
|
mtpRequestId requestId,
|
||||||
Response &&result) mutable {
|
Response &&result) mutable {
|
||||||
|
@ -298,7 +285,7 @@ template <typename Request>
|
||||||
|
|
||||||
template <typename Request>
|
template <typename Request>
|
||||||
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::fail(
|
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::fail(
|
||||||
Fn<void(RPCError &&)> &&handler) -> SpecificRequestBuilder & {
|
FnMut<void(RPCError &&)> &&handler) -> SpecificRequestBuilder & {
|
||||||
setFailHandler([handler = std::move(handler)](
|
setFailHandler([handler = std::move(handler)](
|
||||||
mtpRequestId requestId,
|
mtpRequestId requestId,
|
||||||
RPCError &&error) mutable {
|
RPCError &&error) mutable {
|
||||||
|
@ -309,7 +296,7 @@ template <typename Request>
|
||||||
|
|
||||||
template <typename Request>
|
template <typename Request>
|
||||||
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::fail(
|
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::fail(
|
||||||
Fn<void(mtpRequestId, RPCError &&)> &&handler)
|
FnMut<void(mtpRequestId, RPCError &&)> &&handler)
|
||||||
-> SpecificRequestBuilder & {
|
-> SpecificRequestBuilder & {
|
||||||
setFailHandler(std::move(handler));
|
setFailHandler(std::move(handler));
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -317,7 +304,7 @@ template <typename Request>
|
||||||
|
|
||||||
template <typename Request>
|
template <typename Request>
|
||||||
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::fail(
|
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::fail(
|
||||||
Fn<void(mtpRequestId)> &&handler) -> SpecificRequestBuilder & {
|
FnMut<void(mtpRequestId)> &&handler) -> SpecificRequestBuilder & {
|
||||||
setFailHandler([handler = std::move(handler)](
|
setFailHandler([handler = std::move(handler)](
|
||||||
mtpRequestId requestId,
|
mtpRequestId requestId,
|
||||||
RPCError &&error) mutable {
|
RPCError &&error) mutable {
|
||||||
|
@ -328,8 +315,8 @@ template <typename Request>
|
||||||
|
|
||||||
template <typename Request>
|
template <typename Request>
|
||||||
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::fail(
|
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::fail(
|
||||||
Fn<void()> &&handler) -> SpecificRequestBuilder & {
|
FnMut<void()> &&handler) -> SpecificRequestBuilder & {
|
||||||
setFailHandler([handler = move(handler)](
|
setFailHandler([handler = std::move(handler)](
|
||||||
mtpRequestId requestId,
|
mtpRequestId requestId,
|
||||||
RPCError &&error) mutable {
|
RPCError &&error) mutable {
|
||||||
std::move(handler)();
|
std::move(handler)();
|
||||||
|
|
|
@ -376,9 +376,9 @@ void ConnectionPrivate::appendTestConnection(
|
||||||
}
|
}
|
||||||
|
|
||||||
int16 ConnectionPrivate::getProtocolDcId() const {
|
int16 ConnectionPrivate::getProtocolDcId() const {
|
||||||
const auto dcId = MTP::bareDcId(_shiftedDcId);
|
const auto dcId = BareDcId(_shiftedDcId);
|
||||||
const auto simpleDcId = MTP::isTemporaryDcId(dcId)
|
const auto simpleDcId = isTemporaryDcId(dcId)
|
||||||
? MTP::getRealIdFromTemporaryDcId(dcId)
|
? getRealIdFromTemporaryDcId(dcId)
|
||||||
: dcId;
|
: dcId;
|
||||||
const auto testedDcId = cTestMode()
|
const auto testedDcId = cTestMode()
|
||||||
? (kTestModeDcIdShift + simpleDcId)
|
? (kTestModeDcIdShift + simpleDcId)
|
||||||
|
@ -724,13 +724,13 @@ void ConnectionPrivate::tryToSend() {
|
||||||
int32 state = getState();
|
int32 state = getState();
|
||||||
bool prependOnly = (state != ConnectedState);
|
bool prependOnly = (state != ConnectedState);
|
||||||
mtpRequest pingRequest;
|
mtpRequest pingRequest;
|
||||||
if (_shiftedDcId == bareDcId(_shiftedDcId)) { // main session
|
if (_shiftedDcId == BareDcId(_shiftedDcId)) { // main session
|
||||||
if (!prependOnly && !_pingIdToSend && !_pingId && _pingSendAt <= getms(true)) {
|
if (!prependOnly && !_pingIdToSend && !_pingId && _pingSendAt <= getms(true)) {
|
||||||
_pingIdToSend = rand_value<mtpPingId>();
|
_pingIdToSend = rand_value<mtpPingId>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_pingIdToSend) {
|
if (_pingIdToSend) {
|
||||||
if (prependOnly || _shiftedDcId != bareDcId(_shiftedDcId)) {
|
if (prependOnly || _shiftedDcId != BareDcId(_shiftedDcId)) {
|
||||||
MTPPing ping(MTPping(MTP_long(_pingIdToSend)));
|
MTPPing ping(MTPping(MTP_long(_pingIdToSend)));
|
||||||
uint32 pingSize = ping.innerLength() >> 2; // copy from Session::send
|
uint32 pingSize = ping.innerLength() >> 2; // copy from Session::send
|
||||||
pingRequest = mtpRequestData::prepare(pingSize);
|
pingRequest = mtpRequestData::prepare(pingSize);
|
||||||
|
@ -748,7 +748,7 @@ void ConnectionPrivate::tryToSend() {
|
||||||
_pingSendAt = pingRequest->msDate + kPingSendAfter;
|
_pingSendAt = pingRequest->msDate + kPingSendAfter;
|
||||||
pingRequest->requestId = 0; // dont add to haveSent / wereAcked maps
|
pingRequest->requestId = 0; // dont add to haveSent / wereAcked maps
|
||||||
|
|
||||||
if (_shiftedDcId == bareDcId(_shiftedDcId) && !prependOnly) { // main session
|
if (_shiftedDcId == BareDcId(_shiftedDcId) && !prependOnly) { // main session
|
||||||
_pingSender.callOnce(kPingSendAfterForce);
|
_pingSender.callOnce(kPingSendAfterForce);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -916,7 +916,7 @@ void ConnectionPrivate::tryToSend() {
|
||||||
mtpRequest wrappedRequest(mtpRequestData::prepare(toSendSize));
|
mtpRequest wrappedRequest(mtpRequestData::prepare(toSendSize));
|
||||||
memcpy(wrappedRequest->data(), toSendRequest->constData(), 7 * sizeof(mtpPrime)); // all except length
|
memcpy(wrappedRequest->data(), toSendRequest->constData(), 7 * sizeof(mtpPrime)); // all except length
|
||||||
wrappedRequest->push_back(mtpc_invokeWithLayer);
|
wrappedRequest->push_back(mtpc_invokeWithLayer);
|
||||||
wrappedRequest->push_back(MTP::internal::CurrentLayer);
|
wrappedRequest->push_back(internal::CurrentLayer);
|
||||||
initWrapper.write(*wrappedRequest);
|
initWrapper.write(*wrappedRequest);
|
||||||
wrappedRequest->resize(wrappedRequest->size() + noWrapSize);
|
wrappedRequest->resize(wrappedRequest->size() + noWrapSize);
|
||||||
memcpy(wrappedRequest->data() + wrappedRequest->size() - noWrapSize, toSendRequest->constData() + 8, noWrapSize * sizeof(mtpPrime));
|
memcpy(wrappedRequest->data() + wrappedRequest->size() - noWrapSize, toSendRequest->constData() + 8, noWrapSize * sizeof(mtpPrime));
|
||||||
|
@ -948,7 +948,7 @@ void ConnectionPrivate::tryToSend() {
|
||||||
if (willNeedInit) {
|
if (willNeedInit) {
|
||||||
initSerialized.reserve(initSizeInInts);
|
initSerialized.reserve(initSizeInInts);
|
||||||
initSerialized.push_back(mtpc_invokeWithLayer);
|
initSerialized.push_back(mtpc_invokeWithLayer);
|
||||||
initSerialized.push_back(MTP::internal::CurrentLayer);
|
initSerialized.push_back(internal::CurrentLayer);
|
||||||
initWrapper.write(initSerialized);
|
initWrapper.write(initSerialized);
|
||||||
}
|
}
|
||||||
toSendRequest = mtpRequestData::prepare(containerSize, containerSize + 3 * toSend.size()); // prepare container + each in invoke after
|
toSendRequest = mtpRequestData::prepare(containerSize, containerSize + 3 * toSend.size()); // prepare container + each in invoke after
|
||||||
|
@ -1080,7 +1080,7 @@ void ConnectionPrivate::connectToServer(bool afterConfig) {
|
||||||
sessionData->connectionOptions());
|
sessionData->connectionOptions());
|
||||||
hasKey = (sessionData->getKey() != nullptr);
|
hasKey = (sessionData->getKey() != nullptr);
|
||||||
}
|
}
|
||||||
auto bareDc = bareDcId(_shiftedDcId);
|
auto bareDc = BareDcId(_shiftedDcId);
|
||||||
_dcType = _instance->dcOptions()->dcType(_shiftedDcId);
|
_dcType = _instance->dcOptions()->dcType(_shiftedDcId);
|
||||||
|
|
||||||
// Use media_only addresses only if key for this dc is already created.
|
// Use media_only addresses only if key for this dc is already created.
|
||||||
|
@ -2570,7 +2570,7 @@ void ConnectionPrivate::pqAnswered() {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto rsaKey = internal::RSAPublicKey();
|
auto rsaKey = internal::RSAPublicKey();
|
||||||
if (!_instance->dcOptions()->getDcRSAKey(bareDcId(_shiftedDcId), res_pq.c_resPQ().vserver_public_key_fingerprints.v, &rsaKey)) {
|
if (!_instance->dcOptions()->getDcRSAKey(BareDcId(_shiftedDcId), res_pq.c_resPQ().vserver_public_key_fingerprints.v, &rsaKey)) {
|
||||||
if (_dcType == DcType::Cdn) {
|
if (_dcType == DcType::Cdn) {
|
||||||
LOG(("Warning: CDN public RSA key not found"));
|
LOG(("Warning: CDN public RSA key not found"));
|
||||||
requestCDNConfig();
|
requestCDNConfig();
|
||||||
|
@ -2587,7 +2587,7 @@ void ConnectionPrivate::pqAnswered() {
|
||||||
auto &pq = res_pq_data.vpq.v;
|
auto &pq = res_pq_data.vpq.v;
|
||||||
auto p = QByteArray();
|
auto p = QByteArray();
|
||||||
auto q = QByteArray();
|
auto q = QByteArray();
|
||||||
if (!MTP::internal::parsePQ(pq, p, q)) {
|
if (!internal::parsePQ(pq, p, q)) {
|
||||||
LOG(("AuthKey Error: could not factor pq!"));
|
LOG(("AuthKey Error: could not factor pq!"));
|
||||||
DEBUG_LOG(("AuthKey Error: problematic pq: %1").arg(Logs::mb(pq.constData(), pq.length()).str()));
|
DEBUG_LOG(("AuthKey Error: problematic pq: %1").arg(Logs::mb(pq.constData(), pq.length()).str()));
|
||||||
return restart();
|
return restart();
|
||||||
|
@ -2624,7 +2624,7 @@ void ConnectionPrivate::pqAnswered() {
|
||||||
|
|
||||||
bytes::vector ConnectionPrivate::encryptPQInnerRSA(
|
bytes::vector ConnectionPrivate::encryptPQInnerRSA(
|
||||||
const MTPP_Q_inner_data &data,
|
const MTPP_Q_inner_data &data,
|
||||||
const MTP::internal::RSAPublicKey &key) {
|
const internal::RSAPublicKey &key) {
|
||||||
auto p_q_inner_size = data.innerLength();
|
auto p_q_inner_size = data.innerLength();
|
||||||
auto encSize = (p_q_inner_size >> 2) + 6;
|
auto encSize = (p_q_inner_size >> 2) + 6;
|
||||||
if (encSize >= 65) {
|
if (encSize >= 65) {
|
||||||
|
@ -2880,7 +2880,7 @@ void ConnectionPrivate::dhClientParamsAnswered() {
|
||||||
uint64 salt1 = _authKeyData->new_nonce.l.l, salt2 = _authKeyData->server_nonce.l, serverSalt = salt1 ^ salt2;
|
uint64 salt1 = _authKeyData->new_nonce.l.l, salt2 = _authKeyData->server_nonce.l, serverSalt = salt1 ^ salt2;
|
||||||
sessionData->setSalt(serverSalt);
|
sessionData->setSalt(serverSalt);
|
||||||
|
|
||||||
auto authKey = std::make_shared<AuthKey>(AuthKey::Type::Generated, bareDcId(_shiftedDcId), _authKeyStrings->auth_key);
|
auto authKey = std::make_shared<AuthKey>(AuthKey::Type::Generated, BareDcId(_shiftedDcId), _authKeyStrings->auth_key);
|
||||||
|
|
||||||
DEBUG_LOG(("AuthKey Info: auth key gen succeed, id: %1, server salt: %2").arg(authKey->keyId()).arg(serverSalt));
|
DEBUG_LOG(("AuthKey Info: auth key gen succeed, id: %1, server salt: %2").arg(authKey->keyId()).arg(serverSalt));
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,7 @@ private:
|
||||||
|
|
||||||
bool setState(int32 state, int32 ifState = Connection::UpdateAlways);
|
bool setState(int32 state, int32 ifState = Connection::UpdateAlways);
|
||||||
|
|
||||||
bytes::vector encryptPQInnerRSA(const MTPP_Q_inner_data &data, const MTP::internal::RSAPublicKey &key);
|
bytes::vector encryptPQInnerRSA(const MTPP_Q_inner_data &data, const internal::RSAPublicKey &key);
|
||||||
std::string encryptClientDHInner(const MTPClient_DH_Inner_Data &data);
|
std::string encryptClientDHInner(const MTPClient_DH_Inner_Data &data);
|
||||||
void appendTestConnection(
|
void appendTestConnection(
|
||||||
DcOptions::Variants::Protocol protocol,
|
DcOptions::Variants::Protocol protocol,
|
||||||
|
|
|
@ -24,6 +24,28 @@ namespace MTP {
|
||||||
using DcId = int32;
|
using DcId = int32;
|
||||||
using ShiftedDcId = int32;
|
using ShiftedDcId = int32;
|
||||||
|
|
||||||
|
constexpr auto kDcShift = ShiftedDcId(10000);
|
||||||
|
constexpr auto kConfigDcShift = 0x01;
|
||||||
|
constexpr auto kLogoutDcShift = 0x02;
|
||||||
|
constexpr auto kUpdaterDcShift = 0x03;
|
||||||
|
constexpr auto kExportDcShift = 0x04;
|
||||||
|
constexpr auto kMaxMediaDcCount = 0x10;
|
||||||
|
constexpr auto kBaseDownloadDcShift = 0x10;
|
||||||
|
constexpr auto kBaseUploadDcShift = 0x20;
|
||||||
|
constexpr auto kDestroyKeyStartDcShift = 0x100;
|
||||||
|
|
||||||
|
constexpr DcId BareDcId(ShiftedDcId shiftedDcId) {
|
||||||
|
return (shiftedDcId % kDcShift);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ShiftedDcId ShiftDcId(DcId dcId, int value) {
|
||||||
|
return dcId + kDcShift * value;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr int GetDcIdShift(ShiftedDcId shiftedDcId) {
|
||||||
|
return shiftedDcId / kDcShift;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace MTP
|
} // namespace MTP
|
||||||
|
|
||||||
using mtpPrime = int32;
|
using mtpPrime = int32;
|
||||||
|
|
|
@ -233,7 +233,7 @@ void DcOptions::constructAddOne(
|
||||||
int port,
|
int port,
|
||||||
const bytes::vector &secret) {
|
const bytes::vector &secret) {
|
||||||
WriteLocker lock(this);
|
WriteLocker lock(this);
|
||||||
applyOneGuarded(bareDcId(id), flags, ip, port, secret);
|
applyOneGuarded(BareDcId(id), flags, ip, port, secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DcOptions::applyOneGuarded(
|
bool DcOptions::applyOneGuarded(
|
||||||
|
@ -525,7 +525,7 @@ DcType DcOptions::dcType(ShiftedDcId shiftedDcId) const {
|
||||||
return DcType::Temporary;
|
return DcType::Temporary;
|
||||||
}
|
}
|
||||||
ReadLocker lock(this);
|
ReadLocker lock(this);
|
||||||
if (_cdnDcIds.find(bareDcId(shiftedDcId)) != _cdnDcIds.cend()) {
|
if (_cdnDcIds.find(BareDcId(shiftedDcId)) != _cdnDcIds.cend()) {
|
||||||
return DcType::Cdn;
|
return DcType::Cdn;
|
||||||
}
|
}
|
||||||
if (isDownloadDcId(shiftedDcId)) {
|
if (isDownloadDcId(shiftedDcId)) {
|
||||||
|
@ -642,7 +642,7 @@ void DcOptions::computeCdnDcIds() {
|
||||||
for (auto &item : _data) {
|
for (auto &item : _data) {
|
||||||
Assert(!item.second.empty());
|
Assert(!item.second.empty());
|
||||||
if (item.second.front().flags & Flag::f_cdn) {
|
if (item.second.front().flags & Flag::f_cdn) {
|
||||||
_cdnDcIds.insert(bareDcId(item.first));
|
_cdnDcIds.insert(BareDcId(item.first));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -675,7 +675,7 @@ bool DcOptions::loadFromFile(const QString &path) {
|
||||||
auto ip = components[1];
|
auto ip = components[1];
|
||||||
auto port = components[2].toInt();
|
auto port = components[2].toInt();
|
||||||
auto host = QHostAddress();
|
auto host = QHostAddress();
|
||||||
if (dcId <= 0 || dcId >= internal::kDcShift || !host.setAddress(ip) || port <= 0) {
|
if (dcId <= 0 || dcId >= kDcShift || !host.setAddress(ip) || port <= 0) {
|
||||||
return error();
|
return error();
|
||||||
}
|
}
|
||||||
auto flags = Flags(0);
|
auto flags = Flags(0);
|
||||||
|
|
|
@ -18,15 +18,6 @@ bool paused();
|
||||||
void pause();
|
void pause();
|
||||||
void unpause();
|
void unpause();
|
||||||
|
|
||||||
constexpr auto kDcShift = ShiftedDcId(10000);
|
|
||||||
constexpr auto kConfigDcShift = 0x01;
|
|
||||||
constexpr auto kLogoutDcShift = 0x02;
|
|
||||||
constexpr auto kUpdaterDcShift = 0x03;
|
|
||||||
constexpr auto kMaxMediaDcCount = 0x10;
|
|
||||||
constexpr auto kBaseDownloadDcShift = 0x10;
|
|
||||||
constexpr auto kBaseUploadDcShift = 0x20;
|
|
||||||
constexpr auto kDestroyKeyStartDcShift = 0x100;
|
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
class PauseHolder {
|
class PauseHolder {
|
||||||
|
@ -53,29 +44,19 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr DcId bareDcId(ShiftedDcId shiftedDcId) {
|
|
||||||
return (shiftedDcId % internal::kDcShift);
|
|
||||||
}
|
|
||||||
constexpr ShiftedDcId shiftDcId(DcId dcId, int value) {
|
|
||||||
return dcId + internal::kDcShift * value;
|
|
||||||
}
|
|
||||||
constexpr int getDcIdShift(ShiftedDcId shiftedDcId) {
|
|
||||||
return shiftedDcId / internal::kDcShift;
|
|
||||||
}
|
|
||||||
|
|
||||||
// send(MTPhelp_GetConfig(), MTP::configDcId(dc)) - for dc enumeration
|
// send(MTPhelp_GetConfig(), MTP::configDcId(dc)) - for dc enumeration
|
||||||
constexpr ShiftedDcId configDcId(DcId dcId) {
|
constexpr ShiftedDcId configDcId(DcId dcId) {
|
||||||
return shiftDcId(dcId, internal::kConfigDcShift);
|
return ShiftDcId(dcId, kConfigDcShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
// send(MTPauth_LogOut(), MTP::logoutDcId(dc)) - for logout of guest dcs enumeration
|
// send(MTPauth_LogOut(), MTP::logoutDcId(dc)) - for logout of guest dcs enumeration
|
||||||
constexpr ShiftedDcId logoutDcId(DcId dcId) {
|
constexpr ShiftedDcId logoutDcId(DcId dcId) {
|
||||||
return shiftDcId(dcId, internal::kLogoutDcShift);
|
return ShiftDcId(dcId, kLogoutDcShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
// send(MTPupload_GetFile(), MTP::updaterDcId(dc)) - for autoupdater
|
// send(MTPupload_GetFile(), MTP::updaterDcId(dc)) - for autoupdater
|
||||||
constexpr ShiftedDcId updaterDcId(DcId dcId) {
|
constexpr ShiftedDcId updaterDcId(DcId dcId) {
|
||||||
return shiftDcId(dcId, internal::kUpdaterDcShift);
|
return ShiftDcId(dcId, kUpdaterDcShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr auto kDownloadSessionsCount = 2;
|
constexpr auto kDownloadSessionsCount = 2;
|
||||||
|
@ -84,8 +65,8 @@ constexpr auto kUploadSessionsCount = 2;
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
constexpr ShiftedDcId downloadDcId(DcId dcId, int index) {
|
constexpr ShiftedDcId downloadDcId(DcId dcId, int index) {
|
||||||
static_assert(kDownloadSessionsCount < internal::kMaxMediaDcCount, "Too large MTPDownloadSessionsCount!");
|
static_assert(kDownloadSessionsCount < kMaxMediaDcCount, "Too large MTPDownloadSessionsCount!");
|
||||||
return shiftDcId(dcId, internal::kBaseDownloadDcShift + index);
|
return ShiftDcId(dcId, kBaseDownloadDcShift + index);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
@ -97,7 +78,7 @@ inline ShiftedDcId downloadDcId(DcId dcId, int index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline constexpr bool isDownloadDcId(ShiftedDcId shiftedDcId) {
|
inline constexpr bool isDownloadDcId(ShiftedDcId shiftedDcId) {
|
||||||
return (shiftedDcId >= internal::downloadDcId(0, 0)) && (shiftedDcId < internal::downloadDcId(0, kDownloadSessionsCount - 1) + internal::kDcShift);
|
return (shiftedDcId >= internal::downloadDcId(0, 0)) && (shiftedDcId < internal::downloadDcId(0, kDownloadSessionsCount - 1) + kDcShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isCdnDc(MTPDdcOption::Flags flags) {
|
inline bool isCdnDc(MTPDdcOption::Flags flags) {
|
||||||
|
@ -105,43 +86,44 @@ inline bool isCdnDc(MTPDdcOption::Flags flags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isTemporaryDcId(ShiftedDcId shiftedDcId) {
|
inline bool isTemporaryDcId(ShiftedDcId shiftedDcId) {
|
||||||
auto dcId = bareDcId(shiftedDcId);
|
auto dcId = BareDcId(shiftedDcId);
|
||||||
return (dcId >= Instance::Config::kTemporaryMainDc);
|
return (dcId >= Instance::Config::kTemporaryMainDc);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline DcId getRealIdFromTemporaryDcId(ShiftedDcId shiftedDcId) {
|
inline DcId getRealIdFromTemporaryDcId(ShiftedDcId shiftedDcId) {
|
||||||
auto dcId = bareDcId(shiftedDcId);
|
auto dcId = BareDcId(shiftedDcId);
|
||||||
return (dcId >= Instance::Config::kTemporaryMainDc) ? (dcId - Instance::Config::kTemporaryMainDc) : 0;
|
return (dcId >= Instance::Config::kTemporaryMainDc) ? (dcId - Instance::Config::kTemporaryMainDc) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline DcId getTemporaryIdFromRealDcId(ShiftedDcId shiftedDcId) {
|
inline DcId getTemporaryIdFromRealDcId(ShiftedDcId shiftedDcId) {
|
||||||
auto dcId = bareDcId(shiftedDcId);
|
auto dcId = BareDcId(shiftedDcId);
|
||||||
return (dcId < Instance::Config::kTemporaryMainDc) ? (dcId + Instance::Config::kTemporaryMainDc) : 0;
|
return (dcId < Instance::Config::kTemporaryMainDc) ? (dcId + Instance::Config::kTemporaryMainDc) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
constexpr ShiftedDcId uploadDcId(DcId dcId, int index) {
|
constexpr ShiftedDcId uploadDcId(DcId dcId, int index) {
|
||||||
static_assert(kUploadSessionsCount < internal::kMaxMediaDcCount, "Too large MTPUploadSessionsCount!");
|
static_assert(kUploadSessionsCount < kMaxMediaDcCount, "Too large MTPUploadSessionsCount!");
|
||||||
return shiftDcId(dcId, internal::kBaseUploadDcShift + index);
|
return ShiftDcId(dcId, kBaseUploadDcShift + index);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
// send(req, callbacks, MTP::uploadDcId(index)) - for upload shifted dc id
|
// send(req, callbacks, MTP::uploadDcId(index)) - for upload shifted dc id
|
||||||
// uploading always to the main dc so bareDcId == 0
|
// uploading always to the main dc so BareDcId(result) == 0
|
||||||
inline ShiftedDcId uploadDcId(int index) {
|
inline ShiftedDcId uploadDcId(int index) {
|
||||||
Expects(index >= 0 && index < kUploadSessionsCount);
|
Expects(index >= 0 && index < kUploadSessionsCount);
|
||||||
|
|
||||||
return internal::uploadDcId(0, index);
|
return internal::uploadDcId(0, index);
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr bool isUploadDcId(ShiftedDcId shiftedDcId) {
|
constexpr bool isUploadDcId(ShiftedDcId shiftedDcId) {
|
||||||
return (shiftedDcId >= internal::uploadDcId(0, 0)) && (shiftedDcId < internal::uploadDcId(0, kUploadSessionsCount - 1) + internal::kDcShift);
|
return (shiftedDcId >= internal::uploadDcId(0, 0)) && (shiftedDcId < internal::uploadDcId(0, kUploadSessionsCount - 1) + kDcShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ShiftedDcId destroyKeyNextDcId(ShiftedDcId shiftedDcId) {
|
inline ShiftedDcId destroyKeyNextDcId(ShiftedDcId shiftedDcId) {
|
||||||
auto shift = getDcIdShift(shiftedDcId);
|
const auto shift = GetDcIdShift(shiftedDcId);
|
||||||
return shiftDcId(bareDcId(shiftedDcId), shift ? (shift + 1) : internal::kDestroyKeyStartDcShift);
|
return ShiftDcId(BareDcId(shiftedDcId), shift ? (shift + 1) : kDestroyKeyStartDcShift);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
|
|
@ -460,9 +460,9 @@ void Instance::Private::restart() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Instance::Private::restart(ShiftedDcId shiftedDcId) {
|
void Instance::Private::restart(ShiftedDcId shiftedDcId) {
|
||||||
auto dcId = bareDcId(shiftedDcId);
|
auto dcId = BareDcId(shiftedDcId);
|
||||||
for (auto &session : _sessions) {
|
for (auto &session : _sessions) {
|
||||||
if (bareDcId(session.second->getDcWithShift()) == dcId) {
|
if (BareDcId(session.second->getDcWithShift()) == dcId) {
|
||||||
session.second->restart();
|
session.second->restart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -474,9 +474,9 @@ int32 Instance::Private::dcstate(ShiftedDcId shiftedDcId) {
|
||||||
return _mainSession->getState();
|
return _mainSession->getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bareDcId(shiftedDcId)) {
|
if (!BareDcId(shiftedDcId)) {
|
||||||
Assert(_mainSession != nullptr);
|
Assert(_mainSession != nullptr);
|
||||||
shiftedDcId += bareDcId(_mainSession->getDcWithShift());
|
shiftedDcId += BareDcId(_mainSession->getDcWithShift());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = _sessions.find(shiftedDcId);
|
auto it = _sessions.find(shiftedDcId);
|
||||||
|
@ -492,9 +492,9 @@ QString Instance::Private::dctransport(ShiftedDcId shiftedDcId) {
|
||||||
Assert(_mainSession != nullptr);
|
Assert(_mainSession != nullptr);
|
||||||
return _mainSession->transport();
|
return _mainSession->transport();
|
||||||
}
|
}
|
||||||
if (!bareDcId(shiftedDcId)) {
|
if (!BareDcId(shiftedDcId)) {
|
||||||
Assert(_mainSession != nullptr);
|
Assert(_mainSession != nullptr);
|
||||||
shiftedDcId += bareDcId(_mainSession->getDcWithShift());
|
shiftedDcId += BareDcId(_mainSession->getDcWithShift());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = _sessions.find(shiftedDcId);
|
auto it = _sessions.find(shiftedDcId);
|
||||||
|
@ -583,7 +583,7 @@ void Instance::Private::stopSession(ShiftedDcId shiftedDcId) {
|
||||||
|
|
||||||
void Instance::Private::reInitConnection(DcId dcId) {
|
void Instance::Private::reInitConnection(DcId dcId) {
|
||||||
for (auto &session : _sessions) {
|
for (auto &session : _sessions) {
|
||||||
if (bareDcId(session.second->getDcWithShift()) == dcId) {
|
if (BareDcId(session.second->getDcWithShift()) == dcId) {
|
||||||
session.second->reInitConnection();
|
session.second->reInitConnection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -632,7 +632,7 @@ bool Instance::Private::logoutGuestDone(mtpRequestId requestId) {
|
||||||
std::shared_ptr<internal::Dcenter> Instance::Private::getDcById(ShiftedDcId shiftedDcId) {
|
std::shared_ptr<internal::Dcenter> Instance::Private::getDcById(ShiftedDcId shiftedDcId) {
|
||||||
auto it = _dcenters.find(shiftedDcId);
|
auto it = _dcenters.find(shiftedDcId);
|
||||||
if (it == _dcenters.cend()) {
|
if (it == _dcenters.cend()) {
|
||||||
auto dcId = bareDcId(shiftedDcId);
|
auto dcId = BareDcId(shiftedDcId);
|
||||||
if (isTemporaryDcId(dcId)) {
|
if (isTemporaryDcId(dcId)) {
|
||||||
if (auto realDcId = getRealIdFromTemporaryDcId(dcId)) {
|
if (auto realDcId = getRealIdFromTemporaryDcId(dcId)) {
|
||||||
dcId = realDcId;
|
dcId = realDcId;
|
||||||
|
@ -809,7 +809,7 @@ base::optional<ShiftedDcId> Instance::Private::changeRequestByDc(
|
||||||
if (it->second < 0) {
|
if (it->second < 0) {
|
||||||
it->second = -newdc;
|
it->second = -newdc;
|
||||||
} else {
|
} else {
|
||||||
it->second = shiftDcId(newdc, getDcIdShift(it->second));
|
it->second = ShiftDcId(newdc, GetDcIdShift(it->second));
|
||||||
}
|
}
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
@ -1081,7 +1081,7 @@ void Instance::Private::importDone(const MTPauth_Authorization &result, mtpReque
|
||||||
//}
|
//}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto newdc = bareDcId(*shiftedDcId);
|
auto newdc = BareDcId(*shiftedDcId);
|
||||||
|
|
||||||
DEBUG_LOG(("MTP Info: auth import to dc %1 succeeded").arg(newdc));
|
DEBUG_LOG(("MTP Info: auth import to dc %1 succeeded").arg(newdc));
|
||||||
|
|
||||||
|
@ -1149,7 +1149,7 @@ bool Instance::Private::exportFail(const RPCError &error, mtpRequestId requestId
|
||||||
|
|
||||||
auto it = _authExportRequests.find(requestId);
|
auto it = _authExportRequests.find(requestId);
|
||||||
if (it != _authExportRequests.cend()) {
|
if (it != _authExportRequests.cend()) {
|
||||||
_authWaiters[bareDcId(it->second)].clear();
|
_authWaiters[BareDcId(it->second)].clear();
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
// Don't log out on export/import problems, perhaps this is a server side error.
|
// Don't log out on export/import problems, perhaps this is a server side error.
|
||||||
|
@ -1203,7 +1203,7 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e
|
||||||
_instance->setMainDcId(newdcWithShift);
|
_instance->setMainDcId(newdcWithShift);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newdcWithShift = shiftDcId(newdcWithShift, getDcIdShift(dcWithShift));
|
newdcWithShift = ShiftDcId(newdcWithShift, GetDcIdShift(dcWithShift));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto request = mtpRequest();
|
auto request = mtpRequest();
|
||||||
|
@ -1255,7 +1255,7 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e
|
||||||
} else {
|
} else {
|
||||||
LOG(("MTP Error: unauthorized request without dc info, requestId %1").arg(requestId));
|
LOG(("MTP Error: unauthorized request without dc info, requestId %1").arg(requestId));
|
||||||
}
|
}
|
||||||
auto newdc = bareDcId(qAbs(dcWithShift));
|
auto newdc = BareDcId(qAbs(dcWithShift));
|
||||||
if (!newdc || newdc == mainDcId() || !hasAuthorization()) {
|
if (!newdc || newdc == mainDcId() || !hasAuthorization()) {
|
||||||
if (!badGuestDc && _globalHandler.onFail) {
|
if (!badGuestDc && _globalHandler.onFail) {
|
||||||
(*_globalHandler.onFail)(requestId, error); // auth failed in main dc
|
(*_globalHandler.onFail)(requestId, error); // auth failed in main dc
|
||||||
|
@ -1336,7 +1336,7 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e
|
||||||
request->needsLayer = true;
|
request->needsLayer = true;
|
||||||
session->sendPrepared(request);
|
session->sendPrepared(request);
|
||||||
} else {
|
} else {
|
||||||
auto newdc = bareDcId(qAbs(dcWithShift));
|
auto newdc = BareDcId(qAbs(dcWithShift));
|
||||||
auto &waiters(_authWaiters[newdc]);
|
auto &waiters(_authWaiters[newdc]);
|
||||||
if (base::contains(waiters, request->after->requestId)) {
|
if (base::contains(waiters, request->after->requestId)) {
|
||||||
if (!base::contains(waiters, requestId)) {
|
if (!base::contains(waiters, requestId)) {
|
||||||
|
@ -1372,9 +1372,9 @@ not_null<internal::Session*> Instance::Private::getSession(
|
||||||
Assert(_mainSession != nullptr);
|
Assert(_mainSession != nullptr);
|
||||||
return _mainSession;
|
return _mainSession;
|
||||||
}
|
}
|
||||||
if (!bareDcId(shiftedDcId)) {
|
if (!BareDcId(shiftedDcId)) {
|
||||||
Assert(_mainSession != nullptr);
|
Assert(_mainSession != nullptr);
|
||||||
shiftedDcId += bareDcId(_mainSession->getDcWithShift());
|
shiftedDcId += BareDcId(_mainSession->getDcWithShift());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = _sessions.find(shiftedDcId);
|
auto it = _sessions.find(shiftedDcId);
|
||||||
|
|
|
@ -596,7 +596,7 @@ void Session::tryToReceive() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isUpdate) {
|
if (isUpdate) {
|
||||||
if (dcWithShift == bareDcId(dcWithShift)) { // call globalCallback only in main session
|
if (dcWithShift == BareDcId(dcWithShift)) { // call globalCallback only in main session
|
||||||
_instance->globalCallback(message.constData(), message.constData() + message.size());
|
_instance->globalCallback(message.constData(), message.constData() + message.size());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -50,6 +50,8 @@
|
||||||
'<(submodules_loc)/crl/src',
|
'<(submodules_loc)/crl/src',
|
||||||
],
|
],
|
||||||
'sources': [
|
'sources': [
|
||||||
|
'<(src_loc)/export/export_api_wrap.cpp',
|
||||||
|
'<(src_loc)/export/export_api_wrap.h',
|
||||||
'<(src_loc)/export/export_controller.cpp',
|
'<(src_loc)/export/export_controller.cpp',
|
||||||
'<(src_loc)/export/export_controller.h',
|
'<(src_loc)/export/export_controller.h',
|
||||||
'<(src_loc)/export/export_settings.h',
|
'<(src_loc)/export/export_settings.h',
|
||||||
|
|
Loading…
Reference in New Issue