mirror of
https://github.com/Mahdi-zarei/nekoray.git
synced 2025-12-19 13:50:12 +08:00
264 lines
8.3 KiB
C++
264 lines
8.3 KiB
C++
#include <csignal>
|
|
|
|
#include <QApplication>
|
|
#include <QCryptographicHash>
|
|
#include <QDir>
|
|
#include <QTranslator>
|
|
#include <QMessageBox>
|
|
#include <QStandardPaths>
|
|
#include <QLocalSocket>
|
|
#include <QLocalServer>
|
|
#include <QThread>
|
|
#include <3rdparty/WinCommander.hpp>
|
|
|
|
#include "include/global/NekoGui.hpp"
|
|
#include "include/sys/windows/vcCheck.h"
|
|
|
|
#include "include/ui/mainwindow_interface.h"
|
|
|
|
#ifdef Q_OS_WIN
|
|
#include "include/sys/windows/MiniDump.h"
|
|
#pragma comment (lib, "cpr.lib")
|
|
#pragma comment (lib, "libcurl.lib")
|
|
#pragma comment (lib, "Ws2_32.lib")
|
|
#pragma comment (lib, "Wldap32.lib")
|
|
#pragma comment (lib, "Crypt32.lib")
|
|
#endif
|
|
|
|
void signal_handler(int signum) {
|
|
if (qApp) {
|
|
GetMainWindow()->on_commitDataRequest();
|
|
qApp->exit();
|
|
}
|
|
}
|
|
|
|
QTranslator* trans = nullptr;
|
|
QTranslator* trans_qt = nullptr;
|
|
|
|
void loadTranslate(const QString& locale) {
|
|
QT_TRANSLATE_NOOP("QPlatformTheme", "Cancel");
|
|
QT_TRANSLATE_NOOP("QPlatformTheme", "Apply");
|
|
QT_TRANSLATE_NOOP("QPlatformTheme", "Yes");
|
|
QT_TRANSLATE_NOOP("QPlatformTheme", "No");
|
|
QT_TRANSLATE_NOOP("QPlatformTheme", "OK");
|
|
if (trans != nullptr) {
|
|
trans->deleteLater();
|
|
}
|
|
if (trans_qt != nullptr) {
|
|
trans_qt->deleteLater();
|
|
}
|
|
//
|
|
trans = new QTranslator;
|
|
trans_qt = new QTranslator;
|
|
QLocale::setDefault(QLocale(locale));
|
|
//
|
|
if (trans->load(":/translations/" + locale + ".qm")) {
|
|
QCoreApplication::installTranslator(trans);
|
|
}
|
|
}
|
|
|
|
#define LOCAL_SERVER_PREFIX "nekoray-"
|
|
|
|
int main(int argc, char* argv[]) {
|
|
// Core dump
|
|
#ifdef Q_OS_WIN
|
|
Windows_SetCrashHandler();
|
|
#endif
|
|
|
|
QApplication::setAttribute(Qt::AA_DontUseNativeDialogs);
|
|
QApplication::setQuitOnLastWindowClosed(false);
|
|
QApplication a(argc, argv);
|
|
|
|
#ifdef Q_OS_WIN
|
|
if (!checkVCRedist())
|
|
{
|
|
QMessageBox::critical(nullptr, "Cannot run Nekoray", "You need to install VC 2022 Redistributable, Download it from: https://aka.ms/vs/17/release/vc_redist.x64.exe");
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
// Clean
|
|
QDir::setCurrent(QApplication::applicationDirPath());
|
|
if (QFile::exists("updater.old")) {
|
|
QFile::remove("updater.old");
|
|
}
|
|
|
|
// Flags
|
|
NekoGui::dataStore->argv = QApplication::arguments();
|
|
if (NekoGui::dataStore->argv.contains("-many")) NekoGui::dataStore->flag_many = true;
|
|
if (NekoGui::dataStore->argv.contains("-appdata")) {
|
|
NekoGui::dataStore->flag_use_appdata = true;
|
|
int appdataIndex = NekoGui::dataStore->argv.indexOf("-appdata");
|
|
if (NekoGui::dataStore->argv.size() > appdataIndex + 1 && !NekoGui::dataStore->argv.at(appdataIndex + 1).startsWith("-")) {
|
|
NekoGui::dataStore->appdataDir = NekoGui::dataStore->argv.at(appdataIndex + 1);
|
|
}
|
|
}
|
|
if (NekoGui::dataStore->argv.contains("-tray")) NekoGui::dataStore->flag_tray = true;
|
|
if (NekoGui::dataStore->argv.contains("-debug")) NekoGui::dataStore->flag_debug = true;
|
|
if (NekoGui::dataStore->argv.contains("-flag_restart_tun_on")) NekoGui::dataStore->flag_restart_tun_on = true;
|
|
if (NekoGui::dataStore->argv.contains("-flag_restart_dns_set")) NekoGui::dataStore->flag_dns_set = true;
|
|
if (NekoGui::dataStore->argv.contains("-flag_reorder")) NekoGui::dataStore->flag_reorder = true;
|
|
#ifdef NKR_CPP_USE_APPDATA
|
|
NekoGui::dataStore->flag_use_appdata = true; // Example: Package & MacOS
|
|
#endif
|
|
#ifdef NKR_CPP_DEBUG
|
|
NekoGui::dataStore->flag_debug = true;
|
|
#endif
|
|
|
|
// dirs & clean
|
|
auto wd = QDir(QApplication::applicationDirPath());
|
|
if (NekoGui::dataStore->flag_use_appdata) {
|
|
QApplication::setApplicationName("nekoray");
|
|
if (!NekoGui::dataStore->appdataDir.isEmpty()) {
|
|
wd.setPath(NekoGui::dataStore->appdataDir);
|
|
} else {
|
|
wd.setPath(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
|
|
}
|
|
}
|
|
if (!wd.exists()) wd.mkpath(wd.absolutePath());
|
|
if (!wd.exists("config")) wd.mkdir("config");
|
|
QDir::setCurrent(wd.absoluteFilePath("config"));
|
|
QDir("temp").removeRecursively();
|
|
|
|
#ifdef Q_OS_LINUX
|
|
QApplication::addLibraryPath(QApplication::applicationDirPath() + "/usr/plugins");
|
|
#endif
|
|
|
|
// dispatchers
|
|
DS_cores = new QThread;
|
|
DS_cores->start();
|
|
|
|
// icons
|
|
QIcon::setFallbackSearchPaths(QStringList{
|
|
":/nekoray",
|
|
":/icon",
|
|
});
|
|
|
|
// icon for no theme
|
|
if (QIcon::themeName().isEmpty()) {
|
|
QIcon::setThemeName("breeze");
|
|
}
|
|
|
|
// Load coreType
|
|
auto coreLoaded = ReadFileText("groups/coreType");
|
|
if (coreLoaded.isEmpty()) {
|
|
NekoGui::coreType = -1;
|
|
loadTranslate(QLocale().name());
|
|
NekoGui::coreType = NekoGui::CoreType::SING_BOX;
|
|
QDir().mkdir("groups");
|
|
QFile file;
|
|
file.setFileName("groups/coreType");
|
|
file.open(QIODevice::ReadWrite | QIODevice::Truncate);
|
|
file.write(Int2String(NekoGui::coreType).toUtf8());
|
|
file.close();
|
|
} else {
|
|
NekoGui::coreType = coreLoaded.toInt();
|
|
}
|
|
|
|
// Dir
|
|
QDir dir;
|
|
bool dir_success = true;
|
|
if (!dir.exists("profiles")) {
|
|
dir_success &= dir.mkdir("profiles");
|
|
}
|
|
if (!dir.exists("groups")) {
|
|
dir_success &= dir.mkdir("groups");
|
|
}
|
|
if (!dir.exists(ROUTES_PREFIX_NAME)) {
|
|
dir_success &= dir.mkdir(ROUTES_PREFIX_NAME);
|
|
}
|
|
if (!dir.exists(RULE_SETS_DIR)) {
|
|
dir_success &= dir.mkdir(RULE_SETS_DIR);
|
|
}
|
|
if (!dir_success) {
|
|
QMessageBox::critical(nullptr, "Error", "No permission to write " + dir.absolutePath());
|
|
return 1;
|
|
}
|
|
|
|
// Load dataStore
|
|
NekoGui::dataStore->fn = "groups/nekobox.json";
|
|
auto isLoaded = NekoGui::dataStore->Load();
|
|
if (!isLoaded) {
|
|
NekoGui::dataStore->Save();
|
|
}
|
|
|
|
#ifdef Q_OS_WIN
|
|
if (NekoGui::dataStore->windows_set_admin && !NekoGui::IsAdmin())
|
|
{
|
|
NekoGui::dataStore->windows_set_admin = false; // so that if permission denied, we will run as user on the next run
|
|
NekoGui::dataStore->Save();
|
|
WinCommander::runProcessElevated(QApplication::applicationFilePath(), {}, "", WinCommander::SW_NORMAL, false);
|
|
QApplication::quit();
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
// Datastore & Flags
|
|
if (NekoGui::dataStore->start_minimal) NekoGui::dataStore->flag_tray = true;
|
|
|
|
// load routing
|
|
NekoGui::dataStore->routing = std::make_unique<NekoGui::Routing>();
|
|
NekoGui::dataStore->routing->fn = ROUTES_PREFIX + "Default";
|
|
isLoaded = NekoGui::dataStore->routing->Load();
|
|
if (!isLoaded) {
|
|
NekoGui::dataStore->routing->Save();
|
|
}
|
|
|
|
// Translate
|
|
QString locale;
|
|
switch (NekoGui::dataStore->language) {
|
|
case 1: // English
|
|
break;
|
|
case 2:
|
|
locale = "zh_CN";
|
|
break;
|
|
case 3:
|
|
locale = "fa_IR"; // farsi(iran)
|
|
break;
|
|
case 4:
|
|
locale = "ru_RU"; // Russian
|
|
break;
|
|
default:
|
|
locale = QLocale().name();
|
|
}
|
|
QGuiApplication::tr("QT_LAYOUT_DIRECTION");
|
|
loadTranslate(locale);
|
|
|
|
// Signals
|
|
signal(SIGTERM, signal_handler);
|
|
signal(SIGINT, signal_handler);
|
|
|
|
// Check if another instance is running
|
|
QByteArray hashBytes = QCryptographicHash::hash(wd.absolutePath().toUtf8(), QCryptographicHash::Md5).toBase64(QByteArray::OmitTrailingEquals);
|
|
hashBytes.replace('+', '0').replace('/', '1');
|
|
auto serverName = LOCAL_SERVER_PREFIX + QString::fromUtf8(hashBytes);
|
|
qDebug() << "server name: " << serverName;
|
|
QLocalSocket socket;
|
|
socket.connectToServer(serverName);
|
|
if (socket.waitForConnected(250))
|
|
{
|
|
qDebug() << "Another instance is running, let's wake it up and quit";
|
|
socket.disconnectFromServer();
|
|
return 0;
|
|
}
|
|
|
|
// QLocalServer
|
|
QLocalServer server(qApp);
|
|
QLocalServer::removeServer(serverName);
|
|
server.setSocketOptions(QLocalServer::WorldAccessOption);
|
|
if (!server.listen(serverName)) {
|
|
qWarning() << "Failed to start QLocalServer! Error:" << server.errorString();
|
|
return 1;
|
|
}
|
|
QObject::connect(&server, &QLocalServer::newConnection, qApp, [&] {
|
|
auto s = server.nextPendingConnection();
|
|
qDebug() << "Another instance tried to wake us up on " << serverName << s;
|
|
s->close();
|
|
// raise main window
|
|
MW_dialog_message("", "Raise");
|
|
});
|
|
|
|
UI_InitMainWindow();
|
|
return QApplication::exec();
|
|
}
|