mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Download and applying updates, qtox-updater
The auto-updater is essentially done, except for the GUI. For now this is only an API. The API works, but the tools to manage the update server would need some love
This commit is contained in:
parent
1350fbbaf9
commit
45e20187cc
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -7,3 +7,5 @@ qrc_*
|
|||
Makefile
|
||||
qtox
|
||||
*.qm
|
||||
build-*-Release
|
||||
build-*-Debug
|
||||
|
|
2
qtox.pro
2
qtox.pro
|
@ -57,7 +57,7 @@ contains(JENKINS,YES) {
|
|||
# Rules for Windows, Mac OSX, and Linux
|
||||
win32 {
|
||||
RC_FILE = windows/qtox.rc
|
||||
LIBS += -liphlpapi -L$$PWD/libs/lib -ltoxav -ltoxcore -ltoxencryptsave -ltoxdns -lvpx -lpthread -lsodium
|
||||
LIBS += -liphlpapi -L$$PWD/libs/lib -lsodium -ltoxav -ltoxcore -ltoxencryptsave -ltoxdns -lvpx -lpthread
|
||||
LIBS += -L$$PWD/libs/lib -lopencv_core248 -lopencv_highgui248 -lopencv_imgproc248 -lOpenAL32 -lopus
|
||||
LIBS += -lz -lopengl32 -lole32 -loleaut32 -luuid -lvfw32 -ljpeg -ltiff -lpng -ljasper -lIlmImf -lHalf -lws2_32
|
||||
} else {
|
||||
|
|
|
@ -17,14 +17,25 @@
|
|||
|
||||
#include "src/autoupdate.h"
|
||||
#include "src/misc/serialize.h"
|
||||
#include "src/misc/settings.h"
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QCoreApplication>
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QProcess>
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef Q_OS_WIN
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
const QString AutoUpdater::platform = "win32";
|
||||
const QString AutoUpdater::updaterBin = "qtox-updater.exe";
|
||||
#else
|
||||
const QString AutoUpdater::platform = "win32"; ///TODO: FIXME: undefine, we want an empty qstring
|
||||
const QString AutoUpdater::platform;
|
||||
const QString AutoUpdater::updaterBin;
|
||||
#endif
|
||||
const QString AutoUpdater::updateServer = "http://127.0.0.1";
|
||||
const QString AutoUpdater::checkURI = AutoUpdater::updateServer+"/qtox/"+AutoUpdater::platform+"/version";
|
||||
|
@ -96,20 +107,7 @@ QString AutoUpdater::getUpdateVersion()
|
|||
return version;
|
||||
}
|
||||
|
||||
QList<AutoUpdater::UpdateFileMeta> AutoUpdater::genUpdateDiff()
|
||||
{
|
||||
QList<UpdateFileMeta> diff;
|
||||
|
||||
// Updates only for supported platforms
|
||||
if (platform.isEmpty())
|
||||
return diff;
|
||||
|
||||
QList<UpdateFileMeta> newFlist = getUpdateFlist();
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
QList<AutoUpdater::UpdateFileMeta> AutoUpdater::parseflist(QByteArray flistData)
|
||||
QList<AutoUpdater::UpdateFileMeta> AutoUpdater::parseFlist(QByteArray flistData)
|
||||
{
|
||||
QList<UpdateFileMeta> flist;
|
||||
|
||||
|
@ -148,8 +146,6 @@ QList<AutoUpdater::UpdateFileMeta> AutoUpdater::parseflist(QByteArray flistData)
|
|||
// Parse. We assume no errors handling needed since the signature is valid.
|
||||
while (!flistData.isEmpty())
|
||||
{
|
||||
qDebug() << "Got "<<flistData.size()<<" bytes of data left";
|
||||
|
||||
UpdateFileMeta newFile;
|
||||
|
||||
memcpy(newFile.sig, flistData.data(), crypto_sign_BYTES);
|
||||
|
@ -164,22 +160,15 @@ QList<AutoUpdater::UpdateFileMeta> AutoUpdater::parseflist(QByteArray flistData)
|
|||
newFile.size = dataToUint64(flistData);
|
||||
flistData = flistData.mid(8);
|
||||
|
||||
qDebug() << "AutoUpdater::parseflist: New file:";
|
||||
qDebug() << "- Id: "<<newFile.id;
|
||||
qDebug() << "- Install path: "<<newFile.installpath;
|
||||
qDebug() << "- Size: "<<newFile.size<<" bytes";
|
||||
qDebug() << "- Signature: "<<QByteArray((char*)newFile.sig, crypto_sign_BYTES).toHex();
|
||||
|
||||
flist += newFile;
|
||||
}
|
||||
qDebug() << "AutoUpdater::parseflist: Done parsing flist";
|
||||
|
||||
return flist;
|
||||
}
|
||||
|
||||
QList<AutoUpdater::UpdateFileMeta> AutoUpdater::getUpdateFlist()
|
||||
QByteArray AutoUpdater::getUpdateFlist()
|
||||
{
|
||||
QList<UpdateFileMeta> flist;
|
||||
QByteArray flist;
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager;
|
||||
QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(flistURI)));
|
||||
|
@ -194,10 +183,212 @@ QList<AutoUpdater::UpdateFileMeta> AutoUpdater::getUpdateFlist()
|
|||
return flist;
|
||||
}
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
flist = reply->readAll();
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
|
||||
flist = parseflist(data);
|
||||
return flist;
|
||||
}
|
||||
|
||||
QByteArray AutoUpdater::getLocalFlist()
|
||||
{
|
||||
QByteArray flist;
|
||||
|
||||
QFile flistFile("flist");
|
||||
if (!flistFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qWarning() << "AutoUpdater::getLocalFlist: Can't open local flist";
|
||||
return flist;
|
||||
}
|
||||
|
||||
flist = flistFile.readAll();
|
||||
flistFile.close();
|
||||
|
||||
return flist;
|
||||
}
|
||||
|
||||
QList<AutoUpdater::UpdateFileMeta> AutoUpdater::genUpdateDiff(QList<UpdateFileMeta> updateFlist)
|
||||
{
|
||||
QList<UpdateFileMeta> diff;
|
||||
QList<UpdateFileMeta> localFlist = parseFlist(getLocalFlist());
|
||||
|
||||
for (UpdateFileMeta file : updateFlist)
|
||||
if (!localFlist.contains(file))
|
||||
diff += file;
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
AutoUpdater::UpdateFile AutoUpdater::getUpdateFile(UpdateFileMeta fileMeta)
|
||||
{
|
||||
UpdateFile file;
|
||||
file.metadata = fileMeta;
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager;
|
||||
QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(filesURI+fileMeta.id)));
|
||||
while (!reply->isFinished())
|
||||
qApp->processEvents();
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError)
|
||||
{
|
||||
qWarning() << "AutoUpdater: getUpdateFile: network error: "<<reply->errorString();
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
return file;
|
||||
}
|
||||
|
||||
file.data = reply->readAll();
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
bool AutoUpdater::downloadUpdate()
|
||||
{
|
||||
// Updates only for supported platforms
|
||||
if (platform.isEmpty())
|
||||
return false;
|
||||
|
||||
// Get a list of files to update
|
||||
QByteArray newFlistData = getUpdateFlist();
|
||||
QList<UpdateFileMeta> newFlist = parseFlist(newFlistData);
|
||||
QList<UpdateFileMeta> diff = genUpdateDiff(newFlist);
|
||||
|
||||
qDebug() << "AutoUpdater: Need to update "<<diff.size()<<" files";
|
||||
|
||||
// Create an empty directory to download updates into
|
||||
QString updateDirStr = Settings::getInstance().getSettingsDirPath() + "/update/";
|
||||
QDir updateDir(updateDirStr);
|
||||
if (updateDir.exists())
|
||||
updateDir.removeRecursively();
|
||||
QDir().mkdir(updateDirStr);
|
||||
updateDir = QDir(updateDirStr);
|
||||
if (!updateDir.exists())
|
||||
{
|
||||
qWarning() << "AutoUpdater::downloadUpdate: Can't create update directory, aborting...";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the new flist for the updater
|
||||
QFile newFlistFile(updateDirStr+"flist");
|
||||
if (!newFlistFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
|
||||
{
|
||||
qWarning() << "AutoUpdater::downloadUpdate: Can't save new flist file, aborting...";
|
||||
return false;
|
||||
}
|
||||
newFlistFile.write(newFlistData);
|
||||
newFlistFile.close();
|
||||
|
||||
// Download and write each new file
|
||||
for (UpdateFileMeta fileMeta : diff)
|
||||
{
|
||||
qDebug() << "AutoUpdater: Downloading '"+fileMeta.installpath+"' ...";
|
||||
|
||||
// Create subdirs if necessary
|
||||
QString fileDirStr{QFileInfo(updateDirStr+fileMeta.installpath).absolutePath()};
|
||||
if (!QDir(fileDirStr).exists())
|
||||
QDir().mkpath(fileDirStr);
|
||||
|
||||
// Download
|
||||
UpdateFile file = getUpdateFile(fileMeta);
|
||||
if (file.data.isNull())
|
||||
{
|
||||
qWarning() << "AutoUpdater::downloadUpdate: Error downloading a file, aborting...";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check signature
|
||||
if (crypto_sign_verify_detached(file.metadata.sig, (unsigned char*)file.data.data(),
|
||||
file.data.size(), key) != 0)
|
||||
{
|
||||
qCritical() << "AutoUpdater: downloadUpdate: RECEIVED FORGED FILE, aborting...";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Save
|
||||
QFile fileFile(updateDirStr+fileMeta.installpath);
|
||||
if (!fileFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
|
||||
{
|
||||
qWarning() << "AutoUpdater::downloadUpdate: Can't save new update file, aborting...";
|
||||
return false;
|
||||
}
|
||||
fileFile.write(file.data);
|
||||
fileFile.close();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AutoUpdater::isLocalUpdateReady()
|
||||
{
|
||||
// Updates only for supported platforms
|
||||
if (platform.isEmpty())
|
||||
return false;
|
||||
|
||||
// Check that there's an update dir in the first place, valid or not
|
||||
QString updateDirStr = Settings::getInstance().getSettingsDirPath() + "/update/";
|
||||
QDir updateDir(updateDirStr);
|
||||
if (!updateDir.exists())
|
||||
return false;
|
||||
|
||||
// Check that we have a flist and that every file on the diff exists
|
||||
QFile updateFlistFile(updateDirStr+"flist");
|
||||
if (!updateFlistFile.open(QIODevice::ReadOnly))
|
||||
return false;
|
||||
QByteArray updateFlistData = updateFlistFile.readAll();
|
||||
updateFlistFile.close();
|
||||
|
||||
QList<UpdateFileMeta> updateFlist = parseFlist(updateFlistData);
|
||||
QList<UpdateFileMeta> diff = genUpdateDiff(updateFlist);
|
||||
|
||||
for (UpdateFileMeta fileMeta : diff)
|
||||
if (!QFile::exists(updateDirStr+fileMeta.installpath))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AutoUpdater::installLocalUpdate()
|
||||
{
|
||||
qDebug() << "AutoUpdater: About to start the qTox updater to install a local update";
|
||||
|
||||
// Delete the update if we fail so we don't fail again.
|
||||
|
||||
// Updates only for supported platforms.
|
||||
if (platform.isEmpty())
|
||||
{
|
||||
qCritical() << "AutoUpdater: Failed to start the qTox updater, removing the update and exiting";
|
||||
QString updateDirStr = Settings::getInstance().getSettingsDirPath() + "/update/";
|
||||
QDir(updateDirStr).removeRecursively();
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Workaround QTBUG-7645
|
||||
// QProcess fails silently when elevation is required instead of showing a UAC prompt on Win7/Vista
|
||||
#ifdef Q_OS_WIN
|
||||
int result = (int)::ShellExecuteA(0, "open", updaterBin.toUtf8().constData(), 0, 0, SW_SHOWNORMAL);
|
||||
if (SE_ERR_ACCESSDENIED == result)
|
||||
{
|
||||
// Requesting elevation
|
||||
result = (int)::ShellExecuteA(0, "runas", updaterBin.toUtf8().constData(), 0, 0, SW_SHOWNORMAL);
|
||||
}
|
||||
if (result <= 32)
|
||||
{
|
||||
goto fail;
|
||||
}
|
||||
#else
|
||||
if (!QProcess::startDetached(updaterBin))
|
||||
goto fail;
|
||||
#endif
|
||||
|
||||
exit(0);
|
||||
|
||||
// Centralized error handling
|
||||
fail:
|
||||
qCritical() << "AutoUpdater: Failed to start the qTox updater, removing the update and exiting";
|
||||
QString updateDirStr = Settings::getInstance().getSettingsDirPath() + "/update/";
|
||||
QDir(updateDirStr).removeRecursively();
|
||||
exit(-1);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,16 @@
|
|||
#include <QList>
|
||||
#include <sodium.h>
|
||||
|
||||
/// For now we only support auto updates on Windows, although extending it is not a technical issue.
|
||||
/// Linux and Mac users are expected to use their package managers or update manually through official channels.
|
||||
#ifdef Q_OS_WIN
|
||||
#define AUTOUPDATE_ENABLED 1
|
||||
#else
|
||||
#define AUTOUPDATE_ENABLED 0
|
||||
#endif
|
||||
|
||||
/// Handles checking and applying updates for qTox
|
||||
/// Do *NOT* use auto update unless AUTOUPDATE_ENABLED is defined to 1
|
||||
class AutoUpdater
|
||||
{
|
||||
public:
|
||||
|
@ -32,6 +41,13 @@ public:
|
|||
QString id; ///< Unique id of the file
|
||||
QString installpath; ///< Local path including the file name. May be relative to qtox-updater or absolute
|
||||
uint64_t size; ///< Size in bytes of the file
|
||||
|
||||
bool operator==(const UpdateFileMeta& other)
|
||||
{
|
||||
return (size == other.size
|
||||
&& id == other.id && installpath == other.installpath
|
||||
&& memcmp(sig, other.sig, crypto_sign_BYTES) == 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct UpdateFile
|
||||
|
@ -47,16 +63,33 @@ public:
|
|||
/// Fetch the version string of the last update available from the qTox update server
|
||||
/// Will try to follow qTox's proxy settings, may block and processEvents
|
||||
static QString getUpdateVersion();
|
||||
/// Generates a list of files we need to update
|
||||
/// Will try to download an update, if successful returns true and qTox will apply it after a restart
|
||||
/// Will try to follow qTox's proxy settings, may block and processEvents
|
||||
static QList<UpdateFileMeta> genUpdateDiff();
|
||||
static bool downloadUpdate();
|
||||
/// Returns true if an update is downloaded and ready to be installed
|
||||
/// If so, call installLocalUpdate. If not, call downloadUpdate.
|
||||
/// This only checks that we downloaded an update and didn't stop in the middle, not that every file is still valid
|
||||
static bool isLocalUpdateReady();
|
||||
/// Launches the qTox updater to try to install the local update and exits immediately
|
||||
/// Will not check that the update actually exists, use isLocalUpdateReady first for that
|
||||
/// The qTox updater will restart us after the update is done
|
||||
/// Note: If we fail to start the qTox updater, we will delete the update and exit
|
||||
[[ noreturn ]] static void installLocalUpdate();
|
||||
|
||||
protected:
|
||||
/// Parses and validates a flist file. Returns an empty list on error
|
||||
static QList<UpdateFileMeta> parseflist(QByteArray flistData);
|
||||
/// Get the update server's flist and parse it. Returns an empty list on error
|
||||
static QList<UpdateFileMeta> parseFlist(QByteArray flistData);
|
||||
/// Gets the update server's flist. Returns an empty array on error
|
||||
/// Will try to follow qTox's proxy settings, may block and processEvents
|
||||
static QList<UpdateFileMeta> getUpdateFlist();
|
||||
static QByteArray getUpdateFlist();
|
||||
/// Gets the local flist. Returns an empty array on error
|
||||
static QByteArray getLocalFlist();
|
||||
/// Generates a list of files we need to update
|
||||
static QList<UpdateFileMeta> genUpdateDiff(QList<UpdateFileMeta> updateFlist);
|
||||
/// Tries to fetch the file from the update server. Returns a file with a null QByteArray on error.
|
||||
/// Note that a file with an empty but non-null QByteArray is not an error, merely a file of size 0.
|
||||
/// Will try to follow qTox's proxy settings, may block and processEvents
|
||||
static UpdateFile getUpdateFile(UpdateFileMeta fileMeta);
|
||||
|
||||
private:
|
||||
AutoUpdater() = delete;
|
||||
|
@ -68,6 +101,7 @@ private:
|
|||
static const QString checkURI; ///< URI of the file containing the latest version string
|
||||
static const QString flistURI; ///< URI of the file containing info on each file (hash, signature, size, name, ..)
|
||||
static const QString filesURI; ///< URI of the actual files of the latest version
|
||||
static const QString updaterBin; ///< Path to the qtox-updater binary
|
||||
static unsigned char key[];
|
||||
};
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "src/ipc.h"
|
||||
#include "src/widget/toxuri.h"
|
||||
#include "src/widget/toxsave.h"
|
||||
#include "src/autoupdate.h"
|
||||
#include <QApplication>
|
||||
#include <QFontDatabase>
|
||||
#include <QDebug>
|
||||
|
@ -87,6 +88,12 @@ int main(int argc, char *argv[])
|
|||
// Install Unicode 6.1 supporting font
|
||||
QFontDatabase::addApplicationFont("://DejaVuSans.ttf");
|
||||
|
||||
// Check whether we have an update waiting to be installed
|
||||
#if AUTOUPDATE_ENABLED
|
||||
if (AutoUpdater::isLocalUpdateReady())
|
||||
AutoUpdater::installLocalUpdate(); ///< NORETURN
|
||||
#endif
|
||||
|
||||
// Inter-process communication
|
||||
IPC ipc;
|
||||
ipc.registerEventHandler(&toxURIEventHandler);
|
||||
|
|
|
@ -196,16 +196,6 @@ void Widget::init()
|
|||
addFriendForm = new AddFriendForm;
|
||||
settingsWidget = new SettingsWidget();
|
||||
|
||||
// Check for updates
|
||||
{
|
||||
QString newVersion = AutoUpdater::getUpdateVersion();
|
||||
if (!newVersion.isEmpty() && newVersion != GIT_VERSION)
|
||||
{
|
||||
qWarning() << "New update:"<<newVersion;
|
||||
AutoUpdater::genUpdateDiff();
|
||||
}
|
||||
}
|
||||
|
||||
connect(core, &Core::connected, this, &Widget::onConnected);
|
||||
connect(core, &Core::disconnected, this, &Widget::onDisconnected);
|
||||
connect(core, &Core::failedToStart, this, &Widget::onFailedToStartCore);
|
||||
|
|
28
updater/main.cpp
Normal file
28
updater/main.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
|
||||
#include "widget.h"
|
||||
#include <QApplication>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
Widget w;
|
||||
w.show();
|
||||
|
||||
return a.exec();
|
||||
}
|
5
updater/res.qrc
Normal file
5
updater/res.qrc
Normal file
|
@ -0,0 +1,5 @@
|
|||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>res/qtox-256x256.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
BIN
updater/res/qtox-256x256.png
Normal file
BIN
updater/res/qtox-256x256.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.5 KiB |
254
updater/serialize.cpp
Normal file
254
updater/serialize.cpp
Normal file
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
|
||||
#include "serialize.h"
|
||||
|
||||
QByteArray doubleToData(double num)
|
||||
{
|
||||
union
|
||||
{
|
||||
char tab[8];
|
||||
double n;
|
||||
} castUnion;
|
||||
//char n[8];
|
||||
//*((double*) n) = num;
|
||||
|
||||
castUnion.n=num;
|
||||
return QByteArray(castUnion.tab,8);
|
||||
}
|
||||
|
||||
QByteArray floatToData(float num)
|
||||
{
|
||||
union
|
||||
{
|
||||
char tab[4];
|
||||
float n;
|
||||
} castUnion;
|
||||
|
||||
castUnion.n=num;
|
||||
return QByteArray(castUnion.tab,4);
|
||||
}
|
||||
|
||||
float dataToFloat(QByteArray data)
|
||||
{
|
||||
union
|
||||
{
|
||||
char tab[4];
|
||||
float n;
|
||||
} castUnion;
|
||||
|
||||
castUnion.tab[0]=data.data()[0];
|
||||
castUnion.tab[1]=data.data()[1];
|
||||
castUnion.tab[2]=data.data()[2];
|
||||
castUnion.tab[3]=data.data()[3];
|
||||
return castUnion.n;
|
||||
}
|
||||
|
||||
// Converts a string into PNet string data
|
||||
QByteArray stringToData(QString str)
|
||||
{
|
||||
QByteArray data(4,0);
|
||||
// Write the size in a Uint of variable lenght (8-32 bits)
|
||||
int i=0;
|
||||
uint num1 = (uint)str.toUtf8().size();
|
||||
while (num1 >= 0x80)
|
||||
{
|
||||
data[i] = (unsigned char)(num1 | 0x80); i++;
|
||||
num1 = num1 >> 7;
|
||||
}
|
||||
data[i]=num1;
|
||||
data.resize(i+1);
|
||||
data+=str.toUtf8();
|
||||
return data;
|
||||
}
|
||||
|
||||
QString dataToString(QByteArray data)
|
||||
{
|
||||
// Variable UInt32
|
||||
unsigned char num3;
|
||||
int num = 0;
|
||||
int num2 = 0;
|
||||
int i=0;
|
||||
do
|
||||
{
|
||||
num3 = data[i]; i++;
|
||||
num |= (num3 & 0x7f) << num2;
|
||||
num2 += 7;
|
||||
} while ((num3 & 0x80) != 0);
|
||||
unsigned int strlen = (uint) num;
|
||||
|
||||
if (!strlen)
|
||||
return QString();
|
||||
|
||||
data = data.right(data.size()-i); // Remove the strlen
|
||||
data.truncate(strlen);
|
||||
return QString(data);
|
||||
}
|
||||
|
||||
float dataToRangedSingle(float min, float max, int numberOfBits, QByteArray data)
|
||||
{
|
||||
uint endvalue=0;
|
||||
uint value=0;
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
endvalue = (uchar)data[0];
|
||||
goto done;
|
||||
}
|
||||
value = (uchar)data[0];
|
||||
numberOfBits -= 8;
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
endvalue = (value | ((uint) ((uchar)data[1]) << 8));
|
||||
goto done;
|
||||
}
|
||||
value |= (uint) (((uchar)data[1]) << 8);
|
||||
numberOfBits -= 8;
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
uint num2 = (uint) (((uchar)data[2]) << 0x10);
|
||||
endvalue = (value | num2);
|
||||
goto done;
|
||||
}
|
||||
value |= (uint) (((uchar)data[2]) << 0x10);
|
||||
numberOfBits -= 8;
|
||||
endvalue = (value | ((uint) (((uchar)data[3]) << 0x18)));
|
||||
goto done;
|
||||
|
||||
done:
|
||||
|
||||
float num = max - min;
|
||||
int num2 = (((int) 1) << numberOfBits) - 1;
|
||||
float num3 = endvalue;
|
||||
float num4 = num3 / ((float) num2);
|
||||
return (min + (num4 * num));
|
||||
}
|
||||
|
||||
QByteArray rangedSingleToData(float value, float min, float max, int numberOfBits)
|
||||
{
|
||||
QByteArray data;
|
||||
float num = max - min;
|
||||
float num2 = (value - min) / num;
|
||||
int num3 = (((int) 1) << numberOfBits) - 1;
|
||||
uint source = num3 * num2;
|
||||
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
data += (unsigned char)source;
|
||||
return data;
|
||||
}
|
||||
data += (unsigned char)source;
|
||||
numberOfBits -= 8;
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
data += (unsigned char)source>>8;
|
||||
return data;
|
||||
}
|
||||
data += (unsigned char)source>>8;
|
||||
numberOfBits -= 8;
|
||||
if (numberOfBits <= 8)
|
||||
{
|
||||
data += (unsigned char)source>>16;
|
||||
return data;
|
||||
}
|
||||
data += (unsigned char)source>>16;
|
||||
data += (unsigned char)source>>24;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
uint8_t dataToUint8(QByteArray data)
|
||||
{
|
||||
return (uint8_t)data[0];
|
||||
}
|
||||
|
||||
uint16_t dataToUint16(QByteArray data)
|
||||
{
|
||||
return ((uint16_t)(uint8_t)data[0])
|
||||
+(((uint16_t)(uint8_t)data[1])<<8);
|
||||
}
|
||||
|
||||
uint32_t dataToUint32(QByteArray data)
|
||||
{
|
||||
return ((uint32_t)(uint8_t)data[0])
|
||||
+(((uint32_t)(uint8_t)data[1])<<8)
|
||||
+(((uint32_t)(uint8_t)data[2])<<16)
|
||||
+(((uint32_t)(uint8_t)data[3])<<24);
|
||||
}
|
||||
|
||||
uint64_t dataToUint64(QByteArray data)
|
||||
{
|
||||
return ((uint64_t)(uint8_t)data[0])
|
||||
+(((uint64_t)(uint8_t)data[1])<<8)
|
||||
+(((uint64_t)(uint8_t)data[2])<<16)
|
||||
+(((uint64_t)(uint8_t)data[3])<<24)
|
||||
+(((uint64_t)(uint8_t)data[4])<<32)
|
||||
+(((uint64_t)(uint8_t)data[5])<<40)
|
||||
+(((uint64_t)(uint8_t)data[6])<<48)
|
||||
+(((uint64_t)(uint8_t)data[7])<<56);
|
||||
}
|
||||
|
||||
unsigned getVUint32Size(QByteArray data)
|
||||
{
|
||||
unsigned lensize=0;
|
||||
{
|
||||
unsigned char num3;
|
||||
do {
|
||||
num3 = data[lensize];
|
||||
lensize++;
|
||||
} while ((num3 & 0x80) != 0);
|
||||
}
|
||||
return lensize;
|
||||
}
|
||||
|
||||
QByteArray uint8ToData(uint8_t num)
|
||||
{
|
||||
QByteArray data(1,0);
|
||||
data[0] = (uint8_t)num;
|
||||
return data;
|
||||
}
|
||||
|
||||
QByteArray uint16ToData(uint16_t num)
|
||||
{
|
||||
QByteArray data(2,0);
|
||||
data[0] = (uint8_t)(num & 0xFF);
|
||||
data[1] = (uint8_t)((num>>8) & 0xFF);
|
||||
return data;
|
||||
}
|
||||
|
||||
QByteArray uint32ToData(uint32_t num)
|
||||
{
|
||||
QByteArray data(4,0);
|
||||
data[0] = (uint8_t)(num & 0xFF);
|
||||
data[1] = (uint8_t)((num>>8) & 0xFF);
|
||||
data[2] = (uint8_t)((num>>16) & 0xFF);
|
||||
data[3] = (uint8_t)((num>>24) & 0xFF);
|
||||
return data;
|
||||
}
|
||||
|
||||
QByteArray uint64ToData(uint64_t num)
|
||||
{
|
||||
QByteArray data(8,0);
|
||||
data[0] = (uint8_t)(num & 0xFF);
|
||||
data[1] = (uint8_t)((num>>8) & 0xFF);
|
||||
data[2] = (uint8_t)((num>>16) & 0xFF);
|
||||
data[3] = (uint8_t)((num>>24) & 0xFF);
|
||||
data[4] = (uint8_t)((num>>32) & 0xFF);
|
||||
data[5] = (uint8_t)((num>>40) & 0xFF);
|
||||
data[6] = (uint8_t)((num>>48) & 0xFF);
|
||||
data[7] = (uint8_t)((num>>56) & 0xFF);
|
||||
return data;
|
||||
}
|
45
updater/serialize.h
Normal file
45
updater/serialize.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SERIALIZE_H
|
||||
#define SERIALIZE_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
|
||||
/// Most of those functions are unsafe unless otherwise specified
|
||||
/// Do not use them on untrusted data (e.g. check a signature first)
|
||||
|
||||
QByteArray doubleToData(double num);
|
||||
QByteArray floatToData(float num);
|
||||
float dataToFloat(QByteArray data);
|
||||
QByteArray stringToData(QString str);
|
||||
QString dataToString(QByteArray data);
|
||||
float dataToRangedSingle(float min, float max, int numberOfBits, QByteArray data);
|
||||
QByteArray rangedSingleToData(float value, float min, float max, int numberOfBits);
|
||||
uint8_t dataToUint8(QByteArray data);
|
||||
uint16_t dataToUint16(QByteArray data);
|
||||
uint32_t dataToUint32(QByteArray data);
|
||||
uint64_t dataToUint64(QByteArray data);
|
||||
unsigned getVUint32Size(QByteArray data);
|
||||
QByteArray uint8ToData(uint8_t num);
|
||||
QByteArray uint16ToData(uint16_t num);
|
||||
QByteArray uint32ToData(uint32_t num);
|
||||
QByteArray uint64ToData(uint64_t num);
|
||||
|
||||
#endif // SERIALIZE_H
|
52
updater/settingsDir.cpp
Normal file
52
updater/settingsDir.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
|
||||
#include "settingsDir.h"
|
||||
#include <QFile>
|
||||
#include <QSettings>
|
||||
#include <QDir>
|
||||
#include <QStandardPaths>
|
||||
|
||||
const QString FILENAME = "settings.ini";
|
||||
|
||||
QString getSettingsDirPath()
|
||||
{
|
||||
if (isToxPortableEnabled())
|
||||
return ".";
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
return QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
|
||||
+ QDir::separator() + "AppData" + QDir::separator() + "Roaming" + QDir::separator() + "tox");
|
||||
#else
|
||||
return QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QDir::separator() + "tox");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool isToxPortableEnabled()
|
||||
{
|
||||
QFile portableSettings(FILENAME);
|
||||
if (portableSettings.exists())
|
||||
{
|
||||
QSettings ps(FILENAME, QSettings::IniFormat);
|
||||
ps.beginGroup("General");
|
||||
return ps.value("makeToxPortable", false).toBool();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
27
updater/settingsDir.h
Normal file
27
updater/settingsDir.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SETTINGSDIR_H
|
||||
#define SETTINGSDIR_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
QString getSettingsDirPath();
|
||||
|
||||
bool isToxPortableEnabled();
|
||||
|
||||
#endif // SETTINGSDIR_H
|
115
updater/update.cpp
Normal file
115
updater/update.cpp
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
|
||||
#include "update.h"
|
||||
#include "serialize.h"
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
|
||||
unsigned char key[crypto_sign_PUBLICKEYBYTES] =
|
||||
{
|
||||
0xa5, 0x80, 0xf3, 0xb7, 0xd0, 0x10, 0xc0, 0xf9, 0xd6, 0xcf, 0x48, 0x15, 0x99, 0x70, 0x92, 0x49,
|
||||
0xf6, 0xe8, 0xe5, 0xe2, 0x6c, 0x73, 0x8c, 0x48, 0x25, 0xed, 0x01, 0x72, 0xf7, 0x6c, 0x17, 0x28
|
||||
};
|
||||
|
||||
QByteArray getLocalFlist()
|
||||
{
|
||||
QByteArray flist;
|
||||
|
||||
QFile flistFile("flist");
|
||||
if (!flistFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qWarning() << "getLocalFlist: Can't open local flist";
|
||||
return flist;
|
||||
}
|
||||
|
||||
flist = flistFile.readAll();
|
||||
flistFile.close();
|
||||
|
||||
return flist;
|
||||
}
|
||||
|
||||
QList<UpdateFileMeta> genUpdateDiff(QList<UpdateFileMeta> updateFlist)
|
||||
{
|
||||
QList<UpdateFileMeta> diff;
|
||||
QList<UpdateFileMeta> localFlist = parseFlist(getLocalFlist());
|
||||
|
||||
for (UpdateFileMeta file : updateFlist)
|
||||
if (!localFlist.contains(file))
|
||||
diff += file;
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
QList<UpdateFileMeta> parseFlist(QByteArray flistData)
|
||||
{
|
||||
QList<UpdateFileMeta> flist;
|
||||
|
||||
if (flistData.isEmpty())
|
||||
{
|
||||
qWarning() << "AutoUpdater::parseflist: Empty data";
|
||||
return flist;
|
||||
}
|
||||
|
||||
// Check version
|
||||
if (flistData[0] != '1')
|
||||
{
|
||||
qWarning() << "AutoUpdater: parseflist: Bad version "<<(uint8_t)flistData[0];
|
||||
return flist;
|
||||
}
|
||||
flistData = flistData.mid(1);
|
||||
|
||||
// Check signature
|
||||
if (flistData.size() < (int)(crypto_sign_BYTES))
|
||||
{
|
||||
qWarning() << "AutoUpdater::parseflist: Truncated data";
|
||||
return flist;
|
||||
}
|
||||
else
|
||||
{
|
||||
QByteArray msgData = flistData.mid(crypto_sign_BYTES);
|
||||
unsigned char* msg = (unsigned char*)msgData.data();
|
||||
if (crypto_sign_verify_detached((unsigned char*)flistData.data(), msg, msgData.size(), key) != 0)
|
||||
{
|
||||
qCritical() << "AutoUpdater: parseflist: FORGED FLIST FILE";
|
||||
return flist;
|
||||
}
|
||||
flistData = flistData.mid(crypto_sign_BYTES);
|
||||
}
|
||||
|
||||
// Parse. We assume no errors handling needed since the signature is valid.
|
||||
while (!flistData.isEmpty())
|
||||
{
|
||||
UpdateFileMeta newFile;
|
||||
|
||||
memcpy(newFile.sig, flistData.data(), crypto_sign_BYTES);
|
||||
flistData = flistData.mid(crypto_sign_BYTES);
|
||||
|
||||
newFile.id = dataToString(flistData);
|
||||
flistData = flistData.mid(newFile.id.size() + getVUint32Size(flistData));
|
||||
|
||||
newFile.installpath = dataToString(flistData);
|
||||
flistData = flistData.mid(newFile.installpath.size() + getVUint32Size(flistData));
|
||||
|
||||
newFile.size = dataToUint64(flistData);
|
||||
flistData = flistData.mid(8);
|
||||
|
||||
flist += newFile;
|
||||
}
|
||||
|
||||
return flist;
|
||||
}
|
55
updater/update.h
Normal file
55
updater/update.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UPDATE_H
|
||||
#define UPDATE_H
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QString>
|
||||
#include <sodium.h>
|
||||
|
||||
struct UpdateFileMeta
|
||||
{
|
||||
unsigned char sig[crypto_sign_BYTES]; ///< Signature of the file (ed25519)
|
||||
QString id; ///< Unique id of the file
|
||||
QString installpath; ///< Local path including the file name. May be relative to qtox-updater or absolute
|
||||
uint64_t size; ///< Size in bytes of the file
|
||||
|
||||
bool operator==(const UpdateFileMeta& other)
|
||||
{
|
||||
return (size == other.size
|
||||
&& id == other.id && installpath == other.installpath
|
||||
&& memcmp(sig, other.sig, crypto_sign_BYTES) == 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct UpdateFile
|
||||
{
|
||||
UpdateFileMeta metadata;
|
||||
QByteArray data;
|
||||
};
|
||||
|
||||
/// Gets the local flist. Returns an empty array on error
|
||||
QByteArray getLocalFlist();
|
||||
/// Parses and validates a flist file. Returns an empty list on error
|
||||
QList<UpdateFileMeta> parseFlist(QByteArray flistData);
|
||||
/// Generates a list of files we need to update
|
||||
QList<UpdateFileMeta> genUpdateDiff(QList<UpdateFileMeta> updateFlist);
|
||||
|
||||
extern unsigned char key[crypto_sign_PUBLICKEYBYTES];
|
||||
|
||||
#endif // UPDATE_H
|
36
updater/updater.pro
Normal file
36
updater/updater.pro
Normal file
|
@ -0,0 +1,36 @@
|
|||
#-------------------------------------------------
|
||||
#
|
||||
# Project created by QtCreator 2014-11-09T21:09:08
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
QT += core gui
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
|
||||
TARGET = qtox-updater
|
||||
TEMPLATE = app
|
||||
|
||||
CONFIG += c++11
|
||||
|
||||
SOURCES += main.cpp\
|
||||
widget.cpp \
|
||||
settingsDir.cpp \
|
||||
update.cpp \
|
||||
serialize.cpp
|
||||
|
||||
HEADERS += widget.h \
|
||||
settingsDir.h \
|
||||
update.h \
|
||||
serialize.h
|
||||
|
||||
FORMS += widget.ui
|
||||
|
||||
RESOURCES += \
|
||||
res.qrc
|
||||
|
||||
INCLUDEPATH += libs/include
|
||||
|
||||
RC_FILE = windows/updater.rc
|
||||
|
||||
LIBS += -L$$PWD/libs/lib/ -lsodium
|
156
updater/widget.cpp
Normal file
156
updater/widget.cpp
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
|
||||
#include "widget.h"
|
||||
#include "ui_widget.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QProcess>
|
||||
#include <QMessageBox>
|
||||
#include <QMetaObject>
|
||||
|
||||
#include "settingsDir.h"
|
||||
#include "update.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
const bool supported = true;
|
||||
const QString QTOX_PATH = "qtox.exe";
|
||||
#else
|
||||
const bool supported = false;
|
||||
const QString QTOX_PATH;
|
||||
#endif
|
||||
|
||||
Widget::Widget(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::Widget)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
// Updates only for supported platforms
|
||||
if (!supported)
|
||||
{
|
||||
fatalError(tr("The qTox updater is not supported on this platform."));
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
Widget::~Widget()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void Widget::setProgress(int value)
|
||||
{
|
||||
ui->progress->setValue(value);
|
||||
qApp->processEvents();
|
||||
}
|
||||
|
||||
void Widget::fatalError(QString message)
|
||||
{
|
||||
QMessageBox::critical(this,tr("Error"), message+'\n'+tr("qTox will restart now."));
|
||||
deleteUpdate();
|
||||
startQToxAndExit();
|
||||
}
|
||||
|
||||
void Widget::deleteUpdate()
|
||||
{
|
||||
QDir updateDir(getSettingsDirPath()+"/update/");
|
||||
updateDir.removeRecursively();
|
||||
}
|
||||
|
||||
void Widget::startQToxAndExit()
|
||||
{
|
||||
QProcess::startDetached(QTOX_PATH);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
void Widget::update()
|
||||
{
|
||||
/// 1. Find and parse the update (0-5%)
|
||||
// Check that the dir exists
|
||||
QString updateDirStr = getSettingsDirPath()+"/update/";
|
||||
QDir updateDir(updateDirStr);
|
||||
if (!updateDir.exists())
|
||||
fatalError(tr("No update found."));
|
||||
|
||||
// Check that we have a flist and that every file on the diff exists
|
||||
QFile updateFlistFile(updateDirStr+"flist");
|
||||
if (!updateFlistFile.open(QIODevice::ReadOnly))
|
||||
fatalError(tr("The update is incomplete."));
|
||||
QByteArray updateFlistData = updateFlistFile.readAll();
|
||||
updateFlistFile.close();
|
||||
|
||||
setProgress(1);
|
||||
|
||||
QList<UpdateFileMeta> updateFlist = parseFlist(updateFlistData);
|
||||
setProgress(2);
|
||||
QList<UpdateFileMeta> diff = genUpdateDiff(updateFlist);
|
||||
setProgress(4);
|
||||
for (UpdateFileMeta fileMeta : diff)
|
||||
if (!QFile::exists(updateDirStr+fileMeta.installpath))
|
||||
fatalError(tr("The update is incomplete."));
|
||||
setProgress(5);
|
||||
|
||||
/// 2. Check the update (5-50%)
|
||||
float checkProgressStep = 45/diff.size();
|
||||
float checkProgress = 5;
|
||||
for (UpdateFileMeta fileMeta : diff)
|
||||
{
|
||||
UpdateFile file;
|
||||
file.metadata = fileMeta;
|
||||
|
||||
QFile fileFile(updateDirStr+fileMeta.installpath);
|
||||
if (!fileFile.open(QIODevice::ReadOnly))
|
||||
fatalError(tr("Update files are unreadable."));
|
||||
file.data = fileFile.readAll();
|
||||
fileFile.close();
|
||||
|
||||
if (file.data.size() != (int)fileMeta.size)
|
||||
fatalError(tr("Update files are corrupted."));
|
||||
|
||||
if (crypto_sign_verify_detached(file.metadata.sig, (unsigned char*)file.data.data(),
|
||||
file.data.size(), key) != 0)
|
||||
fatalError(tr("Update files are corrupted."));
|
||||
|
||||
checkProgress += checkProgressStep;
|
||||
setProgress(checkProgress);
|
||||
}
|
||||
setProgress(50);
|
||||
|
||||
/// 3. Install the update (50-95%)
|
||||
float installProgressStep = 45/diff.size();
|
||||
float installProgress = 50;
|
||||
for (UpdateFileMeta fileMeta : diff)
|
||||
{
|
||||
QFile fileFile(updateDirStr+fileMeta.installpath);
|
||||
if (!fileFile.copy(fileMeta.installpath))
|
||||
fatalError(tr("Unable to copy the update's files."));
|
||||
|
||||
installProgress += installProgressStep;
|
||||
setProgress(installProgress);
|
||||
}
|
||||
setProgress(95);
|
||||
|
||||
/// 4. Delete the update (95-100%)
|
||||
deleteUpdate();
|
||||
setProgress(100);
|
||||
|
||||
/// 5. Start qTox and exit
|
||||
startQToxAndExit();
|
||||
}
|
51
updater/widget.h
Normal file
51
updater/widget.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef WIDGET_H
|
||||
#define WIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace Ui {
|
||||
class Widget;
|
||||
}
|
||||
|
||||
class Widget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit Widget(QWidget *parent = 0);
|
||||
~Widget();
|
||||
|
||||
// Utilities
|
||||
void setProgress(int value);
|
||||
|
||||
// Noreturn
|
||||
void fatalError(QString message); ///< Calls deleteUpdate and startQToxAndExit
|
||||
void deleteUpdate();
|
||||
void startQToxAndExit();
|
||||
|
||||
public slots:
|
||||
// Finds and applies the update
|
||||
void update();
|
||||
|
||||
private:
|
||||
Ui::Widget *ui;
|
||||
};
|
||||
|
||||
#endif // WIDGET_H
|
139
updater/widget.ui
Normal file
139
updater/widget.ui
Normal file
|
@ -0,0 +1,139 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Widget</class>
|
||||
<widget class="QWidget" name="Widget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>401</width>
|
||||
<height>224</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>qTox Updater</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="res.qrc">
|
||||
<normaloff>:/res/qtox-256x256.png</normaloff>:/res/qtox-256x256.png</iconset>
|
||||
</property>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>13</y>
|
||||
<width>191</width>
|
||||
<height>191</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="pixmap">
|
||||
<pixmap resource="res.qrc">:/res/qtox-256x256.png</pixmap>
|
||||
</property>
|
||||
<property name="scaledContents">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QProgressBar" name="progress">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>206</x>
|
||||
<y>95</y>
|
||||
<width>171</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="invertedAppearance">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>205</x>
|
||||
<y>115</y>
|
||||
<width>171</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Updating qTox ...</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>201</x>
|
||||
<y>170</y>
|
||||
<width>181</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><a href="https://tox.im">https://tox.im</a></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>200</x>
|
||||
<y>183</y>
|
||||
<width>181</width>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><a href="https://github.com/tux3/qtox">https://github.com/tux3/qtox</a></string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>195</x>
|
||||
<y>32</y>
|
||||
<width>191</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>14</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>qTox Update</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources>
|
||||
<include location="res.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
12
updater/windows/updater.exe.manifest
Normal file
12
updater/windows/updater.exe.manifest
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel
|
||||
level="requireAdministrator"
|
||||
uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
</assembly>
|
BIN
updater/windows/updater.ico
Normal file
BIN
updater/windows/updater.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
2
updater/windows/updater.rc
Normal file
2
updater/windows/updater.rc
Normal file
|
@ -0,0 +1,2 @@
|
|||
ID_ICON ICON DISCARDABLE "updater.ico"
|
||||
1 24 "updater.exe.manifest"
|
Loading…
Reference in New Issue
Block a user