/* This file is part of Telegram Desktop, the official desktop version of Telegram messaging app, see https://telegram.org Telegram Desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. It is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. In addition, as a special exception, the copyright holders give permission to link the code of portions of this program with the OpenSSL library. Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once #include "core/single_timer.h" #include "mtproto/rpc_sender.h" namespace MTP { class Instance; class AuthKey; using AuthKeyPtr = std::shared_ptr; namespace internal { class Dcenter; class Connection; class ReceivedMsgIds { public: bool registerMsgId(mtpMsgId msgId, bool needAck) { auto i = _idsNeedAck.constFind(msgId); if (i == _idsNeedAck.cend()) { if (_idsNeedAck.size() < MTPIdsBufferSize || msgId > min()) { _idsNeedAck.insert(msgId, needAck); return true; } MTP_LOG(-1, ("No need to handle - %1 < min = %2").arg(msgId).arg(min())); } else { MTP_LOG(-1, ("No need to handle - %1 already is in map").arg(msgId)); } return false; } mtpMsgId min() const { return _idsNeedAck.isEmpty() ? 0 : _idsNeedAck.cbegin().key(); } mtpMsgId max() const { auto end = _idsNeedAck.cend(); return _idsNeedAck.isEmpty() ? 0 : (--end).key(); } void shrink() { auto size = _idsNeedAck.size(); while (size-- > MTPIdsBufferSize) { _idsNeedAck.erase(_idsNeedAck.begin()); } } enum class State { NotFound, NeedsAck, NoAckNeeded, }; State lookup(mtpMsgId msgId) const { auto i = _idsNeedAck.constFind(msgId); if (i == _idsNeedAck.cend()) { return State::NotFound; } return i.value() ? State::NeedsAck : State::NoAckNeeded; } void clear() { _idsNeedAck.clear(); } private: QMap _idsNeedAck; }; using SerializedMessage = mtpBuffer; inline bool ResponseNeedsAck(const SerializedMessage &response) { if (response.size() < 8) { return false; } auto seqNo = *(uint32*)(response.constData() + 6); return (seqNo & 0x01) ? true : false; } class Session; class SessionData { public: SessionData(not_null creator) : _owner(creator) { } void setSession(uint64 session) { DEBUG_LOG(("MTP Info: setting server_session: %1").arg(session)); QWriteLocker locker(&_lock); if (_session != session) { _session = session; _messagesSent = 0; } } uint64 getSession() const { QReadLocker locker(&_lock); return _session; } bool layerWasInited() const { QReadLocker locker(&_lock); return _layerInited; } void setLayerWasInited(bool was) { QWriteLocker locker(&_lock); _layerInited = was; } QString systemLangCode() const { QReadLocker locker(&_lock); return _systemLangCode; } void setSystemLangCode(const QString &code) { QWriteLocker locker(&_lock); _systemLangCode = code; } QString cloudLangCode() const { QReadLocker locker(&_lock); return _cloudLangCode; } void setCloudLangCode(const QString &code) { QWriteLocker locker(&_lock); _cloudLangCode = code; } void setSalt(uint64 salt) { QWriteLocker locker(&_lock); _salt = salt; } uint64 getSalt() const { QReadLocker locker(&_lock); return _salt; } const AuthKeyPtr &getKey() const { return _authKey; } void setKey(const AuthKeyPtr &key); bool isCheckedKey() const { QReadLocker locker(&_lock); return _keyChecked; } void setCheckedKey(bool checked) { QWriteLocker locker(&_lock); _keyChecked = checked; } not_null keyMutex() const; not_null toSendMutex() const { return &_toSendLock; } not_null haveSentMutex() const { return &_haveSentLock; } not_null toResendMutex() const { return &_toResendLock; } not_null wereAckedMutex() const { return &_wereAckedLock; } not_null receivedIdsMutex() const { return &_receivedIdsLock; } not_null haveReceivedMutex() const { return &_haveReceivedLock; } not_null stateRequestMutex() const { return &_stateRequestLock; } mtpPreRequestMap &toSendMap() { return _toSend; } const mtpPreRequestMap &toSendMap() const { return _toSend; } mtpRequestMap &haveSentMap() { return _haveSent; } const mtpRequestMap &haveSentMap() const { return _haveSent; } mtpRequestIdsMap &toResendMap() { // msgId -> requestId, on which toSend: requestId -> request for resended requests return _toResend; } const mtpRequestIdsMap &toResendMap() const { return _toResend; } ReceivedMsgIds &receivedIdsSet() { return _receivedIds; } const ReceivedMsgIds &receivedIdsSet() const { return _receivedIds; } mtpRequestIdsMap &wereAckedMap() { return _wereAcked; } const mtpRequestIdsMap &wereAckedMap() const { return _wereAcked; } QMap &haveReceivedResponses() { return _receivedResponses; } const QMap &haveReceivedResponses() const { return _receivedResponses; } QList &haveReceivedUpdates() { return _receivedUpdates; } const QList &haveReceivedUpdates() const { return _receivedUpdates; } mtpMsgIdsSet &stateRequestMap() { return _stateRequest; } const mtpMsgIdsSet &stateRequestMap() const { return _stateRequest; } not_null owner() { return _owner; } not_null owner() const { return _owner; } uint32 nextRequestSeqNumber(bool needAck = true) { QWriteLocker locker(&_lock); auto result = _messagesSent; _messagesSent += (needAck ? 1 : 0); return result * 2 + (needAck ? 1 : 0); } void clear(Instance *instance); private: uint64 _session = 0; uint64 _salt = 0; uint32 _messagesSent = 0; not_null _owner; AuthKeyPtr _authKey; bool _keyChecked = false; bool _layerInited = false; QString _systemLangCode; QString _cloudLangCode; mtpPreRequestMap _toSend; // map of request_id -> request, that is waiting to be sent mtpRequestMap _haveSent; // map of msg_id -> request, that was sent, msDate = 0 for msgs_state_req (no resend / state req), msDate = 0, seqNo = 0 for containers mtpRequestIdsMap _toResend; // map of msg_id -> request_id, that request_id -> request lies in toSend and is waiting to be resent ReceivedMsgIds _receivedIds; // set of received msg_id's, for checking new msg_ids mtpRequestIdsMap _wereAcked; // map of msg_id -> request_id, this msg_ids already were acked or do not need ack mtpMsgIdsSet _stateRequest; // set of msg_id's, whose state should be requested QMap _receivedResponses; // map of request_id -> response that should be processed in the main thread QList _receivedUpdates; // list of updates that should be processed in the main thread // mutexes mutable QReadWriteLock _lock; mutable QReadWriteLock _toSendLock; mutable QReadWriteLock _haveSentLock; mutable QReadWriteLock _toResendLock; mutable QReadWriteLock _receivedIdsLock; mutable QReadWriteLock _wereAckedLock; mutable QReadWriteLock _haveReceivedLock; mutable QReadWriteLock _stateRequestLock; }; class Session : public QObject { Q_OBJECT public: Session(not_null instance, ShiftedDcId shiftedDcId); void start(); void restart(); void stop(); void kill(); void unpaused(); ShiftedDcId getDcWithShift() const; QReadWriteLock *keyMutex() const; void notifyKeyCreated(AuthKeyPtr &&key); void destroyKey(); void notifyLayerInited(bool wasInited); template mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), TimeMs msCanWait = 0, bool needsLayer = false, bool toMainDC = false, mtpRequestId after = 0); // send mtp request void ping(); void cancel(mtpRequestId requestId, mtpMsgId msgId); int32 requestState(mtpRequestId requestId) const; int32 getState() const; QString transport() const; void sendPrepared(const mtpRequest &request, TimeMs msCanWait = 0, bool newRequest = true); // nulls msgId and seqNo in request, if newRequest = true ~Session(); signals: void authKeyCreated(); void needToSend(); void needToPing(); void needToRestart(); public slots: void needToResumeAndSend(); mtpRequestId resend(quint64 msgId, qint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false); void resendMany(QVector msgIds, qint64 msCanWait, bool forceContainer, bool sendMsgStateInfo); void resendAll(); // after connection restart void authKeyCreatedForDC(); void layerWasInitedForDC(bool wasInited); void tryToReceive(); void checkRequestsByTimer(); void onConnectionStateChange(qint32 newState); void onResetDone(); void sendAnything(qint64 msCanWait = 0); void sendPong(quint64 msgId, quint64 pingId); void sendMsgsStateInfo(quint64 msgId, QByteArray data); private: void createDcData(); void registerRequest(mtpRequestId requestId, ShiftedDcId dcWithShift); mtpRequestId storeRequest(mtpRequest &request, const RPCResponseHandler &parser); mtpRequest getRequest(mtpRequestId requestId); bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err); not_null _instance; std::unique_ptr _connection; bool _killed = false; bool _needToReceive = false; SessionData data; ShiftedDcId dcWithShift = 0; std::shared_ptr dc; TimeMs msSendCall = 0; TimeMs msWait = 0; bool _ping = false; QTimer timeouter; SingleTimer sender; }; inline not_null SessionData::keyMutex() const { return _owner->keyMutex(); } MTPrpcError rpcClientError(const QString &type, const QString &description = QString()); } // namespace internal } // namespace MTP