Improve autoupdate code, move it from Application.

This commit is contained in:
John Preston 2018-04-26 20:14:21 +04:00
parent 65f968ec1b
commit 993cb987a6
18 changed files with 745 additions and 556 deletions

View File

@ -38,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "observer_peer.h"
#include "auth_session.h"
#include "core/crash_reports.h"
#include "core/update_checker.h"
#include "storage/storage_facade.h"
#include "storage/storage_shared_media.h"
#include "window/themes/window_theme.h"
@ -1667,7 +1668,7 @@ namespace {
void restart() {
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
bool updateReady = (Sandbox::updatingState() == Application::UpdatingReady);
bool updateReady = (Core::UpdateChecker().state() == Core::UpdateChecker::State::Ready);
#else // !TDESKTOP_DISABLE_AUTOUPDATE
bool updateReady = false;
#endif // else for !TDESKTOP_DISABLE_AUTOUPDATE

View File

@ -11,11 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwidget.h"
#include "mainwindow.h"
#include "storage/localstorage.h"
#include "autoupdater.h"
#include "window/notifications_manager.h"
#include "core/crash_reports.h"
#include "messenger.h"
#include "base/timer.h"
#include "core/update_checker.h"
#include "core/crash_report_window.h"
namespace {
@ -65,7 +65,11 @@ Application::Application(
int &argc,
char **argv)
: QApplication(argc, argv)
, _launcher(launcher) {
, _launcher(launcher)
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
, _updateChecker(std::make_unique<Core::UpdateChecker>())
#endif // TDESKTOP_DISABLE_AUTOUPDATE
{
const auto d = QFile::encodeName(QDir(cWorkingDir()).absolutePath());
char h[33] = { 0 };
hashMd5Hex(d.constData(), d.size(), h);
@ -86,13 +90,6 @@ Application::Application(
QTimer::singleShot(0, this, SLOT(startApplication()));
connect(this, SIGNAL(aboutToQuit()), this, SLOT(closeApplication()));
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
_updateCheckTimer.create(this);
connect(_updateCheckTimer, SIGNAL(timeout()), this, SLOT(updateCheck()));
connect(this, SIGNAL(updateFailed()), this, SLOT(onUpdateFailed()));
connect(this, SIGNAL(updateReady()), this, SLOT(onUpdateReady()));
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
if (cManyInstance()) {
LOG(("Many instance allowed, starting..."));
singleInstanceChecked();
@ -180,7 +177,7 @@ void Application::socketError(QLocalSocket::LocalSocketError e) {
#endif // !Q_OS_WINRT
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
if (!cNoStartUpdate() && checkReadyUpdate()) {
if (!cNoStartUpdate() && Core::checkReadyUpdate()) {
cSetRestartingUpdate(true);
DEBUG_LOG(("Application Info: installing update instead of starting app..."));
return App::quit();
@ -356,168 +353,10 @@ void Application::closeApplication() {
_localSocket.close();
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
delete _updateReply;
_updateReply = 0;
if (_updateChecker) _updateChecker->deleteLater();
_updateChecker = 0;
if (_updateThread) {
_updateThread->quit();
}
_updateThread = 0;
_updateChecker = nullptr;
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
}
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
void Application::updateCheck() {
startUpdateCheck(false);
}
void Application::updateGotCurrent() {
if (!_updateReply || _updateThread) return;
cSetLastUpdateCheck(unixtime());
QRegularExpressionMatch m = QRegularExpression(qsl("^\\s*(\\d+)\\s*:\\s*([\\x21-\\x7f]+)\\s*$")).match(QString::fromLatin1(_updateReply->readAll()));
if (m.hasMatch()) {
uint64 currentVersion = m.captured(1).toULongLong();
QString url = m.captured(2);
bool betaVersion = false;
if (url.startsWith(qstr("beta_"))) {
betaVersion = true;
url = url.mid(5) + '_' + countBetaVersionSignature(currentVersion);
}
if ((!betaVersion || cBetaVersion()) && currentVersion > (betaVersion ? cBetaVersion() : uint64(AppVersion))) {
_updateThread = new QThread();
connect(_updateThread, SIGNAL(finished()), _updateThread, SLOT(deleteLater()));
_updateChecker = new UpdateChecker(_updateThread, url);
_updateThread->start();
}
}
if (_updateReply) _updateReply->deleteLater();
_updateReply = 0;
if (!_updateThread) {
QDir updates(cWorkingDir() + "tupdates");
if (updates.exists()) {
QFileInfoList list = updates.entryInfoList(QDir::Files);
for (QFileInfoList::iterator i = list.begin(), e = list.end(); i != e; ++i) {
if (QRegularExpression("^(tupdate|tmacupd|tmac32upd|tlinuxupd|tlinux32upd)\\d+(_[a-z\\d]+)?$", QRegularExpression::CaseInsensitiveOption).match(i->fileName()).hasMatch()) {
QFile(i->absoluteFilePath()).remove();
}
}
}
emit updateLatest();
}
startUpdateCheck(true);
Local::writeSettings();
}
void Application::updateFailedCurrent(QNetworkReply::NetworkError e) {
LOG(("App Error: could not get current version (update check): %1").arg(e));
if (_updateReply) _updateReply->deleteLater();
_updateReply = 0;
emit updateFailed();
startUpdateCheck(true);
}
void Application::onUpdateReady() {
if (_updateChecker) {
_updateChecker->deleteLater();
_updateChecker = nullptr;
}
_updateCheckTimer->stop();
cSetLastUpdateCheck(unixtime());
Local::writeSettings();
}
void Application::onUpdateFailed() {
if (_updateChecker) {
_updateChecker->deleteLater();
_updateChecker = 0;
if (_updateThread) _updateThread->quit();
_updateThread = 0;
}
cSetLastUpdateCheck(unixtime());
Local::writeSettings();
}
Application::UpdatingState Application::updatingState() {
if (!_updateThread) return Application::UpdatingNone;
if (!_updateChecker) return Application::UpdatingReady;
return Application::UpdatingDownload;
}
int32 Application::updatingSize() {
if (!_updateChecker) return 0;
return _updateChecker->size();
}
int32 Application::updatingReady() {
if (!_updateChecker) return 0;
return _updateChecker->ready();
}
void Application::stopUpdate() {
if (_updateReply) {
_updateReply->abort();
_updateReply->deleteLater();
_updateReply = 0;
}
if (_updateChecker) {
_updateChecker->deleteLater();
_updateChecker = 0;
if (_updateThread) _updateThread->quit();
_updateThread = 0;
}
}
void Application::startUpdateCheck(bool forceWait) {
if (!Sandbox::started()) return;
_updateCheckTimer->stop();
if (_updateThread || _updateReply || !cAutoUpdate() || cExeName().isEmpty()) return;
int32 constDelay = cBetaVersion() ? 600 : UpdateDelayConstPart, randDelay = cBetaVersion() ? 300 : UpdateDelayRandPart;
int32 updateInSecs = cLastUpdateCheck() + constDelay + int32(rand() % randDelay) - unixtime();
bool sendRequest = (updateInSecs <= 0 || updateInSecs > (constDelay + randDelay));
if (!sendRequest && !forceWait) {
QDir updates(cWorkingDir() + "tupdates");
if (updates.exists()) {
QFileInfoList list = updates.entryInfoList(QDir::Files);
for (QFileInfoList::iterator i = list.begin(), e = list.end(); i != e; ++i) {
if (QRegularExpression("^(tupdate|tmacupd|tmac32upd|tlinuxupd|tlinux32upd)\\d+(_[a-z\\d]+)?$", QRegularExpression::CaseInsensitiveOption).match(i->fileName()).hasMatch()) {
sendRequest = true;
}
}
}
}
if (cManyInstance() && !cDebug()) return; // only main instance is updating
if (sendRequest) {
QUrl url(cUpdateURL());
if (cBetaVersion()) {
url.setQuery(qsl("version=%1&beta=%2").arg(AppVersion).arg(cBetaVersion()));
} else if (cAlphaVersion()) {
url.setQuery(qsl("version=%1&alpha=1").arg(AppVersion));
} else {
url.setQuery(qsl("version=%1").arg(AppVersion));
}
QString u = url.toString();
QNetworkRequest checkVersion(url);
if (_updateReply) _updateReply->deleteLater();
_updateReply = _updateManager.get(checkVersion);
connect(_updateReply, SIGNAL(finished()), this, SLOT(updateGotCurrent()));
connect(_updateReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(updateFailedCurrent(QNetworkReply::NetworkError)));
emit updateChecking();
} else {
_updateCheckTimer->start((updateInSecs + 5) * 1000);
}
}
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
inline Application *application() {
return qobject_cast<Application*>(QApplication::instance());
}
@ -569,73 +408,6 @@ void adjustSingleTimers() {
base::Timer::Adjust();
}
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
void startUpdateCheck() {
if (auto a = application()) {
return a->startUpdateCheck(false);
}
}
void stopUpdate() {
if (auto a = application()) {
return a->stopUpdate();
}
}
Application::UpdatingState updatingState() {
if (auto a = application()) {
return a->updatingState();
}
return Application::UpdatingNone;
}
int32 updatingSize() {
if (auto a = application()) {
return a->updatingSize();
}
return 0;
}
int32 updatingReady() {
if (auto a = application()) {
return a->updatingReady();
}
return 0;
}
void updateChecking() {
if (auto a = application()) {
emit a->updateChecking();
}
}
void updateLatest() {
if (auto a = application()) {
emit a->updateLatest();
}
}
void updateProgress(qint64 ready, qint64 total) {
if (auto a = application()) {
emit a->updateProgress(ready, total);
}
}
void updateFailed() {
if (auto a = application()) {
emit a->updateFailed();
}
}
void updateReady() {
if (auto a = application()) {
emit a->updateReady();
}
}
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
void connect(const char *signal, QObject *object, const char *method) {
if (auto a = application()) {
a->connect(a, signal, object, method);

View File

@ -7,10 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class UpdateChecker;
namespace Core {
class Launcher;
class UpdateChecker;
} // namespace Core
class Application : public QApplication {
@ -59,46 +58,11 @@ private:
void singleInstanceChecked();
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
// Autoupdating
public:
void startUpdateCheck(bool forceWait);
void stopUpdate();
enum UpdatingState {
UpdatingNone,
UpdatingDownload,
UpdatingReady,
};
UpdatingState updatingState();
int32 updatingSize();
int32 updatingReady();
signals:
void updateChecking();
void updateLatest();
void updateProgress(qint64 ready, qint64 total);
void updateReady();
void updateFailed();
public slots:
void updateCheck();
void updateGotCurrent();
void updateFailedCurrent(QNetworkReply::NetworkError e);
void onUpdateReady();
void onUpdateFailed();
private:
object_ptr<SingleTimer> _updateCheckTimer = { nullptr };
QNetworkReply *_updateReply = nullptr;
QNetworkAccessManager _updateManager;
QThread *_updateThread = nullptr;
UpdateChecker *_updateChecker = nullptr;
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
std::unique_ptr<Core::UpdateChecker> _updateChecker;
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
};
namespace Sandbox {
@ -112,23 +76,6 @@ void execExternal(const QString &cmd);
void adjustSingleTimers();
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
void startUpdateCheck();
void stopUpdate();
Application::UpdatingState updatingState();
int32 updatingSize();
int32 updatingReady();
void updateChecking();
void updateLatest();
void updateProgress(qint64 ready, qint64 total);
void updateFailed();
void updateReady();
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
void refreshGlobalProxy();
void connect(const char *signal, QObject *object, const char *method);

View File

@ -1,63 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
#include <QtNetwork/QLocalSocket>
#include <QtNetwork/QLocalServer>
#include <QtNetwork/QNetworkReply>
class UpdateChecker : public QObject {
Q_OBJECT
public:
UpdateChecker(QThread *thread, const QString &url);
void unpackUpdate();
int32 ready();
int32 size();
static void clearAll();
~UpdateChecker();
public slots:
void start();
void partMetaGot();
void partFinished(qint64 got, qint64 total);
void partFailed(QNetworkReply::NetworkError e);
void sendRequest();
private:
void initOutput();
void fatalFail();
QString updateUrl;
QNetworkAccessManager manager;
QNetworkReply *reply;
int32 already, full;
QFile outputFile;
QMutex mutex;
};
bool checkReadyUpdate();
#else // TDESKTOP_DISABLE_AUTOUPDATE
class UpdateChecker : public QObject {
Q_OBJECT
};
#endif // TDESKTOP_DISABLE_AUTOUPDATE
QString countBetaVersionSignature(uint64 version);

View File

@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "autoupdater.h"
#include "boxes/confirm_box.h"
#include "application.h"
#include "ui/widgets/buttons.h"
@ -18,6 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_boxes.h"
#include "platform/platform_file_utilities.h"
#include "core/click_handler_types.h"
#include "core/update_checker.h"
AboutBox::AboutBox(QWidget *parent)
: _version(this, lng_about_version(lt_version, QString::fromLatin1(AppVersionStr.c_str()) + (cAlphaVersion() ? " alpha" : "") + (cBetaVersion() ? qsl(" beta %1").arg(cBetaVersion()) : QString())), st::aboutVersionLink)
@ -68,7 +68,7 @@ void AboutBox::showVersionHistory() {
case dbipLinux32: url += qsl("linux32/%1.tar.xz"); break;
case dbipLinux64: url += qsl("linux/%1.tar.xz"); break;
}
url = url.arg(qsl("tbeta%1_%2").arg(cRealBetaVersion()).arg(countBetaVersionSignature(cRealBetaVersion())));
url = url.arg(qsl("tbeta%1_%2").arg(cRealBetaVersion()).arg(Core::countBetaVersionSignature(cRealBetaVersion())));
Application::clipboard()->setText(url);

View File

@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/platform_specific.h"
#include "application.h"
#include "base/zlib_help.h"
#include "autoupdater.h"
#include "core/update_checker.h"
PreLaunchWindow *PreLaunchWindowInstance = nullptr;
@ -299,23 +299,36 @@ LastCrashedWindow::LastCrashedWindow()
_updatingSkip.setText(qsl("SKIP"));
connect(&_updatingSkip, SIGNAL(clicked()), this, SLOT(onUpdateSkip()));
Sandbox::connect(SIGNAL(updateChecking()), this, SLOT(onUpdateChecking()));
Sandbox::connect(SIGNAL(updateLatest()), this, SLOT(onUpdateLatest()));
Sandbox::connect(SIGNAL(updateProgress(qint64,qint64)), this, SLOT(onUpdateDownloading(qint64,qint64)));
Sandbox::connect(SIGNAL(updateFailed()), this, SLOT(onUpdateFailed()));
Sandbox::connect(SIGNAL(updateReady()), this, SLOT(onUpdateReady()));
Core::UpdateChecker checker;
using Progress = Core::UpdateChecker::Progress;
checker.checking(
) | rpl::start_with_next([=] { onUpdateChecking(); }, _lifetime);
checker.isLatest(
) | rpl::start_with_next([=] { onUpdateLatest(); }, _lifetime);
checker.progress(
) | rpl::start_with_next([=](const Progress &result) {
onUpdateDownloading(result.already, result.size);
}, _lifetime);
checker.failed(
) | rpl::start_with_next([=] { onUpdateFailed(); }, _lifetime);
checker.ready(
) | rpl::start_with_next([=] { onUpdateReady(); }, _lifetime);
switch (Sandbox::updatingState()) {
case Application::UpdatingDownload:
switch (checker.state()) {
case Core::UpdateChecker::State::Download:
setUpdatingState(UpdatingDownload, true);
setDownloadProgress(Sandbox::updatingReady(), Sandbox::updatingSize());
break;
case Application::UpdatingReady: setUpdatingState(UpdatingReady, true); break;
default: setUpdatingState(UpdatingCheck, true); break;
setDownloadProgress(checker.already(), checker.size());
break;
case Core::UpdateChecker::State::Ready:
setUpdatingState(UpdatingReady, true);
break;
default:
setUpdatingState(UpdatingCheck, true);
break;
}
cSetLastUpdateCheck(0);
Sandbox::startUpdateCheck();
checker.start();
#else // !TDESKTOP_DISABLE_AUTOUPDATE
_updating.setText(qsl("Please check if there is a new version available."));
if (_sendingState != SendingNoReport) {
@ -790,9 +803,10 @@ void LastCrashedWindow::onNetworkSettingsSaved(
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
if ((_updatingState == UpdatingFail && (_sendingState == SendingNoReport || _sendingState == SendingUpdateCheck)) || (_updatingState == UpdatingCheck)) {
Sandbox::stopUpdate();
Core::UpdateChecker checker;
checker.stop();
cSetLastUpdateCheck(0);
Sandbox::startUpdateCheck();
checker.start();
} else
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
if (_sendingState == SendingFail || _sendingState == SendingProgress) {
@ -815,7 +829,7 @@ void LastCrashedWindow::setUpdatingState(UpdatingState state, bool force) {
}
break;
case UpdatingReady:
if (checkReadyUpdate()) {
if (Core::checkReadyUpdate()) {
cSetRestartingUpdate(true);
App::quit();
return;
@ -849,7 +863,8 @@ void LastCrashedWindow::setDownloadProgress(qint64 ready, qint64 total) {
void LastCrashedWindow::onUpdateRetry() {
cSetLastUpdateCheck(0);
Sandbox::startUpdateCheck();
Core::UpdateChecker checker;
checker.start();
}
void LastCrashedWindow::onUpdateSkip() {
@ -857,7 +872,8 @@ void LastCrashedWindow::onUpdateSkip() {
onContinue();
} else {
if (_updatingState == UpdatingCheck || _updatingState == UpdatingDownload) {
Sandbox::stopUpdate();
Core::UpdateChecker checker;
checker.stop();
setUpdatingState(UpdatingFail);
}
_sendingState = SendingNone;

View File

@ -114,6 +114,11 @@ private:
QString minidumpFileName();
void updateControls();
void excludeReportUsername();
QString getReportField(const QLatin1String &name, const QLatin1String &prefix);
void addReportFieldPart(const QLatin1String &name, const QLatin1String &prefix, QHttpMultiPart *multipart);
QString _host, _username, _password;
quint32 _port;
@ -128,8 +133,6 @@ private:
bool _reportShown, _reportSaved;
void excludeReportUsername();
enum SendingState {
SendingNoReport,
SendingUpdateCheck,
@ -167,8 +170,7 @@ private:
void setDownloadProgress(qint64 ready, qint64 total);
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
QString getReportField(const QLatin1String &name, const QLatin1String &prefix);
void addReportFieldPart(const QLatin1String &name, const QLatin1String &prefix, QHttpMultiPart *multipart);
rpl::lifetime _lifetime;
};

View File

@ -5,8 +5,12 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "core/update_checker.h"
#include "autoupdater.h"
#include "application.h"
#include "platform/platform_specific.h"
#include "base/timer.h"
#include "storage/localstorage.h"
#include <openssl/rsa.h>
#include <openssl/pem.h>
@ -19,131 +23,554 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include <lzma.h>
#endif // else of Q_OS_WIN
#include "application.h"
#include "platform/platform_specific.h"
namespace Core {
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
namespace {
constexpr auto kCheckTimeout = TimeMs(10000);
#ifdef Q_OS_WIN
typedef DWORD VerInt;
typedef WCHAR VerChar;
using VersionInt = DWORD;
using VersionChar = WCHAR;
#else // Q_OS_WIN
typedef int VerInt;
typedef wchar_t VerChar;
using VersionInt = int;
using VersionChar = wchar_t;
#endif // Q_OS_WIN
UpdateChecker::UpdateChecker(QThread *thread, const QString &url) : reply(0), already(0), full(0) {
updateUrl = url;
moveToThread(thread);
manager.moveToThread(thread);
using ErrorSignal = void(QNetworkReply::*)(QNetworkReply::NetworkError);
const auto QNetworkReply_error = ErrorSignal(&QNetworkReply::error);
connect(thread, SIGNAL(started()), this, SLOT(start()));
std::weak_ptr<Updater> UpdaterInstance;
std::shared_ptr<Updater> GetUpdaterInstance() {
if (const auto result = UpdaterInstance.lock()) {
return result;
}
const auto result = std::make_shared<Updater>();
UpdaterInstance = result;
return result;
}
void ClearAll() {
psDeleteDir(cWorkingDir() + qsl("tupdates"));
}
QString FindUpdateFile() {
QDir updates(cWorkingDir() + "tupdates");
if (!updates.exists()) {
return QString();
}
const auto list = updates.entryInfoList(QDir::Files);
for (const auto &info : list) {
if (QRegularExpression(
"^("
"tupdate|"
"tmacupd|"
"tmac32upd|"
"tlinuxupd|"
"tlinux32upd"
")\\d+(_[a-z\\d]+)?$",
QRegularExpression::CaseInsensitiveOption
).match(info.fileName()).hasMatch()) {
return info.absoluteFilePath();
}
}
return QString();
}
} // namespace
class Updater : public base::has_weak_ptr {
public:
using Progress = UpdateChecker::Progress;
using State = UpdateChecker::State;
Updater();
rpl::producer<> checking() const;
rpl::producer<> isLatest() const;
rpl::producer<Progress> progress() const;
rpl::producer<> failed() const;
rpl::producer<> ready() const;
void start(bool forceWait);
void stop();
State state() const;
int already() const;
int size() const;
// Thread-safe methods.
void onProgress(Progress progress);
void onFailed();
void onReady();
~Updater();
private:
class Private;
void check();
void handleFailed();
void handleReady();
void gotResponse();
void gotFailure(QNetworkReply::NetworkError e);
void clearSentRequest();
void requestTimeout();
bool _isReady = false;
base::Timer _timer;
base::Timer _retryTimer;
rpl::event_stream<> _checking;
rpl::event_stream<> _isLatest;
rpl::event_stream<Progress> _progress;
rpl::event_stream<> _failed;
rpl::event_stream<> _ready;
std::unique_ptr<QNetworkAccessManager> _manager;
QNetworkReply *_reply = nullptr;
std::unique_ptr<QThread> _thread;
Private *_private = nullptr;
rpl::lifetime _lifetime;
};
class Updater::Private : public QObject {
public:
Private(
not_null<Updater*> parent,
not_null<QThread*> thread,
const QString &url);
void unpackUpdate();
// Thread-safe methods.
int already() const;
int size() const;
private:
void start();
void sendRequest();
void initOutput();
void gotMetaData();
void partFinished(qint64 got, qint64 total);
void partFailed(QNetworkReply::NetworkError e);
void fatalFail();
not_null<Updater*> _parent;
QString _url;
QNetworkAccessManager _manager;
std::unique_ptr<QNetworkReply> _reply;
int _already = 0;
int _size = 0;
QFile _output;
mutable QMutex _mutex;
};
Updater::Updater()
: _timer([=] { check(); })
, _retryTimer([=] { requestTimeout(); }) {
failed() | rpl::start_with_next([=] {
handleFailed();
}, _lifetime);
ready() | rpl::start_with_next([=] {
handleReady();
}, _lifetime);
}
rpl::producer<> Updater::checking() const {
return _checking.events();
}
rpl::producer<> Updater::isLatest() const {
return _isLatest.events();
}
auto Updater::progress() const
-> rpl::producer<Progress> {
return _progress.events();
}
rpl::producer<> Updater::failed() const {
return _failed.events();
}
rpl::producer<> Updater::ready() const {
return _ready.events();
}
void Updater::onProgress(Progress progress) {
crl::on_main(this, [=] {
_progress.fire_copy(progress);
});
}
void Updater::onFailed() {
crl::on_main(this, [=] {
_failed.fire({});
});
}
void Updater::onReady() {
crl::on_main(this, [=] {
_ready.fire({});
});
}
void Updater::check() {
start(false);
}
void Updater::gotResponse() {
if (!_reply || _thread) {
return;
}
cSetLastUpdateCheck(unixtime());
const auto m = QRegularExpression(qsl("^\\s*(\\d+)\\s*:\\s*([\\x21-\\x7f]+)\\s*$")).match(QString::fromLatin1(_reply->readAll()));
if (m.hasMatch()) {
uint64 currentVersion = m.captured(1).toULongLong();
QString url = m.captured(2);
bool betaVersion = false;
if (url.startsWith(qstr("beta_"))) {
betaVersion = true;
url = url.mid(5) + '_' + Core::countBetaVersionSignature(currentVersion);
}
if ((!betaVersion || cBetaVersion()) && currentVersion > (betaVersion ? cBetaVersion() : uint64(AppVersion))) {
_thread = std::make_unique<QThread>();
_private = new Private(this, _thread.get(), url);
_thread->start();
}
}
clearSentRequest();
if (!_thread) {
if (const auto update = FindUpdateFile(); !update.isEmpty()) {
QFile(update).remove();
}
_isLatest.fire({});
}
start(true);
Local::writeSettings();
}
void Updater::gotFailure(QNetworkReply::NetworkError e) {
LOG(("App Error: could not get current version (update check): %1").arg(e));
if (const auto reply = base::take(_reply)) {
reply->deleteLater();
}
_failed.fire({});
start(true);
}
void Updater::handleReady() {
_isReady = true;
stop();
cSetLastUpdateCheck(unixtime());
Local::writeSettings();
}
void Updater::handleFailed() {
stop();
cSetLastUpdateCheck(unixtime());
Local::writeSettings();
}
auto Updater::state() const -> State {
if (_isReady) {
return State::Ready;
} else if (!_thread) {
return State::None;
}
return State::Download;
}
int Updater::size() const {
return _private ? _private->size() : 0;
}
int Updater::already() const {
return _private ? _private->already() : 0;
}
void Updater::clearSentRequest() {
const auto reply = base::take(_reply);
if (!reply) {
return;
}
reply->disconnect(reply, &QNetworkReply::finished, nullptr, nullptr);
reply->disconnect(reply, QNetworkReply_error, nullptr, nullptr);
reply->abort();
reply->deleteLater();
_manager = nullptr;
}
void Updater::stop() {
clearSentRequest();
if (const auto checker = base::take(_private)) {
InvokeQueued(checker, [=] { checker->deleteLater(); });
if (const auto thread = base::take(_thread)) {
thread->quit();
thread->wait();
}
}
}
void Updater::start(bool forceWait) {
if (!Sandbox::started() || _isReady) {
return;
}
_timer.cancel();
if (_thread || _reply || !cAutoUpdate() || cExeName().isEmpty()) {
return;
}
_retryTimer.cancel();
const auto constDelay = cBetaVersion() ? 600 : UpdateDelayConstPart;
const auto randDelay = cBetaVersion() ? 300 : UpdateDelayRandPart;
const auto updateInSecs = cLastUpdateCheck()
+ constDelay
+ int(rand() % randDelay)
- unixtime();
auto sendRequest = (updateInSecs <= 0)
|| (updateInSecs > constDelay + randDelay);
if (!sendRequest && !forceWait) {
if (!FindUpdateFile().isEmpty()) {
sendRequest = true;
}
}
if (cManyInstance() && !cDebug()) {
// Only main instance is updating.
return;
}
if (sendRequest) {
clearSentRequest();
auto url = QUrl(cUpdateURL());
if (cBetaVersion()) {
url.setQuery(qsl("version=%1&beta=%2"
).arg(AppVersion
).arg(cBetaVersion()));
} else if (cAlphaVersion()) {
url.setQuery(qsl("version=%1&alpha=1").arg(AppVersion));
} else {
url.setQuery(qsl("version=%1").arg(AppVersion));
}
DEBUG_LOG(("App Info: requesting update state from '%1'"
).arg(url.toDisplayString()));
const auto request = QNetworkRequest(url);
_manager = std::make_unique<QNetworkAccessManager>();
_reply = _manager->get(request);
_reply->connect(_reply, &QNetworkReply::finished, [=] {
gotResponse();
});
_reply->connect(_reply, QNetworkReply_error, [=](auto e) {
gotFailure(e);
});
_checking.fire({});
_retryTimer.callOnce(kCheckTimeout);
} else {
_timer.callOnce((updateInSecs + 5) * TimeMs(1000));
}
}
void Updater::requestTimeout() {
if (_reply) {
stop();
_failed.fire({});
cSetLastUpdateCheck(0);
_timer.callOnce(kCheckTimeout);
}
}
Updater::~Updater() {
stop();
}
UpdateChecker::UpdateChecker()
: _updater(GetUpdaterInstance()) {
}
rpl::producer<> UpdateChecker::checking() const {
return _updater->checking();
}
rpl::producer<> UpdateChecker::isLatest() const {
return _updater->isLatest();
}
auto UpdateChecker::progress() const
-> rpl::producer<Progress> {
return _updater->progress();
}
rpl::producer<> UpdateChecker::failed() const {
return _updater->failed();
}
rpl::producer<> UpdateChecker::ready() const {
return _updater->ready();
}
void UpdateChecker::start(bool forceWait) {
_updater->start(forceWait);
}
void UpdateChecker::stop() {
_updater->stop();
}
auto UpdateChecker::state() const
-> State {
return _updater->state();
}
int UpdateChecker::already() const {
return _updater->already();
}
int UpdateChecker::size() const {
return _updater->size();
}
Updater::Private::Private(
not_null<Updater*> parent,
not_null<QThread*> thread,
const QString &url)
: _parent(parent) {
_url = url;
moveToThread(thread);
_manager.moveToThread(thread);
connect(thread, &QThread::started, this, [=] { start(); });
initOutput();
}
void UpdateChecker::initOutput() {
QString fileName;
QRegularExpressionMatch m = QRegularExpression(qsl("/([^/\\?]+)(\\?|$)")).match(updateUrl);
if (m.hasMatch()) {
fileName = m.captured(1).replace(QRegularExpression(qsl("[^a-zA-Z0-9_\\-]")), QString());
}
if (fileName.isEmpty()) {
fileName = qsl("tupdate-%1").arg(rand_value<uint32>() % 1000000);
}
QString dirStr = cWorkingDir() + qsl("tupdates/");
fileName = dirStr + fileName;
QFileInfo file(fileName);
void Updater::Private::initOutput() {
auto fileName = QString();
const auto nameRegExp = QRegularExpression(qsl("/([^/\\?]+)(\\?|$)"));
const auto nameMatch = nameRegExp.match(_url);
QDir dir(dirStr);
if (nameMatch.hasMatch()) {
fileName = nameMatch.captured(1).replace(
QRegularExpression(qsl("[^a-zA-Z0-9_\\-]")),
QString());
} else if (fileName.isEmpty()) {
fileName = qsl("tupdate-%1").arg(rand() % 1000000);
}
const auto folder = cWorkingDir() + qsl("tupdates/");
const auto finalName = folder + fileName;
QFileInfo info(finalName);
QDir dir(folder);
if (dir.exists()) {
QFileInfoList all = dir.entryInfoList(QDir::Files);
for (QFileInfoList::iterator i = all.begin(), e = all.end(); i != e; ++i) {
if (i->absoluteFilePath() != file.absoluteFilePath()) {
const auto all = dir.entryInfoList(QDir::Files);
for (auto i = all.begin(), e = all.end(); i != e; ++i) {
if (i->absoluteFilePath() != info.absoluteFilePath()) {
QFile::remove(i->absoluteFilePath());
}
}
} else {
dir.mkdir(dir.absolutePath());
}
outputFile.setFileName(fileName);
if (file.exists()) {
uint64 fullSize = file.size();
_output.setFileName(finalName);
if (info.exists()) {
uint64 fullSize = info.size();
if (fullSize < INT_MAX) {
int32 goodSize = (int32)fullSize;
if (goodSize % UpdateChunk) {
goodSize = goodSize - (goodSize % UpdateChunk);
if (goodSize) {
if (outputFile.open(QIODevice::ReadOnly)) {
QByteArray goodData = outputFile.readAll().mid(0, goodSize);
outputFile.close();
if (outputFile.open(QIODevice::WriteOnly)) {
outputFile.write(goodData);
outputFile.close();
if (_output.open(QIODevice::ReadOnly)) {
QByteArray goodData = _output.readAll().mid(0, goodSize);
_output.close();
if (_output.open(QIODevice::WriteOnly)) {
_output.write(goodData);
_output.close();
QMutexLocker lock(&mutex);
already = goodSize;
QMutexLocker lock(&_mutex);
_already = goodSize;
}
}
}
} else {
QMutexLocker lock(&mutex);
already = goodSize;
QMutexLocker lock(&_mutex);
_already = goodSize;
}
}
if (!already) {
QFile::remove(fileName);
if (!_already) {
QFile::remove(finalName);
}
}
}
void UpdateChecker::start() {
void Updater::Private::start() {
sendRequest();
}
void UpdateChecker::sendRequest() {
QNetworkRequest req(updateUrl);
QByteArray rangeHeaderValue = "bytes=" + QByteArray::number(already) + "-";
req.setRawHeader("Range", rangeHeaderValue);
req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true);
if (reply) reply->deleteLater();
reply = manager.get(req);
connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(partFinished(qint64,qint64)));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(partFailed(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(metaDataChanged()), this, SLOT(partMetaGot()));
void Updater::Private::sendRequest() {
auto request = QNetworkRequest(_url);
const auto rangeHeaderValue = "bytes="
+ QByteArray::number(_already)
+ "-";
request.setRawHeader("Range", rangeHeaderValue);
request.setAttribute(
QNetworkRequest::HttpPipeliningAllowedAttribute,
true);
_reply.reset(_manager.get(request));
connect(
_reply.get(),
&QNetworkReply::downloadProgress,
this,
&Private::partFinished);
connect(_reply.get(), QNetworkReply_error, [=](auto error) {
partFailed(error);
});
connect(_reply.get(), &QNetworkReply::metaDataChanged, [=] {
gotMetaData();
});
}
void UpdateChecker::partMetaGot() {
typedef QList<QNetworkReply::RawHeaderPair> Pairs;
Pairs pairs = reply->rawHeaderPairs();
for (Pairs::iterator i = pairs.begin(), e = pairs.end(); i != e; ++i) {
if (QString::fromUtf8(i->first).toLower() == "content-range") {
QRegularExpressionMatch m = QRegularExpression(qsl("/(\\d+)([^\\d]|$)")).match(QString::fromUtf8(i->second));
void Updater::Private::gotMetaData() {
const auto pairs = _reply->rawHeaderPairs();
for (const auto pair : pairs) {
if (QString::fromUtf8(pair.first).toLower() == "content-range") {
const auto m = QRegularExpression(qsl("/(\\d+)([^\\d]|$)")).match(QString::fromUtf8(pair.second));
if (m.hasMatch()) {
{
QMutexLocker lock(&mutex);
full = m.captured(1).toInt();
QMutexLocker lock(&_mutex);
_size = m.captured(1).toInt();
}
Sandbox::updateProgress(already, full);
_parent->onProgress({ _already, _size });
}
}
}
}
int32 UpdateChecker::ready() {
QMutexLocker lock(&mutex);
return already;
int Updater::Private::already() const {
QMutexLocker lock(&_mutex);
return _already;
}
int32 UpdateChecker::size() {
QMutexLocker lock(&mutex);
return full;
int Updater::Private::size() const {
QMutexLocker lock(&_mutex);
return _size;
}
void UpdateChecker::partFinished(qint64 got, qint64 total) {
if (!reply) return;
void Updater::Private::partFinished(qint64 got, qint64 total) {
if (!_reply) return;
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
QVariant statusCode = _reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (statusCode.isValid()) {
int status = statusCode.toInt();
if (status != 200 && status != 206 && status != 416) {
@ -152,60 +579,55 @@ void UpdateChecker::partFinished(qint64 got, qint64 total) {
}
}
if (!already && !full) {
QMutexLocker lock(&mutex);
full = total;
if (!_already && !_size) {
QMutexLocker lock(&_mutex);
_size = total;
}
DEBUG_LOG(("Update Info: part %1 of %2").arg(got).arg(total));
if (!outputFile.isOpen()) {
if (!outputFile.open(QIODevice::Append)) {
LOG(("Update Error: Could not open output file '%1' for appending").arg(outputFile.fileName()));
if (!_output.isOpen()) {
if (!_output.open(QIODevice::Append)) {
LOG(("Update Error: Could not open output file '%1' for appending").arg(_output.fileName()));
return fatalFail();
}
}
QByteArray r = reply->readAll();
QByteArray r = _reply->readAll();
if (!r.isEmpty()) {
outputFile.write(r);
_output.write(r);
QMutexLocker lock(&mutex);
already += r.size();
QMutexLocker lock(&_mutex);
_already += r.size();
}
if (got >= total) {
reply->deleteLater();
reply = 0;
outputFile.close();
_reply.release()->deleteLater();
_output.close();
unpackUpdate();
} else {
Sandbox::updateProgress(already, full);
_parent->onProgress({ _already, _size });
}
}
void UpdateChecker::partFailed(QNetworkReply::NetworkError e) {
if (!reply) return;
void Updater::Private::partFailed(QNetworkReply::NetworkError e) {
if (!_reply) return;
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
reply->deleteLater();
reply = 0;
const auto statusCode = _reply->attribute(
QNetworkRequest::HttpStatusCodeAttribute);
_reply.release()->deleteLater();
if (statusCode.isValid()) {
int status = statusCode.toInt();
if (status == 416) { // Requested range not satisfiable
outputFile.close();
_output.close();
unpackUpdate();
return;
}
}
LOG(("Update Error: failed to download part starting from %1, error %2").arg(already).arg(e));
Sandbox::updateFailed();
LOG(("Update Error: failed to download part starting from %1, error %2").arg(_already).arg(e));
_parent->onFailed();
}
void UpdateChecker::fatalFail() {
clearAll();
Sandbox::updateFailed();
}
void UpdateChecker::clearAll() {
psDeleteDir(cWorkingDir() + qsl("tupdates"));
void Updater::Private::fatalFail() {
ClearAll();
_parent->onFailed();
}
//QString winapiErrorWrap() {
@ -223,9 +645,9 @@ void UpdateChecker::clearAll() {
// return QString::fromWCharArray(errMsg);
//}
void UpdateChecker::unpackUpdate() {
void Updater::Private::unpackUpdate() {
QByteArray packed;
if (!outputFile.open(QIODevice::ReadOnly)) {
if (!_output.open(QIODevice::ReadOnly)) {
LOG(("Update Error: cant read updates file!"));
return fatalFail();
}
@ -236,13 +658,13 @@ void UpdateChecker::unpackUpdate() {
const int32 hSigLen = 128, hShaLen = 20, hPropsLen = 0, hOriginalSizeLen = sizeof(int32), hSize = hSigLen + hShaLen + hOriginalSizeLen; // header
#endif // Q_OS_WIN
QByteArray compressed = outputFile.readAll();
QByteArray compressed = _output.readAll();
int32 compressedLen = compressed.size() - hSize;
if (compressedLen <= 0) {
LOG(("Update Error: bad compressed size: %1").arg(compressed.size()));
return fatalFail();
}
outputFile.close();
_output.close();
QString tempDirPath = cWorkingDir() + qsl("tupdates/temp"), readyFilePath = cWorkingDir() + qsl("tupdates/temp/ready");
psDeleteDir(tempDirPath);
@ -429,8 +851,9 @@ void UpdateChecker::unpackUpdate() {
tempDir.mkdir(QDir(tempDirPath + qsl("/tdata")).absolutePath());
std::wstring versionString = ((version % 1000) ? QString("%1.%2.%3").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000)).arg(int(version % 1000)) : QString("%1.%2").arg(int(version / 1000000)).arg(int((version % 1000000) / 1000))).toStdWString();
VerInt versionNum = VerInt(version), versionLen = VerInt(versionString.size() * sizeof(VerChar));
VerChar versionStr[32];
const auto versionNum = VersionInt(version);
const auto versionLen = VersionInt(versionString.size() * sizeof(VersionChar));
VersionChar versionStr[32];
memcpy(versionStr, versionString.c_str(), versionLen);
QFile fVersion(tempDirPath + qsl("/tdata/version"));
@ -438,11 +861,11 @@ void UpdateChecker::unpackUpdate() {
LOG(("Update Error: cant write version file '%1'").arg(tempDirPath + qsl("/version")));
return fatalFail();
}
fVersion.write((const char*)&versionNum, sizeof(VerInt));
fVersion.write((const char*)&versionNum, sizeof(VersionInt));
if (versionNum == 0x7FFFFFFF) { // beta version
fVersion.write((const char*)&betaVersion, sizeof(quint64));
} else {
fVersion.write((const char*)&versionLen, sizeof(VerInt));
fVersion.write((const char*)&versionLen, sizeof(VersionInt));
fVersion.write((const char*)&versionStr[0], versionLen);
}
fVersion.close();
@ -460,21 +883,16 @@ void UpdateChecker::unpackUpdate() {
LOG(("Update Error: cant create ready file '%1'").arg(readyFilePath));
return fatalFail();
}
outputFile.remove();
_output.remove();
Sandbox::updateReady();
}
UpdateChecker::~UpdateChecker() {
delete reply;
reply = 0;
_parent->onReady();
}
bool checkReadyUpdate() {
QString readyFilePath = cWorkingDir() + qsl("tupdates/temp/ready"), readyPath = cWorkingDir() + qsl("tupdates/temp");
if (!QFile(readyFilePath).exists() || cExeName().isEmpty()) {
if (QDir(cWorkingDir() + qsl("tupdates/ready")).exists() || QDir(cWorkingDir() + qsl("tupdates/temp")).exists()) {
UpdateChecker::clearAll();
ClearAll();
}
return false;
}
@ -485,30 +903,30 @@ bool checkReadyUpdate() {
QFile fVersion(versionPath);
if (!fVersion.open(QIODevice::ReadOnly)) {
LOG(("Update Error: cant read version file '%1'").arg(versionPath));
UpdateChecker::clearAll();
ClearAll();
return false;
}
VerInt versionNum;
if (fVersion.read((char*)&versionNum, sizeof(VerInt)) != sizeof(VerInt)) {
auto versionNum = VersionInt();
if (fVersion.read((char*)&versionNum, sizeof(VersionInt)) != sizeof(VersionInt)) {
LOG(("Update Error: cant read version from file '%1'").arg(versionPath));
UpdateChecker::clearAll();
ClearAll();
return false;
}
if (versionNum == 0x7FFFFFFF) { // beta version
quint64 betaVersion = 0;
if (fVersion.read((char*)&betaVersion, sizeof(quint64)) != sizeof(quint64)) {
LOG(("Update Error: cant read beta version from file '%1'").arg(versionPath));
UpdateChecker::clearAll();
ClearAll();
return false;
}
if (!cBetaVersion() || betaVersion <= cBetaVersion()) {
LOG(("Update Error: cant install beta version %1 having beta version %2").arg(betaVersion).arg(cBetaVersion()));
UpdateChecker::clearAll();
ClearAll();
return false;
}
} else if (versionNum <= AppVersion) {
LOG(("Update Error: cant install version %1 having version %2").arg(versionNum).arg(AppVersion));
UpdateChecker::clearAll();
ClearAll();
return false;
}
fVersion.close();
@ -527,11 +945,11 @@ bool checkReadyUpdate() {
if (!updater.exists()) {
QFileInfo current(curUpdater);
if (!current.exists()) {
UpdateChecker::clearAll();
ClearAll();
return false;
}
if (!QFile(current.absoluteFilePath()).copy(updater.absoluteFilePath())) {
UpdateChecker::clearAll();
ClearAll();
return false;
}
}
@ -542,24 +960,24 @@ bool checkReadyUpdate() {
cSetWriteProtected(true);
return true;
} else {
UpdateChecker::clearAll();
ClearAll();
return false;
}
}
if (DeleteFile(updater.absoluteFilePath().toStdWString().c_str()) == FALSE) {
UpdateChecker::clearAll();
ClearAll();
return false;
}
#elif defined Q_OS_MAC // Q_OS_WIN
QDir().mkpath(QFileInfo(curUpdater).absolutePath());
DEBUG_LOG(("Update Info: moving %1 to %2...").arg(updater.absoluteFilePath()).arg(curUpdater));
if (!objc_moveFile(updater.absoluteFilePath(), curUpdater)) {
UpdateChecker::clearAll();
ClearAll();
return false;
}
#elif defined Q_OS_LINUX // Q_OS_MAC
if (!linuxMoveFile(QFile::encodeName(updater.absoluteFilePath()).constData(), QFile::encodeName(curUpdater).constData())) {
UpdateChecker::clearAll();
ClearAll();
return false;
}
#endif // Q_OS_LINUX
@ -611,3 +1029,5 @@ QString countBetaVersionSignature(uint64 version) { // duplicated in packer.cpp
signature = signature.replace('-', '8').replace('_', 'B');
return QString::fromUtf8(signature.mid(19, 32));
}
} // namespace Core

View File

@ -0,0 +1,58 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Core {
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
class Updater;
class UpdateChecker {
public:
enum class State {
None,
Download,
Ready,
};
struct Progress {
int64 already;
int64 size;
};
UpdateChecker();
rpl::producer<> checking() const;
rpl::producer<> isLatest() const;
rpl::producer<Progress> progress() const;
rpl::producer<> failed() const;
rpl::producer<> ready() const;
void start(bool forceWait = false);
void stop();
State state() const;
int already() const;
int size() const;
private:
const std::shared_ptr<Updater> _updater;
};
bool checkReadyUpdate();
#else // TDESKTOP_DISABLE_AUTOUPDATE
class UpdateChecker {
};
#endif // TDESKTOP_DISABLE_AUTOUPDATE
QString countBetaVersionSignature(uint64 version);
} // namespace Core

View File

@ -20,7 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "application.h"
#include "mainwindow.h"
#include "mainwidget.h"
#include "autoupdater.h"
#include "core/update_checker.h"
#include "auth_session.h"
#include "apiwrap.h"
#include "messenger.h"
@ -119,10 +119,15 @@ DialogsWidget::DialogsWidget(QWidget *parent, not_null<Window::Controller*> cont
connect(_filter, SIGNAL(cursorPositionChanged(int,int)), this, SLOT(onFilterCursorMoved(int,int)));
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
Sandbox::connect(SIGNAL(updateLatest()), this, SLOT(onCheckUpdateStatus()));
Sandbox::connect(SIGNAL(updateFailed()), this, SLOT(onCheckUpdateStatus()));
Sandbox::connect(SIGNAL(updateReady()), this, SLOT(onCheckUpdateStatus()));
onCheckUpdateStatus();
Core::UpdateChecker checker;
rpl::merge(
rpl::single(rpl::empty_value()),
checker.isLatest(),
checker.failed(),
checker.ready()
) | rpl::start_with_next([=] {
checkUpdateStatus();
}, lifetime());
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
subscribe(Adaptive::Changed(), [this] { updateForwardBar(); });
@ -168,13 +173,14 @@ DialogsWidget::DialogsWidget(QWidget *parent, not_null<Window::Controller*> cont
}
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
void DialogsWidget::onCheckUpdateStatus() {
if (Sandbox::updatingState() == Application::UpdatingReady) {
void DialogsWidget::checkUpdateStatus() {
using Checker = Core::UpdateChecker;
if (Checker().state() == Checker::State::Ready) {
if (_updateTelegram) return;
_updateTelegram.create(this);
_updateTelegram->show();
_updateTelegram->setClickedCallback([] {
checkReadyUpdate();
Core::checkReadyUpdate();
App::restart();
});
} else {

View File

@ -120,10 +120,6 @@ public slots:
private slots:
void onDraggingScrollTimer();
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
void onCheckUpdateStatus();
#endif // TDESKTOP_DISABLE_AUTOUPDATE
protected:
void dragEnterEvent(QDragEnterEvent *e) override;
void dragMoveEvent(QDragMoveEvent *e) override;
@ -167,6 +163,10 @@ private:
void updateControlsGeometry();
void updateForwardBar();
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
void checkUpdateStatus();
#endif // TDESKTOP_DISABLE_AUTOUPDATE
bool dialogsFailed(const RPCError &error, mtpRequestId req);
bool searchFailed(DialogsSearchRequestType type, const RPCError &error, mtpRequestId req);
bool peopleFailed(const RPCError &error, mtpRequestId req);

View File

@ -396,7 +396,7 @@ bool CheckBetaVersionDir() {
quint64 v;
QByteArray k;
dataStream >> v >> k;
if (dataStream.status() == QDataStream::Ok) {
if (dataStream.status() == QDataStream::Ok && !k.isEmpty()) {
cSetBetaVersion(qMax(v, AppVersion * 1000ULL));
cSetBetaPrivateKey(k);
cSetRealBetaVersion(v);

View File

@ -26,7 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/labels.h"
#include "ui/wrap/fade_wrap.h"
#include "ui/effects/slide_animation.h"
#include "autoupdater.h"
#include "core/update_checker.h"
#include "window/window_slide_animation.h"
#include "styles/style_boxes.h"
#include "styles/style_intro.h"
@ -42,7 +42,7 @@ constexpr str_const kDefaultCountry = "US";
} // namespace
Widget::Widget(QWidget *parent) : TWidget(parent)
Widget::Widget(QWidget *parent) : RpWidget(parent)
, _back(this, object_ptr<Ui::IconButton>(this, st::introBackButton))
, _settings(
this,
@ -82,10 +82,17 @@ Widget::Widget(QWidget *parent) : TWidget(parent)
cSetPasswordRecovered(false);
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
Sandbox::connect(SIGNAL(updateLatest()), this, SLOT(onCheckUpdateStatus()));
Sandbox::connect(SIGNAL(updateFailed()), this, SLOT(onCheckUpdateStatus()));
Sandbox::connect(SIGNAL(updateReady()), this, SLOT(onCheckUpdateStatus()));
Sandbox::startUpdateCheck();
Core::UpdateChecker checker;
checker.isLatest() | rpl::start_with_next([=] {
onCheckUpdateStatus();
}, lifetime());
checker.failed() | rpl::start_with_next([=] {
onCheckUpdateStatus();
}, lifetime());
checker.ready() | rpl::start_with_next([=] {
onCheckUpdateStatus();
}, lifetime());
checker.start();
onCheckUpdateStatus();
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
}
@ -132,7 +139,7 @@ void Widget::createLanguageLink() {
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
void Widget::onCheckUpdateStatus() {
if (Sandbox::updatingState() == Application::UpdatingReady) {
if (Core::UpdateChecker().state() == Core::UpdateChecker::State::Ready) {
if (_update) return;
_update.create(
this,
@ -144,7 +151,7 @@ void Widget::onCheckUpdateStatus() {
_update->setVisible(true);
}
_update->entity()->setClickedCallback([] {
checkReadyUpdate();
Core::checkReadyUpdate();
App::restart();
});
} else {

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "mtproto/sender.h"
#include "ui/rp_widget.h"
namespace Ui {
class IconButton;
@ -22,7 +23,7 @@ class FadeWrap;
namespace Intro {
class Widget : public TWidget, private MTP::Sender, private base::Subscriber {
class Widget : public Ui::RpWidget, private MTP::Sender, private base::Subscriber {
Q_OBJECT
public:

View File

@ -74,6 +74,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/themes/window_theme.h"
#include "mtproto/dc_options.h"
#include "core/file_utilities.h"
#include "core/update_checker.h"
#include "calls/calls_instance.h"
#include "calls/calls_top_bar.h"
#include "auth_session.h"
@ -323,7 +324,8 @@ MainWidget::MainWidget(
orderWidgets();
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
Sandbox::startUpdateCheck();
Core::UpdateChecker checker;
checker.start();
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
}
@ -704,7 +706,7 @@ void MainWidget::finishForwarding(not_null<History*> history) {
}
void MainWidget::updateMutedIn(TimeMs delay) {
accumulate_max(delay, 24 * 3600 * 1000LL);
accumulate_min(delay, 24 * 3600 * 1000LL);
if (!_updateMutedTimer.isActive()
|| _updateMutedTimer.remainingTime() > delay) {
_updateMutedTimer.start(delay);

View File

@ -23,7 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_file_parser.h"
#include "lang/lang_cloud_manager.h"
#include "messenger.h"
#include "autoupdater.h"
#include "core/update_checker.h"
namespace Settings {
@ -34,21 +34,37 @@ UpdateStateRow::UpdateStateRow(QWidget *parent) : RpWidget(parent)
connect(_check, SIGNAL(clicked()), this, SLOT(onCheck()));
connect(_restart, SIGNAL(clicked()), this, SIGNAL(restart()));
Sandbox::connect(SIGNAL(updateChecking()), this, SLOT(onChecking()));
Sandbox::connect(SIGNAL(updateLatest()), this, SLOT(onLatest()));
Sandbox::connect(SIGNAL(updateProgress(qint64, qint64)), this, SLOT(onDownloading(qint64, qint64)));
Sandbox::connect(SIGNAL(updateFailed()), this, SLOT(onFailed()));
Sandbox::connect(SIGNAL(updateReady()), this, SLOT(onReady()));
_versionText = lng_settings_current_version_label(lt_version, currentVersionText());
switch (Sandbox::updatingState()) {
case Application::UpdatingDownload:
Core::UpdateChecker checker;
checker.checking() | rpl::start_with_next([=] {
onChecking();
}, lifetime());
checker.isLatest() | rpl::start_with_next([=] {
onLatest();
}, lifetime());
checker.progress(
) | rpl::start_with_next([=](Core::UpdateChecker::Progress progress) {
onDownloading(progress.already, progress.size);
}, lifetime());
checker.failed() | rpl::start_with_next([=] {
onFailed();
}, lifetime());
checker.ready() | rpl::start_with_next([=] {
onReady();
}, lifetime());
switch (checker.state()) {
case Core::UpdateChecker::State::Download:
setState(State::Download, true);
setDownloadProgress(Sandbox::updatingReady(), Sandbox::updatingSize());
break;
case Application::UpdatingReady: setState(State::Ready, true); break;
default: setState(State::None, true); break;
setDownloadProgress(checker.already(), checker.size());
break;
case Core::UpdateChecker::State::Ready:
setState(State::Ready, true);
break;
default:
setState(State::None, true);
break;
}
}
@ -89,9 +105,11 @@ void UpdateStateRow::paintEvent(QPaintEvent *e) {
void UpdateStateRow::onCheck() {
if (!cAutoUpdate()) return;
Core::UpdateChecker checker;
setState(State::Check);
cSetLastUpdateCheck(0);
Sandbox::startUpdateCheck();
checker.start();
}
void UpdateStateRow::setState(State state, bool force) {
@ -212,7 +230,7 @@ void GeneralWidget::onChangeLanguage() {
void GeneralWidget::onRestart() {
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
checkReadyUpdate();
Core::checkReadyUpdate();
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
App::restart();
}
@ -224,10 +242,11 @@ void GeneralWidget::onUpdateAutomatically() {
_updateRow->toggle(
cAutoUpdate(),
anim::type::normal);
Core::UpdateChecker checker;
if (cAutoUpdate()) {
Sandbox::startUpdateCheck();
checker.start();
} else {
Sandbox::stopUpdate();
checker.stop();
}
}
#endif // !TDESKTOP_DISABLE_AUTOUPDATE

View File

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/send_files_box.h"
#include "window/themes/window_theme.h"
#include "core/crash_reports.h"
#include "core/update_checker.h"
#include "observer_peer.h"
#include "mainwidget.h"
#include "mainwindow.h"
@ -1251,7 +1252,7 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting
cSetAutoUpdate(v == 1);
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
if (!cAutoUpdate()) {
Sandbox::stopUpdate();
Core::UpdateChecker().stop();
}
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
} break;

View File

@ -158,6 +158,8 @@
<(src_loc)/core/single_timer.cpp
<(src_loc)/core/single_timer.h
<(src_loc)/core/tl_help.h
<(src_loc)/core/update_checker.cpp
<(src_loc)/core/update_checker.h
<(src_loc)/core/utils.cpp
<(src_loc)/core/utils.h
<(src_loc)/core/version.h
@ -725,8 +727,6 @@
<(src_loc)/application.h
<(src_loc)/auth_session.cpp
<(src_loc)/auth_session.h
<(src_loc)/autoupdater.cpp
<(src_loc)/autoupdater.h
<(src_loc)/config.h
<(src_loc)/countries.h
<(src_loc)/facades.cpp