#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 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; }