all deinit moved to Application::aboutToQuit(), mtproto connection thread management refactored, disabled -style=0 fake argument for Application, beta 9028002

This commit is contained in:
John Preston 2016-02-29 19:53:26 +03:00
parent 08072346fd
commit 99b52d4cc1
21 changed files with 300 additions and 270 deletions

View File

@ -1,4 +1,5 @@
@echo OFF @echo OFF
setlocal
FOR /F "tokens=1,2* delims= " %%i in (Version) do set "%%i=%%j" FOR /F "tokens=1,2* delims= " %%i in (Version) do set "%%i=%%j"

View File

@ -255,7 +255,7 @@ bool update() {
} else { } else {
break; break;
} }
} while (copyTries < 30); } while (copyTries < 100);
if (!copyResult) { if (!copyResult) {
writeLog(L"Error: failed to copy, asking to retry.."); writeLog(L"Error: failed to copy, asking to retry..");
WCHAR errMsg[2048]; WCHAR errMsg[2048];

View File

@ -110,6 +110,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
connect(&_localSocket, SIGNAL(readyRead()), this, SLOT(socketReading())); connect(&_localSocket, SIGNAL(readyRead()), this, SLOT(socketReading()));
connect(&_localServer, SIGNAL(newConnection()), this, SLOT(newInstanceConnected())); connect(&_localServer, SIGNAL(newConnection()), this, SLOT(newInstanceConnected()));
QTimer::singleShot(0, this, SLOT(startApplication()));
connect(this, SIGNAL(aboutToQuit()), this, SLOT(closeApplication())); connect(this, SIGNAL(aboutToQuit()), this, SLOT(closeApplication()));
#ifndef TDESKTOP_DISABLE_AUTOUPDATE #ifndef TDESKTOP_DISABLE_AUTOUPDATE
@ -127,27 +128,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
} }
} }
Application::~Application() {
App::setQuiting();
Sandbox::finish();
delete AppObject;
_localSocket.close();
closeApplication();
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
delete _updateReply;
_updateReply = 0;
if (_updateChecker) _updateChecker->deleteLater();
_updateChecker = 0;
if (_updateThread) _updateThread->quit();
_updateThread = 0;
#endif
}
void Application::socketConnected() { void Application::socketConnected() {
LOG(("Socket connected, this is not the first application instance, sending show command..")); LOG(("Socket connected, this is not the first application instance, sending show command.."));
_secondInstance = true; _secondInstance = true;
@ -333,13 +313,54 @@ void Application::removeClients() {
} }
} }
void Application::startApplication() {
if (App::quiting()) {
quit();
}
}
void Application::closeApplication() { void Application::closeApplication() {
App::quit();
delete AppObject;
AppObject = 0;
Sandbox::finish();
_localServer.close(); _localServer.close();
for (LocalClients::iterator i = _localClients.begin(), e = _localClients.end(); i != e; ++i) { for (LocalClients::iterator i = _localClients.begin(), e = _localClients.end(); i != e; ++i) {
disconnect(i->first, SIGNAL(disconnected()), this, SLOT(removeClients())); disconnect(i->first, SIGNAL(disconnected()), this, SLOT(removeClients()));
i->first->close(); i->first->close();
} }
_localClients.clear(); _localClients.clear();
_localSocket.close();
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
delete _updateReply;
_updateReply = 0;
if (_updateChecker) _updateChecker->deleteLater();
_updateChecker = 0;
if (_updateThread) _updateThread->quit();
_updateThread = 0;
#endif
DEBUG_LOG(("Telegram finished, result: %1").arg("unknown"));
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
if (cRestartingUpdate()) {
DEBUG_LOG(("Application Info: executing updater to install update.."));
psExecUpdater();
} else
#endif
if (cRestarting()) {
DEBUG_LOG(("Application Info: executing Telegram, because of restart.."));
psExecTelegram();
}
SignalHandlers::finish();
PlatformSpecific::finish();
Logs::finish();
} }
#ifndef TDESKTOP_DISABLE_AUTOUPDATE #ifndef TDESKTOP_DISABLE_AUTOUPDATE
@ -899,7 +920,7 @@ void AppClass::killDownloadSessions() {
for (QMap<int32, uint64>::iterator i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) { for (QMap<int32, uint64>::iterator i = killDownloadSessionTimes.begin(); i != killDownloadSessionTimes.end(); ) {
if (i.value() <= ms) { if (i.value() <= ms) {
for (int j = 0; j < MTPDownloadSessionsCount; ++j) { for (int j = 0; j < MTPDownloadSessionsCount; ++j) {
MTP::stopSession(MTP::dld[j] + i.key()); MTP::stopSession(MTP::dld(j) + i.key());
} }
i = killDownloadSessionTimes.erase(i); i = killDownloadSessionTimes.erase(i);
} else { } else {

View File

@ -30,7 +30,6 @@ class Application : public QApplication {
public: public:
Application(int &argc, char **argv); Application(int &argc, char **argv);
~Application();
// Single instance application // Single instance application
public slots: public slots:
@ -41,11 +40,13 @@ public slots:
void socketWritten(qint64 bytes); void socketWritten(qint64 bytes);
void socketReading(); void socketReading();
void newInstanceConnected(); void newInstanceConnected();
void closeApplication();
void readClients(); void readClients();
void removeClients(); void removeClients();
void startApplication(); // will be done in exec()
void closeApplication(); // will be done in aboutToQuit()
private: private:
typedef QPair<QLocalSocket*, QByteArray> LocalClient; typedef QPair<QLocalSocket*, QByteArray> LocalClient;

View File

@ -23,7 +23,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
static const int32 AppVersion = 9028; static const int32 AppVersion = 9028;
static const wchar_t *AppVersionStr = L"0.9.28"; static const wchar_t *AppVersionStr = L"0.9.28";
static const bool DevVersion = false; static const bool DevVersion = false;
#define BETA_VERSION (9028001ULL) // just comment this line to build public version #define BETA_VERSION (9028002ULL) // just comment this line to build public version
static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)"; static const wchar_t *AppNameOld = L"Telegram Win (Unofficial)";
static const wchar_t *AppName = L"Telegram Desktop"; static const wchar_t *AppName = L"Telegram Desktop";

View File

@ -292,7 +292,7 @@ namespace SignalHandlers {
namespace Logs { namespace Logs {
Initializer::Initializer() { void start() {
t_assert(LogsData == 0); t_assert(LogsData == 0);
if (!Sandbox::CheckBetaVersionDir()) { if (!Sandbox::CheckBetaVersionDir()) {
@ -378,7 +378,7 @@ namespace Logs {
LOG(("Logs started")); LOG(("Logs started"));
} }
Initializer::~Initializer() { void finish() {
delete LogsData; delete LogsData;
LogsData = 0; LogsData = 0;

View File

@ -23,11 +23,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
class MTPlong; class MTPlong;
namespace Logs { namespace Logs {
struct Initializer { void start();
Initializer();
~Initializer();
};
bool started(); bool started();
void finish();
bool instanceChecked(); bool instanceChecked();
void multipleInstances(); void multipleInstances();

View File

@ -25,8 +25,6 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "localstorage.h" #include "localstorage.h"
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
int result = 0;
settingsParseArgs(argc, argv); settingsParseArgs(argc, argv);
if (cLaunchMode() == LaunchModeFixPrevious) { if (cLaunchMode() == LaunchModeFixPrevious) {
return psFixPrevious(); return psFixPrevious();
@ -36,34 +34,15 @@ int main(int argc, char *argv[]) {
return showCrashReportWindow(QFileInfo(cStartUrl()).absoluteFilePath()); return showCrashReportWindow(QFileInfo(cStartUrl()).absoluteFilePath());
} }
Logs::Initializer _logs; // both are finished in Application::closeApplication
{ Logs::start(); // must be started before PlatformSpecific is started
PlatformSpecific::Initializer _ps; PlatformSpecific::start(); // must be started before QApplication is created
QByteArray args[] = { "-style=0" }; // prepare fake args to disable QT_STYLE_OVERRIDE env variable //QByteArray args[] = { "-style=0" }; // prepare fake args to disable QT_STYLE_OVERRIDE env variable
static const int a_cnt = sizeof(args) / sizeof(args[0]); //static const int a_cnt = sizeof(args) / sizeof(args[0]);
int a_argc = a_cnt + 1; //int a_argc = a_cnt + 1;
char *a_argv[a_cnt + 1] = { argv[0], args[0].data() }; //char *a_argv[a_cnt + 1] = { argv[0], args[0].data() };
Application app(a_argc, a_argv); Application app(argc, argv);
if (!App::quiting()) { return app.exec();
result = app.exec();
}
}
DEBUG_LOG(("Telegram finished, result: %1").arg(result));
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
if (cRestartingUpdate()) {
DEBUG_LOG(("Application Info: executing updater to install update.."));
psExecUpdater();
} else
#endif
if (cRestarting()) {
DEBUG_LOG(("Application Info: executing Telegram, because of restart.."));
psExecTelegram();
}
SignalHandlers::finish();
return result;
} }

View File

@ -24,10 +24,9 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "localstorage.h" #include "localstorage.h"
namespace { namespace {
typedef QMap<int32, MTProtoSessionPtr> Sessions; typedef QMap<int32, MTProtoSession*> Sessions;
Sessions sessions; Sessions sessions;
QVector<MTProtoSessionPtr> sessionsToKill; MTProtoSession *mainSession;
MTProtoSessionPtr mainSession;
typedef QMap<mtpRequestId, int32> RequestsByDC; // holds dcWithShift for request to this dc or -dc for request to main dc typedef QMap<mtpRequestId, int32> RequestsByDC; // holds dcWithShift for request to this dc or -dc for request to main dc
RequestsByDC requestsByDC; RequestsByDC requestsByDC;
@ -62,13 +61,16 @@ namespace {
typedef QMap<int32, DCAuthWaiters> AuthWaiters; // holds request ids waiting for auth import to specific dc typedef QMap<int32, DCAuthWaiters> AuthWaiters; // holds request ids waiting for auth import to specific dc
AuthWaiters authWaiters; AuthWaiters authWaiters;
typedef OrderedSet<MTProtoConnection*> MTPQuittingConnections;
MTPQuittingConnections quittingConnections;
QMutex toClearLock; QMutex toClearLock;
RPCCallbackClears toClear; RPCCallbackClears toClear;
RPCResponseHandler globalHandler; RPCResponseHandler globalHandler;
MTPStateChangedHandler stateChangedHandler = 0; MTPStateChangedHandler stateChangedHandler = 0;
MTPSessionResetHandler sessionResetHandler = 0; MTPSessionResetHandler sessionResetHandler = 0;
_mtp_internal::RequestResender *resender = 0; _mtp_internal::GlobalSlotCarrier *_globalSlotCarrier = 0;
void importDone(const MTPauth_Authorization &result, mtpRequestId req) { void importDone(const MTPauth_Authorization &result, mtpRequestId req) {
QMutexLocker locker1(&requestByDCLock); QMutexLocker locker1(&requestByDCLock);
@ -111,7 +113,7 @@ namespace {
} }
DEBUG_LOG(("MTP Info: resending request %1 to dc %2 after import auth").arg(requestId).arg(k.value())); DEBUG_LOG(("MTP Info: resending request %1 to dc %2 after import auth").arg(requestId).arg(k.value()));
} }
if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift)) { if (MTProtoSession *session = _mtp_internal::getSession(dcWithShift)) {
session->sendPrepared(j.value()); session->sendPrepared(j.value());
} }
} }
@ -202,7 +204,7 @@ namespace {
} }
req = i.value(); req = i.value();
} }
if (MTProtoSessionPtr session = _mtp_internal::getSession(newdcWithShift)) { if (MTProtoSession *session = _mtp_internal::getSession(newdcWithShift)) {
_mtp_internal::registerRequest(requestId, (dcWithShift < 0) ? -newdcWithShift : newdcWithShift); _mtp_internal::registerRequest(requestId, (dcWithShift < 0) ? -newdcWithShift : newdcWithShift);
session->sendPrepared(req); session->sendPrepared(req);
} }
@ -230,7 +232,7 @@ namespace {
} }
delayedRequests.insert(i, DelayedRequest(requestId, sendAt)); delayedRequests.insert(i, DelayedRequest(requestId, sendAt));
if (resender) resender->checkDelayed(); if (_globalSlotCarrier) _globalSlotCarrier->checkDelayed();
return true; return true;
} else if (code == 401 || (badGuestDC && badGuestDCRequests.constFind(requestId) == badGuestDCRequests.cend())) { } else if (code == 401 || (badGuestDC && badGuestDCRequests.constFind(requestId) == badGuestDCRequests.cend())) {
@ -281,7 +283,7 @@ namespace {
} }
if (!dcWithShift) return false; if (!dcWithShift) return false;
if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift < 0 ? (-dcWithShift) : dcWithShift)) { if (MTProtoSession *session = _mtp_internal::getSession(dcWithShift < 0 ? (-dcWithShift) : dcWithShift)) {
req->needsLayer = true; req->needsLayer = true;
session->sendPrepared(req); session->sendPrepared(req);
} }
@ -319,7 +321,7 @@ namespace {
if (!dcWithShift) return false; if (!dcWithShift) return false;
if (!req->after) { if (!req->after) {
if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift < 0 ? (-dcWithShift) : dcWithShift)) { if (MTProtoSession *session = _mtp_internal::getSession(dcWithShift < 0 ? (-dcWithShift) : dcWithShift)) {
req->needsLayer = true; req->needsLayer = true;
session->sendPrepared(req); session->sendPrepared(req);
} }
@ -346,7 +348,7 @@ namespace {
delayedRequests.insert(i, DelayedRequest(requestId, i->second)); delayedRequests.insert(i, DelayedRequest(requestId, i->second));
} }
if (resender) resender->checkDelayed(); if (_globalSlotCarrier) _globalSlotCarrier->checkDelayed();
} }
} }
return true; return true;
@ -360,21 +362,18 @@ namespace {
} }
namespace _mtp_internal { namespace _mtp_internal {
MTProtoSessionPtr getSession(int32 dcWithShift) { MTProtoSession *getSession(int32 dcWithShift) {
if (!_started) return MTProtoSessionPtr(); if (!_started) return 0;
if (!dcWithShift) return mainSession; if (!dcWithShift) return mainSession;
if (!(dcWithShift % _mtp_internal::dcShift)) { if (!(dcWithShift % _mtp_internal::dcShift)) {
dcWithShift += (mainSession->getDcWithShift() % _mtp_internal::dcShift); dcWithShift += (mainSession->getDcWithShift() % _mtp_internal::dcShift);
} }
Sessions::const_iterator i = sessions.constFind(dcWithShift); Sessions::const_iterator i = sessions.constFind(dcWithShift);
if (i != sessions.cend()) return *i; if (i == sessions.cend()) {
i = sessions.insert(dcWithShift, new MTProtoSession(dcWithShift));
MTProtoSessionPtr result(new MTProtoSession()); }
result->start(dcWithShift); return i.value();
sessions.insert(dcWithShift, result);
return result;
} }
bool paused() { bool paused() {
@ -580,11 +579,11 @@ namespace _mtp_internal {
return true; return true;
} }
RequestResender::RequestResender() { GlobalSlotCarrier::GlobalSlotCarrier() {
connect(&_timer, SIGNAL(timeout()), this, SLOT(checkDelayed())); connect(&_timer, SIGNAL(timeout()), this, SLOT(checkDelayed()));
} }
void RequestResender::checkDelayed() { void GlobalSlotCarrier::checkDelayed() {
uint64 now = getms(true); uint64 now = getms(true);
while (!delayedRequests.isEmpty() && now >= delayedRequests.front().second) { while (!delayedRequests.isEmpty() && now >= delayedRequests.front().second) {
mtpRequestId requestId = delayedRequests.front().first; mtpRequestId requestId = delayedRequests.front().first;
@ -612,7 +611,7 @@ namespace _mtp_internal {
} }
req = j.value(); req = j.value();
} }
if (MTProtoSessionPtr session = _mtp_internal::getSession(dcWithShift < 0 ? (-dcWithShift) : dcWithShift)) { if (MTProtoSession *session = _mtp_internal::getSession(dcWithShift < 0 ? (-dcWithShift) : dcWithShift)) {
session->sendPrepared(req); session->sendPrepared(req);
} }
} }
@ -621,6 +620,25 @@ namespace _mtp_internal {
_timer.start(delayedRequests.front().second - now); _timer.start(delayedRequests.front().second - now);
} }
} }
void GlobalSlotCarrier::connectionFinished(MTProtoConnection *connection) {
MTPQuittingConnections::iterator i = quittingConnections.find(connection);
if (i != quittingConnections.cend()) {
quittingConnections.erase(i);
}
connection->waitTillFinish();
delete connection;
}
GlobalSlotCarrier *globalSlotCarrier() {
return _globalSlotCarrier;
}
void queueQuittingConnection(MTProtoConnection *connection) {
quittingConnections.insert(connection);
}
}; };
namespace MTP { namespace MTP {
@ -637,12 +655,12 @@ namespace MTP {
MTProtoDCMap &dcs(mtpDCMap()); MTProtoDCMap &dcs(mtpDCMap());
mainSession = MTProtoSessionPtr(new MTProtoSession()); _globalSlotCarrier = new _mtp_internal::GlobalSlotCarrier();
mainSession->start(mtpMainDC());
sessions[mainSession->getDcWithShift()] = mainSession; mainSession = new MTProtoSession(mtpMainDC());
sessions.insert(mainSession->getDcWithShift(), mainSession);
_started = true; _started = true;
resender = new _mtp_internal::RequestResender();
if (mtpNeedConfig()) { if (mtpNeedConfig()) {
mtpConfigLoader()->load(); mtpConfigLoader()->load();
@ -657,7 +675,7 @@ namespace MTP {
if (!_started) return; if (!_started) return;
for (Sessions::const_iterator i = sessions.cbegin(), e = sessions.cend(); i != e; ++i) { for (Sessions::const_iterator i = sessions.cbegin(), e = sessions.cend(); i != e; ++i) {
(*i)->restart(); i.value()->restart();
} }
} }
@ -666,8 +684,8 @@ namespace MTP {
dcMask %= _mtp_internal::dcShift; dcMask %= _mtp_internal::dcShift;
for (Sessions::const_iterator i = sessions.cbegin(), e = sessions.cend(); i != e; ++i) { for (Sessions::const_iterator i = sessions.cbegin(), e = sessions.cend(); i != e; ++i) {
if (((*i)->getDcWithShift() % int(_mtp_internal::dcShift)) == dcMask) { if ((i.value()->getDcWithShift() % int(_mtp_internal::dcShift)) == dcMask) {
(*i)->restart(); i.value()->restart();
} }
} }
} }
@ -681,7 +699,7 @@ namespace MTP {
if (!_started) return; if (!_started) return;
_paused = false; _paused = false;
for (Sessions::const_iterator i = sessions.cbegin(), e = sessions.cend(); i != e; ++i) { for (Sessions::const_iterator i = sessions.cbegin(), e = sessions.cend(); i != e; ++i) {
(*i)->unpaused(); i.value()->unpaused();
} }
} }
@ -714,7 +732,7 @@ namespace MTP {
} }
Sessions::const_iterator i = sessions.constFind(dc); Sessions::const_iterator i = sessions.constFind(dc);
if (i != sessions.cend()) return (*i)->getState(); if (i != sessions.cend()) return i.value()->getState();
return MTProtoConnection::Disconnected; return MTProtoConnection::Disconnected;
} }
@ -728,13 +746,13 @@ namespace MTP {
} }
Sessions::const_iterator i = sessions.constFind(dc); Sessions::const_iterator i = sessions.constFind(dc);
if (i != sessions.cend()) return (*i)->transport(); if (i != sessions.cend()) return i.value()->transport();
return QString(); return QString();
} }
void ping() { void ping() {
if (MTProtoSessionPtr session = _mtp_internal::getSession(0)) { if (MTProtoSession *session = _mtp_internal::getSession(0)) {
session->ping(); session->ping();
} }
} }
@ -754,7 +772,7 @@ namespace MTP {
QMutexLocker locker(&requestByDCLock); QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(requestId); RequestsByDC::iterator i = requestsByDC.find(requestId);
if (i != requestsByDC.end()) { if (i != requestsByDC.end()) {
if (MTProtoSessionPtr session = _mtp_internal::getSession(abs(i.value()))) { if (MTProtoSession *session = _mtp_internal::getSession(abs(i.value()))) {
session->cancel(requestId, msgId); session->cancel(requestId, msgId);
} }
requestsByDC.erase(i); requestsByDC.erase(i);
@ -763,26 +781,25 @@ namespace MTP {
_mtp_internal::clearCallbacks(requestId); _mtp_internal::clearCallbacks(requestId);
} }
void killSessionsDelayed() {
if (!sessionsToKill.isEmpty()) {
sessionsToKill.clear();
}
}
void killSession(int32 dc) { void killSession(int32 dc) {
Sessions::iterator i = sessions.find(dc); Sessions::iterator i = sessions.find(dc);
if (i != sessions.end()) { if (i != sessions.cend()) {
bool wasMain = (i.value() == mainSession); bool wasMain = (i.value() == mainSession);
i.value()->kill(); i.value()->kill();
if (sessionsToKill.isEmpty()) QTimer::singleShot(0, killSessionsDelayed); i.value()->deleteLater();
sessionsToKill.push_back(i.value());
sessions.erase(i); sessions.erase(i);
if (wasMain) { if (wasMain) {
mainSession = MTProtoSessionPtr(new MTProtoSession()); mainSession = new MTProtoSession(mtpMainDC());
mainSession->start(mtpMainDC()); int32 newdc = mainSession->getDcWithShift();
sessions[mainSession->getDcWithShift()] = mainSession; i = sessions.find(newdc);
if (i != sessions.cend()) {
i.value()->kill();
i.value()->deleteLater();
sessions.erase(i);
}
sessions.insert(newdc, mainSession);
} }
} }
} }
@ -801,14 +818,14 @@ namespace MTP {
QMutexLocker locker(&requestByDCLock); QMutexLocker locker(&requestByDCLock);
RequestsByDC::iterator i = requestsByDC.find(requestId); RequestsByDC::iterator i = requestsByDC.find(requestId);
if (i != requestsByDC.end()) { if (i != requestsByDC.end()) {
if (MTProtoSessionPtr session = _mtp_internal::getSession(abs(i.value()))) { if (MTProtoSession *session = _mtp_internal::getSession(abs(i.value()))) {
return session->requestState(requestId); return session->requestState(requestId);
} }
return MTP::RequestConnecting; return MTP::RequestConnecting;
} }
return MTP::RequestSent; return MTP::RequestSent;
} }
if (MTProtoSessionPtr session = _mtp_internal::getSession(-requestId)) { if (MTProtoSession *session = _mtp_internal::getSession(-requestId)) {
return session->requestState(0); return session->requestState(0);
} }
return MTP::RequestConnecting; return MTP::RequestConnecting;
@ -817,11 +834,20 @@ namespace MTP {
void stop() { void stop() {
for (Sessions::iterator i = sessions.begin(), e = sessions.end(); i != e; ++i) { for (Sessions::iterator i = sessions.begin(), e = sessions.end(); i != e; ++i) {
i.value()->kill(); i.value()->kill();
delete i.value();
} }
sessions.clear(); sessions.clear();
mainSession = MTProtoSessionPtr(); mainSession = nullptr;
delete resender;
resender = 0; for (MTPQuittingConnections::const_iterator i = quittingConnections.cbegin(), e = quittingConnections.cend(); i != e; ++i) {
i.key()->waitTillFinish();
delete i.key();
}
quittingConnections.clear();
delete _globalSlotCarrier;
_globalSlotCarrier = nullptr;
mtpDestroyConfigLoader(); mtpDestroyConfigLoader();
_started = false; _started = false;

View File

@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
#include "mtproto/mtpFileLoader.h" #include "mtproto/mtpFileLoader.h"
namespace _mtp_internal { namespace _mtp_internal {
MTProtoSessionPtr getSession(int32 dc = 0); // 0 - current set dc MTProtoSession *getSession(int32 dc); // 0 - current set dc
bool paused(); bool paused();
@ -49,21 +49,28 @@ namespace _mtp_internal {
return rpcErrorOccured(requestId, handler.onFail, err); return rpcErrorOccured(requestId, handler.onFail, err);
} }
class RequestResender : public QObject { // used for:
// - resending requests by timer which were postponed by flood delay
// - destroying MTProtoConnections whose thread has finished
class GlobalSlotCarrier : public QObject {
Q_OBJECT Q_OBJECT
public: public:
RequestResender(); GlobalSlotCarrier();
public slots: public slots:
void checkDelayed(); void checkDelayed();
void connectionFinished(MTProtoConnection *connection);
private: private:
SingleTimer _timer; SingleTimer _timer;
}; };
GlobalSlotCarrier *globalSlotCarrier();
void queueQuittingConnection(MTProtoConnection *connection);
}; };
namespace MTP { namespace MTP {
@ -99,7 +106,7 @@ namespace MTP {
template <typename TRequest> template <typename TRequest>
inline mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), int32 dc = 0, uint64 msCanWait = 0, mtpRequestId after = 0) { inline mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), int32 dc = 0, uint64 msCanWait = 0, mtpRequestId after = 0) {
if (MTProtoSessionPtr session = _mtp_internal::getSession(dc)) { if (MTProtoSession *session = _mtp_internal::getSession(dc)) {
return session->send(request, callbacks, msCanWait, true, !dc, after); return session->send(request, callbacks, msCanWait, true, !dc, after);
} }
return 0; return 0;
@ -109,7 +116,7 @@ namespace MTP {
return send(request, RPCResponseHandler(onDone, onFail), dc, msCanWait, after); return send(request, RPCResponseHandler(onDone, onFail), dc, msCanWait, after);
} }
inline void sendAnything(int32 dc = 0, uint64 msCanWait = 0) { inline void sendAnything(int32 dc = 0, uint64 msCanWait = 0) {
if (MTProtoSessionPtr session = _mtp_internal::getSession(dc)) { if (MTProtoSession *session = _mtp_internal::getSession(dc)) {
return session->sendAnything(msCanWait); return session->sendAnything(msCanWait);
} }
} }

View File

@ -252,8 +252,6 @@ namespace {
typedef QMap<uint64, mtpPublicRSA> PublicRSAKeys; typedef QMap<uint64, mtpPublicRSA> PublicRSAKeys;
PublicRSAKeys gPublicRSA; PublicRSAKeys gPublicRSA;
MTProtoConnection gMainConnection;
bool gConfigInited = false; bool gConfigInited = false;
void initRSAConfig() { void initRSAConfig() {
if (gConfigInited) return; if (gConfigInited) return;
@ -277,66 +275,75 @@ namespace {
} }
} }
MTPThread::MTPThread(QObject *parent) : QThread(parent) { uint32 MTPThreadIdIncrement = 0;
static uint32 gThreadId = 0;
threadId = ++gThreadId; MTPThread::MTPThread() : QThread(0)
, _threadId(++MTPThreadIdIncrement) {
} }
uint32 MTPThread::getThreadId() const { uint32 MTPThread::getThreadId() const {
return threadId; return _threadId;
} }
MTProtoConnection::MTProtoConnection() : thread(0), data(0) { MTPThread::~MTPThread() {
}
MTProtoConnection::MTProtoConnection() : thread(nullptr), data(nullptr) {
} }
int32 MTProtoConnection::start(MTPSessionData *sessionData, int32 dc) { int32 MTProtoConnection::start(MTPSessionData *sessionData, int32 dc) {
t_assert(thread == nullptr && data == nullptr);
initRSAConfig(); initRSAConfig();
if (thread) { thread = new MTPThread();
DEBUG_LOG(("MTP Info: MTP start called for already working connection"));
return dc;
}
thread = new MTPThread(QApplication::instance());
data = new MTProtoConnectionPrivate(thread, this, sessionData, dc); data = new MTProtoConnectionPrivate(thread, this, sessionData, dc);
dc = data->getDC(); dc = data->getDC();
if (!dc) { if (!dc) {
delete data; delete data;
data = nullptr;
delete thread; delete thread;
data = 0; thread = nullptr;
thread = 0;
return 0; return 0;
} }
thread->start(); thread->start();
return dc; return dc;
} }
void MTProtoConnection::stop() { void MTProtoConnection::kill() {
if (data) data->stop(); t_assert(data != nullptr && thread != nullptr);
if (thread) thread->quit(); data->stop();
data = nullptr; // will be deleted in thread::finished signal
thread->quit();
_mtp_internal::queueQuittingConnection(this);
} }
void MTProtoConnection::stopped() { void MTProtoConnection::waitTillFinish() {
if (thread) thread->deleteLater(); t_assert(data == nullptr && thread != nullptr);
if (data) data->deleteLater();
thread = 0; thread->wait();
data = 0; delete thread;
delete this; thread = nullptr;
} }
int32 MTProtoConnection::state() const { int32 MTProtoConnection::state() const {
if (!data) return Disconnected; t_assert(data != nullptr && thread != nullptr);
return data->getState(); return data->getState();
} }
QString MTProtoConnection::transport() const { QString MTProtoConnection::transport() const {
if (!data) return QString(); t_assert(data != nullptr && thread != nullptr);
return data->transport(); return data->transport();
} }
MTProtoConnection::~MTProtoConnection() {
t_assert(data == nullptr && thread == nullptr);
}
namespace { namespace {
mtpBuffer _handleHttpResponse(QNetworkReply *reply) { mtpBuffer _handleHttpResponse(QNetworkReply *reply) {
QByteArray response = reply->readAll(); QByteArray response = reply->readAll();
@ -1311,30 +1318,31 @@ void MTProtoConnectionPrivate::destroyConn(MTPabstractConnection **conn) {
} }
} }
MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConnection *owner, MTPSessionData *data, uint32 _dc) MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConnection *owner, MTPSessionData *data, uint32 _dc) : QObject(0)
: QObject(0) , _state(MTProtoConnection::Disconnected)
, _state(MTProtoConnection::Disconnected) , _needSessionReset(false)
, _needSessionReset(false) , dc(_dc)
, dc(_dc) , _owner(owner)
, _owner(owner) , _conn(0)
, _conn(0), _conn4(0), _conn6(0) , _conn4(0)
, retryTimeout(1) , _conn6(0)
, oldConnection(true) , retryTimeout(1)
, _waitForReceived(MTPMinReceiveDelay) , oldConnection(true)
, _waitForConnected(MTPMinConnectDelay) , _waitForReceived(MTPMinReceiveDelay)
, firstSentAt(-1) , _waitForConnected(MTPMinConnectDelay)
, _pingId(0) , firstSentAt(-1)
, _pingIdToSend(0) , _pingId(0)
, _pingSendAt(0) , _pingIdToSend(0)
, _pingMsgId(0) , _pingSendAt(0)
, restarted(false) , _pingMsgId(0)
, keyId(0) , restarted(false)
, _finished(false)
, keyId(0)
// , sessionDataMutex(QReadWriteLock::Recursive) // , sessionDataMutex(QReadWriteLock::Recursive)
, sessionData(data) , sessionData(data)
, myKeyLock(false) , myKeyLock(false)
, authKeyData(0) , authKeyData(0)
, authKeyStrings(0) { , authKeyStrings(0) {
oldConnectionTimer.moveToThread(thread); oldConnectionTimer.moveToThread(thread);
_waitForConnectedTimer.moveToThread(thread); _waitForConnectedTimer.moveToThread(thread);
_waitForReceivedTimer.moveToThread(thread); _waitForReceivedTimer.moveToThread(thread);
@ -1357,6 +1365,7 @@ MTProtoConnectionPrivate::MTProtoConnectionPrivate(QThread *thread, MTProtoConne
connect(thread, SIGNAL(started()), this, SLOT(socketStart())); connect(thread, SIGNAL(started()), this, SLOT(socketStart()));
connect(thread, SIGNAL(finished()), this, SLOT(doFinish())); connect(thread, SIGNAL(finished()), this, SLOT(doFinish()));
connect(this, SIGNAL(finished(MTProtoConnection*)), _mtp_internal::globalSlotCarrier(), SLOT(connectionFinished(MTProtoConnection*)), Qt::QueuedConnection);
connect(&retryTimer, SIGNAL(timeout()), this, SLOT(retryByTimer())); connect(&retryTimer, SIGNAL(timeout()), this, SLOT(retryByTimer()));
connect(&_waitForConnectedTimer, SIGNAL(timeout()), this, SLOT(onWaitConnectedFailed())); connect(&_waitForConnectedTimer, SIGNAL(timeout()), this, SLOT(onWaitConnectedFailed()));
@ -1966,6 +1975,10 @@ void MTProtoConnectionPrivate::restartNow() {
} }
void MTProtoConnectionPrivate::socketStart(bool afterConfig) { void MTProtoConnectionPrivate::socketStart(bool afterConfig) {
if (_finished) {
DEBUG_LOG(("MTP Error: socketStart() called for finished connection!"));
return;
}
bool isDldDc = (dc >= MTP::dldStart) && (dc < MTP::dldEnd); bool isDldDc = (dc >= MTP::dldStart) && (dc < MTP::dldEnd);
if (isDldDc) { // using media_only addresses only if key for this dc is already created if (isDldDc) { // using media_only addresses only if key for this dc is already created
QReadLocker lockFinished(&sessionDataMutex); QReadLocker lockFinished(&sessionDataMutex);
@ -2231,7 +2244,9 @@ void MTProtoConnectionPrivate::doDisconnect() {
void MTProtoConnectionPrivate::doFinish() { void MTProtoConnectionPrivate::doFinish() {
doDisconnect(); doDisconnect();
_owner->stopped(); _finished = true;
emit finished(_owner);
deleteLater();
} }
void MTProtoConnectionPrivate::handleReceived() { void MTProtoConnectionPrivate::handleReceived() {
@ -3928,7 +3943,7 @@ void MTProtoConnectionPrivate::unlockKey() {
} }
MTProtoConnectionPrivate::~MTProtoConnectionPrivate() { MTProtoConnectionPrivate::~MTProtoConnectionPrivate() {
doDisconnect(); t_assert(_finished && _conn == nullptr && _conn4 == nullptr && _conn6 == nullptr);
} }
void MTProtoConnectionPrivate::stop() { void MTProtoConnectionPrivate::stop() {
@ -3939,9 +3954,6 @@ void MTProtoConnectionPrivate::stop() {
sessionData->keyMutex()->unlock(); sessionData->keyMutex()->unlock();
myKeyLock = false; myKeyLock = false;
} }
sessionData = 0; sessionData = nullptr;
} }
} }
MTProtoConnection::~MTProtoConnection() {
}

View File

@ -58,11 +58,13 @@ class MTPThread : public QThread {
Q_OBJECT Q_OBJECT
public: public:
MTPThread(QObject *parent = 0); MTPThread();
uint32 getThreadId() const; uint32 getThreadId() const;
~MTPThread();
private: private:
uint32 threadId; uint32 _threadId;
}; };
class MTProtoConnection { class MTProtoConnection {
@ -75,8 +77,8 @@ public:
MTProtoConnection(); MTProtoConnection();
int32 start(MTPSessionData *data, int32 dc = 0); // return dc int32 start(MTPSessionData *data, int32 dc = 0); // return dc
void stop(); void kill();
void stopped(); void waitTillFinish();
~MTProtoConnection(); ~MTProtoConnection();
enum { enum {
@ -358,6 +360,8 @@ signals:
void resendManyAsync(QVector<quint64> msgIds, quint64 msCanWait, bool forceContainer, bool sendMsgStateInfo); void resendManyAsync(QVector<quint64> msgIds, quint64 msCanWait, bool forceContainer, bool sendMsgStateInfo);
void resendAllAsync(); void resendAllAsync();
void finished(MTProtoConnection *connection);
public slots: public slots:
void retryByTimer(); void retryByTimer();
@ -466,7 +470,7 @@ private:
template <typename TResponse> template <typename TResponse>
bool readResponseNotSecure(TResponse &response); bool readResponseNotSecure(TResponse &response);
bool restarted; bool restarted, _finished;
uint64 keyId; uint64 keyId;
QReadWriteLock sessionDataMutex; QReadWriteLock sessionDataMutex;

View File

@ -66,7 +66,8 @@ void MTPSessionData::clear() {
} }
MTProtoSession::MTProtoSession() : QObject() MTProtoSession::MTProtoSession(int32 dcenter) : QObject()
, _connection(0)
, _killed(false) , _killed(false)
, _needToReceive(false) , _needToReceive(false)
, data(this) , data(this)
@ -75,9 +76,6 @@ MTProtoSession::MTProtoSession() : QObject()
, msSendCall(0) , msSendCall(0)
, msWait(0) , msWait(0)
, _ping(false) { , _ping(false) {
}
void MTProtoSession::start(int32 dcenter) {
if (_killed) { if (_killed) {
DEBUG_LOG(("Session Error: can't start a killed session")); DEBUG_LOG(("Session Error: can't start a killed session"));
return; return;
@ -96,37 +94,32 @@ void MTProtoSession::start(int32 dcenter) {
MTProtoDCMap &dcs(mtpDCMap()); MTProtoDCMap &dcs(mtpDCMap());
connections.reserve(cConnectionsInSession()); _connection = new MTProtoConnection();
for (uint32 i = 0; i < cConnectionsInSession(); ++i) { dcWithShift = _connection->start(&data, dcenter);
connections.push_back(new MTProtoConnection()); if (!dcWithShift) {
dcWithShift = connections.back()->start(&data, dcenter); delete _connection;
if (!dcWithShift) { _connection = 0;
for (MTProtoConnections::const_iterator j = connections.cbegin(), e = connections.cend(); j != e; ++j) { DEBUG_LOG(("Session Info: could not start connection to dc %1").arg(dcenter));
delete *j; return;
} }
connections.clear(); if (!dc) {
DEBUG_LOG(("Session Info: could not start connection %1 to dc %2").arg(i).arg(dcenter)); dcenter = dcWithShift;
return; int32 dcId = dcWithShift % _mtp_internal::dcShift;
MTProtoDCMap::const_iterator dcIndex = dcs.constFind(dcId);
if (dcIndex == dcs.cend()) {
dc = MTProtoDCPtr(new MTProtoDC(dcId, mtpAuthKeyPtr()));
dcs.insert(dcWithShift % _mtp_internal::dcShift, dc);
} else {
dc = dcIndex.value();
} }
if (!dc) {
dcenter = dcWithShift;
int32 dcId = dcWithShift % _mtp_internal::dcShift;
MTProtoDCMap::const_iterator dcIndex = dcs.constFind(dcId);
if (dcIndex == dcs.cend()) {
dc = MTProtoDCPtr(new MTProtoDC(dcId, mtpAuthKeyPtr()));
dcs.insert(dcWithShift % _mtp_internal::dcShift, dc);
} else {
dc = dcIndex.value();
}
ReadLockerAttempt lock(keyMutex()); ReadLockerAttempt lock(keyMutex());
data.setKey(lock ? dc->getKey() : mtpAuthKeyPtr(0)); data.setKey(lock ? dc->getKey() : mtpAuthKeyPtr(0));
if (lock && dc->connectionInited()) { if (lock && dc->connectionInited()) {
data.setLayerWasInited(true); data.setLayerWasInited(true);
}
connect(dc.data(), SIGNAL(authKeyCreated()), this, SLOT(authKeyCreatedForDC()), Qt::QueuedConnection);
connect(dc.data(), SIGNAL(layerWasInited(bool)), this, SLOT(layerWasInitedForDC(bool)), Qt::QueuedConnection);
} }
connect(dc.data(), SIGNAL(authKeyCreated()), this, SLOT(authKeyCreatedForDC()), Qt::QueuedConnection);
connect(dc.data(), SIGNAL(layerWasInited(bool)), this, SLOT(layerWasInitedForDC(bool)), Qt::QueuedConnection);
} }
} }
@ -139,10 +132,14 @@ void MTProtoSession::restart() {
} }
void MTProtoSession::stop() { void MTProtoSession::stop() {
if (_killed) {
DEBUG_LOG(("Session Error: can't kill a killed session"));
return;
}
DEBUG_LOG(("Session Info: stopping session dcWithShift %1").arg(dcWithShift)); DEBUG_LOG(("Session Info: stopping session dcWithShift %1").arg(dcWithShift));
while (!connections.isEmpty()) { if (_connection) {
connections.back()->stop(); _connection->kill();
connections.pop_back(); _connection = 0;
} }
} }
@ -194,22 +191,17 @@ void MTProtoSession::needToResumeAndSend() {
DEBUG_LOG(("Session Info: can't resume a killed session")); DEBUG_LOG(("Session Info: can't resume a killed session"));
return; return;
} }
if (connections.isEmpty()) { if (!_connection) {
DEBUG_LOG(("Session Info: resuming session dcWithShift %1").arg(dcWithShift)); DEBUG_LOG(("Session Info: resuming session dcWithShift %1").arg(dcWithShift));
MTProtoDCMap &dcs(mtpDCMap()); MTProtoDCMap &dcs(mtpDCMap());
connections.reserve(cConnectionsInSession()); _connection = new MTProtoConnection();
for (uint32 i = 0; i < cConnectionsInSession(); ++i) { if (!_connection->start(&data, dcWithShift)) {
connections.push_back(new MTProtoConnection()); delete _connection;
if (!connections.back()->start(&data, dcWithShift)) { _connection = 0;
for (MTProtoConnections::const_iterator j = connections.cbegin(), e = connections.cend(); j != e; ++j) { DEBUG_LOG(("Session Info: could not start connection to dcWithShift %1").arg(dcWithShift));
delete *j; dcWithShift = 0;
} return;
connections.clear();
DEBUG_LOG(("Session Info: could not start connection %1 to dcWithShift %2").arg(i).arg(dcWithShift));
dcWithShift = 0;
return;
}
} }
} }
if (_ping) { if (_ping) {
@ -324,12 +316,13 @@ void MTProtoSession::ping() {
} }
int32 MTProtoSession::requestState(mtpRequestId requestId) const { int32 MTProtoSession::requestState(mtpRequestId requestId) const {
MTProtoConnections::const_iterator j = connections.cbegin(), e = connections.cend();
int32 result = MTP::RequestSent; int32 result = MTP::RequestSent;
for (; j != e; ++j) {
int32 s = (*j)->state(); bool connected = false;
if (_connection) {
int32 s = _connection->state();
if (s == MTProtoConnection::Connected) { if (s == MTProtoConnection::Connected) {
break; connected = true;
} else if (s == MTProtoConnection::Connecting || s == MTProtoConnection::Disconnected) { } else if (s == MTProtoConnection::Connecting || s == MTProtoConnection::Disconnected) {
if (result < 0 || result == MTP::RequestSent) { if (result < 0 || result == MTP::RequestSent) {
result = MTP::RequestConnecting; result = MTP::RequestConnecting;
@ -340,7 +333,7 @@ int32 MTProtoSession::requestState(mtpRequestId requestId) const {
} }
} }
} }
if (j == e) { // no one is connected if (!connected) {
return result; return result;
} }
if (!requestId) return MTP::RequestSent; if (!requestId) return MTP::RequestSent;
@ -356,10 +349,10 @@ int32 MTProtoSession::requestState(mtpRequestId requestId) const {
} }
int32 MTProtoSession::getState() const { int32 MTProtoSession::getState() const {
MTProtoConnections::const_iterator j = connections.cbegin(), e = connections.cend();
int32 result = -86400000; int32 result = -86400000;
for (; j != e; ++j) {
int32 s = (*j)->state(); if (_connection) {
int32 s = _connection->state();
if (s == MTProtoConnection::Connected) { if (s == MTProtoConnection::Connected) {
return s; return s;
} else if (s == MTProtoConnection::Connecting || s == MTProtoConnection::Disconnected) { } else if (s == MTProtoConnection::Connecting || s == MTProtoConnection::Disconnected) {
@ -379,12 +372,7 @@ int32 MTProtoSession::getState() const {
} }
QString MTProtoSession::transport() const { QString MTProtoSession::transport() const {
MTProtoConnections::const_iterator j = connections.cbegin(), e = connections.cend(); return _connection ? _connection->transport() : QString();
for (; j != e; ++j) {
QString s = (*j)->transport();
if (!s.isEmpty()) return s;
}
return QString();
} }
mtpRequestId MTProtoSession::resend(quint64 msgId, quint64 msCanWait, bool forceContainer, bool sendMsgStateInfo) { mtpRequestId MTProtoSession::resend(quint64 msgId, quint64 msCanWait, bool forceContainer, bool sendMsgStateInfo) {
@ -507,6 +495,10 @@ int32 MTProtoSession::getDcWithShift() const {
} }
void MTProtoSession::tryToReceive() { void MTProtoSession::tryToReceive() {
if (_killed) {
DEBUG_LOG(("Session Error: can't receive in a killed session"));
return;
}
if (_mtp_internal::paused()) { if (_mtp_internal::paused()) {
_needToReceive = true; _needToReceive = true;
return; return;
@ -537,9 +529,7 @@ void MTProtoSession::tryToReceive() {
} }
MTProtoSession::~MTProtoSession() { MTProtoSession::~MTProtoSession() {
for (MTProtoConnections::const_iterator i = connections.cbegin(), e = connections.cend(); i != e; ++i) { t_assert(_connection == 0);
delete *i;
}
} }
MTPrpcError rpcClientError(const QString &type, const QString &description) { MTPrpcError rpcClientError(const QString &type, const QString &description) {

View File

@ -222,9 +222,8 @@ class MTProtoSession : public QObject {
public: public:
MTProtoSession(); MTProtoSession(int32 dcenter);
void start(int32 dcenter = 0);
void restart(); void restart();
void stop(); void stop();
void kill(); void kill();
@ -279,8 +278,7 @@ public slots:
private: private:
typedef QList<MTProtoConnection*> MTProtoConnections; MTProtoConnection *_connection;
MTProtoConnections connections;
bool _killed; bool _killed;
bool _needToReceive; bool _needToReceive;
@ -304,7 +302,3 @@ inline QReadWriteLock *MTPSessionData::keyMutex() const {
} }
MTPrpcError rpcClientError(const QString &type, const QString &description = QString()); MTPrpcError rpcClientError(const QString &type, const QString &description = QString());
// here
typedef QSharedPointer<MTProtoSession> MTProtoSessionPtr;

View File

@ -38,10 +38,8 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
namespace PlatformSpecific { namespace PlatformSpecific {
struct Initializer { void start();
Initializer(); void finish();
~Initializer();
};
namespace ThirdParty { namespace ThirdParty {
void start(); void start();

View File

@ -1237,10 +1237,10 @@ void psShowInFolder(const QString &name) {
namespace PlatformSpecific { namespace PlatformSpecific {
Initializer::Initializer() { void start() {
} }
Initializer::~Initializer() { void finish() {
delete _psEventFilter; delete _psEventFilter;
_psEventFilter = 0; _psEventFilter = 0;
} }

View File

@ -850,11 +850,11 @@ void psShowInFolder(const QString &name) {
namespace PlatformSpecific { namespace PlatformSpecific {
Initializer::Initializer() { void start() {
objc_start(); objc_start();
} }
Initializer::~Initializer() { void finish() {
delete _psEventFilter; delete _psEventFilter;
_psEventFilter = 0; _psEventFilter = 0;

View File

@ -2159,10 +2159,10 @@ void psShowInFolder(const QString &name) {
namespace PlatformSpecific { namespace PlatformSpecific {
Initializer::Initializer() { void start() {
} }
Initializer::~Initializer() { void finish() {
delete _psEventFilter; delete _psEventFilter;
_psEventFilter = 0; _psEventFilter = 0;

View File

@ -77,7 +77,6 @@ inline QString cInlineGifBotUsername() {
return cTestMode() ? qstr("contextbot") : qstr("gif"); return cTestMode() ? qstr("contextbot") : qstr("gif");
} }
DeclareSetting(QString, LoggedPhoneNumber); DeclareSetting(QString, LoggedPhoneNumber);
DeclareReadSetting(uint32, ConnectionsInSession);
DeclareSetting(bool, AutoStart); DeclareSetting(bool, AutoStart);
DeclareSetting(bool, StartMinimized); DeclareSetting(bool, StartMinimized);
DeclareSetting(bool, StartInTray); DeclareSetting(bool, StartInTray);

View File

@ -34,8 +34,8 @@ IDI_ICON1 ICON "SourceFiles\\art\\icon256.ico"
// //
VS_VERSION_INFO VERSIONINFO VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,9,28,1 FILEVERSION 0,9,28,2
PRODUCTVERSION 0,9,28,1 PRODUCTVERSION 0,9,28,2
FILEFLAGSMASK 0x3fL FILEFLAGSMASK 0x3fL
#ifdef _DEBUG #ifdef _DEBUG
FILEFLAGS 0x1L FILEFLAGS 0x1L
@ -51,10 +51,10 @@ BEGIN
BLOCK "040904b0" BLOCK "040904b0"
BEGIN BEGIN
VALUE "CompanyName", "Telegram Messenger LLP" VALUE "CompanyName", "Telegram Messenger LLP"
VALUE "FileVersion", "0.9.28.1" VALUE "FileVersion", "0.9.28.2"
VALUE "LegalCopyright", "Copyright (C) 2014-2016" VALUE "LegalCopyright", "Copyright (C) 2014-2016"
VALUE "ProductName", "Telegram Desktop" VALUE "ProductName", "Telegram Desktop"
VALUE "ProductVersion", "0.9.28.1" VALUE "ProductVersion", "0.9.28.2"
END END
END END
BLOCK "VarFileInfo" BLOCK "VarFileInfo"

View File

@ -3,4 +3,4 @@ AppVersionStrMajor 0.9
AppVersionStrSmall 0.9.28 AppVersionStrSmall 0.9.28
AppVersionStr 0.9.28 AppVersionStr 0.9.28
DevChannel 0 DevChannel 0
BetaVersion 9028001 BetaVersion 9028002