From c587c011d21059ccaff5f2db0a114f59afc4ffc1 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 2 Jun 2018 17:29:21 +0300 Subject: [PATCH] Start data export in lib_export. --- Telegram/Resources/langs/lang.strings | 6 + Telegram/SourceFiles/base/timer.h | 3 + Telegram/SourceFiles/base/unique_any.h | 14 +- .../codegen/scheme/codegen_scheme.py | 4 +- Telegram/SourceFiles/config.h | 2 - Telegram/SourceFiles/core/basic_types.h | 4 +- Telegram/SourceFiles/core/utils.cpp | 14 +- Telegram/SourceFiles/core/utils.h | 27 +- .../SourceFiles/export/export_controller.cpp | 229 ++++++++++++ .../SourceFiles/export/export_controller.h | 119 ++++++ Telegram/SourceFiles/export/export_pch.cpp | 9 + Telegram/SourceFiles/export/export_pch.h | 15 + Telegram/SourceFiles/export/export_settings.h | 65 ++++ Telegram/SourceFiles/export/view/export.style | 14 + .../export/view/export_view_done.cpp | 51 +++ .../export/view/export_view_done.h | 29 ++ .../view/export_view_panel_controller.cpp | 88 +++++ .../view/export_view_panel_controller.h | 46 +++ .../export/view/export_view_settings.cpp | 141 +++++++ .../export/view/export_view_settings.h | 47 +++ .../SourceFiles/mtproto/concurrent_sender.cpp | 212 +++++++++++ .../SourceFiles/mtproto/concurrent_sender.h | 348 ++++++++++++++++++ Telegram/SourceFiles/mtproto/connection.cpp | 4 +- Telegram/SourceFiles/mtproto/core_types.cpp | 30 ++ Telegram/SourceFiles/mtproto/core_types.h | 51 ++- Telegram/SourceFiles/mtproto/mtp_instance.cpp | 184 ++++----- Telegram/SourceFiles/mtproto/mtp_instance.h | 74 +++- Telegram/SourceFiles/mtproto/rpc_sender.cpp | 34 +- Telegram/SourceFiles/mtproto/rpc_sender.h | 65 ++-- Telegram/SourceFiles/mtproto/session.cpp | 65 +--- Telegram/SourceFiles/mtproto/session.h | 13 - .../SourceFiles/passport/passport_panel.cpp | 11 +- .../passport/passport_panel_controller.cpp | 4 + .../passport/passport_panel_controller.h | 1 + Telegram/SourceFiles/rpl/variable.h | 43 ++- .../SourceFiles/settings/settings_widget.cpp | 5 + .../SourceFiles/ui/widgets/separate_panel.cpp | 21 +- .../SourceFiles/ui/widgets/separate_panel.h | 9 +- .../SourceFiles/window/window_controller.cpp | 20 + .../SourceFiles/window/window_controller.h | 12 + Telegram/gyp/PrecompiledHeader.cmake | 8 +- Telegram/gyp/Telegram.gyp | 5 + Telegram/gyp/codegen_rules.gypi | 16 - Telegram/gyp/crl.gyp | 3 +- Telegram/gyp/lib_export.gyp | 58 +++ Telegram/gyp/lib_scheme.gyp | 58 +++ Telegram/gyp/pch.gypi | 20 + Telegram/gyp/settings_mac.gypi | 1 + Telegram/gyp/telegram_linux.gypi | 2 - Telegram/gyp/telegram_mac.gypi | 2 - Telegram/gyp/telegram_sources.txt | 10 +- Telegram/gyp/telegram_win.gypi | 2 - 52 files changed, 1996 insertions(+), 322 deletions(-) create mode 100644 Telegram/SourceFiles/export/export_controller.cpp create mode 100644 Telegram/SourceFiles/export/export_controller.h create mode 100644 Telegram/SourceFiles/export/export_pch.cpp create mode 100644 Telegram/SourceFiles/export/export_pch.h create mode 100644 Telegram/SourceFiles/export/export_settings.h create mode 100644 Telegram/SourceFiles/export/view/export.style create mode 100644 Telegram/SourceFiles/export/view/export_view_done.cpp create mode 100644 Telegram/SourceFiles/export/view/export_view_done.h create mode 100644 Telegram/SourceFiles/export/view/export_view_panel_controller.cpp create mode 100644 Telegram/SourceFiles/export/view/export_view_panel_controller.h create mode 100644 Telegram/SourceFiles/export/view/export_view_settings.cpp create mode 100644 Telegram/SourceFiles/export/view/export_view_settings.h create mode 100644 Telegram/SourceFiles/mtproto/concurrent_sender.cpp create mode 100644 Telegram/SourceFiles/mtproto/concurrent_sender.h create mode 100644 Telegram/gyp/lib_export.gyp create mode 100644 Telegram/gyp/lib_scheme.gyp create mode 100644 Telegram/gyp/pch.gypi diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index ce4a04b686c5d4..6d35c0574d71f9 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1653,6 +1653,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_passport_error_cant_read" = "Can't read this file. Please choose an image."; "lng_passport_bad_name" = "Please use latin characters only."; +"lng_export_title" = "Personal data export"; +"lng_export_option_info" = "Personal info"; +"lng_export_option_contacts" = "Contacts list"; +"lng_export_option_sessions" = "Sessions list"; +"lng_export_start" = "Export"; + // Wnd specific "lng_wnd_choose_program_menu" = "Choose Default Program..."; diff --git a/Telegram/SourceFiles/base/timer.h b/Telegram/SourceFiles/base/timer.h index f6b83280fff412..033a068cfeb51f 100644 --- a/Telegram/SourceFiles/base/timer.h +++ b/Telegram/SourceFiles/base/timer.h @@ -7,7 +7,10 @@ For license and copyright information please follow this link: */ #pragma once +#include +#include #include "base/observer.h" +#include "base/flat_map.h" namespace base { diff --git a/Telegram/SourceFiles/base/unique_any.h b/Telegram/SourceFiles/base/unique_any.h index a83e90a956259e..c7ff4ea446a6f2 100644 --- a/Telegram/SourceFiles/base/unique_any.h +++ b/Telegram/SourceFiles/base/unique_any.h @@ -69,11 +69,11 @@ class unique_any final { unique_any(const unique_any &other) = delete; unique_any &operator=(const unique_any &other) = delete; - + unique_any(unique_any &&other) noexcept : _impl(std::move(other._impl)) { } - + unique_any &operator=(unique_any &&other) noexcept { _impl = std::move(other._impl); return *this; @@ -88,7 +88,7 @@ class unique_any final { std::forward(other), std::is_copy_constructible>()) { } - + template < typename Value, typename = std::enable_if_t< @@ -106,7 +106,7 @@ class unique_any final { } return *this; } - + template < typename Value, typename ...Args, @@ -143,7 +143,7 @@ class unique_any final { unique_any(Value &&other, std::true_type) : _impl(std::forward(other)) { } - + template < typename Value, typename = std::enable_if_t< @@ -177,7 +177,7 @@ inline void swap(unique_any &a, unique_any &b) noexcept { template < typename Value, typename ...Args> -inline auto make_any(Args &&...args) +inline auto make_any(Args &&...args) -> std::enable_if_t< std::is_copy_constructible_v>, unique_any> { @@ -187,7 +187,7 @@ inline auto make_any(Args &&...args) template < typename Value, typename ...Args> -inline auto make_any(Args &&...args) +inline auto make_any(Args &&...args) -> std::enable_if_t< !std::is_copy_constructible_v> && std::is_move_constructible_v>, diff --git a/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py b/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py index 81a595d7625536..3fa944ca210972 100644 --- a/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py +++ b/Telegram/SourceFiles/codegen/scheme/codegen_scheme.py @@ -635,7 +635,7 @@ def addTextSerializeInit(lst, dct): getters += '\tconst MTPD' + name + ' &c_' + name + '() const;\n'; # const getter constructsBodies += 'const MTPD' + name + ' &MTP' + restype + '::c_' + name + '() const {\n'; if (withType): - constructsBodies += '\tAssert(_type == mtpc_' + name + ');\n'; + constructsBodies += '\tExpects(_type == mtpc_' + name + ');\n\n'; constructsBodies += '\treturn queryData();\n'; constructsBodies += '}\n'; @@ -771,7 +771,7 @@ def addTextSerializeInit(lst, dct): typesText += '\tmtpTypeId type() const;\n'; # type id method methods += 'mtpTypeId MTP' + restype + '::type() const {\n'; if (withType): - methods += '\tAssert(_type != 0);\n'; + methods += '\tExpects(_type != 0);\n\n'; methods += '\treturn _type;\n'; else: methods += '\treturn mtpc_' + v[0][0] + ';\n'; diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 5bcb92472cbd68..e77b81092a93e5 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -26,8 +26,6 @@ enum { MTPKillFileSessionTimeout = 5000, // how much time without upload / download causes additional session kill - MTPDebugBufferSize = 1024 * 1024, // 1 mb start size - MaxUsersPerInvite = 100, // max users in one super group invite request MTPChannelGetDifferenceLimit = 100, diff --git a/Telegram/SourceFiles/core/basic_types.h b/Telegram/SourceFiles/core/basic_types.h index bf2ed209da0c22..9a67d963caaddb 100644 --- a/Telegram/SourceFiles/core/basic_types.h +++ b/Telegram/SourceFiles/core/basic_types.h @@ -12,8 +12,8 @@ For license and copyright information please follow this link: #include #include #include +#include -#include #include "base/build_config.h" #include "base/ordered_set.h" #include "base/unique_function.h" @@ -39,5 +39,7 @@ using uint64 = quint64; using float32 = float; using float64 = double; +using TimeMs = int64; + #define qsl(s) QStringLiteral(s) #define qstr(s) QLatin1String((s), sizeof(s) - 1) diff --git a/Telegram/SourceFiles/core/utils.cpp b/Telegram/SourceFiles/core/utils.cpp index 962e90b2e58dc1..9ea27a7346043c 100644 --- a/Telegram/SourceFiles/core/utils.cpp +++ b/Telegram/SourceFiles/core/utils.cpp @@ -54,11 +54,13 @@ static_assert(sizeof(MTPdouble) == 8, "Basic types size check failed"); // Unixtime functions namespace { + +std::atomic GlobalAtomicRequestId = 0; + QReadWriteLock unixtimeLock; volatile int32 unixtimeDelta = 0; volatile bool unixtimeWasSet = false; volatile uint64 _msgIdStart, _msgIdLocal = 0, _msgIdMsStart; - int32 _reqId = 0; void _initMsgIdConstants() { #ifdef Q_OS_WIN @@ -433,12 +435,12 @@ uint64 msgid() { return result + (_msgIdLocal += 4); } -int32 reqid() { - QWriteLocker locker(&unixtimeLock); - if (_reqId == INT_MAX) { - _reqId = 0; +int GetNextRequestId() { + const auto result = ++GlobalAtomicRequestId; + if (result == std::numeric_limits::max() / 2) { + GlobalAtomicRequestId = 0; } - return ++_reqId; + return result; } // crc32 hash, taken somewhere from the internet diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index b4acd521310a06..be7f3e5952ab9a 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -7,9 +7,12 @@ For license and copyright information please follow this link: */ #pragma once +#include +#include "logs.h" #include "core/basic_types.h" #include "base/flags.h" #include "base/algorithm.h" +#include "base/assertion.h" // Define specializations for QByteArray for Qt 5.3.2, because // QByteArray in Qt 5.3.2 doesn't declare "pointer" subtype. @@ -179,33 +182,12 @@ inline void accumulate_max(T &a, const T &b) { if (a < b) a = b; } template inline void accumulate_min(T &a, const T &b) { if (a > b) a = b; } -class Exception : public std::exception { -public: - Exception(const QString &msg, bool isFatal = true) : _fatal(isFatal), _msg(msg.toUtf8()) { - LOG(("Exception: %1").arg(msg)); - } - bool fatal() const { - return _fatal; - } - - virtual const char *what() const throw() { - return _msg.constData(); - } - virtual ~Exception() throw() { - } - -private: - bool _fatal; - QByteArray _msg; - -}; - using TimeId = int32; void unixtimeInit(); void unixtimeSet(TimeId serverTime, bool force = false); TimeId unixtime(); uint64 msgid(); -int32 reqid(); +int GetNextRequestId(); QDateTime ParseDateTime(TimeId serverTime); @@ -224,7 +206,6 @@ void finish(); } -using TimeMs = int64; bool checkms(); // returns true if time has changed TimeMs getms(bool checked = false); diff --git a/Telegram/SourceFiles/export/export_controller.cpp b/Telegram/SourceFiles/export/export_controller.cpp new file mode 100644 index 00000000000000..d1898284af340b --- /dev/null +++ b/Telegram/SourceFiles/export/export_controller.cpp @@ -0,0 +1,229 @@ +/* +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_controller.h" + +#include "export/export_settings.h" +#include "mtproto/rpc_sender.h" +#include "mtproto/concurrent_sender.h" + +namespace Export { + +class Controller { +public: + Controller(crl::weak_on_queue weak); + + rpl::producer state() const; + + // Password step. + void submitPassword(const QString &password); + void requestPasswordRecover(); + rpl::producer passwordUpdate() const; + void reloadPasswordState(); + void cancelUnconfirmedPassword(); + + // Processing step. + void startExport(const Settings &settings); + +private: + void setState(State &&state); + void apiError(const RPCError &error); + void apiError(const QString &error); + void ioError(const QString &path); + void setFinishedState(); + + void requestPasswordState(); + void passwordStateDone(const MTPaccount_Password &password); + + MTP::ConcurrentSender _mtp; + Settings _settings; + + // rpl::variable fails to compile in MSVC :( + State _state; + rpl::event_stream _stateChanges; + + mtpRequestId _passwordRequestId = 0; + +}; + +Controller::Controller(crl::weak_on_queue weak) +: _mtp(weak) +, _state(PasswordCheckState{}) { + requestPasswordState(); +} + +rpl::producer Controller::state() const { + return rpl::single( + _state + ) | rpl::then( + _stateChanges.events() + ) | rpl::filter([](const State &state) { + const auto password = base::get_if(&state); + return !password || !password->requesting; + }); +} + +void Controller::setState(State &&state) { + _state = std::move(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 }); +} + +void Controller::submitPassword(const QString &password) { + +} + +void Controller::requestPasswordRecover() { + +} + +rpl::producer Controller::passwordUpdate() const { + return rpl::never(); +} + +void Controller::reloadPasswordState() { + _mtp.request(base::take(_passwordRequestId)).cancel(); + requestPasswordState(); +} + +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(); +} + +void Controller::passwordStateDone(const MTPaccount_Password &result) { + auto state = PasswordCheckState(); + state.checked = false; + state.requesting = false; + state.hasPassword; + state.hint; + state.unconfirmedPattern; + setState(std::move(state)); +} + +void Controller::cancelUnconfirmedPassword() { + +} + +void Controller::startExport(const Settings &settings) { + _settings = base::duplicate(settings); + setState(ProcessingState()); + + _mtp.request(MTPusers_GetFullUser( + MTP_inputUserSelf() + )).done([=](const MTPUserFull &result) { + Expects(result.type() == mtpc_userFull); + + const auto &full = result.c_userFull(); + if (full.vuser.type() != mtpc_user) { + apiError("Bad user type."); + return; + } + const auto &user = full.vuser.c_user(); + + QFile f(_settings.path + "personal.txt"); + if (!f.open(QIODevice::WriteOnly)) { + ioError(f.fileName()); + return; + } + QTextStream stream(&f); + stream.setCodec("UTF-8"); + if (user.has_first_name()) { + stream << "First name: " << qs(user.vfirst_name) << "\n"; + } + if (user.has_last_name()) { + stream << "Last name: " << qs(user.vlast_name) << "\n"; + } + if (user.has_phone()) { + stream << "Phone number: " << qs(user.vphone) << "\n"; + } + if (user.has_username()) { + stream << "Username: @" << qs(user.vusername) << "\n"; + } + setFinishedState(); + }).fail([=](const RPCError &error) { + apiError(error); + }).send(); +} + +void Controller::setFinishedState() { + setState(FinishedState{ _settings.path }); +} + +ControllerWrap::ControllerWrap() { +} + +rpl::producer ControllerWrap::state() const { + return _wrapped.producer_on_main([=](const Controller &controller) { + return controller.state(); + }); +} + +void ControllerWrap::submitPassword(const QString &password) { + _wrapped.with([=](Controller &controller) { + controller.submitPassword(password); + }); +} + +void ControllerWrap::requestPasswordRecover() { + _wrapped.with([=](Controller &controller) { + controller.requestPasswordRecover(); + }); +} + +rpl::producer ControllerWrap::passwordUpdate() const { + return _wrapped.producer_on_main([=](const Controller &controller) { + return controller.passwordUpdate(); + }); +} + +void ControllerWrap::reloadPasswordState() { + _wrapped.with([=](Controller &controller) { + controller.reloadPasswordState(); + }); +} + +void ControllerWrap::cancelUnconfirmedPassword() { + _wrapped.with([=](Controller &controller) { + controller.cancelUnconfirmedPassword(); + }); +} + +void ControllerWrap::startExport(const Settings &settings) { + LOG(("Export Info: Started export to '%1'.").arg(settings.path)); + + _wrapped.with([=](Controller &controller) { + controller.startExport(settings); + }); +} + +rpl::lifetime &ControllerWrap::lifetime() { + return _lifetime; +} + +ControllerWrap::~ControllerWrap() = default; + +} // namespace Export diff --git a/Telegram/SourceFiles/export/export_controller.h b/Telegram/SourceFiles/export/export_controller.h new file mode 100644 index 00000000000000..7e10a89129ac1a --- /dev/null +++ b/Telegram/SourceFiles/export/export_controller.h @@ -0,0 +1,119 @@ +/* +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 +#include "base/variant.h" +#include "mtproto/rpc_sender.h" + +namespace Export { + +class Controller; +struct Settings; + +struct PasswordCheckState { + QString hint; + QString unconfirmedPattern; + bool requesting = true; + bool hasPassword = false; + bool checked = false; + +}; + +struct ProcessingState { + enum class Step { + PersonalInfo, + Avatars, + Contacts, + Sessions, + Chats, + }; + enum class Item { + Other, + Photo, + Video, + File, + }; + + Step step = Step::PersonalInfo; + + int entityIndex = 0; + int entityCount = 1; + QString entityName; + + int itemIndex = 0; + int itemCount = 0; + Item itemType = Item::Other; + QString itemName; + + int bytesLoaded = 0; + int bytesCount = 0; + +}; + +struct ErrorState { + enum class Type { + Unknown, + API, + IO, + }; + Type type = Type::Unknown; + base::optional apiError; + base::optional ioErrorPath; + +}; + +struct FinishedState { + QString path; + +}; + +using State = base::optional_variant< + PasswordCheckState, + ProcessingState, + ErrorState, + FinishedState>; + +struct PasswordUpdate { + enum class Type { + CheckSucceed, + WrongPassword, + FloodLimit, + RecoverUnavailable, + }; + Type type = Type::WrongPassword; + +}; + +class ControllerWrap { +public: + ControllerWrap(); + + rpl::producer state() const; + + // Password step. + void submitPassword(const QString &password); + void requestPasswordRecover(); + rpl::producer passwordUpdate() const; + void reloadPasswordState(); + void cancelUnconfirmedPassword(); + + // Processing step. + void startExport(const Settings &settings); + + rpl::lifetime &lifetime(); + + ~ControllerWrap(); + +private: + crl::object_on_queue _wrapped; + rpl::lifetime _lifetime; + +}; + +} // namespace Export diff --git a/Telegram/SourceFiles/export/export_pch.cpp b/Telegram/SourceFiles/export/export_pch.cpp new file mode 100644 index 00000000000000..8fc9994328a1e3 --- /dev/null +++ b/Telegram/SourceFiles/export/export_pch.cpp @@ -0,0 +1,9 @@ +/* +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_pch.h" + diff --git a/Telegram/SourceFiles/export/export_pch.h b/Telegram/SourceFiles/export/export_pch.h new file mode 100644 index 00000000000000..62a6a0222e007e --- /dev/null +++ b/Telegram/SourceFiles/export/export_pch.h @@ -0,0 +1,15 @@ +/* +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 +#include +#include +#include +#include +#include "scheme.h" +#include "logs.h" diff --git a/Telegram/SourceFiles/export/export_settings.h b/Telegram/SourceFiles/export/export_settings.h new file mode 100644 index 00000000000000..33ebbfb0e90bd2 --- /dev/null +++ b/Telegram/SourceFiles/export/export_settings.h @@ -0,0 +1,65 @@ +/* +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 "base/flags.h" +#include "base/flat_map.h" + +namespace Export { + +struct MediaSettings { + enum class Type { + Photo, + Video, + Sticker, + GIF, + File, + }; + using Types = base::flags; + friend inline constexpr auto is_flag_type(Type) { return true; }; + + Types types = DefaultTypes(); + int sizeLimit = 8 * 1024 * 1024; + + static inline Types DefaultTypes() { + return Type::Photo; + } + +}; + +struct Settings { + enum class Type { + PersonalInfo, + Avatars, + Contacts, + Sessions, + PersonalChats, + PrivateGroups, + PublicGroups, + MyChannels, + }; + using Types = base::flags; + friend inline constexpr auto is_flag_type(Type) { return true; }; + + QString path; + + Types types = DefaultTypes(); + MediaSettings defaultMedia; + base::flat_map customMedia; + + static inline Types DefaultTypes() { + return Type::PersonalInfo + | Type::Avatars + | Type::Contacts + | Type::Sessions + | Type::PersonalChats; + } + +}; + +} // namespace Export diff --git a/Telegram/SourceFiles/export/view/export.style b/Telegram/SourceFiles/export/view/export.style new file mode 100644 index 00000000000000..ecbcc27631cb43 --- /dev/null +++ b/Telegram/SourceFiles/export/view/export.style @@ -0,0 +1,14 @@ +/* +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 +*/ +using "basic.style"; + +using "ui/widgets/widgets.style"; +using "boxes/boxes.style"; + +exportPanelSize: size(364px, 480px); +exportSettingPadding: margins(22px, 8px, 22px, 8px); diff --git a/Telegram/SourceFiles/export/view/export_view_done.cpp b/Telegram/SourceFiles/export/view/export_view_done.cpp new file mode 100644 index 00000000000000..33f6882a00a6eb --- /dev/null +++ b/Telegram/SourceFiles/export/view/export_view_done.cpp @@ -0,0 +1,51 @@ +/* +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/view/export_view_done.h" + +#include "lang/lang_keys.h" +#include "ui/widgets/labels.h" +#include "ui/wrap/vertical_layout.h" +#include "platform/platform_specific.h" +#include "styles/style_widgets.h" +#include "styles/style_export.h" +#include "styles/style_boxes.h" + +namespace Export { +namespace View { + +DoneWidget::DoneWidget(QWidget *parent) +: RpWidget(parent) { + setupContent(); +} + +void DoneWidget::setupContent() { + const auto content = Ui::CreateChild(this); + + const auto label = content->add( + object_ptr( + content, + "Done! " + textcmdLink(1, "Press here") + " to view your data.", + Ui::FlatLabel::InitType::Rich, + st::defaultFlatLabel), + st::exportSettingPadding); + label->setLink(1, std::make_shared([=] { + _showClicks.fire({}); + })); + + sizeValue( + ) | rpl::start_with_next([=](QSize size) { + content->resizeToWidth(size.width()); + }, lifetime()); +} + +rpl::producer<> DoneWidget::showClicks() const { + return _showClicks.events(); +} + +} // namespace View +} // namespace Export diff --git a/Telegram/SourceFiles/export/view/export_view_done.h b/Telegram/SourceFiles/export/view/export_view_done.h new file mode 100644 index 00000000000000..0aaed85d1b3716 --- /dev/null +++ b/Telegram/SourceFiles/export/view/export_view_done.h @@ -0,0 +1,29 @@ +/* +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 "ui/rp_widget.h" + +namespace Export { +namespace View { + +class DoneWidget : public Ui::RpWidget { +public: + DoneWidget(QWidget *parent); + + rpl::producer<> showClicks() const; + +private: + void setupContent(); + + rpl::event_stream<> _showClicks; + +}; + +} // namespace View +} // namespace Export diff --git a/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp b/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp new file mode 100644 index 00000000000000..5996d510378da8 --- /dev/null +++ b/Telegram/SourceFiles/export/view/export_view_panel_controller.cpp @@ -0,0 +1,88 @@ +/* +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/view/export_view_panel_controller.h" + +#include "export/view/export_view_settings.h" +#include "export/view/export_view_done.h" +#include "ui/widgets/separate_panel.h" +#include "lang/lang_keys.h" +#include "core/file_utilities.h" +#include "styles/style_export.h" + +namespace Export { +namespace View { + +PanelController::PanelController(not_null process) +: _process(process) { + _process->state( + ) | rpl::start_with_next([=](State &&state) { + updateState(std::move(state)); + }, _lifetime); +} + +void PanelController::createPanel() { + _panel = base::make_unique_q(); + _panel->setTitle(Lang::Viewer(lng_export_title)); + _panel->setInnerSize(st::exportPanelSize); + _panel->closeRequests( + ) | rpl::start_with_next([=] { + _panel->hideGetDuration(); + }, _panel->lifetime()); + _panelCloseEvents.fire(_panel->closeEvents()); + + showSettings(); +} + +void PanelController::showSettings() { + auto settings = base::make_unique_q(_panel); + + settings->startClicks( + ) | rpl::start_with_next([=](const Settings &settings) { + _process->startExport(settings); + }, settings->lifetime()); + + settings->cancelClicks( + ) | rpl::start_with_next([=] { + _panel->hideGetDuration(); + }, settings->lifetime()); + + _panel->showInner(std::move(settings)); +} + +rpl::producer<> PanelController::closed() const { + return _panelCloseEvents.events( + ) | rpl::flatten_latest( + ) | rpl::filter([=] { + return !_state.is(); + }); +} + +void PanelController::updateState(State &&state) { + if (!_panel) { + createPanel(); + } + _state = std::move(state); + if (const auto finished = base::get_if(&_state)) { + const auto path = finished->path; + + auto done = base::make_unique_q(_panel.get()); + + done->showClicks( + ) | rpl::start_with_next([=] { + File::ShowInFolder(path + "personal.txt"); + _panel->hideGetDuration(); + }, done->lifetime()); + + _panel->showInner(std::move(done)); + } +} + +PanelController::~PanelController() = default; + +} // namespace View +} // namespace Export diff --git a/Telegram/SourceFiles/export/view/export_view_panel_controller.h b/Telegram/SourceFiles/export/view/export_view_panel_controller.h new file mode 100644 index 00000000000000..9048a9004bd3b6 --- /dev/null +++ b/Telegram/SourceFiles/export/view/export_view_panel_controller.h @@ -0,0 +1,46 @@ +/* +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 "export/export_controller.h" +#include "base/unique_qptr.h" + +namespace Ui { +class SeparatePanel; +} // namespace Ui + +namespace Export { +namespace View { + +class Panel; + +class PanelController { +public: + PanelController(not_null process); + + rpl::producer<> closed() const; + + ~PanelController(); + +private: + void createPanel(); + void updateState(State &&state); + void showSettings(); + + not_null _process; + + base::unique_qptr _panel; + + State _state; + rpl::event_stream> _panelCloseEvents; + rpl::lifetime _lifetime; + +}; + +} // namespace View +} // namespace Export diff --git a/Telegram/SourceFiles/export/view/export_view_settings.cpp b/Telegram/SourceFiles/export/view/export_view_settings.cpp new file mode 100644 index 00000000000000..986283d89ac27c --- /dev/null +++ b/Telegram/SourceFiles/export/view/export_view_settings.cpp @@ -0,0 +1,141 @@ +/* +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/view/export_view_settings.h" + +#include "lang/lang_keys.h" +#include "ui/widgets/checkbox.h" +#include "ui/widgets/buttons.h" +#include "ui/wrap/vertical_layout.h" +#include "ui/wrap/padding_wrap.h" +#include "platform/platform_specific.h" +#include "styles/style_widgets.h" +#include "styles/style_export.h" +#include "styles/style_boxes.h" + +namespace Export { +namespace View { + +SettingsWidget::SettingsWidget(QWidget *parent) +: RpWidget(parent) { + if (Global::DownloadPath().isEmpty()) { + _data.path = psDownloadPath(); + } else if (Global::DownloadPath() == qsl("tmp")) { + _data.path = cTempDir(); + } else { + _data.path = Global::DownloadPath(); + } + setupContent(); +} + +void SettingsWidget::setupContent() { + const auto content = Ui::CreateChild(this); + + const auto buttonsPadding = st::boxButtonPadding; + const auto buttonsHeight = buttonsPadding.top() + + st::defaultBoxButton.height + + buttonsPadding.bottom(); + const auto buttons = Ui::CreateChild( + this, + buttonsHeight); + const auto refreshButtonsCallback = [=] { + refreshButtons(buttons); + }; + + const auto addOption = [&](LangKey key, Types types) { + const auto checkbox = content->add( + object_ptr( + content, + lang(key), + ((_data.types & types) == types), + st::defaultBoxCheckbox), + st::exportSettingPadding); + base::ObservableViewer( + checkbox->checkedChanged + ) | rpl::start_with_next([=](bool checked) { + if (checked) { + _data.types |= types; + } else { + _data.types &= ~types; + } + refreshButtonsCallback(); + }, lifetime()); + }; + addOption(lng_export_option_info, Type::PersonalInfo | Type::Avatars); + addOption(lng_export_option_contacts, Type::Contacts); + addOption(lng_export_option_sessions, Type::Sessions); + refreshButtonsCallback(); + + sizeValue( + ) | rpl::start_with_next([=](QSize size) { + content->resizeToWidth(size.width()); + buttons->resizeToWidth(size.width()); + buttons->moveToLeft(0, size.height() - buttons->height()); + }, lifetime()); +} + +void SettingsWidget::refreshButtons(not_null container) { + container->hideChildren(); + const auto children = container->children(); + for (const auto child : children) { + if (child->isWidgetType()) { + child->deleteLater(); + } + } + const auto start = _data.types + ? Ui::CreateChild( + container.get(), + langFactory(lng_export_start), + st::defaultBoxButton) + : nullptr; + if (start) { + _startClicks = start->clicks(); + + container->sizeValue( + ) | rpl::start_with_next([=](QSize size) { + const auto right = st::boxButtonPadding.right(); + const auto top = st::boxButtonPadding.top(); + start->moveToRight(right, top); + }, start->lifetime()); + } + + const auto cancel = Ui::CreateChild( + container.get(), + langFactory(lng_cancel), + st::defaultBoxButton); + _cancelClicks = cancel->clicks(); + + rpl::combine( + container->sizeValue(), + start ? start->widthValue() : rpl::single(0) + ) | rpl::start_with_next([=](QSize size, int width) { + const auto right = st::boxButtonPadding.right() + + (width ? width + st::boxButtonPadding.left() : 0); + const auto top = st::boxButtonPadding.top(); + cancel->moveToRight(right, top); + }, cancel->lifetime()); +} + +rpl::producer SettingsWidget::startClicks() const { + return _startClicks.value( + ) | rpl::map([](Wrap &&wrap) { + return std::move(wrap.value); + }) | rpl::flatten_latest( + ) | rpl::map([=] { + return _data; + }); +} + +rpl::producer<> SettingsWidget::cancelClicks() const { + return _cancelClicks.value( + ) | rpl::map([](Wrap &&wrap) { + return std::move(wrap.value); + }) | rpl::flatten_latest(); +} + +} // namespace View +} // namespace Export diff --git a/Telegram/SourceFiles/export/view/export_view_settings.h b/Telegram/SourceFiles/export/view/export_view_settings.h new file mode 100644 index 00000000000000..b0ec348231e4f5 --- /dev/null +++ b/Telegram/SourceFiles/export/view/export_view_settings.h @@ -0,0 +1,47 @@ +/* +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 "export/export_settings.h" +#include "ui/rp_widget.h" + +namespace Export { +namespace View { + +class SettingsWidget : public Ui::RpWidget { +public: + SettingsWidget(QWidget *parent); + + rpl::producer startClicks() const; + rpl::producer<> cancelClicks() const; + +private: + using Type = Settings::Type; + using Types = Settings::Types; + using MediaType = MediaSettings::Type; + using MediaTypes = MediaSettings::Types; + + void setupContent(); + void refreshButtons(not_null container); + + Settings _data; + struct Wrap { + Wrap(rpl::producer<> value = rpl::never<>()) + : value(std::move(value)) { + } + + rpl::producer<> value; + + }; + rpl::variable _startClicks; + rpl::variable _cancelClicks; + +}; + +} // namespace View +} // namespace Export diff --git a/Telegram/SourceFiles/mtproto/concurrent_sender.cpp b/Telegram/SourceFiles/mtproto/concurrent_sender.cpp new file mode 100644 index 00000000000000..96ca6c1aaaffc1 --- /dev/null +++ b/Telegram/SourceFiles/mtproto/concurrent_sender.cpp @@ -0,0 +1,212 @@ +/* +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 "mtproto/concurrent_sender.h" + +#include "mtproto/mtp_instance.h" +#include "mtproto/rpc_sender.h" + +namespace MTP { + +class ConcurrentSender::RPCDoneHandler : public RPCAbstractDoneHandler { +public: + RPCDoneHandler( + not_null sender, + Fn)> run); + + void operator()( + mtpRequestId requestId, + const mtpPrime *from, + const mtpPrime *end) override; + +private: + base::weak_ptr _weak; + Fn)> _run; + +}; + +class ConcurrentSender::RPCFailHandler : public RPCAbstractFailHandler { +public: + RPCFailHandler( + not_null sender, + Fn)> run, + FailSkipPolicy skipPolicy); + + bool operator()( + mtpRequestId requestId, + const RPCError &error) override; + +private: + base::weak_ptr _weak; + Fn)> _run; + FailSkipPolicy _skipPolicy = FailSkipPolicy::Simple; + +}; + +ConcurrentSender::RPCDoneHandler::RPCDoneHandler( + not_null sender, + Fn)> run) +: _weak(sender) +, _run(std::move(run)) { +} + +void ConcurrentSender::RPCDoneHandler::operator()( + mtpRequestId requestId, + const mtpPrime *from, + const mtpPrime *end) { + auto response = gsl::make_span( + from, + end - from); + _run([=, weak = _weak, moved = bytes::make_vector(response)]() mutable { + if (const auto strong = weak.get()) { + strong->senderRequestDone(requestId, std::move(moved)); + } + }); +} + +ConcurrentSender::RPCFailHandler::RPCFailHandler( + not_null sender, + Fn)> run, + FailSkipPolicy skipPolicy) +: _weak(sender) +, _run(std::move(run)) +, _skipPolicy(skipPolicy) { +} + +bool ConcurrentSender::RPCFailHandler::operator()( + mtpRequestId requestId, + const RPCError &error) { + if (_skipPolicy == FailSkipPolicy::Simple) { + if (MTP::isDefaultHandledError(error)) { + return false; + } + } else if (_skipPolicy == FailSkipPolicy::HandleFlood) { + if (MTP::isDefaultHandledError(error) && !MTP::isFloodError(error)) { + return false; + } + } + _run([=, weak = _weak, error = error]() mutable { + if (const auto strong = weak.get()) { + strong->senderRequestFail(requestId, std::move(error)); + } + }); + return true; +} + +template +auto ConcurrentSender::with_instance(Method &&method) +-> std::enable_if_t>> { + crl::on_main([method = std::forward(method)]() mutable { + if (const auto instance = MainInstance()) { + std::move(method)(instance); + } + }); +} + +ConcurrentSender::RequestBuilder::RequestBuilder( + not_null sender, + mtpRequest &&serialized) noexcept +: _sender(sender) +, _serialized(std::move(serialized)) { +} + +void ConcurrentSender::RequestBuilder::setToDC(ShiftedDcId dcId) noexcept { + _dcId = dcId; +} + +void ConcurrentSender::RequestBuilder::setCanWait(TimeMs ms) noexcept { + _canWait = ms; +} + +void ConcurrentSender::RequestBuilder::setFailSkipPolicy( + FailSkipPolicy policy) noexcept { + _failSkipPolicy = policy; +} + +void ConcurrentSender::RequestBuilder::setAfter( + mtpRequestId requestId) noexcept { + _afterRequestId = requestId; +} + +mtpRequestId ConcurrentSender::RequestBuilder::send() { + const auto requestId = GetNextRequestId(); + const auto dcId = _dcId; + const auto msCanWait = _canWait; + const auto afterRequestId = _afterRequestId; + + _sender->senderRequestRegister(requestId, std::move(_handlers)); + _sender->with_instance([ + =, + request = std::move(_serialized), + done = std::make_shared(_sender, _sender->_run), + fail = std::make_shared( + _sender, + _sender->_run, + _failSkipPolicy) + ](not_null instance) mutable { + instance->sendSerialized( + requestId, + std::move(request), + RPCResponseHandler(std::move(done), std::move(fail)), + dcId, + msCanWait, + afterRequestId); + }); + + return requestId; +} + +ConcurrentSender::ConcurrentSender(Fn)> run) +: _run(run) { +} + +ConcurrentSender::~ConcurrentSender() { + senderRequestCancelAll(); +} + +void ConcurrentSender::senderRequestRegister( + mtpRequestId requestId, + Handlers &&handlers) { + _requests.emplace(requestId, std::move(handlers)); +} + +void ConcurrentSender::senderRequestDone( + mtpRequestId requestId, + bytes::const_span result) { + if (auto handlers = _requests.take(requestId)) { + std::move(handlers->done)(requestId, result); + } +} + +void ConcurrentSender::senderRequestFail( + mtpRequestId requestId, + RPCError &&error) { + if (auto handlers = _requests.take(requestId)) { + std::move(handlers->fail)(requestId, std::move(error)); + } +} + +void ConcurrentSender::senderRequestCancel(mtpRequestId requestId) { + _requests.erase(requestId); + with_instance([=](not_null instance) { + instance->cancel(requestId); + }); +} + +void ConcurrentSender::senderRequestCancelAll() { + auto list = std::vector(_requests.size()); + for (const auto &[requestId, handlers] : base::take(_requests)) { + list.push_back(requestId); + } + with_instance([list = std::move(list)](not_null instance) { + for (const auto requestId : list) { + instance->cancel(requestId); + } + }); +} + +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/concurrent_sender.h b/Telegram/SourceFiles/mtproto/concurrent_sender.h new file mode 100644 index 00000000000000..b7af429766636f --- /dev/null +++ b/Telegram/SourceFiles/mtproto/concurrent_sender.h @@ -0,0 +1,348 @@ +/* +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 +#include +#include "base/bytes.h" +#include "base/weak_ptr.h" +#include "base/flat_map.h" +#include "mtproto/core_types.h" + +class RPCError; + +namespace MTP { + +class Instance; + +class ConcurrentSender : public base::has_weak_ptr { + template + static constexpr bool is_callable_v = rpl::details::is_callable_v; + + template + auto with_instance(Method &&method) + -> std::enable_if_t>>; + + using DoneHandler = FnMut; + using FailHandler = FnMut; + struct Handlers { + DoneHandler done; + FailHandler fail; + }; + + enum class FailSkipPolicy { + Simple, + HandleFlood, + HandleAll, + }; + + class RequestBuilder { + public: + RequestBuilder(const RequestBuilder &other) = delete; + RequestBuilder(RequestBuilder &&other) = default; + RequestBuilder &operator=(const RequestBuilder &other) = delete; + RequestBuilder &operator=(RequestBuilder &&other) = delete; + + mtpRequestId send(); + + protected: + RequestBuilder( + not_null sender, + mtpRequest &&serialized) noexcept; + + void setToDC(ShiftedDcId dcId) noexcept; + void setCanWait(TimeMs ms) noexcept; + template + void setDoneHandler(InvokeFullDone &&invoke) noexcept; + template + void setFailHandler(InvokeFullFail &&invoke) noexcept; + void setFailSkipPolicy(FailSkipPolicy policy) noexcept; + void setAfter(mtpRequestId requestId) noexcept; + + private: + not_null _sender; + mtpRequest _serialized; + ShiftedDcId _dcId = 0; + TimeMs _canWait = 0; + + Handlers _handlers; + FailSkipPolicy _failSkipPolicy = FailSkipPolicy::Simple; + mtpRequestId _afterRequestId = 0; + + }; + +public: + ConcurrentSender(Fn)> run); + + template + ConcurrentSender(const crl::weak_on_queue &weak); + + template + class SpecificRequestBuilder : public RequestBuilder { + public: + SpecificRequestBuilder( + const SpecificRequestBuilder &other) = delete; + SpecificRequestBuilder( + SpecificRequestBuilder &&other) = default; + SpecificRequestBuilder &operator=( + const SpecificRequestBuilder &other) = delete; + SpecificRequestBuilder &operator=( + SpecificRequestBuilder &&other) = delete; + + [[nodiscard]] SpecificRequestBuilder &toDC( + ShiftedDcId dcId) noexcept; + [[nodiscard]] SpecificRequestBuilder &afterDelay( + TimeMs ms) noexcept; + template + [[nodiscard]] SpecificRequestBuilder &done(Handler &&handler); + template + [[nodiscard]] SpecificRequestBuilder &fail(Handler &&handler); + [[nodiscard]] SpecificRequestBuilder &handleFloodErrors() noexcept; + [[nodiscard]] SpecificRequestBuilder &handleAllErrors() noexcept; + [[nodiscard]] SpecificRequestBuilder &afterRequest( + mtpRequestId requestId) noexcept; + + private: + SpecificRequestBuilder( + not_null sender, + Request &&request) noexcept; + + friend class ConcurrentSender; + + }; + + class SentRequestWrap { + public: + void cancel() { + _sender->senderRequestCancel(_requestId); + } + + private: + friend class ConcurrentSender; + SentRequestWrap(not_null sender, mtpRequestId requestId) : _sender(sender), _requestId(requestId) { + } + not_null _sender; + mtpRequestId _requestId = 0; + + }; + + template < + typename Request, + typename = std::enable_if_t>, + typename = typename Request::Unboxed> + [[nodiscard]] SpecificRequestBuilder request( + Request &&request) noexcept; + + [[nodiscard]] SentRequestWrap request(mtpRequestId requestId) noexcept; + + [[nodiscard]] auto requestCanceller() noexcept { + return [=](mtpRequestId requestId) { + request(requestId).cancel(); + }; + } + + ~ConcurrentSender(); + +private: + class RPCDoneHandler; + friend class RPCDoneHandler; + class RPCFailHandler; + friend class RPCFailHandler; + friend class RequestBuilder; + friend class SentRequestWrap; + + void senderRequestRegister(mtpRequestId requestId, Handlers &&handlers); + void senderRequestDone( + mtpRequestId requestId, + bytes::const_span result); + void senderRequestFail( + mtpRequestId requestId, + RPCError &&error); + void senderRequestCancel(mtpRequestId requestId); + void senderRequestCancelAll(); + + const Fn)> _run; + 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 { + _handlers.done = [handler = std::move(invoke)]( + mtpRequestId requestId, + bytes::const_span result) mutable { + try { + auto from = reinterpret_cast(result.data()); + const auto end = from + result.size() / sizeof(mtpPrime); + Response data; + data.read(from, end); + std::move(handler)(requestId, std::move(data)); + } catch (...) { + } + }; +} + +template +void ConcurrentSender::RequestBuilder::setFailHandler( + InvokeFullFail &&invoke) noexcept { + _handlers.fail = std::move(invoke); +} + +template +ConcurrentSender::SpecificRequestBuilder::SpecificRequestBuilder( + not_null sender, + Request &&request) noexcept +: RequestBuilder(sender, mtpRequestData::serialize(request)) { +} + +template +[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder::toDC( + ShiftedDcId dcId) noexcept -> SpecificRequestBuilder & { + setToDC(dcId); + return *this; +} + +template +[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder::afterDelay( + TimeMs ms) noexcept -> SpecificRequestBuilder & { + setCanWait(ms); + return *this; +} + +template +template +[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder::done( + Handler &&handler) -> SpecificRequestBuilder & { + using Response = typename Request::ResponseType; + constexpr auto takesFull = rpl::details::is_callable_plain_v< + Handler, + mtpRequestId, + Response>; + constexpr auto takesResponse = rpl::details::is_callable_plain_v< + Handler, + Response>; + constexpr auto takesRequestId = rpl::details::is_callable_plain_v< + Handler, + mtpRequestId>; + constexpr auto takesNone = rpl::details::is_callable_plain_v; + + if constexpr (takesFull) { + setDoneHandler(std::forward(handler)); + } else if constexpr (takesResponse) { + setDoneHandler([handler = std::forward(handler)]( + mtpRequestId requestId, + Response &&result) mutable { + std::move(handler)(std::move(result)); + }); + } else if constexpr (takesRequestId) { + setDoneHandler([handler = std::forward(handler)]( + mtpRequestId requestId, + Response &&result) mutable { + std::move(handler)(requestId); + }); + } else if constexpr (takesNone) { + setDoneHandler([handler = std::forward(handler)]( + mtpRequestId requestId, + Response &&result) mutable { + std::move(handler)(); + }); + } else { + static_assert(false_t(Handler{}), "Bad done handler."); + } + return *this; +} + +template +template +[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder::fail( + Handler &&handler) -> SpecificRequestBuilder & { + constexpr auto takesFull = rpl::details::is_callable_plain_v< + Handler, + mtpRequestId, + RPCError>; + constexpr auto takesError = rpl::details::is_callable_plain_v< + Handler, + RPCError>; + constexpr auto takesRequestId = rpl::details::is_callable_plain_v< + Handler, + mtpRequestId>; + constexpr auto takesNone = rpl::details::is_callable_plain_v; + + if constexpr (takesFull) { + setFailHandler(std::forward(handler)); + } else if constexpr (takesError) { + setFailHandler([handler = std::forward(handler)]( + mtpRequestId requestId, + RPCError &&error) mutable { + std::move(handler)(std::move(error)); + }); + } else if constexpr (takesRequestId) { + setFailHandler([handler = std::forward(handler)]( + mtpRequestId requestId, + RPCError &&error) mutable { + std::move(handler)(requestId); + }); + } else if constexpr (takesNone) { + setFailHandler([handler = std::forward(handler)]( + mtpRequestId requestId, + RPCError &&error) mutable { + std::move(handler)(); + }); + } else { + static_assert(false_t(Handler{}), "Bad fail handler."); + } + return *this; +} + +template +[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder::handleFloodErrors() noexcept +-> SpecificRequestBuilder & { + setFailSkipPolicy(FailSkipPolicy::HandleFlood); + return *this; +} + +template +[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder::handleAllErrors() noexcept +-> SpecificRequestBuilder & { + setFailSkipPolicy(FailSkipPolicy::HandleAll); + return *this; +} + +template +[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder::afterRequest( + mtpRequestId requestId) noexcept -> SpecificRequestBuilder & { + setAfter(requestId); + return *this; +} + +template +inline auto ConcurrentSender::request(Request &&request) noexcept +-> SpecificRequestBuilder { + return SpecificRequestBuilder(this, std::move(request)); +} + +inline auto ConcurrentSender::request(mtpRequestId requestId) noexcept +-> SentRequestWrap { + return SentRequestWrap(this, requestId); +} + +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp index b23e56b01febb9..e53bda6e2514f1 100644 --- a/Telegram/SourceFiles/mtproto/connection.cpp +++ b/Telegram/SourceFiles/mtproto/connection.cpp @@ -445,7 +445,7 @@ ConnectionPrivate::ConnectionPrivate( connect(this, SIGNAL(sendMsgsStateInfoAsync(quint64, QByteArray)), sessionData->owner(), SLOT(sendMsgsStateInfo(quint64,QByteArray)), Qt::QueuedConnection); connect(this, SIGNAL(resendAsync(quint64,qint64,bool,bool)), sessionData->owner(), SLOT(resend(quint64,qint64,bool,bool)), Qt::QueuedConnection); connect(this, SIGNAL(resendManyAsync(QVector,qint64,bool,bool)), sessionData->owner(), SLOT(resendMany(QVector,qint64,bool,bool)), Qt::QueuedConnection); - connect(this, SIGNAL(resendAllAsync()), sessionData->owner(), SLOT(resendAll())); + connect(this, SIGNAL(resendAllAsync()), sessionData->owner(), SLOT(resendAll()), Qt::QueuedConnection); } void ConnectionPrivate::onConfigLoaded() { @@ -806,7 +806,7 @@ void ConnectionPrivate::tryToSend() { req.write(*stateRequest); stateRequest->msDate = getms(true); // > 0 - can send without container - stateRequest->requestId = reqid();// add to haveSent / wereAcked maps, but don't add to requestMap + stateRequest->requestId = GetNextRequestId();// add to haveSent / wereAcked maps, but don't add to requestMap } if (_connection->usingHttpWait()) { MTPHttpWait req(MTP_http_wait(MTP_int(100), MTP_int(30), MTP_int(25000))); diff --git a/Telegram/SourceFiles/mtproto/core_types.cpp b/Telegram/SourceFiles/mtproto/core_types.cpp index f5e2d241d5e362..b54241c043bc91 100644 --- a/Telegram/SourceFiles/mtproto/core_types.cpp +++ b/Telegram/SourceFiles/mtproto/core_types.cpp @@ -9,6 +9,36 @@ For license and copyright information please follow this link: #include "zlib.h" +Exception::Exception(const QString &msg) noexcept : _msg(msg.toUtf8()) { + LOG(("Exception: %1").arg(msg)); +} + +mtpErrorUnexpected::mtpErrorUnexpected( + mtpTypeId typeId, + const QString &type) noexcept +: Exception( + QString("MTP Unexpected type id #%1 read in %2" + ).arg(uint32(typeId), 0, 16 + ).arg(type)) { +} + +mtpErrorInsufficient::mtpErrorInsufficient() noexcept +: Exception("MTP Insufficient bytes in input buffer") { +} + +mtpErrorBadTypeId::mtpErrorBadTypeId( + mtpTypeId typeId, + const QString &type) noexcept +: Exception( + QString("MTP Bad type id #%1 passed to constructor of %2" + ).arg(uint32(typeId), 0, 16 + ).arg(type)) { +} + +const char *Exception::what() const noexcept { + return _msg.constData(); +} + uint32 MTPstring::innerLength() const { uint32 l = v.length(); if (l < 254) { diff --git a/Telegram/SourceFiles/mtproto/core_types.h b/Telegram/SourceFiles/mtproto/core_types.h index 3792e0c3fa17b6..f1882b6dac35c0 100644 --- a/Telegram/SourceFiles/mtproto/core_types.h +++ b/Telegram/SourceFiles/mtproto/core_types.h @@ -7,9 +7,15 @@ For license and copyright information please follow this link: */ #pragma once +#include +#include +#include +#include #include "core/basic_types.h" #include "base/flags.h" #include "base/bytes.h" +#include "base/algorithm.h" +#include "base/assertion.h" namespace MTP { @@ -47,7 +53,7 @@ class mtpRequestData : public mtpBuffer { public: // in toSend: = 0 - must send in container, > 0 - can send without container // in haveSent: = 0 - container with msgIds, > 0 - when was sent - TimeMs msDate = 0; + int64 msDate = 0; mtpRequestId requestId = 0; mtpRequest after; @@ -103,24 +109,32 @@ class mtpRequestIdsMap : public QMap { } }; +class Exception : public std::exception { +public: + explicit Exception(const QString &msg) noexcept; + + const char *what() const noexcept override; + +private: + QByteArray _msg; + +}; + class mtpErrorUnexpected : public Exception { public: - mtpErrorUnexpected(mtpTypeId typeId, const QString &type) : Exception(QString("MTP Unexpected type id #%1 read in %2").arg(uint32(typeId), 0, 16).arg(type), false) { // maybe api changed?.. - } + mtpErrorUnexpected(mtpTypeId typeId, const QString &type) noexcept; }; class mtpErrorInsufficient : public Exception { public: - mtpErrorInsufficient() : Exception("MTP Insufficient bytes in input buffer") { - } + mtpErrorInsufficient() noexcept; }; class mtpErrorBadTypeId : public Exception { public: - mtpErrorBadTypeId(mtpTypeId typeId, const QString &type) : Exception(QString("MTP Bad type id %1 passed to constructor of %2").arg(typeId).arg(type)) { - } + mtpErrorBadTypeId(mtpTypeId typeId, const QString &type) noexcept; }; @@ -656,8 +670,8 @@ class MTPvector { MTPvector() = default; uint32 innerLength() const { - uint32 result(sizeof(uint32)); - for_const (auto &item, v) { + auto result = uint32(sizeof(uint32)); + for (const auto &item : v) { result += item.innerLength(); } return result; @@ -678,7 +692,7 @@ class MTPvector { } void write(mtpBuffer &to) const { to.push_back(v.size()); - for_const (auto &item, v) { + for (const auto &item : v) { item.write(to); } } @@ -730,7 +744,11 @@ inline bool operator!=(const MTPvector &a, const MTPvector &b) { // Human-readable text serialization struct MTPStringLogger { - MTPStringLogger() : p(new char[MTPDebugBufferSize]), size(0), alloced(MTPDebugBufferSize) { + static constexpr auto kBufferSize = 1024 * 1024; // 1 mb start size + + MTPStringLogger() + : p(new char[kBufferSize]) + , alloced(kBufferSize) { } ~MTPStringLogger() { delete[] p; @@ -767,15 +785,20 @@ struct MTPStringLogger { if (size + add <= alloced) return; int32 newsize = size + add; - if (newsize % MTPDebugBufferSize) newsize += MTPDebugBufferSize - (newsize % MTPDebugBufferSize); + if (newsize % kBufferSize) { + newsize += kBufferSize - (newsize % kBufferSize); + } char *b = new char[newsize]; memcpy(b, p, size); alloced = newsize; delete[] p; p = b; } - char *p; - int32 size, alloced; + + char *p = nullptr; + int size = 0; + int alloced = 0; + }; void mtpTextSerializeType(MTPStringLogger &to, const mtpPrime *&from, const mtpPrime *end, mtpPrime cons = 0, uint32 level = 0, mtpPrime vcons = 0); diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.cpp b/Telegram/SourceFiles/mtproto/mtp_instance.cpp index 0cf12ebaf38796..46698ffa017761 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_instance.cpp @@ -76,10 +76,19 @@ class Instance::Private : private Sender { std::unique_ptr &&connection); void connectionFinished(internal::Connection *connection); - void registerRequest(mtpRequestId requestId, ShiftedDcId dcWithShift); + void sendRequest( + mtpRequestId requestId, + mtpRequest &&request, + RPCResponseHandler &&callbacks, + ShiftedDcId shiftedDcId, + TimeMs msCanWait, + bool needsLayer, + mtpRequestId afterRequestId); + void registerRequest(mtpRequestId requestId, ShiftedDcId shiftedDcId); void unregisterRequest(mtpRequestId requestId); - mtpRequestId storeRequest( - mtpRequest &request, + void storeRequest( + mtpRequestId requestId, + const mtpRequest &request, RPCResponseHandler &&callbacks); mtpRequest getRequest(mtpRequestId requestId); void clearCallbacksDelayed(std::vector &&ids); @@ -87,8 +96,8 @@ class Instance::Private : private Sender { bool hasCallbacks(mtpRequestId requestId); void globalCallback(const mtpPrime *from, const mtpPrime *end); - void onStateChange(ShiftedDcId dcWithShift, int32 state); - void onSessionReset(ShiftedDcId dcWithShift); + void onStateChange(ShiftedDcId shiftedDcId, int32 state); + void onSessionReset(ShiftedDcId shiftedDcId); // return true if need to clean request data bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err); @@ -102,7 +111,7 @@ class Instance::Private : private Sender { void setSessionResetHandler(Fn handler); void clearGlobalHandlers(); - internal::Session *getSession(ShiftedDcId shiftedDcId); + not_null getSession(ShiftedDcId shiftedDcId); bool isNormal() const { return (_mode == Instance::Mode::Normal); @@ -497,9 +506,7 @@ QString Instance::Private::dctransport(ShiftedDcId shiftedDcId) { } void Instance::Private::ping() { - if (auto session = getSession(0)) { - session->ping(); - } + getSession(0)->ping(); } void Instance::Private::cancel(mtpRequestId requestId) { @@ -517,27 +524,23 @@ void Instance::Private::cancel(mtpRequestId requestId) { } unregisterRequest(requestId); if (shiftedDcId) { - if (const auto session = getSession(qAbs(*shiftedDcId))) { - session->cancel(requestId, msgId); - } + const auto session = getSession(qAbs(*shiftedDcId)); + session->cancel(requestId, msgId); } clearCallbacks(requestId); } -int32 Instance::Private::state(mtpRequestId requestId) { // < 0 means waiting for such count of ms +// result < 0 means waiting for such count of ms. +int32 Instance::Private::state(mtpRequestId requestId) { if (requestId > 0) { if (const auto shiftedDcId = queryRequestByDc(requestId)) { - if (auto session = getSession(qAbs(*shiftedDcId))) { - return session->requestState(requestId); - } - return MTP::RequestConnecting; + const auto session = getSession(qAbs(*shiftedDcId)); + return session->requestState(requestId); } return MTP::RequestSent; } - if (auto session = getSession(-requestId)) { - return session->requestState(0); - } - return MTP::RequestConnecting; + const auto session = getSession(-requestId); + return session->requestState(0); } void Instance::Private::killSession(ShiftedDcId shiftedDcId) { @@ -837,9 +840,8 @@ void Instance::Private::checkDelayedRequests() { } request = it->second; } - if (auto session = getSession(qAbs(dcWithShift))) { - session->sendPrepared(request); - } + const auto session = getSession(qAbs(dcWithShift)); + session->sendPrepared(request); } if (!_delayedRequests.empty()) { @@ -847,11 +849,38 @@ void Instance::Private::checkDelayedRequests() { } } +void Instance::Private::sendRequest( + mtpRequestId requestId, + mtpRequest &&request, + RPCResponseHandler &&callbacks, + ShiftedDcId shiftedDcId, + TimeMs msCanWait, + bool needsLayer, + mtpRequestId afterRequestId) { + const auto session = getSession(shiftedDcId); + + request->requestId = requestId; + storeRequest(requestId, request, std::move(callbacks)); + + const auto toMainDc = (shiftedDcId == 0); + const auto realShiftedDcId = session->getDcWithShift(); + const auto signedDcId = toMainDc ? -realShiftedDcId : realShiftedDcId; + registerRequest(requestId, signedDcId); + + if (afterRequestId) { + request->after = getRequest(afterRequestId); + } + request->msDate = getms(true); // > 0 - can send without container + request->needsLayer = needsLayer; + + session->sendPrepared(request, msCanWait); +} + void Instance::Private::registerRequest( mtpRequestId requestId, - ShiftedDcId dcWithShift) { + ShiftedDcId shiftedDcId) { QMutexLocker locker(&_requestByDcLock); - _requestsByDc.emplace(requestId, dcWithShift); + _requestsByDc.emplace(requestId, shiftedDcId); } void Instance::Private::unregisterRequest(mtpRequestId requestId) { @@ -866,11 +895,10 @@ void Instance::Private::unregisterRequest(mtpRequestId requestId) { _requestsByDc.erase(requestId); } -mtpRequestId Instance::Private::storeRequest( - mtpRequest &request, +void Instance::Private::storeRequest( + mtpRequestId requestId, + const mtpRequest &request, RPCResponseHandler &&callbacks) { - const auto requestId = reqid(); - request->requestId = requestId; if (callbacks.onDone || callbacks.onFail) { QMutexLocker locker(&_parserMapLock); _parserMap.emplace(requestId, std::move(callbacks)); @@ -879,7 +907,6 @@ mtpRequestId Instance::Private::storeRequest( QWriteLocker locker(&_requestMapLock); _requestMap.emplace(requestId, request); } - return requestId; } mtpRequest Instance::Private::getRequest(mtpRequestId requestId) { @@ -1075,9 +1102,8 @@ void Instance::Private::importDone(const MTPauth_Authorization &result, mtpReque _instance->setMainDcId(newdc); } DEBUG_LOG(("MTP Info: resending request %1 to dc %2 after import auth").arg(waitedRequestId).arg(*shiftedDcId)); - if (auto session = getSession(*shiftedDcId)) { - session->sendPrepared(it->second); - } + const auto session = getSession(*shiftedDcId); + session->sendPrepared(it->second); } waiters.clear(); } @@ -1190,12 +1216,11 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e } request = it->second; } - if (auto session = getSession(newdcWithShift)) { - registerRequest( - requestId, - (dcWithShift < 0) ? -newdcWithShift : newdcWithShift); - session->sendPrepared(request); - } + const auto session = getSession(newdcWithShift); + registerRequest( + requestId, + (dcWithShift < 0) ? -newdcWithShift : newdcWithShift); + session->sendPrepared(request); return true; } else if (code < 0 || code >= 500 || (m = QRegularExpression("^FLOOD_WAIT_(\\d+)$").match(err)).hasMatch()) { if (!requestId) return false; @@ -1270,10 +1295,9 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e } if (!dcWithShift) return false; - if (auto session = getSession(qAbs(dcWithShift))) { - request->needsLayer = true; - session->sendPrepared(request); - } + const auto session = getSession(qAbs(dcWithShift)); + request->needsLayer = true; + session->sendPrepared(request); return true; } else if (err == qstr("CONNECTION_LANG_CODE_INVALID")) { Lang::CurrentCloudManager().resetToDefault(); @@ -1308,10 +1332,9 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e if (!dcWithShift) return false; if (!request->after) { - if (auto session = getSession(qAbs(dcWithShift))) { - request->needsLayer = true; - session->sendPrepared(request); - } + const auto session = getSession(qAbs(dcWithShift)); + request->needsLayer = true; + session->sendPrepared(request); } else { auto newdc = bareDcId(qAbs(dcWithShift)); auto &waiters(_authWaiters[newdc]); @@ -1343,7 +1366,8 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e return false; } -internal::Session *Instance::Private::getSession(ShiftedDcId shiftedDcId) { +not_null Instance::Private::getSession( + ShiftedDcId shiftedDcId) { if (!shiftedDcId) { Assert(_mainSession != nullptr); return _mainSession; @@ -1600,28 +1624,12 @@ void Instance::clearGlobalHandlers() { _private->clearGlobalHandlers(); } -void Instance::onStateChange(ShiftedDcId dcWithShift, int32 state) { - _private->onStateChange(dcWithShift, state); -} - -void Instance::onSessionReset(ShiftedDcId dcWithShift) { - _private->onSessionReset(dcWithShift); +void Instance::onStateChange(ShiftedDcId shiftedDcId, int32 state) { + _private->onStateChange(shiftedDcId, state); } -void Instance::registerRequest( - mtpRequestId requestId, - ShiftedDcId dcWithShift) { - _private->registerRequest(requestId, dcWithShift); -} - -mtpRequestId Instance::storeRequest( - mtpRequest &request, - RPCResponseHandler &&callbacks) { - return _private->storeRequest(request, std::move(callbacks)); -} - -mtpRequest Instance::getRequest(mtpRequestId requestId) { - return _private->getRequest(requestId); +void Instance::onSessionReset(ShiftedDcId shiftedDcId) { + _private->onSessionReset(shiftedDcId); } void Instance::clearCallbacksDelayed(std::vector &&ids) { @@ -1655,29 +1663,27 @@ void Instance::scheduleKeyDestroy(ShiftedDcId shiftedDcId) { void Instance::onKeyDestroyed(qint32 shiftedDcId) { _private->completedKeyDestroy(shiftedDcId); } - -mtpRequestId Instance::send( +void Instance::sendRequest( + mtpRequestId requestId, mtpRequest &&request, RPCResponseHandler &&callbacks, - ShiftedDcId dcId, + ShiftedDcId shiftedDcId, TimeMs msCanWait, - mtpRequestId after) { - if (const auto session = _private->getSession(dcId)) { - return session->send( - mtpRequestData::serialize(request), - std::move(callbacks), - msCanWait, - true, - !dcId, - after); - } - return 0; -} - -void Instance::sendAnything(ShiftedDcId dcId, TimeMs msCanWait) { - if (const auto session = _private->getSession(dcId)) { - session->sendAnything(msCanWait); - } + bool needsLayer, + mtpRequestId afterRequestId) { + return _private->sendRequest( + requestId, + std::move(request), + std::move(callbacks), + shiftedDcId, + msCanWait, + needsLayer, + afterRequestId); +} + +void Instance::sendAnything(ShiftedDcId shiftedDcId, TimeMs msCanWait) { + const auto session = _private->getSession(shiftedDcId); + session->sendAnything(msCanWait); } Instance::~Instance() { diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.h b/Telegram/SourceFiles/mtproto/mtp_instance.h index f0629606b2eeaa..7c71e6865ed80e 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.h +++ b/Telegram/SourceFiles/mtproto/mtp_instance.h @@ -64,15 +64,18 @@ class Instance : public QObject { mtpRequestId send( const TRequest &request, RPCResponseHandler &&callbacks = {}, - ShiftedDcId dcId = 0, + ShiftedDcId shiftedDcId = 0, TimeMs msCanWait = 0, - mtpRequestId after = 0) { - return send( + mtpRequestId afterRequestId = 0) { + const auto requestId = GetNextRequestId(); + sendSerialized( + requestId, mtpRequestData::serialize(request), std::move(callbacks), - dcId, + shiftedDcId, msCanWait, - after); + afterRequestId); + return requestId; } template @@ -80,18 +83,52 @@ class Instance : public QObject { const TRequest &request, RPCDoneHandlerPtr &&onDone, RPCFailHandlerPtr &&onFail = nullptr, - ShiftedDcId dc = 0, + ShiftedDcId shiftedDcId = 0, TimeMs msCanWait = 0, - mtpRequestId after = 0) { + mtpRequestId afterRequestId = 0) { return send( request, RPCResponseHandler(std::move(onDone), std::move(onFail)), - dc, + shiftedDcId, + msCanWait, + afterRequestId); + } + + template + mtpRequestId sendProtocolMessage( + ShiftedDcId shiftedDcId, + const TRequest &request) { + const auto requestId = GetNextRequestId(); + sendRequest( + requestId, + mtpRequestData::serialize(request), + {}, + shiftedDcId, + 0, + false, + 0); + return requestId; + } + + void sendSerialized( + mtpRequestId requestId, + mtpRequest &&request, + RPCResponseHandler &&callbacks, + ShiftedDcId shiftedDcId, + TimeMs msCanWait, + mtpRequestId afterRequestId) { + const auto needsLayer = true; + sendRequest( + requestId, + std::move(request), + std::move(callbacks), + shiftedDcId, msCanWait, - after); + needsLayer, + afterRequestId); } - void sendAnything(ShiftedDcId dcId = 0, TimeMs msCanWait = 0); + void sendAnything(ShiftedDcId shiftedDcId = 0, TimeMs msCanWait = 0); void restart(); void restart(ShiftedDcId shiftedDcId); @@ -116,14 +153,9 @@ class Instance : public QObject { void setSessionResetHandler(Fn handler); void clearGlobalHandlers(); - void onStateChange(ShiftedDcId dcWithShift, int32 state); - void onSessionReset(ShiftedDcId dcWithShift); + void onStateChange(ShiftedDcId shiftedDcId, int32 state); + void onSessionReset(ShiftedDcId shiftedDcId); - void registerRequest(mtpRequestId requestId, ShiftedDcId dcWithShift); - mtpRequestId storeRequest( - mtpRequest &request, - RPCResponseHandler &&callbacks); - mtpRequest getRequest(mtpRequestId requestId); void clearCallbacksDelayed(std::vector &&ids); void execCallback(mtpRequestId requestId, const mtpPrime *from, const mtpPrime *end); @@ -161,12 +193,14 @@ private slots: void onKeyDestroyed(qint32 shiftedDcId); private: - mtpRequestId send( + void sendRequest( + mtpRequestId requestId, mtpRequest &&request, RPCResponseHandler &&callbacks, - ShiftedDcId dcId, + ShiftedDcId shiftedDcId, TimeMs msCanWait, - mtpRequestId after); + bool needsLayer, + mtpRequestId afterRequestId); class Private; const std::unique_ptr _private; diff --git a/Telegram/SourceFiles/mtproto/rpc_sender.cpp b/Telegram/SourceFiles/mtproto/rpc_sender.cpp index b41081ee808c13..f9ab9162a94a80 100644 --- a/Telegram/SourceFiles/mtproto/rpc_sender.cpp +++ b/Telegram/SourceFiles/mtproto/rpc_sender.cpp @@ -7,18 +7,44 @@ For license and copyright information please follow this link: */ #include "mtproto/rpc_sender.h" +RPCError::RPCError(const MTPrpcError &error) +: _code(error.c_rpc_error().verror_code.v) { + QString text = qs(error.c_rpc_error().verror_message); + if (_code < 0 || _code >= 500) { + _type = qsl("INTERNAL_SERVER_ERROR"); + _description = text; + } else { + const auto expression = QRegularExpression( + "^([A-Z0-9_]+)(: .*)?$", + reMultiline); + const auto match = expression.match(text); + if (match.hasMatch()) { + _type = match.captured(1); + _description = match.captured(2).mid(2); + } else { + _type = qsl("CLIENT_BAD_RPC_ERROR"); + _description = qsl("Bad rpc error received, text = '") + text + '\''; + } + } +} + + RPCOwnedDoneHandler::RPCOwnedDoneHandler(RPCSender *owner) : _owner(owner) { - _owner->_rpcRegHandler(this); + _owner->rpcRegHandler(this); } RPCOwnedDoneHandler::~RPCOwnedDoneHandler() { - if (_owner) _owner->_rpcUnregHandler(this); + if (_owner) { + _owner->rpcUnregHandler(this); + } } RPCOwnedFailHandler::RPCOwnedFailHandler(RPCSender *owner) : _owner(owner) { - _owner->_rpcRegHandler(this); + _owner->rpcRegHandler(this); } RPCOwnedFailHandler::~RPCOwnedFailHandler() { - if (_owner) _owner->_rpcUnregHandler(this); + if (_owner) { + _owner->rpcUnregHandler(this); + } } diff --git a/Telegram/SourceFiles/mtproto/rpc_sender.h b/Telegram/SourceFiles/mtproto/rpc_sender.h index 7985716b8d3cd5..fc8ecffaa2f5d2 100644 --- a/Telegram/SourceFiles/mtproto/rpc_sender.h +++ b/Telegram/SourceFiles/mtproto/rpc_sender.h @@ -8,25 +8,11 @@ For license and copyright information please follow this link: #pragma once #include +#include "base/flat_set.h" class RPCError { public: - RPCError(const MTPrpcError &error) : _code(error.c_rpc_error().verror_code.v) { - QString text = qs(error.c_rpc_error().verror_message); - if (_code < 0 || _code >= 500) { - _type = qsl("INTERNAL_SERVER_ERROR"); - _description = text; - } else { - auto m = QRegularExpression("^([A-Z0-9_]+)(: .*)?$", reMultiline).match(text); - if (m.hasMatch()) { - _type = m.captured(1); - _description = m.captured(2).mid(2); - } else { - _type = qsl("CLIENT_BAD_RPC_ERROR"); - _description = qsl("Bad rpc error received, text = '") + text + '\''; - } - } - } + RPCError(const MTPrpcError &error); int32 code() const { return _code; @@ -688,30 +674,6 @@ class RPCBindedFailHandlerOwnedNoReq : public RPCOwnedFailHandler { // fail(b, r }; class RPCSender { - using DoneHandlers = QSet; - DoneHandlers _rpcDoneHandlers; - using FailHandlers = QSet; - FailHandlers _rpcFailHandlers; - - void _rpcRegHandler(RPCOwnedDoneHandler *handler) { - _rpcDoneHandlers.insert(handler); - } - - void _rpcUnregHandler(RPCOwnedDoneHandler *handler) { - _rpcDoneHandlers.remove(handler); - } - - void _rpcRegHandler(RPCOwnedFailHandler *handler) { - _rpcFailHandlers.insert(handler); - } - - void _rpcUnregHandler(RPCOwnedFailHandler *handler) { - _rpcFailHandlers.remove(handler); - } - - friend class RPCOwnedDoneHandler; - friend class RPCOwnedFailHandler; - public: template // done(from, end) RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(const mtpPrime *, const mtpPrime *)) { @@ -831,6 +793,29 @@ class RPCSender { } } +private: + base::flat_set _rpcDoneHandlers; + base::flat_set _rpcFailHandlers; + + void rpcRegHandler(RPCOwnedDoneHandler *handler) { + _rpcDoneHandlers.emplace(handler); + } + + void rpcUnregHandler(RPCOwnedDoneHandler *handler) { + _rpcDoneHandlers.remove(handler); + } + + void rpcRegHandler(RPCOwnedFailHandler *handler) { + _rpcFailHandlers.emplace(handler); + } + + void rpcUnregHandler(RPCOwnedFailHandler *handler) { + _rpcFailHandlers.remove(handler); + } + + friend class RPCOwnedDoneHandler; + friend class RPCOwnedFailHandler; + }; using MTPStateChangedHandler = void (*)(int32 dcId, int32 state); diff --git a/Telegram/SourceFiles/mtproto/session.cpp b/Telegram/SourceFiles/mtproto/session.cpp index 54d5e199be3c4b..20090d2eeebcc5 100644 --- a/Telegram/SourceFiles/mtproto/session.cpp +++ b/Telegram/SourceFiles/mtproto/session.cpp @@ -153,20 +153,6 @@ void Session::createDcData() { connect(dc.get(), SIGNAL(connectionWasInited()), this, SLOT(connectionWasInitedForDC()), Qt::QueuedConnection); } -void Session::registerRequest(mtpRequestId requestId, ShiftedDcId dcWithShift) { - return _instance->registerRequest(requestId, dcWithShift); -} - -mtpRequestId Session::storeRequest( - mtpRequest &request, - RPCResponseHandler &&callbacks) { - return _instance->storeRequest(request, std::move(callbacks)); -} - -mtpRequest Session::getRequest(mtpRequestId requestId) { - return _instance->getRequest(requestId); -} - bool Session::rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err) { // return true if need to clean request data return _instance->rpcErrorOccured(requestId, onFail, err); } @@ -280,9 +266,9 @@ void Session::needToResumeAndSend() { } void Session::sendPong(quint64 msgId, quint64 pingId) { - send(mtpRequestData::serialize(MTPPong(MTP_pong( - MTP_long(msgId), - MTP_long(pingId))))); + _instance->sendProtocolMessage( + dcWithShift, + MTPPong(MTP_pong(MTP_long(msgId), MTP_long(pingId)))); } void Session::sendMsgsStateInfo(quint64 msgId, QByteArray data) { @@ -291,8 +277,10 @@ void Session::sendMsgsStateInfo(quint64 msgId, QByteArray data) { info.resize(data.size()); bytes::copy(info, bytes::make_span(data)); } - send(mtpRequestData::serialize(MTPMsgsStateInfo( - MTP_msgs_state_info(MTP_long(msgId), MTP_bytes(data))))); + _instance->sendProtocolMessage( + dcWithShift, + MTPMsgsStateInfo( + MTP_msgs_state_info(MTP_long(msgId), MTP_bytes(data)))); } void Session::checkRequestsByTimer() { @@ -457,10 +445,12 @@ mtpRequestId Session::resend(quint64 msgId, qint64 msCanWait, bool forceContaine DEBUG_LOG(("Message Info: cant resend %1, request not found").arg(msgId)); auto info = std::string(cantResend, cantResend + 1); - return send(mtpRequestData::serialize(MTPMsgsStateInfo( - MTP_msgs_state_info( - MTP_long(msgId), - MTP_string(std::move(info)))))); + return _instance->sendProtocolMessage( + dcWithShift, + MTPMsgsStateInfo( + MTP_msgs_state_info( + MTP_long(msgId), + MTP_string(std::move(info))))); } return 0; } @@ -509,31 +499,12 @@ void Session::resendAll() { } } -mtpRequestId Session::send( - mtpRequest &&request, - RPCResponseHandler &&callbacks, +void Session::sendPrepared( + const mtpRequest &request, TimeMs msCanWait, - bool needsLayer, - bool toMainDC, - mtpRequestId after) { - DEBUG_LOG(("MTP Info: adding request to toSendMap, msCanWait %1").arg(msCanWait)); - - request->msDate = getms(true); // > 0 - can send without container - request->needsLayer = needsLayer; - if (after) { - request->after = getRequest(after); - } - const auto requestId = storeRequest(request, std::move(callbacks)); - Assert(requestId != 0); - - const auto signedDcId = toMainDC ? -getDcWithShift() : getDcWithShift(); - sendPrepared(request, msCanWait); - registerRequest(requestId, signedDcId); - - return requestId; -} - -void Session::sendPrepared(const mtpRequest &request, TimeMs msCanWait, bool newRequest) { // returns true, if emit of needToSend() is needed + bool newRequest) { + DEBUG_LOG(("MTP Info: adding request to toSendMap, msCanWait %1" + ).arg(msCanWait)); { QWriteLocker locker(data.toSendMutex()); data.toSendMap().insert(request->requestId, request); diff --git a/Telegram/SourceFiles/mtproto/session.h b/Telegram/SourceFiles/mtproto/session.h index 96f15d48b2c9f3..531ce6c094ec37 100644 --- a/Telegram/SourceFiles/mtproto/session.h +++ b/Telegram/SourceFiles/mtproto/session.h @@ -319,14 +319,6 @@ class Session : public QObject { int32 getState() const; QString transport() const; - mtpRequestId send( - mtpRequest &&request, - RPCResponseHandler &&callbacks = {}, - TimeMs msCanWait = 0, - bool needsLayer = false, - bool toMainDC = false, - mtpRequestId after = 0); - // Nulls msgId and seqNo in request, if newRequest = true. void sendPrepared( const mtpRequest &request, @@ -363,11 +355,6 @@ public slots: private: void createDcData(); - void registerRequest(mtpRequestId requestId, ShiftedDcId dcWithShift); - mtpRequestId storeRequest( - mtpRequest &request, - RPCResponseHandler &&callbacks); - mtpRequest getRequest(mtpRequestId requestId); bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err); not_null _instance; diff --git a/Telegram/SourceFiles/passport/passport_panel.cpp b/Telegram/SourceFiles/passport/passport_panel.cpp index 0848c9c66fa449..754bb7e23f5f4e 100644 --- a/Telegram/SourceFiles/passport/passport_panel.cpp +++ b/Telegram/SourceFiles/passport/passport_panel.cpp @@ -26,12 +26,15 @@ Panel::Panel(not_null controller) _widget->setTitle(Lang::Viewer(lng_passport_title)); _widget->setInnerSize(st::passportPanelSize); - rpl::merge( - _widget->closeRequests(), - _widget->destroyRequests() + _widget->closeRequests( ) | rpl::start_with_next([=] { _controller->cancelAuth(); }, _widget->lifetime()); + + _widget->closeEvents( + ) | rpl::start_with_next([=] { + _controller->cancelAuthSure(); + }, _widget->lifetime()); } rpl::producer<> Panel::backRequests() const { @@ -47,7 +50,7 @@ not_null Panel::widget() const { } int Panel::hideAndDestroyGetDuration() { - return _widget->hideAndDestroyGetDuration(); + return _widget->hideGetDuration(); } void Panel::showAskPassword() { diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.cpp b/Telegram/SourceFiles/passport/passport_panel_controller.cpp index 32bac96aa0e2ba..38c5bb0d5979bb 100644 --- a/Telegram/SourceFiles/passport/passport_panel_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_controller.cpp @@ -1206,6 +1206,10 @@ void PanelController::cancelAuth() { _form->cancel(); } +void PanelController::cancelAuthSure() { + _form->cancelSure(); +} + void PanelController::showBox( object_ptr box, LayerOptions options, diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.h b/Telegram/SourceFiles/passport/passport_panel_controller.h index a3007d9c1000de..e92b97c295d471 100644 --- a/Telegram/SourceFiles/passport/passport_panel_controller.h +++ b/Telegram/SourceFiles/passport/passport_panel_controller.h @@ -123,6 +123,7 @@ class PanelController : public ViewController { int closeGetDuration() override; void cancelAuth(); + void cancelAuthSure(); rpl::lifetime &lifetime(); diff --git a/Telegram/SourceFiles/rpl/variable.h b/Telegram/SourceFiles/rpl/variable.h index d64217d0dbfeac..757e5e8e699153 100644 --- a/Telegram/SourceFiles/rpl/variable.h +++ b/Telegram/SourceFiles/rpl/variable.h @@ -10,6 +10,15 @@ For license and copyright information please follow this link: #include #include +namespace mapbox { +namespace util { + +template +class variant; + +} // namespace util +} // namespace mapbox + namespace rpl { namespace details { @@ -17,18 +26,38 @@ template struct supports_equality_compare { template static auto test(const U *u, const V *v) - -> decltype(*u == *v, details::true_t()); - static details::false_t test(...); + -> decltype(*u == *v, true_t()); + static false_t test(...); static constexpr bool value - = (sizeof(test( - (std::decay_t*)nullptr, - (std::decay_t*)nullptr - )) == sizeof(details::true_t)); + = (sizeof(test((const A*)nullptr, (const B*)nullptr)) + == sizeof(true_t)); }; +// Fix for MSVC expression SFINAE. +// It still doesn't work! :( +// +//template +//struct supports_equality_compare< +// mapbox::util::variant, +// mapbox::util::variant> { +// static constexpr bool value +// = (supports_equality_compare::value +// && supports_equality_compare< +// mapbox::util::variant, +// mapbox::util::variant>::value); +// +//}; +//template +//struct supports_equality_compare< +// mapbox::util::variant, +// mapbox::util::variant> { +// static constexpr bool value = supports_equality_compare::value; +// +//}; + template constexpr bool supports_equality_compare_v - = supports_equality_compare::value; + = supports_equality_compare, std::decay_t>::value; } // namespace details diff --git a/Telegram/SourceFiles/settings/settings_widget.cpp b/Telegram/SourceFiles/settings/settings_widget.cpp index 0592d58cfb3174..f666157ca5ebdc 100644 --- a/Telegram/SourceFiles/settings/settings_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_widget.cpp @@ -30,6 +30,8 @@ For license and copyright information please follow this link: #include "window/themes/window_theme.h" #include "window/themes/window_theme_editor.h" #include "media/media_audio_track.h" +#include "mainwindow.h" +#include "window/window_controller.h" namespace Settings { namespace { @@ -125,6 +127,9 @@ void fillCodes() { Platform::RegisterCustomScheme(); Ui::Toast::Show("Forced custom scheme register."); }); + Codes.insert(qsl("export"), [] { + App::wnd()->controller()->startDataExport(); + }); auto audioFilters = qsl("Audio files (*.wav *.mp3);;") + FileDialog::AllFilesFilter(); auto audioKeys = { diff --git a/Telegram/SourceFiles/ui/widgets/separate_panel.cpp b/Telegram/SourceFiles/ui/widgets/separate_panel.cpp index f6e33d51581ef0..90bcb4d8cd3151 100644 --- a/Telegram/SourceFiles/ui/widgets/separate_panel.cpp +++ b/Telegram/SourceFiles/ui/widgets/separate_panel.cpp @@ -85,11 +85,13 @@ rpl::producer<> SeparatePanel::backRequests() const { } rpl::producer<> SeparatePanel::closeRequests() const { - return _close->clicks(); + return rpl::merge( + _close->clicks(), + _userCloseRequests.events()); } -rpl::producer<> SeparatePanel::destroyRequests() const { - return _destroyRequests.events(); +rpl::producer<> SeparatePanel::closeEvents() const { + return _closeEvents.events(); } void SeparatePanel::setBackAllowed(bool allowed) { @@ -191,7 +193,7 @@ void SeparatePanel::finishAnimating() { showControls(); _inner->setFocus(); } else { - destroyDelayed(); + finishClose(); } } @@ -202,15 +204,15 @@ void SeparatePanel::showControls() { } } -void SeparatePanel::destroyDelayed() { +void SeparatePanel::finishClose() { hide(); - _destroyRequests.fire({}); + _closeEvents.fire({}); } -int SeparatePanel::hideAndDestroyGetDuration() { +int SeparatePanel::hideGetDuration() { toggleOpacityAnimation(false); if (_animationCache.isNull()) { - destroyDelayed(); + finishClose(); return 0; } return st::callPanelDuration; @@ -485,7 +487,8 @@ void SeparatePanel::paintOpaqueBorder(Painter &p) const { } void SeparatePanel::closeEvent(QCloseEvent *e) { - // #TODO passport + e->ignore(); + _userCloseRequests.fire({}); } void SeparatePanel::mousePressEvent(QMouseEvent *e) { diff --git a/Telegram/SourceFiles/ui/widgets/separate_panel.h b/Telegram/SourceFiles/ui/widgets/separate_panel.h index 6bcd6ce93276f4..a415b20b8eb295 100644 --- a/Telegram/SourceFiles/ui/widgets/separate_panel.h +++ b/Telegram/SourceFiles/ui/widgets/separate_panel.h @@ -31,7 +31,7 @@ class SeparatePanel : public Ui::RpWidget { void setInnerSize(QSize size); void showAndActivate(); - int hideAndDestroyGetDuration(); + int hideGetDuration(); void showInner(base::unique_qptr inner); void showBox( @@ -42,7 +42,7 @@ class SeparatePanel : public Ui::RpWidget { rpl::producer<> backRequests() const; rpl::producer<> closeRequests() const; - rpl::producer<> destroyRequests() const; + rpl::producer<> closeEvents() const; void setBackAllowed(bool allowed); protected: @@ -74,7 +74,7 @@ class SeparatePanel : public Ui::RpWidget { void toggleOpacityAnimation(bool visible); void finishAnimating(); - void destroyDelayed(); + void finishClose(); object_ptr _close; object_ptr _title = { nullptr }; @@ -83,7 +83,8 @@ class SeparatePanel : public Ui::RpWidget { base::unique_qptr _inner; object_ptr _layer = { nullptr }; rpl::event_stream<> _synteticBackRequests; - rpl::event_stream<> _destroyRequests; + rpl::event_stream<> _userCloseRequests; + rpl::event_stream<> _closeEvents; bool _useTransparency = true; style::margins _padding; diff --git a/Telegram/SourceFiles/window/window_controller.cpp b/Telegram/SourceFiles/window/window_controller.cpp index 44ef88b4cffada..5e177213425b41 100644 --- a/Telegram/SourceFiles/window/window_controller.cpp +++ b/Telegram/SourceFiles/window/window_controller.cpp @@ -16,6 +16,8 @@ For license and copyright information please follow this link: #include "data/data_session.h" #include "data/data_feed.h" #include "passport/passport_form_controller.h" +#include "export/export_controller.h" +#include "export/view/export_view_panel_controller.h" #include "boxes/calendar_box.h" #include "mainwidget.h" #include "mainwindow.h" @@ -416,6 +418,24 @@ void Controller::clearPassportForm() { _passportForm = nullptr; } +void Controller::startDataExport() { + using namespace Export; + + _export = std::make_unique(); + _exportPanel = std::make_unique( + _export.get()); + + _exportPanel->closed( + ) | rpl::start_with_next([=] { + clearDataExport(); + }, _export->lifetime()); +} + +void Controller::clearDataExport() { + _exportPanel = nullptr; + _export = nullptr; +} + void Controller::updateColumnLayout() { App::main()->updateColumnLayout(); } diff --git a/Telegram/SourceFiles/window/window_controller.h b/Telegram/SourceFiles/window/window_controller.h index e286e33f441f9f..c30456ec33870e 100644 --- a/Telegram/SourceFiles/window/window_controller.h +++ b/Telegram/SourceFiles/window/window_controller.h @@ -26,6 +26,13 @@ struct FormRequest; class FormController; } // namespace Passport +namespace Export { +class ControllerWrap; +namespace View { +class PanelController; +} // namespace View +} // namespace Export + namespace Window { class LayerWidget; @@ -208,6 +215,8 @@ class Controller : public Navigation { void showPassportForm(const Passport::FormRequest &request); void clearPassportForm(); + void startDataExport(); + base::Variable &dialogsListFocused() { return _dialogsListFocused; } @@ -251,10 +260,13 @@ class Controller : public Navigation { int dialogsWidth, int thirdWidth, int bodyWidth) const; + void clearDataExport(); not_null _window; std::unique_ptr _passportForm; + std::unique_ptr _export; + std::unique_ptr _exportPanel; GifPauseReasons _gifPauseReasons = 0; base::Observable _gifPauseLevelChanged; diff --git a/Telegram/gyp/PrecompiledHeader.cmake b/Telegram/gyp/PrecompiledHeader.cmake index 5d6830e9633197..a0e1e048999844 100644 --- a/Telegram/gyp/PrecompiledHeader.cmake +++ b/Telegram/gyp/PrecompiledHeader.cmake @@ -69,13 +69,15 @@ endmacro() function(export_all_flags _filename _source_name_for_flags) set(_include_directories "$") set(_compile_definitions "$") - get_source_file_property(_compile_flags "${_source_name_for_flags}" COMPILE_FLAGS) + get_source_file_property(_compile_file_flags "${_source_name_for_flags}" COMPILE_FLAGS) + set(_compile_flags "$") set(_compile_options "$") set(_include_directories "$<$:-I$\n>") set(_compile_definitions "$<$:-D$\n>") + set(_compile_file_flags "$<$:$\n>") set(_compile_flags "$<$:$\n>") set(_compile_options "$<$:$\n>") - file(GENERATE OUTPUT "${_filename}" CONTENT "${_compile_definitions}${_include_directories}${_compile_flags}${_compile_options}\n") + file(GENERATE OUTPUT "${_filename}" CONTENT "${_compile_definitions}${_include_directories}${_compile_file_flags}${_compile_flags}${_compile_options}\n") endfunction() function(add_precompiled_header _target _input) @@ -126,7 +128,7 @@ function(add_precompiled_header _target _input) COMMAND "${CMAKE_CXX_COMPILER}" ${_compiler_FLAGS} -x c++-header -o "${_output_cxx}" -c "${_pchfile}" DEPENDS "${_pchfile}" "${_pch_cpp_flags_file}" IMPLICIT_DEPENDS CXX "${_pch_header}" - COMMENT "Precompiling ${_name} for ${_target} (C++)") + COMMENT "Precompiling header ${_name} for ${_target} (C++)") endif() foreach(_source ${_sources}) diff --git a/Telegram/gyp/Telegram.gyp b/Telegram/gyp/Telegram.gyp index 2261f6de9ca7dd..699068eeeaca2f 100644 --- a/Telegram/gyp/Telegram.gyp +++ b/Telegram/gyp/Telegram.gyp @@ -27,6 +27,7 @@ '<(src_loc)/boxes/boxes.style', '<(src_loc)/calls/calls.style', '<(src_loc)/dialogs/dialogs.style', + '<(src_loc)/export/view/export.style', '<(src_loc)/history/history.style', '<(src_loc)/info/info.style', '<(src_loc)/intro/intro.style', @@ -51,6 +52,8 @@ ], 'build_defines%': '', 'list_sources_command': 'python <(DEPTH)/list_sources.py --input <(DEPTH)/telegram_sources.txt --replace src_loc=<(src_loc)', + 'pch_source': '<(src_loc)/stdafx.cpp', + 'pch_header': '<(src_loc)/stdafx.h', }, 'includes': [ 'common_executable.gypi', @@ -62,6 +65,7 @@ 'qt_moc.gypi', 'qt_rcc.gypi', 'codegen_rules.gypi', + 'pch.gypi', ], 'dependencies': [ @@ -73,6 +77,7 @@ 'utils.gyp:Updater', '../ThirdParty/libtgvoip/libtgvoip.gyp:libtgvoip', 'crl.gyp:crl', + 'lib_export.gyp:lib_export', ], 'defines': [ diff --git a/Telegram/gyp/codegen_rules.gypi b/Telegram/gyp/codegen_rules.gypi index bb611a87f2ec6a..a901db3bb61469 100644 --- a/Telegram/gyp/codegen_rules.gypi +++ b/Telegram/gyp/codegen_rules.gypi @@ -98,22 +98,6 @@ ], 'message': 'codegen_numbers-ing numbers.txt..', 'process_outputs_as_sources': 1, - }, { - 'action_name': 'codegen_scheme', - 'inputs': [ - '<(src_loc)/codegen/scheme/codegen_scheme.py', - '<(res_loc)/scheme.tl', - ], - 'outputs': [ - '<(SHARED_INTERMEDIATE_DIR)/scheme.cpp', - '<(SHARED_INTERMEDIATE_DIR)/scheme.h', - ], - 'action': [ - 'python', '<(src_loc)/codegen/scheme/codegen_scheme.py', - '-o', '<(SHARED_INTERMEDIATE_DIR)', '<(res_loc)/scheme.tl', - ], - 'message': 'codegen_scheme-ing scheme.tl..', - 'process_outputs_as_sources': 1, }, { 'action_name': 'codegen_emoji', 'inputs': [ diff --git a/Telegram/gyp/crl.gyp b/Telegram/gyp/crl.gyp index 23ce16e69c3509..cf1dc56d736bc6 100644 --- a/Telegram/gyp/crl.gyp +++ b/Telegram/gyp/crl.gyp @@ -11,7 +11,8 @@ 'targets': [{ 'target_name': 'crl', 'type': 'static_library', - 'dependencies': [], + 'dependencies': [ + ], 'includes': [ 'common.gypi', 'qt.gypi', diff --git a/Telegram/gyp/lib_export.gyp b/Telegram/gyp/lib_export.gyp new file mode 100644 index 00000000000000..138dd8d215ca48 --- /dev/null +++ b/Telegram/gyp/lib_export.gyp @@ -0,0 +1,58 @@ +# 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 + +{ + 'includes': [ + 'common.gypi', + ], + 'targets': [{ + 'target_name': 'lib_export', + 'type': 'static_library', + 'includes': [ + 'common.gypi', + 'qt.gypi', + 'pch.gypi', + ], + 'variables': { + 'src_loc': '../SourceFiles', + 'libs_loc': '../../../Libraries', + 'official_build_target%': '', + 'submodules_loc': '../ThirdParty', + 'pch_source': '<(src_loc)/export/export_pch.cpp', + 'pch_header': '<(src_loc)/export/export_pch.h', + }, + 'defines': [ + ], + 'dependencies': [ + 'lib_scheme.gyp:lib_scheme', + 'crl.gyp:crl', + ], + 'export_dependent_settings': [ + 'lib_scheme.gyp:lib_scheme', + ], + 'conditions': [[ 'build_macold', { + 'xcode_settings': { + 'OTHER_CPLUSPLUSFLAGS': [ '-nostdinc++' ], + }, + 'include_dirs': [ + '/usr/local/macold/include/c++/v1', + ], + }]], + 'include_dirs': [ + '<(src_loc)', + '<(SHARED_INTERMEDIATE_DIR)', + '<(libs_loc)/range-v3/include', + '<(submodules_loc)/GSL/include', + '<(submodules_loc)/variant/include', + '<(submodules_loc)/crl/src', + ], + 'sources': [ + '<(src_loc)/export/export_controller.cpp', + '<(src_loc)/export/export_controller.h', + '<(src_loc)/export/export_settings.h', + ], + }], +} diff --git a/Telegram/gyp/lib_scheme.gyp b/Telegram/gyp/lib_scheme.gyp new file mode 100644 index 00000000000000..b52a9d0714af0a --- /dev/null +++ b/Telegram/gyp/lib_scheme.gyp @@ -0,0 +1,58 @@ +# 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 + +{ + 'includes': [ + 'common.gypi', + ], + 'targets': [{ + 'target_name': 'lib_scheme', + 'type': 'static_library', + 'hard_dependency': 1, + 'includes': [ + 'common.gypi', + 'qt.gypi', + ], + 'variables': { + 'src_loc': '../SourceFiles', + 'res_loc': '../Resources', + 'official_build_target%': '', + 'submodules_loc': '../ThirdParty', + }, + 'defines': [ + ], + 'conditions': [[ 'build_macold', { + 'xcode_settings': { + 'OTHER_CPLUSPLUSFLAGS': [ '-nostdinc++' ], + }, + 'include_dirs': [ + '/usr/local/macold/include/c++/v1', + ], + }]], + 'include_dirs': [ + '<(src_loc)', + '<(SHARED_INTERMEDIATE_DIR)', + '<(submodules_loc)/GSL/include', + ], + 'actions': [{ + 'action_name': 'codegen_scheme', + 'inputs': [ + '<(src_loc)/codegen/scheme/codegen_scheme.py', + '<(res_loc)/scheme.tl', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/scheme.cpp', + '<(SHARED_INTERMEDIATE_DIR)/scheme.h', + ], + 'action': [ + 'python', '<(src_loc)/codegen/scheme/codegen_scheme.py', + '-o', '<(SHARED_INTERMEDIATE_DIR)', '<(res_loc)/scheme.tl', + ], + 'message': 'codegen_scheme-ing scheme.tl..', + 'process_outputs_as_sources': 1, + }], + }], +} diff --git a/Telegram/gyp/pch.gypi b/Telegram/gyp/pch.gypi new file mode 100644 index 00000000000000..17334d7e49de1c --- /dev/null +++ b/Telegram/gyp/pch.gypi @@ -0,0 +1,20 @@ +# 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 + +{ + 'cmake_precompiled_header': '<(pch_header)', + 'cmake_precompiled_header_script': 'PrecompiledHeader.cmake', + 'msvs_precompiled_source': '<(pch_source)', + 'msvs_precompiled_header': '<(pch_header)', + 'xcode_settings': { + 'GCC_PREFIX_HEADER': '<(pch_header)', + 'GCC_PRECOMPILE_PREFIX_HEADER': 'YES', + }, + 'sources': [ + '<(pch_source)', + '<(pch_header)', + ], +} diff --git a/Telegram/gyp/settings_mac.gypi b/Telegram/gyp/settings_mac.gypi index b20a17aa10eb9f..7bb67036cf8275 100644 --- a/Telegram/gyp/settings_mac.gypi +++ b/Telegram/gyp/settings_mac.gypi @@ -60,6 +60,7 @@ 'COMBINE_HIDPI_IMAGES': 'YES', 'COPY_PHASE_STRIP': 'NO', 'CLANG_CXX_LANGUAGE_STANDARD': 'c++1z', + 'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES', }, 'configurations': { 'Debug': { diff --git a/Telegram/gyp/telegram_linux.gypi b/Telegram/gyp/telegram_linux.gypi index 22f3e161a6c788..099705c4b721e0 100644 --- a/Telegram/gyp/telegram_linux.gypi +++ b/Telegram/gyp/telegram_linux.gypi @@ -113,7 +113,5 @@ ], }] ], - 'cmake_precompiled_header': '<(src_loc)/stdafx.h', - 'cmake_precompiled_header_script': 'PrecompiledHeader.cmake', }]], } diff --git a/Telegram/gyp/telegram_mac.gypi b/Telegram/gyp/telegram_mac.gypi index 97b7638bb86fc7..43aabb0f8ec7c2 100644 --- a/Telegram/gyp/telegram_mac.gypi +++ b/Telegram/gyp/telegram_mac.gypi @@ -7,8 +7,6 @@ { 'conditions': [[ 'build_mac', { 'xcode_settings': { - 'GCC_PREFIX_HEADER': '<(src_loc)/stdafx.h', - 'GCC_PRECOMPILE_PREFIX_HEADER': 'YES', 'INFOPLIST_FILE': '../Telegram.plist', 'CURRENT_PROJECT_VERSION': '