launcher/install-i2p.cpp

231 lines
7.2 KiB
C++

#include "install-i2p.h"
#include "fs-tools.h"
#include "network-downloader.h"
#include "prog-constains.h"
void addI2PDconfig(QString i2pdConf, QString tunnelsConf)
{
auto writeIfMissing = [](const QString &path, const QString &content)
{
QFileInfo info(path);
// create directory if missing
QDir dir = info.dir();
if (!dir.exists())
dir.mkpath(".");
// create file only if it doesn't exist
if (!QFile::exists(path))
{
QFile file(path);
if (file.open(QIODevice::WriteOnly | QIODevice::Text))
{
QTextStream out(&file);
out << content;
file.close();
}
else
{
qWarning() << "Failed to create config:" << path;
}
}
};
writeIfMissing(i2pdConf, DEFAULT_I2PD_CONF);
writeIfMissing(tunnelsConf, DEFAULT_TUNNELS_CONF);
}
void configCheckAndMake () {
#if defined(Q_OS_WIN)
#elif defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
QString configsFolder = QDir(I2P_INSTALL_PATH).filePath("configs");
QString i2pdConf = QDir(configsFolder).filePath("i2pd.conf");
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);
}
#else
#endif
}
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;
}