591 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			591 lines
		
	
	
	
		
			14 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 "mtproto/session.h"
 | 
						|
 | 
						|
#include "mtproto/details/mtproto_dcenter.h"
 | 
						|
#include "mtproto/session_private.h"
 | 
						|
#include "mtproto/mtproto_auth_key.h"
 | 
						|
#include "core/application.h"
 | 
						|
#include "core/core_settings.h"
 | 
						|
#include "base/unixtime.h"
 | 
						|
 | 
						|
namespace MTP {
 | 
						|
namespace details {
 | 
						|
 | 
						|
SessionOptions::SessionOptions(
 | 
						|
	const QString &systemLangCode,
 | 
						|
	const QString &cloudLangCode,
 | 
						|
	const QString &langPackName,
 | 
						|
	const ProxyData &proxy,
 | 
						|
	bool useIPv4,
 | 
						|
	bool useIPv6,
 | 
						|
	bool useHttp,
 | 
						|
	bool useTcp)
 | 
						|
: systemLangCode(systemLangCode)
 | 
						|
, cloudLangCode(cloudLangCode)
 | 
						|
, langPackName(langPackName)
 | 
						|
, proxy(proxy)
 | 
						|
, useIPv4(useIPv4)
 | 
						|
, useIPv6(useIPv6)
 | 
						|
, useHttp(useHttp)
 | 
						|
, useTcp(useTcp) {
 | 
						|
}
 | 
						|
 | 
						|
template <typename Callback>
 | 
						|
void SessionData::withSession(Callback &&callback) {
 | 
						|
	QMutexLocker lock(&_ownerMutex);
 | 
						|
	if (const auto session = _owner) {
 | 
						|
		InvokeQueued(session, [
 | 
						|
			session,
 | 
						|
			callback = std::forward<Callback>(callback)
 | 
						|
		] {
 | 
						|
			callback(session);
 | 
						|
		});
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void SessionData::notifyConnectionInited(const SessionOptions &options) {
 | 
						|
	// #TODO race
 | 
						|
	const auto current = this->options();
 | 
						|
	if (current.cloudLangCode == _options.cloudLangCode
 | 
						|
		&& current.systemLangCode == _options.systemLangCode
 | 
						|
		&& current.langPackName == _options.langPackName
 | 
						|
		&& current.proxy == _options.proxy) {
 | 
						|
		QMutexLocker lock(&_ownerMutex);
 | 
						|
		if (_owner) {
 | 
						|
			_owner->notifyDcConnectionInited();
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void SessionData::queueTryToReceive() {
 | 
						|
	withSession([](not_null<Session*> session) {
 | 
						|
		session->tryToReceive();
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
void SessionData::queueNeedToResumeAndSend() {
 | 
						|
	withSession([](not_null<Session*> session) {
 | 
						|
		session->needToResumeAndSend();
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
void SessionData::queueConnectionStateChange(int newState) {
 | 
						|
	withSession([=](not_null<Session*> session) {
 | 
						|
		session->connectionStateChange(newState);
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
void SessionData::queueResetDone() {
 | 
						|
	withSession([](not_null<Session*> session) {
 | 
						|
		session->resetDone();
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
void SessionData::queueSendAnything(crl::time msCanWait) {
 | 
						|
	withSession([=](not_null<Session*> session) {
 | 
						|
		session->sendAnything(msCanWait);
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
bool SessionData::connectionInited() const {
 | 
						|
	QMutexLocker lock(&_ownerMutex);
 | 
						|
	return _owner ? _owner->connectionInited() : false;
 | 
						|
}
 | 
						|
 | 
						|
AuthKeyPtr SessionData::getTemporaryKey(TemporaryKeyType type) const {
 | 
						|
	QMutexLocker lock(&_ownerMutex);
 | 
						|
	return _owner ? _owner->getTemporaryKey(type) : nullptr;
 | 
						|
}
 | 
						|
 | 
						|
AuthKeyPtr SessionData::getPersistentKey() const {
 | 
						|
	QMutexLocker lock(&_ownerMutex);
 | 
						|
	return _owner ? _owner->getPersistentKey() : nullptr;
 | 
						|
}
 | 
						|
 | 
						|
CreatingKeyType SessionData::acquireKeyCreation(DcType type) {
 | 
						|
	QMutexLocker lock(&_ownerMutex);
 | 
						|
	return _owner ? _owner->acquireKeyCreation(type) : CreatingKeyType::None;
 | 
						|
}
 | 
						|
 | 
						|
bool SessionData::releaseKeyCreationOnDone(
 | 
						|
		const AuthKeyPtr &temporaryKey,
 | 
						|
		const AuthKeyPtr &persistentKeyUsedForBind) {
 | 
						|
	QMutexLocker lock(&_ownerMutex);
 | 
						|
	return _owner
 | 
						|
		? _owner->releaseKeyCreationOnDone(
 | 
						|
			temporaryKey,
 | 
						|
			persistentKeyUsedForBind)
 | 
						|
		: false;
 | 
						|
}
 | 
						|
 | 
						|
bool SessionData::releaseCdnKeyCreationOnDone(
 | 
						|
		const AuthKeyPtr &temporaryKey) {
 | 
						|
	QMutexLocker lock(&_ownerMutex);
 | 
						|
	return _owner
 | 
						|
		? _owner->releaseCdnKeyCreationOnDone(temporaryKey)
 | 
						|
		: false;
 | 
						|
}
 | 
						|
 | 
						|
void SessionData::releaseKeyCreationOnFail() {
 | 
						|
	QMutexLocker lock(&_ownerMutex);
 | 
						|
	if (_owner) {
 | 
						|
		_owner->releaseKeyCreationOnFail();
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void SessionData::destroyTemporaryKey(uint64 keyId) {
 | 
						|
	QMutexLocker lock(&_ownerMutex);
 | 
						|
	if (_owner) {
 | 
						|
		_owner->destroyTemporaryKey(keyId);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void SessionData::detach() {
 | 
						|
	QMutexLocker lock(&_ownerMutex);
 | 
						|
	_owner = nullptr;
 | 
						|
}
 | 
						|
 | 
						|
Session::Session(
 | 
						|
	not_null<Instance*> instance,
 | 
						|
	not_null<QThread*> thread,
 | 
						|
	ShiftedDcId shiftedDcId,
 | 
						|
	not_null<Dcenter*> dc)
 | 
						|
: _instance(instance)
 | 
						|
, _shiftedDcId(shiftedDcId)
 | 
						|
, _dc(dc)
 | 
						|
, _data(std::make_shared<SessionData>(this))
 | 
						|
, _thread(thread)
 | 
						|
, _sender([=] { needToResumeAndSend(); }) {
 | 
						|
	refreshOptions();
 | 
						|
	watchDcKeyChanges();
 | 
						|
	watchDcOptionsChanges();
 | 
						|
	start();
 | 
						|
}
 | 
						|
 | 
						|
Session::~Session() {
 | 
						|
	Expects(!_private);
 | 
						|
 | 
						|
	if (_myKeyCreation != CreatingKeyType::None) {
 | 
						|
		releaseKeyCreationOnFail();
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void Session::watchDcKeyChanges() {
 | 
						|
	_instance->dcTemporaryKeyChanged(
 | 
						|
	) | rpl::filter([=](DcId dcId) {
 | 
						|
		return (dcId == _shiftedDcId) || (dcId == BareDcId(_shiftedDcId));
 | 
						|
	}) | rpl::start_with_next([=] {
 | 
						|
		DEBUG_LOG(("AuthKey Info: dcTemporaryKeyChanged in Session %1"
 | 
						|
			).arg(_shiftedDcId));
 | 
						|
		if (const auto captured = _private) {
 | 
						|
			InvokeQueued(captured, [=] {
 | 
						|
				DEBUG_LOG(("AuthKey Info: calling Connection::updateAuthKey in Session %1"
 | 
						|
					).arg(_shiftedDcId));
 | 
						|
				captured->updateAuthKey();
 | 
						|
			});
 | 
						|
		}
 | 
						|
	}, _lifetime);
 | 
						|
}
 | 
						|
 | 
						|
void Session::watchDcOptionsChanges() {
 | 
						|
	_instance->dcOptions().changed(
 | 
						|
	) | rpl::filter([=](DcId dcId) {
 | 
						|
		return (BareDcId(_shiftedDcId) == dcId) && (_private != nullptr);
 | 
						|
	}) | rpl::start_with_next([=] {
 | 
						|
		InvokeQueued(_private, [captured = _private] {
 | 
						|
			captured->dcOptionsChanged();
 | 
						|
		});
 | 
						|
	}, _lifetime);
 | 
						|
 | 
						|
	_instance->dcOptions().cdnConfigChanged(
 | 
						|
	) | rpl::filter([=] {
 | 
						|
		return (_private != nullptr)
 | 
						|
			&& (_instance->dcOptions().dcType(_shiftedDcId) == DcType::Cdn);
 | 
						|
	}) | rpl::start_with_next([=] {
 | 
						|
		InvokeQueued(_private, [captured = _private] {
 | 
						|
			captured->cdnConfigChanged();
 | 
						|
		});
 | 
						|
	}, _lifetime);
 | 
						|
}
 | 
						|
 | 
						|
void Session::start() {
 | 
						|
	killConnection();
 | 
						|
	_private = new SessionPrivate(
 | 
						|
		_instance,
 | 
						|
		_thread.get(),
 | 
						|
		_data,
 | 
						|
		_shiftedDcId);
 | 
						|
}
 | 
						|
 | 
						|
void Session::restart() {
 | 
						|
	if (_killed) {
 | 
						|
		DEBUG_LOG(("Session Error: can't restart a killed session"));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	refreshOptions();
 | 
						|
	if (const auto captured = _private) {
 | 
						|
		InvokeQueued(captured, [=] {
 | 
						|
			captured->restartNow();
 | 
						|
		});
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void Session::refreshOptions() {
 | 
						|
	auto &settings = Core::App().settings().proxy();
 | 
						|
	const auto &proxy = settings.selected();
 | 
						|
	const auto isEnabled = settings.isEnabled();
 | 
						|
	const auto proxyType = (isEnabled ? proxy.type : ProxyData::Type::None);
 | 
						|
	const auto useTcp = (proxyType != ProxyData::Type::Http);
 | 
						|
	const auto useHttp = (proxyType != ProxyData::Type::Mtproto);
 | 
						|
	const auto useIPv4 = true;
 | 
						|
	const auto useIPv6 = settings.tryIPv6();
 | 
						|
	_data->setOptions(SessionOptions(
 | 
						|
		_instance->systemLangCode(),
 | 
						|
		_instance->cloudLangCode(),
 | 
						|
		_instance->langPackName(),
 | 
						|
		(isEnabled ? proxy : ProxyData()),
 | 
						|
		useIPv4,
 | 
						|
		useIPv6,
 | 
						|
		useHttp,
 | 
						|
		useTcp));
 | 
						|
}
 | 
						|
 | 
						|
void Session::reInitConnection() {
 | 
						|
	setConnectionNotInited();
 | 
						|
	restart();
 | 
						|
}
 | 
						|
 | 
						|
void Session::setConnectionNotInited() {
 | 
						|
	_dc->setConnectionInited(false);
 | 
						|
}
 | 
						|
 | 
						|
void Session::stop() {
 | 
						|
	if (_killed) {
 | 
						|
		DEBUG_LOG(("Session Error: can't stop a killed session"));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	DEBUG_LOG(("Session Info: stopping session dcWithShift %1").arg(_shiftedDcId));
 | 
						|
	killConnection();
 | 
						|
}
 | 
						|
 | 
						|
void Session::kill() {
 | 
						|
	stop();
 | 
						|
	_killed = true;
 | 
						|
	_data->detach();
 | 
						|
	DEBUG_LOG(("Session Info: marked session dcWithShift %1 as killed").arg(_shiftedDcId));
 | 
						|
}
 | 
						|
 | 
						|
void Session::unpaused() {
 | 
						|
	if (_needToReceive) {
 | 
						|
		_needToReceive = false;
 | 
						|
		InvokeQueued(this, [=] {
 | 
						|
			tryToReceive();
 | 
						|
		});
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void Session::sendAnything(crl::time msCanWait) {
 | 
						|
	if (_killed) {
 | 
						|
		DEBUG_LOG(("Session Error: can't send anything in a killed session"));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	const auto ms = crl::now();
 | 
						|
	if (_msSendCall) {
 | 
						|
		if (ms > _msSendCall + _msWait) {
 | 
						|
			_msWait = 0;
 | 
						|
		} else {
 | 
						|
			_msWait = (_msSendCall + _msWait) - ms;
 | 
						|
			if (_msWait > msCanWait) {
 | 
						|
				_msWait = msCanWait;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		_msWait = msCanWait;
 | 
						|
	}
 | 
						|
	if (_msWait) {
 | 
						|
		DEBUG_LOG(("MTP Info: dcWithShift %1 can wait for %2ms from current %3").arg(_shiftedDcId).arg(_msWait).arg(_msSendCall));
 | 
						|
		_msSendCall = ms;
 | 
						|
		_sender.callOnce(_msWait);
 | 
						|
	} else {
 | 
						|
		DEBUG_LOG(("MTP Info: dcWithShift %1 stopped send timer, can wait for %2ms from current %3").arg(_shiftedDcId).arg(_msWait).arg(_msSendCall));
 | 
						|
		_sender.cancel();
 | 
						|
		_msSendCall = 0;
 | 
						|
		needToResumeAndSend();
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void Session::needToResumeAndSend() {
 | 
						|
	if (_killed) {
 | 
						|
		DEBUG_LOG(("Session Info: can't resume a killed session"));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (!_private) {
 | 
						|
		DEBUG_LOG(("Session Info: resuming session dcWithShift %1").arg(_shiftedDcId));
 | 
						|
		start();
 | 
						|
	}
 | 
						|
	const auto captured = _private;
 | 
						|
	const auto ping = base::take(_ping);
 | 
						|
	InvokeQueued(captured, [=] {
 | 
						|
		if (ping) {
 | 
						|
			captured->sendPingForce();
 | 
						|
		} else {
 | 
						|
			captured->tryToSend();
 | 
						|
		}
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
void Session::connectionStateChange(int newState) {
 | 
						|
	_instance->onStateChange(_shiftedDcId, newState);
 | 
						|
}
 | 
						|
 | 
						|
void Session::resetDone() {
 | 
						|
	_instance->onSessionReset(_shiftedDcId);
 | 
						|
}
 | 
						|
 | 
						|
void Session::cancel(mtpRequestId requestId, mtpMsgId msgId) {
 | 
						|
	if (requestId) {
 | 
						|
		QWriteLocker locker(_data->toSendMutex());
 | 
						|
		_data->toSendMap().remove(requestId);
 | 
						|
	}
 | 
						|
	if (msgId) {
 | 
						|
		QWriteLocker locker(_data->haveSentMutex());
 | 
						|
		_data->haveSentMap().remove(msgId);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void Session::ping() {
 | 
						|
	_ping = true;
 | 
						|
	sendAnything();
 | 
						|
}
 | 
						|
 | 
						|
int32 Session::requestState(mtpRequestId requestId) const {
 | 
						|
	int32 result = MTP::RequestSent;
 | 
						|
 | 
						|
	bool connected = false;
 | 
						|
	if (_private) {
 | 
						|
		const auto s = _private->getState();
 | 
						|
		if (s == ConnectedState) {
 | 
						|
			connected = true;
 | 
						|
		} else if (s == ConnectingState || s == DisconnectedState) {
 | 
						|
			if (result < 0 || result == MTP::RequestSent) {
 | 
						|
				result = MTP::RequestConnecting;
 | 
						|
			}
 | 
						|
		} else if (s < 0) {
 | 
						|
			if ((result < 0 && s > result) || result == MTP::RequestSent) {
 | 
						|
				result = s;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (!connected) {
 | 
						|
		return result;
 | 
						|
	} else if (!requestId) {
 | 
						|
		return MTP::RequestSent;
 | 
						|
	}
 | 
						|
 | 
						|
	QWriteLocker locker(_data->toSendMutex());
 | 
						|
	return _data->toSendMap().contains(requestId)
 | 
						|
		? MTP::RequestSending
 | 
						|
		: MTP::RequestSent;
 | 
						|
}
 | 
						|
 | 
						|
int32 Session::getState() const {
 | 
						|
	int32 result = -86400000;
 | 
						|
 | 
						|
	if (_private) {
 | 
						|
		const auto s = _private->getState();
 | 
						|
		if (s == ConnectedState) {
 | 
						|
			return s;
 | 
						|
		} else if (s == ConnectingState || s == DisconnectedState) {
 | 
						|
			if (result < 0) {
 | 
						|
				return s;
 | 
						|
			}
 | 
						|
		} else if (s < 0) {
 | 
						|
			if (result < 0 && s > result) {
 | 
						|
				result = s;
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if (result == -86400000) {
 | 
						|
		result = DisconnectedState;
 | 
						|
	}
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
QString Session::transport() const {
 | 
						|
	return _private ? _private->transport() : QString();
 | 
						|
}
 | 
						|
 | 
						|
void Session::sendPrepared(
 | 
						|
		const SerializedRequest &request,
 | 
						|
		crl::time msCanWait) {
 | 
						|
	DEBUG_LOG(("MTP Info: adding request to toSendMap, msCanWait %1"
 | 
						|
		).arg(msCanWait));
 | 
						|
	{
 | 
						|
		QWriteLocker locker(_data->toSendMutex());
 | 
						|
		_data->toSendMap().emplace(request->requestId, request);
 | 
						|
		*(mtpMsgId*)(request->data() + 4) = 0;
 | 
						|
		*(request->data() + 6) = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	DEBUG_LOG(("MTP Info: added, requestId %1").arg(request->requestId));
 | 
						|
	if (msCanWait >= 0) {
 | 
						|
		InvokeQueued(this, [=] {
 | 
						|
			sendAnything(msCanWait);
 | 
						|
		});
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
CreatingKeyType Session::acquireKeyCreation(DcType type) {
 | 
						|
	Expects(_myKeyCreation == CreatingKeyType::None);
 | 
						|
 | 
						|
	_myKeyCreation = _dc->acquireKeyCreation(type);
 | 
						|
	return _myKeyCreation;
 | 
						|
}
 | 
						|
 | 
						|
bool Session::releaseKeyCreationOnDone(
 | 
						|
		const AuthKeyPtr &temporaryKey,
 | 
						|
		const AuthKeyPtr &persistentKeyUsedForBind) {
 | 
						|
	Expects(_myKeyCreation != CreatingKeyType::None);
 | 
						|
	Expects(persistentKeyUsedForBind != nullptr);
 | 
						|
 | 
						|
	return releaseGenericKeyCreationOnDone(
 | 
						|
		temporaryKey,
 | 
						|
		persistentKeyUsedForBind);
 | 
						|
}
 | 
						|
 | 
						|
bool Session::releaseCdnKeyCreationOnDone(
 | 
						|
		const AuthKeyPtr &temporaryKey) {
 | 
						|
	Expects(_myKeyCreation == CreatingKeyType::TemporaryRegular);
 | 
						|
 | 
						|
	return releaseGenericKeyCreationOnDone(temporaryKey, nullptr);
 | 
						|
}
 | 
						|
 | 
						|
bool Session::releaseGenericKeyCreationOnDone(
 | 
						|
		const AuthKeyPtr &temporaryKey,
 | 
						|
		const AuthKeyPtr &persistentKeyUsedForBind) {
 | 
						|
	const auto wasKeyCreation = std::exchange(
 | 
						|
		_myKeyCreation,
 | 
						|
		CreatingKeyType::None);
 | 
						|
	const auto result = _dc->releaseKeyCreationOnDone(
 | 
						|
		wasKeyCreation,
 | 
						|
		temporaryKey,
 | 
						|
		persistentKeyUsedForBind);
 | 
						|
 | 
						|
	if (!result) {
 | 
						|
		DEBUG_LOG(("AuthKey Info: Persistent key changed "
 | 
						|
			"while binding temporary, dcWithShift %1"
 | 
						|
			).arg(_shiftedDcId));
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	DEBUG_LOG(("AuthKey Info: Session key bound, setting, dcWithShift %1"
 | 
						|
		).arg(_shiftedDcId));
 | 
						|
 | 
						|
	const auto dcId = _dc->id();
 | 
						|
	const auto instance = _instance;
 | 
						|
	InvokeQueued(instance, [=] {
 | 
						|
		if (wasKeyCreation == CreatingKeyType::Persistent) {
 | 
						|
			instance->dcPersistentKeyChanged(dcId, persistentKeyUsedForBind);
 | 
						|
		} else {
 | 
						|
			instance->dcTemporaryKeyChanged(dcId);
 | 
						|
		}
 | 
						|
	});
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
void Session::releaseKeyCreationOnFail() {
 | 
						|
	Expects(_myKeyCreation != CreatingKeyType::None);
 | 
						|
 | 
						|
	const auto wasKeyCreation = std::exchange(
 | 
						|
		_myKeyCreation,
 | 
						|
		CreatingKeyType::None);
 | 
						|
	_dc->releaseKeyCreationOnFail(wasKeyCreation);
 | 
						|
}
 | 
						|
 | 
						|
void Session::notifyDcConnectionInited() {
 | 
						|
	DEBUG_LOG(("MTP Info: MTProtoDC::connectionWasInited(), dcWithShift %1"
 | 
						|
		).arg(_shiftedDcId));
 | 
						|
	_dc->setConnectionInited();
 | 
						|
}
 | 
						|
 | 
						|
void Session::destroyTemporaryKey(uint64 keyId) {
 | 
						|
	if (!_dc->destroyTemporaryKey(keyId)) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	const auto dcId = _dc->id();
 | 
						|
	const auto instance = _instance;
 | 
						|
	InvokeQueued(instance, [=] {
 | 
						|
		instance->dcTemporaryKeyChanged(dcId);
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
int32 Session::getDcWithShift() const {
 | 
						|
	return _shiftedDcId;
 | 
						|
}
 | 
						|
 | 
						|
AuthKeyPtr Session::getTemporaryKey(TemporaryKeyType type) const {
 | 
						|
	return _dc->getTemporaryKey(type);
 | 
						|
}
 | 
						|
 | 
						|
AuthKeyPtr Session::getPersistentKey() const {
 | 
						|
	return _dc->getPersistentKey();
 | 
						|
}
 | 
						|
 | 
						|
bool Session::connectionInited() const {
 | 
						|
	return _dc->connectionInited();
 | 
						|
}
 | 
						|
 | 
						|
void Session::tryToReceive() {
 | 
						|
	if (_killed) {
 | 
						|
		DEBUG_LOG(("Session Error: can't receive in a killed session"));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (paused()) {
 | 
						|
		_needToReceive = true;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	while (true) {
 | 
						|
		auto lock = QWriteLocker(_data->haveReceivedMutex());
 | 
						|
		const auto messages = base::take(_data->haveReceivedMessages());
 | 
						|
		lock.unlock();
 | 
						|
		if (messages.empty()) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		const auto guard = QPointer<Session>(this);
 | 
						|
		const auto instance = QPointer<Instance>(_instance);
 | 
						|
		const auto main = (_shiftedDcId == BareDcId(_shiftedDcId));
 | 
						|
		for (const auto &message : messages) {
 | 
						|
			if (message.requestId) {
 | 
						|
				instance->processCallback(message);
 | 
						|
			} else if (main) {
 | 
						|
				// Process updates only in main session.
 | 
						|
				instance->processUpdate(message);
 | 
						|
			}
 | 
						|
			if (!instance) {
 | 
						|
				return;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if (!guard) {
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
void Session::killConnection() {
 | 
						|
	if (!_private) {
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	base::take(_private)->deleteLater();
 | 
						|
 | 
						|
	Ensures(_private == nullptr);
 | 
						|
}
 | 
						|
 | 
						|
} // namespace details
 | 
						|
} // namespace MTP
 |