From e106bd143e2b46c5e6e648d85959b12907cd5a88 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 2 Apr 2021 16:59:54 +0400 Subject: [PATCH] Add a warning once per bot on payment. --- .../SourceFiles/core/click_handler_types.cpp | 4 +- .../payments/payments_checkout_process.cpp | 11 ++++ .../payments/payments_checkout_process.h | 2 +- .../SourceFiles/payments/payments_form.cpp | 11 ++++ Telegram/SourceFiles/payments/payments_form.h | 6 ++ .../SourceFiles/payments/ui/payments.style | 4 ++ .../payments/ui/payments_field.cpp | 2 +- .../payments/ui/payments_panel.cpp | 23 +++++++ .../SourceFiles/payments/ui/payments_panel.h | 1 + .../payments/ui/payments_panel_delegate.h | 1 + .../SourceFiles/storage/storage_account.cpp | 62 +++++++++++++++---- .../SourceFiles/storage/storage_account.h | 14 ++++- 12 files changed, 123 insertions(+), 18 deletions(-) diff --git a/Telegram/SourceFiles/core/click_handler_types.cpp b/Telegram/SourceFiles/core/click_handler_types.cpp index 38a16c4b7..3255f417c 100644 --- a/Telegram/SourceFiles/core/click_handler_types.cpp +++ b/Telegram/SourceFiles/core/click_handler_types.cpp @@ -86,12 +86,12 @@ void BotGameUrlClickHandler::onClick(ClickContext context) const { open(); } else if (!_bot || _bot->isVerified() - || _bot->session().local().isBotTrusted(_bot)) { + || _bot->session().local().isBotTrustedOpenGame(_bot->id)) { open(); } else { const auto callback = [=, bot = _bot] { Ui::hideLayer(); - bot->session().local().markBotTrusted(bot); + bot->session().local().markBotTrustedOpenGame(bot->id); open(); }; Ui::show(Box( diff --git a/Telegram/SourceFiles/payments/payments_checkout_process.cpp b/Telegram/SourceFiles/payments/payments_checkout_process.cpp index 30b56a694..cc0fa4fe4 100644 --- a/Telegram/SourceFiles/payments/payments_checkout_process.cpp +++ b/Telegram/SourceFiles/payments/payments_checkout_process.cpp @@ -166,6 +166,12 @@ void CheckoutProcess::handleFormUpdate(const FormUpdate &update) { }, [&](const TmpPasswordRequired &) { _submitState = SubmitState::Validated; requestPassword(); + }, [&](const BotTrustRequired &data) { + _submitState = SubmitState::Validated; + _panel->showWarning(data.bot->name, data.provider->name); + if (const auto box = _enterPasswordBox.data()) { + box->closeBox(); + } }, [&](const VerificationNeeded &data) { auto bottomText = tr::lng_payments_processed_by( lt_provider, @@ -366,6 +372,11 @@ void CheckoutProcess::panelSubmit() { } } +void CheckoutProcess::panelTrustAndSubmit() { + _form->trustBot(); + panelSubmit(); +} + void CheckoutProcess::panelWebviewMessage( const QJsonDocument &message, bool saveInformation) { diff --git a/Telegram/SourceFiles/payments/payments_checkout_process.h b/Telegram/SourceFiles/payments/payments_checkout_process.h index 26d207d85..0c03f69bf 100644 --- a/Telegram/SourceFiles/payments/payments_checkout_process.h +++ b/Telegram/SourceFiles/payments/payments_checkout_process.h @@ -87,7 +87,6 @@ private: void editPaymentMethod(); void requestSetPassword(); - void requestSetPasswordSure(QPointer old); void requestPassword(); void getPasswordState( Fn callback); @@ -97,6 +96,7 @@ private: void panelRequestClose() override; void panelCloseSure() override; void panelSubmit() override; + void panelTrustAndSubmit() override; void panelWebviewMessage( const QJsonDocument &message, bool saveInformation) override; diff --git a/Telegram/SourceFiles/payments/payments_form.cpp b/Telegram/SourceFiles/payments/payments_form.cpp index 4c94b109c..33b1ded9f 100644 --- a/Telegram/SourceFiles/payments/payments_form.cpp +++ b/Telegram/SourceFiles/payments/payments_form.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "stripe/stripe_error.h" #include "stripe/stripe_token.h" #include "stripe/stripe_card_validator.h" +#include "storage/storage_account.h" #include "ui/image/image.h" #include "apiwrap.h" #include "core/core_cloud_password.h" @@ -478,6 +479,12 @@ void Form::submit() { if (!_paymentMethod.newCredentials && password.isEmpty()) { _updates.fire(TmpPasswordRequired{}); return; + } else if (!_session->local().isBotTrustedPayment(_details.botId)) { + _updates.fire(BotTrustRequired{ + .bot = _session->data().user(_details.botId), + .provider = _session->data().user(_details.providerId), + }); + return; } using Flag = MTPpayments_SendPaymentForm::Flag; @@ -773,6 +780,10 @@ void Form::setTips(int64 value) { _invoice.tipsSelected = std::min(value, _invoice.tipsMax); } +void Form::trustBot() { + _session->local().markBotTrustedPayment(_details.botId); +} + void Form::processShippingOptions(const QVector &data) { const auto currency = _invoice.currency; _shippingOptions = Ui::ShippingOptions{ currency, ranges::views::all( diff --git a/Telegram/SourceFiles/payments/payments_form.h b/Telegram/SourceFiles/payments/payments_form.h index 90e84b5ca..71c2690c8 100644 --- a/Telegram/SourceFiles/payments/payments_form.h +++ b/Telegram/SourceFiles/payments/payments_form.h @@ -118,6 +118,10 @@ struct VerificationNeeded { QString url; }; struct TmpPasswordRequired {}; +struct BotTrustRequired { + not_null bot; + not_null provider; +}; struct PaymentFinished { MTPUpdates updates; }; @@ -148,6 +152,7 @@ struct FormUpdate : std::variant< PaymentMethodUpdate, VerificationNeeded, TmpPasswordRequired, + BotTrustRequired, PaymentFinished, Error> { using variant::variant; @@ -187,6 +192,7 @@ public: void setHasPassword(bool has); void setShippingOption(const QString &id); void setTips(int64 value); + void trustBot(); void submit(); void submit(const Core::CloudPasswordResult &result); diff --git a/Telegram/SourceFiles/payments/ui/payments.style b/Telegram/SourceFiles/payments/ui/payments.style index fa068a568..b35eace9a 100644 --- a/Telegram/SourceFiles/payments/ui/payments.style +++ b/Telegram/SourceFiles/payments/ui/payments.style @@ -79,6 +79,10 @@ paymentsIconPhone: icon {{ "payments/payment_phone", menuIconFg }}; paymentsIconShippingMethod: icon {{ "payments/payment_shipping", menuIconFg }}; paymentsField: defaultInputField; +paymentsMoneyField: InputField(paymentsField) { + textMargins: margins(0px, 4px, 0px, 4px); + heightMin: 30px; +} paymentsFieldAdditional: FlatLabel(defaultFlatLabel) { style: boxTextStyle; } diff --git a/Telegram/SourceFiles/payments/ui/payments_field.cpp b/Telegram/SourceFiles/payments/ui/payments_field.cpp index f4d99fccd..8296a7be4 100644 --- a/Telegram/SourceFiles/payments/ui/payments_field.cpp +++ b/Telegram/SourceFiles/payments/ui/payments_field.cpp @@ -289,7 +289,7 @@ struct SimpleFieldState { }; const auto state = wrap->lifetime().make_state(State{ .rule = LookupCurrencyRule(config.currency), - .st = st::paymentsField, + .st = st::paymentsMoneyField, }); const auto &rule = state->rule; state->currencySkip = rule.space ? state->st.font->spacew : 0; diff --git a/Telegram/SourceFiles/payments/ui/payments_panel.cpp b/Telegram/SourceFiles/payments/ui/payments_panel.cpp index 4ece012d3..32ea3b513 100644 --- a/Telegram/SourceFiles/payments/ui/payments_panel.cpp +++ b/Telegram/SourceFiles/payments/ui/payments_panel.cpp @@ -409,6 +409,29 @@ void Panel::showCloseConfirm() { })); } +void Panel::showWarning(const QString &bot, const QString &provider) { + showBox(Box([=](not_null box) { + box->setTitle(tr::lng_payments_warning_title()); + box->addRow(object_ptr( + box.get(), + tr::lng_payments_warning_body( + lt_bot1, + rpl::single(bot), + lt_provider, + rpl::single(provider), + lt_bot2, + rpl::single(bot), + lt_bot3, + rpl::single(bot)), + st::boxLabel)); + box->addButton(tr::lng_continue(), [=] { + _delegate->panelTrustAndSubmit(); + box->closeBox(); + }); + box->addButton(tr::lng_cancel(), [=] { box->closeBox(); }); + })); +} + void Panel::showEditCard( const NativeMethodDetails &native, CardField field) { diff --git a/Telegram/SourceFiles/payments/ui/payments_panel.h b/Telegram/SourceFiles/payments/ui/payments_panel.h index 88e8b17cb..db7d34bdd 100644 --- a/Telegram/SourceFiles/payments/ui/payments_panel.h +++ b/Telegram/SourceFiles/payments/ui/payments_panel.h @@ -69,6 +69,7 @@ public: void choosePaymentMethod(const PaymentMethodDetails &method); void askSetPassword(); void showCloseConfirm(); + void showWarning(const QString &bot, const QString &provider); bool showWebview( const QString &url, diff --git a/Telegram/SourceFiles/payments/ui/payments_panel_delegate.h b/Telegram/SourceFiles/payments/ui/payments_panel_delegate.h index 13a9a86c6..b3f7b1a73 100644 --- a/Telegram/SourceFiles/payments/ui/payments_panel_delegate.h +++ b/Telegram/SourceFiles/payments/ui/payments_panel_delegate.h @@ -28,6 +28,7 @@ public: virtual void panelRequestClose() = 0; virtual void panelCloseSure() = 0; virtual void panelSubmit() = 0; + virtual void panelTrustAndSubmit() = 0; virtual void panelWebviewMessage( const QJsonDocument &message, bool saveInformation) = 0; diff --git a/Telegram/SourceFiles/storage/storage_account.cpp b/Telegram/SourceFiles/storage/storage_account.cpp index 6b5f78eba..a996f30e4 100644 --- a/Telegram/SourceFiles/storage/storage_account.cpp +++ b/Telegram/SourceFiles/storage/storage_account.cpp @@ -2509,8 +2509,12 @@ void Account::writeTrustedBots() { quint32 size = sizeof(qint32) + _trustedBots.size() * sizeof(quint64); EncryptedDescriptor data(size); data.stream << qint32(_trustedBots.size()); - for_const (auto botId, _trustedBots) { - data.stream << quint64(botId); + for (const auto &[peerId, mask] : _trustedBots) { + // value: 8 bit mask, 56 bit bot peer_id. + auto value = quint64(peerId); + Assert((value >> 56) == 0); + value |= (quint64(mask) << 56); + data.stream << value; } FileWriteDescriptor file(_trustedBotsKey, _basePath); @@ -2531,25 +2535,61 @@ void Account::readTrustedBots() { qint32 size = 0; trusted.stream >> size; for (int i = 0; i < size; ++i) { - quint64 botId = 0; - trusted.stream >> botId; - _trustedBots.insert(botId); + auto value = quint64(); + trusted.stream >> value; + const auto mask = base::flags::from_raw( + uchar(value >> 56)); + const auto peerId = value & ~(0xFFULL << 56); + _trustedBots.emplace(peerId, mask); } } -void Account::markBotTrusted(not_null bot) { - if (!isBotTrusted(bot)) { - _trustedBots.insert(bot->id); - writeTrustedBots(); +void Account::markBotTrustedOpenGame(PeerId botId) { + if (isBotTrustedOpenGame(botId)) { + return; } + const auto i = _trustedBots.find(botId); + if (i == end(_trustedBots)) { + _trustedBots.emplace(botId, BotTrustFlag()); + } else { + i->second &= ~BotTrustFlag::NoOpenGame; + } + writeTrustedBots(); } -bool Account::isBotTrusted(not_null bot) { +bool Account::isBotTrustedOpenGame(PeerId botId) { if (!_trustedBotsRead) { readTrustedBots(); _trustedBotsRead = true; } - return _trustedBots.contains(bot->id); + const auto i = _trustedBots.find(botId); + return (i != end(_trustedBots)) + && ((i->second & BotTrustFlag::NoOpenGame) == 0); +} + +void Account::markBotTrustedPayment(PeerId botId) { + if (isBotTrustedPayment(botId)) { + return; + } + const auto i = _trustedBots.find(botId); + if (i == end(_trustedBots)) { + _trustedBots.emplace( + botId, + BotTrustFlag::NoOpenGame | BotTrustFlag::Payment); + } else { + i->second |= BotTrustFlag::Payment; + } + writeTrustedBots(); +} + +bool Account::isBotTrustedPayment(PeerId botId) { + if (!_trustedBotsRead) { + readTrustedBots(); + _trustedBotsRead = true; + } + const auto i = _trustedBots.find(botId); + return (i != end(_trustedBots)) + && ((i->second & BotTrustFlag::Payment) != 0); } bool Account::encrypt( diff --git a/Telegram/SourceFiles/storage/storage_account.h b/Telegram/SourceFiles/storage/storage_account.h index 4cb6fbc5a..e3212631a 100644 --- a/Telegram/SourceFiles/storage/storage_account.h +++ b/Telegram/SourceFiles/storage/storage_account.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "base/timer.h" +#include "base/flags.h" #include "storage/cache/storage_cache_database.h" #include "data/stickers/data_stickers_set.h" #include "data/data_drafts.h" @@ -135,8 +136,10 @@ public: const QByteArray& serialized, int32 streamVersion); - void markBotTrusted(not_null bot); - [[nodiscard]] bool isBotTrusted(not_null bot); + void markBotTrustedOpenGame(PeerId botId); + [[nodiscard]] bool isBotTrustedOpenGame(PeerId botId); + void markBotTrustedPayment(PeerId botId); + [[nodiscard]] bool isBotTrustedPayment(PeerId botId); [[nodiscard]] bool encrypt( const void *src, @@ -157,6 +160,11 @@ private: IncorrectPasscode, Failed, }; + enum class BotTrustFlag : uchar { + NoOpenGame = (1 << 0), + Payment = (1 << 1), + }; + friend inline constexpr bool is_flag_type(BotTrustFlag) { return true; }; [[nodiscard]] base::flat_set collectGoodNames() const; [[nodiscard]] auto prepareReadSettingsContext() const @@ -253,7 +261,7 @@ private: qint32 _cacheTotalTimeLimit = 0; qint32 _cacheBigFileTotalTimeLimit = 0; - base::flat_set _trustedBots; + base::flat_map> _trustedBots; bool _trustedBotsRead = false; bool _readingUserSettings = false; bool _recentHashtagsAndBotsWereRead = false;