/* 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 */ #pragma once #include "mtproto/details/mtproto_received_ids_manager.h" #include "mtproto/details/mtproto_serialized_request.h" #include "mtproto/mtproto_auth_key.h" #include "mtproto/mtproto_dc_options.h" #include "mtproto/connection_abstract.h" #include "mtproto/facade.h" #include "base/openssl_help.h" #include "base/timer.h" namespace MTP { namespace details { class BoundKeyCreator; } // namespace details class Instance; namespace details { class AbstractConnection; class SessionData; class RSAPublicKey; struct SessionOptions; class SessionPrivate final : public QObject { public: SessionPrivate( not_null instance, not_null thread, std::shared_ptr data, ShiftedDcId shiftedDcId); ~SessionPrivate(); [[nodiscard]] int32 getShiftedDcId() const; void dcOptionsChanged(); void cdnConfigChanged(); [[nodiscard]] int32 getState() const; [[nodiscard]] QString transport() const; void updateAuthKey(); void restartNow(); void sendPingForce(); void tryToSend(); private: static constexpr auto kUpdateStateAlways = 666; struct TestConnection { ConnectionPointer data; int priority = 0; }; struct SentContainer { crl::time sent = 0; std::vector messages; }; enum class HandleResult { Success, Ignored, RestartConnection, ResetSession, DestroyTemporaryKey, ParseError, }; void connectToServer(bool afterConfig = false); void connectingTimedOut(); void doDisconnect(); void restart(); void requestCDNConfig(); void handleError(int errorCode); void onError( not_null connection, qint32 errorCode); void onConnected(not_null connection); void onDisconnected(not_null connection); void onSentSome(uint64 size); void onReceivedSome(); void handleReceived(); void retryByTimer(); void waitConnectedFailed(); void waitReceivedFailed(); void waitBetterFailed(); void markConnectionOld(); void sendPingByTimer(); void destroyAllConnections(); void confirmBestConnection(); void removeTestConnection(not_null connection); [[nodiscard]] int16 getProtocolDcId() const; void checkSentRequests(); void clearOldContainers(); mtpMsgId placeToContainer( SerializedRequest &toSendRequest, mtpMsgId &bigMsgId, bool forceNewMsgId, SerializedRequest &req); mtpMsgId prepareToSend( SerializedRequest &request, mtpMsgId currentLastId, bool forceNewMsgId); mtpMsgId replaceMsgId( SerializedRequest &request, mtpMsgId newId); bool sendSecureRequest( SerializedRequest &&request, bool needAnyResponse); mtpRequestId wasSent(mtpMsgId msgId) const; struct OuterInfo { mtpMsgId outerMsgId = 0; uint64 serverSalt = 0; int32 serverTime = 0; bool badTime = false; }; [[nodiscard]] HandleResult handleOneReceived( const mtpPrime *from, const mtpPrime *end, uint64 msgId, OuterInfo info); [[nodiscard]] HandleResult handleBindResponse( mtpMsgId requestMsgId, const mtpBuffer &response); mtpBuffer ungzip(const mtpPrime *from, const mtpPrime *end) const; void handleMsgsStates(const QVector &ids, const QByteArray &states); // _sessionDataMutex must be locked for read. bool setState(int state, int ifState = kUpdateStateAlways); void appendTestConnection( DcOptions::Variants::Protocol protocol, const QString &ip, int port, const bytes::vector &protocolSecret); // if badTime received - search for ids in sessionData->haveSent and sessionData->wereAcked and sync time/salt, return true if found bool requestsFixTimeSalt(const QVector &ids, const OuterInfo &info); // if we had a confirmed fast request use its unixtime as a correct one. void correctUnixtimeByFastRequest( const QVector &ids, TimeId serverTime); void correctUnixtimeWithBadLocal(TimeId serverTime); // remove msgs with such ids from sessionData->haveSent, add to sessionData->wereAcked void requestsAcked(const QVector &ids, bool byResponse = false); void resend( mtpMsgId msgId, crl::time msCanWait = 0, bool forceContainer = false); void resendAll(); void clearSpecialMsgId(mtpMsgId msgId); [[nodiscard]] DcType tryAcquireKeyCreation(); void resetSession(); void checkAuthKey(); void authKeyChecked(); void destroyTemporaryKey(); void clearUnboundKeyCreator(); void releaseKeyCreationOnFail(); void applyAuthKey(AuthKeyPtr &&encryptionKey); [[nodiscard]] bool noMediaKeyWithExistingRegularKey() const; bool destroyOldEnoughPersistentKey(); void setCurrentKeyId(uint64 newKeyId); void changeSessionId(); [[nodiscard]] bool markSessionAsStarted(); [[nodiscard]] uint32 nextRequestSeqNumber(bool needAck); [[nodiscard]] bool realDcTypeChanged(); [[nodiscard]] MTPVector prepareInitParams(); const not_null _instance; const ShiftedDcId _shiftedDcId = 0; DcType _realDcType = DcType(); DcType _currentDcType = DcType(); mutable QReadWriteLock _stateMutex; int _state = DisconnectedState; bool _needSessionReset = false; ConnectionPointer _connection; std::vector _testConnections; crl::time _startedConnectingAt = 0; base::Timer _retryTimer; // exp retry timer int _retryTimeout = 1; qint64 _retryWillFinish = 0; base::Timer _oldConnectionTimer; bool _oldConnection = true; base::Timer _waitForConnectedTimer; base::Timer _waitForReceivedTimer; base::Timer _waitForBetterTimer; crl::time _waitForReceived = 0; crl::time _waitForConnected = 0; crl::time _firstSentAt = -1; mtpPingId _pingId = 0; mtpPingId _pingIdToSend = 0; crl::time _pingSendAt = 0; mtpMsgId _pingMsgId = 0; base::Timer _pingSender; base::Timer _checkSentRequestsTimer; std::shared_ptr _sessionData; std::unique_ptr _options; AuthKeyPtr _encryptionKey; uint64 _keyId = 0; uint64 _sessionId = 0; uint64 _sessionSalt = 0; uint32 _messagesCounter = 0; bool _sessionMarkedAsStarted = false; QVector _ackRequestData; QVector _resendRequestData; base::flat_set _stateRequestData; ReceivedIdsManager _receivedMessageIds; base::flat_map _resendingIds; base::flat_map _ackedIds; base::flat_map _stateAndResendRequests; base::flat_map _sentContainers; std::unique_ptr _keyCreator; mtpMsgId _bindMsgId = 0; crl::time _bindMessageSent = 0; }; } // namespace details } // namespace MTP