From 7c99f947eb6f05f8dc318edd1f0f27e9c988fcc3 Mon Sep 17 00:00:00 2001 From: John Preston Date: Thu, 24 Mar 2016 15:57:10 +0300 Subject: [PATCH] added connection implementations to git, protocol improved --- Telegram/SourceFiles/audio.cpp | 13 +- Telegram/SourceFiles/autoupdater.cpp | 6 + Telegram/SourceFiles/gui/animation.cpp | 7 + Telegram/SourceFiles/localstorage.cpp | 28 +- Telegram/SourceFiles/mtproto/auth_key.cpp | 22 +- Telegram/SourceFiles/mtproto/auth_key.h | 48 ++- Telegram/SourceFiles/mtproto/connection.cpp | 37 +- .../mtproto/connection_abstract.cpp | 87 ++++ .../SourceFiles/mtproto/connection_abstract.h | 92 ++++ .../SourceFiles/mtproto/connection_auto.cpp | 344 +++++++++++++++ .../SourceFiles/mtproto/connection_auto.h | 94 +++++ .../SourceFiles/mtproto/connection_http.cpp | 218 ++++++++++ .../SourceFiles/mtproto/connection_http.h | 76 ++++ .../SourceFiles/mtproto/connection_tcp.cpp | 395 ++++++++++++++++++ Telegram/SourceFiles/mtproto/connection_tcp.h | 119 ++++++ Telegram/SourceFiles/mtproto/core_types.cpp | 2 + Telegram/SourceFiles/mtproto/dcenter.cpp | 99 ++--- Telegram/SourceFiles/mtproto/dcenter.h | 51 ++- Telegram/SourceFiles/mtproto/facade.cpp | 40 +- Telegram/SourceFiles/mtproto/facade.h | 4 +- Telegram/SourceFiles/mtproto/session.cpp | 14 +- Telegram/SourceFiles/mtproto/session.h | 10 +- Telegram/SourceFiles/pspecific_linux.cpp | 8 +- Telegram/SourceFiles/pspecific_win.h | 2 + Telegram/SourceFiles/stdafx.h | 38 +- Telegram/SourceFiles/types.cpp | 5 + Telegram/SourceFiles/window.cpp | 13 +- 27 files changed, 1663 insertions(+), 209 deletions(-) create mode 100644 Telegram/SourceFiles/mtproto/connection_abstract.cpp create mode 100644 Telegram/SourceFiles/mtproto/connection_abstract.h create mode 100644 Telegram/SourceFiles/mtproto/connection_auto.cpp create mode 100644 Telegram/SourceFiles/mtproto/connection_auto.h create mode 100644 Telegram/SourceFiles/mtproto/connection_http.cpp create mode 100644 Telegram/SourceFiles/mtproto/connection_http.h create mode 100644 Telegram/SourceFiles/mtproto/connection_tcp.cpp create mode 100644 Telegram/SourceFiles/mtproto/connection_tcp.h diff --git a/Telegram/SourceFiles/audio.cpp b/Telegram/SourceFiles/audio.cpp index 25f11dc58..139c617d6 100644 --- a/Telegram/SourceFiles/audio.cpp +++ b/Telegram/SourceFiles/audio.cpp @@ -19,6 +19,7 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" + #include "audio.h" #include @@ -27,10 +28,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #define AL_ALEXT_PROTOTYPES #include -#ifdef Q_OS_MAC - extern "C" { +#include +#include +#include +#include +#ifdef Q_OS_MAC #include #undef iconv_open @@ -46,10 +50,9 @@ size_t iconv (iconv_t cd, char* * inbuf, size_t *inbytesleft, char* * outbuf, s int iconv_close (iconv_t cd) { return libiconv_close(cd); } +#endif // Q_OS_MAC -} - -#endif +} // extern "C" namespace { ALCdevice *audioDevice = 0; diff --git a/Telegram/SourceFiles/autoupdater.cpp b/Telegram/SourceFiles/autoupdater.cpp index 0734becdf..d82574cb9 100644 --- a/Telegram/SourceFiles/autoupdater.cpp +++ b/Telegram/SourceFiles/autoupdater.cpp @@ -28,6 +28,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include #include +#ifdef Q_OS_WIN // use Lzma SDK for win +#include +#else // Q_OS_WIN +#include +#endif // else of Q_OS_WIN + #include "application.h" #include "pspecific.h" diff --git a/Telegram/SourceFiles/gui/animation.cpp b/Telegram/SourceFiles/gui/animation.cpp index 6628f6ef2..047a3346b 100644 --- a/Telegram/SourceFiles/gui/animation.cpp +++ b/Telegram/SourceFiles/gui/animation.cpp @@ -22,6 +22,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "animation.h" +extern "C" { +#include +#include +#include +#include +} + #include "mainwidget.h" #include "window.h" diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index b75e86761..359b3b521 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -160,8 +160,8 @@ namespace { QByteArray _settingsSalt, _passKeySalt, _passKeyEncrypted; - mtpAuthKey _oldKey, _settingsKey, _passKey, _localKey; - void createLocalKey(const QByteArray &pass, QByteArray *salt, mtpAuthKey *result) { + MTP::AuthKey _oldKey, _settingsKey, _passKey, _localKey; + void createLocalKey(const QByteArray &pass, QByteArray *salt, MTP::AuthKey *result) { uchar key[LocalEncryptKeySize] = { 0 }; int32 iterCount = pass.size() ? LocalEncryptIterCount : LocalEncryptNoPwdIterCount; // dont slow down for no password QByteArray newSalt; @@ -282,7 +282,7 @@ namespace { return true; } - static QByteArray prepareEncrypted(EncryptedDescriptor &data, const mtpAuthKey &key = _localKey) { + static QByteArray prepareEncrypted(EncryptedDescriptor &data, const MTP::AuthKey &key = _localKey) { data.finish(); QByteArray &toEncrypt(data.data); @@ -296,11 +296,11 @@ namespace { *(uint32*)toEncrypt.data() = size; QByteArray encrypted(0x10 + fullSize, Qt::Uninitialized); // 128bit of sha1 - key128, sizeof(data), data hashSha1(toEncrypt.constData(), toEncrypt.size(), encrypted.data()); - aesEncryptLocal(toEncrypt.constData(), encrypted.data() + 0x10, fullSize, &key, encrypted.constData()); + MTP::aesEncryptLocal(toEncrypt.constData(), encrypted.data() + 0x10, fullSize, &key, encrypted.constData()); return encrypted; } - bool writeEncrypted(EncryptedDescriptor &data, const mtpAuthKey &key = _localKey) { + bool writeEncrypted(EncryptedDescriptor &data, const MTP::AuthKey &key = _localKey) { return writeData(prepareEncrypted(data, key)); } void finish() { @@ -468,7 +468,7 @@ namespace { return false; } - bool decryptLocal(EncryptedDescriptor &result, const QByteArray &encrypted, const mtpAuthKey &key = _localKey) { + bool decryptLocal(EncryptedDescriptor &result, const QByteArray &encrypted, const MTP::AuthKey &key = _localKey) { if (encrypted.size() <= 16 || (encrypted.size() & 0x0F)) { LOG(("App Error: bad encrypted part size: %1").arg(encrypted.size())); return false; @@ -504,7 +504,7 @@ namespace { return true; } - bool readEncryptedFile(FileReadDescriptor &result, const QString &name, int options = UserPath | SafePath, const mtpAuthKey &key = _localKey) { + bool readEncryptedFile(FileReadDescriptor &result, const QString &name, int options = UserPath | SafePath, const MTP::AuthKey &key = _localKey) { if (!readFile(result, name, options)) { return false; } @@ -534,7 +534,7 @@ namespace { return true; } - bool readEncryptedFile(FileReadDescriptor &result, const FileKey &fkey, int options = UserPath | SafePath, const mtpAuthKey &key = _localKey) { + bool readEncryptedFile(FileReadDescriptor &result, const FileKey &fkey, int options = UserPath | SafePath, const MTP::AuthKey &key = _localKey) { return readEncryptedFile(result, toFilePart(fkey), options, key); } @@ -932,7 +932,7 @@ namespace { DEBUG_LOG(("MTP Info: key found, dc %1, key: %2").arg(dcId).arg(Logs::mb(key, 256).str())); dcId = MTP::bareDcId(dcId); - mtpAuthKeyPtr keyPtr(new mtpAuthKey()); + MTP::AuthKeyPtr keyPtr(new MTP::AuthKey()); keyPtr->setKey(key); keyPtr->setDC(dcId); @@ -1668,16 +1668,16 @@ namespace { return; } - mtpKeysMap keys = MTP::getKeys(); + MTP::AuthKeysMap keys = MTP::getKeys(); quint32 size = sizeof(quint32) + sizeof(qint32) + sizeof(quint32); size += keys.size() * (sizeof(quint32) + sizeof(quint32) + 256); EncryptedDescriptor data(size); data.stream << quint32(dbiUser) << qint32(MTP::authedId()) << quint32(MTP::maindc()); - for (mtpKeysMap::const_iterator i = keys.cbegin(), e = keys.cend(); i != e; ++i) { - data.stream << quint32(dbiKey) << quint32((*i)->getDC()); - (*i)->write(data.stream); + for_const (const MTP::AuthKeyPtr &key, keys) { + data.stream << quint32(dbiKey) << quint32(key->getDC()); + key->write(data.stream); } mtp.writeEncrypted(data, _localKey); @@ -2291,7 +2291,7 @@ namespace Local { } bool checkPasscode(const QByteArray &passcode) { - mtpAuthKey tmp; + MTP::AuthKey tmp; createLocalKey(passcode, &_passKeySalt, &tmp); return (tmp == _passKey); } diff --git a/Telegram/SourceFiles/mtproto/auth_key.cpp b/Telegram/SourceFiles/mtproto/auth_key.cpp index 29f297f6b..cd0f3497b 100644 --- a/Telegram/SourceFiles/mtproto/auth_key.cpp +++ b/Telegram/SourceFiles/mtproto/auth_key.cpp @@ -24,22 +24,36 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include -void aesEncrypt(const void *src, void *dst, uint32 len, void *key, void *iv) { +namespace MTP { + +void aesIgeEncrypt(const void *src, void *dst, uint32 len, const void *key, const void *iv) { uchar aes_key[32], aes_iv[32]; memcpy(aes_key, key, 32); memcpy(aes_iv, iv, 32); AES_KEY aes; AES_set_encrypt_key(aes_key, 256, &aes); - AES_ige_encrypt((const uchar*)src, (uchar*)dst, len, &aes, aes_iv, AES_ENCRYPT); + AES_ige_encrypt(static_cast(src), static_cast(dst), len, &aes, aes_iv, AES_ENCRYPT); } -void aesDecrypt(const void *src, void *dst, uint32 len, void *key, void *iv) { +void aesIgeDecrypt(const void *src, void *dst, uint32 len, const void *key, const void *iv) { uchar aes_key[32], aes_iv[32]; memcpy(aes_key, key, 32); memcpy(aes_iv, iv, 32); AES_KEY aes; AES_set_decrypt_key(aes_key, 256, &aes); - AES_ige_encrypt((const uchar*)src, (uchar*)dst, len, &aes, aes_iv, AES_DECRYPT); + AES_ige_encrypt(static_cast(src), static_cast(dst), len, &aes, aes_iv, AES_DECRYPT); } + +void aesCtrEncrypt(void *data, uint32 len, const void *key, CTRState *state) { + AES_KEY aes; + AES_set_encrypt_key(static_cast(key), 256, &aes); + + static_assert(CTRState::IvecSize == AES_BLOCK_SIZE, "Wrong size of ctr ivec!"); + static_assert(CTRState::EcountSize == AES_BLOCK_SIZE, "Wrong size of ctr ecount!"); + + AES_ctr128_encrypt(static_cast(data), static_cast(data), len, &aes, state->ivec, state->ecount, &state->num); +} + +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/auth_key.h b/Telegram/SourceFiles/mtproto/auth_key.h index 9bd994461..d2c720c7e 100644 --- a/Telegram/SourceFiles/mtproto/auth_key.h +++ b/Telegram/SourceFiles/mtproto/auth_key.h @@ -20,10 +20,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -class mtpAuthKey { +namespace MTP { + +class AuthKey { public: - mtpAuthKey() : _isset(false), _dc(0) { + AuthKey() : _isset(false), _dc(0) { } bool created() const { @@ -94,7 +96,7 @@ public: static const uint64 RecreateKeyId = 0xFFFFFFFFFFFFFFFFL; - friend bool operator==(const mtpAuthKey &a, const mtpAuthKey &b); + friend bool operator==(const AuthKey &a, const AuthKey &b); private: @@ -105,40 +107,54 @@ private: }; -inline bool operator==(const mtpAuthKey &a, const mtpAuthKey &b) { +inline bool operator==(const AuthKey &a, const AuthKey &b) { return !memcmp(a._key, b._key, 256); } -typedef QSharedPointer mtpAuthKeyPtr; -typedef QVector mtpKeysMap; +typedef QSharedPointer AuthKeyPtr; +typedef QVector AuthKeysMap; -void aesEncrypt(const void *src, void *dst, uint32 len, void *key, void *iv); -void aesDecrypt(const void *src, void *dst, uint32 len, void *key, void *iv); +void aesIgeEncrypt(const void *src, void *dst, uint32 len, const void *key, const void *iv); +void aesIgeDecrypt(const void *src, void *dst, uint32 len, const void *key, const void *iv); -inline void aesEncrypt(const void *src, void *dst, uint32 len, const mtpAuthKeyPtr &authKey, const MTPint128 &msgKey) { +inline void aesIgeEncrypt(const void *src, void *dst, uint32 len, const AuthKeyPtr &authKey, const MTPint128 &msgKey) { MTPint256 aesKey, aesIV; authKey->prepareAES(msgKey, aesKey, aesIV); - return aesEncrypt(src, dst, len, &aesKey, &aesIV); + return aesIgeEncrypt(src, dst, len, static_cast(&aesKey), static_cast(&aesIV)); } -inline void aesEncryptLocal(const void *src, void *dst, uint32 len, const mtpAuthKey *authKey, const void *key128) { +inline void aesEncryptLocal(const void *src, void *dst, uint32 len, const AuthKey *authKey, const void *key128) { MTPint256 aesKey, aesIV; authKey->prepareAES(*(const MTPint128*)key128, aesKey, aesIV, false); - return aesEncrypt(src, dst, len, &aesKey, &aesIV); + return aesIgeEncrypt(src, dst, len, static_cast(&aesKey), static_cast(&aesIV)); } -inline void aesDecrypt(const void *src, void *dst, uint32 len, const mtpAuthKeyPtr &authKey, const MTPint128 &msgKey) { +inline void aesIgeDecrypt(const void *src, void *dst, uint32 len, const AuthKeyPtr &authKey, const MTPint128 &msgKey) { MTPint256 aesKey, aesIV; authKey->prepareAES(msgKey, aesKey, aesIV, false); - return aesDecrypt(src, dst, len, &aesKey, &aesIV); + return aesIgeDecrypt(src, dst, len, static_cast(&aesKey), static_cast(&aesIV)); } -inline void aesDecryptLocal(const void *src, void *dst, uint32 len, const mtpAuthKey *authKey, const void *key128) { +inline void aesDecryptLocal(const void *src, void *dst, uint32 len, const AuthKey *authKey, const void *key128) { MTPint256 aesKey, aesIV; authKey->prepareAES(*(const MTPint128*)key128, aesKey, aesIV, false); - return aesDecrypt(src, dst, len, &aesKey, &aesIV); + return aesIgeDecrypt(src, dst, len, static_cast(&aesKey), static_cast(&aesIV)); } + +// ctr used inplace, encrypt the data and leave it at the same place +struct CTRState { + static constexpr int KeySize = 32; + static constexpr int IvecSize = 16; + static constexpr int EcountSize = 16; + + uchar ivec[IvecSize] = { 0 }; + uint32 num = 0; + uchar ecount[EcountSize] = { 0 }; +}; +void aesCtrEncrypt(void *data, uint32 len, const void *key, CTRState *state); + +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp index c04dcaba3..1a84f8e28 100644 --- a/Telegram/SourceFiles/mtproto/connection.cpp +++ b/Telegram/SourceFiles/mtproto/connection.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include #include #include +#include "zlib.h" #include "mtproto/rsa_public_key.h" @@ -438,7 +439,7 @@ ConnectionPrivate::ConnectionPrivate(QThread *thread, Connection *owner, Session moveToThread(thread); if (!dc) { - QReadLocker lock(mtpDcOptionsMutex()); + QReadLocker lock(dcOptionsMutex()); const MTP::DcOptions &options(Global::DcOptions()); if (options.isEmpty()) { LOG(("MTP Error: connect failed, no DCs")); @@ -1042,7 +1043,7 @@ void ConnectionPrivate::retryByTimer() { } else if (retryTimeout < 64000) { retryTimeout *= 2; } - if (keyId == mtpAuthKey::RecreateKeyId) { + if (keyId == AuthKey::RecreateKeyId) { if (sessionData->getKey()) { unlockKey(); @@ -1083,7 +1084,7 @@ void ConnectionPrivate::socketStart(bool afterConfig) { string ip[2][2]; uint32 port[2][2] = { { 0 } }; { - QReadLocker lock(mtpDcOptionsMutex()); + QReadLocker lock(dcOptionsMutex()); const MTP::DcOptions &options(Global::DcOptions()); int32 shifts[2][2][4] = { { // IPv4 @@ -1138,8 +1139,8 @@ void ConnectionPrivate::socketStart(bool afterConfig) { } if (noIPv4) DEBUG_LOG(("MTP Info: DC %1 options for IPv4 over HTTP not found, waiting for config").arg(dc)); if (cTryIPv6() && noIPv6) DEBUG_LOG(("MTP Info: DC %1 options for IPv6 over HTTP not found, waiting for config").arg(dc)); - connect(mtpConfigLoader(), SIGNAL(loaded()), this, SLOT(onConfigLoaded())); - mtpConfigLoader()->load(); + connect(configLoader(), SIGNAL(loaded()), this, SLOT(onConfigLoaded())); + configLoader()->load(); return; } @@ -1180,12 +1181,12 @@ void ConnectionPrivate::restart(bool mayBeBadKey) { _waitForReceivedTimer.stop(); _waitForConnectedTimer.stop(); - mtpAuthKeyPtr key(sessionData->getKey()); + AuthKeyPtr key(sessionData->getKey()); if (key) { if (!sessionData->isCheckedKey()) { if (mayBeBadKey) { clearMessages(); - keyId = mtpAuthKey::RecreateKeyId; + keyId = AuthKey::RecreateKeyId; // retryTimeout = 1; // no ddos please LOG(("MTP Info: key may be bad and was not checked - but won't be destroyed, no log outs because of bad server right now..")); } @@ -1351,7 +1352,7 @@ void ConnectionPrivate::handleReceived() { return restart(); } - mtpAuthKeyPtr key(sessionData->getKey()); + AuthKeyPtr key(sessionData->getKey()); if (!key || key->keyId() != keyId) { DEBUG_LOG(("MTP Error: auth_key id for dc %1 changed").arg(dc)); @@ -1383,7 +1384,7 @@ void ConnectionPrivate::handleReceived() { const mtpPrime *from(msg), *end; MTPint128 msgKey(*(MTPint128*)(encrypted + 2)); - aesDecrypt(encrypted + 6, data, dataBuffer.size(), key, msgKey); + aesIgeDecrypt(encrypted + 6, data, dataBuffer.size(), key, msgKey); uint64 serverSalt = *(uint64*)&data[0], session = *(uint64*)&data[2], msgId = *(uint64*)&data[4]; uint32 seqNo = *(uint32*)&data[6], msgLen = *(uint32*)&data[7]; @@ -2351,7 +2352,7 @@ void ConnectionPrivate::updateAuthKey() { keyId = newKeyId; return; // some other connection is getting key } - const mtpAuthKeyPtr &key(sessionData->getKey()); + const AuthKeyPtr &key(sessionData->getKey()); newKeyId = key ? key->keyId() : 0; } if (keyId != newKeyId) { @@ -2366,7 +2367,7 @@ void ConnectionPrivate::updateAuthKey() { DEBUG_LOG(("AuthKey Info: No key in updateAuthKey(), will be creating auth_key")); lockKey(); - const mtpAuthKeyPtr &key(sessionData->getKey()); + const AuthKeyPtr &key(sessionData->getKey()); if (key) { if (keyId != key->keyId()) clearMessages(); keyId = key->keyId(); @@ -2390,7 +2391,7 @@ void ConnectionPrivate::updateAuthKey() { } void ConnectionPrivate::clearMessages() { - if (keyId && keyId != mtpAuthKey::RecreateKeyId && _conn) { + if (keyId && keyId != AuthKey::RecreateKeyId && _conn) { _conn->received().clear(); } } @@ -2543,7 +2544,7 @@ void ConnectionPrivate::dhParamsAnswered() { memcpy(authKeyData->aesIV + 8, sha1nn, 20); memcpy(authKeyData->aesIV + 28, &authKeyData->new_nonce, 4); - aesDecrypt(&encDHStr[0], &decBuffer[0], encDHLen, authKeyData->aesKey, authKeyData->aesIV); + aesIgeDecrypt(&encDHStr[0], &decBuffer[0], encDHLen, authKeyData->aesKey, authKeyData->aesIV); const mtpPrime *from(&decBuffer[5]), *to(from), *end(from + (encDHBufLen - 5)); MTPServer_DH_inner_data dh_inner(to, end); @@ -2667,7 +2668,7 @@ void ConnectionPrivate::dhClientParamsSend() { sdhEncString.resize(encFullSize * 4); - aesEncrypt(&encBuffer[0], &sdhEncString[0], encFullSize * sizeof(mtpPrime), authKeyData->aesKey, authKeyData->aesIV); + aesIgeEncrypt(&encBuffer[0], &sdhEncString[0], encFullSize * sizeof(mtpPrime), authKeyData->aesKey, authKeyData->aesIV); connect(_conn, SIGNAL(receivedData()), this, SLOT(dhClientParamsAnswered())); @@ -2718,7 +2719,7 @@ void ConnectionPrivate::dhClientParamsAnswered() { uint64 salt1 = authKeyData->new_nonce.l.l, salt2 = authKeyData->server_nonce.l, serverSalt = salt1 ^ salt2; sessionData->setSalt(serverSalt); - mtpAuthKeyPtr authKey(new mtpAuthKey()); + AuthKeyPtr authKey(new AuthKey()); authKey->setKey(authKeyData->auth_key); authKey->setDC(bareDcId(dc)); @@ -2946,7 +2947,7 @@ bool ConnectionPrivate::sendRequest(mtpRequest &request, bool needAnyResponse, Q return false; } - mtpAuthKeyPtr key(sessionData->getKey()); + AuthKeyPtr key(sessionData->getKey()); if (!key || key->keyId() != keyId) { DEBUG_LOG(("MTP Error: auth_key id for dc %1 changed").arg(dc)); @@ -2973,7 +2974,7 @@ bool ConnectionPrivate::sendRequest(mtpRequest &request, bool needAnyResponse, Q *((uint64*)&result[2]) = keyId; *((MTPint128*)&result[4]) = msgKey; - aesEncrypt(request->constData(), &result[8], fullSize * sizeof(mtpPrime), key, msgKey); + aesIgeEncrypt(request->constData(), &result[8], fullSize * sizeof(mtpPrime), key, msgKey); DEBUG_LOG(("MTP Info: sending request, size: %1, num: %2, time: %3").arg(fullSize + 6).arg((*request)[4]).arg((*request)[5])); @@ -3031,7 +3032,7 @@ void ConnectionPrivate::stop() { QWriteLocker lockFinished(&sessionDataMutex); if (sessionData) { if (myKeyLock) { - sessionData->owner()->notifyKeyCreated(mtpAuthKeyPtr()); // release key lock, let someone else create it + sessionData->owner()->notifyKeyCreated(AuthKeyPtr()); // release key lock, let someone else create it sessionData->keyMutex()->unlock(); myKeyLock = false; } diff --git a/Telegram/SourceFiles/mtproto/connection_abstract.cpp b/Telegram/SourceFiles/mtproto/connection_abstract.cpp new file mode 100644 index 000000000..6d5f3c433 --- /dev/null +++ b/Telegram/SourceFiles/mtproto/connection_abstract.cpp @@ -0,0 +1,87 @@ +/* +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-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" + +#include "mtproto/connection_abstract.h" + +#include "mtproto/connection_tcp.h" +#include "mtproto/connection_http.h" +#include "mtproto/connection_auto.h" + +namespace MTP { +namespace internal { + +mtpBuffer AbstractConnection::preparePQFake(const MTPint128 &nonce) { + MTPReq_pq req_pq(nonce); + mtpBuffer buffer; + uint32 requestSize = req_pq.innerLength() >> 2; + + buffer.resize(0); + buffer.reserve(8 + requestSize); + buffer.push_back(0); // tcp packet len + buffer.push_back(0); // tcp packet num + buffer.push_back(0); + buffer.push_back(0); + buffer.push_back(0); + buffer.push_back(unixtime()); + buffer.push_back(requestSize * 4); + req_pq.write(buffer); + buffer.push_back(0); // tcp crc32 hash + + return buffer; +} + +MTPResPQ AbstractConnection::readPQFakeReply(const mtpBuffer &buffer) { + const mtpPrime *answer(buffer.constData()); + uint32 len = buffer.size(); + if (len < 5) { + LOG(("Fake PQ Error: bad request answer, len = %1").arg(len * sizeof(mtpPrime))); + DEBUG_LOG(("Fake PQ Error: answer bytes %1").arg(Logs::mb(answer, len * sizeof(mtpPrime)).str())); + throw Exception("bad pq reply"); + } + if (answer[0] != 0 || answer[1] != 0 || (((uint32)answer[2]) & 0x03) != 1/* || (unixtime() - answer[3] > 300) || (answer[3] - unixtime() > 60)*/) { // didnt sync time yet + LOG(("Fake PQ Error: bad request answer start (%1 %2 %3)").arg(answer[0]).arg(answer[1]).arg(answer[2])); + DEBUG_LOG(("Fake PQ Error: answer bytes %1").arg(Logs::mb(answer, len * sizeof(mtpPrime)).str())); + throw Exception("bad pq reply"); + } + uint32 answerLen = (uint32)answer[4]; + if (answerLen != (len - 5) * sizeof(mtpPrime)) { + LOG(("Fake PQ Error: bad request answer %1 <> %2").arg(answerLen).arg((len - 5) * sizeof(mtpPrime))); + DEBUG_LOG(("Fake PQ Error: answer bytes %1").arg(Logs::mb(answer, len * sizeof(mtpPrime)).str())); + throw Exception("bad pq reply"); + } + const mtpPrime *from(answer + 5), *end(from + len - 5); + MTPResPQ response; + response.read(from, end); + return response; +} + +AbstractConnection *AbstractConnection::create(QThread *thread) { + if (cConnectionType() == dbictHttpProxy) { + return new HTTPConnection(thread); + } else if (cConnectionType() == dbictTcpProxy) { + return new TCPConnection(thread); + } + return new AutoConnection(thread); +} + +} // namespace internal +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/connection_abstract.h b/Telegram/SourceFiles/mtproto/connection_abstract.h new file mode 100644 index 000000000..3fa7c7195 --- /dev/null +++ b/Telegram/SourceFiles/mtproto/connection_abstract.h @@ -0,0 +1,92 @@ +/* +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-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "mtproto/core_types.h" + +namespace MTP { +namespace internal { + +class AbstractConnection : public QObject { + Q_OBJECT + +public: + + AbstractConnection(QThread *thread) : _sentEncrypted(false) { + moveToThread(thread); + } + AbstractConnection(const AbstractConnection &other) = delete; + AbstractConnection &operator=(const AbstractConnection &other) = delete; + virtual ~AbstractConnection() = 0 { + } + + // virtual constructor + static AbstractConnection *create(QThread *thread); + + void setSentEncrypted() { + _sentEncrypted = true; + } + + virtual void sendData(mtpBuffer &buffer) = 0; // has size + 3, buffer[0] = len, buffer[1] = packetnum, buffer[last] = crc32 + virtual void disconnectFromServer() = 0; + virtual void connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) = 0; + virtual void connectHttp(const QString &addr, int32 port, MTPDdcOption::Flags flags) = 0; + virtual bool isConnected() const = 0; + virtual bool usingHttpWait() { + return false; + } + virtual bool needHttpWait() { + return false; + } + + virtual int32 debugState() const = 0; + + virtual QString transport() const = 0; + + typedef QList BuffersQueue; + BuffersQueue &received() { + return receivedQueue; + } + +signals: + + void receivedData(); + void receivedSome(); // to stop restart timer + + void error(bool mayBeBadKey = false); + + void connected(); + void disconnected(); + +protected: + + BuffersQueue receivedQueue; // list of received packets, not processed yet + bool _sentEncrypted; + + // first we always send fake MTPReq_pq to see if connection works at all + // we send them simultaneously through TCP/HTTP/IPv4/IPv6 to choose the working one + static mtpBuffer preparePQFake(const MTPint128 &nonce); + static MTPResPQ readPQFakeReply(const mtpBuffer &buffer); + +}; + +} // namespace internal +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/connection_auto.cpp b/Telegram/SourceFiles/mtproto/connection_auto.cpp new file mode 100644 index 000000000..3723f495c --- /dev/null +++ b/Telegram/SourceFiles/mtproto/connection_auto.cpp @@ -0,0 +1,344 @@ +/* +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-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" + +#include "mtproto/connection_auto.h" + +#include "mtproto/connection_http.h" + +namespace MTP { +namespace internal { + +AutoConnection::AutoConnection(QThread *thread) : AbstractTCPConnection(thread) +, status(WaitingBoth) +, tcpNonce(rand_value()) +, httpNonce(rand_value()) +, _flagsTcp(0) +, _flagsHttp(0) +, _tcpTimeout(MTPMinReceiveDelay) { + manager.moveToThread(thread); +#ifndef TDESKTOP_DISABLE_NETWORK_PROXY + manager.setProxy(QNetworkProxy(QNetworkProxy::DefaultProxy)); +#endif + + httpStartTimer.moveToThread(thread); + httpStartTimer.setSingleShot(true); + connect(&httpStartTimer, SIGNAL(timeout()), this, SLOT(onHttpStart())); + + tcpTimeoutTimer.moveToThread(thread); + tcpTimeoutTimer.setSingleShot(true); + connect(&tcpTimeoutTimer, SIGNAL(timeout()), this, SLOT(onTcpTimeoutTimer())); + + sock.moveToThread(thread); +#ifndef TDESKTOP_DISABLE_NETWORK_PROXY + sock.setProxy(QNetworkProxy(QNetworkProxy::NoProxy)); +#endif + connect(&sock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); + connect(&sock, SIGNAL(connected()), this, SLOT(onSocketConnected())); + connect(&sock, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected())); +} + +void AutoConnection::onHttpStart() { + if (status == HttpReady) { + DEBUG_LOG(("Connection Info: HTTP/%1-transport chosen by timer").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); + status = UsingHttp; + sock.disconnectFromHost(); + emit connected(); + } +} + +void AutoConnection::onSocketConnected() { + if (status == HttpReady || status == WaitingBoth || status == WaitingTcp) { + mtpBuffer buffer(preparePQFake(tcpNonce)); + + DEBUG_LOG(("Connection Info: sending fake req_pq through TCP/%1 transport").arg((_flagsTcp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); + + if (_tcpTimeout < 0) _tcpTimeout = -_tcpTimeout; + tcpTimeoutTimer.start(_tcpTimeout); + + tcpSend(buffer); + } else if (status == WaitingHttp || status == UsingHttp) { + sock.disconnectFromHost(); + } +} + +void AutoConnection::onTcpTimeoutTimer() { + if (status == HttpReady || status == WaitingBoth || status == WaitingTcp) { + if (_tcpTimeout < MTPMaxReceiveDelay) _tcpTimeout *= 2; + _tcpTimeout = -_tcpTimeout; + + QAbstractSocket::SocketState state = sock.state(); + if (state == QAbstractSocket::ConnectedState || state == QAbstractSocket::ConnectingState || state == QAbstractSocket::HostLookupState) { + sock.disconnectFromHost(); + } else if (state != QAbstractSocket::ClosingState) { + sock.connectToHost(QHostAddress(_addrTcp), _portTcp); + } + } +} + +void AutoConnection::onSocketDisconnected() { + if (_tcpTimeout < 0) { + _tcpTimeout = -_tcpTimeout; + if (status == HttpReady || status == WaitingBoth || status == WaitingTcp) { + sock.connectToHost(QHostAddress(_addrTcp), _portTcp); + return; + } + } + if (status == WaitingBoth) { + status = WaitingHttp; + } else if (status == WaitingTcp || status == UsingTcp) { + emit disconnected(); + } else if (status == HttpReady) { + DEBUG_LOG(("Connection Info: HTTP/%1-transport chosen by socket disconnect").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); + status = UsingHttp; + emit connected(); + } +} + +void AutoConnection::sendData(mtpBuffer &buffer) { + if (status == FinishedWork) return; + + if (buffer.size() < 3) { + LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime))); + TCP_LOG(("TCP Error: bad packet %1").arg(Logs::mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str())); + emit error(); + return; + } + + if (status == UsingTcp) { + tcpSend(buffer); + } else { + httpSend(buffer); + } +} + +void AutoConnection::httpSend(mtpBuffer &buffer) { + int32 requestSize = (buffer.size() - 3) * sizeof(mtpPrime); + + QNetworkRequest request(address); + request.setHeader(QNetworkRequest::ContentLengthHeader, QVariant(requestSize)); + request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(qsl("application/x-www-form-urlencoded"))); + + TCP_LOG(("HTTP Info: sending %1 len request").arg(requestSize)); + requests.insert(manager.post(request, QByteArray((const char*)(&buffer[2]), requestSize))); +} + +void AutoConnection::disconnectFromServer() { + if (status == FinishedWork) return; + status = FinishedWork; + + Requests copy = requests; + requests.clear(); + for (Requests::const_iterator i = copy.cbegin(), e = copy.cend(); i != e; ++i) { + (*i)->abort(); + (*i)->deleteLater(); + } + + disconnect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*))); + + address = QUrl(); + + disconnect(&sock, SIGNAL(readyRead()), 0, 0); + sock.close(); + + httpStartTimer.stop(); +} + +void AutoConnection::connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) { + _addrTcp = addr; + _portTcp = port; + _flagsTcp = flags; + + connect(&sock, SIGNAL(readyRead()), this, SLOT(socketRead())); + sock.connectToHost(QHostAddress(_addrTcp), _portTcp); +} + +void AutoConnection::connectHttp(const QString &addr, int32 port, MTPDdcOption::Flags flags) { + address = QUrl(((flags & MTPDdcOption::Flag::f_ipv6) ? qsl("http://[%1]:%2/api") : qsl("http://%1:%2/api")).arg(addr).arg(80));//not p - always 80 port for http transport + TCP_LOG(("HTTP Info: address is %1").arg(address.toDisplayString())); + connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*))); + + _addrHttp = addr; + _portHttp = port; + _flagsHttp = flags; + + mtpBuffer buffer(preparePQFake(httpNonce)); + + DEBUG_LOG(("Connection Info: sending fake req_pq through HTTP/%1 transport").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); + + httpSend(buffer); +} + +bool AutoConnection::isConnected() const { + return (status == UsingTcp) || (status == UsingHttp); +} + +void AutoConnection::requestFinished(QNetworkReply *reply) { + if (status == FinishedWork) return; + + reply->deleteLater(); + if (reply->error() == QNetworkReply::NoError) { + requests.remove(reply); + + mtpBuffer data = HTTPConnection::handleResponse(reply); + if (data.size() == 1) { + if (status == WaitingBoth) { + status = WaitingTcp; + } else { + emit error(); + } + } else if (!data.isEmpty()) { + if (status == UsingHttp) { + receivedQueue.push_back(data); + emit receivedData(); + } else if (status == WaitingBoth || status == WaitingHttp) { + try { + MTPResPQ res_pq = readPQFakeReply(data); + const MTPDresPQ &res_pq_data(res_pq.c_resPQ()); + if (res_pq_data.vnonce == httpNonce) { + if (status == WaitingBoth) { + status = HttpReady; + httpStartTimer.start(MTPTcpConnectionWaitTimeout); + } else { + DEBUG_LOG(("Connection Info: HTTP/%1-transport chosen by pq-response, awaited").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); + status = UsingHttp; + sock.disconnectFromHost(); + emit connected(); + } + } + } catch (Exception &e) { + DEBUG_LOG(("Connection Error: exception in parsing HTTP fake pq-responce, %1").arg(e.what())); + if (status == WaitingBoth) { + status = WaitingTcp; + } else { + emit error(); + } + } + } else if (status == UsingTcp) { + DEBUG_LOG(("Connection Info: already using tcp, ignoring http response")); + } + } + } else { + if (!requests.remove(reply)) { + return; + } + + bool mayBeBadKey = HTTPConnection::handleError(reply) && _sentEncrypted; + if (status == WaitingBoth) { + status = WaitingTcp; + } else if (status == WaitingHttp || status == UsingHttp) { + emit error(mayBeBadKey); + } else { + LOG(("Strange Http Error: status %1").arg(status)); + } + } +} + +void AutoConnection::socketPacket(const char *packet, uint32 length) { + if (status == FinishedWork) return; + + mtpBuffer data = TCPConnection::handleResponse(packet, length); + if (data.size() == 1) { + if (status == WaitingBoth) { + status = WaitingHttp; + sock.disconnectFromHost(); + } else if (status == HttpReady) { + DEBUG_LOG(("Connection Info: HTTP/%1-transport chosen by bad tcp response, ready").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); + status = UsingHttp; + sock.disconnectFromHost(); + emit connected(); + } else if (status == WaitingTcp || status == UsingTcp) { + bool mayBeBadKey = (data[0] == -410) && _sentEncrypted; + emit error(mayBeBadKey); + } else { + LOG(("Strange Tcp Error; status %1").arg(status)); + } + } else if (status == UsingTcp) { + receivedQueue.push_back(data); + emit receivedData(); + } else if (status == WaitingBoth || status == WaitingTcp || status == HttpReady) { + tcpTimeoutTimer.stop(); + try { + MTPResPQ res_pq = readPQFakeReply(data); + const MTPDresPQ &res_pq_data(res_pq.c_resPQ()); + if (res_pq_data.vnonce == tcpNonce) { + DEBUG_LOG(("Connection Info: TCP/%1-transport chosen by pq-response").arg((_flagsTcp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); + status = UsingTcp; + emit connected(); + } + } catch (Exception &e) { + DEBUG_LOG(("Connection Error: exception in parsing TCP fake pq-responce, %1").arg(e.what())); + if (status == WaitingBoth) { + status = WaitingHttp; + sock.disconnectFromHost(); + } else if (status == HttpReady) { + DEBUG_LOG(("Connection Info: HTTP/%1-transport chosen by bad tcp response, awaited").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); + status = UsingHttp; + sock.disconnectFromHost(); + emit connected(); + } else { + emit error(); + } + } + } +} + +bool AutoConnection::usingHttpWait() { + return (status == UsingHttp); +} + +bool AutoConnection::needHttpWait() { + return (status == UsingHttp) ? requests.isEmpty() : false; +} + +int32 AutoConnection::debugState() const { + return (status == UsingHttp) ? -1 : (UsingTcp ? sock.state() : -777); +} + +QString AutoConnection::transport() const { + if (status == UsingTcp) { + return qsl("TCP"); + } else if (status == UsingHttp) { + return qsl("HTTP"); + } else { + return QString(); + } +} + +void AutoConnection::socketError(QAbstractSocket::SocketError e) { + if (status == FinishedWork) return; + + TCPConnection::handleError(e, sock); + if (status == WaitingBoth) { + status = WaitingHttp; + } else if (status == HttpReady) { + DEBUG_LOG(("Connection Info: HTTP/%1-transport chosen by tcp error, ready").arg((_flagsHttp & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); + status = UsingHttp; + emit connected(); + } else if (status == WaitingTcp || status == UsingTcp) { + emit error(); + } else { + LOG(("Strange Tcp Error: status %1").arg(status)); + } +} + +} // namespace internal +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/connection_auto.h b/Telegram/SourceFiles/mtproto/connection_auto.h new file mode 100644 index 000000000..60a5b9b44 --- /dev/null +++ b/Telegram/SourceFiles/mtproto/connection_auto.h @@ -0,0 +1,94 @@ +/* +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-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "mtproto/core_types.h" +#include "mtproto/connection_tcp.h" + +namespace MTP { +namespace internal { + +class AutoConnection : public AbstractTCPConnection { + Q_OBJECT + +public: + + AutoConnection(QThread *thread); + + void sendData(mtpBuffer &buffer) override; + void disconnectFromServer() override; + void connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override; + void connectHttp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override; + bool isConnected() const override; + bool usingHttpWait() override; + bool needHttpWait() override; + + int32 debugState() const override; + + QString transport() const override; + +public slots: + + void socketError(QAbstractSocket::SocketError e); + void requestFinished(QNetworkReply *reply); + + void onSocketConnected(); + void onSocketDisconnected(); + void onHttpStart(); + + void onTcpTimeoutTimer(); + +protected: + + void socketPacket(const char *packet, uint32 length) override; + +private: + + void httpSend(mtpBuffer &buffer); + enum Status { + WaitingBoth = 0, + WaitingHttp, + WaitingTcp, + HttpReady, + UsingHttp, + UsingTcp, + FinishedWork + }; + Status status; + MTPint128 tcpNonce, httpNonce; + QTimer httpStartTimer; + + QNetworkAccessManager manager; + QUrl address; + + typedef QSet Requests; + Requests requests; + + QString _addrTcp, _addrHttp; + int32 _portTcp, _portHttp; + MTPDdcOption::Flags _flagsTcp, _flagsHttp; + int32 _tcpTimeout; + QTimer tcpTimeoutTimer; + +}; + +} // namespace internal +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/connection_http.cpp b/Telegram/SourceFiles/mtproto/connection_http.cpp new file mode 100644 index 000000000..39f26e131 --- /dev/null +++ b/Telegram/SourceFiles/mtproto/connection_http.cpp @@ -0,0 +1,218 @@ +/* +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-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" + +#include "mtproto/connection_http.h" + +namespace MTP { +namespace internal { + +mtpBuffer HTTPConnection::handleResponse(QNetworkReply *reply) { + QByteArray response = reply->readAll(); + TCP_LOG(("HTTP Info: read %1 bytes").arg(response.size())); + + if (response.isEmpty()) return mtpBuffer(); + + if (response.size() & 0x03 || response.size() < 8) { + LOG(("HTTP Error: bad response size %1").arg(response.size())); + return mtpBuffer(1, -500); + } + + mtpBuffer data(response.size() >> 2); + memcpy(data.data(), response.constData(), response.size()); + + return data; +} + +bool HTTPConnection::handleError(QNetworkReply *reply) { // returnes "maybe bad key" + bool mayBeBadKey = false; + + QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + if (statusCode.isValid()) { + int status = statusCode.toInt(); + mayBeBadKey = (status == 410); + if (status == 429) { + LOG(("Protocol Error: 429 flood code returned!")); + } + } + + switch (reply->error()) { + case QNetworkReply::ConnectionRefusedError: LOG(("HTTP Error: connection refused - %1").arg(reply->errorString())); break; + case QNetworkReply::RemoteHostClosedError: LOG(("HTTP Error: remote host closed - %1").arg(reply->errorString())); break; + case QNetworkReply::HostNotFoundError: LOG(("HTTP Error: host not found - %2").arg(reply->error()).arg(reply->errorString())); break; + case QNetworkReply::TimeoutError: LOG(("HTTP Error: timeout - %2").arg(reply->error()).arg(reply->errorString())); break; + case QNetworkReply::OperationCanceledError: LOG(("HTTP Error: cancelled - %2").arg(reply->error()).arg(reply->errorString())); break; + case QNetworkReply::SslHandshakeFailedError: + case QNetworkReply::TemporaryNetworkFailureError: + case QNetworkReply::NetworkSessionFailedError: + case QNetworkReply::BackgroundRequestNotAllowedError: + case QNetworkReply::UnknownNetworkError: LOG(("HTTP Error: network error %1 - %2").arg(reply->error()).arg(reply->errorString())); break; + + // proxy errors (101-199): + case QNetworkReply::ProxyConnectionRefusedError: + case QNetworkReply::ProxyConnectionClosedError: + case QNetworkReply::ProxyNotFoundError: + case QNetworkReply::ProxyTimeoutError: + case QNetworkReply::ProxyAuthenticationRequiredError: + case QNetworkReply::UnknownProxyError:LOG(("HTTP Error: proxy error %1 - %2").arg(reply->error()).arg(reply->errorString())); break; + + // content errors (201-299): + case QNetworkReply::ContentAccessDenied: + case QNetworkReply::ContentOperationNotPermittedError: + case QNetworkReply::ContentNotFoundError: + case QNetworkReply::AuthenticationRequiredError: + case QNetworkReply::ContentReSendError: + case QNetworkReply::UnknownContentError: LOG(("HTTP Error: content error %1 - %2").arg(reply->error()).arg(reply->errorString())); break; + + // protocol errors + case QNetworkReply::ProtocolUnknownError: + case QNetworkReply::ProtocolInvalidOperationError: + case QNetworkReply::ProtocolFailure: LOG(("HTTP Error: protocol error %1 - %2").arg(reply->error()).arg(reply->errorString())); break; + }; + TCP_LOG(("HTTP Error %1, restarting! - %2").arg(reply->error()).arg(reply->errorString())); + + return mayBeBadKey; +} + +HTTPConnection::HTTPConnection(QThread *thread) : AbstractConnection(thread) +, status(WaitingHttp) +, httpNonce(rand_value()) +, _flags(0) { + manager.moveToThread(thread); + App::setProxySettings(manager); +} + +void HTTPConnection::sendData(mtpBuffer &buffer) { + if (status == FinishedWork) return; + + if (buffer.size() < 3) { + LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime))); + TCP_LOG(("TCP Error: bad packet %1").arg(Logs::mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str())); + emit error(); + return; + } + + int32 requestSize = (buffer.size() - 3) * sizeof(mtpPrime); + + QNetworkRequest request(address); + request.setHeader(QNetworkRequest::ContentLengthHeader, QVariant(requestSize)); + request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(qsl("application/x-www-form-urlencoded"))); + + TCP_LOG(("HTTP Info: sending %1 len request %2").arg(requestSize).arg(Logs::mb(&buffer[2], requestSize).str())); + requests.insert(manager.post(request, QByteArray((const char*)(&buffer[2]), requestSize))); +} + +void HTTPConnection::disconnectFromServer() { + if (status == FinishedWork) return; + status = FinishedWork; + + Requests copy = requests; + requests.clear(); + for (Requests::const_iterator i = copy.cbegin(), e = copy.cend(); i != e; ++i) { + (*i)->abort(); + (*i)->deleteLater(); + } + + disconnect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*))); + + address = QUrl(); +} + +void HTTPConnection::connectHttp(const QString &addr, int32 p, MTPDdcOption::Flags flags) { + address = QUrl(((flags & MTPDdcOption::Flag::f_ipv6) ? qsl("http://[%1]:%2/api") : qsl("http://%1:%2/api")).arg(addr).arg(80));//not p - always 80 port for http transport + TCP_LOG(("HTTP Info: address is %1").arg(address.toDisplayString())); + connect(&manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*))); + + _flags = flags; + + mtpBuffer buffer(preparePQFake(httpNonce)); + + DEBUG_LOG(("Connection Info: sending fake req_pq through HTTP/%1 transport").arg((flags & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); + + sendData(buffer); +} + +bool HTTPConnection::isConnected() const { + return (status == UsingHttp); +} + +void HTTPConnection::requestFinished(QNetworkReply *reply) { + if (status == FinishedWork) return; + + reply->deleteLater(); + if (reply->error() == QNetworkReply::NoError) { + requests.remove(reply); + + mtpBuffer data = handleResponse(reply); + if (data.size() == 1) { + emit error(); + } else if (!data.isEmpty()) { + if (status == UsingHttp) { + receivedQueue.push_back(data); + emit receivedData(); + } else { + try { + MTPResPQ res_pq = readPQFakeReply(data); + const MTPDresPQ &res_pq_data(res_pq.c_resPQ()); + if (res_pq_data.vnonce == httpNonce) { + DEBUG_LOG(("Connection Info: HTTP/%1-transport connected by pq-response").arg((_flags & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); + status = UsingHttp; + emit connected(); + } + } catch (Exception &e) { + DEBUG_LOG(("Connection Error: exception in parsing HTTP fake pq-responce, %1").arg(e.what())); + emit error(); + } + } + } + } else { + if (!requests.remove(reply)) { + return; + } + + bool mayBeBadKey = handleError(reply) && _sentEncrypted; + + emit error(mayBeBadKey); + } +} + +bool HTTPConnection::usingHttpWait() { + return true; +} + +bool HTTPConnection::needHttpWait() { + return requests.isEmpty(); +} + +int32 HTTPConnection::debugState() const { + return -1; +} + +QString HTTPConnection::transport() const { + if (status == UsingHttp) { + return qsl("HTTP"); + } else { + return QString(); + } +} + +} // namespace internal +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/connection_http.h b/Telegram/SourceFiles/mtproto/connection_http.h new file mode 100644 index 000000000..54f3af11d --- /dev/null +++ b/Telegram/SourceFiles/mtproto/connection_http.h @@ -0,0 +1,76 @@ +/* +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-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "mtproto/core_types.h" +#include "mtproto/connection_abstract.h" + +namespace MTP { +namespace internal { + +class HTTPConnection : public AbstractConnection { + Q_OBJECT + +public: + + HTTPConnection(QThread *thread); + + void sendData(mtpBuffer &buffer) override; + void disconnectFromServer() override; + void connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override { // not supported + } + void connectHttp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override; + bool isConnected() const override; + bool usingHttpWait() override; + bool needHttpWait() override; + + int32 debugState() const override; + + QString transport() const override; + + public slots: + + void requestFinished(QNetworkReply *reply); + + static mtpBuffer handleResponse(QNetworkReply *reply); + static bool handleError(QNetworkReply *reply); // returnes "maybe bad key" + +private: + + enum Status { + WaitingHttp = 0, + UsingHttp, + FinishedWork + }; + Status status; + MTPint128 httpNonce; + MTPDdcOption::Flags _flags; + + QNetworkAccessManager manager; + QUrl address; + + typedef QSet Requests; + Requests requests; + +}; + +} // namespace internal +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/connection_tcp.cpp b/Telegram/SourceFiles/mtproto/connection_tcp.cpp new file mode 100644 index 000000000..8f18e4549 --- /dev/null +++ b/Telegram/SourceFiles/mtproto/connection_tcp.cpp @@ -0,0 +1,395 @@ +/* +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-2016 John Preston, https://desktop.telegram.org +*/ +#include "stdafx.h" + +#include "mtproto/connection_tcp.h" + +#include + +namespace MTP { +namespace internal { + +namespace { + +uint32 tcpPacketSize(const char *packet) { // must have at least 4 bytes readable + uint32 result = (packet[0] > 0) ? packet[0] : 0; + if (result == 0x7f) { + const uchar *bytes = reinterpret_cast(packet); + result = (((uint32(bytes[3]) << 8) | uint32(bytes[2])) << 8) | uint32(bytes[1]); + return (result << 2) + 4; + } + return (result << 2) + 1; +} + +} // namespace + +AbstractTCPConnection::AbstractTCPConnection(QThread *thread) : AbstractConnection(thread) +, packetNum(0) +, packetRead(0) +, packetLeft(0) +, readingToShort(true) +, currentPos((char*)shortBuffer) { +} + +void AbstractTCPConnection::socketRead() { + if (sock.state() != QAbstractSocket::ConnectedState) { + LOG(("MTP error: socket not connected in socketRead(), state: %1").arg(sock.state())); + emit error(); + return; + } + + do { + uint32 toRead = packetLeft ? packetLeft : (readingToShort ? (MTPShortBufferSize * sizeof(mtpPrime) - packetRead) : 4); + if (readingToShort) { + if (currentPos + toRead > ((char*)shortBuffer) + MTPShortBufferSize * sizeof(mtpPrime)) { + longBuffer.resize(((packetRead + toRead) >> 2) + 1); + memcpy(&longBuffer[0], shortBuffer, packetRead); + currentPos = ((char*)&longBuffer[0]) + packetRead; + readingToShort = false; + } + } else { + if (longBuffer.size() * sizeof(mtpPrime) < packetRead + toRead) { + longBuffer.resize(((packetRead + toRead) >> 2) + 1); + currentPos = ((char*)&longBuffer[0]) + packetRead; + } + } + int32 bytes = (int32)sock.read(currentPos, toRead); + if (bytes > 0) { + aesCtrEncrypt(currentPos, bytes, _receiveKey, &_receiveState); + TCP_LOG(("TCP Info: read %1 bytes").arg(bytes)); + + packetRead += bytes; + currentPos += bytes; + if (packetLeft) { + packetLeft -= bytes; + if (!packetLeft) { + socketPacket(currentPos - packetRead, packetRead); + currentPos = (char*)shortBuffer; + packetRead = packetLeft = 0; + readingToShort = true; + longBuffer.clear(); + } else { + TCP_LOG(("TCP Info: not enough %1 for packet! read %2").arg(packetLeft).arg(packetRead)); + emit receivedSome(); + } + } else { + bool move = false; + while (packetRead >= 4) { + uint32 packetSize = tcpPacketSize(currentPos - packetRead); + if (packetSize < 5 || packetSize > MTPPacketSizeMax) { + LOG(("TCP Error: packet size = %1").arg(packetSize)); + emit error(); + return; + } + if (packetRead >= packetSize) { + socketPacket(currentPos - packetRead, packetSize); + packetRead -= packetSize; + packetLeft = 0; + move = true; + } else { + packetLeft = packetSize - packetRead; + TCP_LOG(("TCP Info: not enough %1 for packet! size %2 read %3").arg(packetLeft).arg(packetSize).arg(packetRead)); + emit receivedSome(); + break; + } + } + if (move) { + if (!packetRead) { + currentPos = (char*)shortBuffer; + readingToShort = true; + longBuffer.clear(); + } else if (!readingToShort && packetRead < MTPShortBufferSize * sizeof(mtpPrime)) { + memcpy(shortBuffer, currentPos - packetRead, packetRead); + currentPos = (char*)shortBuffer + packetRead; + readingToShort = true; + longBuffer.clear(); + } + } + } + } else if (bytes < 0) { + LOG(("TCP Error: socket read return -1")); + emit error(); + return; + } else { + TCP_LOG(("TCP Info: no bytes read, but bytes available was true..")); + break; + } + } while (sock.state() == QAbstractSocket::ConnectedState && sock.bytesAvailable()); +} + +mtpBuffer AbstractTCPConnection::handleResponse(const char *packet, uint32 length) { + if (length < 5 || length > MTPPacketSizeMax) { + LOG(("TCP Error: bad packet size %1").arg(length)); + return mtpBuffer(1, -500); + } + int32 size = packet[0], len = length - 1; + if (size == 0x7f) { + const uchar *bytes = reinterpret_cast(packet); + size = (((uint32(bytes[3]) << 8) | uint32(bytes[2])) << 8) | uint32(bytes[1]); + len -= 3; + } + if (size * int32(sizeof(mtpPrime)) != len) { + LOG(("TCP Error: bad packet header")); + TCP_LOG(("TCP Error: bad packet header, packet: %1").arg(Logs::mb(packet, length).str())); + return mtpBuffer(1, -500); + } + const mtpPrime *packetdata = reinterpret_cast(packet + (length - len)); + TCP_LOG(("TCP Info: packet received, size = %1").arg(size * sizeof(mtpPrime))); + if (size == 1) { + if (*packetdata == -429) { + LOG(("Protocol Error: -429 flood code returned!")); + } else { + LOG(("TCP Error: error packet received, code = %1").arg(*packetdata)); + } + return mtpBuffer(1, *packetdata); + } + + mtpBuffer data(size); + memcpy(data.data(), packetdata, size * sizeof(mtpPrime)); + + return data; +} + +void AbstractTCPConnection::handleError(QAbstractSocket::SocketError e, QTcpSocket &sock) { + switch (e) { + case QAbstractSocket::ConnectionRefusedError: + LOG(("TCP Error: socket connection refused - %1").arg(sock.errorString())); + break; + + case QAbstractSocket::RemoteHostClosedError: + TCP_LOG(("TCP Info: remote host closed socket connection - %1").arg(sock.errorString())); + break; + + case QAbstractSocket::HostNotFoundError: + LOG(("TCP Error: host not found - %1").arg(sock.errorString())); + break; + + case QAbstractSocket::SocketTimeoutError: + LOG(("TCP Error: socket timeout - %1").arg(sock.errorString())); + break; + + case QAbstractSocket::NetworkError: + LOG(("TCP Error: network - %1").arg(sock.errorString())); + break; + + case QAbstractSocket::ProxyAuthenticationRequiredError: + case QAbstractSocket::ProxyConnectionRefusedError: + case QAbstractSocket::ProxyConnectionClosedError: + case QAbstractSocket::ProxyConnectionTimeoutError: + case QAbstractSocket::ProxyNotFoundError: + case QAbstractSocket::ProxyProtocolError: + LOG(("TCP Error: proxy (%1) - %2").arg(e).arg(sock.errorString())); + break; + + default: + LOG(("TCP Error: other (%1) - %2").arg(e).arg(sock.errorString())); + break; + } + + TCP_LOG(("TCP Error %1, restarting! - %2").arg(e).arg(sock.errorString())); +} + +TCPConnection::TCPConnection(QThread *thread) : AbstractTCPConnection(thread) +, status(WaitingTcp) +, tcpNonce(rand_value()) +, _tcpTimeout(MTPMinReceiveDelay) +, _flags(0) { + tcpTimeoutTimer.moveToThread(thread); + tcpTimeoutTimer.setSingleShot(true); + connect(&tcpTimeoutTimer, SIGNAL(timeout()), this, SLOT(onTcpTimeoutTimer())); + + sock.moveToThread(thread); + App::setProxySettings(sock); + connect(&sock, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(socketError(QAbstractSocket::SocketError))); + connect(&sock, SIGNAL(connected()), this, SLOT(onSocketConnected())); + connect(&sock, SIGNAL(disconnected()), this, SLOT(onSocketDisconnected())); +} + +void TCPConnection::onSocketConnected() { + if (status == WaitingTcp) { + mtpBuffer buffer(preparePQFake(tcpNonce)); + + DEBUG_LOG(("Connection Info: sending fake req_pq through TCP/%1 transport").arg((_flags & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); + + if (_tcpTimeout < 0) _tcpTimeout = -_tcpTimeout; + tcpTimeoutTimer.start(_tcpTimeout); + + sendData(buffer); + } +} + +void TCPConnection::onTcpTimeoutTimer() { + if (status == WaitingTcp) { + if (_tcpTimeout < MTPMaxReceiveDelay) _tcpTimeout *= 2; + _tcpTimeout = -_tcpTimeout; + + QAbstractSocket::SocketState state = sock.state(); + if (state == QAbstractSocket::ConnectedState || state == QAbstractSocket::ConnectingState || state == QAbstractSocket::HostLookupState) { + sock.disconnectFromHost(); + } else if (state != QAbstractSocket::ClosingState) { + sock.connectToHost(QHostAddress(_addr), _port); + } + } +} + +void TCPConnection::onSocketDisconnected() { + if (_tcpTimeout < 0) { + _tcpTimeout = -_tcpTimeout; + if (status == WaitingTcp) { + sock.connectToHost(QHostAddress(_addr), _port); + return; + } + } + if (status == WaitingTcp || status == UsingTcp) { + emit disconnected(); + } +} + +void TCPConnection::sendData(mtpBuffer &buffer) { + if (status == FinishedWork) return; + + if (buffer.size() < 3) { + LOG(("TCP Error: writing bad packet, len = %1").arg(buffer.size() * sizeof(mtpPrime))); + TCP_LOG(("TCP Error: bad packet %1").arg(Logs::mb(&buffer[0], buffer.size() * sizeof(mtpPrime)).str())); + emit error(); + return; + } + + tcpSend(buffer); +} + +void AbstractTCPConnection::tcpSend(mtpBuffer &buffer) { + if (!packetNum) { + // prepare random part + char nonce[64]; + uint32 *first = reinterpret_cast(nonce), *second = first + 1; + uint32 first1 = 0x44414548U, first2 = 0x54534f50U, first3 = 0x20544547U, first4 = 0x20544547U, first5 = 0xeeeeeeeeU; + uint32 second1 = 0; + do { + memset_rand(nonce, sizeof(nonce)); + } while (*first == first1 || *first == first2 || *first == first3 || *first == first4 || *first == first5 || *second == second1 || *reinterpret_cast(nonce) == 0xef); + //sock.write(nonce, 64); + + // prepare encryption key/iv + memcpy(_sendKey, nonce + 8, CTRState::KeySize); + memcpy(_sendState.ivec, nonce + 8 + CTRState::KeySize, CTRState::IvecSize); + + // prepare decryption key/iv + char reversed[48]; + memcpy(reversed, nonce + 8, sizeof(reversed)); + std::reverse(reversed, reversed + arraysize(reversed)); + memcpy(_receiveKey, reversed, CTRState::KeySize); + memcpy(_receiveState.ivec, reversed + CTRState::KeySize, CTRState::IvecSize); + + // write protocol identifier + *reinterpret_cast(nonce + 56) = 0xefefefefU; + + sock.write(nonce, 56); + aesCtrEncrypt(nonce, 64, _sendKey, &_sendState); + sock.write(nonce + 56, 8); + } + ++packetNum; + + uint32 size = buffer.size() - 3, len = size * 4; + char *data = reinterpret_cast(&buffer[0]); + if (size < 0x7f) { + data[7] = char(size); + TCP_LOG(("TCP Info: write %1 packet %2").arg(packetNum).arg(len + 1)); + + aesCtrEncrypt(data + 7, len + 1, _sendKey, &_sendState); + sock.write(data + 7, len + 1); + } else { + data[4] = 0x7f; + reinterpret_cast(data)[5] = uchar(size & 0xFF); + reinterpret_cast(data)[6] = uchar((size >> 8) & 0xFF); + reinterpret_cast(data)[7] = uchar((size >> 16) & 0xFF); + TCP_LOG(("TCP Info: write %1 packet %2").arg(packetNum).arg(len + 4)); + + aesCtrEncrypt(data + 4, len + 4, _sendKey, &_sendState); + sock.write(data + 4, len + 4); + } +} + +void TCPConnection::disconnectFromServer() { + if (status == FinishedWork) return; + status = FinishedWork; + + disconnect(&sock, SIGNAL(readyRead()), 0, 0); + sock.close(); +} + +void TCPConnection::connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) { + _addr = addr; + _port = port; + _flags = flags; + + connect(&sock, SIGNAL(readyRead()), this, SLOT(socketRead())); + sock.connectToHost(QHostAddress(_addr), _port); +} + +void TCPConnection::socketPacket(const char *packet, uint32 length) { + if (status == FinishedWork) return; + + mtpBuffer data = handleResponse(packet, length); + if (data.size() == 1) { + bool mayBeBadKey = (data[0] == -410) && _sentEncrypted; + emit error(mayBeBadKey); + } else if (status == UsingTcp) { + receivedQueue.push_back(data); + emit receivedData(); + } else if (status == WaitingTcp) { + tcpTimeoutTimer.stop(); + try { + MTPResPQ res_pq = readPQFakeReply(data); + const MTPDresPQ &res_pq_data(res_pq.c_resPQ()); + if (res_pq_data.vnonce == tcpNonce) { + DEBUG_LOG(("Connection Info: TCP/%1-transport chosen by pq-response").arg((_flags & MTPDdcOption::Flag::f_ipv6) ? "IPv6" : "IPv4")); + status = UsingTcp; + emit connected(); + } + } catch (Exception &e) { + DEBUG_LOG(("Connection Error: exception in parsing TCP fake pq-responce, %1").arg(e.what())); + emit error(); + } + } +} + +bool TCPConnection::isConnected() const { + return (status == UsingTcp); +} + +int32 TCPConnection::debugState() const { + return sock.state(); +} + +QString TCPConnection::transport() const { + return isConnected() ? qsl("TCP") : QString(); +} + +void TCPConnection::socketError(QAbstractSocket::SocketError e) { + if (status == FinishedWork) return; + + handleError(e, sock); + emit error(); +} + +} // namespace internal +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/connection_tcp.h b/Telegram/SourceFiles/mtproto/connection_tcp.h new file mode 100644 index 000000000..17ac5ca0a --- /dev/null +++ b/Telegram/SourceFiles/mtproto/connection_tcp.h @@ -0,0 +1,119 @@ +/* +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-2016 John Preston, https://desktop.telegram.org +*/ +#pragma once + +#include "mtproto/core_types.h" +#include "mtproto/auth_key.h" +#include "mtproto/connection_abstract.h" + +namespace MTP { +namespace internal { + +class AbstractTCPConnection : public AbstractConnection { + Q_OBJECT + +public: + + AbstractTCPConnection(QThread *thread); + virtual ~AbstractTCPConnection() = 0 { + } + +public slots: + + void socketRead(); + +protected: + + QTcpSocket sock; + uint32 packetNum; // sent packet number + + uint32 packetRead, packetLeft; // reading from socket + bool readingToShort; + char *currentPos; + mtpBuffer longBuffer; + mtpPrime shortBuffer[MTPShortBufferSize]; + virtual void socketPacket(const char *packet, uint32 length) = 0; + + static mtpBuffer handleResponse(const char *packet, uint32 length); + static void handleError(QAbstractSocket::SocketError e, QTcpSocket &sock); + static uint32 fourCharsToUInt(char ch1, char ch2, char ch3, char ch4) { + char ch[4] = { ch1, ch2, ch3, ch4 }; + return *reinterpret_cast(ch); + } + + void tcpSend(mtpBuffer &buffer); + uchar _sendKey[CTRState::KeySize]; + CTRState _sendState; + uchar _receiveKey[CTRState::KeySize]; + CTRState _receiveState; + +}; + +class TCPConnection : public AbstractTCPConnection { + Q_OBJECT + +public: + + TCPConnection(QThread *thread); + + void sendData(mtpBuffer &buffer) override; + void disconnectFromServer() override; + void connectTcp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override; + void connectHttp(const QString &addr, int32 port, MTPDdcOption::Flags flags) override { // not supported + } + bool isConnected() const override; + + int32 debugState() const override; + + QString transport() const override; + +public slots: + + void socketError(QAbstractSocket::SocketError e); + + void onSocketConnected(); + void onSocketDisconnected(); + + void onTcpTimeoutTimer(); + +protected: + + void socketPacket(const char *packet, uint32 length) override; + +private: + + enum Status { + WaitingTcp = 0, + UsingTcp, + FinishedWork + }; + Status status; + MTPint128 tcpNonce; + + QString _addr; + int32 _port, _tcpTimeout; + MTPDdcOption::Flags _flags; + QTimer tcpTimeoutTimer; + +}; + +} // namespace internal +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/core_types.cpp b/Telegram/SourceFiles/mtproto/core_types.cpp index a45dc6e16..1e401eefc 100644 --- a/Telegram/SourceFiles/mtproto/core_types.cpp +++ b/Telegram/SourceFiles/mtproto/core_types.cpp @@ -22,6 +22,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mtproto/core_types.h" +#include "zlib.h" + #include "lang.h" QString mtpWrapNumber(float64 number) { diff --git a/Telegram/SourceFiles/mtproto/dcenter.cpp b/Telegram/SourceFiles/mtproto/dcenter.cpp index 577c37bd8..62e6bdaf7 100644 --- a/Telegram/SourceFiles/mtproto/dcenter.cpp +++ b/Telegram/SourceFiles/mtproto/dcenter.cpp @@ -25,39 +25,41 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "mtproto/facade.h" #include "localstorage.h" -namespace { +namespace MTP { +namespace internal { - MTProtoDCMap gDCs; +namespace { + DcenterMap gDCs; bool configLoadedOnce = false; bool mainDCChanged = false; - int32 mainDC = 2; + int32 _mainDC = 2; int32 userId = 0; - typedef QMap _KeysMapForWrite; + typedef QMap _KeysMapForWrite; _KeysMapForWrite _keysMapForWrite; QMutex _keysMapForWriteMutex; -} +} // namespace -int32 mtpAuthed() { +int32 authed() { return userId; } -void mtpAuthed(int32 uid) { +void authed(int32 uid) { if (userId != uid) { userId = uid; } } -MTProtoDCMap &mtpDCMap() { +DcenterMap &DCMap() { return gDCs; } -bool mtpNeedConfig() { +bool configNeeded() { return !configLoadedOnce; } -int32 mtpMainDC() { - return mainDC; +int32 mainDC() { + return _mainDC; } namespace { @@ -74,7 +76,7 @@ namespace { } } -void mtpLogoutOtherDCs() { +void logoutOtherDCs() { QList dcs; { QMutexLocker lock(&_keysMapForWriteMutex); @@ -87,15 +89,15 @@ void mtpLogoutOtherDCs() { } } -void mtpSetDC(int32 dc, bool firstOnly) { +void setDC(int32 dc, bool firstOnly) { if (!dc || (firstOnly && mainDCChanged)) return; mainDCChanged = true; - if (dc != mainDC) { - mainDC = dc; + if (dc != _mainDC) { + _mainDC = dc; } } -MTProtoDC::MTProtoDC(int32 id, const mtpAuthKeyPtr &key) : _id(id), _key(key), _connectionInited(false) { +Dcenter::Dcenter(int32 id, const AuthKeyPtr &key) : _id(id), _key(key), _connectionInited(false) { connect(this, SIGNAL(authKeyCreated()), this, SLOT(authKeyWrite()), Qt::QueuedConnection); QMutexLocker lock(&_keysMapForWriteMutex); @@ -106,14 +108,14 @@ MTProtoDC::MTProtoDC(int32 id, const mtpAuthKeyPtr &key) : _id(id), _key(key), _ } } -void MTProtoDC::authKeyWrite() { +void Dcenter::authKeyWrite() { DEBUG_LOG(("AuthKey Info: MTProtoDC::authKeyWrite() slot, dc %1").arg(_id)); if (_key) { Local::writeMtpData(); } } -void MTProtoDC::setKey(const mtpAuthKeyPtr &key) { +void Dcenter::setKey(const AuthKeyPtr &key) { DEBUG_LOG(("AuthKey Info: MTProtoDC::setKey(%1), emitting authKeyCreated, dc %2").arg(key ? key->keyId() : 0).arg(_id)); _key = key; _connectionInited = false; @@ -127,23 +129,23 @@ void MTProtoDC::setKey(const mtpAuthKeyPtr &key) { } } -QReadWriteLock *MTProtoDC::keyMutex() const { +QReadWriteLock *Dcenter::keyMutex() const { return &keyLock; } -const mtpAuthKeyPtr &MTProtoDC::getKey() const { +const AuthKeyPtr &Dcenter::getKey() const { return _key; } -void MTProtoDC::destroyKey() { - setKey(mtpAuthKeyPtr()); +void Dcenter::destroyKey() { + setKey(AuthKeyPtr()); QMutexLocker lock(&_keysMapForWriteMutex); _keysMapForWrite.remove(_id); } namespace { - MTProtoConfigLoader *configLoader = 0; + ConfigLoader *_configLoader = nullptr; bool loadingConfig = false; void configLoaded(const MTPConfig &result) { loadingConfig = false; @@ -152,7 +154,7 @@ namespace { DEBUG_LOG(("MTP Info: got config, chat_size_max: %1, date: %2, test_mode: %3, this_dc: %4, dc_options.length: %5").arg(data.vchat_size_max.v).arg(data.vdate.v).arg(mtpIsTrue(data.vtest_mode)).arg(data.vthis_dc.v).arg(data.vdc_options.c_vector().v.size())); - mtpUpdateDcOptions(data.vdc_options.c_vector().v); + updateDcOptions(data.vdc_options.c_vector().v); Global::SetChatSizeMax(data.vchat_size_max.v); Global::SetMegagroupSizeMax(data.vmegagroup_size_max.v); @@ -172,7 +174,7 @@ namespace { configLoadedOnce = true; Local::writeSettings(); - mtpConfigLoader()->done(); + configLoader()->done(); } bool configFailed(const RPCError &error) { if (mtpIsFlood(error)) return false; @@ -183,12 +185,12 @@ namespace { } }; -void mtpUpdateDcOptions(const QVector &options) { +void updateDcOptions(const QVector &options) { QSet already, restart; { MTP::DcOptions opts; { - QReadLocker lock(mtpDcOptionsMutex()); + QReadLocker lock(dcOptionsMutex()); opts = Global::DcOptions(); } for (QVector::const_iterator i = options.cbegin(), e = options.cend(); i != e; ++i) { @@ -206,7 +208,7 @@ void mtpUpdateDcOptions(const QVector &options) { } } { - QWriteLocker lock(mtpDcOptionsMutex()); + QWriteLocker lock(dcOptionsMutex()); Global::SetDcOptions(opts); } } @@ -219,15 +221,15 @@ namespace { QReadWriteLock _dcOptionsMutex; } -QReadWriteLock *mtpDcOptionsMutex() { +QReadWriteLock *dcOptionsMutex() { return &_dcOptionsMutex; } -MTProtoConfigLoader::MTProtoConfigLoader() : _enumCurrent(0), _enumRequest(0) { +ConfigLoader::ConfigLoader() : _enumCurrent(0), _enumRequest(0) { connect(&_enumDCTimer, SIGNAL(timeout()), this, SLOT(enumDC())); } -void MTProtoConfigLoader::load() { +void ConfigLoader::load() { if (loadingConfig) return; loadingConfig = true; @@ -236,7 +238,7 @@ void MTProtoConfigLoader::load() { _enumDCTimer.start(MTPEnumDCTimeout); } -void MTProtoConfigLoader::done() { +void ConfigLoader::done() { _enumDCTimer.stop(); if (_enumRequest) { MTP::cancel(_enumRequest); @@ -249,19 +251,19 @@ void MTProtoConfigLoader::done() { emit loaded(); } -void MTProtoConfigLoader::enumDC() { +void ConfigLoader::enumDC() { if (!loadingConfig) return; if (_enumRequest) MTP::cancel(_enumRequest); if (!_enumCurrent) { - _enumCurrent = mainDC; + _enumCurrent = _mainDC; } else { MTP::killSession(MTP::cfgDcId(_enumCurrent)); } OrderedSet dcs; { - QReadLocker lock(mtpDcOptionsMutex()); + QReadLocker lock(dcOptionsMutex()); const MTP::DcOptions &options(Global::DcOptions()); for (auto i = options.cbegin(), e = options.cend(); i != e; ++i) { dcs.insert(MTP::bareDcId(i.key())); @@ -278,26 +280,29 @@ void MTProtoConfigLoader::enumDC() { _enumDCTimer.start(MTPEnumDCTimeout); } -MTProtoConfigLoader *mtpConfigLoader() { - if (!configLoader) configLoader = new MTProtoConfigLoader(); - return configLoader; +ConfigLoader *configLoader() { + if (!_configLoader) _configLoader = new ConfigLoader(); + return _configLoader; } -void mtpDestroyConfigLoader() { - delete configLoader; - configLoader = 0; +void destroyConfigLoader() { + delete _configLoader; + _configLoader = nullptr; } -mtpKeysMap mtpGetKeys() { - mtpKeysMap result; +AuthKeysMap getAuthKeys() { + AuthKeysMap result; QMutexLocker lock(&_keysMapForWriteMutex); - for (_KeysMapForWrite::const_iterator i = _keysMapForWrite.cbegin(), e = _keysMapForWrite.cend(); i != e; ++i) { - result.push_back(i.value()); + for_const (const AuthKeyPtr &key, _keysMapForWrite) { + result.push_back(key); } return result; } -void mtpSetKey(int32 dcId, mtpAuthKeyPtr key) { - MTProtoDCPtr dc(new MTProtoDC(dcId, key)); +void setAuthKey(int32 dcId, AuthKeyPtr key) { + DcenterPtr dc(new Dcenter(dcId, key)); gDCs.insert(dcId, dc); } + +} // namespace internal +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/dcenter.h b/Telegram/SourceFiles/mtproto/dcenter.h index bc8a6e787..4f47c63d3 100644 --- a/Telegram/SourceFiles/mtproto/dcenter.h +++ b/Telegram/SourceFiles/mtproto/dcenter.h @@ -20,16 +20,19 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once -class MTProtoDC : public QObject { +namespace MTP { +namespace internal { + +class Dcenter : public QObject { Q_OBJECT public: - MTProtoDC(int32 id, const mtpAuthKeyPtr &key); + Dcenter(int32 id, const AuthKeyPtr &key); QReadWriteLock *keyMutex() const; - const mtpAuthKeyPtr &getKey() const; - void setKey(const mtpAuthKeyPtr &key); + const AuthKeyPtr &getKey() const; + void setKey(const AuthKeyPtr &key); void destroyKey(); bool connectionInited() const { @@ -56,19 +59,19 @@ private: mutable QReadWriteLock keyLock; mutable QMutex initLock; int32 _id; - mtpAuthKeyPtr _key; + AuthKeyPtr _key; bool _connectionInited; }; -typedef QSharedPointer MTProtoDCPtr; -typedef QMap MTProtoDCMap; +typedef QSharedPointer DcenterPtr; +typedef QMap DcenterMap; -class MTProtoConfigLoader : public QObject { +class ConfigLoader : public QObject { Q_OBJECT public: - MTProtoConfigLoader(); + ConfigLoader(); void load(); void done(); @@ -88,21 +91,23 @@ private: }; -MTProtoConfigLoader *mtpConfigLoader(); -void mtpDestroyConfigLoader(); +ConfigLoader *configLoader(); +void destroyConfigLoader(); -MTProtoDCMap &mtpDCMap(); -bool mtpNeedConfig(); -int32 mtpMainDC(); -void mtpLogoutOtherDCs(); -void mtpSetDC(int32 dc, bool firstOnly = false); -uint32 mtpMaxChatSize(); +DcenterMap &DCMap(); +bool configNeeded(); +int32 mainDC(); +void logoutOtherDCs(); +void setDC(int32 dc, bool firstOnly = false); -int32 mtpAuthed(); -void mtpAuthed(int32 uid); +int32 authed(); +void authed(int32 uid); -mtpKeysMap mtpGetKeys(); -void mtpSetKey(int32 dc, mtpAuthKeyPtr key); +AuthKeysMap getAuthKeys(); +void setAuthKey(int32 dc, AuthKeyPtr key); -void mtpUpdateDcOptions(const QVector &options); -QReadWriteLock *mtpDcOptionsMutex(); +void updateDcOptions(const QVector &options); +QReadWriteLock *dcOptionsMutex(); + +} // namespace internal +} // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/facade.cpp b/Telegram/SourceFiles/mtproto/facade.cpp index bb98d37f0..f7cff78a7 100644 --- a/Telegram/SourceFiles/mtproto/facade.cpp +++ b/Telegram/SourceFiles/mtproto/facade.cpp @@ -249,7 +249,7 @@ namespace { } } int32 newdc = bareDcId(qAbs(dcWithShift)); - if (!newdc || newdc == mtpMainDC() || !authedId()) { + if (!newdc || newdc == internal::mainDC() || !authedId()) { if (!badGuestDC && globalHandler.onFail) (*globalHandler.onFail)(requestId, error); // auth failed in main dc return false; } @@ -649,17 +649,17 @@ void start() { unixtimeInit(); - MTProtoDCMap &dcs(mtpDCMap()); + internal::DcenterMap &dcs(internal::DCMap()); _globalSlotCarrier = new internal::GlobalSlotCarrier(); - mainSession = new internal::Session(mtpMainDC()); + mainSession = new internal::Session(internal::mainDC()); sessions.insert(mainSession->getDcWithShift(), mainSession); _started = true; - if (mtpNeedConfig()) { - mtpConfigLoader()->load(); + if (internal::configNeeded()) { + internal::configLoader()->load(); } } @@ -701,13 +701,13 @@ void unpause() { void configure(int32 dc, int32 user) { if (_started) return; - mtpSetDC(dc); - mtpAuthed(user); + internal::setDC(dc); + internal::authed(user); } void setdc(int32 dc, bool fromZeroOnly) { if (!dc || !_started) return; - mtpSetDC(dc, fromZeroOnly); + internal::setDC(dc, fromZeroOnly); int32 oldMainDc = mainSession->getDcWithShift(); if (maindc() != oldMainDc) { killSession(oldMainDc); @@ -716,7 +716,7 @@ void setdc(int32 dc, bool fromZeroOnly) { } int32 maindc() { - return mtpMainDC(); + return internal::mainDC(); } int32 dcstate(int32 dc) { @@ -789,7 +789,7 @@ void killSession(int32 dc) { sessions.erase(i); if (wasMain) { - mainSession = new internal::Session(mtpMainDC()); + mainSession = new internal::Session(internal::mainDC()); int32 newdc = mainSession->getDcWithShift(); i = sessions.find(newdc); if (i != sessions.cend()) { @@ -846,22 +846,22 @@ void finish() { delete _globalSlotCarrier; _globalSlotCarrier = nullptr; - mtpDestroyConfigLoader(); + internal::destroyConfigLoader(); _started = false; } void authed(int32 uid) { - mtpAuthed(uid); + internal::authed(uid); } int32 authedId() { - return mtpAuthed(); + return internal::authed(); } void logoutKeys(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail) { mtpRequestId req = MTP::send(MTPauth_LogOut(), onDone, onFail); - mtpLogoutOtherDCs(); + internal::logoutOtherDCs(); } void setGlobalDoneHandler(RPCDoneHandlerPtr handler) { @@ -888,20 +888,20 @@ void clearGlobalHandlers() { } void updateDcOptions(const QVector &options) { - mtpUpdateDcOptions(options); + internal::updateDcOptions(options); Local::writeSettings(); } -mtpKeysMap getKeys() { - return mtpGetKeys(); +AuthKeysMap getKeys() { + return internal::getAuthKeys(); } -void setKey(int32 dc, mtpAuthKeyPtr key) { - return mtpSetKey(dc, key); +void setKey(int32 dc, AuthKeyPtr key) { + return internal::setAuthKey(dc, key); } QReadWriteLock *dcOptionsMutex() { - return mtpDcOptionsMutex(); + return internal::dcOptionsMutex(); } } // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/facade.h b/Telegram/SourceFiles/mtproto/facade.h index 05990fde2..2c58d4d3f 100644 --- a/Telegram/SourceFiles/mtproto/facade.h +++ b/Telegram/SourceFiles/mtproto/facade.h @@ -192,8 +192,8 @@ void clearGlobalHandlers(); void updateDcOptions(const QVector &options); -mtpKeysMap getKeys(); -void setKey(int32 dc, mtpAuthKeyPtr key); +AuthKeysMap getKeys(); +void setKey(int32 dc, AuthKeyPtr key); QReadWriteLock *dcOptionsMutex(); diff --git a/Telegram/SourceFiles/mtproto/session.cpp b/Telegram/SourceFiles/mtproto/session.cpp index 9c4ad9c73..76c9de9cd 100644 --- a/Telegram/SourceFiles/mtproto/session.cpp +++ b/Telegram/SourceFiles/mtproto/session.cpp @@ -96,7 +96,7 @@ Session::Session(int32 dcenter) : QObject() connect(&sender, SIGNAL(timeout()), this, SLOT(needToResumeAndSend())); - MTProtoDCMap &dcs(mtpDCMap()); + DcenterMap &dcs(DCMap()); _connection = new Connection(); dcWithShift = _connection->start(&data, dcenter); @@ -109,16 +109,16 @@ Session::Session(int32 dcenter) : QObject() if (!dc) { dcenter = dcWithShift; int32 dcId = bareDcId(dcWithShift); - MTProtoDCMap::const_iterator dcIndex = dcs.constFind(dcId); + auto dcIndex = dcs.constFind(dcId); if (dcIndex == dcs.cend()) { - dc = MTProtoDCPtr(new MTProtoDC(dcId, mtpAuthKeyPtr())); + dc = DcenterPtr(new Dcenter(dcId, AuthKeyPtr())); dcs.insert(dcId, dc); } else { dc = dcIndex.value(); } ReadLockerAttempt lock(keyMutex()); - data.setKey(lock ? dc->getKey() : mtpAuthKeyPtr(0)); + data.setKey(lock ? dc->getKey() : AuthKeyPtr()); if (lock && dc->connectionInited()) { data.setLayerWasInited(true); } @@ -197,7 +197,7 @@ void Session::needToResumeAndSend() { } if (!_connection) { DEBUG_LOG(("Session Info: resuming session dcWithShift %1").arg(dcWithShift)); - MTProtoDCMap &dcs(mtpDCMap()); + DcenterMap &dcs(DCMap()); _connection = new Connection(); if (!_connection->start(&data, dcWithShift)) { @@ -466,7 +466,7 @@ void Session::authKeyCreatedForDC() { emit authKeyCreated(); } -void Session::notifyKeyCreated(const mtpAuthKeyPtr &key) { +void Session::notifyKeyCreated(const AuthKeyPtr &key) { DEBUG_LOG(("AuthKey Info: MTProtoSession::keyCreated(), setting, dcWithShift %1").arg(dcWithShift)); dc->setKey(key); } @@ -490,7 +490,7 @@ void Session::destroyKey() { if (data.getKey() == dc->getKey()) { dc->destroyKey(); } - data.setKey(mtpAuthKeyPtr(0)); + data.setKey(AuthKeyPtr()); } } diff --git a/Telegram/SourceFiles/mtproto/session.h b/Telegram/SourceFiles/mtproto/session.h index 39a15acc5..f46694e3a 100644 --- a/Telegram/SourceFiles/mtproto/session.h +++ b/Telegram/SourceFiles/mtproto/session.h @@ -73,10 +73,10 @@ public: return _salt; } - const mtpAuthKeyPtr &getKey() const { + const AuthKeyPtr &getKey() const { return _authKey; } - void setKey(const mtpAuthKeyPtr &key) { + void setKey(const AuthKeyPtr &key) { if (_authKey != key) { uint64 session = rand_value(); _authKey = key; @@ -200,7 +200,7 @@ private: Session *_owner; - mtpAuthKeyPtr _authKey; + AuthKeyPtr _authKey; bool _keyChecked, _layerInited; mtpPreRequestMap toSend; // map of request_id -> request, that is waiting to be sent @@ -240,7 +240,7 @@ public: ~Session(); QReadWriteLock *keyMutex() const; - void notifyKeyCreated(const mtpAuthKeyPtr &key); + void notifyKeyCreated(const AuthKeyPtr &key); void destroyKey(); void notifyLayerInited(bool wasInited); @@ -292,7 +292,7 @@ private: SessionData data; int32 dcWithShift; - MTProtoDCPtr dc; + DcenterPtr dc; uint64 msSendCall, msWait; diff --git a/Telegram/SourceFiles/pspecific_linux.cpp b/Telegram/SourceFiles/pspecific_linux.cpp index 2134e97dd..9e00f902d 100644 --- a/Telegram/SourceFiles/pspecific_linux.cpp +++ b/Telegram/SourceFiles/pspecific_linux.cpp @@ -33,12 +33,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include -#undef signals extern "C" { - #include - #include -} +#undef signals +#include +#include #define signals public +} // extern "C" #include diff --git a/Telegram/SourceFiles/pspecific_win.h b/Telegram/SourceFiles/pspecific_win.h index 0f149ad29..e6303f27f 100644 --- a/Telegram/SourceFiles/pspecific_win.h +++ b/Telegram/SourceFiles/pspecific_win.h @@ -21,6 +21,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #pragma once +#include + inline QString psServerPrefix() { return qsl("Global\\"); } diff --git a/Telegram/SourceFiles/stdafx.h b/Telegram/SourceFiles/stdafx.h index e88e760af..12885f468 100644 --- a/Telegram/SourceFiles/stdafx.h +++ b/Telegram/SourceFiles/stdafx.h @@ -20,51 +20,15 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #define NOMINMAX // no min() and max() macro declarations - -#ifdef TDESKTOP_WINRT - -#include -#include - -#else // TDESKTOP_WINRT - #define __HUGE -#define PSAPI_VERSION 1 // fix WinXP - #define __STDC_FORMAT_MACROS // fix breakpad for mac -#endif // else of TDESKTOP_WINRT - #ifdef __cplusplus -#include - #include #include #include -#ifdef Q_OS_WIN // use Lzma SDK for win -#include -#else // Q_OS_WIN -#include -#endif // else of Q_OS_WIN - -extern "C" { - -#endif - -#include "zip.h" - -#include -#include -#include -#include -#include - -#ifdef __cplusplus - -} - #include "types.h" #include "config.h" @@ -85,4 +49,4 @@ extern "C" { #include "app.h" -#endif +#endif // __cplusplus diff --git a/Telegram/SourceFiles/types.cpp b/Telegram/SourceFiles/types.cpp index f851dc231..523b02897 100644 --- a/Telegram/SourceFiles/types.cpp +++ b/Telegram/SourceFiles/types.cpp @@ -25,6 +25,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include #include +extern "C" { +#include +#include +} + #include "application.h" uint64 _SharedMemoryLocation[4] = { 0x00, 0x01, 0x02, 0x03 }; diff --git a/Telegram/SourceFiles/window.cpp b/Telegram/SourceFiles/window.cpp index 8d7cbbad4..4e7c413fe 100644 --- a/Telegram/SourceFiles/window.cpp +++ b/Telegram/SourceFiles/window.cpp @@ -19,14 +19,15 @@ Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org */ #include "stdafx.h" -#include "style.h" -#include "lang.h" - -#include "shortcuts.h" #include "window.h" -#include "application.h" +#include "zip.h" + +#include "style.h" +#include "lang.h" +#include "shortcuts.h" +#include "application.h" #include "pspecific.h" #include "title.h" #include "passcodewidget.h" @@ -37,9 +38,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org #include "boxes/confirmbox.h" #include "boxes/contactsbox.h" #include "boxes/addcontactbox.h" - #include "autoupdater.h" - #include "mediaview.h" #include "localstorage.h"