Breaking changes

Fix data URIs not correctly parsed.
Fix downloading files may be caught in infinite redirect loops.
Add experimental JavaScript support for providing subscriptions, filtering nodes, sorting nodes and generating groups.
Add tag support for adding group name to a specific link.
Add complete matcher support for Emoji and rename match rule.
Optimize codes.
Update build scripts.
This commit is contained in:
Tindy X
2020-05-29 19:15:05 +08:00
parent 8385792fec
commit f30b3c7cbf
19 changed files with 650 additions and 249 deletions

View File

@@ -101,6 +101,10 @@ TARGET_LINK_LIBRARIES(subconverter ${YAML_CPP_LIBRARY})
ADD_DEFINITIONS(-DPCRE2_STATIC)
#ENDIF()
FIND_PACKAGE(Duktape REQUIRED)
INCLUDE_DIRECTORIES(${DUKTAPE_INCLUDE_DIRS})
TARGET_LINK_LIBRARIES(subconverter ${DUKTAPE_LIBRARIES})
IF(WIN32)
TARGET_LINK_LIBRARIES(subconverter wsock32 ws2_32)
ELSE()

View File

@@ -7,6 +7,8 @@ common:
prepend_insert_url: true
exclude_remarks: ["(到期|剩余流量|时间|官网|产品)"]
include_remarks: []
enable_filter: false
filter_script: ""
default_external_config: "" # config/example_external_config.yml
base_path: base
clash_rule_base: base/all_base.tpl
@@ -39,6 +41,7 @@ node_pref:
# udp_flag: false
# tcp_fast_open_flag: false
sort_flag: false
sort_script: ""
# skip_cert_verify_flag: false
filter_deprecated_nodes: false
append_sub_userinfo: true

View File

@@ -22,6 +22,13 @@ exclude_remarks=(到期|剩余流量|时间|官网|产品)
;Only include nodes which remarks match the following patterns. Supports regular expression.
;include_remarks=V3.*港
;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. Replace all line break with "\n".
; 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
;default_external_config=config/example_external_config.ini
@@ -84,7 +91,12 @@ time_rule=^.*?流量:(?:.*?) 剩:(.*)$|left=$1d
[node_pref]
;udp_flag=false
;tcp_fast_open_flag=false
sort_flag=false
;Script used for sorting nodes. A "compare" function with 2 arguments which are the 2 nodes to be compared should be defined in the script. Supports inline script and script path.
;Examples can be seen at the filter_script option in [common] section.
;sort_script=function compare(node_a, node_b) {\n const info_a = JSON.parse(node_a.ProxyInfo);\n const info_b = JSON.parse(node_b.ProxyInfo);\n return info_a.Remark > info_b.Remark;\n}
;skip_cert_verify_flag=false
filter_deprecated_nodes=false
append_sub_userinfo=true
@@ -152,7 +164,7 @@ update_ruleset_on_request=false
surge_ruleset=!!import:snippets/rulesets.txt
[clash_proxy_group]
;Generate Clash Proxy Group with the following patterns. Node filterting rule supports regular expression.
;Generate Clash Proxy Group with the following patterns. Node filtering rule supports regular expression.
;Format: Group_Name`select`Rule_1`Rule_2`...
; Group_Name`url-test|fallback|load-balance`Rule_1`Rule_2`...`test_url`interval[,timeout][,tolerance]
;Rule with "[]" prefix will be added directly.
@@ -170,6 +182,9 @@ surge_ruleset=!!import:snippets/rulesets.txt
;custom_proxy_group=g1hk`select`!!GROUPID=0!!(HGC|HKBN|PCCW|HKT|hk|港)
;custom_proxy_group=sstw`select`!!GROUP=V2RayProvider!!(深台|彰化|新北|台|tw)
;Also supports using script for filtering nodes. A "filter" function with one argument which is an array of all available nodes should be defined in the script.
;custom_proxy_group=script`select`script:/path/to/script.js
;for forcerule.yml
;custom_proxy_group=!!import:snippets/groups_forcerule.txt

12
cmake/FindDuktape.cmake Normal file
View File

@@ -0,0 +1,12 @@
find_path(DUKTAPE_INCLUDE_DIRS duktape.h)
find_library(DUKTAPE_LIBRARY duktape)
find_library(DUKTAPE_MODULE_LIBRARY duktape_module)
set(DUKTAPE_LIBRARIES "${DUKTAPE_LIBRARY} ${DUKTAPE_MODULE_LIBRARY}")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(DUKTAPE DEFAULT_MSG
DUKTAPE_INCLUDE_DIRS DUKTAPE_LIBRARY DUKTAPE_MODULE_LIBRARY)
mark_as_advanced(DUKTAPE_INCLUDE_DIRS DUKTAPE_LIBRARY)

View File

@@ -3,7 +3,20 @@ MAINTAINER Tindy X <tindy.it@gmail.com>
# build minimized
RUN apk add git g++ build-base linux-headers cmake && \
apk add libressl-dev curl-dev rapidjson-dev libevent-dev pcre2-dev yaml-cpp-dev && \
apk add libressl-dev curl-dev rapidjson-dev libevent-dev pcre2-dev yaml-cpp-dev python2 py2-pip && \
git clone https://github.com/svaarala/duktape --depth=1 && \
cd duktape && \
pip2 install PyYAML && \
python2 util/dist.py && \
cd dist/src && \
cc -c -O3 -o duktape.o duktape.c && \
cc -c -O3 -o duk_module_node.o -I. ../extras/module-node/duk_module_node.c && \
ar cr libduktape.a duktape.o && \
ar cr libduktape_module.a duk_module_node.o && \
install -m0644 *.a /usr/lib && \
install -m0644 duk*.h /usr/include && \
install -m0644 ../extras/module-node/duk_module_node.h /usr/include && \
cd ../../.. && \
git clone https://github.com/tindy2013/subconverter && \
cd subconverter && \
cmake . && \
@@ -13,7 +26,7 @@ RUN apk add git g++ build-base linux-headers cmake && \
cd .. && \
rm -rf subconverter && \
apk add pcre2 libcurl yaml-cpp libevent && \
apk del git gcc g++ build-base linux-headers cmake libressl-dev curl-dev rapidjson-dev libevent-dev pcre2-dev yaml-cpp-dev
apk del git gcc g++ build-base linux-headers cmake libressl-dev curl-dev rapidjson-dev libevent-dev pcre2-dev yaml-cpp-dev python2 py2-pip
# set entry
WORKDIR /base

View File

@@ -1,15 +1,29 @@
#!/bin/bash
set -xe
apk add gcc g++ build-base linux-headers cmake make autoconf automake libtool
apk add gcc g++ build-base linux-headers cmake make autoconf automake libtool python2 py2-pip
apk add openssl-dev openssl-libs-static curl curl-dev curl-static nghttp2-static zlib-dev rapidjson-dev libevent-dev libevent-static zlib-static pcre2-dev bzip2-static
git clone https://github.com/jbeder/yaml-cpp
git clone https://github.com/jbeder/yaml-cpp --depth=1
cd yaml-cpp
cmake -DYAML_CPP_BUILD_TESTS=OFF -DYAML_CPP_BUILD_TOOLS=OFF . > /dev/null
make install -j2 > /dev/null
cd ..
git clone https://github.com/svaarala/duktape --depth=1
cd duktape
pip2 install PyYAML
python2 util/dist.py
cd dist/src
cc -c -O3 -o duktape.o duktape.c
cc -c -O3 -o duk_module_node.o -I. ../extras/module-node/duk_module_node.c
ar cr libduktape.a duktape.o
ar cr libduktape_module.a duk_module_node.o
install -m0644 *.a /usr/lib
install -m0644 duk*.h /usr/include
install -m0644 ../extras/module-node/duk_module_node.h /usr/include
cd ../../..
cmake .
make -j2
rm subconverter

View File

@@ -15,6 +15,6 @@ c++ -std=c++17 -Wall -fexceptions -c src/upload.cpp -o obj/upload.o
c++ -std=c++17 -Wall -fexceptions -c src/templates.cpp -o obj/templates.o
c++ -std=c++17 -Wall -fexceptions -c src/webget.cpp -o obj/webget.o
c++ -std=c++17 -Wall -fexceptions -c src/webserver_libevent.cpp -o obj/webserver_libevent.o
c++ -o subconverter obj/*.o -lpcre2-8 -levent -lpthread -lyaml-cpp -lcurl -lssl -lcrypto -lz -O3 -s
c++ -o subconverter obj/*.o -lpcre2-8 -levent -lpthread -lyaml-cpp -lcurl -lssl -lcrypto -lz -O3 -s -lduktape -lduktape_module
chmod +x subconverter

View File

@@ -3,7 +3,7 @@ set -xe
brew reinstall rapidjson libevent zlib pcre2 bzip2 libssh2 pkgconfig
git clone https://github.com/curl/curl
git clone https://github.com/curl/curl --depth=1
cd curl
#./buildconf > /dev/null
#./configure --with-ssl=/usr/local/opt/openssl@1.1 --without-mbedtls --disable-ldap --disable-ldaps --disable-rtsp --without-libidn2 > /dev/null
@@ -11,14 +11,29 @@ cmake -DHTTP_ONLY=ON -DBUILD_TESTING=OFF -DBUILD_SHARED_LIBS=OFF -DOPENSSL_ROOT_
make -j8 > /dev/null
cd ..
git clone https://github.com/jbeder/yaml-cpp
git clone https://github.com/jbeder/yaml-cpp --depth=1
cd yaml-cpp
cmake -DYAML_CPP_BUILD_TESTS=OFF -DYAML_CPP_BUILD_TOOLS=OFF . > /dev/null
make install -j8 > /dev/null
cd ..
git clone https://github.com/svaarala/duktape --depth=1
cd duktape
pip2 install PyYAML
python2 util/dist.py
cd dist/src
cc -c -O3 -o duktape.o duktape.c
cc -c -O3 -o duk_module_node.o -I. ../extras/module-node/duk_module_node.c
ar cr libduktape.a duktape.o
ar cr libduktape_module.a duk_module_node.o
install -m0644 *.a /usr/lib
install -m0644 duk*.h /usr/include
install -m0644 ../extras/module-node/duk_module_node.h /usr/include
cd ../../..
cp curl/lib/libcurl.a .
cp yaml-cpp/libyaml-cpp.a .
cp duktape/dist/src/*.a .
cp /usr/local/lib/libevent.a .
cp /usr/local/opt/zlib/lib/libz.a .
cp /usr/local/opt/openssl@1.1/lib/libssl.a .
@@ -31,7 +46,7 @@ export CMAKE_CXX_FLAGS="-I/usr/local/include -I/usr/local/opt/openssl@1.1/includ
cmake -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl@1.1 .
make -j8
rm subconverter
c++ -Xlinker -unexported_symbol -Xlinker "*" -o base/subconverter CMakeFiles/subconverter.dir/src/*.o libpcre2-8.a libevent.a libcurl.a libz.a libssl.a libcrypto.a libyaml-cpp.a libbz2.a libssh2.a -ldl -lpthread -O3
c++ -Xlinker -unexported_symbol -Xlinker "*" -o base/subconverter CMakeFiles/subconverter.dir/src/*.o libpcre2-8.a libevent.a libcurl.a libz.a libssl.a libcrypto.a libyaml-cpp.a libbz2.a libssh2.a libduktape.a libduktape_module.a -ldl -lpthread -O3
cd base
chmod +rx subconverter

View File

@@ -3,15 +3,29 @@ set -xe
apt update
apt install -y git cmake clang pkg-config
apt install -y libevent libcurl openssl pcre2
apt install -y libevent libcurl openssl pcre2 python2
git clone https://github.com/jbeder/yaml-cpp
git clone https://github.com/jbeder/yaml-cpp --depth=1
cd yaml-cpp
cmake -DCMAKE_INSTALL_PREFIX=/data/data/com.termux/files/usr -DYAML_CPP_BUILD_TESTS=OFF -DYAML_CPP_BUILD_TOOLS=OFF .
make install -j3
cd ..
git clone https://github.com/tencent/rapidjson
git clone https://github.com/tencent/rapidjson --depth=1
cd rapidjson
cp -r include/* /data/data/com.termux/files/usr/include/
cd ..
git clone https://github.com/svaarala/duktape --depth=1
cd duktape
pip2 install PyYAML
python2 util/dist.py
cd dist/src
cc -c -O3 -o duktape.o duktape.c
cc -c -O3 -o duk_module_node.o -I. ../extras/module-node/duk_module_node.c
ar cr libduktape.a duktape.o
ar cr libduktape_module.a duk_module_node.o
install -m0644 *.a /data/data/com.termux/files/usr/lib
install -m0644 duk*.h /data/data/com.termux/files/usr/include
install -m0644 ../extras/module-node/duk_module_node.h /data/data/com.termux/files/usr/include
cd ../../..

View File

@@ -19,6 +19,7 @@
#include "string_hash.h"
#include "templates.h"
#include "upload.h"
#include "script_duktape.h"
#define MAX_EXTCONF_RULESET_COUNT 64
@@ -55,6 +56,7 @@ bool do_sort = false, config_update_strict = false;
bool clash_use_new_field_name = false;
std::string proxy_config, proxy_ruleset, proxy_subscription;
int config_update_interval = 0;
std::string sort_script, filter_script;
std::string clash_rule_base;
string_array clash_extra_group;
@@ -84,6 +86,11 @@ template <typename T> T safe_as (const YAML::Node& node)
return T();
};
template <typename T> void operator >>= (const YAML::Node& node, T& i)
{
i = safe_as<T>(node);
};
std::string parseProxy(const std::string &source)
{
std::string proxy = source;
@@ -516,6 +523,9 @@ void readYAMLConf(YAML::Node &node)
section["exclude_remarks"] >> def_exclude_remarks;
if(section["include_remarks"].IsSequence())
section["include_remarks"] >> def_include_remarks;
filter_script = safe_as<bool>(section["enable_filter"]) ? safe_as<std::string>(section["filter_script"]) : "";
if(startsWith(filter_script, "path:"))
filter_script = fileGet(filter_script.substr(5), false);
section["base_path"] >> base_path;
section["clash_rule_base"] >> clash_rule_base;
section["surge_rule_base"] >> surge_rule_base;
@@ -561,6 +571,7 @@ void readYAMLConf(YAML::Node &node)
tfo_flag.set(safe_as<std::string>(section["tcp_fast_open_flag"]));
scv_flag.set(safe_as<std::string>(section["skip_cert_verify_flag"]));
section["sort_flag"] >> do_sort;
section["sort_script"] >> sort_script;
section["filter_deprecated_nodes"] >> filter_deprecated;
section["append_sub_userinfo"] >> append_userinfo;
section["clash_use_new_field_name"] >> clash_use_new_field_name;
@@ -753,6 +764,9 @@ void readConf()
ini.GetAll("exclude_remarks", def_exclude_remarks);
if(ini.ItemPrefixExist("include_remarks"))
ini.GetAll("include_remarks", def_include_remarks);
filter_script = ini.GetBool("enable_filter") ? replace_all_distinct(ini.Get("filter_script"), "\\n", "\n") : "";
if(startsWith(filter_script, "path:"))
filter_script = fileGet(filter_script.substr(5), false);
ini.GetIfExist("base_path", base_path);
ini.GetIfExist("clash_rule_base", clash_rule_base);
ini.GetIfExist("surge_rule_base", surge_rule_base);
@@ -785,6 +799,7 @@ void readConf()
tfo_flag.set(ini.Get("tcp_fast_open_flag"));
scv_flag.set(ini.Get("skip_cert_verify_flag"));
ini.GetBoolIfExist("sort_flag", do_sort);
sort_script = replace_all_distinct(ini.Get("sort_script"), "\\n", "\n");
ini.GetBoolIfExist("filter_deprecated_nodes", filter_deprecated);
ini.GetBoolIfExist("append_sub_userinfo", append_userinfo);
ini.GetBoolIfExist("clash_use_new_field_name", clash_use_new_field_name);
@@ -1163,7 +1178,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
/// switches with default value
tribool upload = getUrlArg(argument, "upload"), emoji = getUrlArg(argument, "emoji");
tribool append_type = getUrlArg(argument, "append_type"), tfo = getUrlArg(argument, "tfo"), udp = getUrlArg(argument, "udp"), nodelist = getUrlArg(argument, "list");
tribool sort_flag = getUrlArg(argument, "sort");
tribool sort_flag = getUrlArg(argument, "sort"), use_sort_script = getUrlArg(argument, "sort_script");
tribool clash_new_field = getUrlArg(argument, "new_name"), clash_script = getUrlArg(argument, "script"), add_insert = getUrlArg(argument, "insert");
tribool scv = getUrlArg(argument, "scv"), fdn = getUrlArg(argument, "fdn"), expand = getUrlArg(argument, "expand"), append_sub_userinfo = getUrlArg(argument, "append_info");
tribool prepend_insert = getUrlArg(argument, "prepend");
@@ -1246,6 +1261,9 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
ext.skip_cert_verify.define(scv_flag);
ext.sort_flag = sort_flag.get(do_sort);
use_sort_script.define(sort_script.size());
if(ext.sort_flag && use_sort_script)
ext.sort_script = sort_script;
ext.filter_deprecated = fdn.get(filter_deprecated);
ext.clash_new_field_name = clash_new_field.get(clash_use_new_field_name);
ext.clash_script = clash_script.get();
@@ -1415,6 +1433,31 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
else
nodes.emplace_back(x);
}
//run filter script
if(filter_script.size())
{
duk_context *ctx = duktape_init();
if(ctx)
{
defer(duk_destroy_heap(ctx);)
if(duktape_peval(ctx, filter_script) == 0)
{
auto filter = [&](const nodeInfo &x)
{
duk_get_global_string(ctx, "filter");
duktape_push_nodeinfo(ctx, x);
duk_pcall(ctx, 1);
return !duktape_get_res_bool(ctx);
};
nodes.erase(std::remove_if(nodes.begin(), nodes.end(), filter), nodes.end());
}
else
{
writeLog(0, "Error when trying to parse script:\n" + duktape_get_err_stack(ctx), LOG_LEVEL_ERROR);
duk_pop(ctx); /// pop err
}
}
}
//check custom group name
if(group.size())
@@ -1662,14 +1705,6 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
std::string simpleToClashR(RESPONSE_CALLBACK_ARGS)
{
std::string url = argument.size() <= 8 ? "" : argument.substr(8);
std::string base_content;
std::vector<nodeInfo> nodes;
string_array extra_group, extra_ruleset, include_remarks, exclude_remarks;
std::vector<ruleset_content> rca;
std::string subInfo;
if(!url.size() && !api_mode)
url = default_url;
if(!url.size() || argument.substr(0, 8) != "sublink=")
{
*status_code = 400;
@@ -1680,71 +1715,7 @@ std::string simpleToClashR(RESPONSE_CALLBACK_ARGS)
*status_code = 400;
return "Please insert your subscription link instead of clicking the default link.";
}
if(insert_url.size())
url = insert_url + "|" + url;
if(!api_mode || cfw_child_process)
readConf();
extra_group = clash_extra_group;
if(update_ruleset_on_request || cfw_child_process)
refreshRulesets(rulesets, ruleset_content_array);
rca = ruleset_content_array;
extra_settings ext = {true, overwrite_original_rules, safe_get_renames(), safe_get_emojis(), add_emoji, remove_old_emoji, append_proxy_type, false, do_sort, filter_deprecated, clash_use_new_field_name, false, "", "", ""};
std::string proxy = parseProxy(proxy_subscription);
include_remarks = def_include_remarks;
exclude_remarks = def_exclude_remarks;
//start parsing urls
int groupID = 0;
string_array dummy;
string_array urls = split(url, "|");
for(std::string &x : urls)
{
x = trim(x);
//std::cerr<<"Fetching node data from url '"<<x<<"'."<<std::endl;
writeLog(0, "Fetching node data from url '" + x + "'.", LOG_LEVEL_INFO);
if(addNodes(x, nodes, groupID, proxy, exclude_remarks, include_remarks, dummy, dummy, subInfo, false) == -1)
{
*status_code = 400;
return std::string("The following link doesn't contain any valid node info: " + x);
}
groupID++;
}
//exit if found nothing
if(!nodes.size())
{
*status_code = 400;
return "No nodes were found!";
}
writeLog(0, "Generate target: ClashR", LOG_LEVEL_INFO);
template_args tpl_args;
tpl_args.global_vars = global_vars;
tpl_args.local_vars["clash.new_field_name"] = clash_use_new_field_name ? "true" : "false";
tpl_args.request_params["target"] = "clashr";
tpl_args.request_params["url"] = url;
if(!enable_base_gen)
{
if(render_template(fetchFile(clash_rule_base, proxy, cache_config), tpl_args, base_content, template_path) != 0)
{
*status_code = 400;
return base_content;
}
//base_content = fetchFile(clash_rule_base, proxy, cache_config);
return netchToClash(nodes, base_content, rca, extra_group, true, ext);
}
else
{
YAML::Node yamlnode = safe_get_clash_base();
netchToClash(nodes, yamlnode, extra_group, true, ext);
return YAML::Dump(yamlnode);
}
return subconverter("target=clashr&url=" + UrlEncode(url), postdata, status_code, extra_headers);
}
std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)

View File

@@ -20,7 +20,9 @@
#define concat(a,b) a ## b
#define do_concat(a,b) concat(a,b)
#define defer(x) std::shared_ptr<void> do_concat(__defer_deleter_,__LINE__) (nullptr, [&](...){x});
template <typename T> class __defer_struct final {private: T fn; bool __cancelled = false; public: __defer_struct(T func) : fn(std::move(func)) {} ~__defer_struct() {if(!__cancelled) fn();} void cancel() {__cancelled = true;} };
//#define defer(x) std::unique_ptr<void> do_concat(__defer_deleter_,__LINE__) (nullptr, [&](...){x});
#define defer(x) __defer_struct do_concat(__defer_deleter,__COUNTER__) ([&](...){x;});
typedef std::string::size_type string_size;
typedef std::vector<std::string> string_array;

View File

@@ -8,6 +8,7 @@
#include "logger.h"
#include "webget.h"
#include "speedtestutil.h"
#include "script_duktape.h"
std::string override_conf_port;
bool ss_libev, ssr_libev;
@@ -26,18 +27,54 @@ int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std
int linkType = -1;
std::vector<nodeInfo> nodes;
nodeInfo node;
std::string strSub, extra_headers;
std::string strSub, extra_headers, custom_group;
// TODO: replace with startsWith if appropriate
link = replace_all_distinct(link, "\"", "");
/// script:filepath,arg1,arg2,...
if(startsWith(link, "script:") && authorized) /// process subscription with script
{
writeLog(0, "Found script link. Start running...", LOG_LEVEL_INFO);
string_array args = split(link.substr(7), ",");
if(args.size() >= 1)
{
std::string script = fileGet(args[0], false);
duk_context *ctx = duktape_init();
defer(duk_destroy_heap(ctx);)
duktape_peval(ctx, script);
duk_get_global_string(ctx, "parse");
for(size_t i = 1; i < args.size(); i++)
duk_push_string(ctx, trim(args[i]).c_str());
if(duk_pcall(ctx, args.size() - 1) == 0)
link = duktape_get_res_str(ctx);
else
{
writeLog(0, "Error when trying to evaluate script:\n" + duktape_get_err_stack(ctx), LOG_LEVEL_ERROR);
duk_pop(ctx); /// pop err
}
}
}
/// tag:group_name,link
if(startsWith(link, "tag:"))
{
string_size pos = link.find(",");
if(pos != link.npos)
{
custom_group = link.substr(4, pos - 4);
link.erase(0, pos + 1);
}
}
writeLog(LOG_TYPE_INFO, "Received Link.");
if(strFind(link, "https://t.me/socks") || strFind(link, "tg://socks"))
if(startsWith(link, "https://t.me/socks") || startsWith(link, "tg://socks"))
linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
else if(strFind(link, "https://t.me/http") || strFind(link, "tg://http"))
else if(startsWith(link, "https://t.me/http") || startsWith(link, "tg://http"))
linkType = SPEEDTEST_MESSAGE_FOUNDHTTP;
else if(startsWith(link, "http://") || startsWith(link, "https://") || startsWith(link, "data:") || strFind(link, "surge:///install-config"))
else if(startsWith(link, "http://") || startsWith(link, "https://") || startsWith(link, "data:") || startsWith(link, "surge:///install-config"))
linkType = SPEEDTEST_MESSAGE_FOUNDSUB;
else if(strFind(link, "Netch://"))
else if(startsWith(link, "Netch://"))
linkType = SPEEDTEST_MESSAGE_FOUNDNETCH;
else if(fileExist(link))
linkType = SPEEDTEST_MESSAGE_FOUNDLOCAL;
@@ -46,7 +83,7 @@ int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std
{
case SPEEDTEST_MESSAGE_FOUNDSUB:
writeLog(LOG_TYPE_INFO, "Downloading subscription data...");
if(strFind(link, "surge:///install-config")) //surge config link
if(startsWith(link, "surge:///install-config")) //surge config link
link = UrlDecode(getUrlArg(link, "url"));
strSub = webGet(link, proxy, extra_headers, cache_subscription);
/*
@@ -82,7 +119,11 @@ int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std
}
filterNodes(nodes, exclude_remarks, include_remarks, groupID);
for(nodeInfo &x : nodes)
{
x.groupID = groupID;
if(custom_group.size())
x.group = custom_group;
}
copyNodes(nodes, allNodes);
}
else
@@ -110,7 +151,11 @@ int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std
}
filterNodes(nodes, exclude_remarks, include_remarks, groupID);
for(nodeInfo &x : nodes)
{
x.groupID = groupID;
if(custom_group.size())
x.group = custom_group;
}
copyNodes(nodes, allNodes);
break;
default:
@@ -121,6 +166,8 @@ int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std
return -1;
}
node.groupID = groupID;
if(custom_group.size())
node.group = custom_group;
allNodes.push_back(node);
}
return 0;

224
src/script.cpp Normal file
View File

@@ -0,0 +1,224 @@
#include <string>
#include <iostream>
#include <duktape.h>
#include <duk_module_node.h>
#include "misc.h"
#include "multithread.h"
#include "nodeinfo.h"
extern int cache_config;
std::string parseProxy(const std::string &source);
std::string foldPathString(const std::string &path)
{
std::string output = path;
string_size pos_up, pos_slash, pos_unres = 0;
do
{
pos_up = output.find("../", pos_unres);
if(pos_up == output.npos)
break;
else if(pos_up == 0)
{
pos_unres = pos_up + 3;
continue;
}
pos_slash = output.rfind("/", pos_up - 1);
if(pos_slash != output.npos)
{
pos_slash = output.rfind("/", pos_slash - 1);
if(pos_slash != output.npos)
output.erase(pos_slash + 1, pos_up - pos_slash + 2);
else
output.erase(0, pos_up + 3);
}
else
pos_unres = pos_up + 3;
} while(pos_up != output.npos);
return output;
}
duk_ret_t cb_resolve_module(duk_context *ctx)
{
const char *requested_id = duk_get_string(ctx, 0);
const char *parent_id = duk_get_string(ctx, 1); /* calling module */
//const char *resolved_id;
std::string resolved_id;
if(strlen(parent_id))
{
std::string parent_path = parent_id;
string_size pos = parent_path.rfind("/");
if(pos != parent_path.npos)
resolved_id += parent_path.substr(0, pos + 1);
}
resolved_id += requested_id;
if(!endsWith(resolved_id, ".js"))
resolved_id += ".js";
resolved_id = foldPathString(resolved_id);
/* Arrive at the canonical module ID somehow. */
std::cout<<resolved_id<<std::endl;
if(!fileExist(resolved_id))
duk_push_undefined(ctx);
else
duk_push_string(ctx, resolved_id.c_str());
return 1; /*nrets*/
}
duk_ret_t cb_load_module(duk_context *ctx)
{
const char *resolved_id = duk_get_string(ctx, 0);
std::string module_source = fileGet(resolved_id, true);
/* Arrive at the JS source code for the module somehow. */
duk_push_string(ctx, module_source.c_str());
return 1; /*nrets*/
}
static duk_ret_t native_print(duk_context *ctx)
{
duk_push_string(ctx, " ");
duk_insert(ctx, 0);
duk_join(ctx, duk_get_top(ctx) - 1);
printf("%s\n", duk_safe_to_string(ctx, -1));
return 0;
}
static duk_ret_t fetch(duk_context *ctx)
{
std::string filepath = duk_safe_to_string(ctx, -1), proxy = duk_safe_to_string(ctx, -1);
std::string content = fetchFile(filepath, proxy, cache_config);
duk_push_lstring(ctx, content.c_str(), content.size());
return 1;
}
static duk_ret_t atob(duk_context *ctx)
{
std::string data = duk_safe_to_string(ctx, -1);
duk_push_string(ctx, base64_encode(data).c_str());
return 1;
}
static duk_ret_t btoa(duk_context *ctx)
{
std::string data = duk_safe_to_string(ctx, -1);
duk_push_string(ctx, base64_decode(data, true).c_str());
return 1;
}
duk_context *duktape_init()
{
duk_context *ctx = duk_create_heap_default();
if(!ctx)
return NULL;
/// init module
duk_push_object(ctx);
duk_push_c_function(ctx, cb_resolve_module, DUK_VARARGS);
duk_put_prop_string(ctx, -2, "resolve");
duk_push_c_function(ctx, cb_load_module, DUK_VARARGS);
duk_put_prop_string(ctx, -2, "load");
duk_module_node_init(ctx);
duk_push_c_function(ctx, native_print, DUK_VARARGS);
duk_put_global_string(ctx, "print");
duk_push_c_function(ctx, fetch, 1);
duk_put_global_string(ctx, "fetch");
duk_push_c_function(ctx, atob, 1);
duk_put_global_string(ctx, "atob");
duk_push_c_function(ctx, btoa, 1);
duk_put_global_string(ctx, "btoa");
return ctx;
}
int duktape_peval(duk_context *ctx, const std::string &script)
{
return duk_peval_string(ctx, script.c_str());
}
int duktape_call_function(duk_context *ctx, const std::string &name, size_t nargs, ...)
{
duk_get_global_string(ctx, name.c_str());
va_list vl;
va_start(vl, nargs);
size_t index = 0;
while(index < nargs)
{
std::string *arg = va_arg(vl, std::string*);
if(arg != NULL)
duk_push_string(ctx, arg->c_str());
else
duk_push_undefined(ctx);
index++;
}
va_end(vl);
return duk_pcall(ctx, nargs);
}
int duktape_get_res_int(duk_context *ctx)
{
int retval = duk_to_int(ctx, -1);
duk_pop(ctx);
return retval;
}
int duktape_push_nodeinfo(duk_context *ctx, const nodeInfo &node)
{
duk_push_object(ctx);
duk_push_string(ctx, node.group.c_str());
duk_put_prop_string(ctx, -2, "Group");
duk_push_int(ctx, node.groupID);
duk_put_prop_string(ctx, -2, "GroupID");
duk_push_int(ctx, node.id);
duk_put_prop_string(ctx, -2, "Index");
duk_push_string(ctx, node.remarks.c_str());
duk_put_prop_string(ctx, -2, "Remark");
duk_push_string(ctx, node.proxyStr.c_str());
duk_put_prop_string(ctx, -2, "ProxyInfo");
return 0;
}
int duktape_push_nodeinfo_arr(duk_context *ctx, const nodeInfo &node, duk_idx_t index)
{
duk_push_object(ctx);
duk_push_string(ctx, "Group");
duk_push_string(ctx, node.group.c_str());
duk_def_prop(ctx, index - 2, DUK_DEFPROP_HAVE_VALUE);
duk_push_string(ctx, "GroupID");
duk_push_int(ctx, node.groupID);
duk_def_prop(ctx, index - 2, DUK_DEFPROP_HAVE_VALUE);
duk_push_string(ctx, "Index");
duk_push_int(ctx, node.id);
duk_def_prop(ctx, index - 2, DUK_DEFPROP_HAVE_VALUE);
duk_push_string(ctx, "Remark");
duk_push_string(ctx, node.remarks.c_str());
duk_def_prop(ctx, index - 2, DUK_DEFPROP_HAVE_VALUE);
duk_push_string(ctx, "ProxyInfo");
duk_push_string(ctx, node.proxyStr.c_str());
duk_def_prop(ctx, index - 2, DUK_DEFPROP_HAVE_VALUE);
return 0;
}
std::string duktape_get_res_str(duk_context *ctx)
{
std::string retstr = duk_safe_to_string(ctx, -1);
duk_pop(ctx);
return retstr;
}
bool duktape_get_res_bool(duk_context *ctx)
{
bool ret = duk_to_boolean(ctx, -1);
duk_pop(ctx);
return ret;
}
std::string duktape_get_err_stack(duk_context *ctx)
{
duk_get_prop_string(ctx, -1, "stack");
std::string stackstr = duk_get_string(ctx, -1);
duk_pop(ctx);
return stackstr;
}

19
src/script_duktape.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef SCRIPT_DUKTAPE_H_INCLUDED
#define SCRIPT_DUKTAPE_H_INCLUDED
#include <string>
#include <duktape.h>
#include "nodeinfo.h"
duk_context *duktape_init();
int duktape_push_nodeinfo(duk_context *ctx, const nodeInfo &node);
int duktape_push_nodeinfo_arr(duk_context *ctx, const nodeInfo &node, duk_idx_t index = -1);
int duktape_peval(duk_context *ctx, const std::string &script);
int duktape_call_function(duk_context *ctx, const std::string &name, size_t nargs, ...);
int duktape_get_res_int(duk_context *ctx);
std::string duktape_get_res_str(duk_context *ctx);
bool duktape_get_res_bool(duk_context *ctx);
std::string duktape_get_err_stack(duk_context *ctx);
#endif // SCRIPT_DUKTAPE_H_INCLUDED

View File

@@ -107,7 +107,7 @@ void explodeVmess(std::string vmess, const std::string &custom_port, nodeInfo &n
node.remarks = ps;
node.server = add;
node.port = to_int(port, 1);
node.proxyStr = vmessConstruct(add, port, type, id, aid, net, "auto", path, host, "", tls);
node.proxyStr = vmessConstruct(node.group, ps, add, port, type, id, aid, net, "auto", path, host, "", tls);
}
void explodeVmessConf(std::string content, const std::string &custom_port, bool libev, std::vector<nodeInfo> &nodes)
@@ -185,11 +185,11 @@ void explodeVmessConf(std::string content, const std::string &custom_port, bool
}
}
node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, edge, tls, udp, tfo, scv);
node.group = V2RAY_DEFAULT_GROUP;
node.remarks = add + ":" + port;
node.server = add;
node.port = to_int(port, 1);
node.proxyStr = vmessConstruct(node.group, node.remarks, add, port, type, id, aid, net, cipher, path, host, edge, tls, udp, tfo, scv);
nodes.push_back(node);
}
return;
@@ -242,19 +242,19 @@ void explodeVmessConf(std::string content, const std::string &custom_port, bool
json["vmess"][i]["security"] >> cipher;
group = V2RAY_DEFAULT_GROUP;
node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, "", tls, udp, tfo, scv);
node.proxyStr = vmessConstruct(group, ps, add, port, type, id, aid, net, cipher, path, host, "", tls, udp, tfo, scv);
break;
case 3: //ss config
json["vmess"][i]["id"] >> id;
json["vmess"][i]["security"] >> cipher;
group = SS_DEFAULT_GROUP;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.proxyStr = ssConstruct(add, port, id, cipher, "", "", ps, libev, udp, tfo, scv);
node.proxyStr = ssConstruct(group, ps, add, port, id, cipher, "", "", libev, udp, tfo, scv);
break;
case 4: //socks config
group = SOCKS_DEFAULT_GROUP;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
node.proxyStr = socksConstruct(ps, add, port, "", "", udp, tfo, scv);
node.proxyStr = socksConstruct(group, ps, add, port, "", "", udp, tfo, scv);
break;
default:
continue;
@@ -338,7 +338,7 @@ void explodeSS(std::string ss, bool libev, const std::string &custom_port, nodeI
node.remarks = ps;
node.server = server;
node.port = to_int(port, 1);
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, ps, libev);
node.proxyStr = ssConstruct(group, ps, server, port, password, method, plugin, pluginopts, libev);
}
void explodeSSD(std::string link, bool libev, const std::string &custom_port, std::vector<nodeInfo> &nodes)
@@ -417,7 +417,7 @@ void explodeSSD(std::string link, bool libev, const std::string &custom_port, st
node.remarks = remarks;
node.server = server;
node.port = to_int(port, 1);
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, remarks, libev);
node.proxyStr = ssConstruct(group, remarks, server, port, password, method, plugin, pluginopts, libev);
node.id = index;
nodes.push_back(node);
index++;
@@ -461,7 +461,7 @@ void explodeSSAndroid(std::string ss, bool libev, const std::string &custom_port
node.remarks = ps;
node.server = server;
node.port = to_int(port, 1);
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, ps, libev);
node.proxyStr = ssConstruct(group, ps, server, port, password, method, plugin, pluginopts, libev);
nodes.push_back(node);
index++;
}
@@ -499,7 +499,7 @@ void explodeSSConf(std::string content, const std::string &custom_port, bool lib
node.id = index;
node.server = server;
node.port = to_int(port, 1);
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, ps, libev);
node.proxyStr = ssConstruct(group, ps, server, port, password, method, plugin, pluginopts, libev);
nodes.push_back(node);
index++;
}
@@ -561,7 +561,7 @@ void explodeSSR(std::string ssr, bool ss_libev, bool ssr_libev, const std::strin
if(find(ss_ciphers.begin(), ss_ciphers.end(), method) != ss_ciphers.end() && (obfs.empty() || obfs == "plain") && (protocol.empty() || protocol == "origin"))
{
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.proxyStr = ssConstruct(server, port, password, method, "", "", remarks, ss_libev);
node.proxyStr = ssConstruct(group, remarks, server, port, password, method, "", "", ss_libev);
}
else
{
@@ -597,7 +597,7 @@ void explodeSSRConf(std::string content, const std::string &custom_port, bool ss
pluginopts = GetMember(json, "plugin_opts");
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.group = SS_DEFAULT_GROUP;
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, node.remarks, ss_libev);
node.proxyStr = ssConstruct(node.group, node.remarks, server, port, password, method, plugin, pluginopts, ss_libev);
}
else
{
@@ -687,7 +687,7 @@ void explodeSocks(std::string link, const std::string &custom_port, nodeInfo &no
node.remarks = remarks;
node.server = server;
node.port = to_int(port, 1);
node.proxyStr = socksConstruct(remarks, server, port, username, password);
node.proxyStr = socksConstruct(group, remarks, server, port, username, password);
}
void explodeHTTP(std::string link, const std::string &custom_port, nodeInfo &node)
@@ -714,7 +714,7 @@ void explodeHTTP(std::string link, const std::string &custom_port, nodeInfo &nod
node.remarks = remarks;
node.server = server;
node.port = to_int(port, 1);
node.proxyStr = httpConstruct(remarks, server, port, username, password, strFind(link, "/https"));
node.proxyStr = httpConstruct(group, remarks, server, port, username, password, strFind(link, "/https"));
}
void explodeHTTPSub(std::string link, const std::string &custom_port, nodeInfo &node)
@@ -776,7 +776,7 @@ void explodeHTTPSub(std::string link, const std::string &custom_port, nodeInfo &
node.remarks = remarks;
node.server = server;
node.port = to_int(port, 1);
node.proxyStr = httpConstruct(remarks, server, port, username, password, tls);
node.proxyStr = httpConstruct(group, remarks, server, port, username, password, tls);
}
void explodeTrojan(std::string trojan, const std::string &custom_port, nodeInfo &node)
@@ -827,7 +827,7 @@ void explodeTrojan(std::string trojan, const std::string &custom_port, nodeInfo
node.remarks = remark;
node.server = server;
node.port = to_int(port, 1);
node.proxyStr = trojanConstruct(remark, server, port, psk, host, true);
node.proxyStr = trojanConstruct(node.group, remark, server, port, psk, host, true);
}
void explodeQuan(std::string quan, const std::string &custom_port, nodeInfo &node)
@@ -889,23 +889,24 @@ void explodeQuan(std::string quan, const std::string &custom_port, nodeInfo &nod
node.remarks = ps;
node.server = add;
node.port = to_int(port, 1);
node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, edge, tls);
node.proxyStr = vmessConstruct(group, ps, add, port, type, id, aid, net, cipher, path, host, edge, tls);
}
}
void explodeNetch(std::string netch, bool ss_libev, bool ssr_libev, const std::string &custom_port, nodeInfo &node)
{
Document json;
std::string type, remark, address, port, username, password, method, plugin, pluginopts, protocol, protoparam, obfs, obfsparam, id, aid, transprot, faketype, host, edge, path, tls;
std::string type, group, remark, address, port, username, password, method, plugin, pluginopts, protocol, protoparam, obfs, obfsparam, id, aid, transprot, faketype, host, edge, path, tls;
tribool udp, tfo, scv;
netch = urlsafe_base64_decode(netch.substr(8));
json.Parse(netch.data());
if(json.HasParseError())
return;
json["Type"] >> type;
json["Remark"] >> remark;
json["Hostname"] >> address;
type = GetMember(json, "Type");
group = GetMember(json, "Group");
remark = GetMember(json, "Remark");
address = GetMember(json, "Hostname");
udp = GetMember(json, "EnableUDP");
tfo = GetMember(json, "EnableTFO");
scv = GetMember(json, "AllowInsecure");
@@ -921,9 +922,11 @@ void explodeNetch(std::string netch, bool ss_libev, bool ssr_libev, const std::s
case "SS"_hash:
plugin = GetMember(json, "Plugin");
pluginopts = GetMember(json, "PluginOption");
node.group = SS_DEFAULT_GROUP;
if(group.empty())
group = SS_DEFAULT_GROUP;
node.group = group;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.proxyStr = ssConstruct(address, port, password, method, plugin, pluginopts, remark, ss_libev, udp, tfo, scv);
node.proxyStr = ssConstruct(group, remark, address, port, password, method, plugin, pluginopts, ss_libev, udp, tfo, scv);
break;
case "SSR"_hash:
protocol = GetMember(json, "Protocol");
@@ -932,17 +935,21 @@ void explodeNetch(std::string netch, bool ss_libev, bool ssr_libev, const std::s
{
plugin = GetMember(json, "Plugin");
pluginopts = GetMember(json, "PluginOption");
node.group = SS_DEFAULT_GROUP;
if(group.empty())
group = SS_DEFAULT_GROUP;
node.group = group;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.proxyStr = ssConstruct(address, port, password, method, plugin, pluginopts, remark, ss_libev, udp, tfo, scv);
node.proxyStr = ssConstruct(group, remark, address, port, password, method, plugin, pluginopts, ss_libev, udp, tfo, scv);
}
else
{
protoparam = GetMember(json, "ProtocolParam");
obfsparam = GetMember(json, "OBFSParam");
node.group = SSR_DEFAULT_GROUP;
if(group.empty())
group = SSR_DEFAULT_GROUP;
node.group = group;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSSR;
node.proxyStr = ssrConstruct(SSR_DEFAULT_GROUP, remark, base64_encode(remark), address, port, protocol, method, obfs, password, obfsparam, protoparam, ssr_libev, udp, tfo, scv);
node.proxyStr = ssrConstruct(group, remark, base64_encode(remark), address, port, protocol, method, obfs, password, obfsparam, protoparam, ssr_libev, udp, tfo, scv);
}
break;
case "VMess"_hash:
@@ -955,34 +962,44 @@ void explodeNetch(std::string netch, bool ss_libev, bool ssr_libev, const std::s
edge = GetMember(json, "Edge");
tls = GetMember(json, "TLSSecure");
node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
node.group = V2RAY_DEFAULT_GROUP;
node.proxyStr = vmessConstruct(address, port, faketype, id, aid, transprot, method, path, host, edge, tls, udp, tfo, scv);
if(group.empty())
group = V2RAY_DEFAULT_GROUP;
node.group = group;
node.proxyStr = vmessConstruct(group, remark, address, port, faketype, id, aid, transprot, method, path, host, edge, tls, udp, tfo, scv);
break;
case "Socks5"_hash:
username = GetMember(json, "Username");
node.linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
node.group = SOCKS_DEFAULT_GROUP;
node.proxyStr = socksConstruct(remark, address, port, username, password, udp, tfo, scv);
if(group.empty())
group = SOCKS_DEFAULT_GROUP;
node.group = group;
node.proxyStr = socksConstruct(group, remark, address, port, username, password, udp, tfo, scv);
break;
case "HTTP"_hash:
case "HTTPS"_hash:
node.linkType = SPEEDTEST_MESSAGE_FOUNDHTTP;
node.group = HTTP_DEFAULT_GROUP;
node.proxyStr = httpConstruct(remark, address, port, username, password, type == "HTTPS", scv);
if(group.empty())
group = HTTP_DEFAULT_GROUP;
node.group = group;
node.proxyStr = httpConstruct(group, remark, address, port, username, password, type == "HTTPS", scv);
break;
case "Trojan"_hash:
host = GetMember(json, "Host");
tls = GetMember(json, "TLSSecure");
node.linkType = SPEEDTEST_MESSAGE_FOUNDTROJAN;
node.group = TROJAN_DEFAULT_GROUP;
node.proxyStr = trojanConstruct(remark, address, port, username, password, tls == "true", udp, tfo, scv);
if(group.empty())
group = TROJAN_DEFAULT_GROUP;
node.group = group;
node.proxyStr = trojanConstruct(group, remark, address, port, username, password, tls == "true", udp, tfo, scv);
break;
case "Snell"_hash:
obfs = GetMember(json, "OBFS");
host = GetMember(json, "Host");
node.linkType = SPEEDTEST_MESSAGE_FOUNDSNELL;
node.group = SNELL_DEFAULT_GROUP;
node.proxyStr = snellConstruct(remark, address, port, password, obfs, host, udp, tfo, scv);
if(group.empty())
group = SNELL_DEFAULT_GROUP;
node.group = group;
node.proxyStr = snellConstruct(group, remark, address, port, password, obfs, host, udp, tfo, scv);
break;
default:
return;
@@ -1031,7 +1048,7 @@ void explodeClash(Node yamlnode, const std::string &custom_port, std::vector<nod
singleproxy["ws-headers"]["Edge"] >>= edge;
node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
node.proxyStr = vmessConstruct(server, port, type, id, aid, net, cipher, path, host, edge, tls, udp, tfo, scv);
node.proxyStr = vmessConstruct(group, ps, server, port, type, id, aid, net, cipher, path, host, edge, tls, udp, tfo, scv);
break;
case "ss"_hash:
group = SS_DEFAULT_GROUP;
@@ -1101,7 +1118,7 @@ void explodeClash(Node yamlnode, const std::string &custom_port, std::vector<nod
}
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.proxyStr = ssConstruct(server, port, password, cipher, plugin, pluginopts, ps, ss_libev, udp, tfo, scv);
node.proxyStr = ssConstruct(group, ps, server, port, password, cipher, plugin, pluginopts, ss_libev, udp, tfo, scv);
break;
case "socks"_hash:
group = SOCKS_DEFAULT_GROUP;
@@ -1110,7 +1127,7 @@ void explodeClash(Node yamlnode, const std::string &custom_port, std::vector<nod
singleproxy["password"] >>= password;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
node.proxyStr = socksConstruct(ps, server, port, user, password);
node.proxyStr = socksConstruct(group, ps, server, port, user, password);
break;
case "ssr"_hash:
group = SSR_DEFAULT_GROUP;
@@ -1133,7 +1150,7 @@ void explodeClash(Node yamlnode, const std::string &custom_port, std::vector<nod
singleproxy["tls"] >>= tls;
node.linkType = SPEEDTEST_MESSAGE_FOUNDHTTP;
node.proxyStr = httpConstruct(ps, server, port, user, password, tls == "true", scv);
node.proxyStr = httpConstruct(group, ps, server, port, user, password, tls == "true", scv);
break;
case "trojan"_hash:
group = TROJAN_DEFAULT_GROUP;
@@ -1141,7 +1158,7 @@ void explodeClash(Node yamlnode, const std::string &custom_port, std::vector<nod
singleproxy["sni"] >>= host;
node.linkType = SPEEDTEST_MESSAGE_FOUNDTROJAN;
node.proxyStr = trojanConstruct(ps, server, port, password, host, true, udp, tfo, scv);
node.proxyStr = trojanConstruct(group, ps, server, port, password, host, true, udp, tfo, scv);
break;
case "snell"_hash:
group = SNELL_DEFAULT_GROUP;
@@ -1150,7 +1167,7 @@ void explodeClash(Node yamlnode, const std::string &custom_port, std::vector<nod
singleproxy["obfs-opts"]["host"] >>= host;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSNELL;
node.proxyStr = snellConstruct(ps, server, port, password, obfs, host, udp, tfo, scv);
node.proxyStr = snellConstruct(group, ps, server, port, password, obfs, host, udp, tfo, scv);
break;
default:
continue;
@@ -1224,7 +1241,7 @@ void explodeShadowrocket(std::string rocket, const std::string &custom_port, nod
node.remarks = remarks;
node.server = add;
node.port = to_int(port, 0);
node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, "", tls);
node.proxyStr = vmessConstruct(node.group, remarks, add, port, type, id, aid, net, cipher, path, host, "", tls);
}
void explodeKitsunebi(std::string kit, const std::string &custom_port, nodeInfo &node)
@@ -1278,7 +1295,7 @@ void explodeKitsunebi(std::string kit, const std::string &custom_port, nodeInfo
node.remarks = remarks;
node.server = add;
node.port = to_int(port, 0);
node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, "", tls);
node.proxyStr = vmessConstruct(node.group, remarks, add, port, type, id, aid, net, cipher, path, host, "", tls);
}
bool explodeSurge(std::string surge, const std::string &custom_port, std::vector<nodeInfo> &nodes, bool libev)
@@ -1385,7 +1402,7 @@ bool explodeSurge(std::string surge, const std::string &custom_port, std::vector
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.group = SS_DEFAULT_GROUP;
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, remarks, libev, udp, tfo, scv);
node.proxyStr = ssConstruct(node.group, remarks, server, port, password, method, plugin, pluginopts, libev, udp, tfo, scv);
}
//else
// continue;
@@ -1425,7 +1442,7 @@ bool explodeSurge(std::string surge, const std::string &custom_port, std::vector
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.group = SS_DEFAULT_GROUP;
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, remarks, libev, udp, tfo, scv);
node.proxyStr = ssConstruct(node.group, remarks, server, port, password, method, plugin, pluginopts, libev, udp, tfo, scv);
break;
case "socks5"_hash: //surge 3 style socks5 proxy
node.linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
@@ -1454,7 +1471,7 @@ bool explodeSurge(std::string surge, const std::string &custom_port, std::vector
default: continue;
}
}
node.proxyStr = socksConstruct(remarks, server, port, username, password, udp, tfo, scv);
node.proxyStr = socksConstruct(node.group, remarks, server, port, username, password, udp, tfo, scv);
break;
case "vmess"_hash: //surge 4 style vmess proxy
server = trim(configs[1]);
@@ -1502,7 +1519,7 @@ bool explodeSurge(std::string surge, const std::string &custom_port, std::vector
node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
node.group = V2RAY_DEFAULT_GROUP;
node.proxyStr = vmessConstruct(server, port, "", id, "0", net, method, path, host, edge, tls, udp, tfo, scv);
node.proxyStr = vmessConstruct(node.group, remarks, server, port, "", id, "0", net, method, path, host, edge, tls, udp, tfo, scv);
break;
case "http"_hash: //http proxy
node.linkType = SPEEDTEST_MESSAGE_FOUNDHTTP;
@@ -1526,7 +1543,7 @@ bool explodeSurge(std::string surge, const std::string &custom_port, std::vector
default: continue;
}
}
node.proxyStr = httpConstruct(remarks, server, port, username, password, false, scv);
node.proxyStr = httpConstruct(node.group, remarks, server, port, username, password, false, scv);
break;
case "trojan"_hash: // surge 4 style trojan proxy
node.linkType = SPEEDTEST_MESSAGE_FOUNDTROJAN;
@@ -1556,7 +1573,7 @@ bool explodeSurge(std::string surge, const std::string &custom_port, std::vector
if(host.empty() && !isIPv4(server) && !isIPv6(server))
host = server;
node.proxyStr = trojanConstruct(remarks, server, port, password, host, true, udp, tfo, scv);
node.proxyStr = trojanConstruct(node.group, remarks, server, port, password, host, true, udp, tfo, scv);
break;
case "snell"_hash:
node.linkType = SPEEDTEST_MESSAGE_FOUNDSNELL;
@@ -1588,7 +1605,7 @@ bool explodeSurge(std::string surge, const std::string &custom_port, std::vector
if(host.empty() && !isIPv4(server) && !isIPv6(server))
host = server;
node.proxyStr = snellConstruct(remarks, server, port, password, plugin, host, udp, tfo, scv);
node.proxyStr = snellConstruct(node.group, remarks, server, port, password, plugin, host, udp, tfo, scv);
break;
default:
switch(hash_(remarks))
@@ -1642,7 +1659,7 @@ bool explodeSurge(std::string surge, const std::string &custom_port, std::vector
{
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.group = SS_DEFAULT_GROUP;
node.proxyStr = ssConstruct(server, port, password, method, plugin, pluginopts, remarks, libev, udp, tfo, scv);
node.proxyStr = ssConstruct(node.group, remarks, server, port, password, method, plugin, pluginopts, libev, udp, tfo, scv);
}
break;
case "vmess"_hash: //quantumult x style vmess link
@@ -1687,7 +1704,7 @@ bool explodeSurge(std::string surge, const std::string &custom_port, std::vector
node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
node.group = V2RAY_DEFAULT_GROUP;
node.proxyStr = vmessConstruct(server, port, "", id, "0", net, method, path, host, "", tls, udp, tfo, scv);
node.proxyStr = vmessConstruct(node.group, remarks, server, port, "", id, "0", net, method, path, host, "", tls, udp, tfo, scv);
break;
case "trojan"_hash: //quantumult x style trojan link
server = trim(configs[0].substr(0, configs[0].rfind(":")));
@@ -1722,7 +1739,7 @@ bool explodeSurge(std::string surge, const std::string &custom_port, std::vector
node.linkType = SPEEDTEST_MESSAGE_FOUNDTROJAN;
node.group = TROJAN_DEFAULT_GROUP;
node.proxyStr = trojanConstruct(remarks, server, port, password, host, tls == "true", udp, tfo, scv);
node.proxyStr = trojanConstruct(node.group, remarks, server, port, password, host, tls == "true", udp, tfo, scv);
break;
case "http"_hash: //quantumult x style http links
server = trim(configs[0].substr(0, configs[0].rfind(":")));
@@ -1760,7 +1777,7 @@ bool explodeSurge(std::string surge, const std::string &custom_port, std::vector
node.linkType = SPEEDTEST_MESSAGE_FOUNDHTTP;
node.group = HTTP_DEFAULT_GROUP;
node.proxyStr = httpConstruct(remarks, server, port, username, password, tls == "true", scv);
node.proxyStr = httpConstruct(node.group, remarks, server, port, username, password, tls == "true", scv);
break;
default:
continue;
@@ -1810,7 +1827,7 @@ void explodeSSTap(std::string sstap, const std::string &custom_port, std::vector
case 5: //socks 5
json["configs"][i]["username"] >> user;
node.linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
node.proxyStr = socksConstruct(remarks, server, port, user, pass);
node.proxyStr = socksConstruct(group, remarks, server, port, user, pass);
break;
case 6: //ss/ssr
json["configs"][i]["protocol"] >> protocol;
@@ -1819,7 +1836,7 @@ void explodeSSTap(std::string sstap, const std::string &custom_port, std::vector
if(find(ss_ciphers.begin(), ss_ciphers.end(), cipher) != ss_ciphers.end() && protocol == "origin" && obfs == "plain") //is ss
{
node.linkType = SPEEDTEST_MESSAGE_FOUNDSS;
node.proxyStr = ssConstruct(server, port, pass, cipher, "", "", remarks, ss_libev);
node.proxyStr = ssConstruct(group, remarks, server, port, pass, cipher, "", "", ss_libev);
}
else //is ssr cipher
{

View File

@@ -7,13 +7,13 @@
#include "misc.h"
#include "nodeinfo.h"
std::string vmessConstruct(std::string add, std::string port, std::string type, std::string id, std::string aid, std::string net, std::string cipher, std::string path, std::string host, std::string edge, std::string tls, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
std::string vmessConstruct(std::string group, std::string remarks, std::string add, std::string port, std::string type, std::string id, std::string aid, std::string net, std::string cipher, std::string path, std::string host, std::string edge, std::string tls, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
std::string ssrConstruct(std::string group, std::string remarks, std::string remarks_base64, std::string server, std::string port, std::string protocol, std::string method, std::string obfs, std::string password, std::string obfsparam, std::string protoparam, bool libev, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
std::string ssConstruct(std::string server, std::string port, std::string password, std::string method, std::string plugin, std::string pluginopts, std::string remarks, bool libev, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
std::string socksConstruct(std::string remarks, std::string server, std::string port, std::string username, std::string password, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
std::string httpConstruct(std::string remarks, std::string server, std::string port, std::string username, std::string password, bool tls, tribool scv = tribool());
std::string trojanConstruct(std::string remarks, std::string server, std::string port, std::string password, std::string host, bool tlssecure, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
std::string snellConstruct(std::string remarks, std::string server, std::string port, std::string password, std::string obfs, std::string host, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
std::string ssConstruct(std::string group, std::string remarks, std::string server, std::string port, std::string password, std::string method, std::string plugin, std::string pluginopts, bool libev, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
std::string socksConstruct(std::string group, std::string remarks, std::string server, std::string port, std::string username, std::string password, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
std::string httpConstruct(std::string group, std::string remarks, std::string server, std::string port, std::string username, std::string password, bool tls, tribool scv = tribool());
std::string trojanConstruct(std::string group, std::string remarks, std::string server, std::string port, std::string password, std::string host, bool tlssecure, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
std::string snellConstruct(std::string group, std::string remarks, std::string server, std::string port, std::string password, std::string obfs, std::string host, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
void explodeVmess(std::string vmess, const std::string &custom_port, nodeInfo &node);
void explodeSSR(std::string ssr, bool ss_libev, bool libev, const std::string &custom_port, nodeInfo &node);
void explodeSS(std::string ss, bool libev, const std::string &custom_port, nodeInfo &node);

View File

@@ -10,6 +10,7 @@
#include "string_hash.h"
#include "logger.h"
#include "templates.h"
#include "script_duktape.h"
#include <algorithm>
#include <iostream>
@@ -19,6 +20,7 @@
#include <rapidjson/writer.h>
#include <rapidjson/document.h>
#include <yaml-cpp/yaml.h>
#include <duktape.h>
#define MAX_RULES_COUNT 32768
@@ -78,7 +80,7 @@ std::string hostnameToIPAddr(const std::string &host)
return retAddr;
}
std::string vmessConstruct(std::string add, std::string port, std::string type, std::string id, std::string aid, std::string net, std::string cipher, std::string path, std::string host, std::string edge, std::string tls, tribool udp, tribool tfo, tribool scv)
std::string vmessConstruct(std::string group, std::string remarks, std::string add, std::string port, std::string type, std::string id, std::string aid, std::string net, std::string cipher, std::string path, std::string host, std::string edge, std::string tls, tribool udp, tribool tfo, tribool scv)
{
if(!path.size())
path = "/";
@@ -94,8 +96,10 @@ std::string vmessConstruct(std::string add, std::string port, std::string type,
writer.StartObject();
writer.Key("Type");
writer.String("VMess");
writer.Key("Group");
writer.String(group.data());
writer.Key("Remark");
writer.String(std::string(add + ":" + port).data());
writer.String(remarks.data());
writer.Key("Hostname");
writer.String(add.data());
writer.Key("Port");
@@ -194,13 +198,15 @@ std::string ssrConstruct(std::string group, std::string remarks, std::string rem
return sb.GetString();
}
std::string ssConstruct(std::string server, std::string port, std::string password, std::string method, std::string plugin, std::string pluginopts, std::string remarks, bool libev, tribool udp, tribool tfo, tribool scv)
std::string ssConstruct(std::string group, std::string remarks, std::string server, std::string port, std::string password, std::string method, std::string plugin, std::string pluginopts, bool libev, tribool udp, tribool tfo, tribool scv)
{
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
writer.StartObject();
writer.Key("Type");
writer.String("SS");
writer.Key("Group");
writer.String(group.data());
writer.Key("Remark");
writer.String(remarks.data());
writer.Key("Hostname");
@@ -234,13 +240,15 @@ std::string ssConstruct(std::string server, std::string port, std::string passwo
return sb.GetString();
}
std::string socksConstruct(std::string remarks, std::string server, std::string port, std::string username, std::string password, tribool udp, tribool tfo, tribool scv)
std::string socksConstruct(std::string group, std::string remarks, std::string server, std::string port, std::string username, std::string password, tribool udp, tribool tfo, tribool scv)
{
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
writer.StartObject();
writer.Key("Type");
writer.String("Socks5");
writer.Key("Group");
writer.String(group.data());
writer.Key("Remark");
writer.String(remarks.data());
writer.Key("Hostname");
@@ -270,13 +278,15 @@ std::string socksConstruct(std::string remarks, std::string server, std::string
return sb.GetString();
}
std::string httpConstruct(std::string remarks, std::string server, std::string port, std::string username, std::string password, bool tls, tribool scv)
std::string httpConstruct(std::string group, std::string remarks, std::string server, std::string port, std::string username, std::string password, bool tls, tribool scv)
{
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
writer.StartObject();
writer.Key("Type");
writer.String(tls ? "HTTPS" : "HTTP");
writer.Key("Group");
writer.String(group.data());
writer.Key("Remark");
writer.String(remarks.data());
writer.Key("Hostname");
@@ -296,13 +306,15 @@ std::string httpConstruct(std::string remarks, std::string server, std::string p
return sb.GetString();
}
std::string trojanConstruct(std::string remarks, std::string server, std::string port, std::string password, std::string host, bool tlssecure, tribool udp, tribool tfo, tribool scv)
std::string trojanConstruct(std::string group, std::string remarks, std::string server, std::string port, std::string password, std::string host, bool tlssecure, tribool udp, tribool tfo, tribool scv)
{
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
writer.StartObject();
writer.Key("Type");
writer.String("Trojan");
writer.Key("Group");
writer.String(group.data());
writer.Key("Remark");
writer.String(remarks.data());
writer.Key("Hostname");
@@ -334,13 +346,15 @@ std::string trojanConstruct(std::string remarks, std::string server, std::string
return sb.GetString();
}
std::string snellConstruct(std::string remarks, std::string server, std::string port, std::string password, std::string obfs, std::string host, tribool udp, tribool tfo, tribool scv)
std::string snellConstruct(std::string group, std::string remarks, std::string server, std::string port, std::string password, std::string obfs, std::string host, tribool udp, tribool tfo, tribool scv)
{
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
writer.StartObject();
writer.Key("Type");
writer.String("Snell");
writer.Key("Group");
writer.String(group.data());
writer.Key("Remark");
writer.String(remarks.data());
writer.Key("Hostname");
@@ -409,7 +423,7 @@ bool matchRange(std::string &range, int target)
bool match = false;
std::string range_begin_str, range_end_str;
int range_begin = 0, range_end = 0;
const std::string reg_num = "-?\\d+", reg_range = "(\\d+)-(\\d+)", reg_not = "\\!-?(\\d+)", reg_not_range = "\\!(\\d+)-(\\d+)", reg_less = "(\\d+)-", reg_more = "(\\d+)\\+";
static const std::string reg_num = "-?\\d+", reg_range = "(\\d+)-(\\d+)", reg_not = "\\!-?(\\d+)", reg_not_range = "\\!(\\d+)-(\\d+)", reg_less = "(\\d+)-", reg_more = "(\\d+)\\+";
for(std::string &x : vArray)
{
if(regMatch(x, reg_num))
@@ -460,15 +474,35 @@ bool matchRange(std::string &range, int target)
return match;
}
std::string nodeRename(const std::string &orig_remark, int groupID, const string_array &rename_array)
bool applyMatcher(const std::string &rule, std::string &real_rule, const nodeInfo &node)
{
std::string group, ret_real_rule;
static const std::string groupid_regex = R"(^!!(?:GROUPID|INSERT)=([\d\-+!,]+)(?:!!(.*))?$)", group_regex = R"(^!!(?:GROUP)=(.*?)(?:!!(.*))?$)";
if(startsWith(rule, "!!GROUP="))
{
regGetMatch(rule, group_regex, 3, NULL, &group, &ret_real_rule);
real_rule = ret_real_rule;
return regFind(node.group, group);
}
else if(startsWith(rule, "!!GROUPID=") || startsWith(rule, "!!INSERT="))
{
int dir = startsWith(rule, "!!INSERT=") ? -1 : 1;
regGetMatch(rule, groupid_regex, 3, NULL, &group, &ret_real_rule);
real_rule = ret_real_rule;
return matchRange(group, dir * node.groupID);
}
else
real_rule = rule;
return true;
}
std::string nodeRename(const nodeInfo &node, const string_array &rename_array)
{
string_array vArray;
std::string targetRange, remark = orig_remark;
string_size pos;
std::string remark = node.remarks, real_rule;
for(const std::string &x : rename_array)
{
targetRange = std::to_string(groupID);
vArray = split(x, "@");
if(vArray.size() == 1)
{
@@ -476,22 +510,11 @@ std::string nodeRename(const std::string &orig_remark, int groupID, const string
}
else if(vArray.size() != 2)
continue;
if(startsWith(vArray[0], "!!GROUPID="))
{
pos = vArray[0].find("!!", vArray[0].find("!!") + 2);
if(pos != vArray[0].npos)
{
targetRange = vArray[0].substr(10, pos - 10);
vArray[0] = vArray[0].substr(pos + 2);
}
else
continue;
}
if(matchRange(targetRange, groupID))
remark = regReplace(remark, vArray[0], vArray[1]);
if(applyMatcher(vArray[0], real_rule, node) && real_rule.size())
remark = regReplace(remark, real_rule, vArray[1]);
}
if(remark.empty())
return orig_remark;
return node.remarks;
return remark;
}
@@ -511,36 +534,21 @@ std::string removeEmoji(const std::string &orig_remark)
return remark;
}
std::string addEmoji(std::string remark, int groupID, const string_array &emoji_array)
std::string addEmoji(const nodeInfo &node, const string_array &emoji_array)
{
string_array vArray;
std::string targetRange;
std::string real_rule;
string_size pos;
for(const std::string &x : emoji_array)
{
targetRange = std::to_string(groupID);
vArray = split(x, ",");
if(vArray.size() != 2)
pos = x.rfind(",");
if(pos == x.npos)
continue;
if(startsWith(vArray[0], "!!GROUPID="))
{
pos = vArray[0].find("!!", vArray[0].find("!!") + 2);
if(pos != vArray[0].npos)
{
targetRange = vArray[0].substr(10, pos - 10);
vArray[0] = vArray[0].substr(pos + 2);
}
else
continue;
}
if(matchRange(targetRange, groupID) && regFind(remark, vArray[0]))
{
remark = vArray[1] + " " + remark;
break;
}
if(applyMatcher(x.substr(0, pos), real_rule, node) && real_rule.size() && regFind(node.remarks, real_rule))
return x.substr(pos + 1) + " " + node.remarks;
}
return remark;
return node.remarks;
}
void processRemark(std::string &oldremark, std::string &newremark, string_array &remarks_list)
@@ -934,61 +942,54 @@ void parseGroupTimes(const std::string &src, int *interval, int *tolerance, int
return;
}
void groupGenerate(std::string &rule, std::vector<nodeInfo> &nodelist, std::vector<std::string> &filtered_nodelist, bool add_direct)
void groupGenerate(std::string &rule, std::vector<nodeInfo> &nodelist, string_array &filtered_nodelist, bool add_direct)
{
std::string group, real_rule;
const std::string groupid_regex = R"(^!!(?:GROUPID|INSERT)=([\d\-+!,]+)(?:!!(.*))?$)", group_regex = R"(^!!(?:GROUP)=(.*?)(?:!!(.*))?$)";
if(rule.find("[]") == 0 && add_direct)
if(startsWith(rule, "[]") && add_direct)
{
filtered_nodelist.emplace_back(rule.substr(2));
}
else if(rule.find("!!GROUP=") == 0)
else if(startsWith(rule, "script:"))
{
regGetMatch(rule, group_regex, 3, NULL, &group, &real_rule);
if(real_rule.empty())
duk_context *ctx = duktape_init();
if(ctx)
{
for(nodeInfo &y : nodelist)
defer(duk_destroy_heap(ctx);)
std::string script = fileGet(rule.substr(7), true);
if(duktape_peval(ctx, script) == 0)
{
if(regFind(y.group, group) && std::find(filtered_nodelist.begin(), filtered_nodelist.end(), y.remarks) == filtered_nodelist.end())
filtered_nodelist.emplace_back(y.remarks);
duk_get_global_string(ctx, "filter");
duk_idx_t arr_idx = duk_push_array(ctx), node_idx = 0;
for(nodeInfo &x : nodelist)
{
duktape_push_nodeinfo_arr(ctx, x, -1);
duk_put_prop_index(ctx, arr_idx, node_idx++);
}
if(duk_pcall(ctx, 1) == 0)
{
std::string result_list = duktape_get_res_str(ctx);
filtered_nodelist = split(regTrim(result_list), "\n");
}
else
{
writeLog(0, "Error when trying to evaluate script:\n" + duktape_get_err_stack(ctx), LOG_LEVEL_ERROR);
duk_pop(ctx);
}
}
}
else
{
for(nodeInfo &y : nodelist)
else
{
if(regFind(y.group, group) && regFind(y.remarks, real_rule) && std::find(filtered_nodelist.begin(), filtered_nodelist.end(), y.remarks) == filtered_nodelist.end())
filtered_nodelist.emplace_back(y.remarks);
}
}
}
else if(rule.find("!!GROUPID=") == 0 || rule.find("!!INSERT=") == 0)
{
int dir = rule.find("!!INSERT=") == 0 ? -1 : 1;
regGetMatch(rule, groupid_regex, 3, NULL, &group, &real_rule);
if(real_rule.empty())
{
for(nodeInfo &y : nodelist)
{
if(matchRange(group, dir * y.groupID) && std::find(filtered_nodelist.begin(), filtered_nodelist.end(), y.remarks) == filtered_nodelist.end())
filtered_nodelist.emplace_back(y.remarks);
}
}
else
{
for(nodeInfo &y : nodelist)
{
if(matchRange(group, dir * y.groupID) && regFind(y.remarks, real_rule) && std::find(filtered_nodelist.begin(), filtered_nodelist.end(), y.remarks) == filtered_nodelist.end())
filtered_nodelist.emplace_back(y.remarks);
writeLog(0, "Error when trying to parse script:\n" + duktape_get_err_stack(ctx), LOG_LEVEL_ERROR);
duk_pop(ctx);
}
}
}
else
{
for(nodeInfo &y : nodelist)
for(nodeInfo &x : nodelist)
{
if(regFind(y.remarks, rule) && std::find(filtered_nodelist.begin(), filtered_nodelist.end(), y.remarks) == filtered_nodelist.end())
filtered_nodelist.emplace_back(y.remarks);
if(applyMatcher(rule, real_rule, x) && (real_rule.empty() || regFind(x.remarks, real_rule)) && std::find(filtered_nodelist.begin(), filtered_nodelist.end(), x.remarks) == filtered_nodelist.end())
filtered_nodelist.emplace_back(x.remarks);
}
}
}
@@ -997,17 +998,45 @@ void preprocessNodes(std::vector<nodeInfo> &nodes, extra_settings &ext)
{
std::for_each(nodes.begin(), nodes.end(), [ext](nodeInfo &x)
{
x.remarks = nodeRename(x.remarks, x.groupID, ext.rename_array);
x.remarks = nodeRename(x, ext.rename_array);
if(ext.remove_emoji)
x.remarks = trim(removeEmoji(x.remarks));
if(ext.add_emoji)
x.remarks = addEmoji(x.remarks, x.groupID, ext.emoji_array);
x.remarks = addEmoji(x, ext.emoji_array);
});
if(ext.sort_flag)
{
std::sort(nodes.begin(), nodes.end(), [](const nodeInfo &a, const nodeInfo &b)
bool failed = true;
if(ext.sort_script.size())
{
duk_context *ctx = duktape_init();
if(ctx)
{
defer(duk_destroy_heap(ctx);)
if(duktape_peval(ctx, ext.sort_script) == 0)
{
auto comparer = [&](const nodeInfo &a, const nodeInfo &b)
{
duk_get_global_string(ctx, "compare");
/// push 2 nodeinfo
duktape_push_nodeinfo(ctx, a);
duktape_push_nodeinfo(ctx, b);
/// call function
return duktape_get_res_int(ctx);
};
std::sort(nodes.begin(), nodes.end(), comparer);
failed = false;
}
else
{
writeLog(0, "Error when trying to parse script:\n" + duktape_get_err_stack(ctx), LOG_LEVEL_ERROR);
duk_pop(ctx); /// pop err
}
}
}
if(failed) std::sort(nodes.begin(), nodes.end(), [](const nodeInfo &a, const nodeInfo &b)
{
return a.remarks < b.remarks;
});

View File

@@ -36,6 +36,7 @@ struct extra_settings
tribool udp = false;
tribool tfo = false;
tribool skip_cert_verify = false;
std::string sort_script;
};
void rulesetToClash(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset_content_array, bool overwrite_original_rules, bool new_field_name);

View File

@@ -59,6 +59,7 @@ static inline void curl_set_common_options(CURL *curl_handle, const char *url)
curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl_handle, CURLOPT_MAXREDIRS, 20L);
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 15L);
@@ -109,12 +110,12 @@ static std::string curlGet(const std::string &url, const std::string &proxy, std
static std::string dataGet(const std::string &url)
{
if (!startsWith(url, "data:"))
return "";
return std::string();
std::string::size_type comma = url.find(',');
if (comma == std::string::npos)
return "";
if (comma == std::string::npos || comma == url.size() - 1)
return std::string();
std::string data = UrlDecode(url.substr(comma));
std::string data = UrlDecode(url.substr(comma + 1));
if (endsWith(url.substr(0, comma), ";base64")) {
return urlsafe_base64_decode(data);
} else {