Start data export in lib_export.

This commit is contained in:
John Preston 2018-06-02 17:29:21 +03:00
parent c2fa149ffd
commit c587c011d2
52 changed files with 1994 additions and 320 deletions

View File

@ -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...";

View File

@ -7,7 +7,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QObject>
#include <QtCore/QThread>
#include "base/observer.h"
#include "base/flat_map.h"
namespace base {

View File

@ -69,11 +69,11 @@ public:
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 @@ public:
std::forward<Value>(other),
std::is_copy_constructible<std::decay_t<Value>>()) {
}
template <
typename Value,
typename = std::enable_if_t<
@ -106,7 +106,7 @@ public:
}
return *this;
}
template <
typename Value,
typename ...Args,
@ -143,7 +143,7 @@ private:
unique_any(Value &&other, std::true_type)
: _impl(std::forward<Value>(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<std::decay_t<Value>>,
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::decay_t<Value>>
&& std::is_move_constructible_v<std::decay_t<Value>>,

View File

@ -635,7 +635,7 @@ for restype in typesList:
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<MTPD' + name + '>();\n';
constructsBodies += '}\n';
@ -771,7 +771,7 @@ for restype in typesList:
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';

View File

@ -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,

View File

@ -12,8 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <memory>
#include <ctime>
#include <functional>
#include <gsl/gsl>
#include <crl/crl.h>
#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)

View File

@ -54,11 +54,13 @@ static_assert(sizeof(MTPdouble) == 8, "Basic types size check failed");
// Unixtime functions
namespace {
std::atomic<int> 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<int>::max() / 2) {
GlobalAtomicRequestId = 0;
}
return ++_reqId;
return result;
}
// crc32 hash, taken somewhere from the internet

View File

@ -7,9 +7,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <set>
#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 <typename T>
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);

View File

@ -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<Controller> weak);
rpl::producer<State> state() const;
// Password step.
void submitPassword(const QString &password);
void requestPasswordRecover();
rpl::producer<PasswordUpdate> 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<State> fails to compile in MSVC :(
State _state;
rpl::event_stream<State> _stateChanges;
mtpRequestId _passwordRequestId = 0;
};
Controller::Controller(crl::weak_on_queue<Controller> weak)
: _mtp(weak)
, _state(PasswordCheckState{}) {
requestPasswordState();
}
rpl::producer<State> Controller::state() const {
return rpl::single(
_state
) | rpl::then(
_stateChanges.events()
) | rpl::filter([](const State &state) {
const auto password = base::get_if<PasswordCheckState>(&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<PasswordUpdate> Controller::passwordUpdate() const {
return rpl::never<PasswordUpdate>();
}
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<State> 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<PasswordUpdate> 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

View File

@ -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 <crl/crl_object_on_queue.h>
#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<RPCError> apiError;
base::optional<QString> 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> state() const;
// Password step.
void submitPassword(const QString &password);
void requestPasswordRecover();
rpl::producer<PasswordUpdate> passwordUpdate() const;
void reloadPasswordState();
void cancelUnconfirmedPassword();
// Processing step.
void startExport(const Settings &settings);
rpl::lifetime &lifetime();
~ControllerWrap();
private:
crl::object_on_queue<Controller> _wrapped;
rpl::lifetime _lifetime;
};
} // namespace Export

View File

@ -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"

View File

@ -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 <QtCore/QFile>
#include <QtCore/QDir>
#include <QtCore/QTextStream>
#include <crl/crl.h>
#include <rpl/rpl.h>
#include "scheme.h"
#include "logs.h"

View File

@ -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<Type>;
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<Type>;
friend inline constexpr auto is_flag_type(Type) { return true; };
QString path;
Types types = DefaultTypes();
MediaSettings defaultMedia;
base::flat_map<Type, MediaSettings> customMedia;
static inline Types DefaultTypes() {
return Type::PersonalInfo
| Type::Avatars
| Type::Contacts
| Type::Sessions
| Type::PersonalChats;
}
};
} // namespace Export

View File

@ -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);

View File

@ -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<Ui::VerticalLayout>(this);
const auto label = content->add(
object_ptr<Ui::FlatLabel>(
content,
"Done! " + textcmdLink(1, "Press here") + " to view your data.",
Ui::FlatLabel::InitType::Rich,
st::defaultFlatLabel),
st::exportSettingPadding);
label->setLink(1, std::make_shared<LambdaClickHandler>([=] {
_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

View File

@ -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

View File

@ -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<ControllerWrap*> process)
: _process(process) {
_process->state(
) | rpl::start_with_next([=](State &&state) {
updateState(std::move(state));
}, _lifetime);
}
void PanelController::createPanel() {
_panel = base::make_unique_q<Ui::SeparatePanel>();
_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<SettingsWidget>(_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<ProcessingState>();
});
}
void PanelController::updateState(State &&state) {
if (!_panel) {
createPanel();
}
_state = std::move(state);
if (const auto finished = base::get_if<FinishedState>(&_state)) {
const auto path = finished->path;
auto done = base::make_unique_q<DoneWidget>(_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

View File

@ -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<ControllerWrap*> process);
rpl::producer<> closed() const;
~PanelController();
private:
void createPanel();
void updateState(State &&state);
void showSettings();
not_null<ControllerWrap*> _process;
base::unique_qptr<Ui::SeparatePanel> _panel;
State _state;
rpl::event_stream<rpl::producer<>> _panelCloseEvents;
rpl::lifetime _lifetime;
};
} // namespace View
} // namespace Export

View File

@ -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<Ui::VerticalLayout>(this);
const auto buttonsPadding = st::boxButtonPadding;
const auto buttonsHeight = buttonsPadding.top()
+ st::defaultBoxButton.height
+ buttonsPadding.bottom();
const auto buttons = Ui::CreateChild<Ui::FixedHeightWidget>(
this,
buttonsHeight);
const auto refreshButtonsCallback = [=] {
refreshButtons(buttons);
};
const auto addOption = [&](LangKey key, Types types) {
const auto checkbox = content->add(
object_ptr<Ui::Checkbox>(
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<Ui::RpWidget*> 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<Ui::RoundButton>(
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<Ui::RoundButton>(
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<Settings> 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

View File

@ -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<Settings> 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<Ui::RpWidget*> container);
Settings _data;
struct Wrap {
Wrap(rpl::producer<> value = rpl::never<>())
: value(std::move(value)) {
}
rpl::producer<> value;
};
rpl::variable<Wrap> _startClicks;
rpl::variable<Wrap> _cancelClicks;
};
} // namespace View
} // namespace Export

View File

@ -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<ConcurrentSender*> sender,
Fn<void(FnMut<void()>)> run);
void operator()(
mtpRequestId requestId,
const mtpPrime *from,
const mtpPrime *end) override;
private:
base::weak_ptr<ConcurrentSender> _weak;
Fn<void(FnMut<void()>)> _run;
};
class ConcurrentSender::RPCFailHandler : public RPCAbstractFailHandler {
public:
RPCFailHandler(
not_null<ConcurrentSender*> sender,
Fn<void(FnMut<void()>)> run,
FailSkipPolicy skipPolicy);
bool operator()(
mtpRequestId requestId,
const RPCError &error) override;
private:
base::weak_ptr<ConcurrentSender> _weak;
Fn<void(FnMut<void()>)> _run;
FailSkipPolicy _skipPolicy = FailSkipPolicy::Simple;
};
ConcurrentSender::RPCDoneHandler::RPCDoneHandler(
not_null<ConcurrentSender*> sender,
Fn<void(FnMut<void()>)> 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<ConcurrentSender*> sender,
Fn<void(FnMut<void()>)> 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 <typename Method>
auto ConcurrentSender::with_instance(Method &&method)
-> std::enable_if_t<is_callable_v<Method, not_null<Instance*>>> {
crl::on_main([method = std::forward<Method>(method)]() mutable {
if (const auto instance = MainInstance()) {
std::move(method)(instance);
}
});
}
ConcurrentSender::RequestBuilder::RequestBuilder(
not_null<ConcurrentSender*> 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<RPCDoneHandler>(_sender, _sender->_run),
fail = std::make_shared<RPCFailHandler>(
_sender,
_sender->_run,
_failSkipPolicy)
](not_null<Instance*> instance) mutable {
instance->sendSerialized(
requestId,
std::move(request),
RPCResponseHandler(std::move(done), std::move(fail)),
dcId,
msCanWait,
afterRequestId);
});
return requestId;
}
ConcurrentSender::ConcurrentSender(Fn<void(FnMut<void()>)> 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) {
instance->cancel(requestId);
});
}
void ConcurrentSender::senderRequestCancelAll() {
auto list = std::vector<mtpRequestId>(_requests.size());
for (const auto &[requestId, handlers] : base::take(_requests)) {
list.push_back(requestId);
}
with_instance([list = std::move(list)](not_null<Instance*> instance) {
for (const auto requestId : list) {
instance->cancel(requestId);
}
});
}
} // namespace MTP

View File

@ -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 <rpl/details/callable.h>
#include <crl/crl_object_on_queue.h>
#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 <typename ...Args>
static constexpr bool is_callable_v = rpl::details::is_callable_v<Args...>;
template <typename Method>
auto with_instance(Method &&method)
-> std::enable_if_t<is_callable_v<Method, not_null<Instance*>>>;
using DoneHandler = FnMut<void(
mtpRequestId requestId,
bytes::const_span result)>;
using FailHandler = FnMut<void(
mtpRequestId requestId,
RPCError &&error)>;
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<ConcurrentSender*> sender,
mtpRequest &&serialized) noexcept;
void setToDC(ShiftedDcId dcId) noexcept;
void setCanWait(TimeMs ms) noexcept;
template <typename Response, typename InvokeFullDone>
void setDoneHandler(InvokeFullDone &&invoke) noexcept;
template <typename InvokeFullFail>
void setFailHandler(InvokeFullFail &&invoke) noexcept;
void setFailSkipPolicy(FailSkipPolicy policy) noexcept;
void setAfter(mtpRequestId requestId) noexcept;
private:
not_null<ConcurrentSender*> _sender;
mtpRequest _serialized;
ShiftedDcId _dcId = 0;
TimeMs _canWait = 0;
Handlers _handlers;
FailSkipPolicy _failSkipPolicy = FailSkipPolicy::Simple;
mtpRequestId _afterRequestId = 0;
};
public:
ConcurrentSender(Fn<void(FnMut<void()>)> run);
template <typename Type>
ConcurrentSender(const crl::weak_on_queue<Type> &weak);
template <typename Request>
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 <typename Handler>
[[nodiscard]] SpecificRequestBuilder &done(Handler &&handler);
template <typename Handler>
[[nodiscard]] SpecificRequestBuilder &fail(Handler &&handler);
[[nodiscard]] SpecificRequestBuilder &handleFloodErrors() noexcept;
[[nodiscard]] SpecificRequestBuilder &handleAllErrors() noexcept;
[[nodiscard]] SpecificRequestBuilder &afterRequest(
mtpRequestId requestId) noexcept;
private:
SpecificRequestBuilder(
not_null<ConcurrentSender*> sender,
Request &&request) noexcept;
friend class ConcurrentSender;
};
class SentRequestWrap {
public:
void cancel() {
_sender->senderRequestCancel(_requestId);
}
private:
friend class ConcurrentSender;
SentRequestWrap(not_null<ConcurrentSender*> sender, mtpRequestId requestId) : _sender(sender), _requestId(requestId) {
}
not_null<ConcurrentSender*> _sender;
mtpRequestId _requestId = 0;
};
template <
typename Request,
typename = std::enable_if_t<!std::is_reference_v<Request>>,
typename = typename Request::Unboxed>
[[nodiscard]] SpecificRequestBuilder<Request> 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<void(FnMut<void()>)> _run;
base::flat_map<mtpRequestId, Handlers> _requests;
};
template <typename Type>
ConcurrentSender::ConcurrentSender(const crl::weak_on_queue<Type> &weak)
: ConcurrentSender([=](FnMut<void()> method) {
weak.with([method = std::move(method)](Type&) mutable {
std::move(method)();
});
}) {
}
template <typename Response, typename InvokeFullDone>
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<const mtpPrime*>(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 <typename InvokeFullFail>
void ConcurrentSender::RequestBuilder::setFailHandler(
InvokeFullFail &&invoke) noexcept {
_handlers.fail = std::move(invoke);
}
template <typename Request>
ConcurrentSender::SpecificRequestBuilder<Request>::SpecificRequestBuilder(
not_null<ConcurrentSender*> sender,
Request &&request) noexcept
: RequestBuilder(sender, mtpRequestData::serialize(request)) {
}
template <typename Request>
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::toDC(
ShiftedDcId dcId) noexcept -> SpecificRequestBuilder & {
setToDC(dcId);
return *this;
}
template <typename Request>
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::afterDelay(
TimeMs ms) noexcept -> SpecificRequestBuilder & {
setCanWait(ms);
return *this;
}
template <typename Request>
template <typename Handler>
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::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<Handler>;
if constexpr (takesFull) {
setDoneHandler<Response>(std::forward<Handler>(handler));
} else if constexpr (takesResponse) {
setDoneHandler<Response>([handler = std::forward<Handler>(handler)](
mtpRequestId requestId,
Response &&result) mutable {
std::move(handler)(std::move(result));
});
} else if constexpr (takesRequestId) {
setDoneHandler<Response>([handler = std::forward<Handler>(handler)](
mtpRequestId requestId,
Response &&result) mutable {
std::move(handler)(requestId);
});
} else if constexpr (takesNone) {
setDoneHandler<Response>([handler = std::forward<Handler>(handler)](
mtpRequestId requestId,
Response &&result) mutable {
std::move(handler)();
});
} else {
static_assert(false_t(Handler{}), "Bad done handler.");
}
return *this;
}
template <typename Request>
template <typename Handler>
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::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<Handler>;
if constexpr (takesFull) {
setFailHandler(std::forward<Handler>(handler));
} else if constexpr (takesError) {
setFailHandler([handler = std::forward<Handler>(handler)](
mtpRequestId requestId,
RPCError &&error) mutable {
std::move(handler)(std::move(error));
});
} else if constexpr (takesRequestId) {
setFailHandler([handler = std::forward<Handler>(handler)](
mtpRequestId requestId,
RPCError &&error) mutable {
std::move(handler)(requestId);
});
} else if constexpr (takesNone) {
setFailHandler([handler = std::forward<Handler>(handler)](
mtpRequestId requestId,
RPCError &&error) mutable {
std::move(handler)();
});
} else {
static_assert(false_t(Handler{}), "Bad fail handler.");
}
return *this;
}
template <typename Request>
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::handleFloodErrors() noexcept
-> SpecificRequestBuilder & {
setFailSkipPolicy(FailSkipPolicy::HandleFlood);
return *this;
}
template <typename Request>
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::handleAllErrors() noexcept
-> SpecificRequestBuilder & {
setFailSkipPolicy(FailSkipPolicy::HandleAll);
return *this;
}
template <typename Request>
[[nodiscard]] auto ConcurrentSender::SpecificRequestBuilder<Request>::afterRequest(
mtpRequestId requestId) noexcept -> SpecificRequestBuilder & {
setAfter(requestId);
return *this;
}
template <typename Request, typename, typename>
inline auto ConcurrentSender::request(Request &&request) noexcept
-> SpecificRequestBuilder<Request> {
return SpecificRequestBuilder<Request>(this, std::move(request));
}
inline auto ConcurrentSender::request(mtpRequestId requestId) noexcept
-> SentRequestWrap {
return SentRequestWrap(this, requestId);
}
} // namespace MTP

View File

@ -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<quint64>,qint64,bool,bool)), sessionData->owner(), SLOT(resendMany(QVector<quint64>,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)));

View File

@ -9,6 +9,36 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#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) {

View File

@ -7,9 +7,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <gsl/gsl>
#include <QtCore/QVector>
#include <QtCore/QString>
#include <QtCore/QByteArray>
#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 @@ public:
}
};
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 @@ public:
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 @@ public:
}
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<T> &a, const MTPvector<T> &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);

View File

@ -76,10 +76,19 @@ public:
std::unique_ptr<internal::Connection> &&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<RPCCallbackClear> &&ids);
@ -87,8 +96,8 @@ public:
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 @@ public:
void setSessionResetHandler(Fn<void(ShiftedDcId shiftedDcId)> handler);
void clearGlobalHandlers();
internal::Session *getSession(ShiftedDcId shiftedDcId);
not_null<internal::Session*> 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<internal::Session*> 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::onStateChange(ShiftedDcId shiftedDcId, int32 state) {
_private->onStateChange(shiftedDcId, state);
}
void Instance::onSessionReset(ShiftedDcId dcWithShift) {
_private->onSessionReset(dcWithShift);
}
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<RPCCallbackClear> &&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;
bool needsLayer,
mtpRequestId afterRequestId) {
return _private->sendRequest(
requestId,
std::move(request),
std::move(callbacks),
shiftedDcId,
msCanWait,
needsLayer,
afterRequestId);
}
void Instance::sendAnything(ShiftedDcId dcId, TimeMs msCanWait) {
if (const auto session = _private->getSession(dcId)) {
session->sendAnything(msCanWait);
}
void Instance::sendAnything(ShiftedDcId shiftedDcId, TimeMs msCanWait) {
const auto session = _private->getSession(shiftedDcId);
session->sendAnything(msCanWait);
}
Instance::~Instance() {

View File

@ -64,15 +64,18 @@ public:
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 <typename TRequest>
@ -80,18 +83,52 @@ public:
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,
after);
afterRequestId);
}
void sendAnything(ShiftedDcId dcId = 0, TimeMs msCanWait = 0);
template <typename TRequest>
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,
needsLayer,
afterRequestId);
}
void sendAnything(ShiftedDcId shiftedDcId = 0, TimeMs msCanWait = 0);
void restart();
void restart(ShiftedDcId shiftedDcId);
@ -116,14 +153,9 @@ public:
void setSessionResetHandler(Fn<void(ShiftedDcId shiftedDcId)> 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<RPCCallbackClear> &&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> _private;

View File

@ -7,18 +7,44 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#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);
}
}

View File

@ -8,25 +8,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include <rpl/details/callable.h>
#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 @@ private:
};
class RPCSender {
using DoneHandlers = QSet<RPCOwnedDoneHandler*>;
DoneHandlers _rpcDoneHandlers;
using FailHandlers = QSet<RPCOwnedFailHandler*>;
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 <typename TReturn, typename TReceiver> // done(from, end)
RPCDoneHandlerPtr rpcDone(TReturn (TReceiver::*onDone)(const mtpPrime *, const mtpPrime *)) {
@ -831,6 +793,29 @@ protected:
}
}
private:
base::flat_set<RPCOwnedDoneHandler*> _rpcDoneHandlers;
base::flat_set<RPCOwnedFailHandler*> _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);

View File

@ -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);

View File

@ -319,14 +319,6 @@ public:
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*> _instance;

View File

@ -26,12 +26,15 @@ Panel::Panel(not_null<PanelController*> 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<Ui::RpWidget*> Panel::widget() const {
}
int Panel::hideAndDestroyGetDuration() {
return _widget->hideAndDestroyGetDuration();
return _widget->hideGetDuration();
}
void Panel::showAskPassword() {

View File

@ -1206,6 +1206,10 @@ void PanelController::cancelAuth() {
_form->cancel();
}
void PanelController::cancelAuthSure() {
_form->cancelSure();
}
void PanelController::showBox(
object_ptr<BoxContent> box,
LayerOptions options,

View File

@ -123,6 +123,7 @@ public:
int closeGetDuration() override;
void cancelAuth();
void cancelAuthSure();
rpl::lifetime &lifetime();

View File

@ -10,6 +10,15 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <rpl/producer.h>
#include <rpl/event_stream.h>
namespace mapbox {
namespace util {
template <typename ...Types>
class variant;
} // namespace util
} // namespace mapbox
namespace rpl {
namespace details {
@ -17,18 +26,38 @@ template <typename A, typename B>
struct supports_equality_compare {
template <typename U, typename V>
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<A>*)nullptr,
(std::decay_t<B>*)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 <typename Type1, typename ...Types1>
//struct supports_equality_compare<
// mapbox::util::variant<Type1, Types1...>,
// mapbox::util::variant<Type1, Types1...>> {
// static constexpr bool value
// = (supports_equality_compare<Type1, Type1>::value
// && supports_equality_compare<
// mapbox::util::variant<Types1...>,
// mapbox::util::variant<Types1...>>::value);
//
//};
//template <typename Type>
//struct supports_equality_compare<
// mapbox::util::variant<Type>,
// mapbox::util::variant<Type>> {
// static constexpr bool value = supports_equality_compare<Type, Type>::value;
//
//};
template <typename A, typename B>
constexpr bool supports_equality_compare_v
= supports_equality_compare<A, B>::value;
= supports_equality_compare<std::decay_t<A>, std::decay_t<B>>::value;
} // namespace details

View File

@ -30,6 +30,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#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 = {

View File

@ -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) {

View File

@ -31,7 +31,7 @@ public:
void setInnerSize(QSize size);
void showAndActivate();
int hideAndDestroyGetDuration();
int hideGetDuration();
void showInner(base::unique_qptr<Ui::RpWidget> inner);
void showBox(
@ -42,7 +42,7 @@ public:
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 @@ private:
void toggleOpacityAnimation(bool visible);
void finishAnimating();
void destroyDelayed();
void finishClose();
object_ptr<Ui::IconButton> _close;
object_ptr<Ui::FlatLabel> _title = { nullptr };
@ -83,7 +83,8 @@ private:
base::unique_qptr<Ui::RpWidget> _inner;
object_ptr<Window::LayerStackWidget> _layer = { nullptr };
rpl::event_stream<> _synteticBackRequests;
rpl::event_stream<> _destroyRequests;
rpl::event_stream<> _userCloseRequests;
rpl::event_stream<> _closeEvents;
bool _useTransparency = true;
style::margins _padding;

View File

@ -16,6 +16,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#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<Export::ControllerWrap>();
_exportPanel = std::make_unique<Export::View::PanelController>(
_export.get());
_exportPanel->closed(
) | rpl::start_with_next([=] {
clearDataExport();
}, _export->lifetime());
}
void Controller::clearDataExport() {
_exportPanel = nullptr;
_export = nullptr;
}
void Controller::updateColumnLayout() {
App::main()->updateColumnLayout();
}

View File

@ -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 @@ public:
void showPassportForm(const Passport::FormRequest &request);
void clearPassportForm();
void startDataExport();
base::Variable<bool> &dialogsListFocused() {
return _dialogsListFocused;
}
@ -251,10 +260,13 @@ private:
int dialogsWidth,
int thirdWidth,
int bodyWidth) const;
void clearDataExport();
not_null<MainWindow*> _window;
std::unique_ptr<Passport::FormController> _passportForm;
std::unique_ptr<Export::ControllerWrap> _export;
std::unique_ptr<Export::View::PanelController> _exportPanel;
GifPauseReasons _gifPauseReasons = 0;
base::Observable<void> _gifPauseLevelChanged;

View File

@ -69,13 +69,15 @@ endmacro()
function(export_all_flags _filename _source_name_for_flags)
set(_include_directories "$<TARGET_PROPERTY:${_target},INCLUDE_DIRECTORIES>")
set(_compile_definitions "$<TARGET_PROPERTY:${_target},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 "$<TARGET_PROPERTY:${_target},COMPILE_FLAGS>")
set(_compile_options "$<TARGET_PROPERTY:${_target},COMPILE_OPTIONS>")
set(_include_directories "$<$<BOOL:${_include_directories}>:-I$<JOIN:${_include_directories},\n-I>\n>")
set(_compile_definitions "$<$<BOOL:${_compile_definitions}>:-D$<JOIN:${_compile_definitions},\n-D>\n>")
set(_compile_file_flags "$<$<BOOL:${_compile_file_flags}>:$<JOIN:${_compile_file_flags},\n>\n>")
set(_compile_flags "$<$<BOOL:${_compile_flags}>:$<JOIN:${_compile_flags},\n>\n>")
set(_compile_options "$<$<BOOL:${_compile_options}>:$<JOIN:${_compile_options},\n>\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})

View File

@ -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': [

View File

@ -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': [

View File

@ -11,7 +11,8 @@
'targets': [{
'target_name': 'crl',
'type': 'static_library',
'dependencies': [],
'dependencies': [
],
'includes': [
'common.gypi',
'qt.gypi',

View File

@ -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',
],
}],
}

View File

@ -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,
}],
}],
}

20
Telegram/gyp/pch.gypi Normal file
View File

@ -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)',
],
}

View File

@ -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': {

View File

@ -113,7 +113,5 @@
],
}]
],
'cmake_precompiled_header': '<(src_loc)/stdafx.h',
'cmake_precompiled_header_script': 'PrecompiledHeader.cmake',
}]],
}

View File

@ -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': '<!(./print_version.sh)',
'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon',

View File

@ -222,6 +222,12 @@
<(src_loc)/dialogs/dialogs_search_from_controllers.h
<(src_loc)/dialogs/dialogs_widget.cpp
<(src_loc)/dialogs/dialogs_widget.h
<(src_loc)/export/view/export_view_done.cpp
<(src_loc)/export/view/export_view_done.h
<(src_loc)/export/view/export_view_panel_controller.cpp
<(src_loc)/export/view/export_view_panel_controller.h
<(src_loc)/export/view/export_view_settings.cpp
<(src_loc)/export/view/export_view_settings.h
<(src_loc)/history/admin_log/history_admin_log_filter.cpp
<(src_loc)/history/admin_log/history_admin_log_filter.h
<(src_loc)/history/admin_log/history_admin_log_inner.cpp
@ -417,6 +423,8 @@
<(src_loc)/media/media_clip_reader.h
<(src_loc)/mtproto/auth_key.cpp
<(src_loc)/mtproto/auth_key.h
<(src_loc)/mtproto/concurrent_sender.cpp
<(src_loc)/mtproto/concurrent_sender.h
<(src_loc)/mtproto/config_loader.cpp
<(src_loc)/mtproto/config_loader.h
<(src_loc)/mtproto/connection.cpp
@ -780,8 +788,6 @@
<(src_loc)/settings.h
<(src_loc)/shortcuts.cpp
<(src_loc)/shortcuts.h
<(src_loc)/stdafx.cpp
<(src_loc)/stdafx.h
<(emoji_suggestions_loc)/emoji_suggestions.cpp
<(emoji_suggestions_loc)/emoji_suggestions.h

View File

@ -6,8 +6,6 @@
{
'conditions': [[ 'build_win', {
'msvs_precompiled_source': '<(src_loc)/stdafx.cpp',
'msvs_precompiled_header': '<(src_loc)/stdafx.h',
'msbuild_toolset': 'v141',
'sources': [
'<(res_loc)/winrc/Telegram.rc',