diff --git a/i18n/across_zh_CN.ts b/i18n/across_zh_CN.ts index 9ec1f46..43b9e41 100644 --- a/i18n/across_zh_CN.ts +++ b/i18n/across_zh_CN.ts @@ -1225,10 +1225,10 @@ across::GroupList - - - - + + + + Copy [%1] URL to clipboard 复制 [%1] 链接到剪贴板 @@ -1236,7 +1236,7 @@ across::NodeFormModel - + custom configuration encoding to url is not supported 未支持自定义配置编码到链接格式 @@ -1244,7 +1244,7 @@ across::NodeList - + Copy [%1] URL to clipboard 复制 [%1] 链接到剪贴板 @@ -1252,28 +1252,28 @@ across::SystemTray - - + + Show 显示 - + Connect 连接 - + Disconnect 断开 - + Reconnect 重连 - + Quit 退出 @@ -1282,12 +1282,12 @@ 入站 - + Hide 隐藏 - + OFF 关闭 @@ -1295,12 +1295,12 @@ across::setting::ConfigTools - + Failed to parse version 无法解析版本 - + New Version: v%1 新版本:v%1 @@ -1309,7 +1309,7 @@ 新版本:%1 - + Already the latest version 已经是最新版本 diff --git a/src/models/serializetools.cpp b/src/models/serializetools.cpp index e8e65fe..2aaaa3c 100644 --- a/src/models/serializetools.cpp +++ b/src/models/serializetools.cpp @@ -307,10 +307,35 @@ SerializeTools::vmessBase64Decode(const std::string &url_str) { do { if (stream->network() == "tcp") { - // TODO: get tcpsetting + // TODO: fake header support + // auto tcp = stream->mutable_tcpsettings(); + + // if (root.contains("type")) { + // auto header = tcp->mutable_header(); + // header->set_type(root["type"].get()); + // } break; } + if (stream->network() == "h2") { + auto http2 = stream->mutable_httpsettings(); + + if (root.contains("host")) { + auto content = + QString::fromStdString(root["host"].get()) + .trimmed() + .split(","); + for (const auto &host : content) { + if (!host.isEmpty()) + http2->add_host(host.trimmed().toStdString()); + } + } + + if (root.contains("path")) { + http2->set_path(root["path"].get()); + } + } + if (stream->network() == "ws") { auto websocket = stream->mutable_wssettings(); @@ -418,6 +443,23 @@ SerializeTools::vmessBase64Encode(const URLMetaObject &meta) { root["net"] = stream.network(); do { + if (stream.network() == "h2" && stream.has_httpsettings()) { + const auto &http2 = stream.httpsettings(); + std::string hosts; + + for (const auto &host : stream.httpsettings().host()) { + if (!host.empty()) { + hosts.append(host); + hosts.append(","); + } + } + + hosts = hosts.substr(0, hosts.length() - 1); + root["host"] = hosts; + root["path"] = http2.path(); + break; + } + if (stream.network() == "ws" && stream.has_wssettings()) { const auto& websocket = stream.wssettings(); root["host"] = websocket.headers().at("Host"); diff --git a/src/view_models/nodeformmodel.cpp b/src/view_models/nodeformmodel.cpp index 24f14b9..16634f7 100644 --- a/src/view_models/nodeformmodel.cpp +++ b/src/view_models/nodeformmodel.cpp @@ -288,6 +288,22 @@ bool NodeFormModel::setVMessOutboud(NodeInfo &node, const QVariantMap &values) { } do { + if (stream->network() == "h2") { + auto http2 = stream->mutable_httpsettings(); + + if (values.contains("host")) { + for (const auto &host : + values.value("host").toString().trimmed().split(",")) { + http2->add_host(host.toStdString()); + } + } + + if (values.contains("path")) { + http2->set_path(values.value("path").toString().toStdString()); + } + break; + } + if (stream->network() == "ws") { auto websocket = stream->mutable_wssettings(); diff --git a/src/views/home/VMESSSetting.qml b/src/views/home/VMESSSetting.qml index 0d6646e..66d77d7 100644 --- a/src/views/home/VMESSSetting.qml +++ b/src/views/home/VMESSSetting.qml @@ -125,7 +125,7 @@ Item { id: networkSelect Layout.fillWidth: true Layout.columnSpan: 3 - model: ["tcp", "ws", "grpc", "quic"] + model: ["none", "h2", "ws", "grpc", "quic"] onCurrentIndexChanged: HomeJS.networkSelectToggle(currentIndex, { "typeLabel": typeLabel, diff --git a/src/views/routing/RulePanel.qml b/src/views/routing/RulePanel.qml new file mode 100644 index 0000000..57b42d4 --- /dev/null +++ b/src/views/routing/RulePanel.qml @@ -0,0 +1,84 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +import ACross + +CardBox { + id: control + implicitWidth: 240 + implicitHeight: 720 + + property string title: "Direct" + property bool isEdited: false + + GridLayout { + anchors.fill: parent + anchors.margins: acrossConfig.itemSpacing * 4 + columns: 4 + + Label { + text: control.title + font.pointSize: fontSize + color: acrossConfig.textColor + } + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 22 + Layout.columnSpan: 3 + + RowLayout { + anchors.fill: parent + + Item { + Layout.fillWidth: true + } + + SVGBox { + source: "qrc:/misc/icons/" + acrossConfig.iconStyle + "/trash.svg" + sourceHeight: 24 + sourceWidth: 24 + } + + SVGBox { + source: "qrc:/misc/icons/" + acrossConfig.iconStyle + "/edit.svg" + sourceHeight: 24 + sourceWidth: 24 + + MouseArea { + anchors.fill: parent + + onClicked: { + control.isEdited = !control.isEdited + } + } + } + } + } + + ListView { + id: directRuleList + Layout.fillWidth: true + Layout.columnSpan: 4 + Layout.fillHeight: true + clip: true + + model: ["baidu.com", "qq.com", "geosite:cn", "ext:customizedGeoSiteFile.dat:cn", "0.0.0.0/8", "10.0.0.0/8", "fc00::/7", "fe80::/10", "geoip:cn", "geoip:!cn", "ext:customizedGeoIPFile.dat:cn", "ext:customizedGeoIPFile.dat:!cn", "ext-ip:customizedGeoIPFile.dat:cn", "ext-ip:customizedGeoIPFile.dat:!cn"] + delegate: RuleItemCard { + state: control.isEdited ? "EditState" : "NormalState" + width: directRuleList.width + } + } + + // SVGBox { + // source: "qrc:/misc/icons/" + acrossConfig.iconStyle + "/add.svg" + // sourceHeight: 24 + // sourceWidth: 24 + // } + TextFieldBox { + Layout.fillWidth: true + Layout.columnSpan: 4 + } + } +} diff --git a/src/views/typescripts/home.js b/src/views/typescripts/home.js index e5cbe6b..78d8662 100644 --- a/src/views/typescripts/home.js +++ b/src/views/typescripts/home.js @@ -20,9 +20,17 @@ function networkSelectToggle(currentIndex, components, control = null) { let quicSecuritySelect = components["quicSecuritySelect"]; hideAllTypeSetting([hostLabel, hostText, pathLabel, pathText, typeLabel, typeSelect, quicSecurityLabel, quicSecuritySelect]); switch (currentIndex) { - case 0: // tcp + case 0: // none break; - case 1: // ws + case 1: + hostLabel.visible = true; + hostLabel.text = "Host"; + hostText.visible = true; + pathLabel.visible = true; + pathLabel.text = "Path"; + pathText.visible = true; + break; + case 2: // ws hostLabel.visible = true; hostLabel.text = "Host"; hostText.visible = true; @@ -30,12 +38,12 @@ function networkSelectToggle(currentIndex, components, control = null) { pathLabel.text = "Path"; pathText.visible = true; break; - case 2: // grpc + case 3: // grpc pathLabel.visible = true; pathLabel.text = "Service Name"; pathText.visible = true; break; - case 3: // quic + case 4: // quic typeLabel.visible = true; typeSelect.visible = true; quicSecurityLabel.visible = true; @@ -99,6 +107,21 @@ function visibleChangeToggle(visible, components, model = null) { let network = streamSettings["network"]; networkSelect.currentIndex = networkSelect.find(network); switch (network) { + case "h2": + if (streamSettings.hasOwnProperty("httpSettings")) { + let httpSettings = streamSettings["httpSettings"]; + if (httpSettings.hasOwnProperty("host") && hostText !== null) { + let hosts = ""; + for (const host of httpSettings["host"]) { + hosts = hosts.concat(host, ","); + } + hostText.text = hosts.substring(0, hosts.length - 1).trim(); + } + if (httpSettings.hasOwnProperty("path") && pathText !== null) { + pathText.text = httpSettings["path"]; + } + } + break; case "ws": if (streamSettings.hasOwnProperty("wsSettings")) { let wsSettings = streamSettings["wsSettings"]; diff --git a/src/views/typescripts/home.ts b/src/views/typescripts/home.ts index 026adfe..9b978be 100644 --- a/src/views/typescripts/home.ts +++ b/src/views/typescripts/home.ts @@ -23,9 +23,9 @@ function networkSelectToggle(currentIndex: number, components: any, control: any hideAllTypeSetting([hostLabel, hostText, pathLabel, pathText, typeLabel, typeSelect, quicSecurityLabel, quicSecuritySelect]) switch (currentIndex) { - case 0: // tcp + case 0: // none break - case 1: // ws + case 1: hostLabel.visible = true hostLabel.text = "Host" hostText.visible = true @@ -33,12 +33,20 @@ function networkSelectToggle(currentIndex: number, components: any, control: any pathLabel.text = "Path" pathText.visible = true break - case 2: // grpc + case 2: // ws + hostLabel.visible = true + hostLabel.text = "Host" + hostText.visible = true + pathLabel.visible = true + pathLabel.text = "Path" + pathText.visible = true + break + case 3: // grpc pathLabel.visible = true pathLabel.text = "Service Name" pathText.visible = true break - case 3: // quic + case 4: // quic typeLabel.visible = true typeSelect.visible = true quicSecurityLabel.visible = true @@ -117,6 +125,23 @@ function visibleChangeToggle(visible: boolean, components: any, model: any = nul networkSelect.currentIndex = networkSelect.find(network) switch (network) { + case "h2": + if (streamSettings.hasOwnProperty("httpSettings")) { + let httpSettings = streamSettings["httpSettings"] + + if (httpSettings.hasOwnProperty("host") && hostText !== null) { + let hosts = "" + for (const host of httpSettings["host"]) { + hosts = hosts.concat(host, ",") + } + hostText.text = hosts.substring(0, hosts.length - 1).trim() + } + + if (httpSettings.hasOwnProperty("path") && pathText !== null) { + pathText.text = httpSettings["path"] + } + } + break case "ws": if (streamSettings.hasOwnProperty("wsSettings")) { let wsSettings = streamSettings["wsSettings"]