| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117 |
- /*
- 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/mtp_instance.h"
- #include "mtproto/details/mtproto_dcenter.h"
- #include "mtproto/details/mtproto_rsa_public_key.h"
- #include "mtproto/special_config_request.h"
- #include "mtproto/session.h"
- #include "mtproto/mtproto_config.h"
- #include "mtproto/mtproto_dc_options.h"
- #include "mtproto/config_loader.h"
- #include "mtproto/sender.h"
- #include "storage/localstorage.h"
- #include "calls/calls_instance.h"
- #include "main/main_account.h" // Account::configUpdated.
- #include "core/application.h"
- #include "core/core_settings.h"
- #include "lang/lang_instance.h"
- #include "lang/lang_cloud_manager.h"
- #include "base/unixtime.h"
- #include "base/call_delayed.h"
- #include "base/timer.h"
- #include "base/network_reachability.h"
- namespace MTP {
- namespace {
- constexpr auto kConfigBecomesOldIn = 2 * 60 * crl::time(1000);
- constexpr auto kConfigBecomesOldForBlockedIn = 8 * crl::time(1000);
- using namespace details;
- std::atomic<int> GlobalAtomicRequestId = 0;
- } // namespace
- namespace details {
- int GetNextRequestId() {
- const auto result = ++GlobalAtomicRequestId;
- if (result == std::numeric_limits<int>::max() / 2) {
- GlobalAtomicRequestId = 0;
- }
- return result;
- }
- } // namespace details
- class Instance::Private : private Sender {
- public:
- Private(
- not_null<Instance*> instance,
- Instance::Mode mode,
- Fields &&fields);
- void start();
- [[nodiscard]] Config &config() const;
- [[nodiscard]] const ConfigFields &configValues() const;
- [[nodiscard]] DcOptions &dcOptions() const;
- [[nodiscard]] Environment environment() const;
- [[nodiscard]] bool isTestMode() const;
- void resolveProxyDomain(const QString &host);
- void setGoodProxyDomain(const QString &host, const QString &ip);
- void suggestMainDcId(DcId mainDcId);
- void setMainDcId(DcId mainDcId);
- [[nodiscard]] bool hasMainDcId() const;
- [[nodiscard]] DcId mainDcId() const;
- [[nodiscard]] rpl::producer<DcId> mainDcIdValue() const;
- [[nodiscard]] rpl::producer<> writeKeysRequests() const;
- void dcPersistentKeyChanged(DcId dcId, const AuthKeyPtr &persistentKey);
- void dcTemporaryKeyChanged(DcId dcId);
- [[nodiscard]] rpl::producer<DcId> dcTemporaryKeyChanged() const;
- [[nodiscard]] AuthKeysList getKeysForWrite() const;
- void addKeysForDestroy(AuthKeysList &&keys);
- [[nodiscard]] rpl::producer<> allKeysDestroyed() const;
- // Thread safe.
- [[nodiscard]] QString deviceModel() const;
- [[nodiscard]] QString systemVersion() const;
- // Main thread.
- void requestConfig();
- void requestConfigIfOld();
- void requestCDNConfig();
- void setUserPhone(const QString &phone);
- void badConfigurationError();
- void syncHttpUnixtime();
- void restartedByTimeout(ShiftedDcId shiftedDcId);
- [[nodiscard]] rpl::producer<ShiftedDcId> restartsByTimeout() const;
- [[nodiscard]] auto nonPremiumDelayedRequests() const
- -> rpl::producer<mtpRequestId>;
- void restart();
- void restart(ShiftedDcId shiftedDcId);
- [[nodiscard]] int32 dcstate(ShiftedDcId shiftedDcId = 0);
- [[nodiscard]] QString dctransport(ShiftedDcId shiftedDcId = 0);
- void ping();
- void cancel(mtpRequestId requestId);
- [[nodiscard]] int32 state(mtpRequestId requestId); // < 0 means waiting for such count of ms
- void killSession(ShiftedDcId shiftedDcId);
- void stopSession(ShiftedDcId shiftedDcId);
- void reInitConnection(DcId dcId);
- void logout(Fn<void()> done);
- not_null<Dcenter*> getDcById(ShiftedDcId shiftedDcId);
- Dcenter *findDc(ShiftedDcId shiftedDcId);
- not_null<Dcenter*> addDc(
- ShiftedDcId shiftedDcId,
- AuthKeyPtr &&key = nullptr);
- void removeDc(ShiftedDcId shiftedDcId);
- void sendRequest(
- mtpRequestId requestId,
- SerializedRequest &&request,
- ResponseHandler &&callbacks,
- ShiftedDcId shiftedDcId,
- crl::time msCanWait,
- bool needsLayer,
- mtpRequestId afterRequestId);
- void registerRequest(mtpRequestId requestId, ShiftedDcId shiftedDcId);
- void unregisterRequest(mtpRequestId requestId);
- void storeRequest(
- mtpRequestId requestId,
- const SerializedRequest &request,
- ResponseHandler &&callbacks);
- SerializedRequest getRequest(mtpRequestId requestId);
- [[nodiscard]] bool hasCallback(mtpRequestId requestId) const;
- void processCallback(const Response &response);
- void processUpdate(const Response &message);
- void onStateChange(ShiftedDcId shiftedDcId, int32 state);
- void onSessionReset(ShiftedDcId shiftedDcId);
- // return true if need to clean request data
- bool rpcErrorOccured(
- const Response &response,
- const FailHandler &onFail,
- const Error &error);
- inline bool rpcErrorOccured(
- const Response &response,
- const ResponseHandler &handler,
- const Error &error) {
- return rpcErrorOccured(response, handler.fail, error);
- }
- void setUpdatesHandler(Fn<void(const Response&)> handler);
- void setGlobalFailHandler(
- Fn<void(const Error&, const Response&)> handler);
- void setStateChangedHandler(Fn<void(ShiftedDcId shiftedDcId, int32 state)> handler);
- void setSessionResetHandler(Fn<void(ShiftedDcId shiftedDcId)> handler);
- void clearGlobalHandlers();
- [[nodiscard]] not_null<Session*> getSession(ShiftedDcId shiftedDcId);
- bool isNormal() const {
- return (_mode == Instance::Mode::Normal);
- }
- bool isKeysDestroyer() const {
- return (_mode == Instance::Mode::KeysDestroyer);
- }
- void scheduleKeyDestroy(ShiftedDcId shiftedDcId);
- void keyWasPossiblyDestroyed(ShiftedDcId shiftedDcId);
- void performKeyDestroy(ShiftedDcId shiftedDcId);
- void completedKeyDestroy(ShiftedDcId shiftedDcId);
- void keyDestroyedOnServer(ShiftedDcId shiftedDcId, uint64 keyId);
- void prepareToDestroy();
- [[nodiscard]] rpl::lifetime &lifetime();
- private:
- void importDone(
- const MTPauth_Authorization &result,
- const Response &response);
- bool importFail(const Error &error, const Response &response);
- void exportDone(
- const MTPauth_ExportedAuthorization &result,
- const Response &response);
- bool exportFail(const Error &error, const Response &response);
- bool onErrorDefault(const Error &error, const Response &response);
- void unpaused();
- Session *findSession(ShiftedDcId shiftedDcId);
- not_null<Session*> startSession(ShiftedDcId shiftedDcId);
- void scheduleSessionDestroy(ShiftedDcId shiftedDcId);
- [[nodiscard]] not_null<QThread*> getThreadForDc(ShiftedDcId shiftedDcId);
- void applyDomainIps(
- const QString &host,
- const QStringList &ips,
- crl::time expireAt);
- void logoutGuestDcs();
- bool logoutGuestDone(mtpRequestId requestId);
- void requestConfigIfExpired();
- void configLoadDone(const MTPConfig &result);
- bool configLoadFail(const Error &error);
- std::optional<ShiftedDcId> queryRequestByDc(
- mtpRequestId requestId) const;
- std::optional<ShiftedDcId> changeRequestByDc(
- mtpRequestId requestId, DcId newdc);
- void checkDelayedRequests();
- const not_null<Instance*> _instance;
- const Instance::Mode _mode = Instance::Mode::Normal;
- const std::unique_ptr<Config> _config;
- const std::shared_ptr<base::NetworkReachability> _networkReachability;
- std::unique_ptr<QThread> _mainSessionThread;
- std::unique_ptr<QThread> _otherSessionsThread;
- std::vector<std::unique_ptr<QThread>> _fileSessionThreads;
- QString _deviceModelDefault;
- QString _systemVersion;
- mutable QMutex _deviceModelMutex;
- QString _customDeviceModel;
- rpl::variable<DcId> _mainDcId = Fields::kDefaultMainDc;
- bool _mainDcIdForced = false;
- base::flat_map<DcId, std::unique_ptr<Dcenter>> _dcenters;
- std::vector<std::unique_ptr<Dcenter>> _dcentersToDestroy;
- rpl::event_stream<DcId> _dcTemporaryKeyChanged;
- Session *_mainSession = nullptr;
- base::flat_map<ShiftedDcId, std::unique_ptr<Session>> _sessions;
- std::vector<std::unique_ptr<Session>> _sessionsToDestroy;
- rpl::event_stream<ShiftedDcId> _restartsByTimeout;
- std::unique_ptr<ConfigLoader> _configLoader;
- std::unique_ptr<DomainResolver> _domainResolver;
- std::unique_ptr<SpecialConfigRequest> _httpUnixtimeLoader;
- QString _userPhone;
- mtpRequestId _cdnConfigLoadRequestId = 0;
- crl::time _lastConfigLoadedTime = 0;
- crl::time _configExpiresAt = 0;
- base::flat_map<DcId, AuthKeyPtr> _keysForWrite;
- base::flat_map<ShiftedDcId, mtpRequestId> _logoutGuestRequestIds;
- rpl::event_stream<> _writeKeysRequests;
- rpl::event_stream<> _allKeysDestroyed;
- // holds dcWithShift for request to this dc or -dc for request to main dc
- std::map<mtpRequestId, ShiftedDcId> _requestsByDc;
- mutable QMutex _requestByDcLock;
- // holds target dcWithShift for auth export request
- std::map<mtpRequestId, ShiftedDcId> _authExportRequests;
- std::map<mtpRequestId, ResponseHandler> _parserMap;
- mutable QMutex _parserMapLock;
- std::map<mtpRequestId, SerializedRequest> _requestMap;
- QReadWriteLock _requestMapLock;
- std::deque<std::pair<mtpRequestId, crl::time>> _delayedRequests;
- base::flat_map<mtpRequestId, mtpRequestId> _dependentRequests;
- mutable QMutex _dependentRequestsLock;
- std::map<mtpRequestId, int> _requestsDelays;
- std::set<mtpRequestId> _badGuestDcRequests;
- std::map<DcId, std::vector<mtpRequestId>> _authWaiters;
- Fn<void(const Response&)> _updatesHandler;
- Fn<void(const Error&, const Response&)> _globalFailHandler;
- Fn<void(ShiftedDcId shiftedDcId, int32 state)> _stateChangedHandler;
- Fn<void(ShiftedDcId shiftedDcId)> _sessionResetHandler;
- rpl::event_stream<mtpRequestId> _nonPremiumDelayedRequests;
- base::Timer _checkDelayedTimer;
- Core::SettingsProxy &_proxySettings;
- rpl::lifetime _lifetime;
- };
- Instance::Fields::Fields() = default;
- Instance::Fields::Fields(Fields &&other) = default;
- auto Instance::Fields::operator=(Fields &&other) -> Fields & = default;
- Instance::Fields::~Fields() = default;
- Instance::Private::Private(
- not_null<Instance*> instance,
- Instance::Mode mode,
- Fields &&fields)
- : Sender(instance)
- , _instance(instance)
- , _mode(mode)
- , _config(std::move(fields.config))
- , _networkReachability(base::NetworkReachability::Instance())
- , _proxySettings(Core::App().settings().proxy()) {
- Expects(_config != nullptr);
- const auto idealThreadPoolSize = QThread::idealThreadCount();
- _fileSessionThreads.resize(2 * std::max(idealThreadPoolSize / 2, 1));
- details::unpaused(
- ) | rpl::start_with_next([=] {
- unpaused();
- }, _lifetime);
- _networkReachability->availableChanges(
- ) | rpl::start_with_next([=](bool available) {
- restart();
- }, _lifetime);
- _deviceModelDefault = std::move(fields.deviceModel);
- _systemVersion = std::move(fields.systemVersion);
- _customDeviceModel = Core::App().settings().customDeviceModel();
- Core::App().settings().customDeviceModelChanges(
- ) | rpl::start_with_next([=](const QString &value) {
- QMutexLocker lock(&_deviceModelMutex);
- _customDeviceModel = value;
- lock.unlock();
- reInitConnection(mainDcId());
- }, _lifetime);
- for (auto &key : fields.keys) {
- auto dcId = key->dcId();
- auto shiftedDcId = dcId;
- if (isKeysDestroyer()) {
- shiftedDcId = MTP::destroyKeyNextDcId(shiftedDcId);
- // There could be several keys for one dc if we're destroying them.
- // Place them all in separate shiftedDcId so that they won't conflict.
- while (_keysForWrite.find(shiftedDcId) != _keysForWrite.cend()) {
- shiftedDcId = MTP::destroyKeyNextDcId(shiftedDcId);
- }
- }
- _keysForWrite[shiftedDcId] = key;
- addDc(shiftedDcId, std::move(key));
- }
- if (fields.mainDcId != Fields::kNotSetMainDc) {
- _mainDcId = fields.mainDcId;
- _mainDcIdForced = true;
- }
- _proxySettings.connectionTypeChanges(
- ) | rpl::start_with_next([=] {
- if (_configLoader) {
- _configLoader->setProxyEnabled(_proxySettings.isEnabled());
- }
- }, _lifetime);
- }
- void Instance::Private::start() {
- if (isKeysDestroyer()) {
- for (const auto &[shiftedDcId, dc] : _dcenters) {
- startSession(shiftedDcId);
- }
- } else if (hasMainDcId()) {
- _mainSession = startSession(mainDcId());
- }
- _checkDelayedTimer.setCallback([this] { checkDelayedRequests(); });
- Assert(!hasMainDcId() == isKeysDestroyer());
- requestConfig();
- }
- void Instance::Private::resolveProxyDomain(const QString &host) {
- if (!_domainResolver) {
- _domainResolver = std::make_unique<DomainResolver>([=](
- const QString &host,
- const QStringList &ips,
- crl::time expireAt) {
- applyDomainIps(host, ips, expireAt);
- });
- }
- _domainResolver->resolve(host);
- }
- void Instance::Private::applyDomainIps(
- const QString &host,
- const QStringList &ips,
- crl::time expireAt) {
- const auto applyToProxy = [&](ProxyData &proxy) {
- if (!proxy.tryCustomResolve() || proxy.host != host) {
- return false;
- }
- proxy.resolvedExpireAt = expireAt;
- auto copy = ips;
- auto ¤t = proxy.resolvedIPs;
- const auto i = ranges::remove_if(current, [&](const QString &ip) {
- const auto index = copy.indexOf(ip);
- if (index < 0) {
- return true;
- }
- copy.removeAt(index);
- return false;
- });
- if (i == end(current) && copy.isEmpty()) {
- // Even if the proxy was changed already, we still want
- // to refreshOptions in all sessions across all instances.
- return true;
- }
- current.erase(i, end(current));
- for (const auto &ip : std::as_const(copy)) {
- proxy.resolvedIPs.push_back(ip);
- }
- return true;
- };
- for (auto &proxy : _proxySettings.list()) {
- applyToProxy(proxy);
- }
- auto selected = _proxySettings.selected();
- if (applyToProxy(selected) && _proxySettings.isEnabled()) {
- _proxySettings.setSelected(selected);
- for (const auto &[shiftedDcId, session] : _sessions) {
- session->refreshOptions();
- }
- }
- _instance->proxyDomainResolved(host, ips, expireAt);
- }
- void Instance::Private::setGoodProxyDomain(
- const QString &host,
- const QString &ip) {
- const auto applyToProxy = [&](ProxyData &proxy) {
- if (!proxy.tryCustomResolve() || proxy.host != host) {
- return false;
- }
- auto ¤t = proxy.resolvedIPs;
- auto i = ranges::find(current, ip);
- if (i == end(current) || i == begin(current)) {
- return false;
- }
- while (i != begin(current)) {
- const auto j = i--;
- std::swap(*i, *j);
- }
- return true;
- };
- for (auto &proxy : _proxySettings.list()) {
- applyToProxy(proxy);
- }
- auto selected = _proxySettings.selected();
- if (applyToProxy(selected) && _proxySettings.isEnabled()) {
- _proxySettings.setSelected(selected);
- Core::App().refreshGlobalProxy();
- }
- }
- void Instance::Private::suggestMainDcId(DcId mainDcId) {
- if (!_mainDcIdForced) {
- setMainDcId(mainDcId);
- }
- }
- void Instance::Private::setMainDcId(DcId mainDcId) {
- if (!_mainSession) {
- LOG(("MTP Error: attempting to change mainDcId in an MTP instance without main session."));
- return;
- }
- _mainDcIdForced = true;
- const auto oldMainDcId = _mainSession->getDcWithShift();
- if (oldMainDcId != mainDcId) {
- scheduleSessionDestroy(oldMainDcId);
- scheduleSessionDestroy(mainDcId);
- _mainSession = startSession(mainDcId);
- }
- _mainDcId = mainDcId;
- _writeKeysRequests.fire({});
- }
- bool Instance::Private::hasMainDcId() const {
- return (_mainDcId.current() != Fields::kNoneMainDc);
- }
- DcId Instance::Private::mainDcId() const {
- Expects(hasMainDcId());
- return _mainDcId.current();
- }
- rpl::producer<DcId> Instance::Private::mainDcIdValue() const {
- Expects(_mainDcId.current() != Fields::kNoneMainDc);
- return _mainDcId.value();
- }
- void Instance::Private::requestConfig() {
- if (_configLoader || isKeysDestroyer()) {
- return;
- }
- _configLoader = std::make_unique<ConfigLoader>(
- _instance,
- _userPhone,
- [=](const MTPConfig &result) { configLoadDone(result); },
- [=](const Error &error, const Response &) {
- return configLoadFail(error);
- },
- _proxySettings.isEnabled());
- _configLoader->load();
- }
- void Instance::Private::setUserPhone(const QString &phone) {
- if (_userPhone != phone) {
- _userPhone = phone;
- if (_configLoader) {
- _configLoader->setPhone(_userPhone);
- }
- }
- }
- void Instance::Private::badConfigurationError() {
- if (_mode == Mode::Normal) {
- Core::App().badMtprotoConfigurationError();
- }
- }
- void Instance::Private::syncHttpUnixtime() {
- if (base::unixtime::http_valid() || _httpUnixtimeLoader) {
- return;
- }
- _httpUnixtimeLoader = std::make_unique<SpecialConfigRequest>([=] {
- InvokeQueued(_instance, [=] {
- _httpUnixtimeLoader = nullptr;
- });
- }, isTestMode(), configValues().txtDomainString);
- }
- void Instance::Private::restartedByTimeout(ShiftedDcId shiftedDcId) {
- _restartsByTimeout.fire_copy(shiftedDcId);
- }
- rpl::producer<ShiftedDcId> Instance::Private::restartsByTimeout() const {
- return _restartsByTimeout.events();
- }
- auto Instance::Private::nonPremiumDelayedRequests() const
- -> rpl::producer<mtpRequestId> {
- return _nonPremiumDelayedRequests.events();
- }
- void Instance::Private::requestConfigIfOld() {
- const auto timeout = _config->values().blockedMode
- ? kConfigBecomesOldForBlockedIn
- : kConfigBecomesOldIn;
- if (crl::now() - _lastConfigLoadedTime >= timeout) {
- requestConfig();
- }
- }
- void Instance::Private::requestConfigIfExpired() {
- const auto requestIn = (_configExpiresAt - crl::now());
- if (requestIn > 0) {
- base::call_delayed(
- std::min(requestIn, 3600 * crl::time(1000)),
- _instance,
- [=] { requestConfigIfExpired(); });
- } else {
- requestConfig();
- }
- }
- void Instance::Private::requestCDNConfig() {
- if (_cdnConfigLoadRequestId || !hasMainDcId()) {
- return;
- }
- _cdnConfigLoadRequestId = request(
- MTPhelp_GetCdnConfig()
- ).done([this](const MTPCdnConfig &result) {
- _cdnConfigLoadRequestId = 0;
- result.match([&](const MTPDcdnConfig &data) {
- dcOptions().setCDNConfig(data);
- });
- Local::writeSettings();
- }).send();
- }
- void Instance::Private::restart() {
- for (const auto &[shiftedDcId, session] : _sessions) {
- session->restart();
- }
- }
- void Instance::Private::restart(ShiftedDcId shiftedDcId) {
- const auto dcId = BareDcId(shiftedDcId);
- for (const auto &[shiftedDcId, session] : _sessions) {
- if (BareDcId(shiftedDcId) == dcId) {
- session->restart();
- }
- }
- }
- int32 Instance::Private::dcstate(ShiftedDcId shiftedDcId) {
- if (!shiftedDcId) {
- Assert(_mainSession != nullptr);
- return _mainSession->getState();
- }
- if (!BareDcId(shiftedDcId)) {
- Assert(_mainSession != nullptr);
- shiftedDcId += BareDcId(_mainSession->getDcWithShift());
- }
- if (const auto session = findSession(shiftedDcId)) {
- return session->getState();
- }
- return DisconnectedState;
- }
- QString Instance::Private::dctransport(ShiftedDcId shiftedDcId) {
- if (!shiftedDcId) {
- Assert(_mainSession != nullptr);
- return _mainSession->transport();
- }
- if (!BareDcId(shiftedDcId)) {
- Assert(_mainSession != nullptr);
- shiftedDcId += BareDcId(_mainSession->getDcWithShift());
- }
- if (const auto session = findSession(shiftedDcId)) {
- return session->transport();
- }
- return QString();
- }
- void Instance::Private::ping() {
- getSession(0)->ping();
- }
- void Instance::Private::cancel(mtpRequestId requestId) {
- if (!requestId) return;
- DEBUG_LOG(("MTP Info: Cancel request %1.").arg(requestId));
- const auto shiftedDcId = queryRequestByDc(requestId);
- auto msgId = mtpMsgId(0);
- {
- QWriteLocker locker(&_requestMapLock);
- auto it = _requestMap.find(requestId);
- if (it != _requestMap.end()) {
- msgId = *(mtpMsgId*)(it->second->constData() + 4);
- _requestMap.erase(it);
- }
- }
- unregisterRequest(requestId);
- if (shiftedDcId) {
- const auto session = getSession(qAbs(*shiftedDcId));
- session->cancel(requestId, msgId);
- }
- QMutexLocker locker(&_parserMapLock);
- _parserMap.erase(requestId);
- }
- // result < 0 means waiting for such count of ms.
- int32 Instance::Private::state(mtpRequestId requestId) {
- if (requestId > 0) {
- if (const auto shiftedDcId = queryRequestByDc(requestId)) {
- const auto session = getSession(qAbs(*shiftedDcId));
- return session->requestState(requestId);
- }
- return MTP::RequestSent;
- }
- const auto session = getSession(-requestId);
- return session->requestState(0);
- }
- void Instance::Private::killSession(ShiftedDcId shiftedDcId) {
- const auto i = _sessions.find(shiftedDcId);
- if (i != _sessions.cend()) {
- Assert(i->second.get() != _mainSession);
- }
- scheduleSessionDestroy(shiftedDcId);
- }
- void Instance::Private::stopSession(ShiftedDcId shiftedDcId) {
- if (const auto session = findSession(shiftedDcId)) {
- if (session != _mainSession) { // don't stop main session
- session->stop();
- }
- }
- }
- void Instance::Private::reInitConnection(DcId dcId) {
- for (const auto &[shiftedDcId, session] : _sessions) {
- if (BareDcId(shiftedDcId) == dcId) {
- session->reInitConnection();
- }
- }
- }
- void Instance::Private::logout(Fn<void()> done) {
- _instance->send(MTPauth_LogOut(), [=](Response) {
- done();
- return true;
- }, [=](const Error&, Response) {
- done();
- return true;
- });
- logoutGuestDcs();
- }
- void Instance::Private::logoutGuestDcs() {
- Expects(!isKeysDestroyer());
- auto dcIds = std::vector<DcId>();
- dcIds.reserve(_keysForWrite.size());
- for (const auto &key : _keysForWrite) {
- dcIds.push_back(key.first);
- }
- for (const auto dcId : dcIds) {
- if (dcId == mainDcId() || dcOptions().dcType(dcId) == DcType::Cdn) {
- continue;
- }
- const auto shiftedDcId = MTP::logoutDcId(dcId);
- const auto requestId = _instance->send(MTPauth_LogOut(), [=](
- const Response &response) {
- logoutGuestDone(response.requestId);
- return true;
- }, [=](const Error &, const Response &response) {
- logoutGuestDone(response.requestId);
- return true;
- }, shiftedDcId);
- _logoutGuestRequestIds.emplace(shiftedDcId, requestId);
- }
- }
- bool Instance::Private::logoutGuestDone(mtpRequestId requestId) {
- for (auto i = _logoutGuestRequestIds.begin(), e = _logoutGuestRequestIds.end(); i != e; ++i) {
- if (i->second == requestId) {
- killSession(i->first);
- _logoutGuestRequestIds.erase(i);
- return true;
- }
- }
- return false;
- }
- Dcenter *Instance::Private::findDc(ShiftedDcId shiftedDcId) {
- const auto i = _dcenters.find(shiftedDcId);
- return (i != _dcenters.end()) ? i->second.get() : nullptr;
- }
- not_null<Dcenter*> Instance::Private::addDc(
- ShiftedDcId shiftedDcId,
- AuthKeyPtr &&key) {
- const auto dcId = BareDcId(shiftedDcId);
- return _dcenters.emplace(
- shiftedDcId,
- std::make_unique<Dcenter>(dcId, std::move(key))
- ).first->second.get();
- }
- void Instance::Private::removeDc(ShiftedDcId shiftedDcId) {
- const auto i = _dcenters.find(shiftedDcId);
- if (i != _dcenters.end()) {
- _dcentersToDestroy.push_back(std::move(i->second));
- _dcenters.erase(i);
- }
- }
- not_null<Dcenter*> Instance::Private::getDcById(
- ShiftedDcId shiftedDcId) {
- if (const auto result = findDc(shiftedDcId)) {
- return result;
- }
- const auto dcId = [&] {
- const auto result = BareDcId(shiftedDcId);
- if (isTemporaryDcId(result)) {
- if (const auto realDcId = getRealIdFromTemporaryDcId(result)) {
- return realDcId;
- }
- }
- return result;
- }();
- if (dcId != shiftedDcId) {
- if (const auto result = findDc(dcId)) {
- return result;
- }
- }
- return addDc(dcId);
- }
- void Instance::Private::dcPersistentKeyChanged(
- DcId dcId,
- const AuthKeyPtr &persistentKey) {
- dcTemporaryKeyChanged(dcId);
- if (isTemporaryDcId(dcId)) {
- return;
- }
- const auto i = _keysForWrite.find(dcId);
- if (i != _keysForWrite.end() && i->second == persistentKey) {
- return;
- } else if (i == _keysForWrite.end() && !persistentKey) {
- return;
- }
- if (!persistentKey) {
- _keysForWrite.erase(i);
- } else if (i != _keysForWrite.end()) {
- i->second = persistentKey;
- } else {
- _keysForWrite.emplace(dcId, persistentKey);
- }
- DEBUG_LOG(("AuthKey Info: writing auth keys, called by dc %1"
- ).arg(dcId));
- _writeKeysRequests.fire({});
- }
- void Instance::Private::dcTemporaryKeyChanged(DcId dcId) {
- _dcTemporaryKeyChanged.fire_copy(dcId);
- }
- rpl::producer<DcId> Instance::Private::dcTemporaryKeyChanged() const {
- return _dcTemporaryKeyChanged.events();
- }
- AuthKeysList Instance::Private::getKeysForWrite() const {
- auto result = AuthKeysList();
- result.reserve(_keysForWrite.size());
- for (const auto &key : _keysForWrite) {
- result.push_back(key.second);
- }
- return result;
- }
- void Instance::Private::addKeysForDestroy(AuthKeysList &&keys) {
- Expects(isKeysDestroyer());
- for (auto &key : keys) {
- const auto dcId = key->dcId();
- auto shiftedDcId = MTP::destroyKeyNextDcId(dcId);
- // There could be several keys for one dc if we're destroying them.
- // Place them all in separate shiftedDcId so that they won't conflict.
- while (_keysForWrite.find(shiftedDcId) != _keysForWrite.cend()) {
- shiftedDcId = MTP::destroyKeyNextDcId(shiftedDcId);
- }
- _keysForWrite[shiftedDcId] = key;
- addDc(shiftedDcId, std::move(key));
- startSession(shiftedDcId);
- }
- }
- rpl::producer<> Instance::Private::allKeysDestroyed() const {
- return _allKeysDestroyed.events();
- }
- rpl::producer<> Instance::Private::writeKeysRequests() const {
- return _writeKeysRequests.events();
- }
- Config &Instance::Private::config() const {
- return *_config;
- }
- const ConfigFields &Instance::Private::configValues() const {
- return _config->values();
- }
- DcOptions &Instance::Private::dcOptions() const {
- return _config->dcOptions();
- }
- Environment Instance::Private::environment() const {
- return _config->environment();
- }
- bool Instance::Private::isTestMode() const {
- return _config->isTestMode();
- }
- QString Instance::Private::deviceModel() const {
- QMutexLocker lock(&_deviceModelMutex);
- return _customDeviceModel.isEmpty()
- ? _deviceModelDefault
- : _customDeviceModel;
- }
- QString Instance::Private::systemVersion() const {
- return _systemVersion;
- }
- void Instance::Private::unpaused() {
- for (const auto &[shiftedDcId, session] : _sessions) {
- session->unpaused();
- }
- }
- void Instance::Private::configLoadDone(const MTPConfig &result) {
- Expects(result.type() == mtpc_config);
- _configLoader.reset();
- _lastConfigLoadedTime = crl::now();
- const auto &data = result.c_config();
- _config->apply(data);
- const auto lang = qs(data.vsuggested_lang_code().value_or_empty());
- Lang::CurrentCloudManager().setSuggestedLanguage(lang);
- Lang::CurrentCloudManager().setCurrentVersions(
- data.vlang_pack_version().value_or_empty(),
- data.vbase_lang_pack_version().value_or_empty());
- if (const auto prefix = data.vautoupdate_url_prefix()) {
- Local::writeAutoupdatePrefix(qs(*prefix));
- }
- _configExpiresAt = crl::now()
- + (data.vexpires().v - base::unixtime::now()) * crl::time(1000);
- requestConfigIfExpired();
- }
- bool Instance::Private::configLoadFail(const Error &error) {
- if (IsDefaultHandledError(error)) return false;
- // loadingConfig = false;
- LOG(("MTP Error: failed to get config!"));
- return false;
- }
- std::optional<ShiftedDcId> Instance::Private::queryRequestByDc(
- mtpRequestId requestId) const {
- QMutexLocker locker(&_requestByDcLock);
- auto it = _requestsByDc.find(requestId);
- if (it != _requestsByDc.cend()) {
- return it->second;
- }
- return std::nullopt;
- }
- std::optional<ShiftedDcId> Instance::Private::changeRequestByDc(
- mtpRequestId requestId,
- DcId newdc) {
- QMutexLocker locker(&_requestByDcLock);
- auto it = _requestsByDc.find(requestId);
- if (it != _requestsByDc.cend()) {
- if (it->second < 0) {
- it->second = -newdc;
- } else {
- it->second = ShiftDcId(newdc, GetDcIdShift(it->second));
- }
- return it->second;
- }
- return std::nullopt;
- }
- void Instance::Private::checkDelayedRequests() {
- auto now = crl::now();
- while (!_delayedRequests.empty() && now >= _delayedRequests.front().second) {
- auto requestId = _delayedRequests.front().first;
- _delayedRequests.pop_front();
- auto dcWithShift = ShiftedDcId(0);
- if (const auto shiftedDcId = queryRequestByDc(requestId)) {
- dcWithShift = *shiftedDcId;
- } else {
- LOG(("MTP Error: could not find request dc for delayed resend, requestId %1").arg(requestId));
- continue;
- }
- auto request = SerializedRequest();
- {
- QReadLocker locker(&_requestMapLock);
- auto it = _requestMap.find(requestId);
- if (it == _requestMap.cend()) {
- DEBUG_LOG(("MTP Error: could not find request %1").arg(requestId));
- continue;
- }
- request = it->second;
- }
- const auto session = getSession(qAbs(dcWithShift));
- session->sendPrepared(request);
- }
- if (!_delayedRequests.empty()) {
- _checkDelayedTimer.callOnce(_delayedRequests.front().second - now);
- }
- }
- void Instance::Private::sendRequest(
- mtpRequestId requestId,
- SerializedRequest &&request,
- ResponseHandler &&callbacks,
- ShiftedDcId shiftedDcId,
- crl::time 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);
- request->lastSentTime = crl::now();
- request->needsLayer = needsLayer;
- if (afterRequestId) {
- request->after = getRequest(afterRequestId);
- if (request->after) {
- // Check if this after request is waiting in _dependentRequests.
- // This happens if it was after some other request and failed
- // to wait for it, but that other request is still processed.
- QMutexLocker locker(&_dependentRequestsLock);
- const auto i = _dependentRequests.find(afterRequestId);
- if (i != end(_dependentRequests)) {
- _dependentRequests.emplace(requestId, afterRequestId);
- return;
- }
- }
- }
- session->sendPrepared(request, msCanWait);
- }
- void Instance::Private::registerRequest(
- mtpRequestId requestId,
- ShiftedDcId shiftedDcId) {
- QMutexLocker locker(&_requestByDcLock);
- _requestsByDc[requestId] = shiftedDcId;
- }
- void Instance::Private::unregisterRequest(mtpRequestId requestId) {
- DEBUG_LOG(("MTP Info: unregistering request %1.").arg(requestId));
- _requestsDelays.erase(requestId);
- {
- QWriteLocker locker(&_requestMapLock);
- _requestMap.erase(requestId);
- }
- {
- QMutexLocker locker(&_requestByDcLock);
- _requestsByDc.erase(requestId);
- }
- {
- auto toRemove = base::flat_set<mtpRequestId>();
- auto toResend = base::flat_set<mtpRequestId>();
- toRemove.emplace(requestId);
- QMutexLocker locker(&_dependentRequestsLock);
- auto handling = 0;
- do {
- handling = toResend.size();
- for (const auto &[resendingId, afterId] : _dependentRequests) {
- if (toRemove.contains(afterId)) {
- toRemove.emplace(resendingId);
- toResend.emplace(resendingId);
- }
- }
- } while (handling != toResend.size());
- for (const auto removingId : toRemove) {
- _dependentRequests.remove(removingId);
- }
- locker.unlock();
- for (const auto resendingId : toResend) {
- if (const auto shiftedDcId = queryRequestByDc(resendingId)) {
- SerializedRequest request;
- {
- QReadLocker locker(&_requestMapLock);
- auto it = _requestMap.find(resendingId);
- if (it == _requestMap.cend()) {
- LOG(("MTP Error: could not find dependent request %1").arg(resendingId));
- return;
- }
- request = it->second;
- }
- getSession(qAbs(*shiftedDcId))->sendPrepared(request);
- }
- }
- }
- }
- void Instance::Private::storeRequest(
- mtpRequestId requestId,
- const SerializedRequest &request,
- ResponseHandler &&callbacks) {
- if (callbacks.done || callbacks.fail) {
- QMutexLocker locker(&_parserMapLock);
- _parserMap.emplace(requestId, std::move(callbacks));
- }
- {
- QWriteLocker locker(&_requestMapLock);
- _requestMap.emplace(requestId, request);
- }
- }
- SerializedRequest Instance::Private::getRequest(mtpRequestId requestId) {
- auto result = SerializedRequest();
- {
- QReadLocker locker(&_requestMapLock);
- auto it = _requestMap.find(requestId);
- if (it != _requestMap.cend()) {
- result = it->second;
- }
- }
- return result;
- }
- bool Instance::Private::hasCallback(mtpRequestId requestId) const {
- QMutexLocker locker(&_parserMapLock);
- auto it = _parserMap.find(requestId);
- return (it != _parserMap.cend());
- }
- void Instance::Private::processCallback(const Response &response) {
- const auto requestId = response.requestId;
- ResponseHandler handler;
- {
- QMutexLocker locker(&_parserMapLock);
- auto it = _parserMap.find(requestId);
- if (it != _parserMap.cend()) {
- handler = std::move(it->second);
- _parserMap.erase(it);
- DEBUG_LOG(("RPC Info: found parser for request %1, trying to parse response...").arg(requestId));
- }
- }
- if (handler.done || handler.fail) {
- const auto handleError = [&](const Error &error) {
- DEBUG_LOG(("RPC Info: "
- "error received, code %1, type %2, description: %3").arg(
- QString::number(error.code()),
- error.type(),
- error.description()));
- const auto guard = QPointer<Instance>(_instance);
- if (rpcErrorOccured(response, handler, error) && guard) {
- unregisterRequest(requestId);
- } else if (guard) {
- QMutexLocker locker(&_parserMapLock);
- _parserMap.emplace(requestId, std::move(handler));
- }
- };
- auto from = response.reply.constData();
- if (response.reply.isEmpty()) {
- handleError(Error::Local(
- "RESPONSE_PARSE_FAILED",
- "Empty response."));
- } else if (*from == mtpc_rpc_error) {
- auto error = MTPRpcError();
- handleError(
- Error(error.read(from, from + response.reply.size())
- ? error
- : Error::MTPLocal(
- "RESPONSE_PARSE_FAILED",
- "Error parse failed.")));
- } else {
- const auto guard = QPointer<Instance>(_instance);
- if (handler.done && !handler.done(response) && guard) {
- handleError(Error::Local(
- "RESPONSE_PARSE_FAILED",
- "Response parse failed."));
- }
- if (guard) {
- unregisterRequest(requestId);
- }
- }
- } else {
- DEBUG_LOG(("RPC Info: parser not found for %1").arg(requestId));
- unregisterRequest(requestId);
- }
- }
- void Instance::Private::processUpdate(const Response &message) {
- if (_updatesHandler) {
- _updatesHandler(message);
- }
- }
- void Instance::Private::onStateChange(ShiftedDcId dcWithShift, int32 state) {
- if (_stateChangedHandler) {
- _stateChangedHandler(dcWithShift, state);
- }
- }
- void Instance::Private::onSessionReset(ShiftedDcId dcWithShift) {
- if (_sessionResetHandler) {
- _sessionResetHandler(dcWithShift);
- }
- }
- bool Instance::Private::rpcErrorOccured(
- const Response &response,
- const FailHandler &onFail,
- const Error &error) { // return true if need to clean request data
- if (IsDefaultHandledError(error)) {
- const auto guard = QPointer<Instance>(_instance);
- if (onFail && onFail(error, response)) {
- return true;
- } else if (!guard) {
- return false;
- }
- }
- if (onErrorDefault(error, response)) {
- return false;
- }
- LOG(("RPC Error: request %1 got fail with code %2, error %3%4").arg(
- QString::number(response.requestId),
- QString::number(error.code()),
- error.type(),
- error.description().isEmpty()
- ? QString()
- : QString(": %1").arg(error.description())));
- if (onFail) {
- const auto guard = QPointer<Instance>(_instance);
- onFail(error, response);
- if (!guard) {
- return false;
- }
- }
- return true;
- }
- void Instance::Private::importDone(
- const MTPauth_Authorization &result,
- const Response &response) {
- const auto shiftedDcId = queryRequestByDc(response.requestId);
- if (!shiftedDcId) {
- LOG(("MTP Error: "
- "auth import request not found in requestsByDC, requestId: %1"
- ).arg(response.requestId));
- //
- // Don't log out on export/import problems, perhaps this is a server side error.
- //
- //const auto error = Error::Local(
- // "AUTH_IMPORT_FAIL",
- // QString("did not find import request in requestsByDC, "
- // "request %1").arg(requestId));
- //if (_globalFailHandler && hasAuthorization()) {
- // _globalFailHandler(error, response); // auth failed in main dc
- //}
- return;
- }
- auto newdc = BareDcId(*shiftedDcId);
- DEBUG_LOG(("MTP Info: auth import to dc %1 succeeded").arg(newdc));
- auto &waiters = _authWaiters[newdc];
- if (waiters.size()) {
- QReadLocker locker(&_requestMapLock);
- for (auto waitedRequestId : waiters) {
- auto it = _requestMap.find(waitedRequestId);
- if (it == _requestMap.cend()) {
- LOG(("MTP Error: could not find request %1 for resending").arg(waitedRequestId));
- continue;
- }
- const auto shiftedDcId = changeRequestByDc(waitedRequestId, newdc);
- if (!shiftedDcId) {
- LOG(("MTP Error: could not find request %1 by dc for resending").arg(waitedRequestId));
- continue;
- } else if (*shiftedDcId < 0) {
- _instance->setMainDcId(newdc);
- }
- DEBUG_LOG(("MTP Info: resending request %1 to dc %2 after import auth").arg(waitedRequestId).arg(*shiftedDcId));
- const auto session = getSession(*shiftedDcId);
- session->sendPrepared(it->second);
- }
- waiters.clear();
- }
- }
- bool Instance::Private::importFail(
- const Error &error,
- const Response &response) {
- if (IsDefaultHandledError(error)) {
- return false;
- }
- //
- // Don't log out on export/import problems, perhaps this is a server side error.
- //
- //if (_globalFailHandler && hasAuthorization()) {
- // _globalFailHandler(error, response); // auth import failed
- //}
- return true;
- }
- void Instance::Private::exportDone(
- const MTPauth_ExportedAuthorization &result,
- const Response &response) {
- auto it = _authExportRequests.find(response.requestId);
- if (it == _authExportRequests.cend()) {
- LOG(("MTP Error: "
- "auth export request target dcWithShift not found, requestId: %1"
- ).arg(response.requestId));
- //
- // Don't log out on export/import problems, perhaps this is a server side error.
- //
- //const auto error = Error::Local(
- // "AUTH_IMPORT_FAIL",
- // QString("did not find target dcWithShift, request %1"
- // ).arg(requestId));
- //if (_globalFailHandler && hasAuthorization()) {
- // _globalFailHandler(error, response); // auth failed in main dc
- //}
- return;
- }
- auto &data = result.c_auth_exportedAuthorization();
- _instance->send(MTPauth_ImportAuthorization(
- data.vid(),
- data.vbytes()
- ), [this](const Response &response) {
- auto result = MTPauth_Authorization();
- auto from = response.reply.constData();
- if (!result.read(from, from + response.reply.size())) {
- return false;
- }
- importDone(result, response);
- return true;
- }, [this](const Error &error, const Response &response) {
- return importFail(error, response);
- }, it->second);
- _authExportRequests.erase(response.requestId);
- }
- bool Instance::Private::exportFail(
- const Error &error,
- const Response &response) {
- if (IsDefaultHandledError(error)) {
- return false;
- }
- auto it = _authExportRequests.find(response.requestId);
- if (it != _authExportRequests.cend()) {
- _authWaiters[BareDcId(it->second)].clear();
- }
- //
- // Don't log out on export/import problems, perhaps this is a server side error.
- //
- //if (_globalFailHandler && hasAuthorization()) {
- // _globalFailHandler(error, response); // auth failed in main dc
- //}
- return true;
- }
- bool Instance::Private::onErrorDefault(
- const Error &error,
- const Response &response) {
- const auto requestId = response.requestId;
- const auto &type = error.type();
- const auto code = error.code();
- auto badGuestDc = (code == 400) && (type == u"FILE_ID_INVALID"_q);
- static const auto MigrateRegExp = QRegularExpression("^(FILE|PHONE|NETWORK|USER)_MIGRATE_(\\d+)$");
- static const auto FloodWaitRegExp = QRegularExpression("^FLOOD_WAIT_(\\d+)$");
- static const auto FloodPremiumWaitRegExp = QRegularExpression("^FLOOD_PREMIUM_WAIT_(\\d+)$");
- static const auto SlowmodeWaitRegExp = QRegularExpression("^SLOWMODE_WAIT_(\\d+)$");
- QRegularExpressionMatch m1, m2, m3;
- if ((m1 = MigrateRegExp.match(type)).hasMatch()) {
- if (!requestId) return false;
- auto dcWithShift = ShiftedDcId(0);
- auto newdcWithShift = ShiftedDcId(m1.captured(2).toInt());
- if (const auto shiftedDcId = queryRequestByDc(requestId)) {
- dcWithShift = *shiftedDcId;
- } else {
- LOG(("MTP Error: could not find request %1 for migrating to %2").arg(requestId).arg(newdcWithShift));
- }
- if (!dcWithShift || !newdcWithShift) return false;
- DEBUG_LOG(("MTP Info: changing request %1 from dcWithShift%2 to dc%3").arg(requestId).arg(dcWithShift).arg(newdcWithShift));
- if (dcWithShift < 0) { // newdc shift = 0
- if (false/* && hasAuthorization() && _authExportRequests.find(requestId) == _authExportRequests.cend()*/) {
- //
- // migrate not supported at this moment
- // this was not tested even once
- //
- //DEBUG_LOG(("MTP Info: importing auth to dc %1").arg(newdcWithShift));
- //auto &waiters(_authWaiters[newdcWithShift]);
- //if (waiters.empty()) {
- // auto exportRequestId = _instance->send(MTPauth_ExportAuthorization(
- // MTP_int(newdcWithShift)
- // ), [this](const Response &response) {
- // auto result = MTPauth_ExportedAuthorization();
- // auto from = response.reply.constData();
- // if (!result.read(from, from + response.reply.size())) {
- // return false;
- // }
- // exportDone(result, response);
- // return true;
- // }, [this](const Error &error, const Response &response) {
- // return exportFail(error, response);
- // });
- // _authExportRequests.emplace(exportRequestId, newdcWithShift);
- //}
- //waiters.push_back(requestId);
- //return true;
- } else {
- _instance->setMainDcId(newdcWithShift);
- }
- } else {
- newdcWithShift = ShiftDcId(newdcWithShift, GetDcIdShift(dcWithShift));
- }
- auto request = SerializedRequest();
- {
- QReadLocker locker(&_requestMapLock);
- auto it = _requestMap.find(requestId);
- if (it == _requestMap.cend()) {
- LOG(("MTP Error: could not find request %1").arg(requestId));
- return false;
- }
- request = it->second;
- }
- const auto session = getSession(newdcWithShift);
- registerRequest(
- requestId,
- (dcWithShift < 0) ? -newdcWithShift : newdcWithShift);
- session->sendPrepared(request);
- return true;
- } else if (type == u"MSG_WAIT_TIMEOUT"_q || type == u"MSG_WAIT_FAILED"_q) {
- SerializedRequest request;
- {
- QReadLocker locker(&_requestMapLock);
- auto it = _requestMap.find(requestId);
- if (it == _requestMap.cend()) {
- LOG(("MTP Error: could not find MSG_WAIT_* request %1").arg(requestId));
- return false;
- }
- request = it->second;
- }
- if (!request->after) {
- LOG(("MTP Error: MSG_WAIT_* for not dependent request %1").arg(requestId));
- return false;
- }
- auto dcWithShift = ShiftedDcId(0);
- if (const auto shiftedDcId = queryRequestByDc(requestId)) {
- dcWithShift = *shiftedDcId;
- if (const auto afterDcId = queryRequestByDc(request->after->requestId)) {
- if (*shiftedDcId != *afterDcId) {
- request->after = SerializedRequest();
- }
- } else {
- request->after = SerializedRequest();
- }
- } else {
- LOG(("MTP Error: could not find MSG_WAIT_* request %1 by dc").arg(requestId));
- }
- if (!dcWithShift) {
- return false;
- }
- if (!request->after) {
- getSession(qAbs(dcWithShift))->sendPrepared(request);
- } else {
- QMutexLocker locker(&_dependentRequestsLock);
- _dependentRequests.emplace(requestId, request->after->requestId);
- }
- return true;
- } else if (code < 0
- || code >= 500
- || (m1 = FloodWaitRegExp.match(type)).hasMatch()
- || (m2 = FloodPremiumWaitRegExp.match(type)).hasMatch()
- || ((m3 = SlowmodeWaitRegExp.match(type)).hasMatch()
- && m3.captured(1).toInt() < 3)) {
- if (!requestId) {
- return false;
- }
- auto secs = 1;
- auto nonPremiumDelay = false;
- if (code < 0 || code >= 500) {
- const auto it = _requestsDelays.find(requestId);
- if (it != _requestsDelays.cend()) {
- secs = (it->second > 60) ? it->second : (it->second *= 2);
- } else {
- _requestsDelays.emplace(requestId, secs);
- }
- } else if (m1.hasMatch()) {
- secs = m1.captured(1).toInt();
- // if (secs >= 60) return false;
- } else if (m2.hasMatch()) {
- secs = m2.captured(1).toInt();
- nonPremiumDelay = true;
- } else if (m3.hasMatch()) {
- secs = m3.captured(1).toInt();
- }
- auto sendAt = crl::now() + secs * 1000 + 10;
- auto it = _delayedRequests.begin(), e = _delayedRequests.end();
- for (; it != e; ++it) {
- if (it->first == requestId) {
- return true;
- } else if (it->second > sendAt) {
- break;
- }
- }
- _delayedRequests.insert(it, std::make_pair(requestId, sendAt));
- checkDelayedRequests();
- if (nonPremiumDelay) {
- _nonPremiumDelayedRequests.fire_copy(requestId);
- }
- return true;
- } else if ((code == 401 && type != u"AUTH_KEY_PERM_EMPTY"_q)
- || (badGuestDc && _badGuestDcRequests.find(requestId) == _badGuestDcRequests.cend())) {
- auto dcWithShift = ShiftedDcId(0);
- if (const auto shiftedDcId = queryRequestByDc(requestId)) {
- dcWithShift = *shiftedDcId;
- } else {
- LOG(("MTP Error: unauthorized request without dc info, requestId %1").arg(requestId));
- }
- auto newdc = BareDcId(qAbs(dcWithShift));
- if (!newdc || !hasMainDcId() || newdc == mainDcId()) {
- if (!badGuestDc && _globalFailHandler) {
- _globalFailHandler(error, response); // auth failed in main dc
- }
- return false;
- }
- DEBUG_LOG(("MTP Info: importing auth to dcWithShift %1"
- ).arg(dcWithShift));
- auto &waiters(_authWaiters[newdc]);
- if (!waiters.size()) {
- auto exportRequestId = _instance->send(MTPauth_ExportAuthorization(
- MTP_int(newdc)
- ), [this](const Response &response) {
- auto result = MTPauth_ExportedAuthorization();
- auto from = response.reply.constData();
- if (!result.read(from, from + response.reply.size())) {
- return false;
- }
- exportDone(result, response);
- return true;
- }, [this](const Error &error, const Response &response) {
- return exportFail(error, response);
- });
- _authExportRequests.emplace(exportRequestId, abs(dcWithShift));
- }
- waiters.push_back(requestId);
- if (badGuestDc) _badGuestDcRequests.insert(requestId);
- return true;
- } else if (type == u"CONNECTION_NOT_INITED"_q
- || type == u"CONNECTION_LAYER_INVALID"_q) {
- SerializedRequest request;
- {
- QReadLocker locker(&_requestMapLock);
- auto it = _requestMap.find(requestId);
- if (it == _requestMap.cend()) {
- LOG(("MTP Error: could not find request %1").arg(requestId));
- return false;
- }
- request = it->second;
- }
- auto dcWithShift = ShiftedDcId(0);
- if (const auto shiftedDcId = queryRequestByDc(requestId)) {
- dcWithShift = *shiftedDcId;
- } else {
- LOG(("MTP Error: could not find request %1 for resending with init connection").arg(requestId));
- }
- if (!dcWithShift) return false;
- const auto session = getSession(qAbs(dcWithShift));
- request->needsLayer = true;
- session->setConnectionNotInited();
- session->sendPrepared(request);
- return true;
- } else if (type == u"CONNECTION_LANG_CODE_INVALID"_q) {
- Lang::CurrentCloudManager().resetToDefault();
- }
- if (badGuestDc) _badGuestDcRequests.erase(requestId);
- return false;
- }
- not_null<Session*> Instance::Private::getSession(
- ShiftedDcId shiftedDcId) {
- if (!shiftedDcId) {
- Assert(_mainSession != nullptr);
- return _mainSession;
- } else if (!BareDcId(shiftedDcId)) {
- Assert(_mainSession != nullptr);
- shiftedDcId += BareDcId(_mainSession->getDcWithShift());
- }
- if (const auto session = findSession(shiftedDcId)) {
- return session;
- }
- return startSession(shiftedDcId);
- }
- rpl::lifetime &Instance::Private::lifetime() {
- return _lifetime;
- }
- Session *Instance::Private::findSession(ShiftedDcId shiftedDcId) {
- const auto i = _sessions.find(shiftedDcId);
- return (i != _sessions.end()) ? i->second.get() : nullptr;
- }
- not_null<Session*> Instance::Private::startSession(ShiftedDcId shiftedDcId) {
- Expects(BareDcId(shiftedDcId) != 0);
- const auto dc = getDcById(shiftedDcId);
- const auto thread = getThreadForDc(shiftedDcId);
- const auto result = _sessions.emplace(
- shiftedDcId,
- std::make_unique<Session>(_instance, thread, shiftedDcId, dc)
- ).first->second.get();
- if (isKeysDestroyer()) {
- scheduleKeyDestroy(shiftedDcId);
- }
- return result;
- }
- void Instance::Private::scheduleSessionDestroy(ShiftedDcId shiftedDcId) {
- const auto i = _sessions.find(shiftedDcId);
- if (i == _sessions.cend()) {
- return;
- }
- i->second->kill();
- _sessionsToDestroy.push_back(std::move(i->second));
- _sessions.erase(i);
- InvokeQueued(_instance, [=] {
- _sessionsToDestroy.clear();
- });
- }
- not_null<QThread*> Instance::Private::getThreadForDc(
- ShiftedDcId shiftedDcId) {
- static const auto EnsureStarted = [](
- std::unique_ptr<QThread> &thread,
- auto name) {
- if (!thread) {
- thread = std::make_unique<QThread>();
- thread->setObjectName(name());
- thread->start();
- }
- return thread.get();
- };
- static const auto FindOne = [](
- std::vector<std::unique_ptr<QThread>> &threads,
- const char *prefix,
- int index,
- bool shift) {
- Expects(!threads.empty());
- Expects(!(threads.size() % 2));
- const auto count = int(threads.size());
- index %= count;
- if (index >= count / 2) {
- index = (count - 1) - (index - count / 2);
- }
- if (shift) {
- index = (index + count / 2) % count;
- }
- return EnsureStarted(threads[index], [=] {
- return QString("MTP %1 Session (%2)").arg(prefix).arg(index);
- });
- };
- if (shiftedDcId == BareDcId(shiftedDcId)) {
- return EnsureStarted(_mainSessionThread, [] {
- return QString("MTP Main Session");
- });
- } else if (isDownloadDcId(shiftedDcId)) {
- const auto index = GetDcIdShift(shiftedDcId) - kBaseDownloadDcShift;
- const auto composed = index + BareDcId(shiftedDcId);
- return FindOne(_fileSessionThreads, "Download", composed, false);
- } else if (isUploadDcId(shiftedDcId)) {
- const auto index = GetDcIdShift(shiftedDcId) - kBaseUploadDcShift;
- const auto composed = index + BareDcId(shiftedDcId);
- return FindOne(_fileSessionThreads, "Upload", composed, true);
- }
- return EnsureStarted(_otherSessionsThread, [] {
- return QString("MTP Other Session");
- });
- }
- void Instance::Private::scheduleKeyDestroy(ShiftedDcId shiftedDcId) {
- Expects(isKeysDestroyer());
- if (dcOptions().dcType(shiftedDcId) == DcType::Cdn) {
- performKeyDestroy(shiftedDcId);
- } else {
- _instance->send(MTPauth_LogOut(), [=](const Response &) {
- performKeyDestroy(shiftedDcId);
- return true;
- }, [=](const Error &error, const Response &) {
- if (IsDefaultHandledError(error)) {
- return false;
- }
- performKeyDestroy(shiftedDcId);
- return true;
- }, shiftedDcId);
- }
- }
- void Instance::Private::keyWasPossiblyDestroyed(ShiftedDcId shiftedDcId) {
- Expects(isKeysDestroyer());
- InvokeQueued(_instance, [=] {
- LOG(("MTP Info: checkIfKeyWasDestroyed on destroying key %1, "
- "assuming it is destroyed.").arg(shiftedDcId));
- completedKeyDestroy(shiftedDcId);
- });
- }
- void Instance::Private::performKeyDestroy(ShiftedDcId shiftedDcId) {
- Expects(isKeysDestroyer());
- _instance->send(MTPDestroy_auth_key(), [=](const Response &response) {
- auto result = MTPDestroyAuthKeyRes();
- auto from = response.reply.constData();
- if (!result.read(from, from + response.reply.size())) {
- return false;
- }
- result.match([&](const MTPDdestroy_auth_key_ok &) {
- LOG(("MTP Info: key %1 destroyed.").arg(shiftedDcId));
- }, [&](const MTPDdestroy_auth_key_fail &) {
- LOG(("MTP Error: key %1 destruction fail, leave it for now."
- ).arg(shiftedDcId));
- killSession(shiftedDcId);
- }, [&](const MTPDdestroy_auth_key_none &) {
- LOG(("MTP Info: key %1 already destroyed.").arg(shiftedDcId));
- });
- _instance->keyWasPossiblyDestroyed(shiftedDcId);
- return true;
- }, [=](const Error &error, const Response &response) {
- LOG(("MTP Error: key %1 destruction resulted in error: %2"
- ).arg(shiftedDcId).arg(error.type()));
- _instance->keyWasPossiblyDestroyed(shiftedDcId);
- return true;
- }, shiftedDcId);
- }
- void Instance::Private::completedKeyDestroy(ShiftedDcId shiftedDcId) {
- Expects(isKeysDestroyer());
- removeDc(shiftedDcId);
- _keysForWrite.erase(shiftedDcId);
- killSession(shiftedDcId);
- if (_dcenters.empty()) {
- _allKeysDestroyed.fire({});
- }
- }
- void Instance::Private::keyDestroyedOnServer(
- ShiftedDcId shiftedDcId,
- uint64 keyId) {
- LOG(("Destroying key for dc: %1").arg(shiftedDcId));
- if (const auto dc = findDc(BareDcId(shiftedDcId))) {
- if (dc->destroyConfirmedForgottenKey(keyId)) {
- LOG(("Key destroyed!"));
- dcPersistentKeyChanged(BareDcId(shiftedDcId), nullptr);
- } else {
- LOG(("Key already is different."));
- }
- }
- restart(shiftedDcId);
- }
- void Instance::Private::setUpdatesHandler(
- Fn<void(const Response&)> handler) {
- _updatesHandler = std::move(handler);
- }
- void Instance::Private::setGlobalFailHandler(
- Fn<void(const Error&, const Response&)> handler) {
- _globalFailHandler = std::move(handler);
- }
- void Instance::Private::setStateChangedHandler(
- Fn<void(ShiftedDcId shiftedDcId, int32 state)> handler) {
- _stateChangedHandler = std::move(handler);
- }
- void Instance::Private::setSessionResetHandler(
- Fn<void(ShiftedDcId shiftedDcId)> handler) {
- _sessionResetHandler = std::move(handler);
- }
- void Instance::Private::clearGlobalHandlers() {
- setUpdatesHandler(nullptr);
- setGlobalFailHandler(nullptr);
- setStateChangedHandler(nullptr);
- setSessionResetHandler(nullptr);
- }
- void Instance::Private::prepareToDestroy() {
- // It accesses Instance in destructor, so it should be destroyed first.
- _configLoader.reset();
- requestCancellingDiscard();
- for (const auto &[shiftedDcId, session] : base::take(_sessions)) {
- session->kill();
- }
- _mainSession = nullptr;
- auto threads = std::vector<std::unique_ptr<QThread>>();
- threads.push_back(base::take(_mainSessionThread));
- threads.push_back(base::take(_otherSessionsThread));
- for (auto &thread : base::take(_fileSessionThreads)) {
- threads.push_back(std::move(thread));
- }
- for (const auto &thread : threads) {
- if (thread) {
- thread->quit();
- }
- }
- for (const auto &thread : threads) {
- if (thread) {
- thread->wait();
- }
- }
- }
- Instance::Instance(Mode mode, Fields &&fields)
- : QObject()
- , _private(std::make_unique<Private>(this, mode, std::move(fields))) {
- _private->start();
- }
- void Instance::resolveProxyDomain(const QString &host) {
- _private->resolveProxyDomain(host);
- }
- void Instance::setGoodProxyDomain(const QString &host, const QString &ip) {
- _private->setGoodProxyDomain(host, ip);
- }
- void Instance::suggestMainDcId(DcId mainDcId) {
- _private->suggestMainDcId(mainDcId);
- }
- void Instance::setMainDcId(DcId mainDcId) {
- _private->setMainDcId(mainDcId);
- }
- DcId Instance::mainDcId() const {
- return _private->mainDcId();
- }
- rpl::producer<DcId> Instance::mainDcIdValue() const {
- return _private->mainDcIdValue();
- }
- QString Instance::systemLangCode() const {
- return Lang::GetInstance().systemLangCode();
- }
- QString Instance::cloudLangCode() const {
- return Lang::GetInstance().cloudLangCode(Lang::Pack::Current);
- }
- QString Instance::langPackName() const {
- return Lang::GetInstance().langPackName();
- }
- rpl::producer<> Instance::writeKeysRequests() const {
- return _private->writeKeysRequests();
- }
- rpl::producer<> Instance::allKeysDestroyed() const {
- return _private->allKeysDestroyed();
- }
- void Instance::requestConfig() {
- _private->requestConfig();
- }
- void Instance::setUserPhone(const QString &phone) {
- _private->setUserPhone(phone);
- }
- void Instance::badConfigurationError() {
- _private->badConfigurationError();
- }
- void Instance::syncHttpUnixtime() {
- _private->syncHttpUnixtime();
- }
- void Instance::restartedByTimeout(ShiftedDcId shiftedDcId) {
- _private->restartedByTimeout(shiftedDcId);
- }
- rpl::producer<ShiftedDcId> Instance::restartsByTimeout() const {
- return _private->restartsByTimeout();
- }
- rpl::producer<mtpRequestId> Instance::nonPremiumDelayedRequests() const {
- return _private->nonPremiumDelayedRequests();
- }
- void Instance::requestConfigIfOld() {
- _private->requestConfigIfOld();
- }
- void Instance::requestCDNConfig() {
- _private->requestCDNConfig();
- }
- void Instance::restart() {
- _private->restart();
- }
- void Instance::restart(ShiftedDcId shiftedDcId) {
- _private->restart(shiftedDcId);
- }
- int32 Instance::dcstate(ShiftedDcId shiftedDcId) {
- return _private->dcstate(shiftedDcId);
- }
- QString Instance::dctransport(ShiftedDcId shiftedDcId) {
- return _private->dctransport(shiftedDcId);
- }
- void Instance::ping() {
- _private->ping();
- }
- void Instance::cancel(mtpRequestId requestId) {
- _private->cancel(requestId);
- }
- int32 Instance::state(mtpRequestId requestId) { // < 0 means waiting for such count of ms
- return _private->state(requestId);
- }
- void Instance::killSession(ShiftedDcId shiftedDcId) {
- _private->killSession(shiftedDcId);
- }
- void Instance::stopSession(ShiftedDcId shiftedDcId) {
- _private->stopSession(shiftedDcId);
- }
- void Instance::reInitConnection(DcId dcId) {
- _private->reInitConnection(dcId);
- }
- void Instance::logout(Fn<void()> done) {
- _private->logout(std::move(done));
- }
- void Instance::dcPersistentKeyChanged(
- DcId dcId,
- const AuthKeyPtr &persistentKey) {
- _private->dcPersistentKeyChanged(dcId, persistentKey);
- }
- void Instance::dcTemporaryKeyChanged(DcId dcId) {
- _private->dcTemporaryKeyChanged(dcId);
- }
- rpl::producer<DcId> Instance::dcTemporaryKeyChanged() const {
- return _private->dcTemporaryKeyChanged();
- }
- AuthKeysList Instance::getKeysForWrite() const {
- return _private->getKeysForWrite();
- }
- void Instance::addKeysForDestroy(AuthKeysList &&keys) {
- _private->addKeysForDestroy(std::move(keys));
- }
- Config &Instance::config() const {
- return _private->config();
- }
- const ConfigFields &Instance::configValues() const {
- return _private->configValues();
- }
- DcOptions &Instance::dcOptions() const {
- return _private->dcOptions();
- }
- Environment Instance::environment() const {
- return _private->environment();
- }
- bool Instance::isTestMode() const {
- return _private->isTestMode();
- }
- QString Instance::deviceModel() const {
- return _private->deviceModel();
- }
- QString Instance::systemVersion() const {
- return _private->systemVersion();
- }
- void Instance::setUpdatesHandler(Fn<void(const Response&)> handler) {
- _private->setUpdatesHandler(std::move(handler));
- }
- void Instance::setGlobalFailHandler(
- Fn<void(const Error&, const Response&)> handler) {
- _private->setGlobalFailHandler(std::move(handler));
- }
- void Instance::setStateChangedHandler(
- Fn<void(ShiftedDcId shiftedDcId, int32 state)> handler) {
- _private->setStateChangedHandler(std::move(handler));
- }
- void Instance::setSessionResetHandler(
- Fn<void(ShiftedDcId shiftedDcId)> handler) {
- _private->setSessionResetHandler(std::move(handler));
- }
- void Instance::clearGlobalHandlers() {
- _private->clearGlobalHandlers();
- }
- void Instance::onStateChange(ShiftedDcId shiftedDcId, int32 state) {
- _private->onStateChange(shiftedDcId, state);
- }
- void Instance::onSessionReset(ShiftedDcId shiftedDcId) {
- _private->onSessionReset(shiftedDcId);
- }
- bool Instance::hasCallback(mtpRequestId requestId) const {
- return _private->hasCallback(requestId);
- }
- void Instance::processCallback(const Response &response) {
- _private->processCallback(response);
- }
- void Instance::processUpdate(const Response &message) {
- _private->processUpdate(message);
- }
- bool Instance::rpcErrorOccured(
- const Response &response,
- const FailHandler &onFail,
- const Error &error) {
- return _private->rpcErrorOccured(response, onFail, error);
- }
- bool Instance::isKeysDestroyer() const {
- return _private->isKeysDestroyer();
- }
- void Instance::keyWasPossiblyDestroyed(ShiftedDcId shiftedDcId) {
- _private->keyWasPossiblyDestroyed(shiftedDcId);
- }
- void Instance::keyDestroyedOnServer(ShiftedDcId shiftedDcId, uint64 keyId) {
- _private->keyDestroyedOnServer(shiftedDcId, keyId);
- }
- void Instance::sendRequest(
- mtpRequestId requestId,
- SerializedRequest &&request,
- ResponseHandler &&callbacks,
- ShiftedDcId shiftedDcId,
- crl::time msCanWait,
- bool needsLayer,
- mtpRequestId afterRequestId) {
- return _private->sendRequest(
- requestId,
- std::move(request),
- std::move(callbacks),
- shiftedDcId,
- msCanWait,
- needsLayer,
- afterRequestId);
- }
- void Instance::sendAnything(ShiftedDcId shiftedDcId, crl::time msCanWait) {
- _private->getSession(shiftedDcId)->sendAnything(msCanWait);
- }
- rpl::lifetime &Instance::lifetime() {
- return _private->lifetime();
- }
- Instance::~Instance() {
- _private->prepareToDestroy();
- }
- } // namespace MTP
|