diff --git a/CMakeLists.txt b/CMakeLists.txt index 750f978..71401ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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() diff --git a/base/pref-new.yml b/base/pref-new.yml index 92da130..be5cf9a 100644 --- a/base/pref-new.yml +++ b/base/pref-new.yml @@ -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 diff --git a/base/pref.ini b/base/pref.ini index f3c922e..388c82c 100644 --- a/base/pref.ini +++ b/base/pref.ini @@ -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 diff --git a/cmake/FindDuktape.cmake b/cmake/FindDuktape.cmake new file mode 100644 index 0000000..1b87aec --- /dev/null +++ b/cmake/FindDuktape.cmake @@ -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) diff --git a/scripts/Dockerfile b/scripts/Dockerfile index 018376e..355d480 100644 --- a/scripts/Dockerfile +++ b/scripts/Dockerfile @@ -3,7 +3,20 @@ MAINTAINER Tindy X # 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 diff --git a/scripts/build.alpine.release.sh b/scripts/build.alpine.release.sh index 6079fbb..5fa222f 100644 --- a/scripts/build.alpine.release.sh +++ b/scripts/build.alpine.release.sh @@ -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 diff --git a/scripts/build.clang.sh b/scripts/build.clang.sh index 5dc9165..ae2670d 100644 --- a/scripts/build.clang.sh +++ b/scripts/build.clang.sh @@ -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 diff --git a/scripts/build.macos.release.sh b/scripts/build.macos.release.sh index 0eb76b7..a4f6146 100644 --- a/scripts/build.macos.release.sh +++ b/scripts/build.macos.release.sh @@ -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 diff --git a/scripts/config.termux.sh b/scripts/config.termux.sh index 72521a2..b49a2ed 100644 --- a/scripts/config.termux.sh +++ b/scripts/config.termux.sh @@ -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 ../../.. diff --git a/src/interfaces.cpp b/src/interfaces.cpp index c1b80ab..c521147 100644 --- a/src/interfaces.cpp +++ b/src/interfaces.cpp @@ -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 T safe_as (const YAML::Node& node) return T(); }; +template void operator >>= (const YAML::Node& node, T& i) +{ + i = safe_as(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(section["enable_filter"]) ? safe_as(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(section["tcp_fast_open_flag"])); scv_flag.set(safe_as(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 nodes; - string_array extra_group, extra_ruleset, include_remarks, exclude_remarks; - std::vector 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 '"< do_concat(__defer_deleter_,__LINE__) (nullptr, [&](...){x}); +template 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 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 string_array; diff --git a/src/nodemanip.cpp b/src/nodemanip.cpp index cca392d..cef809b 100644 --- a/src/nodemanip.cpp +++ b/src/nodemanip.cpp @@ -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 &allNodes, int groupID, std int linkType = -1; std::vector 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 &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 &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 &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 &allNodes, int groupID, std return -1; } node.groupID = groupID; + if(custom_group.size()) + node.group = custom_group; allNodes.push_back(node); } return 0; diff --git a/src/script.cpp b/src/script.cpp new file mode 100644 index 0000000..b70182a --- /dev/null +++ b/src/script.cpp @@ -0,0 +1,224 @@ +#include +#include +#include +#include + +#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<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; +} diff --git a/src/script_duktape.h b/src/script_duktape.h new file mode 100644 index 0000000..f031f0d --- /dev/null +++ b/src/script_duktape.h @@ -0,0 +1,19 @@ +#ifndef SCRIPT_DUKTAPE_H_INCLUDED +#define SCRIPT_DUKTAPE_H_INCLUDED + +#include +#include + +#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 diff --git a/src/speedtestutil.cpp b/src/speedtestutil.cpp index 7adc616..a49a2a5 100644 --- a/src/speedtestutil.cpp +++ b/src/speedtestutil.cpp @@ -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 &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 &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>= 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>= 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>= 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>= 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>= 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 &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 { diff --git a/src/speedtestutil.h b/src/speedtestutil.h index 2bb3076..0cda682 100644 --- a/src/speedtestutil.h +++ b/src/speedtestutil.h @@ -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); diff --git a/src/subexport.cpp b/src/subexport.cpp index 2575f8e..6043e10 100644 --- a/src/subexport.cpp +++ b/src/subexport.cpp @@ -10,6 +10,7 @@ #include "string_hash.h" #include "logger.h" #include "templates.h" +#include "script_duktape.h" #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #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 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 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 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 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 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 &nodelist, std::vector &filtered_nodelist, bool add_direct) +void groupGenerate(std::string &rule, std::vector &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 &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; }); diff --git a/src/subexport.h b/src/subexport.h index 9395185..fda689d 100644 --- a/src/subexport.h +++ b/src/subexport.h @@ -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_array, bool overwrite_original_rules, bool new_field_name); diff --git a/src/webget.cpp b/src/webget.cpp index 1ae4f7d..8947a7f 100644 --- a/src/webget.cpp +++ b/src/webget.cpp @@ -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 {