diff --git a/db/ConfigBuilder.cpp b/db/ConfigBuilder.cpp index b3bd948..f284aad 100644 --- a/db/ConfigBuilder.cpp +++ b/db/ConfigBuilder.cpp @@ -449,8 +449,28 @@ namespace NekoRay { } // chain rules: this - auto mapping_port = MkPort(); + auto ext_mapping_port = 0; + auto ext_socks_port = 0; auto thisExternalStat = ent->bean->NeedExternal(isFirstProfile, dataStore->running_spmode == SystemProxyMode::VPN); + // determine port + if (thisExternalStat > 0) { + if (ent->type == "custom") { + auto bean = ent->CustomBean(); + if (InRange(bean->mapping_port, 0, 65535)) { + ext_mapping_port = bean->mapping_port; + } else { + ext_mapping_port = MkPort(); + } + if (InRange(bean->socks_port, 0, 65535)) { + ext_socks_port = bean->socks_port; + } else { + ext_socks_port = MkPort(); + } + } else { + ext_mapping_port = MkPort(); + ext_socks_port = MkPort(); + } + } if (thisExternalStat == 2) dataStore->need_keep_vpn_off = true; if (thisExternalStat == 1) { // mapping @@ -459,7 +479,7 @@ namespace NekoRay { {"type", "direct"}, {"tag", tagOut + "-mapping"}, {"listen", "127.0.0.1"}, - {"listen_port", mapping_port}, + {"listen_port", ext_mapping_port}, {"override_address", ent->bean->serverAddress}, {"override_port", ent->bean->serverPort}, }; @@ -468,7 +488,7 @@ namespace NekoRay { {"protocol", "dokodemo-door"}, {"tag", tagOut + "-mapping"}, {"listen", "127.0.0.1"}, - {"port", mapping_port}, + {"port", ext_mapping_port}, {"settings", QJsonObject{ // to {"address", ent->bean->serverAddress}, @@ -497,12 +517,9 @@ namespace NekoRay { // Outbound QJsonObject outbound; - fmt::CoreObjOutboundBuildResult coreR; - fmt::ExternalBuildResult extR; if (thisExternalStat > 0) { - auto ext_socks_port = MkPort(); - extR = ent->bean->BuildExternal(mapping_port, ext_socks_port, thisExternalStat); + const auto extR = ent->bean->BuildExternal(ext_mapping_port, ext_socks_port, thisExternalStat); if (extR.program.isEmpty()) { status->result->error = QObject::tr("Core not found: %1").arg(ent->bean->DisplayType()); return {}; @@ -530,14 +547,14 @@ namespace NekoRay { } // EXTERNAL PROCESS - auto extC = new sys::ExternalProcess(); + QSharedPointer extC(new sys::ExternalProcess()); extC->tag = ent->bean->DisplayType(); extC->program = extR.program; extC->arguments = extR.arguments; extC->env = extR.env; - status->result->ext += extC; + status->result->exts.emplace_back(extR, extC); } else { - coreR = IS_NEKO_BOX ? ent->bean->BuildCoreObjSingBox() : ent->bean->BuildCoreObjV2Ray(); + const auto coreR = IS_NEKO_BOX ? ent->bean->BuildCoreObjSingBox() : ent->bean->BuildCoreObjV2Ray(); if (coreR.outbound.isEmpty()) { status->result->error = "unsupported outbound"; return {}; diff --git a/db/ConfigBuilder.hpp b/db/ConfigBuilder.hpp index e80b2b5..7ca4925 100644 --- a/db/ConfigBuilder.hpp +++ b/db/ConfigBuilder.hpp @@ -14,7 +14,7 @@ namespace NekoRay { QSharedPointer outboundStat; // main QStringList ignoreConnTag; - QList ext; + std::list>> exts; // extR to extC }; class BuildConfigStatus { diff --git a/fmt/Bean2External.cpp b/fmt/Bean2External.cpp index a8b45e6..0cf183c 100644 --- a/fmt/Bean2External.cpp +++ b/fmt/Bean2External.cpp @@ -20,7 +20,7 @@ auto TempFile = QFileInfo(f).absoluteFilePath(); namespace NekoRay::fmt { - // 0: no external + // 0: Internal // 1: Mapping External // 2: Direct External @@ -85,13 +85,10 @@ namespace NekoRay::fmt { for (int i = 0; i < result.arguments.length(); i++) { auto arg = result.arguments[i]; - if (arg.contains("%mapping_port%")) { - arg = arg.replace("%mapping_port%", Int2String(mapping_port)); - } else if (arg.contains("%socks_port%")) { - arg = arg.replace("%socks_port%", Int2String(socks_port)); - } else { - continue; - } + arg = arg.replace("%mapping_port%", Int2String(mapping_port)); + arg = arg.replace("%socks_port%", Int2String(socks_port)); + arg = arg.replace("%server_addr%", serverAddress); + arg = arg.replace("%server_port%", Int2String(serverPort)); result.arguments[i] = arg; } diff --git a/fmt/CustomBean.hpp b/fmt/CustomBean.hpp index a6d8409..7c0a610 100644 --- a/fmt/CustomBean.hpp +++ b/fmt/CustomBean.hpp @@ -9,12 +9,16 @@ namespace NekoRay::fmt { QList command; QString config_suffix; QString config_simple; + int mapping_port = 0; + int socks_port = 0; CustomBean() : AbstractBean(0) { _add(new configItem("core", &core, itemType::string)); _add(new configItem("cmd", &command, itemType::stringList)); _add(new configItem("cs", &config_simple, itemType::string)); _add(new configItem("cs_suffix", &config_suffix, itemType::string)); + _add(new configItem("mapping_port", &mapping_port, itemType::integer)); + _add(new configItem("socks_port", &socks_port, itemType::integer)); }; QString DisplayType() override { diff --git a/sys/ExternalProcess.cpp b/sys/ExternalProcess.cpp index 3a0e4d6..da6fc33 100644 --- a/sys/ExternalProcess.cpp +++ b/sys/ExternalProcess.cpp @@ -5,12 +5,17 @@ #include namespace NekoRay::sys { - ExternalProcess::ExternalProcess() : QProcess() {} + ExternalProcess::ExternalProcess() : QProcess() { + // qDebug() << "[Debug] ExternalProcess()" << this << running_ext; + } + + ExternalProcess::~ExternalProcess() { + // qDebug() << "[Debug] ~ExternalProcess()" << this << running_ext; + } void ExternalProcess::Start() { if (started) return; started = true; - if (managed) running_ext.push_back(this); if (show_log) { connect(this, &QProcess::readyReadStandardOutput, this, [&]() { @@ -19,14 +24,11 @@ namespace NekoRay::sys { connect(this, &QProcess::readyReadStandardError, this, [&]() { MW_show_log_ext_vt100(readAllStandardError().trimmed()); }); - } - - if (managed) { connect(this, &QProcess::errorOccurred, this, [&](QProcess::ProcessError error) { if (!killed) { crashed = true; MW_show_log_ext(tag, "errorOccurred:" + errorString()); - MW_dialog_message("ExternalProcess", "Crashed"); + if (managed) MW_dialog_message("ExternalProcess", "Crashed"); } }); connect(this, &QProcess::stateChanged, this, [&](QProcess::ProcessState state) { @@ -37,7 +39,7 @@ namespace NekoRay::sys { crashed = true; MW_show_log_ext(tag, "[Error] Program exited accidentally: " + errorString()); Kill(); - MW_dialog_message("ExternalProcess", "Crashed"); + if (managed) MW_dialog_message("ExternalProcess", "Crashed"); } } }); @@ -51,7 +53,6 @@ namespace NekoRay::sys { void ExternalProcess::Kill() { if (killed) return; killed = true; - if (managed) running_ext.removeAll(this); if (!crashed) { QProcess::kill(); @@ -59,7 +60,7 @@ namespace NekoRay::sys { } } - CoreProcess::CoreProcess(const QString &core_path, const QStringList &args) { + CoreProcess::CoreProcess(const QString &core_path, const QStringList &args) : ExternalProcess() { ExternalProcess::managed = false; ExternalProcess::show_log = false; ExternalProcess::program = core_path; diff --git a/sys/ExternalProcess.hpp b/sys/ExternalProcess.hpp index 0459820..426dcd3 100644 --- a/sys/ExternalProcess.hpp +++ b/sys/ExternalProcess.hpp @@ -12,10 +12,11 @@ namespace NekoRay::sys { QStringList arguments; QStringList env; - bool managed = true; // running_ext & stateChanged + bool managed = true; // MW_dialog_message bool show_log = true; ExternalProcess(); + ~ExternalProcess(); // start & kill is one time @@ -41,6 +42,6 @@ namespace NekoRay::sys { int restart_id = -1; }; - // start & kill change this list - inline QList running_ext; + // 手动管理 + inline QList> running_ext; } // namespace NekoRay::sys diff --git a/translations/fa_IR.ts b/translations/fa_IR.ts index a248846..4d64f03 100644 --- a/translations/fa_IR.ts +++ b/translations/fa_IR.ts @@ -601,10 +601,6 @@ Config Suffix - - Generator - - Please read the documentation. If you don't understand, use a share link instead. @@ -617,6 +613,22 @@ Please pick a core. لطفا یک هسته انتخاب کنید. + + Random if it's empty or zero. + + + + Preview + + + + Preview replace + + + + Preview config + + EditNaive diff --git a/translations/zh_CN.ts b/translations/zh_CN.ts index 9fdbec9..56bf7da 100644 --- a/translations/zh_CN.ts +++ b/translations/zh_CN.ts @@ -591,10 +591,6 @@ Command 命令 - - Generator - 生成器 - Json Editor JSON 编辑器 @@ -615,6 +611,22 @@ Config Suffix 配置文件后缀 + + Random if it's empty or zero. + 如果为空或为零,则表示使用随机端口。 + + + Preview + 预览 + + + Preview replace + 预览替换串 + + + Preview config + 预览配置 + EditNaive diff --git a/ui/edit/dialog_edit_profile.cpp b/ui/edit/dialog_edit_profile.cpp index 97d568e..206048f 100644 --- a/ui/edit/dialog_edit_profile.cpp +++ b/ui/edit/dialog_edit_profile.cpp @@ -234,12 +234,11 @@ void DialogEditProfile::typeSelected(const QString &newType) { delete old; // 左边 bean inner editor - innerEditor->get_edit_dialog = [&]() { - return (QWidget *) this; - }; - innerEditor->editor_cache_updated = [=] { - editor_cache_updated_impl(); - }; + innerEditor->get_edit_dialog = [&]() { return (QWidget *) this; }; + innerEditor->get_edit_text_name = [&]() { return ui->name->text(); }; + innerEditor->get_edit_text_serverAddress = [&]() { return ui->address->text(); }; + innerEditor->get_edit_text_serverPort = [&]() { return ui->port->text(); }; + innerEditor->editor_cache_updated = [=] { editor_cache_updated_impl(); }; innerEditor->onStart(ent); // 左边 common diff --git a/ui/edit/edit_custom.cpp b/ui/edit/edit_custom.cpp index daa5be7..e0dbb9b 100644 --- a/ui/edit/edit_custom.cpp +++ b/ui/edit/edit_custom.cpp @@ -4,6 +4,11 @@ #include "qv2ray/v2/ui/widgets/editors/w_JsonEditor.hpp" #include "fmt/CustomBean.hpp" #include "fmt/Preset.hpp" +#include "db/ConfigBuilder.hpp" +#include "db/Database.hpp" + +#include +#include EditCustom::EditCustom(QWidget *parent) : QWidget(parent), ui(new Ui::EditCustom) { ui->setupUi(this); @@ -20,6 +25,14 @@ EditCustom::~EditCustom() { delete ui; } +#define SAVE_CUSTOM_BEAN \ + P_SAVE_COMBO(core) \ + bean->command = ui->command->text().split(" "); \ + P_SAVE_STRING_QTEXTEDIT(config_simple) \ + P_SAVE_COMBO(config_suffix) \ + P_SAVE_INT(mapping_port) \ + P_SAVE_INT(socks_port) + void EditCustom::onStart(QSharedPointer _ent) { this->ent = _ent; auto bean = this->ent->CustomBean(); @@ -50,6 +63,8 @@ void EditCustom::onStart(QSharedPointer _ent) { ui->command->setText(bean->command.join(" ")); P_LOAD_STRING(config_simple) P_LOAD_COMBO(config_suffix) + P_LOAD_INT(mapping_port) + P_LOAD_INT(socks_port) // custom external if (!bean->core.isEmpty()) { @@ -72,17 +87,46 @@ void EditCustom::onStart(QSharedPointer _ent) { ui->config_suffix_l->hide(); } - // Generators - ui->generator->setVisible(false); + // Preview + connect(ui->preview, &QPushButton::clicked, this, [=] { + // CustomBean::BuildExternal + QStringList th; + auto mapping_port = ui->mapping_port->text().toInt(); + auto socks_port = ui->socks_port->text().toInt(); + th << "%mapping_port% => " + (mapping_port <= 0 ? "Random" : Int2String(mapping_port)); + th << "%socks_port% => " + (socks_port <= 0 ? "Random" : Int2String(socks_port)); + th << "%server_addr% => " + get_edit_text_serverAddress(); + th << "%server_port% => " + get_edit_text_serverPort(); + MessageBoxInfo(tr("Preview replace"), th.join("\n")); + // EditCustom::onEnd + auto tmpEnt = NekoRay::ProfileManager::NewProxyEntity("custom"); + auto bean = tmpEnt->CustomBean(); + SAVE_CUSTOM_BEAN + if (bean->core.isEmpty()) return; + // + auto result = NekoRay::BuildConfig(tmpEnt, false, false); + if (!result->error.isEmpty()) { + MessageBoxInfo(software_name, result->error); + return; + } + for (const auto &ext: result->exts) { + auto extR = ext.first; + auto command = QStringList{extR.program}; + command += extR.arguments; + auto btn = QMessageBox::information(this, tr("Preview config"), + QString("Command: %1\n\n%2").arg(QStringList2Command(command), extR.config_export), + "OK", "Copy", "", 0, 0); + if (btn == 1) { + QApplication::clipboard()->setText(extR.config_export); + } + } + }); } bool EditCustom::onEnd() { auto bean = this->ent->CustomBean(); - P_SAVE_COMBO(core) - bean->command = ui->command->text().split(" "); - P_SAVE_STRING_QTEXTEDIT(config_simple) - P_SAVE_COMBO(config_suffix) + SAVE_CUSTOM_BEAN if (bean->core.isEmpty()) { MessageBoxWarning(software_name, tr("Please pick a core.")); diff --git a/ui/edit/edit_custom.ui b/ui/edit/edit_custom.ui index ce90ad5..4ed650c 100644 --- a/ui/edit/edit_custom.ui +++ b/ui/edit/edit_custom.ui @@ -7,129 +7,136 @@ 0 0 400 - 400 + 450 EditCustom - + - + - - - - - - 0 - 0 - - - - Core - - - - - - - true - - - - - - - Json Editor - - - - + + + + 0 + 0 + + + + Core + + - - - - - Command - - - - - - - %config% - - - - - - - Config Suffix - - - - - - - true - - - - - - - - - json - - - - - + + + true + + - - - - 0 - 300 - + + + Json Editor - - - Generator - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - + + + + + Command + + + + + + + %config% + + + + + + + Config Suffix + + + + + + + true + + + + - - - + + + + json + + + + + yml + + + + + + + + + + + + Random if it's empty or zero. + + + Mapping Port + + + + + + + + + + Random if it's empty or zero. + + + Socks Port + + + + + + + + + + Preview + + + + + + + + + + 0 + 300 + + @@ -138,6 +145,11 @@ core as_json command + config_suffix + mapping_port + socks_port + preview + config_simple diff --git a/ui/edit/profile_editor.h b/ui/edit/profile_editor.h index a7cd5c7..80347ac 100644 --- a/ui/edit/profile_editor.h +++ b/ui/edit/profile_editor.h @@ -12,6 +12,9 @@ public: virtual bool onEnd() = 0; std::function get_edit_dialog; + std::function get_edit_text_name; + std::function get_edit_text_serverAddress; + std::function get_edit_text_serverPort; // cached editor diff --git a/ui/mainwindow_grpc.cpp b/ui/mainwindow_grpc.cpp index a22d94d..10c8949 100644 --- a/ui/mainwindow_grpc.cpp +++ b/ui/mainwindow_grpc.cpp @@ -100,15 +100,15 @@ void MainWindow::speedtest_current_group(int mode) { req.set_url(NekoRay::dataStore->test_url.toStdString()); // - QList ext; + std::list>> exts; if (mode == libcore::TestMode::UrlTest || mode == libcore::FullTest) { auto c = NekoRay::BuildConfig(profile, true, false); - // external test ??? - if (!c->ext.isEmpty()) { - ext = c->ext; - for (auto extC: ext) { - extC->Start(); + // TODO refactor external test + if (!c->exts.empty()) { + exts = c->exts; + for (const auto &ext: exts) { + ext.second->Start(); } QThread::msleep(500); } @@ -128,9 +128,8 @@ void MainWindow::speedtest_current_group(int mode) { bool rpcOK; auto result = defaultClient->Test(&rpcOK, req); - for (auto extC: ext) { - extC->Kill(); - extC->deleteLater(); + for (const auto &ext: exts) { + ext.second->Kill(); } if (!rpcOK) return; @@ -236,8 +235,9 @@ void MainWindow::neko_start(int _id) { NekoRay::traffic::trafficLooper->loop_enabled = true; #endif - for (auto extC: result->ext) { - extC->Start(); + for (const auto &ext: result->exts) { + NekoRay::sys::running_ext.push_back(ext.second); + ext.second->Start(); } NekoRay::dataStore->UpdateStartedId(ent->id); @@ -254,7 +254,6 @@ void MainWindow::neko_stop(bool crash) { while (!NekoRay::sys::running_ext.isEmpty()) { auto extC = NekoRay::sys::running_ext.takeFirst(); extC->Kill(); - extC->deleteLater(); } #ifndef NKR_NO_GRPC