mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
Teach the AutoUpdater to read a flist
We can now read the list of files in an update, along with the install path, file size, and signature Everything, metadata and files, is signed with ed25519, but not encrypted
This commit is contained in:
parent
747fd33592
commit
965254d151
6
qtox.pro
6
qtox.pro
|
@ -147,7 +147,8 @@ HEADERS += src/widget/form/addfriendform.h \
|
|||
src/widget/toxuri.h \
|
||||
src/toxdns.h \
|
||||
src/widget/toxsave.h \
|
||||
src/autoupdate.h
|
||||
src/autoupdate.h \
|
||||
src/misc/serialize.h
|
||||
|
||||
SOURCES += \
|
||||
src/widget/form/addfriendform.cpp \
|
||||
|
@ -211,4 +212,5 @@ SOURCES += \
|
|||
src/toxdns.cpp \
|
||||
src/ipc.cpp \
|
||||
src/widget/toxsave.cpp \
|
||||
src/autoupdate.cpp
|
||||
src/autoupdate.cpp \
|
||||
src/misc/serialize.cpp
|
||||
|
|
|
@ -1,10 +1,26 @@
|
|||
/*
|
||||
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 "src/autoupdate.h"
|
||||
#include "src/misc/serialize.h"
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <sodium.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
const QString AutoUpdater::platform = "win32";
|
||||
#else
|
||||
|
@ -38,7 +54,6 @@ QString AutoUpdater::getUpdateVersion()
|
|||
return version;
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager;
|
||||
|
||||
QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(checkURI)));
|
||||
while (!reply->isFinished())
|
||||
qApp->processEvents();
|
||||
|
@ -46,15 +61,21 @@ QString AutoUpdater::getUpdateVersion()
|
|||
if (reply->error() != QNetworkReply::NoError)
|
||||
{
|
||||
qWarning() << "AutoUpdater: getUpdateVersion: network error: "<<reply->errorString();
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
return version;
|
||||
}
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
if (data.size() < (int)(1+crypto_sign_BYTES))
|
||||
return version;
|
||||
|
||||
// Check updater protocol version
|
||||
if ((int)data[0] != '1')
|
||||
{
|
||||
qWarning() << "AutoUpdater: getUpdateVersion: Bad protocol version "<<(uint8_t)data[0];
|
||||
qWarning() << "AutoUpdater: getUpdateVersion: Bad version "<<(uint8_t)data[0];
|
||||
return version;
|
||||
}
|
||||
|
||||
|
@ -74,3 +95,109 @@ 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<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())
|
||||
{
|
||||
qDebug() << "Got "<<flistData.size()<<" bytes of data left";
|
||||
|
||||
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);
|
||||
|
||||
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()
|
||||
{
|
||||
QList<UpdateFileMeta> flist;
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager;
|
||||
QNetworkReply* reply = manager->get(QNetworkRequest(QUrl(flistURI)));
|
||||
while (!reply->isFinished())
|
||||
qApp->processEvents();
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError)
|
||||
{
|
||||
qWarning() << "AutoUpdater: getUpdateFlist: network error: "<<reply->errorString();
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
return flist;
|
||||
}
|
||||
|
||||
QByteArray data = reply->readAll();
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
|
||||
flist = parseflist(data);
|
||||
return flist;
|
||||
}
|
||||
|
|
|
@ -19,10 +19,27 @@
|
|||
#define AUTOUPDATE_H
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <sodium.h>
|
||||
|
||||
/// Handles checking and applying updates for qTox
|
||||
class AutoUpdater
|
||||
{
|
||||
public:
|
||||
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
|
||||
};
|
||||
|
||||
struct UpdateFile
|
||||
{
|
||||
UpdateFileMeta metadata;
|
||||
QByteArray data;
|
||||
};
|
||||
|
||||
public:
|
||||
/// Connects to the qTox update server, returns true if an update is available for download
|
||||
/// Will call getUpdateVersion, and as such may block and processEvents
|
||||
|
@ -30,6 +47,16 @@ 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 follow qTox's proxy settings, may block and processEvents
|
||||
static QList<UpdateFileMeta> genUpdateDiff();
|
||||
|
||||
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
|
||||
/// Will try to follow qTox's proxy settings, may block and processEvents
|
||||
static QList<UpdateFileMeta> getUpdateFlist();
|
||||
|
||||
private:
|
||||
AutoUpdater() = delete;
|
||||
|
|
254
src/misc/serialize.cpp
Normal file
254
src/misc/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 "src/misc/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
src/misc/serialize.h
Normal file
45
src/misc/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
|
|
@ -31,6 +31,9 @@
|
|||
#include "src/video/camera.h"
|
||||
#include "form/chatform.h"
|
||||
#include "maskablepixmapwidget.h"
|
||||
#include "src/historykeeper.h"
|
||||
#include "form/inputpassworddialog.h"
|
||||
#include "src/autoupdate.h"
|
||||
#include <QMessageBox>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
@ -45,9 +48,7 @@
|
|||
#include <QTimer>
|
||||
#include <QStyleFactory>
|
||||
#include <QTranslator>
|
||||
#include "src/historykeeper.h"
|
||||
#include <tox/tox.h>
|
||||
#include "form/inputpassworddialog.h"
|
||||
|
||||
Widget *Widget::instance{nullptr};
|
||||
|
||||
|
@ -195,6 +196,16 @@ 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);
|
||||
|
|
Loading…
Reference in New Issue
Block a user