intro_password_check.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "intro/intro_password_check.h"
  8. #include "intro/intro_widget.h"
  9. #include "core/core_cloud_password.h"
  10. #include "ui/boxes/confirm_box.h"
  11. #include "boxes/abstract_box.h"
  12. #include "boxes/passcode_box.h"
  13. #include "lang/lang_keys.h"
  14. #include "intro/intro_signup.h"
  15. #include "ui/text/text_utilities.h"
  16. #include "ui/widgets/buttons.h"
  17. #include "ui/widgets/fields/input_field.h"
  18. #include "ui/widgets/fields/password_input.h"
  19. #include "main/main_account.h"
  20. #include "base/random.h"
  21. #include "styles/style_intro.h"
  22. #include "styles/style_boxes.h"
  23. namespace Intro {
  24. namespace details {
  25. PasswordCheckWidget::PasswordCheckWidget(
  26. QWidget *parent,
  27. not_null<Main::Account*> account,
  28. not_null<Data*> data)
  29. : Step(parent, account, data)
  30. , _passwordState(getData()->pwdState)
  31. , _pwdField(this, st::introPassword, tr::lng_signin_password())
  32. , _pwdHint(this, st::introPasswordHint)
  33. , _codeField(this, st::introPassword, tr::lng_signin_code())
  34. , _toRecover(this, tr::lng_signin_recover(tr::now))
  35. , _toPassword(this, tr::lng_signin_try_password(tr::now)) {
  36. Expects(_passwordState.hasPassword);
  37. Lang::Updated(
  38. ) | rpl::start_with_next([=] {
  39. refreshLang();
  40. }, lifetime());
  41. _toRecover->addClickHandler([=] { toRecover(); });
  42. _toPassword->addClickHandler([=] { toPassword(); });
  43. connect(_pwdField, &Ui::PasswordInput::changed, [=] { hideError(); });
  44. _codeField->changes(
  45. ) | rpl::start_with_next([=] {
  46. hideError();
  47. }, _codeField->lifetime());
  48. setTitleText(tr::lng_signin_title());
  49. updateDescriptionText();
  50. if (_passwordState.hint.isEmpty()) {
  51. _pwdHint->hide();
  52. } else {
  53. _pwdHint->setText(tr::lng_signin_hint(
  54. tr::now,
  55. lt_password_hint,
  56. _passwordState.hint));
  57. }
  58. _codeField->hide();
  59. _toPassword->hide();
  60. setMouseTracking(true);
  61. }
  62. void PasswordCheckWidget::refreshLang() {
  63. if (_toRecover) {
  64. _toRecover->setText(tr::lng_signin_recover(tr::now));
  65. }
  66. if (_toPassword) {
  67. _toPassword->setText(
  68. tr::lng_signin_try_password(tr::now));
  69. }
  70. if (!_passwordState.hint.isEmpty()) {
  71. _pwdHint->setText(tr::lng_signin_hint(
  72. tr::now,
  73. lt_password_hint,
  74. _passwordState.hint));
  75. }
  76. updateControlsGeometry();
  77. }
  78. int PasswordCheckWidget::errorTop() const {
  79. return contentTop() + st::introErrorBelowLinkTop;
  80. }
  81. void PasswordCheckWidget::resizeEvent(QResizeEvent *e) {
  82. Step::resizeEvent(e);
  83. updateControlsGeometry();
  84. }
  85. void PasswordCheckWidget::updateControlsGeometry() {
  86. _pwdField->moveToLeft(contentLeft(), contentTop() + st::introPasswordTop);
  87. _pwdHint->moveToLeft(contentLeft() + st::buttonRadius, contentTop() + st::introPasswordHintTop);
  88. _codeField->moveToLeft(contentLeft(), contentTop() + st::introStepFieldTop);
  89. auto linkTop = _codeField->y() + _codeField->height() + st::introLinkTop;
  90. _toRecover->moveToLeft(contentLeft() + st::buttonRadius, linkTop);
  91. _toPassword->moveToLeft(contentLeft() + st::buttonRadius, linkTop);
  92. }
  93. void PasswordCheckWidget::setInnerFocus() {
  94. if (_pwdField->isHidden()) {
  95. _codeField->setFocusFast();
  96. } else {
  97. _pwdField->setFocusFast();
  98. }
  99. }
  100. void PasswordCheckWidget::activate() {
  101. if (_pwdField->isHidden() && _codeField->isHidden()) {
  102. Step::activate();
  103. _pwdField->show();
  104. _pwdHint->show();
  105. _toRecover->show();
  106. }
  107. setInnerFocus();
  108. }
  109. void PasswordCheckWidget::cancelled() {
  110. api().request(base::take(_sentRequest)).cancel();
  111. }
  112. void PasswordCheckWidget::pwdSubmitDone(
  113. bool recover,
  114. const MTPauth_Authorization &result) {
  115. _sentRequest = 0;
  116. if (recover) {
  117. cSetPasswordRecovered(true);
  118. }
  119. finish(result);
  120. }
  121. void PasswordCheckWidget::pwdSubmitFail(const MTP::Error &error) {
  122. if (MTP::IsFloodError(error)) {
  123. _sentRequest = 0;
  124. showError(tr::lng_flood_error());
  125. _pwdField->showError();
  126. return;
  127. }
  128. _sentRequest = 0;
  129. const auto &type = error.type();
  130. if (type == u"PASSWORD_HASH_INVALID"_q
  131. || type == u"SRP_PASSWORD_CHANGED"_q) {
  132. showError(tr::lng_signin_bad_password());
  133. _pwdField->selectAll();
  134. _pwdField->showError();
  135. } else if (type == u"PASSWORD_EMPTY"_q
  136. || type == u"AUTH_KEY_UNREGISTERED"_q) {
  137. goBack();
  138. } else if (type == u"SRP_ID_INVALID"_q) {
  139. handleSrpIdInvalid();
  140. } else {
  141. if (Logs::DebugEnabled()) { // internal server error
  142. showError(rpl::single(type + ": " + error.description()));
  143. } else {
  144. showError(rpl::single(Lang::Hard::ServerError()));
  145. }
  146. _pwdField->setFocus();
  147. }
  148. }
  149. void PasswordCheckWidget::handleSrpIdInvalid() {
  150. const auto now = crl::now();
  151. if (_lastSrpIdInvalidTime > 0
  152. && now - _lastSrpIdInvalidTime < Core::kHandleSrpIdInvalidTimeout) {
  153. _passwordState.mtp.request.id = 0;
  154. showError(rpl::single(Lang::Hard::ServerError()));
  155. } else {
  156. _lastSrpIdInvalidTime = now;
  157. requestPasswordData();
  158. }
  159. }
  160. void PasswordCheckWidget::checkPasswordHash() {
  161. if (_passwordState.mtp.request.id) {
  162. passwordChecked();
  163. } else {
  164. requestPasswordData();
  165. }
  166. }
  167. void PasswordCheckWidget::requestPasswordData() {
  168. api().request(base::take(_sentRequest)).cancel();
  169. _sentRequest = api().request(
  170. MTPaccount_GetPassword()
  171. ).done([=](const MTPaccount_Password &result) {
  172. _sentRequest = 0;
  173. result.match([&](const MTPDaccount_password &data) {
  174. base::RandomAddSeed(bytes::make_span(data.vsecure_random().v));
  175. _passwordState = Core::ParseCloudPasswordState(data);
  176. passwordChecked();
  177. });
  178. }).send();
  179. }
  180. void PasswordCheckWidget::passwordChecked() {
  181. const auto check = Core::ComputeCloudPasswordCheck(
  182. _passwordState.mtp.request,
  183. _passwordHash);
  184. if (!check) {
  185. return serverError();
  186. }
  187. _passwordState.mtp.request.id = 0;
  188. _sentRequest = api().request(
  189. MTPauth_CheckPassword(check.result)
  190. ).done([=](const MTPauth_Authorization &result) {
  191. pwdSubmitDone(false, result);
  192. }).fail([=](const MTP::Error &error) {
  193. pwdSubmitFail(error);
  194. }).handleFloodErrors().send();
  195. }
  196. void PasswordCheckWidget::serverError() {
  197. showError(rpl::single(Lang::Hard::ServerError()));
  198. }
  199. void PasswordCheckWidget::codeSubmitDone(
  200. const QString &code,
  201. const MTPBool &result) {
  202. auto fields = PasscodeBox::CloudFields::From(_passwordState);
  203. fields.fromRecoveryCode = code;
  204. fields.hasRecovery = false;
  205. fields.mtp.curRequest = {};
  206. fields.hasPassword = false;
  207. auto box = Box<PasscodeBox>(&api().instance(), nullptr, fields);
  208. const auto boxShared = std::make_shared<QPointer<PasscodeBox>>();
  209. box->newAuthorization(
  210. ) | rpl::start_with_next([=](const MTPauth_Authorization &result) {
  211. if (boxShared) {
  212. (*boxShared)->closeBox();
  213. }
  214. pwdSubmitDone(true, result);
  215. }, lifetime());
  216. *boxShared = Ui::show(std::move(box));
  217. }
  218. void PasswordCheckWidget::codeSubmitFail(const MTP::Error &error) {
  219. if (MTP::IsFloodError(error)) {
  220. showError(tr::lng_flood_error());
  221. _codeField->showError();
  222. return;
  223. }
  224. _sentRequest = 0;
  225. const auto &type = error.type();
  226. if (type == u"PASSWORD_EMPTY"_q
  227. || type == u"AUTH_KEY_UNREGISTERED"_q) {
  228. goBack();
  229. } else if (type == u"PASSWORD_RECOVERY_NA"_q) {
  230. recoverStartFail(error);
  231. } else if (type == u"PASSWORD_RECOVERY_EXPIRED"_q) {
  232. _emailPattern = QString();
  233. toPassword();
  234. } else if (type == u"CODE_INVALID"_q) {
  235. showError(tr::lng_signin_wrong_code());
  236. _codeField->selectAll();
  237. _codeField->showError();
  238. } else {
  239. if (Logs::DebugEnabled()) { // internal server error
  240. showError(rpl::single(type + ": " + error.description()));
  241. } else {
  242. showError(rpl::single(Lang::Hard::ServerError()));
  243. }
  244. _codeField->setFocus();
  245. }
  246. }
  247. void PasswordCheckWidget::recoverStarted(const MTPauth_PasswordRecovery &result) {
  248. _emailPattern = qs(result.c_auth_passwordRecovery().vemail_pattern());
  249. updateDescriptionText();
  250. }
  251. void PasswordCheckWidget::recoverStartFail(const MTP::Error &error) {
  252. _pwdField->show();
  253. _pwdHint->show();
  254. _codeField->hide();
  255. _pwdField->setFocus();
  256. updateDescriptionText();
  257. update();
  258. hideError();
  259. }
  260. void PasswordCheckWidget::toRecover() {
  261. if (_passwordState.hasRecovery) {
  262. if (_sentRequest) {
  263. api().request(base::take(_sentRequest)).cancel();
  264. }
  265. hideError();
  266. _toRecover->hide();
  267. _toPassword->show();
  268. _pwdField->hide();
  269. _pwdHint->hide();
  270. _pwdField->setText(QString());
  271. _codeField->show();
  272. _codeField->setFocus();
  273. updateDescriptionText();
  274. if (_emailPattern.isEmpty()) {
  275. api().request(
  276. MTPauth_RequestPasswordRecovery()
  277. ).done([=](const MTPauth_PasswordRecovery &result) {
  278. recoverStarted(result);
  279. }).fail([=](const MTP::Error &error) {
  280. recoverStartFail(error);
  281. }).send();
  282. }
  283. } else {
  284. const auto box = Ui::show(
  285. Ui::MakeInformBox(tr::lng_signin_no_email_forgot()));
  286. box->boxClosing(
  287. ) | rpl::start_with_next([=] {
  288. showReset();
  289. }, box->lifetime());
  290. }
  291. }
  292. void PasswordCheckWidget::toPassword() {
  293. const auto box = Ui::show(
  294. Ui::MakeInformBox(tr::lng_signin_cant_email_forgot()));
  295. box->boxClosing(
  296. ) | rpl::start_with_next([=] {
  297. showReset();
  298. }, box->lifetime());
  299. }
  300. void PasswordCheckWidget::showReset() {
  301. if (_sentRequest) {
  302. api().request(base::take(_sentRequest)).cancel();
  303. }
  304. _toRecover->show();
  305. _toPassword->hide();
  306. _pwdField->show();
  307. _pwdHint->show();
  308. _codeField->hide();
  309. _codeField->setText(QString());
  310. _pwdField->setFocus();
  311. showResetButton();
  312. updateDescriptionText();
  313. update();
  314. }
  315. void PasswordCheckWidget::updateDescriptionText() {
  316. auto pwdHidden = _pwdField->isHidden();
  317. auto emailPattern = _emailPattern;
  318. setDescriptionText(pwdHidden
  319. ? tr::lng_signin_recover_desc(
  320. lt_email,
  321. rpl::single(Ui::Text::WrapEmailPattern(emailPattern)),
  322. Ui::Text::WithEntities)
  323. : tr::lng_signin_desc(Ui::Text::WithEntities));
  324. }
  325. void PasswordCheckWidget::submit() {
  326. if (_sentRequest) {
  327. return;
  328. }
  329. if (_pwdField->isHidden()) {
  330. auto code = _codeField->getLastText().trimmed();
  331. if (code.isEmpty()) {
  332. _codeField->showError();
  333. return;
  334. }
  335. const auto send = crl::guard(this, [=] {
  336. _sentRequest = api().request(MTPauth_CheckRecoveryPassword(
  337. MTP_string(code)
  338. )).done([=](const MTPBool &result) {
  339. codeSubmitDone(code, result);
  340. }).fail([=](const MTP::Error &error) {
  341. codeSubmitFail(error);
  342. }).handleFloodErrors().send();
  343. });
  344. if (_passwordState.notEmptyPassport) {
  345. const auto confirmed = [=](Fn<void()> &&close) {
  346. send();
  347. close();
  348. };
  349. Ui::show(Ui::MakeConfirmBox({
  350. .text = tr::lng_cloud_password_passport_losing(),
  351. .confirmed = confirmed,
  352. .confirmText = tr::lng_continue(),
  353. }));
  354. } else {
  355. send();
  356. }
  357. } else {
  358. hideError();
  359. const auto password = _pwdField->getLastText().toUtf8();
  360. _passwordHash = Core::ComputeCloudPasswordHash(
  361. _passwordState.mtp.request.algo,
  362. bytes::make_span(password));
  363. checkPasswordHash();
  364. }
  365. }
  366. rpl::producer<QString> PasswordCheckWidget::nextButtonText() const {
  367. return tr::lng_intro_submit();
  368. }
  369. } // namespace details
  370. } // namespace Intro