mirror of
https://github.com/vale981/tdesktop
synced 2025-03-09 12:36:39 -04:00

If we start logging in and we know, that some of the authorization keys were read from the hard drive, not generated, we destroy all the existing authorization keys and start generating new keys.
371 lines
9.3 KiB
C++
371 lines
9.3 KiB
C++
/*
|
|
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 "mtproto/dcenter.h"
|
|
#include "core/single_timer.h"
|
|
|
|
namespace MTP {
|
|
|
|
class Instance;
|
|
|
|
namespace internal {
|
|
|
|
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<mtpMsgId, bool> _idsNeedAck;
|
|
|
|
};
|
|
|
|
class Session;
|
|
class SessionData {
|
|
public:
|
|
SessionData(Session *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;
|
|
}
|
|
|
|
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) {
|
|
if (_authKey != key) {
|
|
uint64 session = rand_value<uint64>();
|
|
_authKey = key;
|
|
|
|
DEBUG_LOG(("MTP Info: new auth key set in SessionData, id %1, setting random server_session %2").arg(key ? key->keyId() : 0).arg(session));
|
|
QWriteLocker locker(&lock);
|
|
if (_session != session) {
|
|
_session = session;
|
|
_messagesSent = 0;
|
|
}
|
|
_layerInited = false;
|
|
}
|
|
}
|
|
|
|
bool isCheckedKey() const {
|
|
QReadLocker locker(&lock);
|
|
return _keyChecked;
|
|
}
|
|
void setCheckedKey(bool checked) {
|
|
QWriteLocker locker(&lock);
|
|
_keyChecked = checked;
|
|
}
|
|
|
|
QReadWriteLock *keyMutex() const;
|
|
|
|
QReadWriteLock *toSendMutex() const {
|
|
return &toSendLock;
|
|
}
|
|
QReadWriteLock *haveSentMutex() const {
|
|
return &haveSentLock;
|
|
}
|
|
QReadWriteLock *toResendMutex() const {
|
|
return &toResendLock;
|
|
}
|
|
QReadWriteLock *wereAckedMutex() const {
|
|
return &wereAckedLock;
|
|
}
|
|
QReadWriteLock *receivedIdsMutex() const {
|
|
return &receivedIdsLock;
|
|
}
|
|
QReadWriteLock *haveReceivedMutex() const {
|
|
return &haveReceivedLock;
|
|
}
|
|
QReadWriteLock *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;
|
|
}
|
|
mtpResponseMap &haveReceivedMap() {
|
|
return haveReceived;
|
|
}
|
|
const mtpResponseMap &haveReceivedMap() const {
|
|
return haveReceived;
|
|
}
|
|
mtpMsgIdsSet &stateRequestMap() {
|
|
return stateRequest;
|
|
}
|
|
const mtpMsgIdsSet &stateRequestMap() const {
|
|
return stateRequest;
|
|
}
|
|
|
|
mtpRequestId nextFakeRequestId() { // must be locked by haveReceivedMutex()
|
|
if (haveReceived.isEmpty() || haveReceived.cbegin().key() > 0) {
|
|
_fakeRequestId = -2000000000;
|
|
} else {
|
|
++_fakeRequestId;
|
|
}
|
|
return _fakeRequestId;
|
|
}
|
|
|
|
Session *owner() {
|
|
return _owner;
|
|
}
|
|
const Session *owner() const {
|
|
return _owner;
|
|
}
|
|
|
|
uint32 nextRequestSeqNumber(bool needAck = true) {
|
|
QWriteLocker locker(&lock);
|
|
uint32 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;
|
|
mtpRequestId _fakeRequestId = -2000000000;
|
|
|
|
Session *_owner = nullptr;
|
|
|
|
AuthKeyPtr _authKey;
|
|
bool _keyChecked = false;
|
|
bool _layerInited = false;
|
|
|
|
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
|
|
mtpResponseMap haveReceived; // map of request_id -> response, that should be processed in other thread
|
|
mtpMsgIdsSet stateRequest; // set of msg_id's, whose state should be requested
|
|
|
|
// 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(Instance *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 <typename TRequest>
|
|
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<quint64> 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);
|
|
|
|
Instance *_instance;
|
|
std::unique_ptr<Connection> _connection;
|
|
|
|
bool _killed = false;
|
|
bool _needToReceive = false;
|
|
|
|
SessionData data;
|
|
|
|
ShiftedDcId dcWithShift = 0;
|
|
DcenterPtr dc;
|
|
|
|
TimeMs msSendCall = 0;
|
|
TimeMs msWait = 0;
|
|
|
|
bool _ping = false;
|
|
|
|
QTimer timeouter;
|
|
SingleTimer sender;
|
|
|
|
};
|
|
|
|
inline QReadWriteLock *SessionData::keyMutex() const {
|
|
return _owner->keyMutex();
|
|
}
|
|
|
|
MTPrpcError rpcClientError(const QString &type, const QString &description = QString());
|
|
|
|
} // namespace internal
|
|
} // namespace MTP
|