diff --git a/Qv2ray.pro b/Qv2ray.pro index 8101cb4b..0797cd84 100644 --- a/Qv2ray.pro +++ b/Qv2ray.pro @@ -14,7 +14,7 @@ CONFIG += c++11 openssl openssl-linked lrelease embed_translations win32: QMAKE_TARGET_DESCRIPTION = "Qv2ray, a cross-platform v2ray GUI client." win32: QMAKE_TARGET_PRODUCT = "Qv2ray" -VERSION = 1.3.6.1 +VERSION = 1.3.7.0 DEFINES += QV_MAJOR_VERSION=\"\\\"$${VERSION}\\\"\" SOURCES += \ diff --git a/src/Qv2rayBase.h b/src/Qv2rayBase.h index 60e3a006..7e2d55a9 100644 --- a/src/Qv2rayBase.h +++ b/src/Qv2rayBase.h @@ -7,10 +7,22 @@ #define QV2RAY_VERSION_STRING "v" QV_MAJOR_VERSION -#define QV2RAY_CONFIG_VERSION 2 +#define QV2RAY_CONFIG_VERSION 3 +// Base folder. #define QV2RAY_CONFIG_DIR_PATH (Qv2ray::Utils::GetConfigDirPath() + "/") #define QV2RAY_CONFIG_FILE_PATH (QV2RAY_CONFIG_DIR_PATH + "Qv2ray.conf") +// We need v2ray.exe/v2ray executables here! +#define QV2RAY_V2RAY_CORE_DIR_PATH (QV2RAY_CONFIG_DIR_PATH + "vcore/") + +#ifdef __WIN32 +// Win32 has .exe +#define QV2RAY_V2RAY_CORE_PATH (QV2RAY_V2RAY_CORE_DIR_PATH + "v2ray.exe") +#else +// MacOS and Linux.... +#define QV2RAY_V2RAY_CORE_PATH (QV2RAY_V2RAY_CORE_DIR_PATH + "v2ray") +#endif + #define QV2RAY_CONNECTION_FILE_EXTENSION ".qv2ray.json" #define QV2RAY_GENERATED_FILE_PATH (QV2RAY_CONFIG_DIR_PATH + "generated/config.gen.json") @@ -18,15 +30,6 @@ #define QV2RAY_VCORE_ACCESS_LOG_FILENAME "access.log" #define QV2RAY_VCORE_ERROR_LOG_FILENAME "error.log" -// These is for early-2.0 version, final 2.0 will move these content into global config. -#define QV2RAY_CONFIG_TYPE_FILE "File" -#define QV2RAY_CONFIG_TYPE_MANUAL "Manual" -#define QV2RAY_CONFIG_TYPE_CONNECTIONSTRING "ConnectionString" -#define QV2RAY_CONFIG_TYPE_SUBSCRIPTION "Subscription" -#define QV2RAY_CONFIG_TYPE_JSON_KEY "_qv2ray.configSource" - - - // GUI TOOLS #define RED(obj) \ auto _temp = ui->obj->palette(); \ @@ -77,7 +80,6 @@ namespace Qv2ray int logLevel; // string language; - string v2CorePath; string v2AssetsPath; string autoStartConfig; // @@ -97,13 +99,12 @@ namespace Qv2ray map subscribes; MuxObject mux; Qv2rayConfig(): config_version(QV2RAY_CONFIG_VERSION), runAsRoot(false), logLevel(), proxyDefault(), proxyCN(), withLocalDNS(), inBoundSettings(), configs(), subscribes(), mux() { } - Qv2rayConfig(string lang, string exePath, string assetsPath, int log, Qv2rayBasicInboundsConfig _inBoundSettings): Qv2rayConfig() + Qv2rayConfig(string lang, string assetsPath, int log, Qv2rayBasicInboundsConfig _inBoundSettings): Qv2rayConfig() { // These settings below are defaults. ignoredVersion = ""; autoStartConfig = ""; language = lang; - v2CorePath = exePath; v2AssetsPath = assetsPath; logLevel = log; inBoundSettings = _inBoundSettings; @@ -116,7 +117,7 @@ namespace Qv2ray proxyDefault = true; withLocalDNS = true; } - XTOSTRUCT(O(config_version, runAsRoot, logLevel, language, autoStartConfig, ignoredVersion, v2CorePath, v2AssetsPath, proxyDefault, proxyCN, withLocalDNS, dnsList, inBoundSettings, mux, configs, subscribes)) + XTOSTRUCT(O(config_version, runAsRoot, logLevel, language, autoStartConfig, ignoredVersion, v2AssetsPath, proxyDefault, proxyCN, withLocalDNS, dnsList, inBoundSettings, mux, configs, subscribes)) }; QJsonObject UpgradeConfig(int fromVersion, int toVersion, QJsonObject root); diff --git a/src/QvConfigUpgrade.cpp b/src/QvConfigUpgrade.cpp index 87f0a839..49a6c402 100644 --- a/src/QvConfigUpgrade.cpp +++ b/src/QvConfigUpgrade.cpp @@ -3,7 +3,7 @@ // from old to newer versions of Qv2ray. // -#include "Qv2rayBase.h" +#include "QvUtils.h" #define UPGRADELOG(item, old, _new) LOG(MODULE_CONFIG, "Upgrading " item " from old value " + old + " to " + _new); #define XConfLog(oldVersion, newVersion) LOG(MODULE_CONFIG, "Migrating config from version " + oldVersion + " to " + newVersion); @@ -18,15 +18,31 @@ namespace Qv2ray XConfLog(to_string(fromVersion), to_string(fromVersion + 1)) switch (fromVersion) { - case 1: + case 1: { // From 1 to 2, we changed the config_version from 'string' to 'int' auto old_config_version = root["config_version"].toString(); root.remove("config_version"); root["config_version"] = 2; UPGRADELOG("config_version", old_config_version.toStdString(), "2") break; + } + + case 2 : { + auto vCoreFilePath = root["v2CorePath"].toString(); + auto vCoreDestPath = QV2RAY_V2RAY_CORE_PATH; + // We also need v2ctl + auto v2CtlFilePath = QFileInfo(vCoreFilePath).dir().path() + "/v2ctl"; + auto v2CtlDestPath = QFileInfo(vCoreDestPath).dir().path() + "/v2ctl"; + QFile::copy(vCoreFilePath, vCoreDestPath); + QFile::copy(v2CtlFilePath, v2CtlDestPath); + root.remove("v2CorePath"); + UPGRADELOG("v2CorePath", vCoreFilePath.toStdString(), vCoreDestPath.toStdString()) + UPGRADELOG("v2CtlFilePath", v2CtlFilePath.toStdString(), v2CtlDestPath.toStdString()) + break; + } } + root["config_version"] = root["config_version"].toInt() + 1; return root; } diff --git a/src/QvCoreConfigObjects.h b/src/QvCoreConfigObjects.h index da3e4498..b353e8cf 100644 --- a/src/QvCoreConfigObjects.h +++ b/src/QvCoreConfigObjects.h @@ -16,10 +16,6 @@ namespace Qv2ray { namespace V2ConfigModels { - struct VMessProtocolConfigObject { - string v, ps, add, port, id, aid, net, type, host, path, tls; - XTOSTRUCT(O(v, ps, add, port, id, aid, net, type, host, path, tls)) - }; // // Used in config generation struct AccountObject { diff --git a/src/QvCoreConfigOperations.h b/src/QvCoreConfigOperations.h index 5ee7797c..5a112d02 100644 --- a/src/QvCoreConfigOperations.h +++ b/src/QvCoreConfigOperations.h @@ -57,7 +57,7 @@ namespace Qv2ray bool SaveConnectionConfig(QJsonObject obj, const QString *alias); bool RenameConnection(QString originalName, QString newName); // VMess Protocol - QJsonObject ConvertConfigFromVMessString(QString vmess, QString source = QV2RAY_CONFIG_TYPE_CONNECTIONSTRING); + QJsonObject ConvertConfigFromVMessString(QString vmess); QJsonObject ConvertConfigFromFile(QString sourceFilePath, bool overrideInbounds); // Load Configs QMap GetConnections(list connections); diff --git a/src/QvCoreConfigOperations_Convertion.cpp b/src/QvCoreConfigOperations_Convertion.cpp index edca6160..df8a6809 100644 --- a/src/QvCoreConfigOperations_Convertion.cpp +++ b/src/QvCoreConfigOperations_Convertion.cpp @@ -7,25 +7,39 @@ namespace Qv2ray bool SaveConnectionConfig(QJsonObject obj, const QString *alias) { QFile config(QV2RAY_CONFIG_DIR_PATH + *alias + QV2RAY_CONNECTION_FILE_EXTENSION); - return StringToFile(JSONToString(obj), &config); + auto str = JSONToString(obj); + return StringToFile(&str, &config); } // This generates global config containing only one outbound.... - QJsonObject ConvertConfigFromVMessString(QString str, QString source) + QJsonObject ConvertConfigFromVMessString(QString str) { DROOT QStringRef vmessJsonB64(&str, 8, str.length() - 8); - auto vmessConf = StructFromJSONString(Base64Decode(vmessJsonB64.toString())); + auto vmessConf = JSONFromString(Base64Decode(vmessJsonB64.toString())); + string ps, add, id, net, type, host, path, tls; + int port, aid; + ps = vmessConf["ps"].toVariant().toString().toStdString(); + add = vmessConf["add"].toVariant().toString().toStdString(); + id = vmessConf["id"].toVariant().toString().toStdString(); + net = vmessConf["net"].toVariant().toString().toStdString(); + type = vmessConf["type"].toVariant().toString().toStdString(); + host = vmessConf["host"].toVariant().toString().toStdString(); + path = vmessConf["path"].toVariant().toString().toStdString(); + tls = vmessConf["tls"].toVariant().toString().toStdString(); + // + port = vmessConf["port"].toVariant().toInt(); + aid = vmessConf["aid"].toVariant().toInt(); // // User VMessServerObject::UserObject user; - user.id = vmessConf.id; - user.alterId = stoi(vmessConf.aid); + user.id = id; + user.alterId = aid; // // Server VMessServerObject serv; - serv.port = stoi(vmessConf.port); - serv.address = vmessConf.add; + serv.port = port; + serv.address = add; serv.users.push_back(user); // // VMess root config @@ -38,35 +52,34 @@ namespace Qv2ray StreamSettingsObject streaming; // Fill hosts for HTTP - foreach (auto host, QString::fromStdString(vmessConf.host).split(',')) { - streaming.httpSettings.host.push_back(host.toStdString()); + foreach (auto _host, QString::fromStdString(host).split(',')) { + streaming.httpSettings.host.push_back(_host.toStdString()); } // hosts for ws, h2 and security for QUIC - streaming.wsSettings.headers.insert(make_pair("Host", vmessConf.host)); - streaming.quicSettings.security = vmessConf.host; + streaming.wsSettings.headers.insert(make_pair("Host", host)); + streaming.quicSettings.security = host; // // Fake type for tcp, kcp and QUIC - streaming.tcpSettings.header.type = vmessConf.type; - streaming.kcpSettings.header.type = vmessConf.type; - streaming.quicSettings.header.type = vmessConf.type; + streaming.tcpSettings.header.type = type; + streaming.kcpSettings.header.type = type; + streaming.quicSettings.header.type = type; // // Path for ws, h2, Quic - streaming.wsSettings.path = vmessConf.path; - streaming.httpSettings.path = vmessConf.path; - streaming.quicSettings.key = vmessConf.path; - streaming.security = vmessConf.tls; + streaming.wsSettings.path = path; + streaming.httpSettings.path = path; + streaming.quicSettings.key = path; + streaming.security = tls; // // Network type - streaming.network = vmessConf.net; + streaming.network = net; // auto outbound = GenerateOutboundEntry("vmess", vConf, GetRootObject(streaming), GetRootObject(GetGlobalConfig().mux), "0.0.0.0", OUTBOUND_TAG_PROXY); // QJsonArray outbounds; outbounds.append(outbound); root.insert("outbounds", outbounds); - root.insert("QV2RAY_ALIAS", QString::fromStdString(vmessConf.ps)); - root.insert(QV2RAY_CONFIG_TYPE_JSON_KEY, source); + root.insert("QV2RAY_ALIAS", QString::fromStdString(ps)); RROOT } @@ -82,7 +95,6 @@ namespace Qv2ray JSON_ROOT_TRY_REMOVE("api") JSON_ROOT_TRY_REMOVE("stats") JSON_ROOT_TRY_REMOVE("dns") - root.insert(QV2RAY_CONFIG_TYPE_JSON_KEY, QV2RAY_CONFIG_TYPE_FILE); return root; } @@ -107,7 +119,7 @@ namespace Qv2ray int StartPreparation(QJsonObject fullConfig) { QString json = JSONToString(fullConfig); - StringToFile(json, new QFile(QV2RAY_GENERATED_FILE_PATH)); + StringToFile(&json, new QFile(QV2RAY_GENERATED_FILE_PATH)); return 0; } } diff --git a/src/QvCoreConfigOperations_Generation.cpp b/src/QvCoreConfigOperations_Generation.cpp index 71603e4b..e1a4d410 100644 --- a/src/QvCoreConfigOperations_Generation.cpp +++ b/src/QvCoreConfigOperations_Generation.cpp @@ -4,6 +4,7 @@ namespace Qv2ray { namespace ConfigOperations { + static const QStringList vLogLevels = {"none", "debug", "info", "warning", "error"}; // -------------------------- BEGIN CONFIG GENERATIONS ---------------------------------------------------------------------------- QJsonObject GenerateRoutes(bool globalProxy, bool cnProxy) { @@ -129,33 +130,11 @@ namespace Qv2ray { auto gConf = GetGlobalConfig(); QJsonObject logObject; + // //logObject.insert("access", QV2RAY_CONFIG_PATH + QV2RAY_VCORE_LOG_DIRNAME + QV2RAY_VCORE_ACCESS_LOG_FILENAME); //logObject.insert("error", QV2RAY_CONFIG_PATH + QV2RAY_VCORE_LOG_DIRNAME + QV2RAY_VCORE_ERROR_LOG_FILENAME); - QString logLevel_s; - - switch (gConf.logLevel) { - case 0: - logLevel_s = "none"; - break; - - case 1: - logLevel_s = "debug"; - break; - - case 2: - logLevel_s = "info"; - break; - - case 3: - logLevel_s = "warning"; - break; - - case 4: - logLevel_s = "error"; - break; - } - - logObject.insert("loglevel", logLevel_s); + // + logObject.insert("loglevel", vLogLevels[gConf.logLevel]); root.insert("log", logObject); // QStringList dnsList; @@ -166,16 +145,6 @@ namespace Qv2ray auto dnsObject = GenerateDNS(gConf.withLocalDNS, dnsList); root.insert("dns", dnsObject); - - // - // This is for imported config files as there are routing entries already. - // We don't add extra routings. - // We don't use QV2RAY_CONFIG_TYPE_FILE checking scheme because not all connections have this part. - if (!root.contains("routing")) { - auto routeObject = GenerateRoutes(gConf.proxyDefault, gConf.proxyCN); - root.insert("routing", routeObject); - } - // // root.insert("stats", QJsonObject()); @@ -210,23 +179,43 @@ namespace Qv2ray inboundsList.append(socksInBoundObject); } - if (!root.contains("inbounds") || root["inbounds"].toArray().count() == 0) { + if (!root.contains("inbounds") || root["inbounds"].toArray().empty()) { root.insert("inbounds", inboundsList); } - // TODO: MultiOutbound Settings - if (root.contains(QV2RAY_CONFIG_TYPE_JSON_KEY) && root[QV2RAY_CONFIG_TYPE_JSON_KEY] == QV2RAY_CONFIG_TYPE_FILE) { - LOG(MODULE_CONFIG, "Found an imported config file, skipping adding 'freedom' outbound.") - // Do nothing because it's an imported connection. - } else { + // Note: The part below always makes the whole functionality in trouble...... + // BE EXTREME CAREFUL when changing these code below... + // + + // For SOME configs, there is no "route" entries, so, we add some... + + // We don't use QV2RAY_CONFIG_TYPE_FILE to check because not all IMPORTED connections have routings. + if (!root.contains("routing")) { + if (root["outbounds"].toArray().count() != 1) { + // There are no ROUTING but 2 or more outbounds.... This is rare, but possible. + LOG(MODULE_CONNECTION, "WARN: This message usually indicates the config file has some logic errors:") + LOG(MODULE_CONNECTION, "WARN: --> The config file has NO routing section, however more than 1 outbounds are detected.") + } + + LOG(MODULE_CONNECTION, "Current connection has NO ROUTING section, we insert default values.") + auto routeObject = GenerateRoutes(gConf.proxyDefault, gConf.proxyCN); + root.insert("routing", routeObject); QJsonArray outbounds = root["outbounds"].toArray(); - // It's not imported so we add new stuff. - // For DIRECT outbounds.append(GenerateOutboundEntry("freedom", GenerateFreedomOUT("AsIs", ":0", 0), QJsonObject(), QJsonObject(), "0.0.0.0", OUTBOUND_TAG_DIRECT)); - QJsonObject first = outbounds.first().toObject(); - first.insert("mux", GetRootObject(gConf.mux)); - outbounds[0] = first; + // TODO + // + // We don't want to add MUX into the first one in the list..... + // However, this can be added to the Connection Edit Window... + //QJsonObject first = outbounds.first().toObject(); + //first.insert("mux", GetRootObject(gConf.mux)); + //outbounds[0] = first; + // root["outbounds"] = outbounds; + } else { + // For some config files that has routing entries already. + // We don't add extra routings. + // this part has been left blanking + LOG(MODULE_CONNECTION, "Skip adding 'freedom' entry.") } return root; diff --git a/src/QvCoreConfigOperations_Verification.cpp b/src/QvCoreConfigOperations_Verification.cpp index a8e229dc..27e8ce1f 100644 --- a/src/QvCoreConfigOperations_Verification.cpp +++ b/src/QvCoreConfigOperations_Verification.cpp @@ -16,10 +16,15 @@ namespace Qv2ray try { QStringRef vmessJsonB64(&vmess, 8, vmess.length() - 8); auto vmessString = Base64Decode(vmessJsonB64.toString()); - auto vmessConf = StructFromJSONString(vmessString); - return 0; + auto vmessConf = JSONFromString(vmessString); + // C is a quick hack... +#define C(k) vmessConf.contains(k) + //string v, ps, add, port, id, aid, net, type, host, path, tls; + bool flag = C("v") && C("ps") && C("add") && C("port") && C("id") && C("aid") && C("net") && C("type") && C("host") && C("path") && C("tls"); +#undef C + return flag ? 0 : 1; } catch (exception *e) { - LOG(MODULE_CONNECTION, QObject::tr("#VMessDecodeError").toStdString() << e->what()) + LOG(MODULE_CONNECTION, "Failed to decode vmess string: " << e->what()) return -2; } } diff --git a/src/QvCoreInteractions.cpp b/src/QvCoreInteractions.cpp index e3d17832..e7b5c27e 100644 --- a/src/QvCoreInteractions.cpp +++ b/src/QvCoreInteractions.cpp @@ -1,56 +1,58 @@ #include #include +#include #include "QvCoreInteractions.h" #include "QvCoreConfigOperations.h" +#include "QvTinyLog.h" #include "w_MainWindow.h" namespace Qv2ray { - bool Qv2Instance::VerifyVConfigFile(const QString path) + bool Qv2Instance::VerifyVConfigFile(const QString *path) { if (ValidateV2rayCoreExe()) { QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("V2RAY_LOCATION_ASSET", QString::fromStdString(GetGlobalConfig().v2AssetsPath)); QProcess process; process.setProcessEnvironment(env); - process.start(QString::fromStdString(Utils::GetGlobalConfig().v2CorePath), QStringList() << "-test" - << "-config" << path, - QIODevice::ReadWrite | QIODevice::Text); + process.start(QV2RAY_V2RAY_CORE_PATH, QStringList() << "-test" << "-config" << *path, QIODevice::ReadWrite | QIODevice::Text); if (!process.waitForFinished()) { - qDebug() << "v2ray core failed with exit code " << process.exitCode(); + LOG(MODULE_VCORE, "v2ray core failed with exitcode: " << process.exitCode()) return false; } QString output = QString(process.readAllStandardOutput()); - if (!output.contains("Configuration OK")) { - Utils::QvMessageBox(nullptr, QObject::tr("ConfigurationError"), output.mid(output.indexOf("anti-censorship.") + 17)); + if (process.exitCode() != 0) { + Utils::QvMessageBox(nullptr, QObject::tr("Configuration Error"), output.mid(output.indexOf("anti-censorship.") + 17)); return false; - } else - return true; - } else - return false; + } + + return true; + } + + return false; } Qv2Instance::Qv2Instance(QWidget *parent) { - QProcess *proc = new QProcess(); + auto proc = new QProcess(); vProcess = proc; QObject::connect(vProcess, &QProcess::readyReadStandardOutput, static_cast(parent), &MainWindow::UpdateLog); Status = STOPPED; } + QString Qv2Instance::ReadProcessOutput() { return vProcess->readAllStandardOutput(); } + bool Qv2Instance::ValidateV2rayCoreExe() { - auto path = QString::fromStdString(Utils::GetGlobalConfig().v2CorePath); - - if (!QFile::exists(path)) { - Utils::QvMessageBox(nullptr, QObject::tr("CoreNotFound"), QObject::tr("CoreFileNotFoundExplainationAt:") + path); + if (!QFile::exists(QV2RAY_V2RAY_CORE_PATH)) { + Utils::QvMessageBox(nullptr, QObject::tr("Cannot start v2ray"), QObject::tr("v2ray core file cannot be found at:") + QV2RAY_V2RAY_CORE_PATH); return false; } else return true; } @@ -64,13 +66,13 @@ namespace Qv2ray Status = STARTING; if (ValidateV2rayCoreExe()) { - if (VerifyVConfigFile(QV2RAY_GENERATED_FILE_PATH)) { + auto filePath = QV2RAY_GENERATED_FILE_PATH; + + if (VerifyVConfigFile(&filePath)) { QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); env.insert("V2RAY_LOCATION_ASSET", QString::fromStdString(GetGlobalConfig().v2AssetsPath)); vProcess->setProcessEnvironment(env); - vProcess->start(QString::fromStdString(GetGlobalConfig().v2CorePath), QStringList() << "-config" - << QV2RAY_GENERATED_FILE_PATH, - QIODevice::ReadWrite | QIODevice::Text); + vProcess->start(QV2RAY_V2RAY_CORE_PATH, QStringList() << "-config" << filePath, QIODevice::ReadWrite | QIODevice::Text); vProcess->waitForStarted(); Status = STARTED; return true; diff --git a/src/QvCoreInteractions.h b/src/QvCoreInteractions.h index 32003fc2..5e92cfbf 100644 --- a/src/QvCoreInteractions.h +++ b/src/QvCoreInteractions.h @@ -20,7 +20,7 @@ namespace Qv2ray bool Start(); void Stop(); V2RAY_INSTANCE_STARTUP_STATUS Status; - static bool VerifyVConfigFile(QString path); + static bool VerifyVConfigFile(const QString *path); static bool ValidateV2rayCoreExe(); QString ReadProcessOutput(); diff --git a/src/QvTinyLog.h b/src/QvTinyLog.h index 582881e6..809337c8 100644 --- a/src/QvTinyLog.h +++ b/src/QvTinyLog.h @@ -19,4 +19,8 @@ using namespace std; #define MODULE_FILE "FILE" #define MODULE_SUBSCRIPTION "SUBSCRIPTION" +#define WARN "WARN" +#define INFO "INFO" +#define ERROR "ERROR" + #endif // QVTINYLOG_H diff --git a/src/QvUtils.cpp b/src/QvUtils.cpp index fdac502f..a8872bc2 100644 --- a/src/QvUtils.cpp +++ b/src/QvUtils.cpp @@ -22,24 +22,24 @@ namespace Qv2ray return ConfigDirPath; } - void SetConfigDirPath(QString path) + void SetConfigDirPath(const QString *path) { - ConfigDirPath = path; + ConfigDirPath = *path; } void SaveGlobalConfig() { QFile config(QV2RAY_CONFIG_FILE_PATH); QString str = StructToJSONString(GetGlobalConfig()); - StringToFile(str, &config); + StringToFile(&str, &config); } - bool StringToFile(QString text, QFile *targetFile) + bool StringToFile(const QString *text, QFile *targetFile) { bool override = targetFile->exists(); targetFile->open(QFile::WriteOnly); QTextStream stream(targetFile); - stream << text << endl; + stream << *text << endl; stream.flush(); targetFile->close(); return override; diff --git a/src/QvUtils.h b/src/QvUtils.h index 27908e75..e10b2342 100644 --- a/src/QvUtils.h +++ b/src/QvUtils.h @@ -18,7 +18,7 @@ namespace Qv2ray bool CheckFile(QDir *dir, QString fileName); - void SetConfigDirPath(QString path); + void SetConfigDirPath(const QString *path); QString GetConfigDirPath(); void SetGlobalConfig(Qv2rayConfig conf); @@ -30,7 +30,7 @@ namespace Qv2ray void QvMessageBox(QWidget *parent, QString title, QString text); int QvMessageBoxAsk(QWidget *parent, QString title, QString text, QMessageBox::StandardButton extraButtons = QMessageBox::NoButton); // - bool StringToFile(QString text, QFile *target); + bool StringToFile(const QString *text, QFile *target); QString StringFromFile(QFile *sourceFile); // QJsonObject JSONFromString(QString string); diff --git a/src/main.cpp b/src/main.cpp index b104e1d3..0ee14cb3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,68 +7,36 @@ #include "QvRunguard.h" #include "w_MainWindow.h" -using namespace Qv2ray; -using namespace Qv2ray::Utils; -using namespace Qv2ray::QvConfigModels; - bool initQv() { -#ifdef QT_DEBUG - QString configPath = QDir::homePath() + "/.qv2ray_debug"; -#else - QString configPath = QDir::homePath() + "/.qv2ray"; -#endif - /// Qv2ray Config Path and ends with "/" - QString exeDefaultPath = configPath + "/vcore/v2ray"; - QString v2AssetsPath = configPath + "/vcore"; - // -#if defined(__WIN32) - exeDefaultPath = exeDefaultPath + ".exe"; -#elif defined(__linux__) - // Special case for GNU/Linux - // - // Unused these values - Q_UNUSED(v2AssetsPath) - Q_UNUSED(exeDefaultPath) - v2AssetsPath = "/etc/v2ray"; - exeDefaultPath = "/bin/v2ray"; -#endif - // - SetConfigDirPath(configPath); - auto ConfigDir = new QDir(configPath); - - if (!ConfigDir->exists()) { - auto result = QDir().mkdir(QV2RAY_CONFIG_DIR_PATH); - - if (result) { - LOG(MODULE_INIT, "Created Qv2ray config dir at: " + QV2RAY_CONFIG_DIR_PATH.toStdString()) - } else { - LOG(MODULE_INIT, "Failed to create config dir at: " + QV2RAY_CONFIG_DIR_PATH.toStdString()) - return false; - } + if (!QDir(QV2RAY_CONFIG_DIR_PATH).exists()) { + QDir().mkdir(QV2RAY_CONFIG_DIR_PATH); + LOG(MODULE_INIT, "Created Qv2ray config dir at: " + QV2RAY_CONFIG_DIR_PATH.toStdString()) } - auto genPath = QV2RAY_CONFIG_DIR_PATH + "generated/"; - - if (!QDir(genPath).exists()) { - auto result2 = QDir().mkdir(genPath); - - if (result2) { - LOG(MODULE_INIT, "Created config generation dir at: " + genPath.toStdString()) - } else { - LOG(MODULE_INIT, "Failed to create config generation dir at: " + genPath.toStdString()) - return false; - } + if (!QDir(QV2RAY_CONFIG_DIR_PATH + "generated/").exists()) { + QDir().mkdir(QV2RAY_CONFIG_DIR_PATH + "generated/"); + LOG(MODULE_INIT, "Created config generation dir.") } - QFile configFile(QV2RAY_CONFIG_FILE_PATH); + if (!QDir(QV2RAY_V2RAY_CORE_DIR_PATH).exists()) { + QDir().mkdir(QV2RAY_V2RAY_CORE_DIR_PATH); + LOG(MODULE_INIT, "Created dir for v2ray core and assets.") + QFile _readmeFile(QV2RAY_V2RAY_CORE_DIR_PATH + "Put your v2ray.exe here.txt"); + _readmeFile.open(QIODevice::WriteOnly); + _readmeFile.write("Please put your v2ray.exe and assets here!"); + _readmeFile.close(); + LOG(MODULE_INIT, "Done generating readme.") + } - if (!configFile.exists()) { - // This is first run! + QFile qvConfigFile(QV2RAY_CONFIG_FILE_PATH); + + if (!qvConfigFile.exists()) { + // This is first run, even the config file does not exist... // // These below genenrated very basic global config. Qv2rayBasicInboundsConfig inboundSetting = Qv2rayBasicInboundsConfig("127.0.0.1", 1080, 8000); - Qv2rayConfig conf = Qv2rayConfig("zh-CN", exeDefaultPath.toStdString(), v2AssetsPath.toStdString(), 2, inboundSetting); + Qv2rayConfig conf = Qv2rayConfig("zh-CN", QV2RAY_V2RAY_CORE_DIR_PATH.toStdString(), 4, inboundSetting); // // Save initial config. SetGlobalConfig(conf); @@ -77,12 +45,19 @@ bool initQv() LOG(MODULE_INIT, "Created initial config file.") } else { // Some config file upgrades. - auto conf = JSONFromString(StringFromFile(&configFile)); + auto conf = JSONFromString(StringFromFile(&qvConfigFile)); auto confVersion = conf["config_version"].toVariant().toString(); auto newVersion = QSTRING(to_string(QV2RAY_CONFIG_VERSION)); - if (QString::compare(confVersion, newVersion) != 0) { - conf = UpgradeConfig(stoi(conf["config_version"].toString().toStdString()), QV2RAY_CONFIG_VERSION, conf); + // Config version is larger than the current version... + if (stoi(confVersion.toStdString()) > QV2RAY_CONFIG_VERSION) { + QvMessageBox(nullptr, QObject::tr("Qv2ray Cannot Continue"), QObject::tr("You are running a lower version of Qv2ray compared to the current config file.") + + "\r\n" + + QObject::tr("Please report if you think this is an error.") + "\r\n" + + QObject::tr("Qv2ray will now exit.")); + return false; + } else if (QString::compare(confVersion, newVersion) != 0) { + conf = UpgradeConfig(stoi(confVersion.toStdString()), QV2RAY_CONFIG_VERSION, conf); } auto confObject = StructFromJSONString(JSONToString(conf)); @@ -110,7 +85,11 @@ int main(int argc, char *argv[]) // #ifdef QT_DEBUG LOG("DEBUG", "============================== This is a debug build, many features are not stable enough. ==============================") + QString configPath = QDir::homePath() + "/.qv2ray_debug"; +#else + QString configPath = QDir::homePath() + "/.qv2ray"; #endif + SetConfigDirPath(&configPath); QDirIterator it(":/translations"); if (!it.hasNext()) { @@ -155,7 +134,6 @@ int main(int argc, char *argv[]) "DEBUG_VERSION" #endif ); - auto osslReqVersion = QSslSocket::sslLibraryBuildVersionString().toStdString(); auto osslCurVersion = QSslSocket::sslLibraryVersionString().toStdString(); LOG(MODULE_NETWORK, "Current OpenSSL version: " + osslCurVersion) diff --git a/src/w_ConnectionEditWindow.cpp b/src/w_ConnectionEditWindow.cpp index 50420272..f1f98522 100644 --- a/src/w_ConnectionEditWindow.cpp +++ b/src/w_ConnectionEditWindow.cpp @@ -30,10 +30,10 @@ ConnectionEditWindow::ConnectionEditWindow(QWidget *parent) GEN_JSON } -ConnectionEditWindow::ConnectionEditWindow(QJsonObject editRootObject, QString alias, QWidget *parent) +ConnectionEditWindow::ConnectionEditWindow(QJsonObject editRootObject, const QString *alias, QWidget *parent) : ConnectionEditWindow(parent) { - _alias = alias; + _alias = *alias; originalRoot = editRootObject; auto outBoundRoot = originalRoot["outbounds"].toArray().first().toObject(); OutboundType = outBoundRoot["protocol"].toString(); @@ -156,7 +156,6 @@ void ConnectionEditWindow::on_buttonBox_accepted() } originalRoot.insert("outbounds", outbounds); - originalRoot.insert(QV2RAY_CONFIG_TYPE_JSON_KEY, QV2RAY_CONFIG_TYPE_MANUAL); SaveConnectionConfig(originalRoot, &alias); auto globalConf = GetGlobalConfig(); diff --git a/src/w_ConnectionEditWindow.h b/src/w_ConnectionEditWindow.h index 6cba411e..6893ea0d 100644 --- a/src/w_ConnectionEditWindow.h +++ b/src/w_ConnectionEditWindow.h @@ -17,7 +17,7 @@ class ConnectionEditWindow : public QDialog public: explicit ConnectionEditWindow(QWidget *parent = nullptr); - explicit ConnectionEditWindow(QJsonObject editRootObject, QString alias, QWidget *parent = nullptr); + explicit ConnectionEditWindow(QJsonObject editRootObject, const QString *alias, QWidget *parent = nullptr); ~ConnectionEditWindow(); signals: void s_reload_config(bool need_restart); diff --git a/src/w_ImportConfig.cpp b/src/w_ImportConfig.cpp index d51e2866..83d6c2e7 100644 --- a/src/w_ImportConfig.cpp +++ b/src/w_ImportConfig.cpp @@ -33,7 +33,7 @@ void ImportConfigWindow::on_importSourceCombo_currentIndexChanged(int index) void ImportConfigWindow::on_selectFileBtn_clicked() { - QString dir = QFileDialog::getOpenFileName(this, tr("OpenConfigFile"), "~/"); + QString dir = QFileDialog::getOpenFileName(this, tr("Select file to import"), QDir::currentPath()); ui->fileLineTxt->setText(dir); } @@ -45,9 +45,10 @@ void ImportConfigWindow::on_buttonBox_accepted() if (ui->importSourceCombo->currentIndex() == 0) { // From File... bool overrideInBound = !ui->keepImportedInboundCheckBox->isChecked(); + auto fileName = ui->fileLineTxt->text(); - if (!Qv2Instance::VerifyVConfigFile(ui->fileLineTxt->text())) { - QvMessageBox(this, tr("#InvalidConfigFile"), tr("ConfigFileCheckFailed")); + if (!Qv2Instance::VerifyVConfigFile(&fileName)) { + QvMessageBox(this, tr("Import config file"), tr("Failed to check the validity of the config file.")); return; } @@ -58,28 +59,33 @@ void ImportConfigWindow::on_buttonBox_accepted() QString vmess = ui->vmessConnectionStringTxt->toPlainText(); int result = VerifyVMessProtocolString(vmess); - if (result == 0) { - // This result code passes the validation check. - //QvMessageBox(this, tr("#VMessCheck"), tr("#AbleToImportConfig")); - } else if (result == -1) { - QvMessageBox(this, tr("#VMessCheck"), tr("#NotValidVMessProtocolString")); - done(0); - return; - } else { - QvMessageBox(this, tr("#VMessCheck"), tr("#INTERNAL_ERROR")); - return; + switch (result) { + case 0: + // This result code passes the validation check. + //QvMessageBox(this, tr("#VMessCheck"), tr("#AbleToImportConfig")); + break; + + case -1: + QvMessageBox(this, tr("VMess String Check"), tr("VMess string is not valid")); + done(0); + return; + + default: + QvMessageBox(this, tr("VMess String Check"), tr("Some internal error occured")); + return; } config = ConvertConfigFromVMessString(ui->vmessConnectionStringTxt->toPlainText()); // - alias = alias != "" ? alias : config["QV2RAY_ALIAS"].toString(); + alias = alias.isEmpty() ? alias : config["QV2RAY_ALIAS"].toString(); config.remove("QV2RAY_ALIAS"); } Qv2rayConfig conf = GetGlobalConfig(); + // conf.configs.push_back(alias.toStdString()); + // SetGlobalConfig(conf); auto needReload = SaveConnectionConfig(config, &alias); - LOG(MODULE_CONNECTION_VMESS, "WARNING: POSSIBLE LOSS OF DATA") emit s_reload_config(needReload); } diff --git a/src/w_MainWindow.cpp b/src/w_MainWindow.cpp index 00d75962..e61fefc3 100644 --- a/src/w_MainWindow.cpp +++ b/src/w_MainWindow.cpp @@ -15,10 +15,10 @@ #include #endif -#include "w_PrefrencesWindow.h" -#include "w_ImportConfig.h" #include "w_ConnectionEditWindow.h" +#include "w_ImportConfig.h" #include "w_MainWindow.h" +#include "w_PrefrencesWindow.h" #include "w_SubscribeEditor.h" #define TRAY_TOOLTIP_PREFIX "Qv2ray " QV2RAY_VERSION_STRING "\r\n" @@ -75,23 +75,29 @@ MainWindow::MainWindow(QWidget *parent) // if (!vinstance->ValidateV2rayCoreExe()) { - on_prefrencesBtn_clicked(); - } - - auto conf = GetGlobalConfig(); - - if (conf.autoStartConfig != "" && QList::fromStdList(conf.configs).contains(conf.autoStartConfig)) { - CurrentConnectionName = QSTRING(conf.autoStartConfig); - auto item = ui->connectionListWidget->findItems(QSTRING(conf.autoStartConfig), Qt::MatchExactly).front(); - item->setSelected(true); - ui->connectionListWidget->setCurrentItem(item); - on_connectionListWidget_itemClicked(item); - on_startButton_clicked(); - //ToggleVisibility(); - this->hide(); - trayMenu->actions()[0]->setText(tr("Show")); + QDesktopServices::openUrl(QUrl::fromLocalFile(QV2RAY_V2RAY_CORE_DIR_PATH)); } else { - this->show(); + auto conf = GetGlobalConfig(); + + if (conf.autoStartConfig != "" && QList::fromStdList(conf.configs).contains(conf.autoStartConfig)) { + CurrentConnectionName = QSTRING(conf.autoStartConfig); + auto item = ui->connectionListWidget->findItems(QSTRING(conf.autoStartConfig), Qt::MatchExactly).front(); + item->setSelected(true); + ui->connectionListWidget->setCurrentItem(item); + on_connectionListWidget_itemClicked(item); + on_startButton_clicked(); + //ToggleVisibility(); + this->hide(); + trayMenu->actions()[0]->setText(tr("Show")); + } else { + this->show(); + + if (ui->connectionListWidget->count() != 0) { + // The first one is default. + ui->connectionListWidget->setCurrentRow(0); + ShowAndSetConnection(ui->connectionListWidget->item(0)->text(), true, false); + } + } } } @@ -196,8 +202,8 @@ void MainWindow::on_startButton_clicked() bool startFlag = this->vinstance->Start(); if (startFlag) { - this->hTray->showMessage("Qv2ray", tr("Connected To Server: ") + " " + CurrentConnectionName); - hTray->setToolTip(TRAY_TOOLTIP_PREFIX + tr("Connected To Server: ") + ": " + CurrentConnectionName); + this->hTray->showMessage("Qv2ray", tr("Connected To Server: ") + CurrentConnectionName); + hTray->setToolTip(TRAY_TOOLTIP_PREFIX + tr("Connected To Server: ") + CurrentConnectionName); ui->statusLabel->setText(tr("Connected") + ": " + CurrentConnectionName); } @@ -305,11 +311,8 @@ void MainWindow::QTextScrollToBottom() if (bar->value() >= bar->maximum() - 10) bar->setValue(bar->maximum()); } -void MainWindow::ShowAndSetConnection(int index, bool SetConnection, bool ApplyConnection) +void MainWindow::ShowAndSetConnection(QString guiConnectionName, bool SetConnection, bool ApplyConnection) { - if (index < 0) return; - - auto guiConnectionName = ui->connectionListWidget->item(index)->text(); // --------- BRGIN Show Connection auto outBoundRoot = (connections[guiConnectionName])["outbounds"].toArray().first().toObject(); // @@ -362,7 +365,12 @@ void MainWindow::on_connectionListWidget_itemClicked(QListWidgetItem *item) { Q_UNUSED(item) int currentRow = ui->connectionListWidget->currentRow(); - ShowAndSetConnection(currentRow, !isRenamingInProgress && (vinstance->Status != STARTED), false); + + if (currentRow < 0) return; + + QString currentText = ui->connectionListWidget->currentItem()->text(); + bool canSetConnection = !isRenamingInProgress && vinstance->Status != STARTED ; + ShowAndSetConnection(currentText, canSetConnection, false); } void MainWindow::on_prefrencesBtn_clicked() @@ -374,7 +382,13 @@ void MainWindow::on_prefrencesBtn_clicked() void MainWindow::on_connectionListWidget_doubleClicked(const QModelIndex &index) { - ShowAndSetConnection(index.row(), true, true); + Q_UNUSED(index) + int currentRow = ui->connectionListWidget->currentRow(); + + if (currentRow < 0) return; + + QString currentText = ui->connectionListWidget->currentItem()->text(); + ShowAndSetConnection(currentText, true, true); } void MainWindow::on_clearlogButton_clicked() @@ -385,7 +399,7 @@ void MainWindow::on_clearlogButton_clicked() void MainWindow::on_connectionListWidget_currentItemChanged(QListWidgetItem *current, QListWidgetItem *previous) { Q_UNUSED(previous) - isRenamingInProgress = true; + isRenamingInProgress = false; on_connectionListWidget_itemClicked(current); } @@ -401,14 +415,14 @@ void MainWindow::on_action_RenameConnection_triggered() item->setFlags(item->flags() | Qt::ItemIsEditable); ui->connectionListWidget->editItem(item); originalName = item->text(); - isRenamingInProgress = false; + isRenamingInProgress = true; } void MainWindow::on_connectionListWidget_itemChanged(QListWidgetItem *item) { LOG(MODULE_UI, "A connection ListViewItem is changed.") - if (!isRenamingInProgress) { + if (isRenamingInProgress) { // In this case it's after we entered the name. LOG(MODULE_CONNECTION, "RENAME: " + originalName.toStdString() + " -> " + item->text().toStdString()) auto newName = item->text(); @@ -416,16 +430,16 @@ void MainWindow::on_connectionListWidget_itemChanged(QListWidgetItem *item) auto configList = QList::fromStdList(config.configs); if (newName.trimmed().isEmpty()) { - QvMessageBox(this, tr("Rename A Connection"), tr("A name cannot be empty")); + QvMessageBox(this, tr("Rename a Connection"), tr("The name cannot be empty")); return; } - // If I really did some changes. LOG("RENAME", "ORIGINAL: " + originalName.toStdString() + ", NEW: " + newName.toStdString()) + // If I really did some changes. if (originalName != newName) { if (configList.contains(newName.toStdString())) { - QvMessageBox(this, tr("Rename A Connection"), tr("The name has been used already, Please choose another.")); + QvMessageBox(this, tr("Rename a Connection"), tr("The name has been used already, Please choose another.")); return; } @@ -452,7 +466,7 @@ void MainWindow::on_connectionListWidget_itemChanged(QListWidgetItem *item) void MainWindow::on_removeConfigButton_clicked() { - if (QvMessageBoxAsk(this, tr("Removing A Connection"), tr("Are you sure to remove this connection?")) == QMessageBox::Yes) { + if (QvMessageBoxAsk(this, tr("Removing this Connection"), tr("Are you sure to remove this connection?")) == QMessageBox::Yes) { auto conf = GetGlobalConfig(); QList list = QList::fromStdList(conf.configs); auto currentSelected = ui->connectionListWidget->currentIndex().row(); @@ -489,16 +503,14 @@ void MainWindow::on_addConfigButton_clicked() void MainWindow::on_editConfigButton_clicked() { // Check if we have a connection selected... - auto index = ui->connectionListWidget->currentIndex().row(); - - if (index < 0) { + if (ui->connectionListWidget->currentIndex().row() < 0) { QvMessageBox(this, tr("NoConfigSelected"), tr("PleaseSelectAConfig")); return; } auto alias = ui->connectionListWidget->currentItem()->text(); auto outBoundRoot = connections[alias]; - ConnectionEditWindow *w = new ConnectionEditWindow(outBoundRoot, alias, this); + ConnectionEditWindow *w = new ConnectionEditWindow(outBoundRoot, &alias, this); connect(w, &ConnectionEditWindow::s_reload_config, this, &MainWindow::save_reload_globalconfig); w->show(); } @@ -508,3 +520,8 @@ void MainWindow::on_pushButton_clicked() SubscribeEditor *w = new SubscribeEditor(this); w->show(); } + +void MainWindow::on_reconnectButton_clicked() +{ + on_restartButton_clicked(); +} diff --git a/src/w_MainWindow.h b/src/w_MainWindow.h index da43b09f..74db2da3 100644 --- a/src/w_MainWindow.h +++ b/src/w_MainWindow.h @@ -59,6 +59,8 @@ class MainWindow : public QMainWindow void on_pushButton_clicked(); + void on_reconnectButton_clicked(); + private: void on_action_StartThis_triggered(); void on_action_RenameConnection_triggered(); @@ -75,7 +77,7 @@ class MainWindow : public QMainWindow QString originalName; bool isRenamingInProgress; // - void ShowAndSetConnection(int index, bool SetConnection, bool Apply); + void ShowAndSetConnection(QString currentText, bool SetConnection, bool Apply); void LoadConnections(); void closeEvent(QCloseEvent *); }; diff --git a/src/w_MainWindow.ui b/src/w_MainWindow.ui index 1b45f51e..1cac39d9 100644 --- a/src/w_MainWindow.ui +++ b/src/w_MainWindow.ui @@ -37,7 +37,7 @@ - + 5 @@ -58,6 +58,13 @@ + + + + Reconnect + + + diff --git a/src/w_PrefrencesWindow.cpp b/src/w_PrefrencesWindow.cpp index 84923364..5d4ad3fb 100644 --- a/src/w_PrefrencesWindow.cpp +++ b/src/w_PrefrencesWindow.cpp @@ -5,10 +5,6 @@ #include -#ifdef __linux -#include -#endif - #define NEEDRESTART if(finishedLoading) IsConnectionPropertyChanged = true; PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent), @@ -30,7 +26,7 @@ PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent), // ui->languageComboBox->setCurrentText(QSTRING(CurrentConfig.language)); ui->logLevelComboBox->setCurrentIndex(CurrentConfig.logLevel); - ui->runAsRootCheckBox->setChecked(CurrentConfig.runAsRoot); + ui->tProxyCheckBox->setChecked(CurrentConfig.runAsRoot); // // ui->listenIPTxt->setText(QSTRING(CurrentConfig.inBoundSettings.listenip)); @@ -63,7 +59,6 @@ PrefrencesWindow::PrefrencesWindow(QWidget *parent) : QDialog(parent), ui->socksPortLE->setValidator(new QIntValidator()); // // - ui->vCoreExePathTxt->setText(QSTRING(CurrentConfig.v2CorePath)); ui->vCoreAssetsPathTxt->setText(QSTRING(CurrentConfig.v2AssetsPath)); // // @@ -105,7 +100,7 @@ void PrefrencesWindow::on_buttonBox_accepted() int hp = ui->httpPortLE->text().toInt() ; if (!(sp == 0 || hp == 0) && sp == hp) { - QvMessageBox(this, tr("Prefrences"), tr("PortNumbersCannotBeSame")); + QvMessageBox(this, tr("Prefrences"), tr("Port numbers cannot be the same")); return; } @@ -149,38 +144,6 @@ void PrefrencesWindow::on_httpAuthCB_stateChanged(int checked) CurrentConfig.inBoundSettings.http_useAuth = checked == Qt::Checked; } -void PrefrencesWindow::on_runAsRootCheckBox_stateChanged(int arg1) -{ -#ifdef __linux - // Set UID and GID for linux - QString vCorePath = QString::fromStdString(CurrentConfig.v2CorePath); - QFileInfo v2rayCoreExeFile(vCorePath); - - if (arg1 == Qt::Checked && v2rayCoreExeFile.ownerId() != 0) { - QProcess::execute("pkexec", QStringList() << "bash" - << "-c" - << "chown root:root " + vCorePath + " && " - << "chmod +s " + vCorePath); - CurrentConfig.runAsRoot = true; - NEEDRESTART - } else if (arg1 != Qt::Checked && v2rayCoreExeFile.ownerId() == 0) { - uid_t uid = getuid(); - gid_t gid = getgid(); - QProcess::execute("pkexec", QStringList() - << "chown" << QString::number(uid) + ":" + QString::number(gid) - << vCorePath); - CurrentConfig.runAsRoot = false; - NEEDRESTART - } - -#else - Q_UNUSED(arg1) - ui->runAsRootCheckBox->setChecked(false); - // No such uid gid thing on Windows and MacOS is in TODO .... - QvMessageBox(this, tr("Prefrences"), tr("RunAsRootNotOnWindows")); -#endif -} - void PrefrencesWindow::on_socksAuthCB_stateChanged(int checked) { NEEDRESTART @@ -193,7 +156,7 @@ void PrefrencesWindow::on_languageComboBox_currentTextChanged(const QString &arg { CurrentConfig.language = arg1.toStdString(); // - // A strange bug prevents us to change the UI language `live`ly + // A strange bug prevents us to change the UI language online // https://github.com/lhy0403/Qv2ray/issues/34 // //if (QApplication::installTranslator(getTranslator(&arg1))) { @@ -210,12 +173,6 @@ void PrefrencesWindow::on_logLevelComboBox_currentIndexChanged(int index) CurrentConfig.logLevel = index; } -void PrefrencesWindow::on_vCoreExePathTxt_textEdited(const QString &arg1) -{ - NEEDRESTART - CurrentConfig.v2CorePath = arg1.toStdString(); -} - void PrefrencesWindow::on_vCoreAssetsPathTxt_textEdited(const QString &arg1) { NEEDRESTART @@ -294,21 +251,10 @@ void PrefrencesWindow::on_localDNSCb_stateChanged(int arg1) CurrentConfig.withLocalDNS = arg1 == Qt::Checked; } -void PrefrencesWindow::on_selectVCoreBtn_clicked() -{ - NEEDRESTART - QString path = QFileDialog::getOpenFileName(this, tr("#OpenVCoreFile"), QDir::homePath()); - ui->vCoreExePathTxt->setText(path); - on_vCoreExePathTxt_textEdited(path); - auto dir = QFileInfo(path).dir().path(); - ui->vCoreAssetsPathTxt->setText(dir); - on_vCoreAssetsPathTxt_textEdited(dir); -} - void PrefrencesWindow::on_selectVAssetBtn_clicked() { NEEDRESTART - QString dir = QFileDialog::getExistingDirectory(this, tr("OpenVAssetsDir"), QDir::homePath()); + QString dir = QFileDialog::getExistingDirectory(this, tr("Open v2ray assets folder"), QDir::currentPath()); ui->vCoreAssetsPathTxt->setText(dir); on_vCoreAssetsPathTxt_textEdited(dir); } @@ -350,3 +296,55 @@ void PrefrencesWindow::on_cancelIgnoreVersionBtn_clicked() CurrentConfig.ignoredVersion.clear(); ui->cancelIgnoreVersionBtn->setEnabled(false); } + +void PrefrencesWindow::on_tProxyCheckBox_stateChanged(int arg1) +{ +#ifdef __linux + + if (finishedLoading) { + //LOG(MODULE_UI, "Running getcap....") + //QProcess::execute("getcap " + QV2RAY_V2RAY_CORE_PATH); + + // Set UID and GID for linux + // Steps: + // --> 1. Copy v2ray core files to the #CONFIG_DIR#/vcore/ dir. + // --> 2. Change GlobalConfig.v2CorePath. + // --> 3. Call `pkexec setcap CAP_NET_ADMIN,CAP_NET_RAW,CAP_NET_BIND_SERVICE=eip` on the v2ray core. + if (arg1 == Qt::Checked) { + // We enable it! + if (QvMessageBoxAsk(this, tr("Enable tProxy Support"), tr("This will append capabilities to the v2ray executable.") + "\r\n" + + tr("If anything goes wrong after enabling this, please refer to issue #57 or the link below:") + "\r\n" + + " https://github.com/lhy0403/Qv2ray/blob/master/docs/FAQ.md ") != QMessageBox::Yes) { + ui->tProxyCheckBox->setChecked(false); + LOG(MODULE_UI, "Canceled enabling tProxy feature.") + } + + int ret = QProcess::execute("pkexec setcap CAP_NET_ADMIN,CAP_NET_RAW,CAP_NET_BIND_SERVICE=eip " + QV2RAY_V2RAY_CORE_PATH); + + if (ret != 0) { + LOG(MODULE_UI, "WARN: setcap exits with code: " + to_string(ret)) + QvMessageBox(this, tr("Prefrences"), tr("Failed to setcap onto v2ray executable. You may need to run `setcap` manually.")); + } + + CurrentConfig.runAsRoot = true; + NEEDRESTART + } else { + int ret = QProcess::execute("pkexec setcap -r " + QV2RAY_V2RAY_CORE_PATH); + + if (ret != 0) { + LOG(MODULE_UI, "WARN: setcap exits with code: " + to_string(ret)) + QvMessageBox(this, tr("Prefrences"), tr("Failed to setcap onto v2ray executable. You may need to run `setcap` manually.")); + } + + CurrentConfig.runAsRoot = false; + NEEDRESTART + } + } + +#else + Q_UNUSED(arg1) + ui->tProxyCheckBox->setChecked(false); + // No such uid gid thing on Windows and MacOS + QvMessageBox(this, tr("Prefrences"), tr("tProxy is not supported on MacOS and Windows")); +#endif +} diff --git a/src/w_PrefrencesWindow.h b/src/w_PrefrencesWindow.h index a3a5f4f4..93c53f63 100644 --- a/src/w_PrefrencesWindow.h +++ b/src/w_PrefrencesWindow.h @@ -19,23 +19,22 @@ class PrefrencesWindow : public QDialog ~PrefrencesWindow(); signals: void s_reload_config(bool need_restart); + private slots: void on_buttonBox_accepted(); + void on_httpCB_stateChanged(int arg1); + void on_socksCB_stateChanged(int arg1); void on_httpAuthCB_stateChanged(int arg1); - void on_runAsRootCheckBox_stateChanged(int arg1); - void on_socksAuthCB_stateChanged(int arg1); void on_languageComboBox_currentTextChanged(const QString &arg1); void on_logLevelComboBox_currentIndexChanged(int index); - void on_vCoreExePathTxt_textEdited(const QString &arg1); - void on_vCoreAssetsPathTxt_textEdited(const QString &arg1); void on_muxEnabledCB_stateChanged(int arg1); @@ -62,8 +61,6 @@ class PrefrencesWindow : public QDialog void on_localDNSCb_stateChanged(int arg1); - void on_selectVCoreBtn_clicked(); - void on_selectVAssetBtn_clicked(); void on_DNSListTxt_textChanged(); @@ -74,6 +71,8 @@ class PrefrencesWindow : public QDialog void on_cancelIgnoreVersionBtn_clicked(); + void on_tProxyCheckBox_stateChanged(int arg1); + private: bool IsConnectionPropertyChanged = false; bool finishedLoading = false; diff --git a/src/w_PrefrencesWindow.ui b/src/w_PrefrencesWindow.ui index 0c7d3310..20843932 100644 --- a/src/w_PrefrencesWindow.ui +++ b/src/w_PrefrencesWindow.ui @@ -64,27 +64,13 @@ - - - Run As Root - - - - - - - Enabled - - - - Log Level - + @@ -125,14 +111,14 @@ - + Automatically Connect To - + @@ -141,35 +127,28 @@ - - + + - v2ray Core Path + Transparent Proxy Support - - - - - - - - - #Select - - - - + + + + Enabled + + - + v2ray Assets Path - + diff --git a/src/w_SubscribeEditor.cpp b/src/w_SubscribeEditor.cpp index 36a47cc6..f5cdbb5c 100644 --- a/src/w_SubscribeEditor.cpp +++ b/src/w_SubscribeEditor.cpp @@ -69,7 +69,7 @@ void SubscribeEditor::ProcessSubscriptionEntry(QByteArray result, QString subsci auto vmessList = content.split("\n"); for (auto vmess : vmessList) { - auto config = ConvertConfigFromVMessString(vmess, QV2RAY_CONFIG_TYPE_SUBSCRIPTION); + auto config = ConvertConfigFromVMessString(vmess); if (subscriptions.contains(subsciptionName)) { } diff --git a/translations/en-US.ts b/translations/en-US.ts index 799a234a..543e1ede 100644 --- a/translations/en-US.ts +++ b/translations/en-US.ts @@ -211,14 +211,14 @@ - - + + #JsonPrettify - - + + #JsonContainsError @@ -293,33 +293,33 @@ - OpenConfigFile + Select file to import - - #InvalidConfigFile + + Import config file - - ConfigFileCheckFailed - - - - - - #VMessCheck - - - - - #NotValidVMessProtocolString + + Failed to check the validity of the config file. - #INTERNAL_ERROR + + VMess String Check + + + + + VMess string is not valid + + + + + Some internal error occured @@ -343,129 +343,129 @@ - + Clear Log - + Prefrences - + Stopped - + Host List - + Config Details - + Type - + Host - + Port - + Detail - + #AddConnection - + A - - + + #ImportConnection - + I - + #RemoveConnection - + R - + #EditConnection - + ... - + Log - + #ManuallyCreateConnection - + #Exit - + #Preferences - + #Start - + #Stop - + #Restart - + Hide @@ -475,6 +475,7 @@ + Reconnect @@ -485,19 +486,35 @@ - - - + + + Show - + + + Rename a Connection + + + + + The name cannot be empty + + + + + Removing this Connection + + + + NoConfigSelected - + PleaseSelectAConfig @@ -507,99 +524,83 @@ - + Update - + Found a new version: - + Download Link: - + No connection selected! - + Please select a config from the list. - - + + Connected To Server: - + Connected - + Disconnected - + UUID - + AlterID - + Transport - + Email - + Encryption - + Username - - - Rename A Connection - - - - - A name cannot be empty - - - - + The name has been used already, Please choose another. - - Removing A Connection - - - - + Are you sure to remove this connection? @@ -608,8 +609,10 @@ PrefrencesWindow - - + + + + Prefrences @@ -635,271 +638,296 @@ - Run As Root - - - - Log Level - + none - + debug - + info - + warning - + error - + Automatically Connect To - - - v2ray Core Path - - - #Select - + v2ray Assets Path - + Mux Settings - - - - - - - + + + + + + + + - - + Enabled - + + Transparent Proxy Support + + + + Concurrency - + InBound Settings - + Listen IP - + SOCKS InBound Settings - - + + Port - - + + Authentication - - + + Username - - + + Password - + HTTP InBound Settings - + Route Settings - + Enable Proxy - + Chinese Addresses - + Use Local DNS - + DNS List - + About - + Qv2ray - + Version: - + Official Repo: - + <html><head/><body><p><a href="https://github.com/lhy0403/Qv2ray"><span style=" text-decoration: underline; color:#2980b9;">https://github.com/lhy0403/Qv2ray</span></a></p></body></html> - + License: - + <html><head/><body><p><a href="https://www.gnu.org/licenses/gpl-3.0.txt"><span style=" text-decoration: underline; color:#2980b9;">GPLv3 (https://www.gnu.org/licenses/gpl-3.0.txt)</span></a></p></body></html> - + About Qt - + Ingore Next Version - + Cancel - - PortNumbersCannotBeSame + + Port numbers cannot be the same - - RunAsRootNotOnWindows + + Open v2ray assets folder - - #OpenVCoreFile + + Enable tProxy Support - - OpenVAssetsDir + + This will append capabilities to the v2ray executable. + + + + + If anything goes wrong after enabling this, please refer to issue #57 or the link below: + + + + + + Failed to setcap onto v2ray executable. You may need to run `setcap` manually. + + + + + tProxy is not supported on MacOS and Windows QObject - - #VMessDecodeError + + Qv2ray Cannot Continue - - ConfigurationError + + You are running a lower version of Qv2ray compared to the current config file. - - CoreNotFound + + Please report if you think this is an error. - - CoreFileNotFoundExplainationAt: + + Qv2ray will now exit. - + DependencyMissing - + Cannot find openssl libs - + This could be caused by a missing of `openssl` package in your system. Or an AppImage issue. - + If you are using AppImage, please report a bug. - + Another instance of Qv2ray is already running. + + + Configuration Error + + + + + Cannot start v2ray + + + + + v2ray core file cannot be found at: + + RouteEditor diff --git a/translations/ru-RU.ts b/translations/ru-RU.ts index 518322f3..2e1871f5 100644 --- a/translations/ru-RU.ts +++ b/translations/ru-RU.ts @@ -6,59 +6,59 @@ #EditConnectionSettings - #EditConnectionSettings + #РедактироватьНастройкиПодключения Host - Host + Сервер Port - Port + Порт Outbound Type - Outbound Type + Тип сервера Socks - Socks + Socks Use TLS - Use TLS + Использовать TLS UUID - UUID + UUID Alter ID - Alter ID + ID Alter Security - Security + Защита Transport Method - Transport Method + Метод отправки Outbound Settings - Outbound Settings + Настройки соединения @@ -66,161 +66,161 @@ Enabled - Enabled + Включено Transport Settings - Transport Settings + Transport Settings Type - Type + Тип Request - Request + Запрос Prettify - Prettify + Форматировать Response - Response + Ответ Default - Default + По умолчанию Path - Path + Путь Headers - Headers + Заголовок Format: key|value - Format: key|value + Формат: key|value MTU - MTU + MTU TTI (ms) - TTI (ms) + TTI (мс) Uplink (MB/s) - Uplink (MB/s) + Отдача (Мб/с) Congestion Control - Congestion Control + Управление загрузкой Downlink (MB/s) - Downlink (MB/s) + Загрузка (Мб/с) Read Buffer (MB) - Read Buffer (MB) + Чтение буфера (МБ) Write Buffer (MB) - Write Buffer (MB) + Запись буфера (МБ) Key - Key + Ключ Mark - Mark + Отметка TCP Fast Open - TCP Fast Open + TCP Fast Open tProxy - tProxy + tProxy EMail - EMail + Email Password - Password + Пароль Encryption - Encryption + Шифрование User Level - User Level + Тип пользователя OTA - OTA + OTA Username - Username + Имя пользователя Json Preview - Json Preview + Просмотр Json #JsonPrettify - #JsonPrettify + #JsonPrettify #JsonContainsError - #JsonContainsError + #JsonContainsError @@ -228,63 +228,63 @@ Import file - Import file + Импорт файла Name - Name + Имя Import Source - Import Source + Источник импорта File - File + Файл VMess String - VMess String + Строка VMess Subscription Link - Subscription Link + Подписка #Import - #Import + Импорт Path - Path + Путь Select - Select + Выбрать Inbound - Inbound + Входящее Keep imported inbound settings - Keep imported inbound settings + Сохранить импортированные параметры VMess Connection String - VMess Connection String + Строка подключения VMess @@ -294,7 +294,7 @@ OpenConfigFile - OpenConfigFile + ОткрытьФайлКонфигурации @@ -328,64 +328,64 @@ Qv2ray - Qv2ray + Qv2ray Connect - Connect + Подключение Disconnect - Disconnect + Отключен Clear Log - Clear Log + Очистить логи Prefrences - Prefrences + Настройки Stopped - Stopped + Остановлен Host List - Host List + Список Узлов Config Details - Config Details + Детали конфигурации Type - Type + Тип Host - Host + Хост Port - Port + Порт Detail - Detail + Сведения @@ -395,7 +395,7 @@ A - A + А @@ -406,7 +406,7 @@ I - I + I @@ -416,7 +416,7 @@ R - R + R @@ -426,12 +426,12 @@ ... - ... + ... Log - Log + Лог @@ -441,55 +441,55 @@ #Exit - #Exit + #Выход #Preferences - #Preferences + #Параметры #Start - #Start + #Запустить #Stop - #Stop + #Стоп #Restart - #Restart + #Перезапуск Hide - Hide + Скрыть Quit - Quit + Выход Reconnect - Reconnect + Переподключить Rename - Rename + Переименование Show - Show + Показать @@ -504,104 +504,104 @@ Connect to this - Connect to this + Подключиться к этому Update - Update + Обновить Found a new version: - Found a new version: + Найдена новая версия: Download Link: - Download Link: + Ссылка для скачивания: No connection selected! - No connection selected! + Не выбрано соединение! Please select a config from the list. - Please select a config from the list. + Пожалуйста, выберите конфигурацию из списка Connected To Server: - Connected To Server: + Соединяется с Сервером: Connected - Connected + Подключен Disconnected - Disconnected + Отключен UUID - UUID + UUID AlterID - AlterID + AlterID Transport - Transport + Протокол Email - Email + Email Encryption - Encryption + Шифрование Username - Username + Имя пользователя Rename A Connection - Rename A Connection + Переименовать подключение A name cannot be empty - A name cannot be empty + Имя не может быть пустым The name has been used already, Please choose another. - The name has been used already, Please choose another. + Имя уже используется, пожалуйста, выберите другое. Removing A Connection - Removing A Connection + Удалить из списка Are you sure to remove this connection? - Are you sure to remove this connection? + Вы уверены, что хотите удалить это подключение? @@ -611,88 +611,88 @@ Prefrences - Prefrences + Настройки General - General + Общие Language - Language + Язык zh-CN - zh-CN + zh-CN en-US - en-US + en-US Run As Root - Run As Root + Запустить под root Log Level - Log Level + Детализация журнала none - none + Отсутствует debug - debug + отладка info - info + Информация warning - warning + внимание error - error + ошибка Automatically Connect To - Automatically Connect To + Автоматически подключаться к v2ray Core Path - v2ray Core Path + путь к ядру v2ray #Select - #Select + #Выбрать v2ray Assets Path - v2ray Assets Path + путь к директории v2ray Mux Settings - Mux Settings + Настройки Mux @@ -706,101 +706,101 @@ Enabled - Enabled + Включено Concurrency - Concurrency + Согласие InBound Settings - InBound Settings + Настройки соединения Listen IP - Listen IP + Слушать IP SOCKS InBound Settings - SOCKS InBound Settings + Настройки SOCKS InBound Port - Port + Порт Authentication - Authentication + Аутентификация Username - Username + Имя пользователя Password - Password + Пароль HTTP InBound Settings - HTTP InBound Settings + Параметры HTTP InBound Route Settings - Route Settings + Настройки маршрута Enable Proxy - Enable Proxy + Включить прокси Chinese Addresses - Chinese Addresses + Китайские адреса Use Local DNS - Use Local DNS + Использовать локальный DNS DNS List - DNS List + Список DNS About - About + О программе Qv2ray - Qv2ray + Qv2ray Version: - Version: + Версия: Official Repo: - Official Repo: + Официальный репозиторий: @@ -810,7 +810,7 @@ License: - License: + Лицензия: @@ -820,17 +820,17 @@ About Qt - About Qt + Об инструментарии Qt Ingore Next Version - Ingore Next Version + Игнорировать следующую версию Cancel - Cancel + Отменить @@ -863,7 +863,7 @@ ConfigurationError - ConfigurationError + Ошибка конфигурации @@ -883,22 +883,22 @@ Cannot find openssl libs - Cannot find openssl libs + Не удается найти openssl libs This could be caused by a missing of `openssl` package in your system. Or an AppImage issue. - This could be caused by a missing of `openssl` package in your system. Or an AppImage issue. + Это может быть вызвано отсутствием пакета `openssl` в вашей системе. Или проблемой AppImage. If you are using AppImage, please report a bug. - If you are using AppImage, please report a bug. + Если вы используете AppImage, пожалуйста, сообщите об ошибке. Another instance of Qv2ray is already running. - Another instance of Qv2ray is already running. + Другой экземпляр Qv2ray уже запущен. @@ -906,7 +906,7 @@ Dialog - Dialog + Диалог @@ -921,27 +921,27 @@ #Tag - #Tag + #Тэг #Type - #Type + #Тип #Address - #Address + #Адрес #Port - #Port + #Порт #EditCurrentOutbound - #EditCurrentOutbound + #Редактировать исходящие @@ -949,12 +949,12 @@ #UpdateInProcess - #UpdateInProcess + #UpdateInProcess #TryLater - #TryLater + #TryLater @@ -962,77 +962,77 @@ SubscribeEditor - SubscribeEditor + Редактор подписки #AddConnection - #AddConnection + #Добавить соединение A - A + A #RemoveConnection - #RemoveConnection + #Удаленное соединение R - R + R ... - ... + ... Name - Name + Имя URL - URL + URL Config List - Config List + Список конфигурации Update Subscription - Update Subscription + Обновить подписку Config Detail - Config Detail + Детали конфигурации Type - Type + Тип Server - Server + Сервер Config - Config + Настройки Port - Port + Порт