mirror of
https://github.com/MetaCubeX/subconverter.git
synced 2025-09-26 23:09:20 +08:00
Enhancements
Add basic support for generating sing-box configs. Use cpp-httplib as web server. Optimize codes.
This commit is contained in:
parent
f99748b126
commit
bbcb6434e1
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -157,7 +157,7 @@ jobs:
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
update: true
|
||||
install: base-devel git mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-libevent mingw-w64-x86_64-pcre2 patch
|
||||
install: base-devel git mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-pcre2 patch
|
||||
msystem: MINGW64
|
||||
path-type: inherit
|
||||
- name: Add commit id into version
|
||||
@ -197,7 +197,7 @@ jobs:
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
update: true
|
||||
install: base-devel git mingw-w64-i686-gcc mingw-w64-i686-cmake mingw-w64-i686-libevent mingw-w64-i686-pcre2 patch
|
||||
install: base-devel git mingw-w64-i686-gcc mingw-w64-i686-cmake mingw-w64-i686-pcre2 patch
|
||||
msystem: MINGW32
|
||||
path-type: inherit
|
||||
- name: Add commit id into version
|
||||
|
@ -46,7 +46,8 @@ ADD_EXECUTABLE(${BUILD_TARGET_NAME}
|
||||
src/parser/subparser.cpp
|
||||
src/script/cron.cpp
|
||||
src/script/script_quickjs.cpp
|
||||
src/server/webserver_libevent.cpp
|
||||
# src/server/webserver_libevent.cpp
|
||||
src/server/webserver_httplib.cpp
|
||||
src/utils/base64/base64.cpp
|
||||
src/utils/codepage.cpp
|
||||
src/utils/file.cpp
|
||||
@ -66,12 +67,12 @@ SET(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
FIND_PACKAGE(Threads REQUIRED)
|
||||
TARGET_LINK_LIBRARIES(${BUILD_TARGET_NAME} ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
PKG_CHECK_MODULES(LIBEVENT libevent>=2.1.10 REQUIRED)
|
||||
FIND_PATH(LIBEVENT_INCLUDE_DIR NAMES event.h PATHS ${LIBEVENT_INCLUDE_DIRS})
|
||||
FIND_LIBRARY(LIBEVENT_LIBRARY NAMES event PATHS ${LIBEVENT_LIBRARY_DIRS})
|
||||
TARGET_LINK_DIRECTORIES(${BUILD_TARGET_NAME} PRIVATE ${LIBEVENT_LIBRARY_DIRS})
|
||||
TARGET_INCLUDE_DIRECTORIES(${BUILD_TARGET_NAME} PRIVATE ${LIBEVENT_INCLUDE_DIR})
|
||||
TARGET_LINK_LIBRARIES(${BUILD_TARGET_NAME} ${LIBEVENT_LIBRARY})
|
||||
#PKG_CHECK_MODULES(LIBEVENT libevent>=2.1.10 REQUIRED)
|
||||
#FIND_PATH(LIBEVENT_INCLUDE_DIR NAMES event.h PATHS ${LIBEVENT_INCLUDE_DIRS})
|
||||
#FIND_LIBRARY(LIBEVENT_LIBRARY NAMES event PATHS ${LIBEVENT_LIBRARY_DIRS})
|
||||
#TARGET_LINK_DIRECTORIES(${BUILD_TARGET_NAME} PRIVATE ${LIBEVENT_LIBRARY_DIRS})
|
||||
#TARGET_INCLUDE_DIRECTORIES(${BUILD_TARGET_NAME} PRIVATE ${LIBEVENT_INCLUDE_DIR})
|
||||
#TARGET_LINK_LIBRARIES(${BUILD_TARGET_NAME} ${LIBEVENT_LIBRARY})
|
||||
|
||||
FIND_PACKAGE(CURL 7.54.0 REQUIRED)
|
||||
TARGET_LINK_DIRECTORIES(${BUILD_TARGET_NAME} PRIVATE ${CURL_LIBRARY_DIRS})
|
||||
@ -113,7 +114,7 @@ ELSE()
|
||||
INSTALL(DIRECTORY base/ DESTINATION ${CMAKE_INSTALL_BINDIR}/${BUILD_TARGET_NAME} FILES_MATCHING PATTERN "*")
|
||||
ENDIF()
|
||||
|
||||
ELSE()
|
||||
ELSE() #BUILD_STATIC_LIBRARY
|
||||
|
||||
ADD_LIBRARY(${BUILD_TARGET_NAME} STATIC
|
||||
src/generator/config/ruleconvert.cpp
|
||||
@ -154,7 +155,7 @@ IF(WIN32)
|
||||
TARGET_LINK_LIBRARIES(${BUILD_TARGET_NAME} PRIVATE ws2_32)
|
||||
ENDIF()
|
||||
|
||||
ENDIF()
|
||||
ENDIF() #BUILD_STATIC_LIBRARY
|
||||
|
||||
IF(HAVE_TO_STRING)
|
||||
TARGET_COMPILE_DEFINITIONS(${BUILD_TARGET_NAME} PRIVATE -DHAVE_TO_STRING)
|
||||
|
@ -266,3 +266,33 @@ enhanced-mode-by-rule = true
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
{% if request.target == "singbox" %}
|
||||
|
||||
{
|
||||
"log": {
|
||||
"disabled": false,
|
||||
"level": "info",
|
||||
"output": "box.log",
|
||||
"timestamp": true
|
||||
},
|
||||
"dns": {},
|
||||
"ntp": {
|
||||
"enabled": false,
|
||||
"server": "time.apple.com",
|
||||
"server_port": 123,
|
||||
"interval": "30m"
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "socks",
|
||||
"tag": "socks-in",
|
||||
"listen": "127.0.0.1",
|
||||
"listen_port": 2080
|
||||
}
|
||||
],
|
||||
"outbounds": [],
|
||||
"route": {},
|
||||
"experimental": {}
|
||||
}
|
||||
|
||||
{% endif %}
|
||||
|
26
base/base/singbox.json
Normal file
26
base/base/singbox.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"log": {
|
||||
"disabled": false,
|
||||
"level": "info",
|
||||
"output": "box.log",
|
||||
"timestamp": true
|
||||
},
|
||||
"dns": {},
|
||||
"ntp": {
|
||||
"enabled": false,
|
||||
"server": "time.apple.com",
|
||||
"server_port": 123,
|
||||
"interval": "30m"
|
||||
},
|
||||
"inbounds": [
|
||||
{
|
||||
"type": "socks",
|
||||
"tag": "socks-in",
|
||||
"listen": "127.0.0.1",
|
||||
"listen_port": 2080
|
||||
}
|
||||
],
|
||||
"outbounds": [],
|
||||
"route": {},
|
||||
"experimental": {}
|
||||
}
|
@ -43,6 +43,9 @@ clash_rule_base=base/forcerule.yml
|
||||
;mellow_rule_base=base/mellow.conf
|
||||
;quan_rule_base=base/quan.conf
|
||||
;quanx_rule_base=base/quanx.conf
|
||||
;loon_rule_base=base/loon.conf
|
||||
;sssub_rule_base=base/shadowsocks_base.json
|
||||
;singbox_rule_base=base/singbox.json
|
||||
|
||||
;Options for renaming nodes
|
||||
;rename=Test-(.*?)-(.*?)-(.*?)\((.*?)\)@\1\4x测试线路_自\2到\3
|
||||
|
@ -10,6 +10,9 @@ clash_rule_base = "base/forcerule.yml"
|
||||
#mellow_rule_base = "base/mellow.conf"
|
||||
#quan_rule_base = "base/quan.conf"
|
||||
#quanx_rule_base = "base/quanx.conf"
|
||||
#loon_rule_base = "base/loon.conf"
|
||||
#sssub_rule_base = "base/shadowsocks_base.json"
|
||||
#singbox_rule_base = "base/singbox.json"
|
||||
|
||||
# Options for adding emojis
|
||||
#add_emoji = true
|
||||
|
@ -14,6 +14,9 @@ custom:
|
||||
# mellow_rule_base: base/mellow.conf
|
||||
# quan_rule_base: base/quan.conf
|
||||
# quanx_rule_base: base/quanx.conf
|
||||
# loon_rule_base: base/loon.conf
|
||||
# sssub_rule_base: base/shadowsocks_base.json
|
||||
# singbox_rule_base: base/singbox.json
|
||||
|
||||
# rename_node:
|
||||
# - {import: snippet/rename.txt}
|
||||
|
@ -26,7 +26,7 @@ exclude_remarks=(到期|剩余流量|时间|官网|产品|平台)
|
||||
enable_filter=false
|
||||
;Script used for filtering nodes. Supports inline script and script path. A "filter" function with 1 argument which is a node should be defined in the script.
|
||||
;Example: Inline script: Set value to content of script. Replace all line break with "\n".
|
||||
; Script path: Set value to "path:/path/to/script.js".
|
||||
; Script path: set value to "path:/path/to/script.js".
|
||||
;filter_script=function filter(node) {\n const info = JSON.parse(node.ProxyInfo);\n if(info.EncryptMethod.includes('chacha20'))\n return true;\n return false;\n}
|
||||
|
||||
;Setting an external config file as default when none is specified, supports local files/URL
|
||||
@ -59,6 +59,9 @@ loon_rule_base=base/all_base.tpl
|
||||
;Shadowsocks Android config base used by the generator, supports local files/URL
|
||||
sssub_rule_base=base/all_base.tpl
|
||||
|
||||
;sing-box config base used by the generator, supports local files/URL
|
||||
singbox_rule_base=base/all_base.tpl
|
||||
|
||||
;Proxy used to download configs, rulesets or subscriptions, set to NONE or empty to disable it, set to SYSTEM to use system proxy.
|
||||
;Accept cURL-supported proxies (http:// https:// socks4a:// socks5://)
|
||||
;Additional support for CORS proxy ( https://github.com/Rob--W/cors-anywhere https://github.com/Zibri/cloudflare-cors-anywhere etc.), prefix the address with "cors:" to recognize the address as CORS proxy.
|
||||
@ -237,6 +240,7 @@ clash.log_level=info
|
||||
/mellow=/sub?target=mellow
|
||||
/surfboard=/sub?target=surfboard
|
||||
/loon=/sub?target=loon
|
||||
/singbox=/sub?target=singbox
|
||||
/ss=/sub?target=ss
|
||||
/ssd=/sub?target=ssd
|
||||
/sssub=/sub?target=sssub
|
||||
|
@ -25,8 +25,8 @@ exclude_remarks = ["(到期|剩余流量|时间|官网|产品)"]
|
||||
# Enable script support for filtering nodes
|
||||
enable_filter = false
|
||||
# Script used for filtering nodes. Supports inline script and script path. A "filter" function with 1 argument which is a node should be defined in the script.
|
||||
# Example: Inline script: Set value to content of script.
|
||||
# Script path: Set value to "path:/path/to/script.js".
|
||||
# Example: Inline script: set value to content of script.
|
||||
# Script path: set value to "path:/path/to/script.js".
|
||||
#filter_script = '''
|
||||
#function filter(node) {
|
||||
# const info = JSON.parse(node.ProxyInfo);
|
||||
@ -66,6 +66,9 @@ loon_rule_base = "base/all_base.tpl"
|
||||
# Shadowsocks Android config base used by the generator, supports local files/URL
|
||||
sssub_rule_base = "base/all_base.tpl"
|
||||
|
||||
# sing-box config base used by the generator, supports local files/URL
|
||||
singbox_rule_base = "base/all_base.tpl"
|
||||
|
||||
# Proxy used to download rulesets or subscriptions, set to NONE or empty to disable it, set to SYSTEM to use system proxy.
|
||||
# Accept cURL-supported proxies (http:// https:// socks4a:// socks5://)
|
||||
|
||||
@ -266,6 +269,10 @@ target = "/sub?target=surfboard"
|
||||
uri = "/loon"
|
||||
target = "/sub?target=loon"
|
||||
|
||||
[[aliases]]
|
||||
uri = "/singbox"
|
||||
target = "/sub?target=singbox"
|
||||
|
||||
[[aliases]]
|
||||
uri = "/ss"
|
||||
target = "/sub?target=ss"
|
||||
|
@ -19,6 +19,7 @@ common:
|
||||
quanx_rule_base: base/all_base.tpl
|
||||
loon_rule_base: base/all_base.tpl
|
||||
sssub_rule_base: base/all_base.tpl
|
||||
singbox_rule_base: base/all_base.tpl
|
||||
proxy_config: SYSTEM
|
||||
proxy_ruleset: SYSTEM
|
||||
proxy_subscription: NONE
|
||||
@ -116,6 +117,7 @@ aliases:
|
||||
- {uri: /mellow, target: "/sub?target=mellow"}
|
||||
- {uri: /surfboard, target: "/sub?target=surfboard"}
|
||||
- {uri: /loon, target: "/sub?target=loon"}
|
||||
- {uri: /singbox, target: "/sub?target=singbox"}
|
||||
- {uri: /ss, target: "/sub?target=ss"}
|
||||
- {uri: /ssd, target: "/sub?target=ssd"}
|
||||
- {uri: /sssub, target: "/sub?target=sssub"}
|
||||
|
9262
include/httplib.h
Normal file
9262
include/httplib.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,7 @@ ARG SHA=""
|
||||
WORKDIR /
|
||||
RUN set -xe && \
|
||||
apk add --no-cache --virtual .build-tools git g++ build-base linux-headers cmake python3 && \
|
||||
apk add --no-cache --virtual .build-deps curl-dev rapidjson-dev libevent-dev pcre2-dev yaml-cpp-dev && \
|
||||
apk add --no-cache --virtual .build-deps curl-dev rapidjson-dev pcre2-dev yaml-cpp-dev && \
|
||||
git clone https://github.com/ftk/quickjspp --depth=1 && \
|
||||
cd quickjspp && \
|
||||
git submodule update --init && \
|
||||
@ -48,7 +48,7 @@ RUN set -xe && \
|
||||
FROM alpine:3.16
|
||||
LABEL maintainer="tindy.it@gmail.com"
|
||||
|
||||
RUN apk add --no-cache --virtual subconverter-deps pcre2 libcurl yaml-cpp libevent
|
||||
RUN apk add --no-cache --virtual subconverter-deps pcre2 libcurl yaml-cpp
|
||||
|
||||
COPY --from=builder /subconverter/subconverter /usr/bin/
|
||||
COPY --from=builder /subconverter/base /base/
|
||||
|
@ -2,7 +2,7 @@
|
||||
set -xe
|
||||
|
||||
apk add gcc g++ build-base linux-headers cmake make autoconf automake libtool python2 python3
|
||||
apk add mbedtls-dev mbedtls-static zlib-dev rapidjson-dev libevent-dev libevent-static zlib-static pcre2-dev
|
||||
apk add mbedtls-dev mbedtls-static zlib-dev rapidjson-dev zlib-static pcre2-dev
|
||||
|
||||
git clone https://github.com/curl/curl --depth=1 --branch curl-8_4_0
|
||||
cd curl
|
||||
@ -45,7 +45,7 @@ cmake -DCMAKE_BUILD_TYPE=Release .
|
||||
make -j3
|
||||
rm subconverter
|
||||
# shellcheck disable=SC2046
|
||||
g++ -o base/subconverter $(find CMakeFiles/subconverter.dir/src/ -name "*.o") -static -lpcre2-8 -levent -lyaml-cpp -L/usr/lib64 -lcurl -lmbedtls -lmbedcrypto -lmbedx509 -lz -l:quickjs/libquickjs.a -llibcron -O3 -s
|
||||
g++ -o base/subconverter $(find CMakeFiles/subconverter.dir/src/ -name "*.o") -static -lpcre2-8 -lyaml-cpp -L/usr/lib64 -lcurl -lmbedtls -lmbedcrypto -lmbedx509 -lz -l:quickjs/libquickjs.a -llibcron -O3 -s
|
||||
|
||||
python3 -m ensurepip
|
||||
python3 -m pip install gitpython
|
||||
|
@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
set -xe
|
||||
|
||||
brew reinstall rapidjson libevent zlib pcre2 pkgconfig
|
||||
brew reinstall rapidjson zlib pcre2 pkgconfig
|
||||
|
||||
#git clone https://github.com/curl/curl --depth=1 --branch curl-7_88_1
|
||||
#cd curl
|
||||
@ -46,13 +46,13 @@ cmake -DCMAKE_CXX_STANDARD=11 .
|
||||
make install -j4
|
||||
cd ..
|
||||
|
||||
cp /usr/local/lib/libevent.a .
|
||||
cp /usr/local/opt/zlib/lib/libz.a .
|
||||
cp /usr/local/lib/libpcre2-8.a .
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=Release .
|
||||
make -j8
|
||||
rm subconverter
|
||||
# shellcheck disable=SC2046
|
||||
c++ -Xlinker -unexported_symbol -Xlinker "*" -o base/subconverter -framework CoreFoundation -framework Security $(find CMakeFiles/subconverter.dir/src/ -name "*.o") $(find . -name "*.a") -lcurl -O3
|
||||
|
||||
python -m ensurepip
|
||||
|
@ -52,5 +52,6 @@ rm -f C:/Strawberry/perl/bin/pkg-config C:/Strawberry/perl/bin/pkg-config.bat
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" .
|
||||
make -j4
|
||||
rm subconverter.exe
|
||||
g++ $(find CMakeFiles/subconverter.dir/src -name "*.obj") curl/lib/libcurl.a -o base/subconverter.exe -static -lbcrypt -levent -lpcre2-8 -l:quickjs/libquickjs.a -llibcron -lyaml-cpp -liphlpapi -lcrypt32 -lws2_32 -lwsock32 -lz -s
|
||||
# shellcheck disable=SC2046
|
||||
g++ $(find CMakeFiles/subconverter.dir/src -name "*.obj") curl/lib/libcurl.a -o base/subconverter.exe -static -lbcrypt -lpcre2-8 -l:quickjs/libquickjs.a -llibcron -lyaml-cpp -liphlpapi -lcrypt32 -lws2_32 -lwsock32 -lz -s
|
||||
mv base subconverter
|
||||
|
@ -3,7 +3,7 @@ set -xe
|
||||
|
||||
apt update
|
||||
apt install -y git cmake clang pkg-config
|
||||
apt install -y libevent libcurl openssl pcre2
|
||||
apt install -y libcurl openssl pcre2
|
||||
|
||||
git clone https://github.com/jbeder/yaml-cpp --depth=1
|
||||
cd yaml-cpp
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "../../utils/network.h"
|
||||
#include "../../utils/regexp.h"
|
||||
#include "../../utils/string.h"
|
||||
#include "../../utils/rapidjson_extra.h"
|
||||
#include "subexport.h"
|
||||
|
||||
/// rule type lists
|
||||
@ -28,7 +29,7 @@ std::string convertRuleset(const std::string &content, int type)
|
||||
|
||||
if(regFind(content, "^payload:\\r?\\n")) /// Clash
|
||||
{
|
||||
output = regReplace(regReplace(content, "payload:\\r?\\n", "", true), "\\s?^\\s*-\\s+('|\"?)(.*)\\1$", "\n$2", true);
|
||||
output = regReplace(regReplace(content, "payload:\\r?\\n", "", true), R"(\s?^\s*-\s+('|"?)(.*)\1$)", "\n$2", true);
|
||||
if(type == RULESET_CLASH_CLASSICAL) /// classical type
|
||||
return output;
|
||||
std::stringstream ss;
|
||||
@ -51,8 +52,8 @@ std::string convertRuleset(const std::string &content, int type)
|
||||
|
||||
if(!strLine.empty() && (strLine[0] != ';' && strLine[0] != '#' && !(lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')))
|
||||
{
|
||||
pos = strLine.find("/");
|
||||
if(pos != strLine.npos) /// ipcidr
|
||||
pos = strLine.find('/');
|
||||
if(pos != std::string::npos) /// ipcidr
|
||||
{
|
||||
if(isIPv4(strLine.substr(0, pos)))
|
||||
output += "IP-CIDR,";
|
||||
@ -143,7 +144,7 @@ void rulesetToClash(YAML::Node &base_rule, std::vector<RulesetContent> &ruleset_
|
||||
lineSize = strLine.size();
|
||||
if(!lineSize || strLine[0] == ';' || strLine[0] == '#' || (lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')) //empty lines and comments are ignored
|
||||
continue;
|
||||
if(std::none_of(ClashRuleTypes.begin(), ClashRuleTypes.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
if(std::none_of(ClashRuleTypes.begin(), ClashRuleTypes.end(), [strLine](const std::string& type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
if(strFind(strLine, "//"))
|
||||
{
|
||||
@ -218,7 +219,7 @@ std::string rulesetToClashStr(YAML::Node &base_rule, std::vector<RulesetContent>
|
||||
lineSize = strLine.size();
|
||||
if(!lineSize || strLine[0] == ';' || strLine[0] == '#' || (lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')) //empty lines and comments are ignored
|
||||
continue;
|
||||
if(std::none_of(ClashRuleTypes.begin(), ClashRuleTypes.end(), [strLine](std::string type){ return startsWith(strLine, type); }))
|
||||
if(std::none_of(ClashRuleTypes.begin(), ClashRuleTypes.end(), [strLine](const std::string& type){ return startsWith(strLine, type); }))
|
||||
continue;
|
||||
if(strFind(strLine, "//"))
|
||||
{
|
||||
@ -235,7 +236,7 @@ std::string rulesetToClashStr(YAML::Node &base_rule, std::vector<RulesetContent>
|
||||
return output_content;
|
||||
}
|
||||
|
||||
void rulesetToSurge(INIReader &base_rule, std::vector<RulesetContent> &ruleset_content_array, int surge_ver, bool overwrite_original_rules, std::string remote_path_prefix)
|
||||
void rulesetToSurge(INIReader &base_rule, std::vector<RulesetContent> &ruleset_content_array, int surge_ver, bool overwrite_original_rules, const std::string &remote_path_prefix)
|
||||
{
|
||||
string_array allRules;
|
||||
std::string rule_group, rule_path, rule_path_typed, retrieved_rules, strLine;
|
||||
@ -268,6 +269,8 @@ void rulesetToSurge(INIReader &base_rule, std::vector<RulesetContent> &ruleset_c
|
||||
case -4:
|
||||
base_rule.erase_section("Remote Rule");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -313,7 +316,7 @@ void rulesetToSurge(INIReader &base_rule, std::vector<RulesetContent> &ruleset_c
|
||||
}
|
||||
if(fileExist(rule_path))
|
||||
{
|
||||
if(surge_ver > 2 && remote_path_prefix.size())
|
||||
if(surge_ver > 2 && !remote_path_prefix.empty())
|
||||
{
|
||||
strLine = "RULE-SET," + remote_path_prefix + "/getruleset?type=1&url=" + urlSafeBase64Encode(rule_path_typed) + "," + rule_group;
|
||||
if(x.update_interval)
|
||||
@ -321,14 +324,14 @@ void rulesetToSurge(INIReader &base_rule, std::vector<RulesetContent> &ruleset_c
|
||||
allRules.emplace_back(std::move(strLine));
|
||||
continue;
|
||||
}
|
||||
else if(surge_ver == -1 && remote_path_prefix.size())
|
||||
else if(surge_ver == -1 && !remote_path_prefix.empty())
|
||||
{
|
||||
strLine = remote_path_prefix + "/getruleset?type=2&url=" + urlSafeBase64Encode(rule_path_typed) + "&group=" + urlSafeBase64Encode(rule_group);
|
||||
strLine += ", tag=" + rule_group + ", enabled=true";
|
||||
base_rule.set("filter_remote", "{NONAME}", strLine);
|
||||
continue;
|
||||
}
|
||||
else if(surge_ver == -4 && remote_path_prefix.size())
|
||||
else if(surge_ver == -4 && !remote_path_prefix.empty())
|
||||
{
|
||||
strLine = remote_path_prefix + "/getruleset?type=1&url=" + urlSafeBase64Encode(rule_path_typed) + "," + rule_group;
|
||||
base_rule.set("Remote Rule", "{NONAME}", strLine);
|
||||
@ -341,7 +344,7 @@ void rulesetToSurge(INIReader &base_rule, std::vector<RulesetContent> &ruleset_c
|
||||
{
|
||||
if(x.rule_type != RULESET_SURGE)
|
||||
{
|
||||
if(remote_path_prefix.size())
|
||||
if(!remote_path_prefix.empty())
|
||||
strLine = "RULE-SET," + remote_path_prefix + "/getruleset?type=1&url=" + urlSafeBase64Encode(rule_path_typed) + "," + rule_group;
|
||||
else
|
||||
continue;
|
||||
@ -355,7 +358,7 @@ void rulesetToSurge(INIReader &base_rule, std::vector<RulesetContent> &ruleset_c
|
||||
allRules.emplace_back(std::move(strLine));
|
||||
continue;
|
||||
}
|
||||
else if(surge_ver == -1 && remote_path_prefix.size())
|
||||
else if(surge_ver == -1 && !remote_path_prefix.empty())
|
||||
{
|
||||
strLine = remote_path_prefix + "/getruleset?type=2&url=" + urlSafeBase64Encode(rule_path_typed) + "&group=" + urlSafeBase64Encode(rule_group);
|
||||
strLine += ", tag=" + rule_group + ", enabled=true";
|
||||
@ -401,22 +404,22 @@ void rulesetToSurge(INIReader &base_rule, std::vector<RulesetContent> &ruleset_c
|
||||
continue;
|
||||
[[fallthrough]];
|
||||
case -1:
|
||||
if(!std::any_of(QuanXRuleTypes.begin(), QuanXRuleTypes.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
if(!std::any_of(QuanXRuleTypes.begin(), QuanXRuleTypes.end(), [strLine](const std::string& type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
break;
|
||||
case -3:
|
||||
if(!std::any_of(SurfRuleTypes.begin(), SurfRuleTypes.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
if(!std::any_of(SurfRuleTypes.begin(), SurfRuleTypes.end(), [strLine](const std::string& type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
break;
|
||||
default:
|
||||
if(surge_ver > 2)
|
||||
{
|
||||
if(!std::any_of(SurgeRuleTypes.begin(), SurgeRuleTypes.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
if(!std::any_of(SurgeRuleTypes.begin(), SurgeRuleTypes.end(), [strLine](const std::string& type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!std::any_of(Surge2RuleTypes.begin(), Surge2RuleTypes.end(), [strLine](std::string type){return startsWith(strLine, type);}))
|
||||
if(!std::any_of(Surge2RuleTypes.begin(), Surge2RuleTypes.end(), [strLine](const std::string& type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -453,3 +456,100 @@ void rulesetToSurge(INIReader &base_rule, std::vector<RulesetContent> &ruleset_c
|
||||
base_rule.set("{NONAME}", x);
|
||||
}
|
||||
}
|
||||
|
||||
static rapidjson::Value transformRuleToSingBox(const std::string& rule, rapidjson::MemoryPoolAllocator<>& allocator)
|
||||
{
|
||||
std::string type, value, group, option;
|
||||
|
||||
regGetMatch(rule, "^(.*?),(.*?)(?:,(.*?))?(,.*)?$", 5, nullptr, &type, &value, &group, &option);
|
||||
rapidjson::Value rule_obj(rapidjson::kObjectType);
|
||||
type = replaceAllDistinct(toLower(type), "-", "_");
|
||||
type = replaceAllDistinct(type, "ip_cidr6", "ip_cidr");
|
||||
if (type == "match" || type == "final") {
|
||||
rule_obj.AddMember("outbound", rapidjson::Value(value.c_str(), allocator), allocator);
|
||||
} else {
|
||||
rule_obj.AddMember(rapidjson::Value(type.c_str(), allocator), rapidjson::Value(value.c_str(), allocator), allocator);
|
||||
rule_obj.AddMember("outbound", rapidjson::Value(group.c_str(), allocator), allocator);
|
||||
}
|
||||
return rule_obj;
|
||||
}
|
||||
|
||||
void rulesetToSingBox(rapidjson::Document &base_rule, std::vector<RulesetContent> &ruleset_content_array, bool overwrite_original_rules)
|
||||
{
|
||||
std::string rule_group, retrieved_rules, strLine, final;
|
||||
std::stringstream strStrm;
|
||||
size_t total_rules = 0;
|
||||
rapidjson::MemoryPoolAllocator<>& allocator = base_rule.GetAllocator();
|
||||
|
||||
rapidjson::Value rules(rapidjson::kArrayType);
|
||||
if (!overwrite_original_rules)
|
||||
{
|
||||
if (base_rule.HasMember("route") && base_rule["route"].HasMember("rules") && base_rule["route"]["rules"].IsArray())
|
||||
rules.Swap(base_rule["route"]["rules"]);
|
||||
}
|
||||
|
||||
for(RulesetContent &x : ruleset_content_array)
|
||||
{
|
||||
if(global.maxAllowedRules && total_rules > global.maxAllowedRules)
|
||||
break;
|
||||
rule_group = x.rule_group;
|
||||
retrieved_rules = x.rule_content.get();
|
||||
if(retrieved_rules.empty())
|
||||
{
|
||||
writeLog(0, "Failed to fetch ruleset or ruleset is empty: '" + x.rule_path + "'!", LOG_LEVEL_WARNING);
|
||||
continue;
|
||||
}
|
||||
if(startsWith(retrieved_rules, "[]"))
|
||||
{
|
||||
strLine = retrieved_rules.substr(2);
|
||||
if(startsWith(strLine, "FINAL") || startsWith(strLine, "MATCH"))
|
||||
{
|
||||
final = rule_group;
|
||||
continue;
|
||||
}
|
||||
strLine += "," + rule_group;
|
||||
if(count_least(strLine, ',', 3))
|
||||
strLine = regReplace(strLine, "^(.*?,.*?)(,.*)(,.*)$", "$1$3$2");
|
||||
rules.PushBack(transformRuleToSingBox(strLine, allocator), allocator);
|
||||
total_rules++;
|
||||
continue;
|
||||
}
|
||||
retrieved_rules = convertRuleset(retrieved_rules, x.rule_type);
|
||||
char delimiter = getLineBreak(retrieved_rules);
|
||||
|
||||
strStrm.clear();
|
||||
strStrm<<retrieved_rules;
|
||||
std::string::size_type lineSize;
|
||||
while(getline(strStrm, strLine, delimiter))
|
||||
{
|
||||
if(global.maxAllowedRules && total_rules > global.maxAllowedRules)
|
||||
break;
|
||||
strLine = trimWhitespace(strLine, true, true); //remove whitespaces
|
||||
lineSize = strLine.size();
|
||||
if(!lineSize || strLine[0] == ';' || strLine[0] == '#' || (lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')) //empty lines and comments are ignored
|
||||
continue;
|
||||
if(std::none_of(ClashRuleTypes.begin(), ClashRuleTypes.end(), [strLine](const std::string& type){return startsWith(strLine, type);}))
|
||||
continue;
|
||||
if(strFind(strLine, "//"))
|
||||
{
|
||||
strLine.erase(strLine.find("//"));
|
||||
strLine = trimWhitespace(strLine);
|
||||
}
|
||||
strLine += "," + rule_group;
|
||||
if(count_least(strLine, ',', 3))
|
||||
strLine = regReplace(strLine, "^(.*?,.*?)(,.*)(,.*)$", "$1$3$2");
|
||||
rules.PushBack(transformRuleToSingBox(strLine, allocator), allocator);
|
||||
}
|
||||
}
|
||||
|
||||
if (!base_rule.HasMember("route"))
|
||||
base_rule.AddMember("route", rapidjson::Value(rapidjson::kObjectType), allocator);
|
||||
if (!base_rule["route"].HasMember("rules"))
|
||||
base_rule["route"].AddMember("rules", rapidjson::Value(rapidjson::kArrayType), allocator);
|
||||
base_rule["route"]["rules"].Swap(rules);
|
||||
|
||||
if (!base_rule["route"].HasMember("final"))
|
||||
base_rule["route"].AddMember("final", rapidjson::Value(final.c_str(), allocator), allocator);
|
||||
else
|
||||
base_rule["route"]["final"].SetString(final.c_str(), allocator);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <future>
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
#include <rapidjson/document.h>
|
||||
|
||||
#include "../../utils/ini_reader/ini_reader.h"
|
||||
|
||||
@ -31,6 +32,7 @@ struct RulesetContent
|
||||
std::string convertRuleset(const std::string &content, int type);
|
||||
void rulesetToClash(YAML::Node &base_rule, std::vector<RulesetContent> &ruleset_content_array, bool overwrite_original_rules, bool new_field_name);
|
||||
std::string rulesetToClashStr(YAML::Node &base_rule, std::vector<RulesetContent> &ruleset_content_array, bool overwrite_original_rules, bool new_field_name);
|
||||
void rulesetToSurge(INIReader &base_rule, std::vector<RulesetContent> &ruleset_content_array, int surge_ver, bool overwrite_original_rules, std::string remote_path_prefix);
|
||||
void rulesetToSurge(INIReader &base_rule, std::vector<RulesetContent> &ruleset_content_array, int surge_ver, bool overwrite_original_rules, const std::string& remote_path_prefix);
|
||||
void rulesetToSingBox(rapidjson::Document &base_rule, std::vector<RulesetContent> &ruleset_content_array, bool overwrite_original_rules);
|
||||
|
||||
#endif // RULECONVERT_H_INCLUDED
|
||||
|
@ -1817,7 +1817,6 @@ void proxyToMellow(std::vector<Proxy> &nodes, INIReader &ini, std::vector<Rulese
|
||||
|
||||
std::string proxyToLoon(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<RulesetContent> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext)
|
||||
{
|
||||
rapidjson::Document json;
|
||||
INIReader ini;
|
||||
std::string output_nodelist;
|
||||
std::vector<Proxy> nodelist;
|
||||
@ -2042,3 +2041,303 @@ std::string proxyToLoon(std::vector<Proxy> &nodes, const std::string &base_conf,
|
||||
|
||||
return ini.to_string();
|
||||
}
|
||||
|
||||
static std::string formatSingBoxInterval(Integer interval)
|
||||
{
|
||||
std::string result;
|
||||
if(interval >= 3600)
|
||||
{
|
||||
result += std::to_string(interval / 3600) + "h";
|
||||
interval %= 3600;
|
||||
}
|
||||
if(interval >= 60)
|
||||
{
|
||||
result += std::to_string(interval / 60) + "m";
|
||||
interval %= 60;
|
||||
}
|
||||
if(interval > 0)
|
||||
result += std::to_string(interval) + "s";
|
||||
return result;
|
||||
}
|
||||
|
||||
static rapidjson::Value buildV2RayTransport(const Proxy& proxy, rapidjson::MemoryPoolAllocator<>& allocator)
|
||||
{
|
||||
rapidjson::Value transport(rapidjson::kObjectType);
|
||||
switch (hash_(proxy.TransferProtocol))
|
||||
{
|
||||
case "http"_hash:
|
||||
{
|
||||
if (!proxy.Host.empty())
|
||||
transport.AddMember("host", rapidjson::StringRef(proxy.Host.c_str()), allocator);
|
||||
[[fallthrough]];
|
||||
}
|
||||
case "ws"_hash:
|
||||
{
|
||||
transport.AddMember("type", rapidjson::StringRef(proxy.TransferProtocol.c_str()), allocator);
|
||||
if (proxy.Path.empty())
|
||||
transport.AddMember("path", "/", allocator);
|
||||
else
|
||||
transport.AddMember("path", rapidjson::StringRef(proxy.Path.c_str()), allocator);
|
||||
|
||||
rapidjson::Value headers(rapidjson::kObjectType);
|
||||
if (!proxy.Host.empty())
|
||||
headers.AddMember("Host", rapidjson::StringRef(proxy.Host.c_str()), allocator);
|
||||
if (!proxy.Edge.empty())
|
||||
headers.AddMember("Edge", rapidjson::StringRef(proxy.Edge.c_str()), allocator);
|
||||
transport.AddMember("headers", headers, allocator);
|
||||
break;
|
||||
}
|
||||
case "grpc"_hash:
|
||||
{
|
||||
transport.AddMember("type", "grpc", allocator);
|
||||
if (!proxy.Path.empty())
|
||||
transport.AddMember("service_name", rapidjson::StringRef(proxy.Path.c_str()), allocator);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return transport;
|
||||
}
|
||||
|
||||
void proxyToSingBox(std::vector<Proxy> &nodes, rapidjson::Document &json, std::vector<RulesetContent> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext) {
|
||||
rapidjson::Document::AllocatorType &allocator = json.GetAllocator();
|
||||
rapidjson::Value outbounds(rapidjson::kArrayType), route(rapidjson::kArrayType);
|
||||
std::vector<Proxy> nodelist;
|
||||
|
||||
auto direct = buildObject(allocator, "type", "direct", "tag", "DIRECT");
|
||||
outbounds.PushBack(direct, allocator);
|
||||
auto reject = buildObject(allocator, "type", "block", "tag", "REJECT");
|
||||
outbounds.PushBack(reject, allocator);
|
||||
|
||||
for (Proxy &x : nodes)
|
||||
{
|
||||
std::string type = getProxyTypeName(x.Type);
|
||||
if (ext.append_proxy_type)
|
||||
x.Remark = "[" + type + "] " + x.Remark;
|
||||
|
||||
tribool udp = ext.udp, tfo = ext.tfo, scv = ext.skip_cert_verify;
|
||||
udp.define(x.UDP);
|
||||
tfo.define(x.TCPFastOpen);
|
||||
scv.define(x.AllowInsecure);
|
||||
|
||||
rapidjson::Value proxy(rapidjson::kObjectType);
|
||||
switch (x.Type)
|
||||
{
|
||||
case ProxyType::Shadowsocks:
|
||||
{
|
||||
proxy.AddMember("type", "shadowsocks", allocator);
|
||||
proxy.AddMember("tag", rapidjson::StringRef(x.Remark.c_str()), allocator);
|
||||
proxy.AddMember("server", rapidjson::StringRef(x.Hostname.c_str()), allocator);
|
||||
proxy.AddMember("server_port", x.Port, allocator);
|
||||
proxy.AddMember("method", rapidjson::StringRef(x.EncryptMethod.c_str()), allocator);
|
||||
proxy.AddMember("password", rapidjson::StringRef(x.Password.c_str()), allocator);
|
||||
if(!x.Plugin.empty() && !x.PluginOption.empty())
|
||||
{
|
||||
proxy.AddMember("plugin", rapidjson::StringRef(x.Plugin.c_str()), allocator);
|
||||
proxy.AddMember("plugin_opts", rapidjson::StringRef(x.PluginOption.c_str()), allocator);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ProxyType::ShadowsocksR:
|
||||
{
|
||||
proxy.AddMember("type", "shadowsocksr", allocator);
|
||||
proxy.AddMember("tag", rapidjson::StringRef(x.Remark.c_str()), allocator);
|
||||
proxy.AddMember("server", rapidjson::StringRef(x.Hostname.c_str()), allocator);
|
||||
proxy.AddMember("server_port", x.Port, allocator);
|
||||
proxy.AddMember("method", rapidjson::StringRef(x.EncryptMethod.c_str()), allocator);
|
||||
proxy.AddMember("password", rapidjson::StringRef(x.Password.c_str()), allocator);
|
||||
proxy.AddMember("protocol", rapidjson::StringRef(x.Protocol.c_str()), allocator);
|
||||
proxy.AddMember("protocol_param", rapidjson::StringRef(x.ProtocolParam.c_str()), allocator);
|
||||
proxy.AddMember("obfs", rapidjson::StringRef(x.OBFS.c_str()), allocator);
|
||||
proxy.AddMember("obfs_param", rapidjson::StringRef(x.OBFSParam.c_str()), allocator);
|
||||
break;
|
||||
}
|
||||
case ProxyType::VMess:
|
||||
{
|
||||
proxy.AddMember("type", "vmess", allocator);
|
||||
proxy.AddMember("tag", rapidjson::StringRef(x.Remark.c_str()), allocator);
|
||||
proxy.AddMember("server", rapidjson::StringRef(x.Hostname.c_str()), allocator);
|
||||
proxy.AddMember("server_port", x.Port, allocator);
|
||||
proxy.AddMember("uuid", rapidjson::StringRef(x.UserId.c_str()), allocator);
|
||||
proxy.AddMember("alter_id", x.AlterId, allocator);
|
||||
proxy.AddMember("cipher", rapidjson::StringRef(x.EncryptMethod.c_str()), allocator);
|
||||
|
||||
auto transport = buildV2RayTransport(x, allocator);
|
||||
proxy.AddMember("transport", transport, allocator);
|
||||
break;
|
||||
}
|
||||
case ProxyType::Trojan:
|
||||
{
|
||||
proxy.AddMember("type", "trojan", allocator);
|
||||
proxy.AddMember("tag", rapidjson::StringRef(x.Remark.c_str()), allocator);
|
||||
proxy.AddMember("server", rapidjson::StringRef(x.Hostname.c_str()), allocator);
|
||||
proxy.AddMember("server_port", x.Port, allocator);
|
||||
proxy.AddMember("password", rapidjson::StringRef(x.Password.c_str()), allocator);
|
||||
|
||||
auto transport = buildV2RayTransport(x, allocator);
|
||||
proxy.AddMember("transport", transport, allocator);
|
||||
break;
|
||||
}
|
||||
case ProxyType::WireGuard:
|
||||
{
|
||||
proxy.AddMember("type", "wireguard", allocator);
|
||||
proxy.AddMember("tag", rapidjson::StringRef(x.Remark.c_str()), allocator);
|
||||
rapidjson::Value addresses(rapidjson::kArrayType);
|
||||
addresses.PushBack(rapidjson::StringRef(x.SelfIP.c_str()), allocator);
|
||||
if (!x.SelfIPv6.empty())
|
||||
addresses.PushBack(rapidjson::StringRef(x.SelfIPv6.c_str()), allocator);
|
||||
proxy.AddMember("local_address", addresses, allocator);
|
||||
proxy.AddMember("private_key", rapidjson::StringRef(x.PrivateKey.c_str()), allocator);
|
||||
|
||||
rapidjson::Value peer(rapidjson::kObjectType);
|
||||
peer.AddMember("server", rapidjson::StringRef(x.Hostname.c_str()), allocator);
|
||||
peer.AddMember("server_port", x.Port, allocator);
|
||||
peer.AddMember("public_key", rapidjson::StringRef(x.PublicKey.c_str()), allocator);
|
||||
if (!x.PreSharedKey.empty())
|
||||
peer.AddMember("pre_shared_key", rapidjson::StringRef(x.PreSharedKey.c_str()), allocator);
|
||||
|
||||
if (!x.AllowedIPs.empty())
|
||||
{
|
||||
auto allowed = split(x.AllowedIPs, ",");
|
||||
rapidjson::Value allowed_ips(rapidjson::kArrayType);
|
||||
for (const auto &ip: allowed) {
|
||||
allowed_ips.PushBack(rapidjson::Value(trim(ip).c_str(), allocator), allocator);
|
||||
}
|
||||
peer.AddMember("allowed_ips", allowed_ips, allocator);
|
||||
}
|
||||
|
||||
if (!x.ClientId.empty())
|
||||
{
|
||||
auto client_id = split(x.ClientId, ",");
|
||||
rapidjson::Value reserved(rapidjson::kArrayType);
|
||||
for (const auto &id : client_id)
|
||||
{
|
||||
reserved.PushBack(to_int(trim(id)), allocator);
|
||||
}
|
||||
peer.AddMember("reserved", reserved, allocator);
|
||||
}
|
||||
|
||||
rapidjson::Value peers(rapidjson::kArrayType);
|
||||
peers.PushBack(peer, allocator);
|
||||
proxy.AddMember("peers", peers, allocator);
|
||||
proxy.AddMember("mtu", x.Mtu, allocator);
|
||||
break;
|
||||
}
|
||||
case ProxyType::HTTP:
|
||||
case ProxyType::HTTPS:
|
||||
{
|
||||
proxy.AddMember("type", "http", allocator);
|
||||
proxy.AddMember("tag", rapidjson::StringRef(x.Remark.c_str()), allocator);
|
||||
proxy.AddMember("server", rapidjson::StringRef(x.Hostname.c_str()), allocator);
|
||||
proxy.AddMember("server_port", x.Port, allocator);
|
||||
if (x.TLSSecure)
|
||||
{
|
||||
proxy.AddMember("username", rapidjson::StringRef(x.Username.c_str()), allocator);
|
||||
proxy.AddMember("password", rapidjson::StringRef(x.Password.c_str()), allocator);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ProxyType::SOCKS5:
|
||||
{
|
||||
proxy.AddMember("type", "socks", allocator);
|
||||
proxy.AddMember("tag", rapidjson::StringRef(x.Remark.c_str()), allocator);
|
||||
proxy.AddMember("server", rapidjson::StringRef(x.Hostname.c_str()), allocator);
|
||||
proxy.AddMember("port", x.Port, allocator);
|
||||
proxy.AddMember("version", 5, allocator);
|
||||
proxy.AddMember("username", rapidjson::StringRef(x.Username.c_str()), allocator);
|
||||
proxy.AddMember("password", rapidjson::StringRef(x.Password.c_str()), allocator);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
if (x.TLSSecure)
|
||||
{
|
||||
rapidjson::Value tls(rapidjson::kObjectType);
|
||||
tls.AddMember("enable", true, allocator);
|
||||
if (!x.ServerName.empty())
|
||||
tls.AddMember("server_name", rapidjson::StringRef(x.ServerName.c_str()), allocator);
|
||||
tls.AddMember("insecure", buildBooleanValue(scv), allocator);
|
||||
proxy.AddMember("tls", tls, allocator);
|
||||
}
|
||||
if (!udp.is_undef() && !udp)
|
||||
{
|
||||
proxy.AddMember("network", "tcp", allocator);
|
||||
}
|
||||
if (!tfo.is_undef())
|
||||
{
|
||||
proxy.AddMember("tcp_fast_open", buildBooleanValue(tfo), allocator);
|
||||
}
|
||||
nodelist.push_back(x);
|
||||
outbounds.PushBack(proxy, allocator);
|
||||
}
|
||||
for (const ProxyGroupConfig& x: extra_proxy_group)
|
||||
{
|
||||
string_array filtered_nodelist;
|
||||
std::string type;
|
||||
switch (x.Type)
|
||||
{
|
||||
case ProxyGroupType::Select:
|
||||
{
|
||||
type = "selector";
|
||||
break;
|
||||
}
|
||||
case ProxyGroupType::URLTest:
|
||||
case ProxyGroupType::Fallback:
|
||||
case ProxyGroupType::LoadBalance:
|
||||
{
|
||||
type = "urltest";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
for (const auto &y : x.Proxies)
|
||||
groupGenerate(y, nodelist, filtered_nodelist, true, ext);
|
||||
|
||||
if (filtered_nodelist.empty())
|
||||
filtered_nodelist.emplace_back("DIRECT");
|
||||
|
||||
rapidjson::Value group(rapidjson::kObjectType);
|
||||
|
||||
group.AddMember("type", rapidjson::Value(type.c_str(), allocator), allocator);
|
||||
group.AddMember("tag", rapidjson::Value(x.Name.c_str(), allocator), allocator);
|
||||
|
||||
rapidjson::Value group_outbounds(rapidjson::kArrayType);
|
||||
for (const std::string& y: filtered_nodelist)
|
||||
{
|
||||
group_outbounds.PushBack(rapidjson::Value(y.c_str(), allocator), allocator);
|
||||
}
|
||||
group.AddMember("outbounds", group_outbounds, allocator);
|
||||
|
||||
if (x.Type == ProxyGroupType::URLTest)
|
||||
{
|
||||
group.AddMember("url", rapidjson::Value(x.Url.c_str(), allocator), allocator);
|
||||
group.AddMember("interval", rapidjson::Value(formatSingBoxInterval(x.Interval).c_str(), allocator), allocator);
|
||||
if (x.Tolerance > 0)
|
||||
group.AddMember("tolerance", x.Tolerance, allocator);
|
||||
}
|
||||
|
||||
outbounds.PushBack(group, allocator);
|
||||
}
|
||||
if (json.HasMember("outbounds"))
|
||||
json.RemoveMember("outbounds");
|
||||
json.AddMember("outbounds", outbounds, allocator);
|
||||
}
|
||||
|
||||
std::string proxyToSingBox(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<RulesetContent> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext)
|
||||
{
|
||||
rapidjson::Document json;
|
||||
json.Parse(base_conf.data());
|
||||
if(json.HasParseError())
|
||||
{
|
||||
writeLog(0, "sing-box base loader failed with error: " + std::string(rapidjson::GetParseError_En(json.GetParseError())), LOG_LEVEL_ERROR);
|
||||
return "";
|
||||
}
|
||||
|
||||
proxyToSingBox(nodes, json, ruleset_content_array, extra_proxy_group, ext);
|
||||
rulesetToSingBox(json, ruleset_content_array, ext.overwrite_original_rules);
|
||||
|
||||
return SerializeObject(json);
|
||||
}
|
||||
|
@ -37,11 +37,11 @@ struct extra_settings
|
||||
tribool skip_cert_verify = tribool();
|
||||
tribool tls13 = tribool();
|
||||
bool clash_classical_ruleset = false;
|
||||
std::string sort_script = "";
|
||||
std::string sort_script;
|
||||
std::string clash_proxies_style = "flow";
|
||||
bool authorized = false;
|
||||
|
||||
extra_settings() {};
|
||||
extra_settings() = default;
|
||||
extra_settings(const extra_settings&) = delete;
|
||||
extra_settings(extra_settings&&) = delete;
|
||||
|
||||
@ -57,9 +57,6 @@ struct extra_settings
|
||||
#endif // NO_JS_RUNTIME
|
||||
};
|
||||
|
||||
void rulesetToClash(YAML::Node &base_rule, std::vector<RulesetContent> &ruleset_content_array, bool overwrite_original_rules, bool new_field_name);
|
||||
void rulesetToSurge(INIReader &base_rule, std::vector<RulesetContent> &ruleset_content_array, int surge_ver, bool overwrite_original_rules, std::string remote_path_prefix);
|
||||
|
||||
std::string proxyToClash(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<RulesetContent> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, bool clashR, extra_settings &ext);
|
||||
void proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const ProxyGroupConfigs &extra_proxy_group, bool clashR, extra_settings &ext);
|
||||
std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<RulesetContent> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, int surge_ver, extra_settings &ext);
|
||||
@ -73,5 +70,6 @@ void proxyToQuanX(std::vector<Proxy> &nodes, INIReader &ini, std::vector<Ruleset
|
||||
std::string proxyToQuan(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<RulesetContent> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext);
|
||||
void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<RulesetContent> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext);
|
||||
std::string proxyToSSD(std::vector<Proxy> &nodes, std::string &group, std::string &userinfo, extra_settings &ext);
|
||||
std::string proxyToSingBox(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<RulesetContent> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext);
|
||||
|
||||
#endif // SUBEXPORT_H_INCLUDED
|
||||
|
@ -89,30 +89,28 @@ const std::vector<UAProfile> UAMatchList = {
|
||||
{"V2RayX","","","v2ray"}
|
||||
};
|
||||
|
||||
bool verGreaterEqual(const std::string &src_ver, const std::string &target_ver)
|
||||
bool verGreaterEqual(const std::string& src_ver, const std::string& target_ver)
|
||||
{
|
||||
string_size src_pos_beg = 0, src_pos_end, target_pos_beg = 0, target_pos_end;
|
||||
while(true)
|
||||
{
|
||||
src_pos_end = src_ver.find('.', src_pos_beg);
|
||||
if(src_pos_end == src_ver.npos)
|
||||
src_pos_end = src_ver.size();
|
||||
int part_src = std::stoi(src_ver.substr(src_pos_beg, src_pos_end - src_pos_beg));
|
||||
target_pos_end = target_ver.find('.', target_pos_beg);
|
||||
if(target_pos_end == target_ver.npos)
|
||||
target_pos_end = target_ver.size();
|
||||
int part_target = std::stoi(target_ver.substr(target_pos_beg, target_pos_end - target_pos_beg));
|
||||
if(part_src > part_target)
|
||||
break;
|
||||
else if(part_src < part_target)
|
||||
return false;
|
||||
else if(src_pos_end >= src_ver.size() - 1 || target_pos_end >= target_ver.size() - 1)
|
||||
break;
|
||||
src_pos_beg = src_pos_end + 1;
|
||||
target_pos_beg = target_pos_end + 1;
|
||||
std::istringstream src_stream(src_ver), target_stream(target_ver);
|
||||
int src_part, target_part;
|
||||
char dot;
|
||||
while (src_stream >> src_part) {
|
||||
if (target_stream >> target_part) {
|
||||
if (src_part < target_part) {
|
||||
return false;
|
||||
} else if (src_part > target_part) {
|
||||
return true;
|
||||
}
|
||||
// Skip the dot separator in both streams
|
||||
src_stream >> dot;
|
||||
target_stream >> dot;
|
||||
} else {
|
||||
// If we run out of target parts, the source version is greater only if it has more parts
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
// If we get here, the common parts are equal, so check if target_ver has more parts
|
||||
return !bool(target_stream >> target_part);
|
||||
}
|
||||
|
||||
void matchUserAgent(const std::string &user_agent, std::string &target, tribool &clash_new_name, int &surge_ver)
|
||||
@ -321,7 +319,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
case "ss"_hash: case "ssd"_hash: case "ssr"_hash: case "sssub"_hash: case "v2ray"_hash: case "trojan"_hash: case "mixed"_hash:
|
||||
lSimpleSubscription = true;
|
||||
break;
|
||||
case "clash"_hash: case "clashr"_hash: case "surge"_hash: case "quan"_hash: case "quanx"_hash: case "loon"_hash: case "surfboard"_hash: case "mellow"_hash:
|
||||
case "clash"_hash: case "clashr"_hash: case "surge"_hash: case "quan"_hash: case "quanx"_hash: case "loon"_hash: case "surfboard"_hash: case "mellow"_hash: case "singbox"_hash:
|
||||
break;
|
||||
default:
|
||||
*status_code = 400;
|
||||
@ -363,6 +361,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
/// for external configuration
|
||||
std::string lClashBase = global.clashBase, lSurgeBase = global.surgeBase, lMellowBase = global.mellowBase, lSurfboardBase = global.surfboardBase;
|
||||
std::string lQuanBase = global.quanBase, lQuanXBase = global.quanXBase, lLoonBase = global.loonBase, lSSSubBase = global.SSSubBase;
|
||||
std::string lSingBoxBase = global.singBoxBase;
|
||||
|
||||
/// validate urls
|
||||
argEnableInsert.define(global.enableInsert);
|
||||
@ -458,6 +457,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
checkExternalBase(extconf.quan_rule_base, lQuanBase);
|
||||
checkExternalBase(extconf.quanx_rule_base, lQuanXBase);
|
||||
checkExternalBase(extconf.loon_rule_base, lLoonBase);
|
||||
checkExternalBase(extconf.singbox_rule_base, lSingBoxBase);
|
||||
|
||||
if(extconf.surge_ruleset.size())
|
||||
lCustomRulesets = extconf.surge_ruleset;
|
||||
@ -877,6 +877,22 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
if(argUpload)
|
||||
uploadGist("ssd", argUploadPath, output_content, false);
|
||||
break;
|
||||
case "singbox"_hash:
|
||||
writeLog(0, "Generate target: sing-box", LOG_LEVEL_INFO);
|
||||
if(!ext.nodelist)
|
||||
{
|
||||
if(render_template(fetchFile(lSingBoxBase, proxy, global.cacheConfig), tpl_args, base_content, global.templatePath) != 0)
|
||||
{
|
||||
*status_code = 400;
|
||||
return base_content;
|
||||
}
|
||||
}
|
||||
|
||||
output_content = proxyToSingBox(nodes, base_content, lRulesetContent, lCustomProxyGroups, ext);
|
||||
|
||||
if(argUpload)
|
||||
uploadGist("singbox", argUploadPath, output_content, false);
|
||||
break;
|
||||
default:
|
||||
writeLog(0, "Generate target: Unspecified", LOG_LEVEL_INFO);
|
||||
*status_code = 500;
|
||||
|
@ -331,6 +331,7 @@ void readYAMLConf(YAML::Node &node)
|
||||
section["quanx_rule_base"] >> global.quanXBase;
|
||||
section["loon_rule_base"] >> global.loonBase;
|
||||
section["sssub_rule_base"] >> global.SSSubBase;
|
||||
section["singbox_rule_base"] >> global.singBoxBase;
|
||||
|
||||
section["default_external_config"] >> global.defaultExtConfig;
|
||||
section["append_proxy_type"] >> global.appendType;
|
||||
@ -605,6 +606,8 @@ void readTOMLConf(toml::value &root)
|
||||
"quan_rule_base", global.quanBase,
|
||||
"quanx_rule_base", global.quanXBase,
|
||||
"loon_rule_base", global.loonBase,
|
||||
"sssub_rule_base", global.SSSubBase,
|
||||
"singbox_rule_base", global.singBoxBase,
|
||||
"proxy_config", global.proxyConfig,
|
||||
"proxy_ruleset", global.proxyRuleset,
|
||||
"proxy_subscription", global.proxySubscription,
|
||||
@ -842,6 +845,8 @@ void readConf()
|
||||
ini.get_if_exist("quan_rule_base", global.quanBase);
|
||||
ini.get_if_exist("quanx_rule_base", global.quanXBase);
|
||||
ini.get_if_exist("loon_rule_base", global.loonBase);
|
||||
ini.get_if_exist("sssub_rule_base", global.SSSubBase);
|
||||
ini.get_if_exist("singbox_rule_base", global.singBoxBase);
|
||||
ini.get_if_exist("default_external_config", global.defaultExtConfig);
|
||||
ini.get_bool_if_exist("append_proxy_type", global.appendType);
|
||||
ini.get_if_exist("proxy_config", global.proxyConfig);
|
||||
@ -1074,6 +1079,7 @@ int loadExternalYAML(YAML::Node &node, ExternalConfig &ext)
|
||||
section["quanx_rule_base"] >> ext.quanx_rule_base;
|
||||
section["loon_rule_base"] >> ext.loon_rule_base;
|
||||
section["sssub_rule_base"] >> ext.sssub_rule_base;
|
||||
section["singbox_rule_base"] >> ext.singbox_rule_base;
|
||||
|
||||
section["enable_rule_generator"] >> ext.enable_rule_generator;
|
||||
section["overwrite_original_rules"] >> ext.overwrite_original_rules;
|
||||
@ -1146,7 +1152,9 @@ int loadExternalTOML(toml::value &root, ExternalConfig &ext)
|
||||
"mellow_rule_base", ext.mellow_rule_base,
|
||||
"quan_rule_base", ext.quan_rule_base,
|
||||
"quanx_rule_base", ext.quanx_rule_base,
|
||||
"loon_rule_base", ext.loon_rule_base,
|
||||
"sssub_rule_base", ext.sssub_rule_base,
|
||||
"singbox_rule_base", ext.singbox_rule_base,
|
||||
"add_emoji", ext.add_emoji,
|
||||
"remove_old_emoji", ext.remove_old_emoji,
|
||||
"include_remarks", ext.include,
|
||||
@ -1248,6 +1256,7 @@ int loadExternalConfig(std::string &path, ExternalConfig &ext)
|
||||
ini.get_if_exist("quanx_rule_base", ext.quanx_rule_base);
|
||||
ini.get_if_exist("loon_rule_base", ext.loon_rule_base);
|
||||
ini.get_if_exist("sssub_rule_base", ext.sssub_rule_base);
|
||||
ini.get_if_exist("singbox_rule_base", ext.singbox_rule_base);
|
||||
|
||||
ini.get_bool_if_exist("overwrite_original_rules", ext.overwrite_original_rules);
|
||||
ini.get_bool_if_exist("enable_rule_generator", ext.enable_rule_generator);
|
||||
|
@ -54,7 +54,7 @@ struct Settings
|
||||
|
||||
std::string clashBase;
|
||||
ProxyGroupConfigs customProxyGroups;
|
||||
std::string surgeBase, surfboardBase, mellowBase, quanBase, quanXBase, loonBase, SSSubBase;
|
||||
std::string surgeBase, surfboardBase, mellowBase, quanBase, quanXBase, loonBase, SSSubBase, singBoxBase;
|
||||
std::string surgeSSRPath, quanXDevID;
|
||||
|
||||
//cache system
|
||||
@ -83,6 +83,7 @@ struct ExternalConfig
|
||||
std::string quanx_rule_base;
|
||||
std::string loon_rule_base;
|
||||
std::string sssub_rule_base;
|
||||
std::string singbox_rule_base;
|
||||
RegexMatchConfigs rename;
|
||||
RegexMatchConfigs emoji;
|
||||
string_array include;
|
||||
|
12
src/main.cpp
12
src/main.cpp
@ -1,7 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <csignal>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
@ -29,7 +29,7 @@ WebServer webServer;
|
||||
#ifndef _WIN32
|
||||
void SetConsoleTitle(const std::string &title)
|
||||
{
|
||||
system(std::string("echo \"\\033]0;" + title + "\\007\\c\"").data());
|
||||
system(std::string("echo \"\\033]0;" + title + R"(\007\c")").data());
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
@ -46,10 +46,10 @@ void setcd(std::string &file)
|
||||
strrchr(szTemp, '\\')[1] = '\0';
|
||||
#else
|
||||
char *ret = realpath(file.data(), szTemp);
|
||||
if(ret == NULL)
|
||||
if(ret == nullptr)
|
||||
return;
|
||||
ret = strcpy(filename, strrchr(szTemp, '/') + 1);
|
||||
if(ret == NULL)
|
||||
if(ret == nullptr)
|
||||
return;
|
||||
strrchr(szTemp, '/')[1] = '\0';
|
||||
#endif // _WIN32
|
||||
@ -84,7 +84,7 @@ void chkArg(int argc, char *argv[])
|
||||
else if(strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--log") == 0)
|
||||
{
|
||||
if(i < argc - 1)
|
||||
if(freopen(argv[++i], "a", stderr) == NULL)
|
||||
if(freopen(argv[++i], "a", stderr) == nullptr)
|
||||
std::cerr<<"Error redirecting output to file.\n";
|
||||
}
|
||||
}
|
||||
@ -293,7 +293,7 @@ int main(int argc, char *argv[])
|
||||
//webServer.append_response("GET", "/list-profiles", "text/plain;charset=utf-8", listProfiles);
|
||||
|
||||
std::string env_port = getEnv("PORT");
|
||||
if(env_port.size())
|
||||
if(!env_port.empty())
|
||||
global.listenPort = to_int(env_port, global.listenPort);
|
||||
listener_args args = {global.listenAddress, global.listenPort, global.maxPendingConns, global.maxConcurThreads, cron_tick_caller, 200};
|
||||
//std::cout<<"Serving HTTP @ http://"<<listen_address<<":"<<listen_port<<std::endl;
|
||||
|
@ -13,12 +13,28 @@ int script_context_init(qjs::Context &context);
|
||||
int script_cleanup(qjs::Context &context);
|
||||
void script_print_stack(qjs::Context &context);
|
||||
|
||||
inline JSValue JS_NewString(JSContext *ctx, const std::string& str)
|
||||
{
|
||||
return JS_NewStringLen(ctx, str.c_str(), str.size());
|
||||
}
|
||||
|
||||
inline std::string JS_GetPropertyToString(JSContext *ctx, JSValue v, const char* prop)
|
||||
{
|
||||
auto val = JS_GetPropertyStr(ctx, v, prop);
|
||||
auto valData = JS_ToCString(ctx, val);
|
||||
std::string result = valData;
|
||||
JS_FreeCString(ctx, valData);
|
||||
size_t len;
|
||||
const char *str = JS_ToCStringLen(ctx, &len, val);
|
||||
std::string result(str, len);
|
||||
JS_FreeCString(ctx, str);
|
||||
JS_FreeValue(ctx, val);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::string JS_GetPropertyToString(JSContext *ctx, JSValueConst obj, uint32_t index) {
|
||||
JSValue val = JS_GetPropertyUint32(ctx, obj, index);
|
||||
size_t len;
|
||||
const char *str = JS_ToCStringLen(ctx, &len, val);
|
||||
std::string result(str, len);
|
||||
JS_FreeCString(ctx, str);
|
||||
JS_FreeValue(ctx, val);
|
||||
return result;
|
||||
}
|
||||
@ -33,7 +49,7 @@ inline int JS_GetPropertyToInt32(JSContext *ctx, JSValue v, const char* prop, in
|
||||
return result;
|
||||
}
|
||||
|
||||
inline int JS_GetPropertyToUInt32(JSContext *ctx, JSValue v, const char* prop, uint32_t def_value = 0)
|
||||
inline uint32_t JS_GetPropertyToUInt32(JSContext *ctx, JSValue v, const char* prop, uint32_t def_value = 0)
|
||||
{
|
||||
uint32_t result = def_value;
|
||||
auto val = JS_GetPropertyStr(ctx, v, prop);
|
||||
@ -85,79 +101,98 @@ namespace qjs
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct js_traits<StringArray> {
|
||||
static StringArray unwrap(JSContext *ctx, JSValueConst v) {
|
||||
StringArray arr;
|
||||
uint32_t length = JS_GetPropertyToUInt32(ctx, v, "length");
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
arr.push_back(JS_GetPropertyToString(ctx, v, i));
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
static JSValue wrap(JSContext *ctx, const StringArray& arr) {
|
||||
JSValue jsArray = JS_NewArray(ctx);
|
||||
for (std::size_t i = 0; i < arr.size(); i++) {
|
||||
JS_SetPropertyUint32(ctx, jsArray, i, JS_NewString(ctx, arr[i]));
|
||||
}
|
||||
return jsArray;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct js_traits<Proxy>
|
||||
{
|
||||
static JSValue wrap(JSContext *ctx, const Proxy &n) noexcept
|
||||
{
|
||||
auto obj = JS_NewObject(ctx);
|
||||
/*
|
||||
JS_SetPropertyStr(ctx, obj, "LinkType", JS_NewInt32(ctx, n.linkType));
|
||||
JS_SetPropertyStr(ctx, obj, "ID", JS_NewInt32(ctx, n.id));
|
||||
JS_SetPropertyStr(ctx, obj, "GroupID", JS_NewInt32(ctx, n.groupID));
|
||||
JS_SetPropertyStr(ctx, obj, "Group", JS_NewStringLen(ctx, n.group.c_str(), n.group.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Remark", JS_NewStringLen(ctx, n.remarks.c_str(), n.remarks.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Server", JS_NewStringLen(ctx, n.server.c_str(), n.server.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Port", JS_NewInt32(ctx, n.port));
|
||||
JS_SetPropertyStr(ctx, obj, "ProxyInfo", JS_NewStringLen(ctx, n.proxyStr.c_str(), n.proxyStr.size()));
|
||||
*/
|
||||
JS_SetPropertyStr(ctx, obj, "Type", JS_NewInt32(ctx, n.Type));
|
||||
JS_SetPropertyStr(ctx, obj, "Id", JS_NewInt32(ctx, n.Id));
|
||||
JS_SetPropertyStr(ctx, obj, "GroupId", JS_NewInt32(ctx, n.GroupId));
|
||||
JS_SetPropertyStr(ctx, obj, "Group", JS_NewStringLen(ctx, n.Group.c_str(), n.Group.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Remark", JS_NewStringLen(ctx, n.Remark.c_str(), n.Remark.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Server", JS_NewStringLen(ctx, n.Hostname.c_str(), n.Hostname.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Port", JS_NewInt32(ctx, n.Port));
|
||||
JSValue obj = JS_NewObjectProto(ctx, JS_NULL);
|
||||
if (JS_IsException(obj)) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
JS_SetPropertyStr(ctx, obj, "Username", JS_NewStringLen(ctx, n.Username.c_str(), n.Username.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Password", JS_NewStringLen(ctx, n.Password.c_str(), n.Password.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "EncryptMethod", JS_NewStringLen(ctx, n.EncryptMethod.c_str(), n.EncryptMethod.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Plugin", JS_NewStringLen(ctx, n.Plugin.c_str(), n.Plugin.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "PluginOption", JS_NewStringLen(ctx, n.PluginOption.c_str(), n.PluginOption.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Protocol", JS_NewStringLen(ctx, n.Protocol.c_str(), n.Protocol.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "ProtocolParam", JS_NewStringLen(ctx, n.ProtocolParam.c_str(), n.ProtocolParam.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "OBFS", JS_NewStringLen(ctx, n.OBFS.c_str(), n.OBFS.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "OBFSParam", JS_NewStringLen(ctx, n.OBFSParam.c_str(), n.OBFSParam.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "UserId", JS_NewStringLen(ctx, n.UserId.c_str(), n.UserId.size()));
|
||||
JS_DefinePropertyValueStr(ctx, obj, "Type", JS_NewInt32(ctx, n.Type), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "Id", JS_NewUint32(ctx, n.Id), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "GroupId", JS_NewUint32(ctx, n.GroupId), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "Group", JS_NewString(ctx, n.Group), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "Remark", JS_NewString(ctx, n.Remark), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "Server", JS_NewString(ctx, n.Hostname), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "Port", JS_NewInt32(ctx, n.Port), JS_PROP_C_W_E);
|
||||
|
||||
JS_SetPropertyStr(ctx, obj, "AlterId", JS_NewInt32(ctx, n.AlterId));
|
||||
JS_SetPropertyStr(ctx, obj, "TransferProtocol", JS_NewStringLen(ctx, n.TransferProtocol.c_str(), n.TransferProtocol.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "FakeType", JS_NewStringLen(ctx, n.FakeType.c_str(), n.FakeType.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "TLSSecure", JS_NewBool(ctx, n.TLSSecure));
|
||||
JS_DefinePropertyValueStr(ctx, obj, "Username", JS_NewString(ctx, n.Username), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "Password", JS_NewString(ctx, n.Password), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "EncryptMethod", JS_NewString(ctx, n.EncryptMethod), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "Plugin", JS_NewString(ctx, n.Plugin), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "PluginOption", JS_NewString(ctx, n.PluginOption), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "Protocol", JS_NewString(ctx, n.Protocol), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "ProtocolParam", JS_NewString(ctx, n.ProtocolParam), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "OBFS", JS_NewString(ctx, n.OBFS), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "OBFSParam", JS_NewString(ctx, n.OBFSParam), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "UserId", JS_NewString(ctx, n.UserId), JS_PROP_C_W_E);
|
||||
|
||||
JS_SetPropertyStr(ctx, obj, "Host", JS_NewStringLen(ctx, n.Host.c_str(), n.Host.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Path", JS_NewStringLen(ctx, n.Path.c_str(), n.Path.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "Edge", JS_NewStringLen(ctx, n.Edge.c_str(), n.Edge.size()));
|
||||
JS_DefinePropertyValueStr(ctx, obj, "AlterId", JS_NewInt32(ctx, n.AlterId), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "TransferProtocol", JS_NewString(ctx, n.TransferProtocol), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "FakeType", JS_NewString(ctx, n.FakeType), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "TLSSecure", JS_NewBool(ctx, n.TLSSecure), JS_PROP_C_W_E);
|
||||
|
||||
JS_SetPropertyStr(ctx, obj, "QUICSecure", JS_NewStringLen(ctx, n.QUICSecure.c_str(), n.QUICSecure.size()));
|
||||
JS_SetPropertyStr(ctx, obj, "QUICSecret", JS_NewStringLen(ctx, n.QUICSecret.c_str(), n.QUICSecret.size()));
|
||||
JS_DefinePropertyValueStr(ctx, obj, "Host", JS_NewString(ctx, n.Host), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "Path", JS_NewString(ctx, n.Path), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "Edge", JS_NewString(ctx, n.Edge), JS_PROP_C_W_E);
|
||||
|
||||
JS_SetPropertyStr(ctx, obj, "UDP", js_traits<tribool>::wrap(ctx, n.UDP));
|
||||
JS_SetPropertyStr(ctx, obj, "TCPFastOpen", js_traits<tribool>::wrap(ctx, n.TCPFastOpen));
|
||||
JS_SetPropertyStr(ctx, obj, "AllowInsecure", js_traits<tribool>::wrap(ctx, n.AllowInsecure));
|
||||
JS_SetPropertyStr(ctx, obj, "TLS13", js_traits<tribool>::wrap(ctx, n.TLS13));
|
||||
JS_DefinePropertyValueStr(ctx, obj, "QUICSecure", JS_NewString(ctx, n.QUICSecure), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "QUICSecret", JS_NewString(ctx, n.QUICSecret), JS_PROP_C_W_E);
|
||||
|
||||
JS_DefinePropertyValueStr(ctx, obj, "UDP", js_traits<tribool>::wrap(ctx, n.UDP), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "TCPFastOpen", js_traits<tribool>::wrap(ctx, n.TCPFastOpen), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "AllowInsecure", js_traits<tribool>::wrap(ctx, n.AllowInsecure), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "TLS13", js_traits<tribool>::wrap(ctx, n.TLS13), JS_PROP_C_W_E);
|
||||
|
||||
JS_DefinePropertyValueStr(ctx, obj, "SnellVersion", JS_NewInt32(ctx, n.SnellVersion), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "ServerName", JS_NewString(ctx, n.ServerName), JS_PROP_C_W_E);
|
||||
|
||||
JS_DefinePropertyValueStr(ctx, obj, "SelfIP", JS_NewString(ctx, n.SelfIP), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "SelfIPv6", JS_NewString(ctx, n.SelfIPv6), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "PublicKey", JS_NewString(ctx, n.PublicKey), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "PrivateKey", JS_NewString(ctx, n.PrivateKey), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "PreSharedKey", JS_NewString(ctx, n.PreSharedKey), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "DnsServers", js_traits<StringArray>::wrap(ctx, n.DnsServers), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "Mtu", JS_NewUint32(ctx, n.Mtu), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "AllowedIPs", JS_NewString(ctx, n.AllowedIPs), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "KeepAlive", JS_NewUint32(ctx, n.KeepAlive), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "TestUrl", JS_NewString(ctx, n.TestUrl), JS_PROP_C_W_E);
|
||||
JS_DefinePropertyValueStr(ctx, obj, "ClientId", JS_NewString(ctx, n.ClientId), JS_PROP_C_W_E);
|
||||
return obj;
|
||||
}
|
||||
|
||||
static Proxy unwrap(JSContext *ctx, JSValueConst v)
|
||||
{
|
||||
Proxy node;
|
||||
/*
|
||||
node.linkType = JS_GetPropertyToInt32(ctx, v, "LinkType");
|
||||
node.id = JS_GetPropertyToInt32(ctx, v, "ID");
|
||||
node.groupID = JS_GetPropertyToInt32(ctx, v, "GroupID");
|
||||
node.group = JS_GetPropertyToString(ctx, v, "Group");
|
||||
node.remarks = JS_GetPropertyToString(ctx, v, "Remark");
|
||||
node.server = JS_GetPropertyToString(ctx, v, "Server");
|
||||
node.port = JS_GetPropertyToInt32(ctx, v, "Port");
|
||||
node.proxyStr = JS_GetPropertyToString(ctx, v, "ProxyInfo");
|
||||
*/
|
||||
node.Type = JS_GetPropertyToInt32(ctx, v, "Type");
|
||||
node.Id = JS_GetPropertyToInt32(ctx, v, "Id");
|
||||
node.GroupId = JS_GetPropertyToInt32(ctx, v, "GroupId");
|
||||
node.Group = JS_GetPropertyToString(ctx, v, "Group");
|
||||
node.Remark = JS_GetPropertyToString(ctx, v, "Remark");
|
||||
node.Hostname = JS_GetPropertyToString(ctx, v, "Hostname");
|
||||
node.Hostname = JS_GetPropertyToString(ctx, v, "Server");
|
||||
node.Port = JS_GetPropertyToUInt32(ctx, v, "Port");
|
||||
|
||||
node.Username = JS_GetPropertyToString(ctx, v, "Username");
|
||||
@ -187,6 +222,21 @@ namespace qjs
|
||||
node.AllowInsecure = js_traits<tribool>::JS_GetPropertyToTriBool(ctx, v, "AllowInsecure");
|
||||
node.TLS13 = js_traits<tribool>::JS_GetPropertyToTriBool(ctx, v, "TLS13");
|
||||
|
||||
node.SnellVersion = JS_GetPropertyToInt32(ctx, v, "SnellVersion");
|
||||
node.ServerName = JS_GetPropertyToString(ctx, v, "ServerName");
|
||||
|
||||
node.SelfIP = JS_GetPropertyToString(ctx, v, "SelfIP");
|
||||
node.SelfIPv6 = JS_GetPropertyToString(ctx, v, "SelfIPv6");
|
||||
node.PublicKey = JS_GetPropertyToString(ctx, v, "PublicKey");
|
||||
node.PrivateKey = JS_GetPropertyToString(ctx, v, "PrivateKey");
|
||||
node.PreSharedKey = JS_GetPropertyToString(ctx, v, "PreSharedKey");
|
||||
node.DnsServers = js_traits<StringArray>::unwrap(ctx, JS_GetPropertyStr(ctx, v, "DnsServers"));
|
||||
node.Mtu = JS_GetPropertyToUInt32(ctx, v, "Mtu");
|
||||
node.AllowedIPs = JS_GetPropertyToString(ctx, v, "AllowedIPs");
|
||||
node.KeepAlive = JS_GetPropertyToUInt32(ctx, v, "KeepAlive");
|
||||
node.TestUrl = JS_GetPropertyToString(ctx, v, "TestUrl");
|
||||
node.ClientId = JS_GetPropertyToString(ctx, v, "ClientId");
|
||||
|
||||
return node;
|
||||
}
|
||||
};
|
||||
|
@ -74,7 +74,7 @@ void file_not_found(std::string arguments, int sock)
|
||||
std::string response = "HTTP/1.1 404 Not Found\r\n"
|
||||
"Content-Type: text/plain\r\nConnection: close\r\n"
|
||||
"Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: *\r\n"
|
||||
"Content-Length: " + std::__cxx11::to_string(prompt_info.size()) + "\r\n\r\n" + prompt_info + "\r\n";
|
||||
"Content-Length: " + std::to_string(prompt_info.size()) + "\r\n\r\n" + prompt_info + "\r\n";
|
||||
|
||||
if (sendall(sock, response) == -1)
|
||||
{
|
||||
@ -124,7 +124,7 @@ void serve_options(SOCKET sock)
|
||||
void serve_content(SOCKET sock, std::string type, std::string content)
|
||||
{
|
||||
send_header(sock, type.data());
|
||||
std::string extra_header = "Content-Length: " + std::__cxx11::to_string(content.size()) + "\r\n";
|
||||
std::string extra_header = "Content-Length: " + std::to_string(content.size()) + "\r\n";
|
||||
sendall(sock, extra_header);
|
||||
send(sock, "\r\n", 2, 0);
|
||||
if (sendall(sock, content) == -1)
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <event2/http.h>
|
||||
#include <curl/curlver.h>
|
||||
|
||||
#include "../utils/map_extra.h"
|
||||
@ -69,10 +68,7 @@ public:
|
||||
int start_web_server(void *argv);
|
||||
int start_web_server_multi(void *argv);
|
||||
void stop_web_server();
|
||||
private:
|
||||
int serveFile(const std::string &filename, std::string &content_type, std::string &return_data);
|
||||
inline int process_request(Request &request, Response &response, std::string &return_data);
|
||||
void on_request(evhttp_request *req, void *args);
|
||||
|
||||
std::vector<responseRoute> responses;
|
||||
string_map redirect_map;
|
||||
};
|
||||
|
223
src/server/webserver_httplib.cpp
Normal file
223
src/server/webserver_httplib.cpp
Normal file
@ -0,0 +1,223 @@
|
||||
#include <string>
|
||||
#ifdef MALLOC_TRIM
|
||||
#include <malloc.h>
|
||||
#endif // MALLOC_TRIM
|
||||
#include "httplib.h"
|
||||
|
||||
#include "../utils/base64/base64.h"
|
||||
#include "../utils/logger.h"
|
||||
#include "../utils/string_hash.h"
|
||||
#include "../utils/stl_extra.h"
|
||||
#include "../utils/urlencode.h"
|
||||
#include "webserver.h"
|
||||
|
||||
int WebServer::start_web_server(void *argv)
|
||||
{
|
||||
return start_web_server_multi(argv);
|
||||
}
|
||||
|
||||
void WebServer::stop_web_server()
|
||||
{
|
||||
SERVER_EXIT_FLAG = true;
|
||||
}
|
||||
|
||||
static httplib::Server::Handler makeHandler(const responseRoute &rr)
|
||||
{
|
||||
return [rr](const httplib::Request &request, httplib::Response &response)
|
||||
{
|
||||
Request req;
|
||||
Response resp;
|
||||
req.method = request.method;
|
||||
req.url = request.path;
|
||||
for (auto &h: request.headers)
|
||||
{
|
||||
req.headers[h.first] = h.second;
|
||||
}
|
||||
for (auto &p: request.params)
|
||||
{
|
||||
req.argument += p.first + "=" + p.second + "&";
|
||||
}
|
||||
req.argument.pop_back();
|
||||
req.postdata = request.body;
|
||||
if (request.get_header_value("Content-Type") == "application/x-www-form-urlencoded")
|
||||
{
|
||||
req.postdata = urlDecode(req.postdata);
|
||||
}
|
||||
auto result = rr.rc(req, resp);
|
||||
response.status = resp.status_code;
|
||||
for (auto &h: resp.headers) {
|
||||
response.set_header(h.first, h.second);
|
||||
}
|
||||
auto content_type = resp.content_type;
|
||||
if (content_type.empty())
|
||||
{
|
||||
content_type = rr.content_type;
|
||||
}
|
||||
response.set_content(result, content_type);
|
||||
};
|
||||
}
|
||||
|
||||
int WebServer::start_web_server_multi(void *argv)
|
||||
{
|
||||
httplib::Server server;
|
||||
auto *args = (listener_args *)argv;
|
||||
for (auto &x : responses)
|
||||
{
|
||||
switch (hash_(x.method))
|
||||
{
|
||||
case "GET"_hash: case "HEAD"_hash:
|
||||
server.Get(x.path, makeHandler(x));
|
||||
break;
|
||||
case "POST"_hash:
|
||||
server.Post(x.path, makeHandler(x));
|
||||
break;
|
||||
case "PUT"_hash:
|
||||
server.Put(x.path, makeHandler(x));
|
||||
break;
|
||||
case "DELETE"_hash:
|
||||
server.Delete(x.path, makeHandler(x));
|
||||
break;
|
||||
case "PATCH"_hash:
|
||||
server.Patch(x.path, makeHandler(x));
|
||||
break;
|
||||
}
|
||||
}
|
||||
server.Options(R"(.*)", [&](const httplib::Request &req, httplib::Response &res) {
|
||||
auto path = req.path;
|
||||
std::string allowed;
|
||||
for (auto &rr : responses) {
|
||||
if (rr.path == path)
|
||||
{
|
||||
allowed += rr.method + ",";
|
||||
}
|
||||
}
|
||||
if (!allowed.empty())
|
||||
{
|
||||
allowed.pop_back();
|
||||
res.status = 200;
|
||||
res.set_header("Access-Control-Allow-Methods", allowed);
|
||||
res.set_header("Access-Control-Allow-Origin", "*");
|
||||
res.set_header("Access-Control-Allow-Headers", "Content-Type,Authorization");
|
||||
}
|
||||
});
|
||||
server.set_pre_routing_handler([&](const httplib::Request &req, httplib::Response &res) {
|
||||
std::string params;
|
||||
for (auto &p: req.params)
|
||||
{
|
||||
params += p.first + "=" + p.second + "&";
|
||||
}
|
||||
if (!params.empty())
|
||||
{
|
||||
params.pop_back();
|
||||
params = "?" + params;
|
||||
}
|
||||
writeLog(0, "Accept connection from client " + req.remote_addr + ":" + std::to_string(req.remote_port), LOG_LEVEL_DEBUG);
|
||||
writeLog(0, "handle_cmd: " + req.method + " handle_uri: " + req.path + params, LOG_LEVEL_VERBOSE);
|
||||
|
||||
if (req.has_header("SubConverter-Request"))
|
||||
{
|
||||
res.status = 500;
|
||||
res.set_content("Loop request detected!", "text/plain");
|
||||
return httplib::Server::HandlerResponse::Handled;
|
||||
}
|
||||
res.set_header("Server", "subconverter/" VERSION " cURL/" LIBCURL_VERSION);
|
||||
if (require_auth)
|
||||
{
|
||||
static std::string auth_token = "Basic " + base64Encode(auth_user + ":" + auth_password);
|
||||
auto auth = req.get_header_value("Authorization");
|
||||
if (auth != auth_token)
|
||||
{
|
||||
res.status = 401;
|
||||
res.set_header("WWW-Authenticate", "Basic realm=" + auth_realm + ", charset=\"UTF-8\"");
|
||||
res.set_content("Unauthorized", "text/plain");
|
||||
return httplib::Server::HandlerResponse::Handled;
|
||||
}
|
||||
}
|
||||
res.set_header("X-Client-IP", req.remote_addr);
|
||||
if (req.has_header("Access-Control-Request-Headers"))
|
||||
{
|
||||
res.set_header("Access-Control-Allow-Headers", req.get_header_value("Access-Control-Request-Headers"));
|
||||
}
|
||||
return httplib::Server::HandlerResponse::Unhandled;
|
||||
});
|
||||
for (auto &x : redirect_map)
|
||||
{
|
||||
server.Get(x.first, [x](const httplib::Request &req, httplib::Response &res) {
|
||||
res.set_redirect(x.second);
|
||||
});
|
||||
}
|
||||
server.set_exception_handler([](const httplib::Request &req, httplib::Response &res, const std::exception_ptr &e) {
|
||||
try
|
||||
{
|
||||
std::rethrow_exception(e);
|
||||
}
|
||||
catch (const httplib::Error &err)
|
||||
{
|
||||
res.set_content(to_string(err), "text/plain");
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
std::string params;
|
||||
for (auto &p: req.params)
|
||||
{
|
||||
params += p.first + "=" + p.second + "&";
|
||||
}
|
||||
params.pop_back();
|
||||
std::string return_data = "Internal server error while processing request path '" + req.path + "' with arguments '" + params + "'!\n";
|
||||
return_data += "\n exception: ";
|
||||
return_data += type(ex);
|
||||
return_data += "\n what(): ";
|
||||
return_data += ex.what();
|
||||
res.set_content(return_data, "text/plain");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
res.status = 500;
|
||||
}
|
||||
});
|
||||
if (serve_file)
|
||||
{
|
||||
server.set_mount_point("/", serve_file_root);
|
||||
}
|
||||
server.bind_to_port(args->listen_address, args->port, 0);
|
||||
|
||||
pthread_t tid;
|
||||
pthread_create(&tid, nullptr, [](void *args) -> void * {
|
||||
auto *server = (httplib::Server *)args;
|
||||
server->listen_after_bind();
|
||||
return nullptr;
|
||||
}, &server);
|
||||
|
||||
while (!SERVER_EXIT_FLAG)
|
||||
{
|
||||
if (args->looper_callback)
|
||||
{
|
||||
args->looper_callback();
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(args->looper_interval));
|
||||
}
|
||||
|
||||
server.stop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void WebServer::append_response(const std::string &method, const std::string &uri, const std::string &content_type, response_callback response)
|
||||
{
|
||||
responseRoute rr;
|
||||
rr.method = method;
|
||||
rr.path = uri;
|
||||
rr.content_type = content_type;
|
||||
rr.rc = response;
|
||||
responses.emplace_back(std::move(rr));
|
||||
}
|
||||
|
||||
void WebServer::append_redirect(const std::string &uri, const std::string &target)
|
||||
{
|
||||
redirect_map[uri] = target;
|
||||
}
|
||||
|
||||
void WebServer::reset_redirect()
|
||||
{
|
||||
eraseElements(redirect_map);
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <evhttp.h>
|
||||
#include <atomic>
|
||||
#ifdef MALLOC_TRIM
|
||||
@ -8,13 +7,11 @@
|
||||
#endif // MALLOC_TRIM
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
#include <pthread.h>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
|
||||
#include "../utils/base64/base64.h"
|
||||
#include "../utils/file_extra.h"
|
||||
@ -65,7 +62,7 @@ bool matchSpaceSeparatedList(const std::string& source, const std::string &targe
|
||||
while(pos_begin < total)
|
||||
{
|
||||
pos_end = source.find(' ', pos_begin);
|
||||
if(pos_end == source.npos)
|
||||
if(pos_end == std::string::npos)
|
||||
pos_end = total;
|
||||
if(source.compare(pos_begin, pos_end - pos_begin, target) == 0)
|
||||
return true;
|
||||
@ -76,12 +73,12 @@ bool matchSpaceSeparatedList(const std::string& source, const std::string &targe
|
||||
|
||||
std::string checkMIMEType(const std::string &filename)
|
||||
{
|
||||
string_size name_begin = 0, name_end = 0;
|
||||
string_size name_begin, name_end;
|
||||
name_begin = filename.rfind('/');
|
||||
if(name_begin == filename.npos)
|
||||
if(name_begin == std::string::npos)
|
||||
name_begin = 0;
|
||||
name_end = filename.rfind('.');
|
||||
if(name_end == filename.npos || name_end < name_begin || name_end == filename.size() - 1)
|
||||
if(name_end == std::string::npos || name_end < name_begin || name_end == filename.size() - 1)
|
||||
return "application/octet-stream";
|
||||
std::string extension = filename.substr(name_end + 1);
|
||||
for(MIME_type &x : mime_types)
|
||||
@ -90,10 +87,10 @@ std::string checkMIMEType(const std::string &filename)
|
||||
return "application/octet-stream";
|
||||
}
|
||||
|
||||
int WebServer::serveFile(const std::string &filename, std::string &content_type, std::string &return_data)
|
||||
int serveFile(WebServer *server, const std::string &filename, std::string &content_type, std::string &return_data)
|
||||
{
|
||||
std::string realname = serve_file_root + filename;
|
||||
if(filename.compare("/") == 0)
|
||||
std::string realname = server->serve_file_root + filename;
|
||||
if(filename == "/")
|
||||
realname += "index.html";
|
||||
if(!fileExist(realname))
|
||||
return 1;
|
||||
@ -115,12 +112,12 @@ static inline void buffer_cleanup(struct evbuffer *eb)
|
||||
#endif // MALLOC_TRIM
|
||||
}
|
||||
|
||||
inline int WebServer::process_request(Request &request, Response &response, std::string &return_data)
|
||||
inline int process_request(WebServer *server, Request &request, Response &response, std::string &return_data)
|
||||
{
|
||||
writeLog(0, "handle_cmd: " + request.method + " handle_uri: " + request.url, LOG_LEVEL_VERBOSE);
|
||||
|
||||
string_size pos = request.url.find("?");
|
||||
if(pos != request.url.npos)
|
||||
string_size pos = request.url.find('?');
|
||||
if(pos != std::string::npos)
|
||||
{
|
||||
request.argument = request.url.substr(pos + 1);
|
||||
request.url.erase(pos);
|
||||
@ -128,13 +125,13 @@ inline int WebServer::process_request(Request &request, Response &response, std:
|
||||
|
||||
if(request.method == "OPTIONS")
|
||||
{
|
||||
for(responseRoute &x : responses)
|
||||
for(responseRoute &x : server->responses)
|
||||
if(matchSpaceSeparatedList(replaceAllDistinct(request.postdata, ",", ""), x.method) && x.path == request.url)
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for(responseRoute &x : responses)
|
||||
for(responseRoute &x : server->responses)
|
||||
{
|
||||
if(x.method == request.method && x.path == request.url)
|
||||
{
|
||||
@ -159,13 +156,13 @@ inline int WebServer::process_request(Request &request, Response &response, std:
|
||||
}
|
||||
}
|
||||
|
||||
auto iter = redirect_map.find(request.url);
|
||||
if(iter != redirect_map.end())
|
||||
auto iter = server->redirect_map.find(request.url);
|
||||
if(iter != server->redirect_map.end())
|
||||
{
|
||||
return_data = iter->second;
|
||||
if(request.argument.size())
|
||||
if(!request.argument.empty())
|
||||
{
|
||||
if(return_data.find("?") != return_data.npos)
|
||||
if(return_data.find('?') != std::string::npos)
|
||||
return_data += "&" + request.argument;
|
||||
else
|
||||
return_data += "?" + request.argument;
|
||||
@ -173,19 +170,19 @@ inline int WebServer::process_request(Request &request, Response &response, std:
|
||||
return 2;
|
||||
}
|
||||
|
||||
if(serve_file)
|
||||
if(server->serve_file)
|
||||
{
|
||||
if(request.method.compare("GET") == 0 && serveFile(request.url, response.content_type, return_data) == 0)
|
||||
if(request.method == "GET" && serveFile(server, request.url, response.content_type, return_data) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void WebServer::on_request(evhttp_request *req, void *args)
|
||||
void on_request(evhttp_request *req, void *args)
|
||||
{
|
||||
(void)args;
|
||||
static std::string auth_token = "Basic " + base64Encode(auth_user + ":" + auth_password);
|
||||
auto server = (WebServer*) args;
|
||||
static std::string auth_token = "Basic " + base64Encode(server->auth_user + ":" + server->auth_password);
|
||||
const char *req_content_type = evhttp_find_header(req->input_headers, "Content-Type"), *req_ac_method = evhttp_find_header(req->input_headers, "Access-Control-Request-Method");
|
||||
const char *uri = req->uri, *internal_flag = evhttp_find_header(req->input_headers, "SubConverter-Request");
|
||||
|
||||
@ -201,12 +198,12 @@ void WebServer::on_request(evhttp_request *req, void *args)
|
||||
return;
|
||||
}
|
||||
|
||||
if (require_auth)
|
||||
if (server->require_auth)
|
||||
{
|
||||
const char *auth = evhttp_find_header(req->input_headers, "Authorization");
|
||||
if (auth == nullptr || auth_token != auth)
|
||||
{
|
||||
evhttp_add_header(req->output_headers, "WWW-Authenticate", ("Basic realm=\"" + auth_realm + "\"").data());
|
||||
evhttp_add_header(req->output_headers, "WWW-Authenticate", ("Basic realm=\"" + server->auth_realm + "\"").data());
|
||||
auto buffer = evhttp_request_get_output_buffer(req);
|
||||
evbuffer_add_printf(buffer, "Unauthorized");
|
||||
evhttp_send_reply(req, 401, nullptr, buffer);
|
||||
@ -252,7 +249,7 @@ void WebServer::on_request(evhttp_request *req, void *args)
|
||||
request.headers.emplace("X-Client-IP", client_ip);
|
||||
|
||||
std::string return_data;
|
||||
int retVal = process_request(request, response, return_data);
|
||||
int retVal = process_request(server, request, response, return_data);
|
||||
std::string &content_type = response.content_type;
|
||||
|
||||
auto *output_buffer = evhttp_request_get_output_buffer(req);
|
||||
@ -277,7 +274,7 @@ void WebServer::on_request(evhttp_request *req, void *args)
|
||||
evhttp_send_reply(req, HTTP_MOVETEMP, nullptr, nullptr);
|
||||
break;
|
||||
case 0: //found normal
|
||||
if (content_type.size())
|
||||
if (!content_type.empty())
|
||||
evhttp_add_header(req->output_headers, "Content-Type", content_type.c_str());
|
||||
evhttp_add_header(req->output_headers, "Access-Control-Allow-Origin", "*");
|
||||
evhttp_add_header(req->output_headers, "Connection", "close");
|
||||
@ -298,7 +295,7 @@ void WebServer::on_request(evhttp_request *req, void *args)
|
||||
|
||||
int WebServer::start_web_server(void *argv)
|
||||
{
|
||||
struct listener_args *args = reinterpret_cast<listener_args*>(argv);
|
||||
auto *args = reinterpret_cast<listener_args*>(argv);
|
||||
std::string listen_address = args->listen_address;
|
||||
int port = args->port;
|
||||
if (!event_init())
|
||||
@ -320,7 +317,7 @@ int WebServer::start_web_server(void *argv)
|
||||
auto call_on_request = [&](evhttp_request *req, void *args) { on_request(req, args); };
|
||||
|
||||
evhttp_set_allowed_methods(server.get(), EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS | EVHTTP_REQ_PUT | EVHTTP_REQ_PATCH | EVHTTP_REQ_DELETE | EVHTTP_REQ_HEAD);
|
||||
evhttp_set_gencb(server.get(), wrap(call_on_request), nullptr);
|
||||
evhttp_set_gencb(server.get(), wrap(call_on_request), this);
|
||||
evhttp_set_timeout(server.get(), 30);
|
||||
if (event_dispatch() == -1)
|
||||
{
|
||||
@ -360,7 +357,7 @@ int httpserver_bindsocket(std::string listen_address, int listen_port, int backl
|
||||
}
|
||||
#endif
|
||||
|
||||
struct sockaddr_in addr;
|
||||
struct sockaddr_in addr {};
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = inet_addr(listen_address.data());
|
||||
@ -380,7 +377,7 @@ int httpserver_bindsocket(std::string listen_address, int listen_port, int backl
|
||||
|
||||
int WebServer::start_web_server_multi(void *argv)
|
||||
{
|
||||
struct listener_args *args = reinterpret_cast<listener_args*>(argv);
|
||||
auto *args = reinterpret_cast<listener_args*>(argv);
|
||||
std::string listen_address = args->listen_address;
|
||||
int port = args->port, nthreads = args->max_workers, max_conn = args->max_conn;
|
||||
|
||||
@ -404,7 +401,7 @@ int WebServer::start_web_server_multi(void *argv)
|
||||
return -1;
|
||||
|
||||
evhttp_set_allowed_methods(httpd, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS | EVHTTP_REQ_PUT | EVHTTP_REQ_PATCH | EVHTTP_REQ_DELETE | EVHTTP_REQ_HEAD);
|
||||
evhttp_set_gencb(httpd, wrap(call_on_request), nullptr);
|
||||
evhttp_set_gencb(httpd, wrap(call_on_request), this);
|
||||
evhttp_set_timeout(httpd, 30);
|
||||
if (pthread_create(&ths[i], nullptr, httpserver_dispatch, base[i]) != 0)
|
||||
return -1;
|
||||
|
@ -72,5 +72,26 @@ inline std::string SerializeObject(const rapidjson::Value& value)
|
||||
return sb.GetString();
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
inline rapidjson::Value buildObject(rapidjson::MemoryPoolAllocator<> & allocator, Args... kvs)
|
||||
{
|
||||
static_assert(sizeof...(kvs) % 2 == 0, "buildObject requires an even number of arguments");
|
||||
static_assert((std::is_same<Args, const char*>::value && ...), "buildObject requires all arguments to be const char*");
|
||||
rapidjson::Value ret(rapidjson::kObjectType);
|
||||
auto args = {kvs...};
|
||||
auto it = args.begin();
|
||||
while (it != args.end())
|
||||
{
|
||||
const char *key = *it++, *value = *it++;
|
||||
ret.AddMember(rapidjson::StringRef(key), rapidjson::StringRef(value), allocator);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline rapidjson::Value buildBooleanValue(bool value)
|
||||
{
|
||||
return value ? rapidjson::Value(rapidjson::kTrueType) : rapidjson::Value(rapidjson::kFalseType);
|
||||
}
|
||||
|
||||
|
||||
#endif // RAPIDJSON_EXTRA_H_INCLUDED
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include <string>
|
||||
#include <stdarg.h>
|
||||
#include <cstdarg>
|
||||
|
||||
/*
|
||||
#ifdef USE_STD_REGEX
|
||||
@ -193,7 +193,7 @@ std::vector<std::string> regGetAllMatch(const std::string &src, const std::strin
|
||||
reg.setPattern(match).addModifier("m").addPcre2Option(PCRE2_UTF|PCRE2_ALT_BSUX).compile();
|
||||
jp::VecNum vec_num;
|
||||
jp::RegexMatch rm;
|
||||
size_t count = rm.setRegexObject(®).setSubject(src).setNumberedSubstringVector(&vec_num).setModifier("g").match();
|
||||
size_t count = rm.setRegexObject(®).setSubject(src).setNumberedSubstringVector(&vec_num).setModifier("gm").match();
|
||||
std::vector<std::string> result;
|
||||
if(!count)
|
||||
return result;
|
||||
@ -209,6 +209,7 @@ std::vector<std::string> regGetAllMatch(const std::string &src, const std::strin
|
||||
{
|
||||
match_index++;
|
||||
index = begin;
|
||||
continue;
|
||||
}
|
||||
result.push_back(std::move(vec_num[match_index][index]));
|
||||
index++;
|
||||
@ -220,5 +221,5 @@ std::vector<std::string> regGetAllMatch(const std::string &src, const std::strin
|
||||
|
||||
std::string regTrim(const std::string &src)
|
||||
{
|
||||
return regReplace(src, "^\\s*([\\s\\S]*)\\s*$", "$1", false, false);
|
||||
return regReplace(src, R"(^\s*([\s\S]*)\s*$)", "$1", false, false);
|
||||
}
|
||||
|
@ -8,105 +8,102 @@
|
||||
|
||||
class tribool
|
||||
{
|
||||
private:
|
||||
|
||||
char _M_VALUE = 0;
|
||||
|
||||
public:
|
||||
tribool() : value_(indeterminate) {}
|
||||
tribool(bool value) : value_(value ? true_value : false_value) {}
|
||||
tribool(const std::string& str) { set(str); }
|
||||
|
||||
tribool() { clear(); }
|
||||
tribool(const tribool& other) = default;
|
||||
tribool& operator=(const tribool& other) = default;
|
||||
|
||||
template <typename T> tribool(const T &value) { set(value); }
|
||||
|
||||
tribool(const tribool &value) { _M_VALUE = value._M_VALUE; }
|
||||
|
||||
~tribool() = default;
|
||||
|
||||
tribool& operator=(const tribool &src)
|
||||
tribool& operator=(bool value)
|
||||
{
|
||||
_M_VALUE = src._M_VALUE;
|
||||
value_ = value ? true_value : false_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T> tribool& operator=(const T &value)
|
||||
bool operator==(const tribool& other) const { return value_ == other.value_; }
|
||||
|
||||
operator bool() const { return value_ == true_value; }
|
||||
|
||||
bool is_undef() const { return value_ == indeterminate; }
|
||||
|
||||
template <typename T> tribool& define(const T& value)
|
||||
{
|
||||
set(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool operator==(const tribool& rhs){ return _M_VALUE == rhs._M_VALUE; }
|
||||
|
||||
operator bool() const { return _M_VALUE == 3; }
|
||||
|
||||
bool is_undef() const { return _M_VALUE <= 1; }
|
||||
|
||||
template <typename T> tribool& define(const T &value)
|
||||
{
|
||||
if(_M_VALUE <= 1)
|
||||
if (is_undef())
|
||||
*this = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T> tribool& parse(const T &value)
|
||||
template <typename T> tribool& parse(const T& value)
|
||||
{
|
||||
return define(value);
|
||||
}
|
||||
|
||||
tribool reverse()
|
||||
{
|
||||
if(_M_VALUE > 1)
|
||||
_M_VALUE = _M_VALUE > 2 ? 2 : 3;
|
||||
if (value_ == false_value)
|
||||
value_ = true_value;
|
||||
else if (value_ == true_value)
|
||||
value_ = false_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool get(const bool &def_value = false) const
|
||||
bool get(const bool& def_value = false) const
|
||||
{
|
||||
if(_M_VALUE <= 1)
|
||||
if (is_undef())
|
||||
return def_value;
|
||||
return _M_VALUE == 3;
|
||||
return value_ == true_value;
|
||||
}
|
||||
|
||||
std::string get_str() const
|
||||
{
|
||||
switch(_M_VALUE)
|
||||
switch (value_)
|
||||
{
|
||||
case 2:
|
||||
return "false";
|
||||
case 3:
|
||||
return "true";
|
||||
case indeterminate:
|
||||
return "undef";
|
||||
case false_value:
|
||||
return "false";
|
||||
case true_value:
|
||||
return "true";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
return "undef";
|
||||
}
|
||||
|
||||
template <typename T> bool set(const T &value)
|
||||
template <typename T> bool set(const T& value)
|
||||
{
|
||||
_M_VALUE = (bool)value + 2;
|
||||
return _M_VALUE > 2;
|
||||
value_ = (bool)value ? true_value : false_value;
|
||||
return value_;
|
||||
}
|
||||
|
||||
bool set(const std::string &str)
|
||||
bool set(const std::string& str)
|
||||
{
|
||||
switch(hash_(str))
|
||||
switch (hash_(str))
|
||||
{
|
||||
case "true"_hash:
|
||||
case "1"_hash:
|
||||
_M_VALUE = 3;
|
||||
break;
|
||||
case "false"_hash:
|
||||
case "0"_hash:
|
||||
_M_VALUE = 2;
|
||||
break;
|
||||
default:
|
||||
if(to_int(str, 0) > 1)
|
||||
_M_VALUE = 3;
|
||||
else
|
||||
_M_VALUE = 0;
|
||||
break;
|
||||
case "true"_hash:
|
||||
case "1"_hash:
|
||||
value_ = true_value;
|
||||
break;
|
||||
case "false"_hash:
|
||||
case "0"_hash:
|
||||
value_ = false_value;
|
||||
break;
|
||||
default:
|
||||
if (to_int(str, 0) > 1)
|
||||
value_ = true_value;
|
||||
else
|
||||
value_ = indeterminate;
|
||||
break;
|
||||
}
|
||||
return _M_VALUE;
|
||||
return !is_undef();
|
||||
}
|
||||
|
||||
void clear() { _M_VALUE = 0; }
|
||||
void clear() { value_ = indeterminate; }
|
||||
|
||||
private:
|
||||
enum value_type : char { indeterminate = 0, false_value = 1, true_value = 2 };
|
||||
value_type value_;
|
||||
};
|
||||
|
||||
#endif // TRIBOOL_H_INCLUDED
|
||||
|
Loading…
Reference in New Issue
Block a user