544 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			544 lines
		
	
	
	
		
			16 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/*
 | 
						|
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 "api/api_cloud_password.h"
 | 
						|
 | 
						|
#include "apiwrap.h"
 | 
						|
#include "base/random.h"
 | 
						|
#include "core/core_cloud_password.h"
 | 
						|
#include "passport/passport_encryption.h"
 | 
						|
 | 
						|
namespace Api {
 | 
						|
namespace {
 | 
						|
 | 
						|
[[nodiscard]] Core::CloudPasswordState ProcessMtpState(
 | 
						|
		const MTPaccount_password &state) {
 | 
						|
	return state.match([&](const MTPDaccount_password &data) {
 | 
						|
		base::RandomAddSeed(bytes::make_span(data.vsecure_random().v));
 | 
						|
		return Core::ParseCloudPasswordState(data);
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
} // namespace
 | 
						|
 | 
						|
CloudPassword::CloudPassword(not_null<ApiWrap*> api)
 | 
						|
: _api(&api->instance()) {
 | 
						|
}
 | 
						|
 | 
						|
void CloudPassword::apply(Core::CloudPasswordState state) {
 | 
						|
	if (_state) {
 | 
						|
		*_state = std::move(state);
 | 
						|
	} else {
 | 
						|
		_state = std::make_unique<Core::CloudPasswordState>(std::move(state));
 | 
						|
	}
 | 
						|
	_stateChanges.fire_copy(*_state);
 | 
						|
}
 | 
						|
 | 
						|
void CloudPassword::reload() {
 | 
						|
	if (_requestId) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	_requestId = _api.request(MTPaccount_GetPassword(
 | 
						|
	)).done([=](const MTPaccount_Password &result) {
 | 
						|
		_requestId = 0;
 | 
						|
		apply(ProcessMtpState(result));
 | 
						|
	}).fail([=] {
 | 
						|
		_requestId = 0;
 | 
						|
	}).send();
 | 
						|
}
 | 
						|
 | 
						|
void CloudPassword::clearUnconfirmedPassword() {
 | 
						|
	_requestId = _api.request(MTPaccount_CancelPasswordEmail(
 | 
						|
	)).done([=] {
 | 
						|
		_requestId = 0;
 | 
						|
		reload();
 | 
						|
	}).fail([=] {
 | 
						|
		_requestId = 0;
 | 
						|
		reload();
 | 
						|
	}).send();
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<Core::CloudPasswordState> CloudPassword::state() const {
 | 
						|
	return _state
 | 
						|
		? _stateChanges.events_starting_with_copy(*_state)
 | 
						|
		: (_stateChanges.events() | rpl::type_erased());
 | 
						|
}
 | 
						|
 | 
						|
auto CloudPassword::stateCurrent() const
 | 
						|
-> std::optional<Core::CloudPasswordState> {
 | 
						|
	return _state
 | 
						|
		? base::make_optional(*_state)
 | 
						|
		: std::nullopt;
 | 
						|
}
 | 
						|
 | 
						|
auto CloudPassword::resetPassword()
 | 
						|
-> rpl::producer<CloudPassword::ResetRetryDate, QString> {
 | 
						|
	return [=](auto consumer) {
 | 
						|
		_api.request(MTPaccount_ResetPassword(
 | 
						|
		)).done([=](const MTPaccount_ResetPasswordResult &result) {
 | 
						|
			result.match([&](const MTPDaccount_resetPasswordOk &data) {
 | 
						|
				reload();
 | 
						|
			}, [&](const MTPDaccount_resetPasswordRequestedWait &data) {
 | 
						|
				if (!_state) {
 | 
						|
					reload();
 | 
						|
					return;
 | 
						|
				}
 | 
						|
				const auto until = data.vuntil_date().v;
 | 
						|
				if (_state->pendingResetDate != until) {
 | 
						|
					_state->pendingResetDate = until;
 | 
						|
					_stateChanges.fire_copy(*_state);
 | 
						|
				}
 | 
						|
			}, [&](const MTPDaccount_resetPasswordFailedWait &data) {
 | 
						|
				consumer.put_next_copy(data.vretry_date().v);
 | 
						|
			});
 | 
						|
			consumer.put_done();
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			consumer.put_error_copy(error.type());
 | 
						|
		}).send();
 | 
						|
 | 
						|
		return rpl::lifetime();
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
auto CloudPassword::cancelResetPassword()
 | 
						|
-> rpl::producer<rpl::no_value, QString> {
 | 
						|
	return [=](auto consumer) {
 | 
						|
		_api.request(MTPaccount_DeclinePasswordReset(
 | 
						|
		)).done([=] {
 | 
						|
			reload();
 | 
						|
			consumer.put_done();
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			consumer.put_error_copy(error.type());
 | 
						|
		}).send();
 | 
						|
 | 
						|
		return rpl::lifetime();
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<CloudPassword::SetOk, QString> CloudPassword::set(
 | 
						|
		const QString &oldPassword,
 | 
						|
		const QString &newPassword,
 | 
						|
		const QString &hint,
 | 
						|
		bool hasRecoveryEmail,
 | 
						|
		const QString &recoveryEmail) {
 | 
						|
 | 
						|
	const auto generatePasswordCheck = [=](
 | 
						|
			const Core::CloudPasswordState &latestState) {
 | 
						|
		if (oldPassword.isEmpty() || !latestState.hasPassword) {
 | 
						|
			return Core::CloudPasswordResult{
 | 
						|
				MTP_inputCheckPasswordEmpty()
 | 
						|
			};
 | 
						|
		}
 | 
						|
		const auto hash = Core::ComputeCloudPasswordHash(
 | 
						|
			latestState.mtp.request.algo,
 | 
						|
			bytes::make_span(oldPassword.toUtf8()));
 | 
						|
		return Core::ComputeCloudPasswordCheck(
 | 
						|
			latestState.mtp.request,
 | 
						|
			hash);
 | 
						|
	};
 | 
						|
 | 
						|
	const auto finish = [=](auto consumer, int unconfirmedEmailLengthCode) {
 | 
						|
		_api.request(MTPaccount_GetPassword(
 | 
						|
		)).done([=](const MTPaccount_Password &result) {
 | 
						|
			apply(ProcessMtpState(result));
 | 
						|
			if (unconfirmedEmailLengthCode) {
 | 
						|
				consumer.put_next(SetOk{ unconfirmedEmailLengthCode });
 | 
						|
			} else {
 | 
						|
				consumer.put_done();
 | 
						|
			}
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			consumer.put_error_copy(error.type());
 | 
						|
		}).handleFloodErrors().send();
 | 
						|
	};
 | 
						|
 | 
						|
	const auto sendMTPaccountUpdatePasswordSettings = [=](
 | 
						|
			const Core::CloudPasswordState &latestState,
 | 
						|
			const QByteArray &secureSecret,
 | 
						|
			auto consumer) {
 | 
						|
		const auto newPasswordBytes = newPassword.toUtf8();
 | 
						|
		const auto newPasswordHash = Core::ComputeCloudPasswordDigest(
 | 
						|
			latestState.mtp.newPassword,
 | 
						|
			bytes::make_span(newPasswordBytes));
 | 
						|
		if (!newPassword.isEmpty() && newPasswordHash.modpow.empty()) {
 | 
						|
			consumer.put_error("INTERNAL_SERVER_ERROR");
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		using Flag = MTPDaccount_passwordInputSettings::Flag;
 | 
						|
		const auto flags = Flag::f_new_algo
 | 
						|
			| Flag::f_new_password_hash
 | 
						|
			| Flag::f_hint
 | 
						|
			| (secureSecret.isEmpty() ? Flag(0) : Flag::f_new_secure_settings)
 | 
						|
			| ((!hasRecoveryEmail) ? Flag(0) : Flag::f_email);
 | 
						|
 | 
						|
		auto newSecureSecret = bytes::vector();
 | 
						|
		auto newSecureSecretId = 0ULL;
 | 
						|
		if (!secureSecret.isEmpty()) {
 | 
						|
			newSecureSecretId = Passport::CountSecureSecretId(
 | 
						|
				bytes::make_span(secureSecret));
 | 
						|
			newSecureSecret = Passport::EncryptSecureSecret(
 | 
						|
				bytes::make_span(secureSecret),
 | 
						|
				Core::ComputeSecureSecretHash(
 | 
						|
					latestState.mtp.newSecureSecret,
 | 
						|
					bytes::make_span(newPasswordBytes)));
 | 
						|
		}
 | 
						|
		const auto settings = MTP_account_passwordInputSettings(
 | 
						|
			MTP_flags(flags),
 | 
						|
			Core::PrepareCloudPasswordAlgo(newPassword.isEmpty()
 | 
						|
				? v::null
 | 
						|
				: latestState.mtp.newPassword),
 | 
						|
			newPassword.isEmpty()
 | 
						|
				? MTP_bytes()
 | 
						|
				: MTP_bytes(newPasswordHash.modpow),
 | 
						|
			MTP_string(hint),
 | 
						|
			MTP_string(recoveryEmail),
 | 
						|
			MTP_secureSecretSettings(
 | 
						|
				Core::PrepareSecureSecretAlgo(
 | 
						|
					latestState.mtp.newSecureSecret),
 | 
						|
				MTP_bytes(newSecureSecret),
 | 
						|
				MTP_long(newSecureSecretId)));
 | 
						|
		_api.request(MTPaccount_UpdatePasswordSettings(
 | 
						|
			generatePasswordCheck(latestState).result,
 | 
						|
			settings
 | 
						|
		)).done([=] {
 | 
						|
			finish(consumer, 0);
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			const auto &type = error.type();
 | 
						|
			const auto prefix = u"EMAIL_UNCONFIRMED_"_q;
 | 
						|
			if (type.startsWith(prefix)) {
 | 
						|
				const auto codeLength = base::StringViewMid(
 | 
						|
					type,
 | 
						|
					prefix.size()).toInt();
 | 
						|
 | 
						|
				finish(consumer, codeLength);
 | 
						|
			} else {
 | 
						|
				consumer.put_error_copy(type);
 | 
						|
			}
 | 
						|
		}).handleFloodErrors().send();
 | 
						|
	};
 | 
						|
 | 
						|
	return [=](auto consumer) {
 | 
						|
		_api.request(MTPaccount_GetPassword(
 | 
						|
		)).done([=](const MTPaccount_Password &result) {
 | 
						|
			const auto latestState = ProcessMtpState(result);
 | 
						|
 | 
						|
			if (latestState.hasPassword
 | 
						|
					&& !oldPassword.isEmpty()
 | 
						|
					&& !newPassword.isEmpty()) {
 | 
						|
 | 
						|
				_api.request(MTPaccount_GetPasswordSettings(
 | 
						|
					generatePasswordCheck(latestState).result
 | 
						|
				)).done([=](const MTPaccount_PasswordSettings &result) {
 | 
						|
					using Settings = MTPDaccount_passwordSettings;
 | 
						|
					const auto &data = result.match([&](
 | 
						|
							const Settings &data) -> const Settings & {
 | 
						|
						return data;
 | 
						|
					});
 | 
						|
					auto secureSecret = QByteArray();
 | 
						|
					if (const auto wrapped = data.vsecure_settings()) {
 | 
						|
						using Secure = MTPDsecureSecretSettings;
 | 
						|
						const auto &settings = wrapped->match([](
 | 
						|
								const Secure &data) -> const Secure & {
 | 
						|
							return data;
 | 
						|
						});
 | 
						|
						const auto passwordUtf = oldPassword.toUtf8();
 | 
						|
						const auto secret = Passport::DecryptSecureSecret(
 | 
						|
							bytes::make_span(settings.vsecure_secret().v),
 | 
						|
							Core::ComputeSecureSecretHash(
 | 
						|
								Core::ParseSecureSecretAlgo(
 | 
						|
									settings.vsecure_algo()),
 | 
						|
								bytes::make_span(passwordUtf)));
 | 
						|
						if (secret.empty()) {
 | 
						|
							LOG(("API Error: "
 | 
						|
								"Failed to decrypt secure secret."));
 | 
						|
							consumer.put_error("SUGGEST_SECRET_RESET");
 | 
						|
							return;
 | 
						|
						} else if (Passport::CountSecureSecretId(secret)
 | 
						|
								!= settings.vsecure_secret_id().v) {
 | 
						|
							LOG(("API Error: Wrong secure secret id."));
 | 
						|
							consumer.put_error("SUGGEST_SECRET_RESET");
 | 
						|
							return;
 | 
						|
						} else {
 | 
						|
							secureSecret = QByteArray(
 | 
						|
								reinterpret_cast<const char*>(secret.data()),
 | 
						|
								secret.size());
 | 
						|
						}
 | 
						|
					}
 | 
						|
					_api.request(MTPaccount_GetPassword(
 | 
						|
					)).done([=](const MTPaccount_Password &result) {
 | 
						|
						const auto latestState = ProcessMtpState(result);
 | 
						|
						sendMTPaccountUpdatePasswordSettings(
 | 
						|
							latestState,
 | 
						|
							secureSecret,
 | 
						|
							consumer);
 | 
						|
					}).fail([=](const MTP::Error &error) {
 | 
						|
						consumer.put_error_copy(error.type());
 | 
						|
					}).send();
 | 
						|
				}).fail([=](const MTP::Error &error) {
 | 
						|
					consumer.put_error_copy(error.type());
 | 
						|
				}).send();
 | 
						|
			} else {
 | 
						|
				sendMTPaccountUpdatePasswordSettings(
 | 
						|
					latestState,
 | 
						|
					QByteArray(),
 | 
						|
					consumer);
 | 
						|
			}
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			consumer.put_error_copy(error.type());
 | 
						|
		}).send();
 | 
						|
		return rpl::lifetime();
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<rpl::no_value, QString> CloudPassword::check(
 | 
						|
		const QString &password) {
 | 
						|
	return [=](auto consumer) {
 | 
						|
		_api.request(MTPaccount_GetPassword(
 | 
						|
		)).done([=](const MTPaccount_Password &result) {
 | 
						|
			const auto latestState = ProcessMtpState(result);
 | 
						|
			const auto input = [&] {
 | 
						|
				if (password.isEmpty()) {
 | 
						|
					return Core::CloudPasswordResult{
 | 
						|
						MTP_inputCheckPasswordEmpty()
 | 
						|
					};
 | 
						|
				}
 | 
						|
				const auto hash = Core::ComputeCloudPasswordHash(
 | 
						|
					latestState.mtp.request.algo,
 | 
						|
					bytes::make_span(password.toUtf8()));
 | 
						|
				return Core::ComputeCloudPasswordCheck(
 | 
						|
					latestState.mtp.request,
 | 
						|
					hash);
 | 
						|
			}();
 | 
						|
 | 
						|
			_api.request(MTPaccount_GetPasswordSettings(
 | 
						|
				input.result
 | 
						|
			)).done([=](const MTPaccount_PasswordSettings &result) {
 | 
						|
				consumer.put_done();
 | 
						|
			}).fail([=](const MTP::Error &error) {
 | 
						|
				consumer.put_error_copy(error.type());
 | 
						|
			}).send();
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			consumer.put_error_copy(error.type());
 | 
						|
		}).send();
 | 
						|
 | 
						|
		return rpl::lifetime();
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<rpl::no_value, QString> CloudPassword::confirmEmail(
 | 
						|
		const QString &code) {
 | 
						|
	return [=](auto consumer) {
 | 
						|
		_api.request(MTPaccount_ConfirmPasswordEmail(
 | 
						|
			MTP_string(code)
 | 
						|
		)).done([=] {
 | 
						|
			_api.request(MTPaccount_GetPassword(
 | 
						|
			)).done([=](const MTPaccount_Password &result) {
 | 
						|
				apply(ProcessMtpState(result));
 | 
						|
				consumer.put_done();
 | 
						|
			}).fail([=](const MTP::Error &error) {
 | 
						|
				consumer.put_error_copy(error.type());
 | 
						|
			}).send();
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			consumer.put_error_copy(error.type());
 | 
						|
		}).handleFloodErrors().send();
 | 
						|
 | 
						|
		return rpl::lifetime();
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<rpl::no_value, QString> CloudPassword::resendEmailCode() {
 | 
						|
	return [=](auto consumer) {
 | 
						|
		_api.request(MTPaccount_ResendPasswordEmail(
 | 
						|
		)).done([=] {
 | 
						|
			_api.request(MTPaccount_GetPassword(
 | 
						|
			)).done([=](const MTPaccount_Password &result) {
 | 
						|
				apply(ProcessMtpState(result));
 | 
						|
				consumer.put_done();
 | 
						|
			}).fail([=](const MTP::Error &error) {
 | 
						|
				consumer.put_error_copy(error.type());
 | 
						|
			}).send();
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			consumer.put_error_copy(error.type());
 | 
						|
		}).handleFloodErrors().send();
 | 
						|
 | 
						|
		return rpl::lifetime();
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<CloudPassword::SetOk, QString> CloudPassword::setEmail(
 | 
						|
		const QString &oldPassword,
 | 
						|
		const QString &recoveryEmail) {
 | 
						|
	const auto generatePasswordCheck = [=](
 | 
						|
			const Core::CloudPasswordState &latestState) {
 | 
						|
		if (oldPassword.isEmpty() || !latestState.hasPassword) {
 | 
						|
			return Core::CloudPasswordResult{
 | 
						|
				MTP_inputCheckPasswordEmpty()
 | 
						|
			};
 | 
						|
		}
 | 
						|
		const auto hash = Core::ComputeCloudPasswordHash(
 | 
						|
			latestState.mtp.request.algo,
 | 
						|
			bytes::make_span(oldPassword.toUtf8()));
 | 
						|
		return Core::ComputeCloudPasswordCheck(
 | 
						|
			latestState.mtp.request,
 | 
						|
			hash);
 | 
						|
	};
 | 
						|
 | 
						|
	const auto finish = [=](auto consumer, int unconfirmedEmailLengthCode) {
 | 
						|
		_api.request(MTPaccount_GetPassword(
 | 
						|
		)).done([=](const MTPaccount_Password &result) {
 | 
						|
			apply(ProcessMtpState(result));
 | 
						|
			if (unconfirmedEmailLengthCode) {
 | 
						|
				consumer.put_next(SetOk{ unconfirmedEmailLengthCode });
 | 
						|
			} else {
 | 
						|
				consumer.put_done();
 | 
						|
			}
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			consumer.put_error_copy(error.type());
 | 
						|
		}).handleFloodErrors().send();
 | 
						|
	};
 | 
						|
 | 
						|
	const auto sendMTPaccountUpdatePasswordSettings = [=](
 | 
						|
			const Core::CloudPasswordState &latestState,
 | 
						|
			auto consumer) {
 | 
						|
		const auto settings = MTP_account_passwordInputSettings(
 | 
						|
			MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email),
 | 
						|
			MTP_passwordKdfAlgoUnknown(),
 | 
						|
			MTP_bytes(),
 | 
						|
			MTP_string(),
 | 
						|
			MTP_string(recoveryEmail),
 | 
						|
			MTPSecureSecretSettings());
 | 
						|
		_api.request(MTPaccount_UpdatePasswordSettings(
 | 
						|
			generatePasswordCheck(latestState).result,
 | 
						|
			settings
 | 
						|
		)).done([=] {
 | 
						|
			finish(consumer, 0);
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			const auto &type = error.type();
 | 
						|
			const auto prefix = u"EMAIL_UNCONFIRMED_"_q;
 | 
						|
			if (type.startsWith(prefix)) {
 | 
						|
				const auto codeLength = base::StringViewMid(
 | 
						|
					type,
 | 
						|
					prefix.size()).toInt();
 | 
						|
 | 
						|
				finish(consumer, codeLength);
 | 
						|
			} else {
 | 
						|
				consumer.put_error_copy(type);
 | 
						|
			}
 | 
						|
		}).handleFloodErrors().send();
 | 
						|
	};
 | 
						|
 | 
						|
	return [=](auto consumer) {
 | 
						|
		_api.request(MTPaccount_GetPassword(
 | 
						|
		)).done([=](const MTPaccount_Password &result) {
 | 
						|
			const auto latestState = ProcessMtpState(result);
 | 
						|
			sendMTPaccountUpdatePasswordSettings(latestState, consumer);
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			consumer.put_error_copy(error.type());
 | 
						|
		}).send();
 | 
						|
		return rpl::lifetime();
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<rpl::no_value, QString> CloudPassword::recoverPassword(
 | 
						|
		const QString &code,
 | 
						|
		const QString &newPassword,
 | 
						|
		const QString &newHint) {
 | 
						|
 | 
						|
	const auto finish = [=](auto consumer) {
 | 
						|
		_api.request(MTPaccount_GetPassword(
 | 
						|
		)).done([=](const MTPaccount_Password &result) {
 | 
						|
			apply(ProcessMtpState(result));
 | 
						|
			consumer.put_done();
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			consumer.put_error_copy(error.type());
 | 
						|
		}).handleFloodErrors().send();
 | 
						|
	};
 | 
						|
 | 
						|
	const auto sendMTPaccountUpdatePasswordSettings = [=](
 | 
						|
			const Core::CloudPasswordState &latestState,
 | 
						|
			auto consumer) {
 | 
						|
		const auto newPasswordBytes = newPassword.toUtf8();
 | 
						|
		const auto newPasswordHash = Core::ComputeCloudPasswordDigest(
 | 
						|
			latestState.mtp.newPassword,
 | 
						|
			bytes::make_span(newPasswordBytes));
 | 
						|
		if (!newPassword.isEmpty() && newPasswordHash.modpow.empty()) {
 | 
						|
			consumer.put_error("INTERNAL_SERVER_ERROR");
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		using Flag = MTPDaccount_passwordInputSettings::Flag;
 | 
						|
		const auto flags = Flag::f_new_algo
 | 
						|
			| Flag::f_new_password_hash
 | 
						|
			| Flag::f_hint;
 | 
						|
 | 
						|
		const auto settings = MTP_account_passwordInputSettings(
 | 
						|
			MTP_flags(flags),
 | 
						|
			Core::PrepareCloudPasswordAlgo(newPassword.isEmpty()
 | 
						|
				? v::null
 | 
						|
				: latestState.mtp.newPassword),
 | 
						|
			newPassword.isEmpty()
 | 
						|
				? MTP_bytes()
 | 
						|
				: MTP_bytes(newPasswordHash.modpow),
 | 
						|
			MTP_string(newHint),
 | 
						|
			MTP_string(),
 | 
						|
			MTPSecureSecretSettings());
 | 
						|
 | 
						|
		_api.request(MTPauth_RecoverPassword(
 | 
						|
			MTP_flags(newPassword.isEmpty()
 | 
						|
				? MTPauth_RecoverPassword::Flags(0)
 | 
						|
				: MTPauth_RecoverPassword::Flag::f_new_settings),
 | 
						|
			MTP_string(code),
 | 
						|
			settings
 | 
						|
		)).done([=](const MTPauth_Authorization &result) {
 | 
						|
			finish(consumer);
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			const auto &type = error.type();
 | 
						|
			consumer.put_error_copy(type);
 | 
						|
		}).handleFloodErrors().send();
 | 
						|
	};
 | 
						|
 | 
						|
	return [=](auto consumer) {
 | 
						|
		_api.request(MTPaccount_GetPassword(
 | 
						|
		)).done([=](const MTPaccount_Password &result) {
 | 
						|
			const auto latestState = ProcessMtpState(result);
 | 
						|
			sendMTPaccountUpdatePasswordSettings(latestState, consumer);
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			consumer.put_error_copy(error.type());
 | 
						|
		}).send();
 | 
						|
		return rpl::lifetime();
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
rpl::producer<QString, QString> CloudPassword::requestPasswordRecovery() {
 | 
						|
	return [=](auto consumer) {
 | 
						|
		_api.request(MTPauth_RequestPasswordRecovery(
 | 
						|
		)).done([=](const MTPauth_PasswordRecovery &result) {
 | 
						|
			result.match([&](const MTPDauth_passwordRecovery &data) {
 | 
						|
				consumer.put_next(qs(data.vemail_pattern().v));
 | 
						|
			});
 | 
						|
			consumer.put_done();
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			consumer.put_error_copy(error.type());
 | 
						|
		}).send();
 | 
						|
		return rpl::lifetime();
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
auto CloudPassword::checkRecoveryEmailAddressCode(const QString &code)
 | 
						|
-> rpl::producer<rpl::no_value, QString> {
 | 
						|
	return [=](auto consumer) {
 | 
						|
		_api.request(MTPauth_CheckRecoveryPassword(
 | 
						|
			MTP_string(code)
 | 
						|
		)).done([=] {
 | 
						|
			consumer.put_done();
 | 
						|
		}).fail([=](const MTP::Error &error) {
 | 
						|
			consumer.put_error_copy(error.type());
 | 
						|
		}).handleFloodErrors().send();
 | 
						|
 | 
						|
		return rpl::lifetime();
 | 
						|
	};
 | 
						|
}
 | 
						|
 | 
						|
} // namespace Api
 |