tdesktop/Telegram/SourceFiles/boxes/passcode_box.cpp

1142 lines
35 KiB
C++
Raw Normal View History

/*
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
*/
2017-04-06 14:38:10 +00:00
#include "boxes/passcode_box.h"
#include "base/bytes.h"
2017-04-13 08:27:10 +00:00
#include "lang/lang_keys.h"
2017-04-06 14:38:10 +00:00
#include "boxes/confirm_box.h"
2018-11-07 09:39:31 +00:00
#include "boxes/confirm_phone_box.h"
#include "mainwindow.h"
2019-11-27 08:02:56 +00:00
#include "apiwrap.h"
2019-07-24 11:45:24 +00:00
#include "main/main_session.h"
#include "main/main_domain.h"
#include "core/application.h"
#include "storage/storage_domain.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/input_fields.h"
2018-11-07 09:39:31 +00:00
#include "ui/widgets/labels.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/fade_wrap.h"
#include "passport/passport_encryption.h"
2018-11-07 09:39:31 +00:00
#include "passport/passport_panel_edit_contact.h"
#include "facades.h"
2019-09-18 11:19:05 +00:00
#include "styles/style_layers.h"
2018-11-07 09:39:31 +00:00
#include "styles/style_passport.h"
2019-09-18 11:19:05 +00:00
#include "styles/style_boxes.h"
2018-11-07 09:39:31 +00:00
namespace {
} // namespace
PasscodeBox::CloudFields PasscodeBox::CloudFields::From(
const Core::CloudPasswordState &current) {
auto result = CloudFields();
result.curRequest = current.request;
result.newAlgo = current.newPassword;
result.newSecureSecretAlgo = current.newSecureSecret;
result.hasRecovery = current.hasRecovery;
result.notEmptyPassport = current.notEmptyPassport;
result.hint = current.hint;
return result;
}
2019-07-24 14:00:30 +00:00
PasscodeBox::PasscodeBox(
QWidget*,
not_null<Main::Session*> session,
bool turningOff)
: _session(session)
, _api(&_session->mtp())
2019-07-24 14:00:30 +00:00
, _turningOff(turningOff)
2015-10-14 11:51:37 +00:00
, _about(st::boxWidth - st::boxPadding.left() * 1.5)
, _oldPasscode(this, st::defaultInputField, tr::lng_passcode_enter_old())
, _newPasscode(this, st::defaultInputField, Global::LocalPasscode() ? tr::lng_passcode_enter_new() : tr::lng_passcode_enter_first())
, _reenterPasscode(this, st::defaultInputField, tr::lng_passcode_confirm_new())
, _passwordHint(this, st::defaultInputField, tr::lng_cloud_password_hint())
, _recoverEmail(this, st::defaultInputField, tr::lng_cloud_password_email())
2019-06-19 15:09:03 +00:00
, _recover(this, tr::lng_signin_recover(tr::now)) {
}
2019-07-24 14:00:30 +00:00
PasscodeBox::PasscodeBox(
QWidget*,
not_null<Main::Session*> session,
const CloudFields &fields)
: _session(session)
, _api(&_session->mtp())
2019-07-24 14:00:30 +00:00
, _turningOff(fields.turningOff)
2015-10-12 21:02:10 +00:00
, _cloudPwd(true)
, _cloudFields(fields)
2015-10-14 11:51:37 +00:00
, _about(st::boxWidth - st::boxPadding.left() * 1.5)
, _oldPasscode(this, st::defaultInputField, tr::lng_cloud_password_enter_old())
, _newPasscode(this, st::defaultInputField, fields.curRequest ? tr::lng_cloud_password_enter_new() : tr::lng_cloud_password_enter_first())
, _reenterPasscode(this, st::defaultInputField, tr::lng_cloud_password_confirm_new())
, _passwordHint(this, st::defaultInputField, fields.curRequest ? tr::lng_cloud_password_change_hint() : tr::lng_cloud_password_hint())
, _recoverEmail(this, st::defaultInputField, tr::lng_cloud_password_email())
2019-06-19 15:09:03 +00:00
, _recover(this, tr::lng_signin_recover(tr::now)) {
Expects(!_turningOff || _cloudFields.curRequest);
2018-08-03 21:48:00 +00:00
if (!_cloudFields.hint.isEmpty()) {
_hintText.setText(
st::passcodeTextStyle,
tr::lng_signin_hint(tr::now, lt_password_hint, _cloudFields.hint));
}
}
rpl::producer<QByteArray> PasscodeBox::newPasswordSet() const {
return _newPasswordSet.events();
}
rpl::producer<> PasscodeBox::passwordReloadNeeded() const {
return _passwordReloadNeeded.events();
}
2018-11-07 09:39:31 +00:00
rpl::producer<> PasscodeBox::clearUnconfirmedPassword() const {
return _clearUnconfirmedPassword.events();
}
2018-08-03 21:48:00 +00:00
bool PasscodeBox::currentlyHave() const {
return _cloudPwd ? (!!_cloudFields.curRequest) : Global::LocalPasscode();
}
bool PasscodeBox::onlyCheckCurrent() const {
2019-06-14 14:04:30 +00:00
return _turningOff || _cloudFields.customCheckCallback;
2018-08-03 21:48:00 +00:00
}
void PasscodeBox::prepare() {
addButton(
(_cloudFields.customSubmitButton
2019-06-19 11:22:25 +00:00
? std::move(_cloudFields.customSubmitButton)
: _turningOff
? tr::lng_passcode_remove_button()
: tr::lng_settings_save()),
[=] { save(); });
addButton(tr::lng_cancel(), [=] { closeBox(); });
2015-10-12 21:02:10 +00:00
_about.setText(
st::passcodeTextStyle,
2019-06-19 15:09:03 +00:00
(_cloudFields.customDescription
? *_cloudFields.customDescription
: _cloudPwd
? tr::lng_cloud_password_about(tr::now)
: tr::lng_passcode_about(tr::now)));
2015-10-14 11:51:37 +00:00
_aboutHeight = _about.countHeight(st::boxWidth - st::boxPadding.left() * 1.5);
const auto onlyCheck = onlyCheckCurrent();
if (onlyCheck) {
_oldPasscode->show();
setTitle(_cloudFields.customTitle
2019-06-19 11:22:25 +00:00
? std::move(_cloudFields.customTitle)
: _cloudPwd
? tr::lng_cloud_password_remove()
: tr::lng_passcode_remove());
setDimensions(st::boxWidth, st::passcodePadding.top() + _oldPasscode->height() + st::passcodeTextLine + ((_cloudFields.hasRecovery && !_hintText.isEmpty()) ? st::passcodeTextLine : 0) + st::passcodeAboutSkip + _aboutHeight + st::passcodePadding.bottom());
} else {
2018-08-03 21:48:00 +00:00
if (currentlyHave()) {
_oldPasscode->show();
setTitle(_cloudPwd
? tr::lng_cloud_password_change()
: tr::lng_passcode_change());
setDimensions(st::boxWidth, st::passcodePadding.top() + _oldPasscode->height() + st::passcodeTextLine + ((_cloudFields.hasRecovery && !_hintText.isEmpty()) ? st::passcodeTextLine : 0) + _newPasscode->height() + st::passcodeLittleSkip + _reenterPasscode->height() + st::passcodeSkip + (_cloudPwd ? _passwordHint->height() + st::passcodeLittleSkip : 0) + st::passcodeAboutSkip + _aboutHeight + st::passcodePadding.bottom());
} else {
_oldPasscode->hide();
setTitle(_cloudPwd
? tr::lng_cloud_password_create()
: tr::lng_passcode_create());
setDimensions(st::boxWidth, st::passcodePadding.top() + _newPasscode->height() + st::passcodeLittleSkip + _reenterPasscode->height() + st::passcodeSkip + (_cloudPwd ? _passwordHint->height() + st::passcodeLittleSkip : 0) + st::passcodeAboutSkip + _aboutHeight + (_cloudPwd ? (st::passcodeLittleSkip + _recoverEmail->height() + st::passcodeSkip) : st::passcodePadding.bottom()));
}
}
connect(_oldPasscode, &Ui::MaskedInputField::changed, [=] { oldChanged(); });
connect(_newPasscode, &Ui::MaskedInputField::changed, [=] { newChanged(); });
connect(_reenterPasscode, &Ui::MaskedInputField::changed, [=] { newChanged(); });
connect(_passwordHint, &Ui::InputField::changed, [=] { newChanged(); });
connect(_recoverEmail, &Ui::InputField::changed, [=] { emailChanged(); });
2015-04-04 20:01:34 +00:00
const auto fieldSubmit = [=] { submit(); };
connect(_oldPasscode, &Ui::MaskedInputField::submitted, fieldSubmit);
connect(_newPasscode, &Ui::MaskedInputField::submitted, fieldSubmit);
connect(_reenterPasscode, &Ui::MaskedInputField::submitted, fieldSubmit);
connect(_passwordHint, &Ui::InputField::submitted, fieldSubmit);
connect(_recoverEmail, &Ui::InputField::submitted, fieldSubmit);
2015-10-12 21:02:10 +00:00
_recover->addClickHandler([=] { recoverByEmail(); });
2018-08-03 21:48:00 +00:00
const auto has = currentlyHave();
_oldPasscode->setVisible(onlyCheck || has);
_recover->setVisible((onlyCheck || has) && _cloudPwd && _cloudFields.hasRecovery);
_newPasscode->setVisible(!onlyCheck);
_reenterPasscode->setVisible(!onlyCheck);
_passwordHint->setVisible(!onlyCheck && _cloudPwd);
_recoverEmail->setVisible(!onlyCheck && _cloudPwd && !has);
}
void PasscodeBox::submit() {
2018-08-03 21:48:00 +00:00
const auto has = currentlyHave();
if (_oldPasscode->hasFocus()) {
if (onlyCheckCurrent()) {
save();
2015-10-12 21:02:10 +00:00
} else {
_newPasscode->setFocus();
}
} else if (_newPasscode->hasFocus()) {
_reenterPasscode->setFocus();
} else if (_reenterPasscode->hasFocus()) {
if (has && _oldPasscode->text().isEmpty()) {
_oldPasscode->setFocus();
_oldPasscode->showError();
} else if (_newPasscode->text().isEmpty()) {
_newPasscode->setFocus();
_newPasscode->showError();
} else if (_reenterPasscode->text().isEmpty()) {
_reenterPasscode->showError();
} else if (!_passwordHint->isHidden()) {
_passwordHint->setFocus();
2015-10-12 21:02:10 +00:00
} else {
save();
2015-10-12 21:02:10 +00:00
}
} else if (_passwordHint->hasFocus()) {
if (_recoverEmail->isHidden()) {
save();
2015-10-12 21:02:10 +00:00
} else {
_recoverEmail->setFocus();
2015-10-12 21:02:10 +00:00
}
} else if (_recoverEmail->hasFocus()) {
save();
}
}
void PasscodeBox::paintEvent(QPaintEvent *e) {
BoxContent::paintEvent(e);
Painter p(this);
2015-10-14 11:51:37 +00:00
int32 w = st::boxWidth - st::boxPadding.left() * 1.5;
int32 abouty = (_passwordHint->isHidden() ? ((_reenterPasscode->isHidden() ? (_oldPasscode->y() + (_cloudFields.hasRecovery && !_hintText.isEmpty() ? st::passcodeTextLine : 0)) : _reenterPasscode->y()) + st::passcodeSkip) : _passwordHint->y()) + _oldPasscode->height() + st::passcodeLittleSkip + st::passcodeAboutSkip;
2016-10-31 12:29:26 +00:00
p.setPen(st::boxTextFg);
2015-10-14 11:51:37 +00:00
_about.drawLeft(p, st::boxPadding.left(), abouty, w, width());
2015-10-12 21:02:10 +00:00
if (!_hintText.isEmpty() && _oldError.isEmpty()) {
_hintText.drawLeftElided(p, st::boxPadding.left(), _oldPasscode->y() + _oldPasscode->height() + ((st::passcodeTextLine - st::normalFont->height) / 2), w, width(), 1, style::al_topleft);
}
if (!_oldError.isEmpty()) {
2016-10-31 12:29:26 +00:00
p.setPen(st::boxTextFgError);
p.drawText(QRect(st::boxPadding.left(), _oldPasscode->y() + _oldPasscode->height(), w, st::passcodeTextLine), _oldError, style::al_left);
}
if (!_newError.isEmpty()) {
2016-10-31 12:29:26 +00:00
p.setPen(st::boxTextFgError);
p.drawText(QRect(st::boxPadding.left(), _reenterPasscode->y() + _reenterPasscode->height(), w, st::passcodeTextLine), _newError, style::al_left);
}
if (!_emailError.isEmpty()) {
2016-10-31 12:29:26 +00:00
p.setPen(st::boxTextFgError);
p.drawText(QRect(st::boxPadding.left(), _recoverEmail->y() + _recoverEmail->height(), w, st::passcodeTextLine), _emailError, style::al_left);
}
}
void PasscodeBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
2018-08-03 21:48:00 +00:00
const auto has = currentlyHave();
2015-10-14 11:51:37 +00:00
int32 w = st::boxWidth - st::boxPadding.left() - st::boxPadding.right();
_oldPasscode->resize(w, _oldPasscode->height());
_oldPasscode->moveToLeft(st::boxPadding.left(), st::passcodePadding.top());
_newPasscode->resize(w, _newPasscode->height());
_newPasscode->moveToLeft(st::boxPadding.left(), _oldPasscode->y() + ((_turningOff || has) ? (_oldPasscode->height() + st::passcodeTextLine + ((_cloudFields.hasRecovery && !_hintText.isEmpty()) ? st::passcodeTextLine : 0)) : 0));
_reenterPasscode->resize(w, _reenterPasscode->height());
_reenterPasscode->moveToLeft(st::boxPadding.left(), _newPasscode->y() + _newPasscode->height() + st::passcodeLittleSkip);
_passwordHint->resize(w, _passwordHint->height());
_passwordHint->moveToLeft(st::boxPadding.left(), _reenterPasscode->y() + _reenterPasscode->height() + st::passcodeSkip);
_recoverEmail->resize(w, _passwordHint->height());
_recoverEmail->moveToLeft(st::boxPadding.left(), _passwordHint->y() + _passwordHint->height() + st::passcodeLittleSkip + _aboutHeight + st::passcodeLittleSkip);
if (!_recover->isHidden()) {
_recover->moveToLeft(st::boxPadding.left(), _oldPasscode->y() + _oldPasscode->height() + (_hintText.isEmpty() ? ((st::passcodeTextLine - _recover->height()) / 2) : st::passcodeTextLine));
}
}
void PasscodeBox::setInnerFocus() {
if (_skipEmailWarning && !_recoverEmail->isHidden()) {
_recoverEmail->setFocusFast();
} else if (_oldPasscode->isHidden()) {
_newPasscode->setFocusFast();
} else {
_oldPasscode->setFocusFast();
}
}
void PasscodeBox::setPasswordDone(const QByteArray &newPasswordBytes) {
if (_replacedBy) {
_replacedBy->closeBox();
}
_setRequest = 0;
_newPasswordSet.fire_copy(newPasswordBytes);
2019-09-13 12:22:54 +00:00
const auto weak = Ui::MakeWeak(this);
2019-06-19 15:09:03 +00:00
const auto text = _reenterPasscode->isHidden()
? tr::lng_cloud_password_removed(tr::now)
: _oldPasscode->isHidden()
? tr::lng_cloud_password_was_set(tr::now)
: tr::lng_cloud_password_updated(tr::now);
getDelegate()->show(Box<InformBox>(text));
if (weak) {
closeBox();
}
}
void PasscodeBox::closeReplacedBy() {
if (isHidden()) {
if (_replacedBy && !_replacedBy->isHidden()) {
_replacedBy->closeBox();
}
}
}
2018-08-10 19:19:46 +00:00
void PasscodeBox::setPasswordFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
closeReplacedBy();
_setRequest = 0;
_oldPasscode->selectAll();
_oldPasscode->setFocus();
_oldPasscode->showError();
2019-06-19 15:09:03 +00:00
_oldError = tr::lng_flood_error(tr::now);
if (_cloudFields.hasRecovery && _hintText.isEmpty()) {
_recover->hide();
}
update();
2018-08-10 19:19:46 +00:00
return;
}
closeReplacedBy();
_setRequest = 0;
2019-06-14 14:04:30 +00:00
const auto &err = error.type();
2018-08-10 19:19:46 +00:00
if (err == qstr("PASSWORD_HASH_INVALID")
|| err == qstr("SRP_PASSWORD_CHANGED")) {
if (_oldPasscode->isHidden()) {
_passwordReloadNeeded.fire({});
closeBox();
} else {
badOldPasscode();
}
2019-06-14 14:04:30 +00:00
} else if (err == qstr("SRP_ID_INVALID")) {
2018-08-10 19:19:46 +00:00
handleSrpIdInvalid();
//} else if (err == qstr("NEW_PASSWORD_BAD")) {
//} else if (err == qstr("NEW_SALT_INVALID")) {
} else if (err == qstr("EMAIL_INVALID")) {
2019-06-19 15:09:03 +00:00
_emailError = tr::lng_cloud_password_bad_email(tr::now);
_recoverEmail->setFocus();
_recoverEmail->showError();
update();
}
}
2018-08-10 19:19:46 +00:00
void PasscodeBox::setPasswordFail(
const QByteArray &newPasswordBytes,
2018-11-07 09:39:31 +00:00
const QString &email,
const RPCError &error) {
2018-11-07 09:39:31 +00:00
const auto prefix = qstr("EMAIL_UNCONFIRMED_");
if (error.type().startsWith(prefix)) {
const auto codeLength = error.type().mid(prefix.size()).toInt();
closeReplacedBy();
_setRequest = 0;
2018-11-07 09:39:31 +00:00
validateEmail(email, codeLength, newPasswordBytes);
} else {
2018-08-10 19:19:46 +00:00
setPasswordFail(error);
}
}
2018-11-07 09:39:31 +00:00
void PasscodeBox::validateEmail(
const QString &email,
int codeLength,
const QByteArray &newPasswordBytes) {
const auto errors = std::make_shared<rpl::event_stream<QString>>();
const auto resent = std::make_shared<rpl::event_stream<QString>>();
const auto set = std::make_shared<bool>(false);
const auto submit = crl::guard(this, [=](QString code) {
2018-11-07 09:39:31 +00:00
if (_setRequest) {
return;
}
2019-11-27 08:02:56 +00:00
_setRequest = _api.request(MTPaccount_ConfirmPasswordEmail(
2018-11-07 09:39:31 +00:00
MTP_string(code)
)).done([=](const MTPBool &result) {
*set = true;
setPasswordDone(newPasswordBytes);
}).fail([=](const RPCError &error) {
_setRequest = 0;
if (MTP::isFloodError(error)) {
2019-06-19 15:09:03 +00:00
errors->fire(tr::lng_flood_error(tr::now));
2018-11-07 09:39:31 +00:00
} else if (error.type() == qstr("CODE_INVALID")) {
2019-06-19 15:09:03 +00:00
errors->fire(tr::lng_signin_wrong_code(tr::now));
2018-11-07 09:39:31 +00:00
} else if (error.type() == qstr("EMAIL_HASH_EXPIRED")) {
2019-09-13 12:22:54 +00:00
const auto weak = Ui::MakeWeak(this);
2018-11-07 09:39:31 +00:00
_clearUnconfirmedPassword.fire({});
if (weak) {
auto box = Box<InformBox>(
Lang::Hard::EmailConfirmationExpired());
weak->getDelegate()->show(
std::move(box),
2019-09-18 11:19:05 +00:00
Ui::LayerOption::CloseOther);
2018-11-07 09:39:31 +00:00
}
} else {
errors->fire(Lang::Hard::ServerError());
}
}).handleFloodErrors().send();
});
const auto resend = crl::guard(this, [=] {
2018-11-07 09:39:31 +00:00
if (_setRequest) {
return;
}
2019-11-27 08:02:56 +00:00
_setRequest = _api.request(MTPaccount_ResendPasswordEmail(
2018-11-07 09:39:31 +00:00
)).done([=](const MTPBool &result) {
_setRequest = 0;
2019-06-19 15:09:03 +00:00
resent->fire(tr::lng_cloud_password_resent(tr::now));
2018-11-07 09:39:31 +00:00
}).fail([=](const RPCError &error) {
_setRequest = 0;
errors->fire(Lang::Hard::ServerError());
}).send();
});
const auto box = _replacedBy = getDelegate()->show(
2018-11-07 09:39:31 +00:00
Passport::VerifyEmailBox(
email,
codeLength,
submit,
resend,
errors->events(),
resent->events()));
2018-11-07 09:39:31 +00:00
box->setCloseByOutsideClick(false);
box->setCloseByEscape(false);
box->boxClosing(
) | rpl::filter([=] {
return !*set;
2019-09-13 12:22:54 +00:00
}) | start_with_next([=, weak = Ui::MakeWeak(this)] {
2018-11-07 09:39:31 +00:00
if (weak) {
weak->_clearUnconfirmedPassword.fire({});
}
if (weak) {
weak->closeBox();
}
}, box->lifetime());
}
2018-08-10 19:19:46 +00:00
void PasscodeBox::handleSrpIdInvalid() {
const auto now = crl::now();
2018-08-10 19:19:46 +00:00
if (_lastSrpIdInvalidTime > 0
&& now - _lastSrpIdInvalidTime < Core::kHandleSrpIdInvalidTimeout) {
_cloudFields.curRequest.id = 0;
2018-08-10 19:19:46 +00:00
_oldError = Lang::Hard::ServerError();
update();
} else {
_lastSrpIdInvalidTime = now;
requestPasswordData();
}
}
void PasscodeBox::save(bool force) {
if (_setRequest) return;
QString old = _oldPasscode->text(), pwd = _newPasscode->text(), conf = _reenterPasscode->text();
2018-08-03 21:48:00 +00:00
const auto has = currentlyHave();
if (!_cloudPwd && (_turningOff || has)) {
2015-04-04 20:01:34 +00:00
if (!passcodeCanTry()) {
2019-06-19 15:09:03 +00:00
_oldError = tr::lng_flood_error(tr::now);
_oldPasscode->setFocus();
_oldPasscode->showError();
2015-04-04 20:01:34 +00:00
update();
return;
}
if (Core::App().domain().local().checkPasscode(old.toUtf8())) {
2015-04-04 20:01:34 +00:00
cSetPasscodeBadTries(0);
if (_turningOff) pwd = conf = QString();
} else {
2015-04-04 20:01:34 +00:00
cSetPasscodeBadTries(cPasscodeBadTries() + 1);
cSetPasscodeLastTry(crl::now());
badOldPasscode();
return;
}
}
const auto onlyCheck = onlyCheckCurrent();
if (!onlyCheck && pwd.isEmpty()) {
_newPasscode->setFocus();
_newPasscode->showError();
closeReplacedBy();
return;
}
if (!onlyCheck && pwd != conf) {
_reenterPasscode->selectAll();
_reenterPasscode->setFocus();
_reenterPasscode->showError();
if (!conf.isEmpty()) {
2019-06-19 15:09:03 +00:00
_newError = _cloudPwd
? tr::lng_cloud_password_differ(tr::now)
: tr::lng_passcode_differ(tr::now);
update();
}
closeReplacedBy();
} else if (!onlyCheck && has && old == pwd) {
_newPasscode->setFocus();
_newPasscode->showError();
2019-06-19 15:09:03 +00:00
_newError = _cloudPwd
? tr::lng_cloud_password_is_same(tr::now)
: tr::lng_passcode_is_same(tr::now);
update();
closeReplacedBy();
} else if (_cloudPwd) {
QString hint = _passwordHint->getLastText(), email = _recoverEmail->getLastText().trimmed();
if (!onlyCheck
&& !_passwordHint->isHidden()
&& !_newPasscode->isHidden()
&& pwd == hint) {
_newPasscode->setFocus();
_newPasscode->showError();
2019-06-19 15:09:03 +00:00
_newError = tr::lng_cloud_password_bad(tr::now);
2015-04-04 20:01:34 +00:00
update();
closeReplacedBy();
2015-04-04 20:01:34 +00:00
return;
}
if (!onlyCheck && !_recoverEmail->isHidden() && email.isEmpty() && !force) {
2015-10-03 10:09:09 +00:00
_skipEmailWarning = true;
_replacedBy = getDelegate()->show(
Box<ConfirmBox>(
2019-06-19 15:09:03 +00:00
tr::lng_cloud_password_about_recover(tr::now),
tr::lng_cloud_password_skip_email(tr::now),
st::attentionBoxButton,
crl::guard(this, [this] { save(true); })));
} else if (onlyCheck) {
submitOnlyCheckCloudPassword(old);
} else if (_oldPasscode->isHidden()) {
setNewCloudPassword(pwd);
} else {
changeCloudPassword(old, pwd);
}
} else {
closeReplacedBy();
2019-09-13 12:22:54 +00:00
const auto weak = Ui::MakeWeak(this);
2015-04-04 20:01:34 +00:00
cSetPasscodeBadTries(0);
Core::App().domain().local().setPasscode(pwd.toUtf8());
Core::App().localPasscodeChanged();
2019-03-04 18:40:21 +00:00
if (weak) {
closeBox();
}
}
}
void PasscodeBox::submitOnlyCheckCloudPassword(const QString &oldPassword) {
Expects(!_oldPasscode->isHidden());
const auto send = [=] {
sendOnlyCheckCloudPassword(oldPassword);
};
if (_cloudFields.turningOff && _cloudFields.notEmptyPassport) {
2019-06-14 14:04:30 +00:00
Assert(!_cloudFields.customCheckCallback);
2019-09-18 11:19:05 +00:00
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
const auto confirmed = [=] {
send();
if (*box) {
(*box)->closeBox();
}
};
*box = getDelegate()->show(Box<ConfirmBox>(
2019-06-19 15:09:03 +00:00
tr::lng_cloud_password_passport_losing(tr::now),
tr::lng_continue(tr::now),
confirmed));
} else {
send();
}
}
void PasscodeBox::sendOnlyCheckCloudPassword(const QString &oldPassword) {
2018-08-10 19:19:46 +00:00
checkPassword(oldPassword, [=](const Core::CloudPasswordResult &check) {
2019-06-14 14:04:30 +00:00
if (const auto onstack = _cloudFields.customCheckCallback) {
onstack(check);
} else {
Assert(_cloudFields.turningOff);
sendClearCloudPassword(check);
}
2018-08-10 19:19:46 +00:00
});
}
void PasscodeBox::checkPassword(
const QString &oldPassword,
CheckPasswordCallback callback) {
const auto passwordUtf = oldPassword.toUtf8();
2018-08-10 19:19:46 +00:00
_checkPasswordHash = Core::ComputeCloudPasswordHash(
_cloudFields.curRequest.algo,
2018-08-03 21:48:00 +00:00
bytes::make_span(passwordUtf));
2018-08-10 19:19:46 +00:00
checkPasswordHash(std::move(callback));
}
void PasscodeBox::checkPasswordHash(CheckPasswordCallback callback) {
_checkPasswordCallback = std::move(callback);
if (_cloudFields.curRequest.id) {
2018-08-10 19:19:46 +00:00
passwordChecked();
} else {
requestPasswordData();
}
}
void PasscodeBox::passwordChecked() {
if (!_cloudFields.curRequest || !_cloudFields.curRequest.id || !_checkPasswordCallback) {
2018-08-10 19:19:46 +00:00
return serverError();
}
const auto check = Core::ComputeCloudPasswordCheck(
_cloudFields.curRequest,
2018-08-10 19:19:46 +00:00
_checkPasswordHash);
if (!check) {
return serverError();
}
_cloudFields.curRequest.id = 0;
2018-08-10 19:19:46 +00:00
_checkPasswordCallback(check);
}
void PasscodeBox::requestPasswordData() {
if (!_checkPasswordCallback) {
return serverError();
}
2019-11-27 08:02:56 +00:00
_api.request(base::take(_setRequest)).cancel();
_setRequest = _api.request(
2018-08-10 19:19:46 +00:00
MTPaccount_GetPassword()
).done([=](const MTPaccount_Password &result) {
_setRequest = 0;
result.match([&](const MTPDaccount_password &data) {
_cloudFields.curRequest = Core::ParseCloudPasswordCheckRequest(data);
2018-08-10 19:19:46 +00:00
passwordChecked();
});
}).send();
}
void PasscodeBox::serverError() {
getDelegate()->show(Box<InformBox>(Lang::Hard::ServerError()));
closeBox();
}
2019-06-14 14:04:30 +00:00
bool PasscodeBox::handleCustomCheckError(const RPCError &error) {
const auto &type = error.type();
if (MTP::isFloodError(error)
|| type == qstr("PASSWORD_HASH_INVALID")
|| type == qstr("SRP_PASSWORD_CHANGED")
|| type == qstr("SRP_ID_INVALID")) {
setPasswordFail(error);
return true;
}
return false;
}
2018-08-10 19:19:46 +00:00
void PasscodeBox::sendClearCloudPassword(
const Core::CloudPasswordResult &check) {
const auto newPasswordData = QByteArray();
const auto newPasswordHash = QByteArray();
const auto hint = QString();
const auto email = QString();
2018-08-03 21:48:00 +00:00
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
| MTPDaccount_passwordInputSettings::Flag::f_hint
| MTPDaccount_passwordInputSettings::Flag::f_email;
2019-11-27 08:02:56 +00:00
_setRequest = _api.request(MTPaccount_UpdatePasswordSettings(
2018-08-10 19:19:46 +00:00
check.result,
MTP_account_passwordInputSettings(
MTP_flags(flags),
Core::PrepareCloudPasswordAlgo(_cloudFields.newAlgo),
2019-07-05 13:38:38 +00:00
MTP_bytes(), // new_password_hash
MTP_string(hint),
MTP_string(email),
2018-08-03 21:48:00 +00:00
MTPSecureSecretSettings())
)).done([=](const MTPBool &result) {
setPasswordDone({});
}).fail([=](const RPCError &error) mutable {
2018-11-07 09:39:31 +00:00
setPasswordFail({}, QString(), error);
}).handleFloodErrors().send();
}
void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
const auto newPasswordBytes = newPassword.toUtf8();
2018-08-10 19:19:46 +00:00
const auto newPasswordHash = Core::ComputeCloudPasswordDigest(
_cloudFields.newAlgo,
2018-08-03 21:48:00 +00:00
bytes::make_span(newPasswordBytes));
2018-08-10 19:19:46 +00:00
if (newPasswordHash.modpow.empty()) {
return serverError();
}
const auto hint = _passwordHint->getLastText();
const auto email = _recoverEmail->getLastText().trimmed();
2018-08-03 21:48:00 +00:00
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
| MTPDaccount_passwordInputSettings::Flag::f_hint
| MTPDaccount_passwordInputSettings::Flag::f_email;
2018-08-10 19:19:46 +00:00
_checkPasswordCallback = nullptr;
2019-11-27 08:02:56 +00:00
_setRequest = _api.request(MTPaccount_UpdatePasswordSettings(
2018-08-10 19:19:46 +00:00
MTP_inputCheckPasswordEmpty(),
MTP_account_passwordInputSettings(
MTP_flags(flags),
Core::PrepareCloudPasswordAlgo(_cloudFields.newAlgo),
2018-08-10 19:19:46 +00:00
MTP_bytes(newPasswordHash.modpow),
MTP_string(hint),
MTP_string(email),
2018-08-03 21:48:00 +00:00
MTPSecureSecretSettings())
)).done([=](const MTPBool &result) {
setPasswordDone(newPasswordBytes);
}).fail([=](const RPCError &error) {
2018-11-07 09:39:31 +00:00
setPasswordFail(newPasswordBytes, email, error);
}).handleFloodErrors().send();
}
void PasscodeBox::changeCloudPassword(
const QString &oldPassword,
const QString &newPassword) {
2018-08-10 19:19:46 +00:00
checkPassword(oldPassword, [=](const Core::CloudPasswordResult &check) {
changeCloudPassword(oldPassword, check, newPassword);
});
}
void PasscodeBox::changeCloudPassword(
const QString &oldPassword,
const Core::CloudPasswordResult &check,
const QString &newPassword) {
2019-11-27 08:02:56 +00:00
_setRequest = _api.request(MTPaccount_GetPasswordSettings(
2018-08-10 19:19:46 +00:00
check.result
)).done([=](const MTPaccount_PasswordSettings &result) {
_setRequest = 0;
Expects(result.type() == mtpc_account_passwordSettings);
const auto &data = result.c_account_passwordSettings();
2019-07-05 13:38:38 +00:00
const auto wrapped = data.vsecure_settings();
if (!wrapped) {
2018-08-10 19:19:46 +00:00
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
const auto empty = QByteArray();
sendChangeCloudPassword(check, newPassword, empty);
});
return;
}
2019-07-05 13:38:38 +00:00
const auto &settings = wrapped->c_secureSecretSettings();
2018-08-10 19:19:46 +00:00
const auto passwordUtf = oldPassword.toUtf8();
const auto secret = Passport::DecryptSecureSecret(
2019-07-05 13:38:38 +00:00
bytes::make_span(settings.vsecure_secret().v),
2018-08-03 21:48:00 +00:00
Core::ComputeSecureSecretHash(
2019-07-05 13:38:38 +00:00
Core::ParseSecureSecretAlgo(settings.vsecure_algo()),
bytes::make_span(passwordUtf)));
if (secret.empty()) {
LOG(("API Error: Failed to decrypt secure secret."));
2018-08-10 19:19:46 +00:00
suggestSecretReset(newPassword);
2018-08-03 21:48:00 +00:00
} else if (Passport::CountSecureSecretId(secret)
2019-07-05 13:38:38 +00:00
!= settings.vsecure_secret_id().v) {
LOG(("API Error: Wrong secure secret id."));
2018-08-10 19:19:46 +00:00
suggestSecretReset(newPassword);
} else {
2018-08-10 19:19:46 +00:00
const auto secureSecret = QByteArray(
reinterpret_cast<const char*>(secret.data()),
secret.size());
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
sendChangeCloudPassword(check, newPassword, secureSecret);
});
}
}).fail([=](const RPCError &error) {
setPasswordFail(error);
}).handleFloodErrors().send();
}
2018-08-10 19:19:46 +00:00
void PasscodeBox::suggestSecretReset(const QString &newPassword) {
2019-09-18 11:19:05 +00:00
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
const auto resetSecretAndSave = [=] {
2018-08-10 19:19:46 +00:00
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
resetSecret(check, newPassword, [=] {
if (*box) {
(*box)->closeBox();
}
});
});
};
*box = getDelegate()->show(Box<ConfirmBox>(
2018-08-10 19:19:46 +00:00
Lang::Hard::PassportCorruptedChange(),
Lang::Hard::PassportCorruptedReset(),
[=] { resetSecretAndSave(); }));
}
2018-08-10 19:19:46 +00:00
void PasscodeBox::resetSecret(
const Core::CloudPasswordResult &check,
const QString &newPassword,
Fn<void()> callback) {
using Flag = MTPDaccount_passwordInputSettings::Flag;
2019-11-27 08:02:56 +00:00
_setRequest = _api.request(MTPaccount_UpdatePasswordSettings(
2018-08-10 19:19:46 +00:00
check.result,
MTP_account_passwordInputSettings(
MTP_flags(Flag::f_new_secure_settings),
MTPPasswordKdfAlgo(), // new_algo
MTPbytes(), // new_password_hash
MTPstring(), // hint
MTPstring(), // email
MTP_secureSecretSettings(
MTP_securePasswordKdfAlgoUnknown(), // secure_algo
2019-07-05 13:38:38 +00:00
MTP_bytes(), // secure_secret
2018-08-10 19:19:46 +00:00
MTP_long(0))) // secure_secret_id
)).done([=](const MTPBool &result) {
_setRequest = 0;
callback();
checkPasswordHash([=](const Core::CloudPasswordResult &check) {
const auto empty = QByteArray();
sendChangeCloudPassword(check, newPassword, empty);
});
}).fail([=](const RPCError &error) {
_setRequest = 0;
if (error.type() == qstr("SRP_ID_INVALID")) {
handleSrpIdInvalid();
}
}).send();
}
void PasscodeBox::sendChangeCloudPassword(
2018-08-10 19:19:46 +00:00
const Core::CloudPasswordResult &check,
const QString &newPassword,
const QByteArray &secureSecret) {
const auto newPasswordBytes = newPassword.toUtf8();
2018-08-10 19:19:46 +00:00
const auto newPasswordHash = Core::ComputeCloudPasswordDigest(
_cloudFields.newAlgo,
2018-08-03 21:48:00 +00:00
bytes::make_span(newPasswordBytes));
2018-08-10 19:19:46 +00:00
if (newPasswordHash.modpow.empty()) {
return serverError();
}
const auto hint = _passwordHint->getLastText();
2018-08-03 21:48:00 +00:00
auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash
| MTPDaccount_passwordInputSettings::Flag::f_hint;
auto newSecureSecret = bytes::vector();
auto newSecureSecretId = 0ULL;
if (!secureSecret.isEmpty()) {
2018-08-03 21:48:00 +00:00
flags |= MTPDaccount_passwordInputSettings::Flag::f_new_secure_settings;
newSecureSecretId = Passport::CountSecureSecretId(
bytes::make_span(secureSecret));
newSecureSecret = Passport::EncryptSecureSecret(
bytes::make_span(secureSecret),
2018-08-03 21:48:00 +00:00
Core::ComputeSecureSecretHash(
_cloudFields.newSecureSecretAlgo,
bytes::make_span(newPasswordBytes)));
}
2019-11-27 08:02:56 +00:00
_setRequest = _api.request(MTPaccount_UpdatePasswordSettings(
2018-08-10 19:19:46 +00:00
check.result,
MTP_account_passwordInputSettings(
MTP_flags(flags),
Core::PrepareCloudPasswordAlgo(_cloudFields.newAlgo),
2018-08-10 19:19:46 +00:00
MTP_bytes(newPasswordHash.modpow),
MTP_string(hint),
MTPstring(), // email is not changing
2018-08-03 21:48:00 +00:00
MTP_secureSecretSettings(
Core::PrepareSecureSecretAlgo(_cloudFields.newSecureSecretAlgo),
2018-08-03 21:48:00 +00:00
MTP_bytes(newSecureSecret),
MTP_long(newSecureSecretId)))
)).done([=](const MTPBool &result) {
setPasswordDone(newPasswordBytes);
}).fail([=](const RPCError &error) {
2018-11-07 09:39:31 +00:00
setPasswordFail(newPasswordBytes, QString(), error);
}).handleFloodErrors().send();
}
void PasscodeBox::badOldPasscode() {
_oldPasscode->selectAll();
_oldPasscode->setFocus();
_oldPasscode->showError();
2019-06-19 15:09:03 +00:00
_oldError = _cloudPwd
? tr::lng_cloud_password_wrong(tr::now)
: tr::lng_passcode_wrong(tr::now);
if (_cloudFields.hasRecovery && _hintText.isEmpty()) {
_recover->hide();
2015-10-12 21:02:10 +00:00
}
update();
}
void PasscodeBox::oldChanged() {
if (!_oldError.isEmpty()) {
_oldError = QString();
if (_cloudFields.hasRecovery && _hintText.isEmpty()) {
_recover->show();
2015-10-12 21:02:10 +00:00
}
update();
}
}
void PasscodeBox::newChanged() {
if (!_newError.isEmpty()) {
_newError = QString();
update();
}
}
void PasscodeBox::emailChanged() {
if (!_emailError.isEmpty()) {
_emailError = QString();
update();
}
}
void PasscodeBox::recoverByEmail() {
if (_pattern.isEmpty()) {
_pattern = "-";
2019-11-27 08:02:56 +00:00
_api.request(MTPauth_RequestPasswordRecovery(
)).done([=](const MTPauth_PasswordRecovery &result) {
recoverStarted(result);
}).fail([=](const RPCError &error) {
recoverStartFail(error);
}).send();
} else {
recover();
}
}
void PasscodeBox::recoverExpired() {
_pattern = QString();
}
void PasscodeBox::recover() {
if (_pattern == "-") return;
const auto box = getDelegate()->show(Box<RecoverBox>(
_session,
_pattern,
_cloudFields.notEmptyPassport));
box->passwordCleared(
) | rpl::map_to(
QByteArray()
) | rpl::start_to_stream(_newPasswordSet, lifetime());
box->recoveryExpired(
) | rpl::start_with_next([=] {
recoverExpired();
}, lifetime());
_replacedBy = box;
}
void PasscodeBox::recoverStarted(const MTPauth_PasswordRecovery &result) {
2019-07-05 13:38:38 +00:00
_pattern = qs(result.c_auth_passwordRecovery().vemail_pattern());
recover();
}
2018-08-10 19:19:46 +00:00
void PasscodeBox::recoverStartFail(const RPCError &error) {
_pattern = QString();
closeBox();
}
RecoverBox::RecoverBox(
QWidget*,
not_null<Main::Session*> session,
const QString &pattern,
bool notEmptyPassport)
: _api(&session->mtp())
, _pattern(st::normalFont->elided(tr::lng_signin_recover_hint(tr::now, lt_recover_email, pattern), st::boxWidth - st::boxPadding.left() * 1.5))
, _notEmptyPassport(notEmptyPassport)
, _recoverCode(this, st::defaultInputField, tr::lng_signin_code()) {
}
2015-10-12 21:02:10 +00:00
rpl::producer<> RecoverBox::passwordCleared() const {
return _passwordCleared.events();
}
rpl::producer<> RecoverBox::recoveryExpired() const {
return _recoveryExpired.events();
}
void RecoverBox::prepare() {
setTitle(tr::lng_signin_recover_title());
addButton(tr::lng_passcode_submit(), [=] { submit(); });
addButton(tr::lng_cancel(), [=] { closeBox(); });
setDimensions(st::boxWidth, st::passcodePadding.top() + st::passcodePadding.bottom() + st::passcodeTextLine + _recoverCode->height() + st::passcodeTextLine);
connect(_recoverCode, &Ui::InputField::changed, [=] { codeChanged(); });
connect(_recoverCode, &Ui::InputField::submitted, [=] { submit(); });
}
void RecoverBox::paintEvent(QPaintEvent *e) {
BoxContent::paintEvent(e);
Painter p(this);
2015-10-12 21:02:10 +00:00
p.setFont(st::normalFont);
2016-10-31 12:29:26 +00:00
p.setPen(st::boxTextFg);
2015-10-14 11:51:37 +00:00
int32 w = st::boxWidth - st::boxPadding.left() * 1.5;
p.drawText(QRect(st::boxPadding.left(), _recoverCode->y() - st::passcodeTextLine - st::passcodePadding.top(), w, st::passcodePadding.top() + st::passcodeTextLine), _pattern, style::al_left);
if (!_error.isEmpty()) {
2016-10-31 12:29:26 +00:00
p.setPen(st::boxTextFgError);
p.drawText(QRect(st::boxPadding.left(), _recoverCode->y() + _recoverCode->height(), w, st::passcodeTextLine), _error, style::al_left);
}
}
void RecoverBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
2015-10-12 21:02:10 +00:00
_recoverCode->resize(st::boxWidth - st::boxPadding.left() - st::boxPadding.right(), _recoverCode->height());
_recoverCode->moveToLeft(st::boxPadding.left(), st::passcodePadding.top() + st::passcodePadding.bottom() + st::passcodeTextLine);
}
void RecoverBox::setInnerFocus() {
_recoverCode->setFocusFast();
}
void RecoverBox::submit() {
if (_submitRequest) return;
QString code = _recoverCode->getLastText().trimmed();
if (code.isEmpty()) {
_recoverCode->setFocus();
_recoverCode->showError();
return;
}
const auto send = crl::guard(this, [=] {
_submitRequest = _api.request(MTPauth_RecoverPassword(
MTP_string(code)
)).done([=](const MTPauth_Authorization &result) {
codeSubmitDone(result);
}).fail([=](const RPCError &error) {
codeSubmitFail(error);
}).handleFloodErrors().send();
});
if (_notEmptyPassport) {
2019-09-18 11:19:05 +00:00
const auto box = std::make_shared<QPointer<Ui::BoxContent>>();
const auto confirmed = [=] {
send();
if (*box) {
(*box)->closeBox();
}
};
*box = getDelegate()->show(Box<ConfirmBox>(
2019-06-19 15:09:03 +00:00
tr::lng_cloud_password_passport_losing(tr::now),
tr::lng_continue(tr::now),
confirmed));
} else {
send();
}
}
void RecoverBox::codeChanged() {
_error = QString();
update();
}
void RecoverBox::codeSubmitDone(const MTPauth_Authorization &result) {
_submitRequest = 0;
_passwordCleared.fire({});
2018-04-16 17:02:40 +00:00
getDelegate()->show(
2019-06-19 15:09:03 +00:00
Box<InformBox>(tr::lng_cloud_password_removed(tr::now)),
2019-09-18 11:19:05 +00:00
Ui::LayerOption::CloseOther);
}
void RecoverBox::codeSubmitFail(const RPCError &error) {
if (MTP::isFloodError(error)) {
_submitRequest = 0;
2019-06-19 15:09:03 +00:00
_error = tr::lng_flood_error(tr::now);
update();
_recoverCode->showError();
return;
}
_submitRequest = 0;
const QString &err = error.type();
if (err == qstr("PASSWORD_EMPTY")) {
_passwordCleared.fire({});
2018-04-16 17:02:40 +00:00
getDelegate()->show(
2019-06-19 15:09:03 +00:00
Box<InformBox>(tr::lng_cloud_password_removed(tr::now)),
2019-09-18 11:19:05 +00:00
Ui::LayerOption::CloseOther);
} else if (err == qstr("PASSWORD_RECOVERY_NA")) {
closeBox();
} else if (err == qstr("PASSWORD_RECOVERY_EXPIRED")) {
_recoveryExpired.fire({});
closeBox();
} else if (err == qstr("CODE_INVALID")) {
2019-06-19 15:09:03 +00:00
_error = tr::lng_signin_wrong_code(tr::now);
update();
_recoverCode->selectAll();
_recoverCode->setFocus();
_recoverCode->showError();
} else {
if (Logs::DebugEnabled()) { // internal server error
_error = err + ": " + error.description();
} else {
_error = Lang::Hard::ServerError();
}
update();
_recoverCode->setFocus();
}
}
2018-11-07 09:39:31 +00:00
RecoveryEmailValidation ConfirmRecoveryEmail(
not_null<Main::Session*> session,
const QString &pattern) {
2018-11-07 09:39:31 +00:00
const auto errors = std::make_shared<rpl::event_stream<QString>>();
const auto resent = std::make_shared<rpl::event_stream<QString>>();
const auto requestId = std::make_shared<mtpRequestId>(0);
2019-09-18 11:19:05 +00:00
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
2018-11-07 09:39:31 +00:00
const auto reloads = std::make_shared<rpl::event_stream<>>();
const auto cancels = std::make_shared<rpl::event_stream<>>();
const auto submit = [=](QString code) {
if (*requestId) {
return;
}
*requestId = session->api().request(MTPaccount_ConfirmPasswordEmail(
MTP_string(code)
)).done([=](const MTPBool &result) {
2018-11-07 09:39:31 +00:00
*requestId = 0;
reloads->fire({});
if (*weak) {
(*weak)->getDelegate()->show(
2019-06-19 15:09:03 +00:00
Box<InformBox>(tr::lng_cloud_password_was_set(tr::now)),
2019-09-18 11:19:05 +00:00
Ui::LayerOption::CloseOther);
2018-11-07 09:39:31 +00:00
}
}).fail([=](const RPCError &error) {
2018-11-07 09:39:31 +00:00
*requestId = 0;
if (MTP::isFloodError(error)) {
2019-06-19 15:09:03 +00:00
errors->fire(tr::lng_flood_error(tr::now));
2018-11-07 09:39:31 +00:00
} else if (error.type() == qstr("CODE_INVALID")) {
2019-06-19 15:09:03 +00:00
errors->fire(tr::lng_signin_wrong_code(tr::now));
2018-11-07 09:39:31 +00:00
} else if (error.type() == qstr("EMAIL_HASH_EXPIRED")) {
cancels->fire({});
if (*weak) {
auto box = Box<InformBox>(
Lang::Hard::EmailConfirmationExpired());
(*weak)->getDelegate()->show(
std::move(box),
2019-09-18 11:19:05 +00:00
Ui::LayerOption::CloseOther);
2018-11-07 09:39:31 +00:00
}
} else {
errors->fire(Lang::Hard::ServerError());
}
}).handleFloodErrors().send();
2018-11-07 09:39:31 +00:00
};
const auto resend = [=] {
if (*requestId) {
return;
}
*requestId = session->api().request(MTPaccount_ResendPasswordEmail(
)).done([=](const MTPBool &result) {
2018-11-07 09:39:31 +00:00
*requestId = 0;
2019-06-19 15:09:03 +00:00
resent->fire(tr::lng_cloud_password_resent(tr::now));
}).fail([=](const RPCError &error) {
2018-11-07 09:39:31 +00:00
*requestId = 0;
errors->fire(Lang::Hard::ServerError());
}).send();
2018-11-07 09:39:31 +00:00
};
auto box = Passport::VerifyEmailBox(
pattern,
0,
submit,
resend,
errors->events(),
resent->events());
*weak = box.data();
return { std::move(box), reloads->events(), cancels->events() };
}