Compare commits

..

2 Commits

27 changed files with 707 additions and 104 deletions

View File

@ -47,6 +47,14 @@ else()
install-java.cpp install-java.cpp
network-downloader.h network-downloader.h
network-downloader.cpp network-downloader.cpp
fs-tools.cpp
fs-tools.h
prog-constains.h
prog-constains.cpp
install-i2p.h
install-i2p.cpp
i2p-controller.h i2p-controller.cpp
personal-account.h personal-account.cpp personal-account.ui
) )
endif() endif()
endif() endif()

72
fs-tools.cpp Normal file
View File

@ -0,0 +1,72 @@
#include "fs-tools.h"
void emptyFolder(const QString& path)
{
QDir dir(path);
if (!dir.exists()) {
dir.mkpath(".");
return;
}
QFileInfoList entries = dir.entryInfoList(
QDir::NoDotAndDotDot |
QDir::AllEntries
);
for (const QFileInfo &entry : entries) {
if (entry.isDir()) {
QDir(entry.absoluteFilePath()).removeRecursively();
} else {
QFile::remove(entry.absoluteFilePath());
}
}
}
QString checkFileHash(QString pathname)
{
QFile file(pathname);
if (!file.open(QFile::ReadOnly))
return QString();
QCryptographicHash hash(QCryptographicHash::Sha256);
while (!file.atEnd())
hash.addData(file.read(8192));
return hash.result().toHex();
}
QString checkDirHash(QString path) {
QCryptographicHash hash(QCryptographicHash::Sha256);
QDir dir(path);
if (!dir.exists()) {
qDebug() << "Directory not exists:" << path;
return QString();
}
QFileInfoList fileList = dir.entryInfoList(
QDir::Files | QDir::NoSymLinks | QDir::AllDirs | QDir::NoDotAndDotDot,
QDir::Name | QDir::DirsFirst
);
for (const QFileInfo &info : fileList) {
if (info.isDir()) {
QString subHash = checkDirHash(info.absoluteFilePath());
hash.addData(subHash.toUtf8());
} else if (info.isFile()) {
QFile file(info.absoluteFilePath());
if (file.open(QIODevice::ReadOnly)) {
while (!file.atEnd()) {
QByteArray chunk = file.read(8192);
hash.addData(chunk);
}
file.close();
}
}
}
return QString(hash.result().toHex());
}

20
fs-tools.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef FS_TOOLS_H
#define FS_TOOLS_H
#include <QDir>
#include <QFileInfo>
#include <QFileInfoList>
#include <QString>
#include <QStandardPaths>
#include <QProcess>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QEventLoop>
#include <QFile>
#include <QDebug>
void emptyFolder(const QString& path);
QString checkDirHash(QString path);
QString checkFileHash(QString pathname);
#endif // FS_TOOLS_H

89
i2p-controller.cpp Normal file
View File

@ -0,0 +1,89 @@
#include "i2p-controller.h"
#include "prog-constains.h"
I2PController::I2PController(QObject *parent)
: QObject(parent)
{
}
void I2PController::start() {
if (i2p.state() != QProcess::NotRunning)
{
qDebug() << "i2pd already running";
return;
}
QStringList args;
#if defined(Q_OS_WIN)
this->i2p.start(QDir(I2P_INSTALL_PATH).filePath("i2pd.exe"), args);
#elif defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
QString configsFolder = QDir(I2P_INSTALL_PATH).filePath("configs");
QString logsFolder = QDir(I2P_INSTALL_PATH).filePath("logs");
args << "--datadir" << QDir(I2P_INSTALL_PATH).filePath("data")
<< "--conf" << QDir(configsFolder).filePath("i2pd.conf")
<< "--tunconf" << QDir(configsFolder).filePath("tunnels.conf")
<< "--log" << QDir(logsFolder).filePath("i2pd.log");
this->i2p.start(QDir(I2P_INSTALL_PATH).filePath("i2pd"), args);
#else
#endif
}
void I2PController::onProcessError(QProcess::ProcessError error)
{
qDebug() << "I2P process error:" << error;
switch (error)
{
case QProcess::FailedToStart:
qCritical() << "i2pd failed to start";
emit fatalError("Failed to start i2pd");
break;
case QProcess::Crashed:
qWarning() << "i2pd crashed";
restartI2P();
break;
default:
break;
}
}
void I2PController::onProcessFinished(int exitCode, QProcess::ExitStatus status)
{
qDebug() << "i2pd finished" << exitCode << status;
if (status == QProcess::CrashExit)
{
qWarning() << "i2pd crashed, restarting...";
restartI2P();
return;
}
if (exitCode != 0)
{
qWarning() << "i2pd exited with error";
restartI2P();
}
}
void I2PController::restartI2P()
{
static int restartCount = 0;
if (restartCount > 5)
{
qCritical() << "i2pd crashed too many times";
emit fatalError("i2pd keeps crashing");
return;
}
restartCount++;
QTimer::singleShot(3000, [this]() {
start();
});
}

24
i2p-controller.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef I2P_CONTROLLER_H
#define I2P_CONTROLLER_H
#include <QProcess>
#include <QDebug>
#include <QTimer>
class I2PController : public QObject
{
Q_OBJECT
public:
I2PController(QObject *parent = nullptr);
void start();
private:
QProcess i2p;
void restartI2P();
public slots:
void onProcessError(QProcess::ProcessError error);
void onProcessFinished(int exitCode, QProcess::ExitStatus status);
signals:
void fatalError(QString);
};
#endif // I2P_CONTROLLER_H

201
install-i2p.cpp Normal file
View File

@ -0,0 +1,201 @@
#include "install-i2p.h"
#include "fs-tools.h"
#include "network-downloader.h"
#include "prog-constains.h"
void addI2PDconfig (QString i2pdConf, QString tunnelsConf) {
#if defined(Q_OS_WIN)
#elif defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
#else
#endif
}
void configCheckAndMake () {
QString configsFolder = QDir(I2P_INSTALL_PATH).filePath("configs");
QString i2pdConf = QDir(configsFolder).filePath("i2pd.confconf");
QString tunnelsConf = QDir(configsFolder).filePath("tunnels.conf");
if (
!QFile(i2pdConf).exists() ||
!QFile(tunnelsConf).exists()
) {
qDebug() << "Configs not found. Creating new";
emptyFolder(configsFolder);
addI2PDconfig(i2pdConf, tunnelsConf);
}
}
bool checkI2P () {
QString I2P_VERSION = "i2pd version 2.59.0 (0.9.68)";
QProcess p;
p.start(QDir(I2P_INSTALL_PATH).filePath("i2pd"), {"--version"});
p.waitForFinished();
QString version = p.readAllStandardOutput();
bool isNeededVersion = version.contains(I2P_VERSION);
if (!isNeededVersion)
qDebug() << "current i2pd version:" << version;
return isNeededVersion;
}
bool installI2P(std::function<void(QString, unsigned int, unsigned int)> logCallback = nullptr) {
// Detect OS and architecture
QString i2pUrl; // https://github.com/PurpleI2P/i2pd/releases/tag/2.59.0
QString archiveType = "zip";
#if defined(Q_OS_WIN)
i2pUrl = "https://github.com/PurpleI2P/i2pd/releases/download/2.59.0/i2pd_2.59.0_win64_mingw.zip";
archiveType = "zip";
#elif defined(Q_OS_LINUX)
i2pUrl = "https://github.com/PurpleI2P/i2pd/archive/refs/tags/2.59.0.tar.gz";
archiveType = "tar.gz";
#elif defined(Q_OS_MACOS)
i2pUrl = "https://github.com/PurpleI2P/i2pd/releases/download/2.59.0/i2pd_2.59.0_osx.tar.gz";
archiveType = "tar.gz";
#endif
if (logCallback) logCallback("Creating directories...", 0, 0);
QDir tempDir(TEMP_PATH);
if (!tempDir.exists()) tempDir.mkpath(".");
QDir i2pDir(I2P_INSTALL_PATH);
if (!i2pDir.exists()) i2pDir.mkpath(".");
QString archivePath = tempDir.filePath(QFileInfo(QUrl(i2pUrl).path()).fileName());
// Download archive
bool isDownloaded = downloadFromUrl(i2pUrl, archivePath, logCallback);
if (!isDownloaded) return false;
// Extract archive
if (logCallback) logCallback("Extracting I2P archive...", 0, 0);
#if defined(Q_OS_WIN)
// TODO: Make it later
#elif defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
// Extract to temp folder first
QString tempExtractDir = QDir(TEMP_PATH).filePath("i2p_extract");
QDir().mkpath(tempExtractDir);
QProcess proc;
if (archiveType == "tar.gz") {
proc.start("tar", {"-xzf", archivePath, "-C", tempExtractDir});
} else if (archiveType == "zip") {
proc.start("unzip", {archivePath, "-d", tempExtractDir});
} else {
if (logCallback) logCallback("Unknown archive format", 0, 0);
return false;
}
proc.waitForFinished(-1);
// ### Build i2pd
if (logCallback) logCallback("Searching source directory...", 0, 4);
// Find extracted source dir (archive contains nested folder with Makefile)
QDir extractDir(tempExtractDir);
QString sourceDir;
for (const QFileInfo &entry : extractDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) {
QDir sub(entry.absoluteFilePath());
if (sub.exists("Makefile")) {
sourceDir = entry.absoluteFilePath();
break;
}
}
if (sourceDir.isEmpty()) {
if (logCallback) logCallback("Failed to find source directory (Makefile missing)", 0, 0);
return false;
}
// ---- BUILD ----
if (logCallback) logCallback("Building i2pd...", 1, 4);
QProcess procBuild;
procBuild.setWorkingDirectory(sourceDir);
procBuild.setProcessChannelMode(QProcess::MergedChannels);
int cpuCount = QThread::idealThreadCount();
procBuild.start("bash", {"-c", QString("make -j%1").arg(cpuCount)});
if (!procBuild.waitForStarted()) {
if (logCallback) logCallback("Failed to start make", 0, 0);
return false;
}
while (procBuild.state() == QProcess::Running) {
procBuild.waitForReadyRead(100);
QByteArray out = procBuild.readAll();
if (!out.isEmpty() && logCallback) {
QString text = QString::fromLocal8Bit(out).trimmed();
if (!text.isEmpty())
logCallback(text, 1, 4);
}
}
procBuild.waitForFinished(-1);
if (procBuild.exitStatus() != QProcess::NormalExit || procBuild.exitCode() != 0) {
QString err = QString::fromLocal8Bit(procBuild.readAll());
if (logCallback) logCallback("Build failed: " + err + ", visit: " + WIKI_URL + "Build-I2P-on-Linux", 0, 0);
return false;
}
// ---- FIND BINARY ----
if (logCallback) logCallback("Locating built binary...", 2, 4);
QString binaryPath;
QDirIterator it(sourceDir, QDirIterator::Subdirectories);
while (it.hasNext()) {
QString path = it.next();
QFileInfo fi(path);
if (fi.isFile() && fi.fileName() == "i2pd") {
binaryPath = fi.absoluteFilePath();
break;
}
}
if (binaryPath.isEmpty()) {
if (logCallback) logCallback("Built binary not found", 0, 0);
return false;
}
// ---- MOVE BINARY ----
if (logCallback) logCallback("Installing binary...", 3, 4);
QString destPath = QDir(I2P_INSTALL_PATH).filePath("i2pd");
QFile::remove(destPath);
if (!QFile::copy(binaryPath, destPath)) {
if (logCallback) logCallback("Failed to copy binary", 0, 0);
return false;
}
QFile::setPermissions(destPath,
QFile::permissions(destPath) |
QFileDevice::ExeOwner |
QFileDevice::ExeGroup |
QFileDevice::ExeOther);
if (logCallback) logCallback("I2P installation complete", 4, 4);
#endif
// ---- ADD CONFIGS ----
QString i2pdConfigsFolder = QDir(I2P_INSTALL_PATH).filePath("configs");
emptyFolder(QDir(I2P_INSTALL_PATH).filePath("data"));
emptyFolder(QDir(I2P_INSTALL_PATH).filePath("logs"));
emptyFolder(i2pdConfigsFolder);
addI2PDconfig(QDir(i2pdConfigsFolder).filePath("i2pd.conf"), QDir(i2pdConfigsFolder).filePath("tunnels.conf"));
// Check hash
QString hash = checkFileHash(QDir(I2P_INSTALL_PATH).filePath("i2pd"));
if (logCallback) logCallback("Total I2P installation hash: " + hash, 0, 0);
return true;
}

23
install-i2p.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef INSTALL_I2P_H
#define INSTALL_I2P_H
#include <QString>
#include <functional>
#include <QStandardPaths>
#include <QProcess>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QEventLoop>
#include <QFileInfo>
#include <QDir>
#include <QFile>
#include <QDebug>
#include <QDirIterator>
#include <QThread>
#include <QProcess>
bool installI2P(std::function<void(QString, unsigned int, unsigned int)> logCallback);
bool checkI2P ();
void configCheckAndMake ();
#endif // INSTALL_I2P_H

View File

@ -1,38 +1,7 @@
#include "install-java.h" #include "install-java.h"
#include "network-downloader.h" #include "network-downloader.h"
#include "fs-tools.h"
QString checkDirHash(QString path) { #include "prog-constains.h"
QCryptographicHash hash(QCryptographicHash::Sha256);
QDir dir(path);
if (!dir.exists()) {
qDebug() << "Directory not exists:" << path;
return QString();
}
QFileInfoList fileList = dir.entryInfoList(
QDir::Files | QDir::NoSymLinks | QDir::AllDirs | QDir::NoDotAndDotDot,
QDir::Name | QDir::DirsFirst
);
for (const QFileInfo &info : fileList) {
if (info.isDir()) {
QString subHash = checkDirHash(info.absoluteFilePath());
hash.addData(subHash.toUtf8());
} else if (info.isFile()) {
QFile file(info.absoluteFilePath());
if (file.open(QIODevice::ReadOnly)) {
while (!file.atEnd()) {
QByteArray chunk = file.read(8192);
hash.addData(chunk);
}
file.close();
}
}
}
return QString(hash.result().toHex());
}
bool checkJava () { bool checkJava () {
QString JAVA_HASH = "null"; QString JAVA_HASH = "null";
@ -45,22 +14,22 @@ bool checkJava () {
#endif #endif
#elif defined(Q_OS_LINUX) #elif defined(Q_OS_LINUX)
#if defined(Q_PROCESSOR_X86_64) #if defined(Q_PROCESSOR_X86_64)
//JAVA_HASH = "6268e94a0d6f37390fd33325725eb2991f9b26eb739baf2506064bfe3ea33075"; JAVA_HASH = "916b3a45f8f405ff2227b68e4225574b57e582585c47fd090b00d944af7c790c";
#elif defined(Q_PROCESSOR_ARM_64) #elif defined(Q_PROCESSOR_ARM_64)
#else #else
//JAVA_HASH = "null"; //JAVA_HASH = "null";
#endif #endif
#endif #endif
QString hash = checkDirHash("./java"); QString hash = checkDirHash(JAVA_INSTALL_PATH);
if (JAVA_HASH == "null" || hash != JAVA_HASH) if (JAVA_HASH == "null" || hash != JAVA_HASH)
qDebug() << "Java hash:" << hash; qDebug() << "Java dir hash:" << hash;
return hash == JAVA_HASH; return hash == JAVA_HASH;
} }
void installJava(std::function<void(QString)> logCallback = nullptr) { bool installJava(std::function<void(QString, unsigned int, unsigned int)> logCallback = nullptr) {
// Detect OS and architecture // Detect OS and architecture
QString javaUrl; QString javaUrl; // https://github.com/bell-sw/Liberica/releases/tag/17.0.18%2B11
QString archiveType = "zip"; QString archiveType = "zip";
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
@ -69,19 +38,19 @@ void installJava(std::function<void(QString)> logCallback = nullptr) {
#elif defined(Q_PROCESSOR_X86) #elif defined(Q_PROCESSOR_X86)
javaUrl = "https://example.com/java-windows-x86.zip"; javaUrl = "https://example.com/java-windows-x86.zip";
#else #else
if (logCallback) logCallback("Unsupported Windows architecture"); if (logCallback) logCallback("Unsupported Windows architecture", 0, 0);
return; return;
#endif #endif
#elif defined(Q_OS_LINUX) #elif defined(Q_OS_LINUX)
#if defined(Q_PROCESSOR_X86_64) #if defined(Q_PROCESSOR_X86_64)
javaUrl = ""; javaUrl = "https://github.com/bell-sw/Liberica/releases/download/17.0.18%2B11/bellsoft-jdk17.0.18+11-linux-amd64-crac.tar.gz";
archiveType = "tar.gz"; archiveType = "tar.gz";
#elif defined(Q_PROCESSOR_ARM_64) #elif defined(Q_PROCESSOR_ARM_64)
javaUrl = ""; javaUrl = "";
archiveType = "tar.gz"; archiveType = "tar.gz";
#else #else
if (logCallback) logCallback("Unsupported Linux architecture"); if (logCallback) logCallback("Unsupported Linux architecture", 0, 0);
return; return;
#endif #endif
@ -91,7 +60,7 @@ void installJava(std::function<void(QString)> logCallback = nullptr) {
#elif defined(Q_PROCESSOR_ARM_64) #elif defined(Q_PROCESSOR_ARM_64)
javaUrl = "https://example.com/java-macos-arm64.tar.gz"; javaUrl = "https://example.com/java-macos-arm64.tar.gz";
#else #else
if (logCallback) logCallback("Unsupported macOS architecture"); if (logCallback) logCallback("Unsupported macOS architecture", 0, 0);
return; return;
#endif #endif
@ -100,23 +69,24 @@ void installJava(std::function<void(QString)> logCallback = nullptr) {
return; return;
#endif #endif
if (logCallback) logCallback("Creating directories..."); if (logCallback) logCallback("Creating directories...", 0, 0);
QDir tempDir("./temp"); QDir tempDir(TEMP_PATH);
if (!tempDir.exists()) tempDir.mkpath("."); if (!tempDir.exists()) tempDir.mkpath(".");
QDir javaDir("./java"); QDir javaDir(JAVA_INSTALL_PATH);
if (!javaDir.exists()) javaDir.mkpath("."); if (!javaDir.exists()) javaDir.mkpath(".");
QString archivePath = tempDir.filePath(QFileInfo(QUrl(javaUrl).path()).fileName()); QString archivePath = tempDir.filePath(QFileInfo(QUrl(javaUrl).path()).fileName());
// Download archive // Download archive
downloadFromUrl(javaUrl, archivePath, logCallback); bool isDownloaded = downloadFromUrl(javaUrl, archivePath, logCallback);
if (!isDownloaded) return false;
// Extract archive // Extract archive
if (logCallback) logCallback("Extracting Java archive..."); if (logCallback) logCallback("Extracting Java archive...", 0, 0);
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
// Unzip and move contents up one level // Unzip and move contents up one level
QProcess proc; QProcess proc;
QString tempExtractDir = "./temp/java_extract"; QString tempExtractDir = QDir(TEMP_PATH).filePath("java_extract");
QDir().mkpath(tempExtractDir); QDir().mkpath(tempExtractDir);
proc.start("powershell", {"-Command", QString("Expand-Archive -Force -Path '%1' -DestinationPath '%2'").arg(archivePath).arg(tempExtractDir)}); proc.start("powershell", {"-Command", QString("Expand-Archive -Force -Path '%1' -DestinationPath '%2'").arg(archivePath).arg(tempExtractDir)});
proc.waitForFinished(-1); proc.waitForFinished(-1);
@ -136,7 +106,7 @@ void installJava(std::function<void(QString)> logCallback = nullptr) {
#elif defined(Q_OS_LINUX) || defined(Q_OS_MACOS) #elif defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
// Extract to temp folder first // Extract to temp folder first
QString tempExtractDir = "./temp/java_extract"; QString tempExtractDir = QDir(TEMP_PATH).filePath("java_extract");
QDir().mkpath(tempExtractDir); QDir().mkpath(tempExtractDir);
QProcess proc; QProcess proc;
@ -145,8 +115,8 @@ void installJava(std::function<void(QString)> logCallback = nullptr) {
} else if (archiveType == "zip") { } else if (archiveType == "zip") {
proc.start("unzip", {archivePath, "-d", tempExtractDir}); proc.start("unzip", {archivePath, "-d", tempExtractDir});
} else { } else {
if (logCallback) logCallback("Unknown archive format"); if (logCallback) logCallback("Unknown archive format", 0, 0);
return; return false;
} }
proc.waitForFinished(-1); proc.waitForFinished(-1);
@ -164,9 +134,11 @@ void installJava(std::function<void(QString)> logCallback = nullptr) {
QDir(tempExtractDir).removeRecursively(); QDir(tempExtractDir).removeRecursively();
#endif #endif
if (logCallback) logCallback("Extraction complete."); if (logCallback) logCallback("Extraction complete.", 0, 0);
// Check hash // Check hash
QString hash = checkDirHash("./java"); QString hash = checkDirHash(JAVA_INSTALL_PATH);
if (logCallback) logCallback("Java installation hash: " + hash); if (logCallback) logCallback("Total Java installation hash: " + hash, 0, 0);
return true;
} }

View File

@ -13,8 +13,7 @@
#include <QFile> #include <QFile>
#include <QDebug> #include <QDebug>
void installJava(std::function<void(QString)> logCallback); bool installJava(std::function<void(QString, unsigned int, unsigned int)> logCallback);
bool checkJava (); bool checkJava ();
QString checkDirHash(QString path);
#endif // INSTALL_JAVA_H #endif // INSTALL_JAVA_H

View File

@ -1,15 +1,17 @@
#include "loading-worker.h" #include "loading-worker.h"
#include "install-i2p.h"
#include "locales.h" #include "locales.h"
#include "install-java.h" #include "install-java.h"
#include <QLocale> #include "fs-tools.h"
#include "prog-constains.h"
#include <QLocale>
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <QCryptographicHash> #include <QCryptographicHash>
#include <QDebug> #include <QDebug>
LoadingWorker::LoadingWorker(Loading *loading, QObject *parent) { LoadingWorker::LoadingWorker(QObject *parent) {
this->loading = loading;
} }
LoadingWorker::~LoadingWorker () { LoadingWorker::~LoadingWorker () {
@ -23,36 +25,63 @@ void LoadingWorker::process() {
} }
LocaleMap* localeMap = locales[locale]; LocaleMap* localeMap = locales[locale];
emptyFolder(TEMP_PATH);
// Checking updates // Checking updates
this->loading->changeStep(localeMap->at("loading.checkUpdates")); emit changeStep(QString::fromStdString(localeMap->at("loading.checkUpdates")));
// TODO: Add it later // TODO: Add it later
// Checking: Is java installed // Checking: Is java installed
this->loading->changeStep(localeMap->at("loading.isJavaInst")); emit changeStep(QString::fromStdString(localeMap->at("loading.isJavaInst")));
if (!checkJava()) { if (!checkJava()) {
// Let's we install the java // Let's we install the java
installJava([this, localeMap](QString msg){
qDebug() << msg; emptyFolder(JAVA_INSTALL_PATH);
this->loading->changeStep(localeMap->at("loading.installJava") + ": " + msg.toStdString()); bool isDownloaded = installJava([this, localeMap](QString msg, unsigned int current, unsigned int total){
//qDebug() << "Java install:" << msg;
emit changeStep(QString::fromStdString(localeMap->at("loading.installJava") + ": " + msg.toStdString()));
if (total != 0) {
emit progressBar(current, total);
}
}); });
if (!isDownloaded) return;
} }
// Start I2P // Start I2P
this->loading->changeStep(localeMap->at("loading.i2p")); emit changeStep(QString::fromStdString(localeMap->at("loading.i2p")));
// TODO: Add it later if (!checkI2P()) {
// Let's we install i2p
emptyFolder(I2P_INSTALL_PATH);
bool isDownloaded = installI2P([this, localeMap](QString msg, unsigned int current, unsigned int total){
qDebug() << "I2P install:" << msg;
emit changeStep(QString::fromStdString(localeMap->at("loading.installI2P") + ": " + msg.toStdString()));
if (total != 0) {
emit progressBar(current, total);
}
});
if (!isDownloaded) return;
}
configCheckAndMake();
emit startI2P();
// Checking: Is minecraft installed
emit changeStep(QString::fromStdString(localeMap->at("loading.isMinecraftInst")));
// Getting endpoint API // Getting endpoint API
this->loading->changeStep(localeMap->at("loading.gettingEndpoint")); emit changeStep(QString::fromStdString(localeMap->at("loading.gettingEndpoint")));
std::string endpointAPI = "TODO: Add it later"; std::string endpointAPI = "TODO: Add it later";
// Handle data // Handle data
this->loading->changeStep(localeMap->at("loading.logging")); emit changeStep(QString::fromStdString(localeMap->at("loading.logging")));
// TODO: Add it later // TODO: Add it later
// Setup current locale for welcome form // Setup current locale for welcome form
this->loading->changeStep(localeMap->at("loading.setupLocale")); emit changeStep(QString::fromStdString(localeMap->at("loading.setupLocale")));
this->loading->setupWelcomeLocale(locale); emit setupWelcomeLocale(QString::fromStdString(locale));
this->loading->changeStep(localeMap->at("loading.end")); emit changeStep(QString::fromStdString(localeMap->at("loading.end")));
emptyFolder(TEMP_PATH);
emit finished(); emit finished();
this->loading->finishWorker();
} }

View File

@ -1,21 +1,25 @@
#ifndef LOADING_WORKER_H #ifndef LOADING_WORKER_H
#define LOADING_WORKER_H #define LOADING_WORKER_H
#include "loading.h" //#include "loading.h"
#include <qobject.h> #include <qobject.h>
class LoadingWorker : public QObject class LoadingWorker : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit LoadingWorker (Loading *loading, QObject *parent = nullptr); explicit LoadingWorker (QObject *parent = nullptr);
~LoadingWorker(); ~LoadingWorker();
private: private:
Loading *loading; //Loading *loading;
public slots: public slots:
void process(); void process();
signals: signals:
void finished(); void finished();
void progress(int value); void progress(int value);
void progressBar(int value, int max);
void changeStep(QString text);
void setupWelcomeLocale(QString locale);
void startI2P();
}; };
#endif // LOADING_WORKER_H #endif // LOADING_WORKER_H

View File

@ -1,8 +1,8 @@
#include "loading.h" #include "loading.h"
#include "loading-worker.h" #include "loading-worker.h"
#include "locales.h"
#include "ui_loading.h" #include "ui_loading.h"
#include <QThread> #include <QThread>
#include <qdebug.h>
Loading::Loading(QWidget *parent) Loading::Loading(QWidget *parent)
: QWidget(parent) : QWidget(parent)
@ -10,6 +10,7 @@ Loading::Loading(QWidget *parent)
{ {
ui->setupUi(this); ui->setupUi(this);
this->setWindowFlags(Qt::FramelessWindowHint); this->setWindowFlags(Qt::FramelessWindowHint);
this->ui->progressBar->hide();
this->startLoading(); this->startLoading();
} }
@ -18,11 +19,11 @@ Loading::~Loading()
delete ui; delete ui;
} }
void Loading::changeStep(std::string text) { void Loading::changeStep(QString text) {
this->ui->condition->setText(QString::fromStdString(text)); this->ui->condition->setText(text);
} }
void Loading::setupWelcomeLocale(std::string locale) { void Loading::setupWelcomeLocale(QString locale) {
this->welcome.setupWelcomeLocale(locale); this->welcome.setupWelcomeLocale(locale);
} }
@ -31,10 +32,30 @@ void Loading::finishWorker() {
this->welcome.show(); this->welcome.show();
} }
void Loading::setupProgressBar(unsigned int current, unsigned int max) {
if (this->ui->progressBar->isHidden() && (max != 0))
this->ui->progressBar->show();
else if (!this->ui->progressBar->isHidden() && (max == 0))
this->ui->progressBar->hide();
if (max != 0) {
// TODO: Fix displayed progressBar
if (this->ui->progressBar->maximum() != max)
this->ui->progressBar->setRange(0, max);
if (this->ui->progressBar->value() != current)
this->ui->progressBar->setValue(current);
}
}
void Loading::startI2P() {
qDebug() << "Starting I2P...";
this->i2p.start();
}
void Loading::startLoading() void Loading::startLoading()
{ {
QThread* thread = new QThread; QThread* thread = new QThread;
LoadingWorker* worker = new LoadingWorker(this); LoadingWorker* worker = new LoadingWorker;
worker->moveToThread(thread); worker->moveToThread(thread);
@ -50,5 +71,22 @@ void Loading::startLoading()
connect(thread, &QThread::finished, connect(thread, &QThread::finished,
thread, &QObject::deleteLater); thread, &QObject::deleteLater);
// Connection between loading and worker
connect(worker, &LoadingWorker::progressBar,
this, &Loading::setupProgressBar);
connect(worker, &LoadingWorker::changeStep,
this, &Loading::changeStep);
connect(worker, &LoadingWorker::setupWelcomeLocale,
this, &Loading::setupWelcomeLocale);
connect(worker, &LoadingWorker::finished,
this, &Loading::finishWorker);
// I2P
connect(worker, &LoadingWorker::startI2P,
this, &Loading::startI2P);
thread->start(); thread->start();
} }

View File

@ -2,6 +2,7 @@
#define LOADING_H #define LOADING_H
#include "welcome.h" #include "welcome.h"
#include "i2p-controller.h"
#include <QWidget> #include <QWidget>
namespace Ui { namespace Ui {
@ -15,14 +16,19 @@ class Loading : public QWidget
public: public:
explicit Loading(QWidget *parent = nullptr); explicit Loading(QWidget *parent = nullptr);
~Loading(); ~Loading();
void changeStep(std::string text);
void setupWelcomeLocale(std::string locale);
void finishWorker();
private: private:
Ui::Loading *ui; Ui::Loading *ui;
void startLoading(); void startLoading();
Welcome welcome; Welcome welcome;
I2PController i2p;
public slots:
void setupProgressBar(unsigned int current, unsigned int max);
void changeStep(QString text);
void setupWelcomeLocale(QString locale);
void finishWorker();
void startI2P();
}; };
#endif // LOADING_H #endif // LOADING_H

View File

@ -55,6 +55,25 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:16pt; font-weight:700;&quot;&gt;Conan Craft&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string> <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p align=&quot;center&quot;&gt;&lt;span style=&quot; font-size:16pt; font-weight:700;&quot;&gt;Conan Craft&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property> </property>
</widget> </widget>
<widget class="QProgressBar" name="progressBar">
<property name="geometry">
<rect>
<x>0</x>
<y>390</y>
<width>301</width>
<height>20</height>
</rect>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>24</number>
</property>
<property name="textVisible">
<bool>false</bool>
</property>
</widget>
</widget> </widget>
<resources> <resources>
<include location="source.qrc"/> <include location="source.qrc"/>

View File

@ -16,6 +16,8 @@ LocaleMap ru_RU = {
{"loading.logging", "Обрабатываем данные лаунчера"}, {"loading.logging", "Обрабатываем данные лаунчера"},
{"loading.setupLocale", "Устанавливаем язык лаунчера"}, {"loading.setupLocale", "Устанавливаем язык лаунчера"},
{"loading.end", "Закончили. Приятной игры"}, {"loading.end", "Закончили. Приятной игры"},
{"loading.installI2P", "Установка I2P"},
{"loading.isMinecraftInst", "Проверим установлен ли Майнкрафт"},
// Welcome // Welcome
{"welcome.main", "Главная"}, {"welcome.main", "Главная"},
{"welcome.settings", "Настройки"}, {"welcome.settings", "Настройки"},
@ -39,6 +41,8 @@ LocaleMap en_US = {
{"loading.logging", "Handle the launcher data"}, {"loading.logging", "Handle the launcher data"},
{"loading.setupLocale", "Setup the launcher's language"}, {"loading.setupLocale", "Setup the launcher's language"},
{"loading.end", "Finished. Let's start"}, {"loading.end", "Finished. Let's start"},
{"loading.installI2P", "Installing I2P"},
{"loading.isMinecraftInst", "Checking Is Minecraft installed"},
// Welcome // Welcome
{"welcome.main", "Home"}, {"welcome.main", "Home"},
{"welcome.settings", "Settings"}, {"welcome.settings", "Settings"},

View File

@ -1,18 +1,24 @@
#include "network-downloader.h" #include "network-downloader.h"
void downloadFromUrl(const QString &url, const QString &installPath, std::function<void(QString)> logCallback = nullptr) {
if (QFile::exists(installPath)) { bool downloadFromUrl(const QString &url, const QString &installPath, std::function<void(QString, unsigned int, unsigned int)> logCallback = nullptr) {
if (logCallback) logCallback("File already exists: " + installPath); if (url == "") {
return; if (logCallback) logCallback("Empty download URL", 0, 0);
return false;
} }
if (logCallback) logCallback("Downloading from url..."); if (QFile::exists(installPath)) {
if (logCallback) logCallback("File already exists: " + installPath, 0, 0);
return true;
}
if (logCallback) logCallback("Downloading from url...", 0, 0);
QNetworkAccessManager manager; QNetworkAccessManager manager;
QNetworkRequest request((QUrl(url))); QNetworkRequest request((QUrl(url)));
QEventLoop loop; QEventLoop loop;
QNetworkReply *reply = manager.get(request); QNetworkReply *reply = manager.get(request);
QObject::connect(reply, &QNetworkReply::downloadProgress, [&](qint64 received, qint64 total){ QObject::connect(reply, &QNetworkReply::downloadProgress, [&](qint64 received, qint64 total){
if (logCallback) logCallback(QString("Download progress: %1/%2 bytes").arg(received).arg(total)); if (logCallback) logCallback(QString("Download progress: %1/%2 bytes").arg(received).arg(total), received, total);
}); });
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
@ -21,36 +27,37 @@ void downloadFromUrl(const QString &url, const QString &installPath, std::functi
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (!statusCode.isValid()) { if (!statusCode.isValid()) {
if (logCallback) logCallback("Download failed: Invalid Status code"); if (logCallback) logCallback("Download failed: Invalid Status code", 0, 0);
reply->deleteLater(); reply->deleteLater();
return; return false;
} }
if (reply->error() != QNetworkReply::NoError) { if (reply->error() != QNetworkReply::NoError) {
if (logCallback) logCallback("Download failed: " + reply->errorString()); if (logCallback) logCallback("Download failed: " + reply->errorString(), 0, 0);
reply->deleteLater(); reply->deleteLater();
return; return false;
} }
if (statusCode.toInt() == 302 || statusCode.toInt() == 301) { if (statusCode.toInt() == 302 || statusCode.toInt() == 301) {
QVariant redir = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); QVariant redir = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
if (!redir.isValid()) { if (!redir.isValid()) {
if (logCallback) logCallback("Download failed: Invalid redirect"); if (logCallback) logCallback("Download failed: Invalid redirect", 0, 0);
reply->deleteLater(); reply->deleteLater();
return; return false;
} }
QUrl newUrl = redir.toUrl(); QUrl newUrl = redir.toUrl();
downloadFromUrl(newUrl.toString(), installPath, logCallback); return downloadFromUrl(newUrl.toString(), installPath, logCallback);
return;
} }
QFile file(installPath); QFile file(installPath);
if (file.open(QIODevice::WriteOnly)) { if (file.open(QIODevice::WriteOnly)) {
file.write(reply->readAll()); file.write(reply->readAll());
file.close(); file.close();
if (logCallback) logCallback("Download complete: " + installPath); if (logCallback) logCallback("Download complete: " + installPath, 0, 0);
} else { } else {
if (logCallback) logCallback("Cannot write file: " + installPath); if (logCallback) logCallback("Cannot write file: " + installPath, 0, 0);
} }
reply->deleteLater(); reply->deleteLater();
return true;
} }

View File

@ -13,6 +13,6 @@
#include <QFile> #include <QFile>
#include <QDebug> #include <QDebug>
void downloadFromUrl(const QString &url, const QString &installPath, std::function<void(QString)> logCallback); bool downloadFromUrl(const QString &url, const QString &installPath, std::function<void(QString, unsigned int, unsigned int)> logCallback);
#endif // NETWORK_DOWNLOADER_H #endif // NETWORK_DOWNLOADER_H

14
personal-account.cpp Normal file
View File

@ -0,0 +1,14 @@
#include "personal-account.h"
#include "ui_personal-account.h"
PersonalAccount::PersonalAccount(QWidget *parent)
: QWidget(parent)
, ui(new Ui::PersonalAccount)
{
ui->setupUi(this);
}
PersonalAccount::~PersonalAccount()
{
delete ui;
}

22
personal-account.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef PERSONAL_ACCOUNT_H
#define PERSONAL_ACCOUNT_H
#include <QWidget>
namespace Ui {
class PersonalAccount;
}
class PersonalAccount : public QWidget
{
Q_OBJECT
public:
explicit PersonalAccount(QWidget *parent = nullptr);
~PersonalAccount();
private:
Ui::PersonalAccount *ui;
};
#endif // PERSONAL_ACCOUNT_H

19
personal-account.ui Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>PersonalAccount</class>
<widget class="QWidget" name="PersonalAccount">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>580</width>
<height>440</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
</widget>
<resources/>
<connections/>
</ui>

7
prog-constains.cpp Normal file
View File

@ -0,0 +1,7 @@
#include "prog-constains.h"
const QString __dirname = QCoreApplication::applicationFilePath();
const QString JAVA_INSTALL_PATH = QDir(__dirname).filePath("java");
const QString I2P_INSTALL_PATH = QDir(__dirname).filePath("i2p");
const QString TEMP_PATH = QDir(__dirname).filePath("temp");
const QString WIKI_URL = "https://git.fullgream.tech/Conan-Craft/launcher/wiki/";

14
prog-constains.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef PROG_CONSTAINS_H
#define PROG_CONSTAINS_H
#include <QString>
#include <QDir>
#include <QCoreApplication>
extern const QString __dirname;
extern const QString JAVA_INSTALL_PATH;
extern const QString TEMP_PATH;
extern const QString I2P_INSTALL_PATH;
extern const QString WIKI_URL;
#endif // PROG_CONSTAINS_H

View File

@ -4,4 +4,7 @@
<file>sources/icons/32.png</file> <file>sources/icons/32.png</file>
<file>sources/icons/16.png</file> <file>sources/icons/16.png</file>
</qresource> </qresource>
<qresource prefix="/bgs">
<file>sources/backgrounds/conan-cary-nord2.jpg</file>
</qresource>
</RCC> </RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 KiB

View File

@ -14,11 +14,11 @@ Welcome::~Welcome()
delete ui; delete ui;
} }
void Welcome::setupWelcomeLocale(std::string locale) { void Welcome::setupWelcomeLocale(QString locale) {
if (locales.find(locale) == locales.end()) { if (locales.find(locale.toStdString()) == locales.end()) {
locale = "en_US"; locale = "en_US";
} }
LocaleMap* localeMap = locales[locale]; LocaleMap* localeMap = locales[locale.toStdString()];
this->ui->authForm->setTitle(QString::fromStdString(localeMap->at("welcome.authorise"))); this->ui->authForm->setTitle(QString::fromStdString(localeMap->at("welcome.authorise")));

View File

@ -16,7 +16,7 @@ class Welcome : public QMainWindow
public: public:
Welcome(QWidget *parent = nullptr); Welcome(QWidget *parent = nullptr);
~Welcome(); ~Welcome();
void setupWelcomeLocale(std::string locale); void setupWelcomeLocale(QString locale);
private: private:
Ui::Welcome *ui; Ui::Welcome *ui;

View File

@ -27,7 +27,16 @@
<height>551</height> <height>551</height>
</rect> </rect>
</property> </property>
<property name="styleSheet">
<string notr="true"/>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="main"> <widget class="QWidget" name="main">
<property name="styleSheet">
<string notr="true"/>
</property>
<attribute name="title"> <attribute name="title">
<string>welcome.main</string> <string>welcome.main</string>
</attribute> </attribute>