diff --git a/Telegram/SourceFiles/base/optional.h b/Telegram/SourceFiles/base/optional.h index 2240a8485f022..bc25131ee7e4e 100644 --- a/Telegram/SourceFiles/base/optional.h +++ b/Telegram/SourceFiles/base/optional.h @@ -160,12 +160,17 @@ class optional : public optional_variant { public: using parent::parent; - Type &operator*() { + Type &operator*() & { Expects(parent::template is()); return parent::template get_unchecked(); } - const Type &operator*() const { + Type &&operator*() && { + Expects(parent::template is()); + + return std::move(parent::template get_unchecked()); + } + const Type &operator*() const & { Expects(parent::template is()); return parent::template get_unchecked(); diff --git a/Telegram/SourceFiles/export/data/export_data_types.cpp b/Telegram/SourceFiles/export/data/export_data_types.cpp index 2be84abcfbb36..f501219491632 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.cpp +++ b/Telegram/SourceFiles/export/data/export_data_types.cpp @@ -18,53 +18,183 @@ Utf8String ParseString(const MTPstring &data) { return data.v; } -PersonalInfo ParsePersonalInfo(const MTPUserFull &data) { - Expects(data.type() == mtpc_userFull); - - const auto &fields = data.c_userFull(); - const auto &small = fields.vuser.c_user(); - auto result = PersonalInfo(); - if (small.has_first_name()) { - result.firstName = ParseString(small.vfirst_name); - } - if (small.has_last_name()) { - result.lastName = ParseString(small.vlast_name); - } - if (small.has_phone()) { - result.phoneNumber = ParseString(small.vphone); +FileLocation ParseLocation(const MTPFileLocation &data) { + 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; } - if (small.has_username()) { - result.username = ParseString(small.vusername); + Unexpected("Type in ParseLocation."); +} + +File ParseMaxImage( + const MTPVector &data, + const QString &suggestedPath) { + auto result = File(); + result.suggestedPath = suggestedPath; + + auto maxArea = int64(0); + for (const auto &size : data.v) { + switch (size.type()) { + case mtpc_photoSize: { + const auto &fields = size.c_photoSize(); + const auto area = fields.vw.v * int64(fields.vh.v); + if (area > maxArea) { + result.location = ParseLocation(fields.vlocation); + result.size = fields.vsize.v; + result.content = QByteArray(); + 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; + } } - if (fields.has_about()) { - result.bio = ParseString(fields.vabout); + 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 &data) { const auto &list = data.v; auto result = UserpicsSlice(); result.list.reserve(list.size()); for (const auto &photo : list) { - switch (photo.type()) { - case mtpc_photo: { - const auto &fields = photo.c_photo(); - auto userpic = Userpic(); - userpic.id = fields.vid.v; - userpic.date = QDateTime::fromTime_t(fields.vdate.v); - userpic.image = File{ "(not saved)" }; - result.list.push_back(std::move(userpic)); - } break; + const auto suggestedPath = "PersonalPhotos/Photo_" + + (photo.type() == mtpc_photo + ? QString::fromUtf8( + FormatDateTime(photo.c_photo().vdate.v, '_', '_', '_')) + : "Empty") + + ".jpg"; + result.list.push_back(ParsePhoto(photo, suggestedPath)); + } + return result; +} - case mtpc_photoEmpty: { - const auto &fields = photo.c_photoEmpty(); - auto userpic = Userpic(); - userpic.id = fields.vid.v; - result.list.push_back(std::move(userpic)); - } break; +User ParseUser(const MTPUser &data) { + auto result = User(); + switch (data.type()) { + case mtpc_user: { + const auto &fields = data.c_user(); + 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; + + case mtpc_userEmpty: { + const auto &fields = data.c_userEmpty(); + result.id = fields.vid.v; + } break; + + default: Unexpected("Type in ParseUser."); + } + return result; +} + +std::map ParseUsersList(const MTPVector &data) { + auto result = std::map(); + 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); - default: Unexpected("Photo type in ParseUserpicsSlice."); + 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; diff --git a/Telegram/SourceFiles/export/data/export_data_types.h b/Telegram/SourceFiles/export/data/export_data_types.h index 4344a900f37b1..8b4722ee6a528 100644 --- a/Telegram/SourceFiles/export/data/export_data_types.h +++ b/Telegram/SourceFiles/export/data/export_data_types.h @@ -8,6 +8,7 @@ For license and copyright information please follow this link: #pragma once #include "scheme.h" +#include "base/optional.h" #include #include @@ -28,46 +29,64 @@ inline auto NumberToString(Type value) return QByteArray(result.data(), int(result.size())); } -struct PersonalInfo { - Utf8String firstName; - Utf8String lastName; - Utf8String phoneNumber; - Utf8String username; - Utf8String bio; -}; - -PersonalInfo ParsePersonalInfo(const MTPUserFull &data); - 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 Userpic { +struct Photo { uint64 id = 0; QDateTime date; + + int width = 0; + int height = 0; File image; }; struct UserpicsSlice { - std::vector list; + std::vector list; }; UserpicsSlice ParseUserpicsSlice(const MTPVector &data); -struct Contact { +struct User { + int id = 0; Utf8String firstName; Utf8String lastName; Utf8String phoneNumber; + Utf8String username; +}; + +User ParseUser(const MTPUser &user); +std::map ParseUsersList(const MTPVector &data); + +struct PersonalInfo { + User user; + Utf8String bio; }; +PersonalInfo ParsePersonalInfo(const MTPUserFull &data); + struct ContactsList { - std::vector list; + std::vector list; }; +ContactsList ParseContactsList(const MTPcontacts_Contacts &data); + struct Session { Utf8String platform; Utf8String deviceModel; @@ -108,6 +127,11 @@ struct MessagesSlice { }; Utf8String FormatPhoneNumber(const Utf8String &phoneNumber); +Utf8String FormatDateTime( + const int32 date, + QChar dateSeparator = QChar('.'), + QChar timeSeparator = QChar(':'), + QChar separator = QChar(' ')); } // namespace Data } // namespace Export diff --git a/Telegram/SourceFiles/export/export_api_wrap.cpp b/Telegram/SourceFiles/export/export_api_wrap.cpp new file mode 100644 index 0000000000000..5050a59081014 --- /dev/null +++ b/Telegram/SourceFiles/export/export_api_wrap.cpp @@ -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 + +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 start; + Fn handleSlice; + FnMut finish; + + base::optional slice; + bool lastSlice = false; + int loading = -1; + +}; + +struct ApiWrap::FileProcess { + FileProcess(const QString &path); + + Output::File file; + QString relativePath; + + FnMut done; + + Data::FileLocation location; + int offset = 0; + int size = 0; + + struct Request { + int offset = 0; + QByteArray bytes; + }; + std::deque requests; + +}; + +ApiWrap::FileProcess::FileProcess(const QString &path) : file(path) { +} + +template +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)> runner) +: _mtp(std::move(runner)) { +} + +void ApiWrap::setFilesBaseFolder(const QString &folder) { + Expects(folder.endsWith('/')); + + _filesFolder = folder; +} + +rpl::producer ApiWrap::errors() const { + return _errors.events(); +} + +void ApiWrap::requestPersonalInfo(FnMut 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 start, + Fn slice, + FnMut finish) { + _userpicsProcess = std::make_unique(); + _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 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 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( + _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 diff --git a/Telegram/SourceFiles/export/export_api_wrap.h b/Telegram/SourceFiles/export/export_api_wrap.h new file mode 100644 index 0000000000000..407f79267d896 --- /dev/null +++ b/Telegram/SourceFiles/export/export_api_wrap.h @@ -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)> runner); + + void setFilesBaseFolder(const QString &folder); + + rpl::producer errors() const; + + void requestPersonalInfo(FnMut done); + + void requestUserpics( + FnMut start, + Fn slice, + FnMut finish); + + void requestContacts(FnMut 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 done); + void loadFilePart(); + void filePartDone(int offset, const MTPupload_File &result); + + template + [[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; + + struct FileProcess; + std::unique_ptr _fileProcess; + + rpl::event_stream _errors; + +}; + +} // namespace Export diff --git a/Telegram/SourceFiles/export/export_controller.cpp b/Telegram/SourceFiles/export/export_controller.cpp index d1bb7ce10e3d0..80f1848c2ecb2 100644 --- a/Telegram/SourceFiles/export/export_controller.cpp +++ b/Telegram/SourceFiles/export/export_controller.cpp @@ -7,18 +7,12 @@ For license and copyright information please follow this link: */ #include "export/export_controller.h" +#include "export/export_api_wrap.h" #include "export/export_settings.h" #include "export/data/export_data_types.h" #include "export/output/export_output_abstract.h" -#include "mtproto/rpc_sender.h" -#include "mtproto/concurrent_sender.h" namespace Export { -namespace { - -constexpr auto kUserpicsSliceLimit = 100; - -} // namespace class Controller { public: @@ -40,8 +34,6 @@ class Controller { using Step = ProcessingState::Step; void setState(State &&state); - void apiError(const RPCError &error); - void apiError(const QString &error); void ioError(const QString &path); void setFinishedState(); @@ -56,11 +48,9 @@ class Controller { void exportSessions(); void exportChats(); - void exportUserpicsSlice(const MTPphotos_Photos &result); - bool normalizePath(); - MTP::ConcurrentSender _mtp; + ApiWrap _api; Settings _settings; // rpl::variable fails to compile in MSVC :( @@ -73,14 +63,23 @@ class Controller { std::vector _steps; int _stepIndex = -1; - MTPInputUser _user = MTP_inputUserSelf(); + rpl::lifetime _lifetime; }; Controller::Controller(crl::weak_on_queue weak) -: _mtp(weak) +: _api(weak.runner()) , _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 Controller::state() const { @@ -99,14 +98,6 @@ void Controller::setState(State &&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) { setState(ErrorState{ ErrorState::Type::IO, base::none, path }); } @@ -124,7 +115,7 @@ rpl::producer Controller::passwordUpdate() const { } void Controller::reloadPasswordState() { - _mtp.request(base::take(_passwordRequestId)).cancel(); + //_mtp.request(base::take(_passwordRequestId)).cancel(); requestPasswordState(); } @@ -132,13 +123,13 @@ void Controller::requestPasswordState() { if (_passwordRequestId) { return; } - _passwordRequestId = _mtp.request(MTPaccount_GetPassword( - )).done([=](const MTPaccount_Password &result) { - _passwordRequestId = 0; - passwordStateDone(result); - }).fail([=](const RPCError &error) { - apiError(error); - }).send(); + //_passwordRequestId = _mtp.request(MTPaccount_GetPassword( + //)).done([=](const MTPaccount_Password &result) { + // _passwordRequestId = 0; + // passwordStateDone(result); + //}).fail([=](const RPCError &error) { + // apiError(error); + //}).send(); } void Controller::passwordStateDone(const MTPaccount_Password &result) { @@ -156,6 +147,9 @@ void Controller::cancelUnconfirmedPassword() { } void Controller::startExport(const Settings &settings) { + if (!_settings.path.isEmpty()) { + return; + } _settings = base::duplicate(settings); if (!normalizePath()) { @@ -163,6 +157,7 @@ void Controller::startExport(const Settings &settings) { return; } _writer = Output::CreateWriter(_settings.format); + _api.setFilesBaseFolder(_settings.path); fillExportSteps(); exportNext(); } @@ -182,7 +177,7 @@ bool Controller::normalizePath() { return true; } 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.month(), 2, 10, QChar('0') ).arg(date.year()); @@ -241,101 +236,28 @@ void Controller::exportNext() { } void Controller::exportPersonalInfo() { - if (!(_settings.types & Settings::Type::PersonalInfo)) { - exportUserpics(); - return; - } - _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(); + _api.requestPersonalInfo([=](Data::PersonalInfo &&result) { + _writer->writePersonal(result); + exportNext(); + }); } void Controller::exportUserpics() { - _mtp.request(MTPphotos_GetUserPhotos( - _user, - MTP_int(0), - MTP_long(0), - 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 = [&] { + _api.requestUserpics([=](Data::UserpicsInfo &&start) { + _writer->writeUserpicsStart(start); + }, [=](Data::UserpicsSlice &&slice) { + _writer->writeUserpicsSlice(slice); + }, [=] { _writer->writeUserpicsEnd(); 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() { - exportNext(); + _api.requestContacts([=](Data::ContactsList &&result) { + _writer->writeContactsList(result); + exportNext(); + }); } void Controller::exportSessions() { diff --git a/Telegram/SourceFiles/export/export_pch.h b/Telegram/SourceFiles/export/export_pch.h index fa05b04d3812b..3f86482e01afc 100644 --- a/Telegram/SourceFiles/export/export_pch.h +++ b/Telegram/SourceFiles/export/export_pch.h @@ -7,6 +7,7 @@ For license and copyright information please follow this link: */ #include +#include #include #include #include @@ -14,6 +15,12 @@ For license and copyright information please follow this link: #include #include #include +#include + +#include +#ifdef Q_OS_WIN +#include "platform/win/windows_range_v3_helpers.h" +#endif // Q_OS_WIN #include "scheme.h" #include "logs.h" diff --git a/Telegram/SourceFiles/export/output/export_output_file.cpp b/Telegram/SourceFiles/export/output/export_output_file.cpp index 07b8f82a18e4f..ebf8ae1622e3a 100644 --- a/Telegram/SourceFiles/export/output/export_output_file.cpp +++ b/Telegram/SourceFiles/export/output/export_output_file.cpp @@ -7,6 +7,8 @@ For license and copyright information please follow this link: */ #include "export/output/export_output_file.h" +#include +#include #include namespace Export { @@ -46,10 +48,45 @@ File::Result File::reopen() { } else if (_offset > 0) { 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::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 File diff --git a/Telegram/SourceFiles/export/output/export_output_file.h b/Telegram/SourceFiles/export/output/export_output_file.h index 8d4dad7e24138..f8083a1e52ac2 100644 --- a/Telegram/SourceFiles/export/output/export_output_file.h +++ b/Telegram/SourceFiles/export/output/export_output_file.h @@ -27,6 +27,10 @@ class File { }; Result writeBlock(const QByteArray &block); + static QString PrepareRelativePath( + const QString &folder, + const QString &suggested); + private: Result reopen(); Result writeBlockAttempt(const QByteArray &block); diff --git a/Telegram/SourceFiles/export/output/export_output_text.cpp b/Telegram/SourceFiles/export/output/export_output_text.cpp index 192abd0110445..0b7dd3ddc96dc 100644 --- a/Telegram/SourceFiles/export/output/export_output_text.cpp +++ b/Telegram/SourceFiles/export/output/export_output_text.cpp @@ -35,6 +35,32 @@ void SerializeMultiline( } while (newline > 0); } +QByteArray JoinList( + const QByteArray &separator, + const std::vector &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( std::vector> &&values) { auto result = QByteArray(); @@ -74,10 +100,10 @@ bool TextWriter::writePersonal(const Data::PersonalInfo &data) { + kLineBreak + kLineBreak + SerializeKeyValue({ - { "First name", data.firstName }, - { "Last name", data.lastName }, - { "Phone number", Data::FormatPhoneNumber(data.phoneNumber) }, - { "Username", FormatUsername(data.username) }, + { "First name", data.user.firstName }, + { "Last name", data.user.lastName }, + { "Phone number", Data::FormatPhoneNumber(data.user.phoneNumber) }, + { "Username", FormatUsername(data.user.username) }, { "Bio", data.bio }, }) + kLineBreak; @@ -101,8 +127,17 @@ bool TextWriter::writeUserpicsStart(const Data::UserpicsInfo &data) { bool TextWriter::writeUserpicsSlice(const Data::UserpicsSlice &data) { auto lines = QByteArray(); for (const auto &userpic : data.list) { - lines.append(userpic.date.toString().toUtf8()).append(": "); - lines.append(userpic.image.relativePath.toUtf8()); + if (!userpic.date.isValid()) { + 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); } return _result->writeBlock(lines) == File::Result::Success; @@ -115,7 +150,52 @@ bool TextWriter::writeUserpicsEnd() { } 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(); + 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) { diff --git a/Telegram/SourceFiles/export/view/export_view_settings.cpp b/Telegram/SourceFiles/export/view/export_view_settings.cpp index 5089a51295435..1c8b9d87af897 100644 --- a/Telegram/SourceFiles/export/view/export_view_settings.cpp +++ b/Telegram/SourceFiles/export/view/export_view_settings.cpp @@ -93,6 +93,7 @@ void SettingsWidget::refreshButtons(not_null container) { st::defaultBoxButton) : nullptr; if (start) { + start->show(); _startClicks = start->clicks(); container->sizeValue( @@ -107,6 +108,7 @@ void SettingsWidget::refreshButtons(not_null container) { container.get(), langFactory(lng_cancel), st::defaultBoxButton); + cancel->show(); _cancelClicks = cancel->clicks(); rpl::combine( diff --git a/Telegram/SourceFiles/mtproto/concurrent_sender.cpp b/Telegram/SourceFiles/mtproto/concurrent_sender.cpp index 96ca6c1aaaffc..93b473d59b44b 100644 --- a/Telegram/SourceFiles/mtproto/concurrent_sender.cpp +++ b/Telegram/SourceFiles/mtproto/concurrent_sender.cpp @@ -16,7 +16,7 @@ class ConcurrentSender::RPCDoneHandler : public RPCAbstractDoneHandler { public: RPCDoneHandler( not_null sender, - Fn)> run); + Fn)> runner); void operator()( mtpRequestId requestId, @@ -25,7 +25,7 @@ class ConcurrentSender::RPCDoneHandler : public RPCAbstractDoneHandler { private: base::weak_ptr _weak; - Fn)> _run; + Fn)> _runner; }; @@ -33,7 +33,7 @@ class ConcurrentSender::RPCFailHandler : public RPCAbstractFailHandler { public: RPCFailHandler( not_null sender, - Fn)> run, + Fn)> runner, FailSkipPolicy skipPolicy); bool operator()( @@ -42,16 +42,16 @@ class ConcurrentSender::RPCFailHandler : public RPCAbstractFailHandler { private: base::weak_ptr _weak; - Fn)> _run; + Fn)> _runner; FailSkipPolicy _skipPolicy = FailSkipPolicy::Simple; }; ConcurrentSender::RPCDoneHandler::RPCDoneHandler( not_null sender, - Fn)> run) + Fn)> runner) : _weak(sender) -, _run(std::move(run)) { +, _runner(std::move(runner)) { } void ConcurrentSender::RPCDoneHandler::operator()( @@ -61,7 +61,7 @@ void ConcurrentSender::RPCDoneHandler::operator()( auto response = gsl::make_span( 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()) { strong->senderRequestDone(requestId, std::move(moved)); } @@ -70,10 +70,10 @@ void ConcurrentSender::RPCDoneHandler::operator()( ConcurrentSender::RPCFailHandler::RPCFailHandler( not_null sender, - Fn)> run, + Fn)> runner, FailSkipPolicy skipPolicy) : _weak(sender) -, _run(std::move(run)) +, _runner(std::move(runner)) , _skipPolicy(skipPolicy) { } @@ -89,7 +89,7 @@ bool ConcurrentSender::RPCFailHandler::operator()( return false; } } - _run([=, weak = _weak, error = error]() mutable { + _runner([=, weak = _weak, error = error]() mutable { if (const auto strong = weak.get()) { strong->senderRequestFail(requestId, std::move(error)); } @@ -142,10 +142,10 @@ mtpRequestId ConcurrentSender::RequestBuilder::send() { _sender->with_instance([ =, request = std::move(_serialized), - done = std::make_shared(_sender, _sender->_run), + done = std::make_shared(_sender, _sender->_runner), fail = std::make_shared( _sender, - _sender->_run, + _sender->_runner, _failSkipPolicy) ](not_null instance) mutable { instance->sendSerialized( @@ -160,8 +160,8 @@ mtpRequestId ConcurrentSender::RequestBuilder::send() { return requestId; } -ConcurrentSender::ConcurrentSender(Fn)> run) -: _run(run) { +ConcurrentSender::ConcurrentSender(Fn)> runner) +: _runner(runner) { } ConcurrentSender::~ConcurrentSender() { diff --git a/Telegram/SourceFiles/mtproto/concurrent_sender.h b/Telegram/SourceFiles/mtproto/concurrent_sender.h index d975ccc302521..f8a5df8c815eb 100644 --- a/Telegram/SourceFiles/mtproto/concurrent_sender.h +++ b/Telegram/SourceFiles/mtproto/concurrent_sender.h @@ -8,7 +8,6 @@ For license and copyright information please follow this link: #pragma once #include -#include #include "base/bytes.h" #include "base/weak_ptr.h" #include "base/flat_map.h" @@ -81,10 +80,7 @@ class ConcurrentSender : public base::has_weak_ptr { }; public: - ConcurrentSender(Fn)> run); - - template - ConcurrentSender(const crl::weak_on_queue &weak); + ConcurrentSender(Fn)> runner); template class SpecificRequestBuilder : public RequestBuilder { @@ -107,21 +103,21 @@ class ConcurrentSender : public base::has_weak_ptr { #ifdef _DEBUG // Allow code completion to show response type. - [[nodiscard]] SpecificRequestBuilder &done(Fn &&handler); - [[nodiscard]] SpecificRequestBuilder &done(Fn &&handler); + [[nodiscard]] SpecificRequestBuilder &done(FnMut &&handler); - [[nodiscard]] SpecificRequestBuilder &done(Fn &&handler); - [[nodiscard]] SpecificRequestBuilder &done(Fn &&handler); - [[nodiscard]] SpecificRequestBuilder &fail(Fn &&handler); - [[nodiscard]] SpecificRequestBuilder &fail(Fn &&handler); + [[nodiscard]] SpecificRequestBuilder &fail(FnMut &&handler); - [[nodiscard]] SpecificRequestBuilder &fail(Fn &&handler); - [[nodiscard]] SpecificRequestBuilder &fail(Fn &&handler); #else // _DEBUG template @@ -194,20 +190,11 @@ class ConcurrentSender : public base::has_weak_ptr { void senderRequestCancel(mtpRequestId requestId); void senderRequestCancelAll(); - const Fn)> _run; + const Fn)> _runner; base::flat_map _requests; }; -template -ConcurrentSender::ConcurrentSender(const crl::weak_on_queue &weak) -: ConcurrentSender([=](FnMut method) { - weak.with([method = std::move(method)](Type&) mutable { - std::move(method)(); - }); -}) { -} - template void ConcurrentSender::RequestBuilder::setDoneHandler( InvokeFullDone &&invoke) noexcept { @@ -256,7 +243,7 @@ template // Allow code completion to show response type. template [[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder::done( - Fn &&handler) + FnMut &&handler) -> SpecificRequestBuilder & { setDoneHandler([handler = std::move(handler)]( mtpRequestId requestId, @@ -268,7 +255,7 @@ template template [[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder::done( - Fn &&handler) + FnMut &&handler) -> SpecificRequestBuilder & { setDoneHandler(std::move(handler)); return *this; @@ -276,7 +263,7 @@ template template [[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder::done( - Fn &&handler) -> SpecificRequestBuilder & { + FnMut &&handler) -> SpecificRequestBuilder & { setDoneHandler([handler = std::move(handler)]( mtpRequestId requestId, Response &&result) mutable { @@ -287,7 +274,7 @@ template template [[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder::done( - Fn &&handler) -> SpecificRequestBuilder & { + FnMut &&handler) -> SpecificRequestBuilder & { setDoneHandler([handler = std::move(handler)]( mtpRequestId requestId, Response &&result) mutable { @@ -298,7 +285,7 @@ template template [[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder::fail( - Fn &&handler) -> SpecificRequestBuilder & { + FnMut &&handler) -> SpecificRequestBuilder & { setFailHandler([handler = std::move(handler)]( mtpRequestId requestId, RPCError &&error) mutable { @@ -309,7 +296,7 @@ template template [[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder::fail( - Fn &&handler) + FnMut &&handler) -> SpecificRequestBuilder & { setFailHandler(std::move(handler)); return *this; @@ -317,7 +304,7 @@ template template [[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder::fail( - Fn &&handler) -> SpecificRequestBuilder & { + FnMut &&handler) -> SpecificRequestBuilder & { setFailHandler([handler = std::move(handler)]( mtpRequestId requestId, RPCError &&error) mutable { @@ -328,8 +315,8 @@ template template [[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder::fail( - Fn &&handler) -> SpecificRequestBuilder & { - setFailHandler([handler = move(handler)]( + FnMut &&handler) -> SpecificRequestBuilder & { + setFailHandler([handler = std::move(handler)]( mtpRequestId requestId, RPCError &&error) mutable { std::move(handler)(); diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp index e53bda6e2514f..87d9c632990e1 100644 --- a/Telegram/SourceFiles/mtproto/connection.cpp +++ b/Telegram/SourceFiles/mtproto/connection.cpp @@ -376,9 +376,9 @@ void ConnectionPrivate::appendTestConnection( } int16 ConnectionPrivate::getProtocolDcId() const { - const auto dcId = MTP::bareDcId(_shiftedDcId); - const auto simpleDcId = MTP::isTemporaryDcId(dcId) - ? MTP::getRealIdFromTemporaryDcId(dcId) + const auto dcId = BareDcId(_shiftedDcId); + const auto simpleDcId = isTemporaryDcId(dcId) + ? getRealIdFromTemporaryDcId(dcId) : dcId; const auto testedDcId = cTestMode() ? (kTestModeDcIdShift + simpleDcId) @@ -724,13 +724,13 @@ void ConnectionPrivate::tryToSend() { int32 state = getState(); bool prependOnly = (state != ConnectedState); mtpRequest pingRequest; - if (_shiftedDcId == bareDcId(_shiftedDcId)) { // main session + if (_shiftedDcId == BareDcId(_shiftedDcId)) { // main session if (!prependOnly && !_pingIdToSend && !_pingId && _pingSendAt <= getms(true)) { _pingIdToSend = rand_value(); } } if (_pingIdToSend) { - if (prependOnly || _shiftedDcId != bareDcId(_shiftedDcId)) { + if (prependOnly || _shiftedDcId != BareDcId(_shiftedDcId)) { MTPPing ping(MTPping(MTP_long(_pingIdToSend))); uint32 pingSize = ping.innerLength() >> 2; // copy from Session::send pingRequest = mtpRequestData::prepare(pingSize); @@ -748,7 +748,7 @@ void ConnectionPrivate::tryToSend() { _pingSendAt = pingRequest->msDate + kPingSendAfter; 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); } @@ -916,7 +916,7 @@ void ConnectionPrivate::tryToSend() { mtpRequest wrappedRequest(mtpRequestData::prepare(toSendSize)); memcpy(wrappedRequest->data(), toSendRequest->constData(), 7 * sizeof(mtpPrime)); // all except length wrappedRequest->push_back(mtpc_invokeWithLayer); - wrappedRequest->push_back(MTP::internal::CurrentLayer); + wrappedRequest->push_back(internal::CurrentLayer); initWrapper.write(*wrappedRequest); wrappedRequest->resize(wrappedRequest->size() + noWrapSize); memcpy(wrappedRequest->data() + wrappedRequest->size() - noWrapSize, toSendRequest->constData() + 8, noWrapSize * sizeof(mtpPrime)); @@ -948,7 +948,7 @@ void ConnectionPrivate::tryToSend() { if (willNeedInit) { initSerialized.reserve(initSizeInInts); initSerialized.push_back(mtpc_invokeWithLayer); - initSerialized.push_back(MTP::internal::CurrentLayer); + initSerialized.push_back(internal::CurrentLayer); initWrapper.write(initSerialized); } 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()); hasKey = (sessionData->getKey() != nullptr); } - auto bareDc = bareDcId(_shiftedDcId); + auto bareDc = BareDcId(_shiftedDcId); _dcType = _instance->dcOptions()->dcType(_shiftedDcId); // Use media_only addresses only if key for this dc is already created. @@ -2570,7 +2570,7 @@ void ConnectionPrivate::pqAnswered() { } 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) { LOG(("Warning: CDN public RSA key not found")); requestCDNConfig(); @@ -2587,7 +2587,7 @@ void ConnectionPrivate::pqAnswered() { auto &pq = res_pq_data.vpq.v; auto p = QByteArray(); auto q = QByteArray(); - if (!MTP::internal::parsePQ(pq, p, q)) { + if (!internal::parsePQ(pq, p, q)) { LOG(("AuthKey Error: could not factor pq!")); DEBUG_LOG(("AuthKey Error: problematic pq: %1").arg(Logs::mb(pq.constData(), pq.length()).str())); return restart(); @@ -2624,7 +2624,7 @@ void ConnectionPrivate::pqAnswered() { bytes::vector ConnectionPrivate::encryptPQInnerRSA( const MTPP_Q_inner_data &data, - const MTP::internal::RSAPublicKey &key) { + const internal::RSAPublicKey &key) { auto p_q_inner_size = data.innerLength(); auto encSize = (p_q_inner_size >> 2) + 6; 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; sessionData->setSalt(serverSalt); - auto authKey = std::make_shared(AuthKey::Type::Generated, bareDcId(_shiftedDcId), _authKeyStrings->auth_key); + auto authKey = std::make_shared(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)); diff --git a/Telegram/SourceFiles/mtproto/connection.h b/Telegram/SourceFiles/mtproto/connection.h index d8628895cabb2..2f4c8b9b860fe 100644 --- a/Telegram/SourceFiles/mtproto/connection.h +++ b/Telegram/SourceFiles/mtproto/connection.h @@ -190,7 +190,7 @@ public slots: 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); void appendTestConnection( DcOptions::Variants::Protocol protocol, diff --git a/Telegram/SourceFiles/mtproto/core_types.h b/Telegram/SourceFiles/mtproto/core_types.h index f1882b6dac35c..539592b56ba9d 100644 --- a/Telegram/SourceFiles/mtproto/core_types.h +++ b/Telegram/SourceFiles/mtproto/core_types.h @@ -24,6 +24,28 @@ namespace MTP { using DcId = 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 using mtpPrime = int32; diff --git a/Telegram/SourceFiles/mtproto/dc_options.cpp b/Telegram/SourceFiles/mtproto/dc_options.cpp index 4ce82a8b17979..8a78f8630cd79 100644 --- a/Telegram/SourceFiles/mtproto/dc_options.cpp +++ b/Telegram/SourceFiles/mtproto/dc_options.cpp @@ -233,7 +233,7 @@ void DcOptions::constructAddOne( int port, const bytes::vector &secret) { WriteLocker lock(this); - applyOneGuarded(bareDcId(id), flags, ip, port, secret); + applyOneGuarded(BareDcId(id), flags, ip, port, secret); } bool DcOptions::applyOneGuarded( @@ -525,7 +525,7 @@ DcType DcOptions::dcType(ShiftedDcId shiftedDcId) const { return DcType::Temporary; } ReadLocker lock(this); - if (_cdnDcIds.find(bareDcId(shiftedDcId)) != _cdnDcIds.cend()) { + if (_cdnDcIds.find(BareDcId(shiftedDcId)) != _cdnDcIds.cend()) { return DcType::Cdn; } if (isDownloadDcId(shiftedDcId)) { @@ -642,7 +642,7 @@ void DcOptions::computeCdnDcIds() { for (auto &item : _data) { Assert(!item.second.empty()); 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 port = components[2].toInt(); 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(); } auto flags = Flags(0); diff --git a/Telegram/SourceFiles/mtproto/facade.h b/Telegram/SourceFiles/mtproto/facade.h index fb5cd4c336fb2..1cc586a793e3b 100644 --- a/Telegram/SourceFiles/mtproto/facade.h +++ b/Telegram/SourceFiles/mtproto/facade.h @@ -18,15 +18,6 @@ bool paused(); void pause(); 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 class PauseHolder { @@ -53,29 +44,19 @@ class PauseHolder { }; -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 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 constexpr ShiftedDcId logoutDcId(DcId dcId) { - return shiftDcId(dcId, internal::kLogoutDcShift); + return ShiftDcId(dcId, kLogoutDcShift); } // send(MTPupload_GetFile(), MTP::updaterDcId(dc)) - for autoupdater constexpr ShiftedDcId updaterDcId(DcId dcId) { - return shiftDcId(dcId, internal::kUpdaterDcShift); + return ShiftDcId(dcId, kUpdaterDcShift); } constexpr auto kDownloadSessionsCount = 2; @@ -84,8 +65,8 @@ constexpr auto kUploadSessionsCount = 2; namespace internal { constexpr ShiftedDcId downloadDcId(DcId dcId, int index) { - static_assert(kDownloadSessionsCount < internal::kMaxMediaDcCount, "Too large MTPDownloadSessionsCount!"); - return shiftDcId(dcId, internal::kBaseDownloadDcShift + index); + static_assert(kDownloadSessionsCount < kMaxMediaDcCount, "Too large MTPDownloadSessionsCount!"); + return ShiftDcId(dcId, kBaseDownloadDcShift + index); }; } // namespace internal @@ -97,7 +78,7 @@ inline ShiftedDcId downloadDcId(DcId dcId, int index) { } 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) { @@ -105,43 +86,44 @@ inline bool isCdnDc(MTPDdcOption::Flags flags) { } inline bool isTemporaryDcId(ShiftedDcId shiftedDcId) { - auto dcId = bareDcId(shiftedDcId); + auto dcId = BareDcId(shiftedDcId); return (dcId >= Instance::Config::kTemporaryMainDc); } inline DcId getRealIdFromTemporaryDcId(ShiftedDcId shiftedDcId) { - auto dcId = bareDcId(shiftedDcId); + auto dcId = BareDcId(shiftedDcId); return (dcId >= Instance::Config::kTemporaryMainDc) ? (dcId - Instance::Config::kTemporaryMainDc) : 0; } inline DcId getTemporaryIdFromRealDcId(ShiftedDcId shiftedDcId) { - auto dcId = bareDcId(shiftedDcId); + auto dcId = BareDcId(shiftedDcId); return (dcId < Instance::Config::kTemporaryMainDc) ? (dcId + Instance::Config::kTemporaryMainDc) : 0; } namespace internal { constexpr ShiftedDcId uploadDcId(DcId dcId, int index) { - static_assert(kUploadSessionsCount < internal::kMaxMediaDcCount, "Too large MTPUploadSessionsCount!"); - return shiftDcId(dcId, internal::kBaseUploadDcShift + index); + static_assert(kUploadSessionsCount < kMaxMediaDcCount, "Too large MTPUploadSessionsCount!"); + return ShiftDcId(dcId, kBaseUploadDcShift + index); }; } // namespace internal // 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) { Expects(index >= 0 && index < kUploadSessionsCount); + return internal::uploadDcId(0, index); }; 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) { - auto shift = getDcIdShift(shiftedDcId); - return shiftDcId(bareDcId(shiftedDcId), shift ? (shift + 1) : internal::kDestroyKeyStartDcShift); + const auto shift = GetDcIdShift(shiftedDcId); + return ShiftDcId(BareDcId(shiftedDcId), shift ? (shift + 1) : kDestroyKeyStartDcShift); } enum { diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.cpp b/Telegram/SourceFiles/mtproto/mtp_instance.cpp index 46698ffa01776..0a06c49924b40 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_instance.cpp @@ -460,9 +460,9 @@ void Instance::Private::restart() { } void Instance::Private::restart(ShiftedDcId shiftedDcId) { - auto dcId = bareDcId(shiftedDcId); + auto dcId = BareDcId(shiftedDcId); for (auto &session : _sessions) { - if (bareDcId(session.second->getDcWithShift()) == dcId) { + if (BareDcId(session.second->getDcWithShift()) == dcId) { session.second->restart(); } } @@ -474,9 +474,9 @@ int32 Instance::Private::dcstate(ShiftedDcId shiftedDcId) { return _mainSession->getState(); } - if (!bareDcId(shiftedDcId)) { + if (!BareDcId(shiftedDcId)) { Assert(_mainSession != nullptr); - shiftedDcId += bareDcId(_mainSession->getDcWithShift()); + shiftedDcId += BareDcId(_mainSession->getDcWithShift()); } auto it = _sessions.find(shiftedDcId); @@ -492,9 +492,9 @@ QString Instance::Private::dctransport(ShiftedDcId shiftedDcId) { Assert(_mainSession != nullptr); return _mainSession->transport(); } - if (!bareDcId(shiftedDcId)) { + if (!BareDcId(shiftedDcId)) { Assert(_mainSession != nullptr); - shiftedDcId += bareDcId(_mainSession->getDcWithShift()); + shiftedDcId += BareDcId(_mainSession->getDcWithShift()); } auto it = _sessions.find(shiftedDcId); @@ -583,7 +583,7 @@ void Instance::Private::stopSession(ShiftedDcId shiftedDcId) { void Instance::Private::reInitConnection(DcId dcId) { for (auto &session : _sessions) { - if (bareDcId(session.second->getDcWithShift()) == dcId) { + if (BareDcId(session.second->getDcWithShift()) == dcId) { session.second->reInitConnection(); } } @@ -632,7 +632,7 @@ bool Instance::Private::logoutGuestDone(mtpRequestId requestId) { std::shared_ptr Instance::Private::getDcById(ShiftedDcId shiftedDcId) { auto it = _dcenters.find(shiftedDcId); if (it == _dcenters.cend()) { - auto dcId = bareDcId(shiftedDcId); + auto dcId = BareDcId(shiftedDcId); if (isTemporaryDcId(dcId)) { if (auto realDcId = getRealIdFromTemporaryDcId(dcId)) { dcId = realDcId; @@ -809,7 +809,7 @@ base::optional Instance::Private::changeRequestByDc( if (it->second < 0) { it->second = -newdc; } else { - it->second = shiftDcId(newdc, getDcIdShift(it->second)); + it->second = ShiftDcId(newdc, GetDcIdShift(it->second)); } return it->second; } @@ -1081,7 +1081,7 @@ void Instance::Private::importDone(const MTPauth_Authorization &result, mtpReque //} return; } - auto newdc = bareDcId(*shiftedDcId); + auto newdc = BareDcId(*shiftedDcId); 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); 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. @@ -1203,7 +1203,7 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e _instance->setMainDcId(newdcWithShift); } } else { - newdcWithShift = shiftDcId(newdcWithShift, getDcIdShift(dcWithShift)); + newdcWithShift = ShiftDcId(newdcWithShift, GetDcIdShift(dcWithShift)); } auto request = mtpRequest(); @@ -1255,7 +1255,7 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e } else { 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 (!badGuestDc && _globalHandler.onFail) { (*_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; session->sendPrepared(request); } else { - auto newdc = bareDcId(qAbs(dcWithShift)); + auto newdc = BareDcId(qAbs(dcWithShift)); auto &waiters(_authWaiters[newdc]); if (base::contains(waiters, request->after->requestId)) { if (!base::contains(waiters, requestId)) { @@ -1372,9 +1372,9 @@ not_null Instance::Private::getSession( Assert(_mainSession != nullptr); return _mainSession; } - if (!bareDcId(shiftedDcId)) { + if (!BareDcId(shiftedDcId)) { Assert(_mainSession != nullptr); - shiftedDcId += bareDcId(_mainSession->getDcWithShift()); + shiftedDcId += BareDcId(_mainSession->getDcWithShift()); } auto it = _sessions.find(shiftedDcId); diff --git a/Telegram/SourceFiles/mtproto/session.cpp b/Telegram/SourceFiles/mtproto/session.cpp index 20090d2eeebcc..b12888b294eb1 100644 --- a/Telegram/SourceFiles/mtproto/session.cpp +++ b/Telegram/SourceFiles/mtproto/session.cpp @@ -596,7 +596,7 @@ void Session::tryToReceive() { } } 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()); } } else { diff --git a/Telegram/gyp/lib_export.gyp b/Telegram/gyp/lib_export.gyp index 1205920347dab..d79f43b039c33 100644 --- a/Telegram/gyp/lib_export.gyp +++ b/Telegram/gyp/lib_export.gyp @@ -50,6 +50,8 @@ '<(submodules_loc)/crl/src', ], '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.h', '<(src_loc)/export/export_settings.h',