1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

dos2unix the updater

This commit is contained in:
tux3 2016-01-15 17:40:12 +01:00
parent 3645e39b89
commit 6b4f081fdf
No known key found for this signature in database
GPG Key ID: 7E086DD661263264
9 changed files with 903 additions and 903 deletions

View File

@ -1,104 +1,104 @@
/* /*
Copyright © 2014 by The qTox Project Copyright © 2014 by The qTox Project
This file is part of qTox, a Qt-based graphical interface for Tox. This file is part of qTox, a Qt-based graphical interface for Tox.
qTox is libre software: you can redistribute it and/or modify qTox is libre software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
qTox is distributed in the hope that it will be useful, qTox is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with qTox. If not, see <http://www.gnu.org/licenses/> along with qTox. If not, see <http://www.gnu.org/licenses/>
*/ */
#include "widget.h" #include "widget.h"
#include <QApplication> #include <QApplication>
#include <QDebug> #include <QDebug>
#include <QFile> #include <QFile>
#include <QMutex> #include <QMutex>
#include <QDateTime> #include <QDateTime>
#include <QDir> #include <QDir>
#include <QStandardPaths> #include <QStandardPaths>
#include <memory> #include <memory>
#include <windows.h> #include <windows.h>
static std::unique_ptr<QTextStream> logFileStream {nullptr}; static std::unique_ptr<QTextStream> logFileStream {nullptr};
static std::unique_ptr<QFile> logFileFile {nullptr}; static std::unique_ptr<QFile> logFileFile {nullptr};
static QMutex mutex; static QMutex mutex;
void logMessageHandler(QtMsgType type, const QMessageLogContext& ctxt, const QString& msg) void logMessageHandler(QtMsgType type, const QMessageLogContext& ctxt, const QString& msg)
{ {
// Silence qWarning spam due to bug in QTextBrowser (trying to open a file for base64 images) // Silence qWarning spam due to bug in QTextBrowser (trying to open a file for base64 images)
if (ctxt.function == QString("virtual bool QFSFileEngine::open(QIODevice::OpenMode)") if (ctxt.function == QString("virtual bool QFSFileEngine::open(QIODevice::OpenMode)")
&& msg == QString("QFSFileEngine::open: No file name specified")) && msg == QString("QFSFileEngine::open: No file name specified"))
return; return;
QString LogMsg = QString("[%1] %2:%3 : ") QString LogMsg = QString("[%1] %2:%3 : ")
.arg(QTime::currentTime().toString("HH:mm:ss.zzz")).arg(ctxt.file).arg(ctxt.line); .arg(QTime::currentTime().toString("HH:mm:ss.zzz")).arg(ctxt.file).arg(ctxt.line);
switch (type) switch (type)
{ {
case QtDebugMsg: case QtDebugMsg:
LogMsg += "Debug"; LogMsg += "Debug";
break; break;
case QtWarningMsg: case QtWarningMsg:
LogMsg += "Warning"; LogMsg += "Warning";
break; break;
case QtCriticalMsg: case QtCriticalMsg:
LogMsg += "Critical"; LogMsg += "Critical";
break; break;
case QtFatalMsg: case QtFatalMsg:
LogMsg += "Fatal"; LogMsg += "Fatal";
break; break;
default: default:
break; break;
} }
LogMsg += ": " + msg + "\n"; LogMsg += ": " + msg + "\n";
QTextStream out(stderr, QIODevice::WriteOnly); QTextStream out(stderr, QIODevice::WriteOnly);
out << LogMsg; out << LogMsg;
if (!logFileStream) if (!logFileStream)
return; return;
QMutexLocker locker(&mutex); QMutexLocker locker(&mutex);
*logFileStream << LogMsg; *logFileStream << LogMsg;
logFileStream->flush(); logFileStream->flush();
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
qInstallMessageHandler(logMessageHandler); qInstallMessageHandler(logMessageHandler);
QApplication a(argc, argv); QApplication a(argc, argv);
logFileStream.reset(new QTextStream); logFileStream.reset(new QTextStream);
logFileFile.reset(new QFile(QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + QDir::separator() logFileFile.reset(new QFile(QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + QDir::separator()
+ "AppData" + QDir::separator() + "Roaming" + QDir::separator() + "tox")+QDir::separator()+"qtox.log")); + "AppData" + QDir::separator() + "Roaming" + QDir::separator() + "tox")+QDir::separator()+"qtox.log"));
if (logFileFile->open(QIODevice::Append)) if (logFileFile->open(QIODevice::Append))
{ {
logFileStream->setDevice(logFileFile.get()); logFileStream->setDevice(logFileFile.get());
*logFileStream << QDateTime::currentDateTime().toString("\nyyyy-MM-dd HH:mm:ss' Updater file logger starting\n'"); *logFileStream << QDateTime::currentDateTime().toString("\nyyyy-MM-dd HH:mm:ss' Updater file logger starting\n'");
} }
else else
{ {
qWarning() << "Couldn't open log file!\n"; qWarning() << "Couldn't open log file!\n";
logFileStream.release(); logFileStream.release();
} }
long unsigned int bufsize=100; long unsigned int bufsize=100;
char buf[100]; char buf[100];
GetUserNameA(buf, &bufsize); GetUserNameA(buf, &bufsize);
qDebug() << "Updater running as user" << buf; qDebug() << "Updater running as user" << buf;
Widget w; Widget w;
w.show(); w.show();
return a.exec(); return a.exec();
} }

View File

@ -1,143 +1,143 @@
/* /*
Copyright © 2014 by The qTox Project Copyright © 2014 by The qTox Project
This file is part of qTox, a Qt-based graphical interface for Tox. This file is part of qTox, a Qt-based graphical interface for Tox.
qTox is libre software: you can redistribute it and/or modify qTox is libre software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
qTox is distributed in the hope that it will be useful, qTox is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with qTox. If not, see <http://www.gnu.org/licenses/> along with qTox. If not, see <http://www.gnu.org/licenses/>
*/ */
#include "update.h" #include "update.h"
#include "serialize.h" #include "serialize.h"
#include "widget.h" #include "widget.h"
#include <QFile> #include <QFile>
#include <QDebug> #include <QDebug>
#include <QMessageBox> #include <QMessageBox>
#include <QDir> #include <QDir>
#include <QCoreApplication> #include <QCoreApplication>
unsigned char key[crypto_sign_PUBLICKEYBYTES] = unsigned char key[crypto_sign_PUBLICKEYBYTES] =
{ {
0x20, 0x89, 0x39, 0xaa, 0x9a, 0xe8, 0xb5, 0x21, 0x0e, 0xac, 0x02, 0xa9, 0xc4, 0x92, 0xd9, 0xa2, 0x20, 0x89, 0x39, 0xaa, 0x9a, 0xe8, 0xb5, 0x21, 0x0e, 0xac, 0x02, 0xa9, 0xc4, 0x92, 0xd9, 0xa2,
0x17, 0x83, 0xbd, 0x78, 0x0a, 0xda, 0x33, 0xcd, 0xa5, 0xc6, 0x44, 0xc7, 0xfc, 0xed, 0x00, 0x13 0x17, 0x83, 0xbd, 0x78, 0x0a, 0xda, 0x33, 0xcd, 0xa5, 0xc6, 0x44, 0xc7, 0xfc, 0xed, 0x00, 0x13
}; };
QByteArray getLocalFlist() QByteArray getLocalFlist()
{ {
QByteArray flist; QByteArray flist;
QFile flistFile("flist"); QFile flistFile("flist");
if (!flistFile.open(QIODevice::ReadOnly)) if (!flistFile.open(QIODevice::ReadOnly))
{ {
qWarning() << "getLocalFlist: Can't open local flist"; qWarning() << "getLocalFlist: Can't open local flist";
return flist; return flist;
} }
flist = flistFile.readAll(); flist = flistFile.readAll();
flistFile.close(); flistFile.close();
return flist; return flist;
} }
bool isUpToDate(UpdateFileMeta fileMeta) bool isUpToDate(UpdateFileMeta fileMeta)
{ {
QString appDir = qApp->applicationDirPath(); QString appDir = qApp->applicationDirPath();
QFile file(appDir+QDir::separator()+fileMeta.installpath); QFile file(appDir+QDir::separator()+fileMeta.installpath);
if (!file.open(QIODevice::ReadOnly)) if (!file.open(QIODevice::ReadOnly))
return false; return false;
// If the data we have is corrupted or old, mark it for update // If the data we have is corrupted or old, mark it for update
QByteArray data = file.readAll(); QByteArray data = file.readAll();
if (crypto_sign_verify_detached(fileMeta.sig, (unsigned char*)data.data(), data.size(), key) != 0) if (crypto_sign_verify_detached(fileMeta.sig, (unsigned char*)data.data(), data.size(), key) != 0)
return false; return false;
return true; return true;
} }
QList<UpdateFileMeta> genUpdateDiff(QList<UpdateFileMeta> updateFlist, Widget* w) QList<UpdateFileMeta> genUpdateDiff(QList<UpdateFileMeta> updateFlist, Widget* w)
{ {
QList<UpdateFileMeta> diff; QList<UpdateFileMeta> diff;
float progressDiff = 45; float progressDiff = 45;
float progress = 5; float progress = 5;
for (UpdateFileMeta file : updateFlist) for (UpdateFileMeta file : updateFlist)
{ {
if (!isUpToDate(file)) if (!isUpToDate(file))
diff += file; diff += file;
progress += progressDiff / updateFlist.size(); progress += progressDiff / updateFlist.size();
w->setProgress(progress); w->setProgress(progress);
} }
return diff; return diff;
} }
QList<UpdateFileMeta> parseFlist(QByteArray flistData) QList<UpdateFileMeta> parseFlist(QByteArray flistData)
{ {
QList<UpdateFileMeta> flist; QList<UpdateFileMeta> flist;
if (flistData.isEmpty()) if (flistData.isEmpty())
{ {
qWarning() << "AutoUpdater::parseflist: Empty data"; qWarning() << "AutoUpdater::parseflist: Empty data";
return flist; return flist;
} }
// Check version // Check version
if (flistData[0] != '1') if (flistData[0] != '1')
{ {
qWarning() << "AutoUpdater: parseflist: Bad version "<<(uint8_t)flistData[0]; qWarning() << "AutoUpdater: parseflist: Bad version "<<(uint8_t)flistData[0];
return flist; return flist;
} }
flistData = flistData.mid(1); flistData = flistData.mid(1);
// Check signature // Check signature
if (flistData.size() < (int)(crypto_sign_BYTES)) if (flistData.size() < (int)(crypto_sign_BYTES))
{ {
qWarning() << "AutoUpdater::parseflist: Truncated data"; qWarning() << "AutoUpdater::parseflist: Truncated data";
return flist; return flist;
} }
else else
{ {
QByteArray msgData = flistData.mid(crypto_sign_BYTES); QByteArray msgData = flistData.mid(crypto_sign_BYTES);
unsigned char* msg = (unsigned char*)msgData.data(); unsigned char* msg = (unsigned char*)msgData.data();
if (crypto_sign_verify_detached((unsigned char*)flistData.data(), msg, msgData.size(), key) != 0) if (crypto_sign_verify_detached((unsigned char*)flistData.data(), msg, msgData.size(), key) != 0)
{ {
qCritical() << "AutoUpdater: parseflist: FORGED FLIST FILE"; qCritical() << "AutoUpdater: parseflist: FORGED FLIST FILE";
return flist; return flist;
} }
flistData = flistData.mid(crypto_sign_BYTES); flistData = flistData.mid(crypto_sign_BYTES);
} }
// Parse. We assume no errors handling needed since the signature is valid. // Parse. We assume no errors handling needed since the signature is valid.
while (!flistData.isEmpty()) while (!flistData.isEmpty())
{ {
UpdateFileMeta newFile; UpdateFileMeta newFile;
memcpy(newFile.sig, flistData.data(), crypto_sign_BYTES); memcpy(newFile.sig, flistData.data(), crypto_sign_BYTES);
flistData = flistData.mid(crypto_sign_BYTES); flistData = flistData.mid(crypto_sign_BYTES);
newFile.id = dataToString(flistData); newFile.id = dataToString(flistData);
flistData = flistData.mid(newFile.id.size() + getVUint32Size(flistData)); flistData = flistData.mid(newFile.id.size() + getVUint32Size(flistData));
newFile.installpath = dataToString(flistData); newFile.installpath = dataToString(flistData);
flistData = flistData.mid(newFile.installpath.size() + getVUint32Size(flistData)); flistData = flistData.mid(newFile.installpath.size() + getVUint32Size(flistData));
newFile.size = dataToUint64(flistData); newFile.size = dataToUint64(flistData);
flistData = flistData.mid(8); flistData = flistData.mid(8);
flist += newFile; flist += newFile;
} }
return flist; return flist;
} }

View File

@ -1,60 +1,60 @@
/* /*
Copyright © 2014 by The qTox Project Copyright © 2014 by The qTox Project
This file is part of qTox, a Qt-based graphical interface for Tox. This file is part of qTox, a Qt-based graphical interface for Tox.
qTox is libre software: you can redistribute it and/or modify qTox is libre software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
qTox is distributed in the hope that it will be useful, qTox is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with qTox. If not, see <http://www.gnu.org/licenses/> along with qTox. If not, see <http://www.gnu.org/licenses/>
*/ */
#ifndef UPDATE_H #ifndef UPDATE_H
#define UPDATE_H #define UPDATE_H
#include <QByteArray> #include <QByteArray>
#include <QString> #include <QString>
#include <sodium.h> #include <sodium.h>
class Widget; class Widget;
struct UpdateFileMeta struct UpdateFileMeta
{ {
unsigned char sig[crypto_sign_BYTES]; ///< Signature of the file (ed25519) unsigned char sig[crypto_sign_BYTES]; ///< Signature of the file (ed25519)
QString id; ///< Unique id of the file QString id; ///< Unique id of the file
QString installpath; ///< Local path including the file name. May be relative to qtox-updater or absolute 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 uint64_t size; ///< Size in bytes of the file
bool operator==(const UpdateFileMeta& other) bool operator==(const UpdateFileMeta& other)
{ {
return (size == other.size return (size == other.size
&& id == other.id && installpath == other.installpath && id == other.id && installpath == other.installpath
&& memcmp(sig, other.sig, crypto_sign_BYTES) == 0); && memcmp(sig, other.sig, crypto_sign_BYTES) == 0);
} }
}; };
struct UpdateFile struct UpdateFile
{ {
UpdateFileMeta metadata; UpdateFileMeta metadata;
QByteArray data; QByteArray data;
}; };
/// Gets the local flist. Returns an empty array on error /// Gets the local flist. Returns an empty array on error
QByteArray getLocalFlist(); QByteArray getLocalFlist();
/// Parses and validates a flist file. Returns an empty list on error /// Parses and validates a flist file. Returns an empty list on error
QList<UpdateFileMeta> parseFlist(QByteArray flistData); QList<UpdateFileMeta> parseFlist(QByteArray flistData);
/// Generates a list of files we need to update /// Generates a list of files we need to update
QList<UpdateFileMeta> genUpdateDiff(QList<UpdateFileMeta> updateFlist, Widget *w); QList<UpdateFileMeta> genUpdateDiff(QList<UpdateFileMeta> updateFlist, Widget *w);
extern unsigned char key[crypto_sign_PUBLICKEYBYTES]; extern unsigned char key[crypto_sign_PUBLICKEYBYTES];
#endif // UPDATE_H #endif // UPDATE_H

View File

@ -1,40 +1,40 @@
#------------------------------------------------- #-------------------------------------------------
# #
# Project created by QtCreator 2014-11-09T21:09:08 # Project created by QtCreator 2014-11-09T21:09:08
# #
#------------------------------------------------- #-------------------------------------------------
QT += core gui QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = qtox-updater TARGET = qtox-updater
TEMPLATE = app TEMPLATE = app
CONFIG += c++11 CONFIG += c++11
QMAKE_CXXFLAGS += -fno-exceptions QMAKE_CXXFLAGS += -fno-exceptions
SOURCES += main.cpp\ SOURCES += main.cpp\
widget.cpp \ widget.cpp \
update.cpp \ update.cpp \
serialize.cpp serialize.cpp
HEADERS += widget.h \ HEADERS += widget.h \
update.h \ update.h \
serialize.h serialize.h
FORMS += widget.ui FORMS += widget.ui
RESOURCES += \ RESOURCES += \
res.qrc res.qrc
INCLUDEPATH += libs/include INCLUDEPATH += libs/include
RC_FILE = windows/updater.rc RC_FILE = windows/updater.rc
LIBS += -L$$PWD/libs/lib/ -lsodium LIBS += -L$$PWD/libs/lib/ -lsodium
win32 { win32 {
LIBS += -lshell32 -luuid LIBS += -lshell32 -luuid
} }

View File

@ -1,338 +1,338 @@
/* /*
Copyright © 2014 by The qTox Project Copyright © 2014 by The qTox Project
This file is part of qTox, a Qt-based graphical interface for Tox. This file is part of qTox, a Qt-based graphical interface for Tox.
qTox is libre software: you can redistribute it and/or modify qTox is libre software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
qTox is distributed in the hope that it will be useful, qTox is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with qTox. If not, see <http://www.gnu.org/licenses/> along with qTox. If not, see <http://www.gnu.org/licenses/>
*/ */
#include "widget.h" #include "widget.h"
#include "ui_widget.h" #include "ui_widget.h"
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <QProcess> #include <QProcess>
#include <QMessageBox> #include <QMessageBox>
#include <QMetaObject> #include <QMetaObject>
#include <QDebug> #include <QDebug>
#include <QSettings> #include <QSettings>
#include "update.h" #include "update.h"
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#ifdef _WIN32_WINNT #ifdef _WIN32_WINNT
#undef _WIN32_WINNT #undef _WIN32_WINNT
#endif #endif
#define _WIN32_WINNT 0x0600 // Vista for SHGetKnownFolderPath #define _WIN32_WINNT 0x0600 // Vista for SHGetKnownFolderPath
#include <windows.h> #include <windows.h>
#include <shldisp.h> #include <shldisp.h>
#include <shlobj.h> #include <shlobj.h>
#include <exdisp.h> #include <exdisp.h>
const bool supported = true; const bool supported = true;
const QString QTOX_PATH = "qtox.exe"; const QString QTOX_PATH = "qtox.exe";
#else #else
const bool supported = false; const bool supported = false;
const QString QTOX_PATH; const QString QTOX_PATH;
#endif #endif
const QString SETTINGS_FILE = "settings.ini"; const QString SETTINGS_FILE = "settings.ini";
Widget::Widget(QWidget *parent) : Widget::Widget(QWidget *parent) :
QWidget(parent), QWidget(parent),
ui(new Ui::Widget) ui(new Ui::Widget)
{ {
ui->setupUi(this); ui->setupUi(this);
// Updates only for supported platforms // Updates only for supported platforms
if (!supported) if (!supported)
fatalError(tr("The qTox updater is not supported on this platform.")); fatalError(tr("The qTox updater is not supported on this platform."));
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
// Get a primary unelevated token of the actual user // Get a primary unelevated token of the actual user
hPrimaryToken = nullptr; hPrimaryToken = nullptr;
HANDLE hShellProcess = nullptr, hShellProcessToken = nullptr; HANDLE hShellProcess = nullptr, hShellProcessToken = nullptr;
const DWORD dwTokenRights = TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_ASSIGN_PRIMARY const DWORD dwTokenRights = TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_ASSIGN_PRIMARY
| TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID; | TOKEN_DUPLICATE | TOKEN_ADJUST_DEFAULT | TOKEN_ADJUST_SESSIONID;
DWORD dwPID = 0; DWORD dwPID = 0;
HWND hwnd = nullptr; HWND hwnd = nullptr;
DWORD dwLastErr = 0; DWORD dwLastErr = 0;
// Enable SeIncreaseQuotaPrivilege // Enable SeIncreaseQuotaPrivilege
HANDLE hProcessToken = NULL; HANDLE hProcessToken = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken)) if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcessToken))
goto unelevateFail; goto unelevateFail;
TOKEN_PRIVILEGES tkp; TOKEN_PRIVILEGES tkp;
tkp.PrivilegeCount = 1; tkp.PrivilegeCount = 1;
LookupPrivilegeValueW(NULL, SE_INCREASE_QUOTA_NAME, &tkp.Privileges[0].Luid); LookupPrivilegeValueW(NULL, SE_INCREASE_QUOTA_NAME, &tkp.Privileges[0].Luid);
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hProcessToken, FALSE, &tkp, 0, NULL, NULL); AdjustTokenPrivileges(hProcessToken, FALSE, &tkp, 0, NULL, NULL);
dwLastErr = GetLastError(); dwLastErr = GetLastError();
CloseHandle(hProcessToken); CloseHandle(hProcessToken);
if (ERROR_SUCCESS != dwLastErr) if (ERROR_SUCCESS != dwLastErr)
goto unelevateFail; goto unelevateFail;
// Get a primary copy of the desktop shell's token, // Get a primary copy of the desktop shell's token,
// we're assuming the shell is running as the actual user // we're assuming the shell is running as the actual user
hwnd = GetShellWindow(); hwnd = GetShellWindow();
if (!hwnd) if (!hwnd)
goto unelevateFail; goto unelevateFail;
GetWindowThreadProcessId(hwnd, &dwPID); GetWindowThreadProcessId(hwnd, &dwPID);
if (!dwPID) if (!dwPID)
goto unelevateFail; goto unelevateFail;
hShellProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwPID); hShellProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwPID);
if (!hShellProcess) if (!hShellProcess)
goto unelevateFail; goto unelevateFail;
if (!OpenProcessToken(hShellProcess, TOKEN_DUPLICATE, &hShellProcessToken)) if (!OpenProcessToken(hShellProcess, TOKEN_DUPLICATE, &hShellProcessToken))
goto unelevateFail; goto unelevateFail;
// Duplicate the shell's process token to get a primary token. // Duplicate the shell's process token to get a primary token.
// Based on experimentation, this is the minimal set of rights required for CreateProcessWithTokenW (contrary to current documentation). // Based on experimentation, this is the minimal set of rights required for CreateProcessWithTokenW (contrary to current documentation).
if (!DuplicateTokenEx(hShellProcessToken, dwTokenRights, NULL, SecurityImpersonation, TokenPrimary, &hPrimaryToken)) if (!DuplicateTokenEx(hShellProcessToken, dwTokenRights, NULL, SecurityImpersonation, TokenPrimary, &hPrimaryToken))
goto unelevateFail; goto unelevateFail;
qDebug() << "Unelevated primary access token acquired"; qDebug() << "Unelevated primary access token acquired";
goto unelevateCleanup; goto unelevateCleanup;
unelevateFail: unelevateFail:
qWarning() << "Unelevate failed, couldn't get access token"; qWarning() << "Unelevate failed, couldn't get access token";
unelevateCleanup: unelevateCleanup:
CloseHandle(hShellProcessToken); CloseHandle(hShellProcessToken);
CloseHandle(hShellProcess); CloseHandle(hShellProcess);
#endif #endif
QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection);
} }
Widget::~Widget() Widget::~Widget()
{ {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
CloseHandle(hPrimaryToken); CloseHandle(hPrimaryToken);
#endif #endif
delete ui; delete ui;
} }
void Widget::setProgress(int value) void Widget::setProgress(int value)
{ {
ui->progress->setValue(value); ui->progress->setValue(value);
ui->progress->repaint(); ui->progress->repaint();
qApp->processEvents(); qApp->processEvents();
} }
void Widget::fatalError(QString message) void Widget::fatalError(QString message)
{ {
qCritical() << "Update aborted with error:"<<message; qCritical() << "Update aborted with error:"<<message;
QMessageBox::critical(this,tr("Error"), message+'\n'+tr("qTox will restart now.")); QMessageBox::critical(this,tr("Error"), message+'\n'+tr("qTox will restart now."));
deleteUpdate(); deleteUpdate();
restoreBackups(); restoreBackups();
startQToxAndExit(); startQToxAndExit();
} }
void Widget::deleteUpdate() void Widget::deleteUpdate()
{ {
QDir updateDir(getSettingsDirPath()+"/update/"); QDir updateDir(getSettingsDirPath()+"/update/");
updateDir.removeRecursively(); updateDir.removeRecursively();
} }
void Widget::startQToxAndExit() void Widget::startQToxAndExit()
{ {
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
// Try to restart qTox as the actual user with our unelevated token // Try to restart qTox as the actual user with our unelevated token
STARTUPINFOW si; STARTUPINFOW si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
SecureZeroMemory(&si, sizeof(si)); SecureZeroMemory(&si, sizeof(si));
SecureZeroMemory(&pi, sizeof(pi)); SecureZeroMemory(&pi, sizeof(pi));
si.cb = sizeof(si); si.cb = sizeof(si);
bool unelevateOk = true; bool unelevateOk = true;
auto advapi32H = LoadLibrary(TEXT("advapi32.dll")); auto advapi32H = LoadLibrary(TEXT("advapi32.dll"));
if ((unelevateOk = (advapi32H != nullptr))) if ((unelevateOk = (advapi32H != nullptr)))
{ {
auto CreateProcessWithTokenWH = (decltype(&CreateProcessWithTokenW)) auto CreateProcessWithTokenWH = (decltype(&CreateProcessWithTokenW))
GetProcAddress(advapi32H, "CreateProcessWithTokenW"); GetProcAddress(advapi32H, "CreateProcessWithTokenW");
if ((unelevateOk = (CreateProcessWithTokenWH != nullptr))) if ((unelevateOk = (CreateProcessWithTokenWH != nullptr)))
{ {
if (!CreateProcessWithTokenWH(hPrimaryToken, 0, QTOX_PATH.toStdWString().c_str(), 0, 0, 0, 0, &si, &pi)) if (!CreateProcessWithTokenWH(hPrimaryToken, 0, QTOX_PATH.toStdWString().c_str(), 0, 0, 0, 0, &si, &pi))
unelevateOk = false; unelevateOk = false;
} }
} }
CloseHandle(pi.hProcess); CloseHandle(pi.hProcess);
CloseHandle(pi.hThread); CloseHandle(pi.hThread);
if (!unelevateOk) if (!unelevateOk)
{ {
qWarning() << "Failed to start unelevated qTox"; qWarning() << "Failed to start unelevated qTox";
QProcess::startDetached(QTOX_PATH); QProcess::startDetached(QTOX_PATH);
} }
#else #else
QProcess::startDetached(QTOX_PATH); QProcess::startDetached(QTOX_PATH);
#endif #endif
exit(0); exit(0);
} }
void Widget::deleteBackups() void Widget::deleteBackups()
{ {
for (QString file : backups) for (QString file : backups)
QFile(file+".bak").remove(); QFile(file+".bak").remove();
} }
void Widget::restoreBackups() void Widget::restoreBackups()
{ {
for (QString file : backups) for (QString file : backups)
QFile(file+".bak").rename(file); QFile(file+".bak").rename(file);
} }
QString Widget::getSettingsDirPath() QString Widget::getSettingsDirPath()
{ {
if (isToxPortableEnabled()) if (isToxPortableEnabled())
return "."; return ".";
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
wchar_t* path; wchar_t* path;
wchar_t pathOld[MAX_PATH]; wchar_t pathOld[MAX_PATH];
bool isOld = false; // If true, we have to use pathOld and older Windows API. bool isOld = false; // If true, we have to use pathOld and older Windows API.
auto shell32H = LoadLibrary(TEXT("shell32.dll")); auto shell32H = LoadLibrary(TEXT("shell32.dll"));
if (!(isOld = (shell32H == nullptr))) if (!(isOld = (shell32H == nullptr)))
{ {
auto SHGetKnownFolderPathH = (decltype(&SHGetKnownFolderPath)) auto SHGetKnownFolderPathH = (decltype(&SHGetKnownFolderPath))
GetProcAddress(shell32H, "SHGetKnownFolderPath"); GetProcAddress(shell32H, "SHGetKnownFolderPath");
if (!(isOld = (SHGetKnownFolderPathH == nullptr))) if (!(isOld = (SHGetKnownFolderPathH == nullptr)))
SHGetKnownFolderPathH(FOLDERID_RoamingAppData, 0, hPrimaryToken, &path); SHGetKnownFolderPathH(FOLDERID_RoamingAppData, 0, hPrimaryToken, &path);
} }
if (isOld) if (isOld)
{ {
qDebug() << "Falling back to legacy APIs..."; qDebug() << "Falling back to legacy APIs...";
SHGetFolderPathW(nullptr, CSIDL_APPDATA, nullptr, 0, pathOld); SHGetFolderPathW(nullptr, CSIDL_APPDATA, nullptr, 0, pathOld);
} }
QString pathStr = QString::fromStdWString(isOld ? pathOld : path); QString pathStr = QString::fromStdWString(isOld ? pathOld : path);
pathStr.replace("\\", "/"); pathStr.replace("\\", "/");
return pathStr + "/tox"; return pathStr + "/tox";
#else #else
return QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QDir::separator() + "tox"); return QDir::cleanPath(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation) + QDir::separator() + "tox");
#endif #endif
} }
bool Widget::isToxPortableEnabled() bool Widget::isToxPortableEnabled()
{ {
QFile portableSettings(SETTINGS_FILE); QFile portableSettings(SETTINGS_FILE);
if (portableSettings.exists()) if (portableSettings.exists())
{ {
QSettings ps(SETTINGS_FILE, QSettings::IniFormat); QSettings ps(SETTINGS_FILE, QSettings::IniFormat);
ps.beginGroup("General"); ps.beginGroup("General");
return ps.value("makeToxPortable", false).toBool(); return ps.value("makeToxPortable", false).toBool();
} }
else else
{ {
return false; return false;
} }
} }
void Widget::update() void Widget::update()
{ {
/// 1. Find and parse the update (0-5%) /// 1. Find and parse the update (0-5%)
// Check that the dir exists // Check that the dir exists
QString updateDirStr = getSettingsDirPath()+"/update/"; QString updateDirStr = getSettingsDirPath()+"/update/";
QDir updateDir(updateDirStr); QDir updateDir(updateDirStr);
if (!updateDir.exists()) if (!updateDir.exists())
fatalError(tr("No update found.")); fatalError(tr("No update found."));
setProgress(2); setProgress(2);
// Check that we have a flist and that every file on the diff exists // Check that we have a flist and that every file on the diff exists
QFile updateFlistFile(updateDirStr+"flist"); QFile updateFlistFile(updateDirStr+"flist");
if (!updateFlistFile.open(QIODevice::ReadOnly)) if (!updateFlistFile.open(QIODevice::ReadOnly))
fatalError(tr("The update is incomplete.")); fatalError(tr("The update is incomplete."));
QByteArray updateFlistData = updateFlistFile.readAll(); QByteArray updateFlistData = updateFlistFile.readAll();
updateFlistFile.close(); updateFlistFile.close();
QList<UpdateFileMeta> updateFlist = parseFlist(updateFlistData); QList<UpdateFileMeta> updateFlist = parseFlist(updateFlistData);
setProgress(5); setProgress(5);
/// 2. Generate a diff (5-50%) /// 2. Generate a diff (5-50%)
QList<UpdateFileMeta> diff = genUpdateDiff(updateFlist, this); QList<UpdateFileMeta> diff = genUpdateDiff(updateFlist, this);
for (UpdateFileMeta fileMeta : diff) for (UpdateFileMeta fileMeta : diff)
if (!QFile::exists(updateDirStr+fileMeta.installpath)) if (!QFile::exists(updateDirStr+fileMeta.installpath))
fatalError(tr("The update is incomplete.")); fatalError(tr("The update is incomplete."));
if (diff.size() == 0) if (diff.size() == 0)
fatalError(tr("The update is empty!")); fatalError(tr("The update is empty!"));
setProgress(50); setProgress(50);
qDebug() << "Diff generated,"<<diff.size()<<"files to update"; qDebug() << "Diff generated,"<<diff.size()<<"files to update";
/// 2. Check the update (50-75%) /// 2. Check the update (50-75%)
float checkProgressStep = 25.0/(float)diff.size(); float checkProgressStep = 25.0/(float)diff.size();
float checkProgress = 50; float checkProgress = 50;
for (UpdateFileMeta fileMeta : diff) for (UpdateFileMeta fileMeta : diff)
{ {
UpdateFile file; UpdateFile file;
file.metadata = fileMeta; file.metadata = fileMeta;
QFile fileFile(updateDirStr+fileMeta.installpath); QFile fileFile(updateDirStr+fileMeta.installpath);
if (!fileFile.open(QIODevice::ReadOnly)) if (!fileFile.open(QIODevice::ReadOnly))
fatalError(tr("Update files are unreadable.")); fatalError(tr("Update files are unreadable."));
file.data = fileFile.readAll(); file.data = fileFile.readAll();
fileFile.close(); fileFile.close();
if (file.data.size() != (int)fileMeta.size) if (file.data.size() != (int)fileMeta.size)
fatalError(tr("Update files are corrupted.")); fatalError(tr("Update files are corrupted."));
if (crypto_sign_verify_detached(file.metadata.sig, (unsigned char*)file.data.data(), if (crypto_sign_verify_detached(file.metadata.sig, (unsigned char*)file.data.data(),
file.data.size(), key) != 0) file.data.size(), key) != 0)
fatalError(tr("Update files are corrupted.")); fatalError(tr("Update files are corrupted."));
checkProgress += checkProgressStep; checkProgress += checkProgressStep;
setProgress(checkProgress); setProgress(checkProgress);
} }
setProgress(75); setProgress(75);
qDebug() << "Update files signature verified, installing"; qDebug() << "Update files signature verified, installing";
/// 3. Install the update (75-95%) /// 3. Install the update (75-95%)
float installProgressStep = 20.0/(float)diff.size(); float installProgressStep = 20.0/(float)diff.size();
float installProgress = 75; float installProgress = 75;
for (UpdateFileMeta fileMeta : diff) for (UpdateFileMeta fileMeta : diff)
{ {
// Backup old files // Backup old files
if (QFile(fileMeta.installpath).exists()) if (QFile(fileMeta.installpath).exists())
{ {
QFile(fileMeta.installpath+".bak").remove(); QFile(fileMeta.installpath+".bak").remove();
QFile(fileMeta.installpath).rename(fileMeta.installpath+".bak"); QFile(fileMeta.installpath).rename(fileMeta.installpath+".bak");
backups.append(fileMeta.installpath); backups.append(fileMeta.installpath);
} }
// Install new ones // Install new ones
QDir().mkpath(QFileInfo(fileMeta.installpath).absolutePath()); QDir().mkpath(QFileInfo(fileMeta.installpath).absolutePath());
QFile fileFile(updateDirStr+fileMeta.installpath); QFile fileFile(updateDirStr+fileMeta.installpath);
if (!fileFile.copy(fileMeta.installpath)) if (!fileFile.copy(fileMeta.installpath))
fatalError(tr("Unable to copy the update's files from ")+(updateDirStr+fileMeta.installpath)+" to "+fileMeta.installpath); fatalError(tr("Unable to copy the update's files from ")+(updateDirStr+fileMeta.installpath)+" to "+fileMeta.installpath);
installProgress += installProgressStep; installProgress += installProgressStep;
setProgress(installProgress); setProgress(installProgress);
} }
setProgress(95); setProgress(95);
/// 4. Delete the update and backups (95-100%) /// 4. Delete the update and backups (95-100%)
deleteUpdate(); deleteUpdate();
setProgress(97); setProgress(97);
deleteBackups(); deleteBackups();
setProgress(100); setProgress(100);
/// 5. Start qTox and exit /// 5. Start qTox and exit
qDebug() << "Update applied, restarting qTox!"; qDebug() << "Update applied, restarting qTox!";
startQToxAndExit(); startQToxAndExit();
} }

View File

@ -1,67 +1,67 @@
/* /*
Copyright © 2014 by The qTox Project Copyright © 2014 by The qTox Project
This file is part of qTox, a Qt-based graphical interface for Tox. This file is part of qTox, a Qt-based graphical interface for Tox.
qTox is libre software: you can redistribute it and/or modify qTox is libre software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
qTox is distributed in the hope that it will be useful, qTox is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details. GNU General Public License for more details.
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with qTox. If not, see <http://www.gnu.org/licenses/> along with qTox. If not, see <http://www.gnu.org/licenses/>
*/ */
#ifndef WIDGET_H #ifndef WIDGET_H
#define WIDGET_H #define WIDGET_H
#include <QWidget> #include <QWidget>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <windows.h> #include <windows.h>
#endif #endif
namespace Ui { namespace Ui {
class Widget; class Widget;
} }
class Widget : public QWidget class Widget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit Widget(QWidget *parent = 0); explicit Widget(QWidget *parent = 0);
~Widget(); ~Widget();
// Utilities // Utilities
void deleteBackups(); void deleteBackups();
void restoreBackups(); void restoreBackups();
void setProgress(int value); void setProgress(int value);
QString getSettingsDirPath(); QString getSettingsDirPath();
bool isToxPortableEnabled(); bool isToxPortableEnabled();
// Noreturn // Noreturn
void fatalError(QString message); ///< Calls deleteUpdate and startQToxAndExit void fatalError(QString message); ///< Calls deleteUpdate and startQToxAndExit
void deleteUpdate(); void deleteUpdate();
void startQToxAndExit(); void startQToxAndExit();
public slots: public slots:
// Finds and applies the update // Finds and applies the update
void update(); void update();
private: private:
Ui::Widget *ui; Ui::Widget *ui;
QStringList backups; QStringList backups;
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
HANDLE hPrimaryToken; HANDLE hPrimaryToken;
#endif #endif
}; };
#endif // WIDGET_H #endif // WIDGET_H

View File

@ -1,139 +1,139 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>Widget</class> <class>Widget</class>
<widget class="QWidget" name="Widget"> <widget class="QWidget" name="Widget">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>401</width> <width>401</width>
<height>224</height> <height>224</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>qTox Updater</string> <string>qTox Updater</string>
</property> </property>
<property name="windowIcon"> <property name="windowIcon">
<iconset resource="res.qrc"> <iconset resource="res.qrc">
<normaloff>:/res/qtox-256x256.png</normaloff>:/res/qtox-256x256.png</iconset> <normaloff>:/res/qtox-256x256.png</normaloff>:/res/qtox-256x256.png</iconset>
</property> </property>
<widget class="QLabel" name="label"> <widget class="QLabel" name="label">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>13</y> <y>13</y>
<width>191</width> <width>191</width>
<height>191</height> <height>191</height>
</rect> </rect>
</property> </property>
<property name="text"> <property name="text">
<string/> <string/>
</property> </property>
<property name="pixmap"> <property name="pixmap">
<pixmap resource="res.qrc">:/res/qtox-256x256.png</pixmap> <pixmap resource="res.qrc">:/res/qtox-256x256.png</pixmap>
</property> </property>
<property name="scaledContents"> <property name="scaledContents">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
<widget class="QProgressBar" name="progress"> <widget class="QProgressBar" name="progress">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>206</x> <x>206</x>
<y>95</y> <y>95</y>
<width>171</width> <width>171</width>
<height>20</height> <height>20</height>
</rect> </rect>
</property> </property>
<property name="minimum"> <property name="minimum">
<number>0</number> <number>0</number>
</property> </property>
<property name="value"> <property name="value">
<number>0</number> <number>0</number>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignCenter</set> <set>Qt::AlignCenter</set>
</property> </property>
<property name="invertedAppearance"> <property name="invertedAppearance">
<bool>false</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>205</x> <x>205</x>
<y>115</y> <y>115</y>
<width>171</width> <width>171</width>
<height>20</height> <height>20</height>
</rect> </rect>
</property> </property>
<property name="text"> <property name="text">
<string>Updating qTox ...</string> <string>Updating qTox ...</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignCenter</set> <set>Qt::AlignCenter</set>
</property> </property>
</widget> </widget>
<widget class="QLabel" name="label_3"> <widget class="QLabel" name="label_3">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>201</x> <x>201</x>
<y>170</y> <y>170</y>
<width>181</width> <width>181</width>
<height>20</height> <height>20</height>
</rect> </rect>
</property> </property>
<property name="text"> <property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://tox.chat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://tox.chat&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://tox.chat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;https://tox.chat&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignCenter</set> <set>Qt::AlignCenter</set>
</property> </property>
<property name="openExternalLinks"> <property name="openExternalLinks">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>200</x> <x>200</x>
<y>183</y> <y>183</y>
<width>181</width> <width>181</width>
<height>20</height> <height>20</height>
</rect> </rect>
</property> </property>
<property name="text"> <property name="text">
<string>&lt;a href=&quot;https://github.com/tux3/qtox&quot;&gt;https://github.com/tux3/qtox&lt;/a&gt;</string> <string>&lt;a href=&quot;https://github.com/tux3/qtox&quot;&gt;https://github.com/tux3/qtox&lt;/a&gt;</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignCenter</set> <set>Qt::AlignCenter</set>
</property> </property>
</widget> </widget>
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>195</x> <x>195</x>
<y>32</y> <y>32</y>
<width>191</width> <width>191</width>
<height>31</height> <height>31</height>
</rect> </rect>
</property> </property>
<property name="font"> <property name="font">
<font> <font>
<pointsize>14</pointsize> <pointsize>14</pointsize>
</font> </font>
</property> </property>
<property name="text"> <property name="text">
<string>qTox Update</string> <string>qTox Update</string>
</property> </property>
<property name="alignment"> <property name="alignment">
<set>Qt::AlignCenter</set> <set>Qt::AlignCenter</set>
</property> </property>
</widget> </widget>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<resources> <resources>
<include location="res.qrc"/> <include location="res.qrc"/>
</resources> </resources>
<connections/> <connections/>
</ui> </ui>

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security> <security>
<requestedPrivileges> <requestedPrivileges>
<requestedExecutionLevel <requestedExecutionLevel
level="requireAdministrator" level="requireAdministrator"
uiAccess="false"/> uiAccess="false"/>
</requestedPrivileges> </requestedPrivileges>
</security> </security>
</trustInfo> </trustInfo>
</assembly> </assembly>

View File

@ -1,2 +1,2 @@
ID_ICON ICON DISCARDABLE "updater.ico" ID_ICON ICON DISCARDABLE "updater.ico"
1 24 "updater.exe.manifest" 1 24 "updater.exe.manifest"