From 9797197c85883ccc43941967225f98e570864983 Mon Sep 17 00:00:00 2001 From: Tindy X <49061470+tindy2013@users.noreply.github.com> Date: Mon, 20 Sep 2021 17:02:13 +0800 Subject: [PATCH] Add support for using TOML as configuration file format Add basic support for trojan-grpc nodes. Refactor codes. --- CMakeLists.txt | 3 + base/config/example_external_config.toml | 30 ++ base/pref.example.toml | 322 +++++++++++++ base/snippets/emoji.toml | 160 +++++++ base/snippets/groups.toml | 91 ++++ base/snippets/groups_forcerule.toml | 74 +++ base/snippets/rename_node.toml | 177 +++++++ base/snippets/rulesets.toml | 80 ++++ scripts/build.alpine.release.sh | 6 + scripts/build.macos.release.sh | 6 + scripts/build.windows.release.sh | 6 + src/config/binding.h | 361 ++++++++++++++ src/config/crontask.h | 16 + src/config/def.h | 16 + src/config/proxygroup.h | 63 +++ src/config/regmatch.h | 15 + src/config/ruleset.h | 29 ++ src/generator/config/nodemanip.cpp | 4 +- src/generator/config/nodemanip.h | 5 +- src/generator/config/subexport.cpp | 379 ++++++--------- src/generator/config/subexport.h | 26 +- src/handler/interfaces.cpp | 572 +++++++++++++++++++---- src/handler/interfaces.h | 3 +- src/handler/multithread.cpp | 20 +- src/handler/multithread.h | 17 +- src/handler/webget.cpp | 2 +- src/main.cpp | 56 ++- src/parser/infoparser.cpp | 27 +- src/parser/infoparser.h | 3 +- src/parser/subparser.cpp | 41 +- src/parser/subparser.h | 2 +- src/script/cron.cpp | 33 +- src/server/webserver.h | 46 +- src/server/webserver_libevent.cpp | 183 ++++---- src/utils/tribool.h | 2 + 35 files changed, 2345 insertions(+), 531 deletions(-) create mode 100644 base/config/example_external_config.toml create mode 100644 base/pref.example.toml create mode 100644 base/snippets/emoji.toml create mode 100644 base/snippets/groups.toml create mode 100644 base/snippets/groups_forcerule.toml create mode 100644 base/snippets/rename_node.toml create mode 100644 base/snippets/rulesets.toml create mode 100644 src/config/binding.h create mode 100644 src/config/crontask.h create mode 100644 src/config/def.h create mode 100644 src/config/proxygroup.h create mode 100644 src/config/regmatch.h create mode 100644 src/config/ruleset.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 02aabba..24da98f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,6 +85,9 @@ ADD_DEFINITIONS(-DCURL_STATICLIB) FIND_PACKAGE(Rapidjson REQUIRED) INCLUDE_DIRECTORIES(${RAPIDJSON_INCLUDE_DIRS}) +FIND_PACKAGE(toml11 REQUIRED) +INCLUDE_DIRECTORIES(${TOML11_INCLUDE_DIRS}) + PKG_CHECK_MODULES(YAML_CPP yaml-cpp>=0.6.3 REQUIRED) FIND_LIBRARY(YAML_CPP_LIBRARY NAMES yaml-cpp yaml-cppd PATHS ${YAML_CPP_LIBRARY_DIRS}) LINK_DIRECTORIES(${YAML_CPP_LIBRARY_DIRS}) diff --git a/base/config/example_external_config.toml b/base/config/example_external_config.toml new file mode 100644 index 0000000..820c089 --- /dev/null +++ b/base/config/example_external_config.toml @@ -0,0 +1,30 @@ +version = 1 +[custom] +enable_rule_generator = false +overwrite_original_rules = false + +# Options for custom base configuration file +clash_rule_base = "base/forcerule.yml" +#surge_rule_base = "base/surge.conf" +#surfboard_rule_base = "base/surfboard.conf" +#mellow_rule_base = "base/mellow.conf" +#quan_rule_base = "base/quan.conf" +#quanx_rule_base = "base/quanx.conf" + +# Options for adding emojis +#add_emoji = true +#remove_old_emoji = true + +# Options for filtering nodes +#include_remarks = [] +#exclude_remarks = [] + +[[custom_groups]] +import = "snippets/groups_forcerule.toml" + +#[[rulesets]] +#import = "" + +[[template_args]] +key = "clash.dns.port" +value = 5353 \ No newline at end of file diff --git a/base/pref.example.toml b/base/pref.example.toml new file mode 100644 index 0000000..cdea62f --- /dev/null +++ b/base/pref.example.toml @@ -0,0 +1,322 @@ +version = 1 +[common] +# API mode, set to true to prevent loading local subscriptions or serving local files directly +api_mode = false + +# Access token used for performing critical action through Web interface +api_access_token = "password" + +# Default URLs, used when no URL is provided in request, use "|" to separate multiple subscription links, supports local files/URL +default_url = [] + +# Insert subscription links to requests. Can be used to add node(s) to all exported subscriptions. +enable_insert = true +# URLs to insert before subscription links, can be used to add node(s) to all exported subscriptions, supports local files/URL +insert_url = [""] +# Prepend inserted URLs to subscription links. Nodes in insert_url will be added to groups first with non-group-specific match pattern. +prepend_insert_url = true + +# Exclude nodes which remarks match the following patterns. Supports regular expression. +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. +# Script path: Set value to "path:/path/to/script.js". +#filter_script = ''' +#function filter(node) { +# const info = JSON.parse(node.ProxyInfo); +# if(info.EncryptMethod.includes('chacha20')) +# return true; +# return false; +#} +#''' + +# Setting an external config file as default when none is specified, supports local files/URL +# default_external_config = "config/example_external_config.toml" + +# The file scope limit of the 'rule_base' options in external configs. +base_path = "base" + +# Clash config base used by the generator, supports local files/URL +clash_rule_base = "base/all_base.tpl" + +# Surge config base used by the generator, supports local files/URL +surge_rule_base = "base/all_base.tpl" + +# Surfboard config base used by the generator, supports local files/URL +surfboard_rule_base = "base/all_base.tpl" + +# Mellow config base used by the generator, supports local files/URL +mellow_rule_base = "base/all_base.tpl" + +# Quantumult config base used by the generator, supports local files/URL +quan_rule_base = "base/all_base.tpl" + +# Quantumult X config base used by the generator, supports local files/URL +quanx_rule_base = "base/all_base.tpl" + +# Loon config base used by the generator, supports local files/URL +loon_rule_base = "base/all_base.tpl" + +# Shadowsocks Android config base used by the generator, supports local files/URL +sssub_rule_base = "base/all_base.tpl" + +# Proxy used to download rulesets or subscriptions, set to NONE or empty to disable it, set to SYSTEM to use system proxy. +# Accept cURL-supported proxies (http:// https:// socks4a:// socks5://) + +proxy_config = "SYSTEM" +proxy_ruleset = "SYSTEM" +proxy_subscription = "NONE" + +# Append a proxy type string ([SS] [SSR] [VMess]) to node remark. +append_proxy_type = false + +[[userinfo.stream_rule]] +# Rules to extract stream data from node +# Format: full_match_regex|new_format_regex +# where new_format_regex should be like "total=$1&left=$2&used=$3" +match = '^剩余流量:(.*?)\|总流量:(.*)$' +replace = 'total=$2&left=$1' + +[[userinfo.stream_rule]] +match = '^剩余流量:(.*?) (.*)$' +replace = 'total=$1&left=$2' + +[[userinfo.stream_rule]] +match = '^Bandwidth: (.*?)/(.*)$' +replace = 'used=$1&total=$2' + +[[userinfo.stream_rule]] +match = '^.*剩余(.*?)(?:\s*?)@(?:.*)$' +replace = 'total=$1' + +[[userinfo.time_rule]] +# Rules to extract expire time data from node +# Format: full_match_regex|new_format_regex +# where new_format_regex should follow this example: yyyy:mm:dd:hh:mm:ss +match = '^过期时间:(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)$' +replace = '$1:$2:$3:$4:$5:$6' + +[[userinfo.time_rule]] +match = '^到期时间:(\d+)-(\d+)-(\d+)$' +replace = '$1:$2:$3:0:0:0' + +[[userinfo.time_rule]] +match = '^Smart Access expire: (\d+)/(\d+)/(\d+)$' +replace = '$1:$2:$3:0:0:0' + +[node_pref] +udp_flag = true +tcp_fast_open_flag = false +skip_cert_verify_flag = true +tls13_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) { +# return info_a.Remark > info_b.Remark; +#} +#''' + +filter_deprecated_nodes = false +append_sub_userinfo = true +clash_use_new_field_name = true + +# Generate style of the proxies section of Clash subscriptions. +# Supported styles: block, flow, compact +# Block: - name: name1 Flow: - {name: name1, key: value} Compact: [{name: name1, key: value},{name: name2, key: value}] +# key: value - {name: name2, key: value} +# - name: name2 +# key: value +clash_proxies_style = "flow" + +[[node_pref.rename_node]] +match = '\(?((x|X)?(\d+)(\.?\d+)?)((\s?倍率?)|(x|X))\)?' +replace = "$1x" + +[managed_config] +# Append a '#!MANAGED-CONFIG' info to Surge configurations +write_managed_config = true + +# Address prefix for MANAGED-CONFIG info, without the trailing "/". +managed_config_prefix = "http://127.0.0.1:25500" + +# Managed config update interval in seconds, determine how long the config will be updated. +config_update_interval = 86400 + +# If config_update_strict is set to true, Surge will require a force update after the interval. +config_update_strict = false + +# Device ID to be written to rewrite scripts for some version of Quantumult X +quanx_device_id = "" + +[surge_external_proxy] +#surge_ssr_path = "/usr/bin/ssr-local" +resolve_hostname = true + +[emojis] +add_emoji = false +remove_old_emoji = true + +[[emojis.emoji]] +#match = '(流量|时间|应急)' +#emoji = '🏳️‍🌈' +import = "snippets/emoji.toml" + +# [[custom_groups]] +# name = "Auto" +# type = "url-test" +# rule = [".*"] +# url = "http://www.gstatic.com/generate_204" +# interval = 300 +# tolerance = 150 +# lazy = true + +# [[custom_groups]] +# name = "Proxy" +# type = "select" +# rule = [".*", "[]DIRECT"] +# disable_udp = false + +# [[custom_groups]] +# name = "LoadBalance" +# type = "load-balance" +# rule = [".*", "[]Proxy", "[]DIRECT"] +# interval = 100 +# strategy = "consistent-hashing" +# url = "http://www.gstatic.com/generate_204" + +[[custom_groups]] +import = "snippets/groups.toml" + +[ruleset] +# Enable generating rules with rulesets +enabled = true + +# Overwrite the existing rules in rule_base +overwrite_original_rules = false + +# Perform a ruleset update on request +update_ruleset_on_request = false + +# [[rulesets]] +# group = "Proxy" +# ruleset = "https://raw.githubusercontent.com/DivineEngine/Profiles/master/Surge/Ruleset/Unbreak.list" +# type = "surge-ruleset" +# interval = 86400 + +[[rulesets]] +import = "snippets/rulesets.toml" + +[template] +template_path = "template" + +[[template.globals]] +key = "clash.http_port" +value = "7890" + +[[template.globals]] +key = "clash.socks_port" +value = "7891" + +[[template.globals]] +key = "clash.allow_lan" +value = "true" + +[[template.globals]] +key = "clash.log_level" +value = "info" + +[[aliases]] +uri = "/clash" +target = "/sub?target=clash" + +[[aliases]] +uri = "/clashr" +target = "/sub?target=clashr" + +[[aliases]] +uri = "/surge" +target = "/sub?target=surge" + +[[aliases]] +uri = "/quan" +target = "/sub?target=quan" + +[[aliases]] +uri = "/quanx" +target = "/sub?target=quanx" + +[[aliases]] +uri = "/mellow" +target = "/sub?target=mellow" + +[[aliases]] +uri = "/surfboard" +target = "/sub?target=surfboard" + +[[aliases]] +uri = "/loon" +target = "/sub?target=loon" + +[[aliases]] +uri = "/ss" +target = "/sub?target=ss" + +[[aliases]] +uri = "/ssd" +target = "/sub?target=ssd" + +[[aliases]] +uri = "/sssub" +target = "/sub?target=sssub" + +[[aliases]] +uri = "/ssr" +target = "/sub?target=ssr" + +[[aliases]] +uri = "/v2ray" +target = "/sub?target=v2ray" + +[[aliases]] +uri = "/trojan" +target = "/sub?target=trojan" + +[[aliases]] +uri = "/test" +target = "/render?path=templates/test.tpl" + +#[[tasks]] +#name = "tick" +#cronexp = "0/10 * * * * ?" +#path = "tick.js" +#timeout = 3 + +[server] +listen = "0.0.0.0" +port = 25500 +serve_file_root = "web" + +[advanced] +log_level = "debug" +print_debug_info = true +max_pending_connections = 10240 +max_concurrent_threads = 4 +max_allowed_rulesets = 64 +max_allowed_rules = 0 +max_allowed_download_size = 0 +enable_cache = true +cache_subscription = 60 +cache_config = 300 +cache_ruleset = 21600 +script_clean_context = true +async_fetch_ruleset = false +skip_failed_links = true diff --git a/base/snippets/emoji.toml b/base/snippets/emoji.toml new file mode 100644 index 0000000..b871e1d --- /dev/null +++ b/base/snippets/emoji.toml @@ -0,0 +1,160 @@ +[[emoji]] +match = "(?i:流量|时间|应急|过期|Bandwidth|expire)" +emoji = "🏳️‍🌈" + +[[emoji]] +match = "AC" +emoji = "🇦🇨" + +[[emoji]] +match = "(AR|阿根廷)" +emoji = "🇦🇷" + +[[emoji]] +match = "(奥地利|维也纳)" +emoji = "🇦🇹" + +[[emoji]] +match = "(AU|Australia|Sydney|澳大利亚|悉尼)" +emoji = "🇦🇺" + +[[emoji]] +match = "BE" +emoji = "🇧🇪" + +[[emoji]] +match = "(BR|Brazil|巴西|圣保罗)" +emoji = "🇧🇷" + +[[emoji]] +match = "(Canada|加拿大|蒙特利尔|温哥华|楓葉|枫叶)" +emoji = "🇨🇦" + +[[emoji]] +match = "(瑞士|苏黎世)" +emoji = "🇨🇭" + +[[emoji]] +match = "(DE|Germany|法兰克福|德)" +emoji = "🇩🇪" + +[[emoji]] +match = "丹麦" +emoji = "🇩🇰" + +[[emoji]] +match = "ES" +emoji = "🇪🇸" + +[[emoji]] +match = "EU" +emoji = "🇪🇺" + +[[emoji]] +match = "(Finland|芬兰|赫尔辛基)" +emoji = "🇫🇮" + +[[emoji]] +match = "(FR|France|法国|巴黎)" +emoji = "🇫🇷" + +[[emoji]] +match = "(?i:UK|England|United.*?Kingdom|英国|[^-]英|伦敦)" +emoji = "🇬🇧" + +[[emoji]] +match = "(?i:HK|Hong.*?Kong|HKT|HKBN|HGC|WTT|CMI|[^-]港)" +emoji = "🇭🇰" + +[[emoji]] +match = "(Indonesia|印尼|印度尼西亚|雅加达)" +emoji = "🇮🇩" + +[[emoji]] +match = "(Ireland|爱尔兰|都柏林)" +emoji = "🇮🇪" + +[[emoji]] +match = "(India|印度|孟买)" +emoji = "🇮🇳" + +[[emoji]] +match = "(Italy|意大利|米兰)" +emoji = "🇮🇹" + +[[emoji]] +match = "(JP|Japan|日本|[^-]日)" +emoji = "🇯🇵" + +[[emoji]] +match = "(KP|朝鲜)" +emoji = "🇰🇵" + +[[emoji]] +match = "(KR|Korea|KOR|首尔|韩|韓)" +emoji = "🇰🇷" + +[[emoji]] +match = "(MO|Macao|澳门|CTM)" +emoji = "🇲🇴" + +[[emoji]] +match = "(MY|Malaysia|马来西亚)" +emoji = "🇲🇾" + +[[emoji]] +match = "(NL|Netherlands|荷兰|阿姆斯特丹)" +emoji = "🇳🇱" + +[[emoji]] +match = "(PH|Philippines|菲律宾)" +emoji = "🇵🇭" + +[[emoji]] +match = "(RO|罗马尼亚)" +emoji = "🇷🇴" + +[[emoji]] +match = "(RU|Russia|伯力|莫斯科|圣彼得堡|西伯利亚|新西伯利亚|俄罗斯|[^-]俄)" +emoji = "🇷🇺" + +[[emoji]] +match = "(沙特|迪拜)" +emoji = "🇸🇦" + +[[emoji]] +match = "(SE|Sweden)" +emoji = "🇸🇪" + +[[emoji]] +match = "(SG|Singapore|新加坡|狮城|[^-]新)" +emoji = "🇸🇬" + +[[emoji]] +match = "(TH|Thailand|泰国|曼谷)" +emoji = "🇹🇭" + +[[emoji]] +match = "(TR|Turkey|土耳其|伊斯坦布尔)" +emoji = "🇹🇷" + +[[emoji]] +match = "(?i:US|America|United.*?States|美国|[^-]美|波特兰|达拉斯|俄勒冈|凤凰城|费利蒙|硅谷|拉斯维加斯|洛杉矶|圣何塞|圣克拉拉|西雅图|芝加哥)" +emoji = "🇺🇲" + +[[emoji]] +match = "(VN|越南)" +emoji = "🇻🇳" + +[[emoji]] +match = "(ZA|南非)" +emoji = "🇿🇦" + +[[emoji]] +match = "(?i:TW|Taiwan|新北|彰化|CHT|台湾|[^-]台|HINET)" +emoji = "🇨🇳" + +[[emoji]] +match = "(?i:CN|China|回国|中国|江苏|北京|上海|广州|深圳|杭州|徐州|青岛|宁波|镇江|back)" +emoji = "🇨🇳" + diff --git a/base/snippets/groups.toml b/base/snippets/groups.toml new file mode 100644 index 0000000..78824f5 --- /dev/null +++ b/base/snippets/groups.toml @@ -0,0 +1,91 @@ +[[custom_groups]] +name = "🔰 节点选择" +type = "select" +rule = ["[]♻️ 自动选择", "[]🎯 全球直连", ".*"] + +[[custom_groups]] +name = "♻️ 自动选择" +type = "url-test" +rule = [".*"] +url = "http://www.gstatic.com/generate_204" +interval = 300 + +[[custom_groups]] +name = "🎥 NETFLIX" +type = "select" +rule = [ + "[]🔰 节点选择", + "[]♻️ 自动选择", + "[]🎯 全球直连", + ".*" +] + +[[custom_groups]] +name = "⛔️ 广告拦截" +type = "select" +rule = ["[]🛑 全球拦截", +"[]🎯 全球直连", +"[]🔰 节点选择" +] + +[[custom_groups]] +name = "🚫 运营劫持" +type = "select" +rule = ["[]🛑 全球拦截", +"[]🎯 全球直连", +"[]🔰 节点选择"] + +[[custom_groups]] +name = "🌍 国外媒体" +type = "select" +rule = ["[]🔰 节点选择", +"[]♻️ 自动选择", +"[]🎯 全球直连", +".*"] + +[[custom_groups]] +name = "🌏 国内媒体" +type = "select" +rule = ["[]🎯 全球直连", +"(HGC|HKBN|PCCW|HKT|深台|彰化|新北|台|hk|港|tw)", +"[]🔰 节点选择"] + +[[custom_groups]] +name = "Ⓜ️ 微软服务" +type = "select" +rule = ["[]🎯 全球直连", +"[]🔰 节点选择", +".*"] + +[[custom_groups]] +name = "📲 电报信息" +type = "select" +rule = ["[]🔰 节点选择", +"[]🎯 全球直连", +".*"] + +[[custom_groups]] +name = "🍎 苹果服务" +type = "select" +rule = ["[]🔰 节点选择", +"[]🎯 全球直连", +"[]♻️ 自动选择", +".*"] + +[[custom_groups]] +name = "🎯 全球直连" +type = "select" +rule = ["[]DIRECT"] + +[[custom_groups]] +name = "🛑 全球拦截" +type = "select" +rule = ["[]REJECT", "[]DIRECT"] + +[[custom_groups]] +name = "🐟 漏网之鱼" +type = "select" +rule = ["[]🔰 节点选择", +"[]🎯 全球直连", +"[]♻️ 自动选择", +".*"] diff --git a/base/snippets/groups_forcerule.toml b/base/snippets/groups_forcerule.toml new file mode 100644 index 0000000..230d8bc --- /dev/null +++ b/base/snippets/groups_forcerule.toml @@ -0,0 +1,74 @@ +# for forcerule.yml + +[[custom_groups]] +name = "Proxy" +type = "select" +rule = [".*", "[]AUTO", "[]DIRECT", ".*"] + +[[custom_groups]] +name = "AUTO" +type = "url-test" +rule = [".*"] +url = "http://www.gstatic.com/generate_204" +interval = 300 + +[[custom_groups]] +name = "google" +type = "select" +rule = [".*"] + +[[custom_groups]] +name = "netflix" +type = "select" +rule = [".*"] + +[[custom_groups]] +name = "动画疯" +type = "select" +rule = ["(深台|彰化|新北|台)"] + +[[custom_groups]] +name = "fox+" +type = "select" +rule = ["(HGC|HKBN|PCCW|HKT|深台|彰化|新北|台|新加坡|sg|hk|tw)"] + +[[custom_groups]] +name = "美区影视" +type = "select" +rule = ["(美|美国)"] + +[[custom_groups]] +name = "Global_media" +type = "select" +rule = [".*"] + +[[custom_groups]] +name = "Domestic" +type = "select" +rule = ["[]DIRECT", "[]Proxy"] + +[[custom_groups]] +name = "Apple" +type = "select" +rule = ["[]DIRECT", "[]Proxy"] + +[[custom_groups]] +name = "Final" +type = "select" +rule = ["[]Proxy", "[]DIRECT"] + +[[custom_groups]] +name = "屏蔽广告" +type = "select" +rule = ["[]REJECT", "[]DIRECT"] + +[[custom_groups]] +name = "UnblockNeteaseMusic" +type = "select" +rule = ["云音乐解锁", "[]DIRECT"] + +[[custom_groups]] +name = "Telegram" +type = "select" +rule = ["新加坡", "[]Proxy"] + diff --git a/base/snippets/rename_node.toml b/base/snippets/rename_node.toml new file mode 100644 index 0000000..bfed86d --- /dev/null +++ b/base/snippets/rename_node.toml @@ -0,0 +1,177 @@ +# short names +[[rename_node]] +match = "中国" +replace = "中" + +[[rename_node]] +match = "徐州" +replace = "徐" + +[[rename_node]] +match = "深圳" +replace = "深" + +[[rename_node]] +match = "上海" +replace = "沪" + +[[rename_node]] +match = "广州" +replace = "穗" + +[[rename_node]] +match = "宁波" +replace = "甬" + +[[rename_node]] +match = "贵阳" +replace = "筑" + +[[rename_node]] +match = "武汉" +replace = "汉" + +[[rename_node]] +match = "南京" +replace = "宁" + +[[rename_node]] +match = "天津" +replace = "津" + +[[rename_node]] +match = "北京" +replace = "京" + +[[rename_node]] +match = "沈阳" +replace = "沈" + +[[rename_node]] +match = "江苏" +replace = "苏" + +[[rename_node]] +match = "浙江" +replace = "浙" + +[[rename_node]] +match = "安徽" +replace = "皖" + +[[rename_node]] +match = "福建" +replace = "闽" + +[[rename_node]] +match = "湖南" +replace = "湘" + +[[rename_node]] +match = "广东" +replace = "粤" + +[[rename_node]] +match = "海南" +replace = "琼" + +[[rename_node]] +match = "四川" +replace = "川" + +[[rename_node]] +match = "贵州" +replace = "贵" + +[[rename_node]] +match = "云南" +replace = "云" + +[[rename_node]] +match = "青海" +replace = "青" + +[[rename_node]] +match = "台湾" +replace = "台" + +[[rename_node]] +match = "香港" +replace = "港" + +[[rename_node]] +match = "澳门" +replace = "澳" + +[[rename_node]] +match = "美国" +replace = "美" + +[[rename_node]] +match = "英国" +replace = "英" + +[[rename_node]] +match = "加拿大" +replace = "加" + +[[rename_node]] +match = "法国" +replace = "法" + +[[rename_node]] +match = "德国" +replace = "德" + +[[rename_node]] +match = "瑞士" +replace = "瑞" + +[[rename_node]] +match = "日本" +replace = "日" + +[[rename_node]] +match = "韩国" +replace = "韩" + +[[rename_node]] +match = "芬兰" +replace = "芬" + +[[rename_node]] +match = "印度" +replace = "印" + +[[rename_node]] +match = "泰国" +replace = "泰" + +[[rename_node]] +match = "越南" +replace = "越" + +[[rename_node]] +match = "新加坡" +replace = "新" + +[[rename_node]] +match = "意大利" +replace = "意" + +[[rename_node]] +match = "菲律宾" +replace = "菲" + +[[rename_node]] +match = "俄罗斯" +replace = "俄" + +[[rename_node]] +match = "土耳其" +replace = "土" + +# times RE +[[rename_node]] +match = '\(?((x|X)?(\d+)(\.?\d+)?)((\s?倍率?)|(x|X))\)?' +replace = "$1x" diff --git a/base/snippets/rulesets.toml b/base/snippets/rulesets.toml new file mode 100644 index 0000000..2049015 --- /dev/null +++ b/base/snippets/rulesets.toml @@ -0,0 +1,80 @@ +[[rulesets]] +group = "🎯 全球直连" +ruleset = "rules/LocalAreaNetwork.list" + +[[rulesets]] +group = "Ⓜ️ 微软服务" +ruleset = "rules/MSServices.list" + +[[rulesets]] +group = "🎯 全球直连" +ruleset = "rules/DivineEngine/Surge/Ruleset/Unbreak.list" + +[[rulesets]] +group = "🛑 全球拦截" +ruleset = "rules/NobyDa/Surge/AdRule.list" + +[[rulesets]] +group = "🛑 全球拦截" +ruleset = "rules/DivineEngine/Surge/Ruleset/Guard/Hijacking.list" + +[[rulesets]] +group = ";🎥 NETFLIX" +ruleset = "rules/DivineEngine/Surge/Ruleset/StreamingMedia/Video/Netflix.list" + +[[rulesets]] +group = "🌍 国外媒体" +ruleset = "rules/DivineEngine/Surge/Ruleset/StreamingMedia/Streaming.list" + +[[rulesets]] +group = "🌏 国内媒体" +ruleset = "rules/lhie1/Surge/Surge 3/Provider/Media/Bilibili.list" + +[[rulesets]] +group = "🌏 国内媒体" +ruleset = "rules/lhie1/Surge/Surge 3/Provider/Media/iQiyi.list" + +[[rulesets]] +group = "🌏 国内媒体" +ruleset = "rules/lhie1/Surge/Surge 3/Provider/Media/Letv.list" + +[[rulesets]] +group = "🌏 国内媒体" +ruleset = "rules/lhie1/Surge/Surge 3/Provider/Media/MOO.list" + +[[rulesets]] +group = "🌏 国内媒体" +ruleset = "rules/lhie1/Surge/Surge 3/Provider/Media/Tencent Video.list" + +[[rulesets]] +group = "🌏 国内媒体" +ruleset = "rules/lhie1/Surge/Surge 3/Provider/Media/Youku.list" + +[[rulesets]] +group = "📲 电报信息" +ruleset = "rules/DivineEngine/Surge/Ruleset/Extra/Telegram/Telegram.list" + +[[rulesets]] +group = "🔰 节点选择" +ruleset = "rules/DivineEngine/Surge/Ruleset/Global.list" + +[[rulesets]] +group = "🍎 苹果服务" +ruleset = "rules/DivineEngine/Surge/Ruleset/Extra/Apple/Apple.list" + +[[rulesets]] +group = "🎯 全球直连" +ruleset = "rules/DivineEngine/Surge/Ruleset/China.list" + +[[rulesets]] +group = "🎯 全球直连" +ruleset = "rules/NobyDa/Surge/Download.list" + +[[rulesets]] +group = "🎯 全球直连" +ruleset = "[]GEOIP,CN" + +[[rulesets]] +group = "🐟 漏网之鱼" +ruleset = "[]FINAL" + diff --git a/scripts/build.alpine.release.sh b/scripts/build.alpine.release.sh index 21e816c..e419934 100644 --- a/scripts/build.alpine.release.sh +++ b/scripts/build.alpine.release.sh @@ -38,6 +38,12 @@ install -d /usr/include/date/ install -m644 libcron/externals/date/include/date/* /usr/include/date/ cd .. +git clone https://github.com/ToruNiina/toml11 --depth=1 +cd toml11 +cmake . +make install -j4 +cd .. + export PKG_CONFIG_PATH=/usr/lib64/pkgconfig cmake -DCMAKE_BUILD_TYPE=Release . make -j2 diff --git a/scripts/build.macos.release.sh b/scripts/build.macos.release.sh index bf39d5a..01aa7ec 100644 --- a/scripts/build.macos.release.sh +++ b/scripts/build.macos.release.sh @@ -39,6 +39,12 @@ install -d /usr/local/include/date/ install -m644 libcron/externals/date/include/date/* /usr/local/include/date/ cd .. +git clone https://github.com/ToruNiina/toml11 --depth=1 +cd toml11 +cmake . +make install -j4 +cd .. + cp /usr/local/lib/libevent.a . cp /usr/local/opt/zlib/lib/libz.a . cp /usr/local/lib/libpcre2-8.a . diff --git a/scripts/build.windows.release.sh b/scripts/build.windows.release.sh index 1b1c6e3..79e642a 100644 --- a/scripts/build.windows.release.sh +++ b/scripts/build.windows.release.sh @@ -42,6 +42,12 @@ cmake -DRAPIDJSON_BUILD_DOC=OFF -DRAPIDJSON_BUILD_EXAMPLES=OFF -DRAPIDJSON_BUILD make install -j4 cd .. +git clone https://github.com/ToruNiina/toml11 --depth=1 +cd toml11 +cmake -DCMAKE_INSTALL_PREFIX="$MINGW_PREFIX" -G "Unix Makefiles" . +make install -j4 +cd .. + rm -f C:/Strawberry/perl/bin/pkg-config C:/Strawberry/perl/bin/pkg-config.bat cmake -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" . make -j4 diff --git a/src/config/binding.h b/src/config/binding.h new file mode 100644 index 0000000..818b609 --- /dev/null +++ b/src/config/binding.h @@ -0,0 +1,361 @@ +#ifndef BINDING_H_INCLUDED +#define BINDING_H_INCLUDED + +#include + +#include "crontask.h" +#include "proxygroup.h" +#include "regmatch.h" +#include "ruleset.h" + +namespace toml +{ + template<> + struct from + { + static ProxyGroupConfig from_toml(const value& v) + { + ProxyGroupConfig conf; + conf.Name = toml::find(v, "name"); + String type = toml::find(v, "type"); + String strategy = toml::find_or(v, "strategy", ""); + switch(hash_(type)) + { + case "select"_hash: + conf.Type = ProxyGroupType::Select; + break; + case "url-test"_hash: + conf.Type = ProxyGroupType::URLTest; + conf.Url = toml::find(v, "url"); + conf.Interval = toml::find(v, "interval"); + conf.Tolerance = toml::find_or(v, "tolerance", 0); + if(v.contains("lazy")) + conf.Lazy = toml::find_or(v, "lazy", false); + break; + case "load-balance"_hash: + conf.Type = ProxyGroupType::LoadBalance; + conf.Url = toml::find(v, "url"); + conf.Interval = toml::find(v, "interval"); + switch(hash_(strategy)) + { + case "consistent-hashing"_hash: + conf.Strategy = BalanceStrategy::ConsistentHashing; + break; + case "round-robin"_hash: + conf.Strategy = BalanceStrategy::RoundRobin; + break; + } + break; + case "fallback"_hash: + conf.Type = ProxyGroupType::Fallback; + conf.Url = toml::find(v, "url"); + conf.Interval = toml::find(v, "interval"); + break; + case "relay"_hash: + conf.Type = ProxyGroupType::Relay; + break; + case "ssid"_hash: + conf.Type = ProxyGroupType::SSID; + break; + default: + throw toml::syntax_error("Proxy Group has incorrect type, should be one of following:\n select, url-test, load-balance, fallback, relay, ssid", v.at("type").location()); + } + conf.Timeout = toml::find_or(v, "timeout", 5); + conf.Proxies = toml::find_or(v, "rule", {}); + conf.UsingProvider = toml::find_or(v, "use", {}); + if(conf.Proxies.empty() && conf.UsingProvider.empty()) + throw toml::syntax_error("Proxy Group must contains at least one of proxy match rule or provider", v.location()); + if(v.contains("disable-udp")) + conf.DisableUdp = toml::find_or(v, "disable-udp", conf.DisableUdp.get()); + return conf; + } + }; + + template<> + struct from + { + static RulesetConfig from_toml(const value& v) + { + RulesetConfig conf; + conf.Group = toml::find(v, "group"); + String type = toml::find_or(v, "type", "surge-ruleset"); + switch(hash_(type)) + { + /* + case "surge-ruleset"_hash: + conf.Type = RulesetType::SurgeRuleset; + conf.Url = "surge:"; + break; + case "quantumultx"_hash: + conf.Type = RulesetType::QuantumultX; + conf.Url = "quanx:"; + break; + case "clash-domain"_hash: + conf.Type = RulesetType::ClashDomain; + conf.Url = type; + break; + case "clash-ipcidr"_hash: + conf.Type = RulesetType::ClashIpCidr; + conf.Url = type; + break; + case "clash-classic"_hash: + conf.Type = RulesetType::ClashClassic; + conf.Url = type; + break; + */ + case "surge-ruleset"_hash: + conf.Url = "surge:"; + break; + case "quantumultx"_hash: + conf.Url = "quanx:"; + break; + case "clash-domain"_hash: + case "clash-ipcidr"_hash: + case "clash-classic"_hash: + conf.Url = type; + break; + default: + throw toml::syntax_error("Ruleset has incorrect type, should be one of following:\n surge-ruleset, quantumultx, clash-domain, clash-ipcidr, clash-classic", v.at("type").location()); + } + conf.Url += toml::find(v, "ruleset"); + conf.Interval = toml::find_or(v, "interval", 86400); + return conf; + } + }; + + template<> + struct from + { + static RegexMatchConfig from_toml(const value& v) + { + RegexMatchConfig conf; + if(v.contains("script")) + { + conf.Script = toml::find(v, "script"); + return conf; + } + conf.Match = toml::find(v, "match"); + if(v.contains("emoji")) + conf.Replace = toml::find(v, "emoji"); + else + conf.Replace = toml::find(v, "replace"); + return conf; + } + }; + + template<> + struct from + { + static CronTaskConfig from_toml(const value& v) + { + CronTaskConfig conf; + conf.Name = toml::find(v, "name"); + conf.CronExp = toml::find(v, "cronexp"); + conf.Path = toml::find(v, "path"); + conf.Timeout = toml::find_or(v, "timeout", 0); + return conf; + } + }; + + template<> + struct from + { + static tribool from_toml(const value& v) + { + tribool t; + t.set(v.as_boolean()); + return t; + } + }; +} + +namespace INIBinding +{ + + void parseGroupTimes(const std::string &src, int *interval, int *tolerance, int *timeout) + { + std::vector ptrs; + ptrs.push_back(interval); + ptrs.push_back(timeout); + ptrs.push_back(tolerance); + string_size bpos = 0, epos = src.find(","); + for(int *x : ptrs) + { + if(x != NULL) + *x = to_int(src.substr(bpos, epos - bpos), 0); + if(epos != src.npos) + { + bpos = epos + 1; + epos = src.find(",", bpos); + } + else + return; + } + return; + } + + template struct from + {}; + + template<> + struct from + { + static ProxyGroupConfigs from_ini(const StrArray &arr) + { + ProxyGroupConfigs confs; + for(const String &x : arr) + { + unsigned int rules_upper_bound = 0; + ProxyGroupConfig conf; + + StrArray vArray = split(x, "`"); + if(vArray.size() < 3) + continue; + + conf.Name = vArray[0]; + String type = vArray[1]; + + rules_upper_bound = vArray.size(); + switch(hash_(type)) + { + case "select"_hash: + conf.Type = ProxyGroupType::Select; + break; + case "relay"_hash: + conf.Type = ProxyGroupType::Relay; + break; + case "url-test"_hash: + conf.Type = ProxyGroupType::URLTest; + break; + case "fallback"_hash: + conf.Type = ProxyGroupType::Fallback; + break; + case "load-balance"_hash: + conf.Type = ProxyGroupType::LoadBalance; + break; + case "ssid"_hash: + conf.Type = ProxyGroupType::SSID; + break; + default: + continue; + } + + if(conf.Type == ProxyGroupType::URLTest || conf.Type == ProxyGroupType::LoadBalance || conf.Type == ProxyGroupType::Fallback) + { + if(rules_upper_bound < 5) + continue; + rules_upper_bound -= 2; + conf.Url = vArray[rules_upper_bound]; + parseGroupTimes(vArray[rules_upper_bound + 1], &conf.Interval, &conf.Tolerance, &conf.Timeout); + } + + for(unsigned int i = 2; i < rules_upper_bound; i++) + { + if(startsWith(vArray[i], "!!PROVIDER=")) + { + string_array list = split(vArray[i].substr(11), ","); + conf.UsingProvider.reserve(conf.UsingProvider.size() + list.size()); + std::move(list.begin(), list.end(), std::back_inserter(conf.UsingProvider)); + } + else + conf.Proxies.emplace_back(std::move(vArray[i])); + } + confs.emplace_back(std::move(conf)); + } + return confs; + } + }; + + template<> + struct from + { + static RulesetConfigs from_ini(const StrArray &arr) + { + /* + static const std::map RulesetTypes = { + {"clash-domain:", RulesetType::ClashDomain}, + {"clash-ipcidr:", RulesetType::ClashIpCidr}, + {"clash-classic:", RulesetType::ClashClassic}, + {"quanx:", RulesetType::QuantumultX}, + {"surge:", RulesetType::SurgeRuleset} + }; + */ + RulesetConfigs confs; + for(const String &x : arr) + { + RulesetConfig conf; + String::size_type pos = x.find(","); + if(pos == String::npos) + continue; + conf.Group = x.substr(0, pos); + if(x.substr(pos + 1, 2) == "[]") + { + conf.Url = x.substr(pos + 1); + //conf.Type = RulesetType::SurgeRuleset; + confs.emplace_back(std::move(conf)); + continue; + } + String::size_type epos = x.rfind(","); + if(pos != epos) + { + conf.Interval = to_int(x.substr(epos + 1), 0); + conf.Url = x.substr(pos + 1, epos - pos - 1); + } + else + conf.Url = x.substr(pos + 1); + confs.emplace_back(std::move(conf)); + } + return confs; + } + }; + + template<> + struct from + { + static CronTaskConfigs from_ini(const StrArray &arr) + { + CronTaskConfigs confs; + for(const String &x : arr) + { + CronTaskConfig conf; + StrArray vArray = split(x, "`"); + if(vArray.size() < 3) + continue; + conf.Name = vArray[0]; + conf.CronExp = vArray[1]; + conf.Path = vArray[2]; + if(vArray.size() > 3) + conf.Timeout = to_int(vArray[3], 0); + confs.emplace_back(std::move(conf)); + } + return confs; + } + }; + + template<> + struct from + { + static RegexMatchConfigs from_ini(const StrArray &arr, const std::string &delimiter) + { + RegexMatchConfigs confs; + for(const String &x : arr) + { + RegexMatchConfig conf; + if(startsWith(x, "script:")) + { + conf.Script = x.substr(7); + confs.emplace_back(std::move(conf)); + continue; + } + String::size_type pos = x.rfind(delimiter); + conf.Match = x.substr(0, pos); + if(pos != String::npos && pos < x.size() - 1) + conf.Replace = x.substr(pos + 1); + confs.emplace_back(std::move(conf)); + } + return confs; + } + }; +} + +#endif // BINDING_H_INCLUDED diff --git a/src/config/crontask.h b/src/config/crontask.h new file mode 100644 index 0000000..b107cfb --- /dev/null +++ b/src/config/crontask.h @@ -0,0 +1,16 @@ +#ifndef CRONTASK_H_INCLUDED +#define CRONTASK_H_INCLUDED + +#include "def.h" + +struct CronTaskConfig +{ + String Name; + String CronExp; + String Path; + Integer Timeout = 0; +}; + +using CronTaskConfigs = std::vector; + +#endif // CRONTASK_H_INCLUDED diff --git a/src/config/def.h b/src/config/def.h new file mode 100644 index 0000000..b588bd7 --- /dev/null +++ b/src/config/def.h @@ -0,0 +1,16 @@ +#ifndef DEF_H_INCLUDED +#define DEF_H_INCLUDED + +#include +#include +#include + +#include "../utils/string.h" +#include "../utils/tribool.h" + +using String = std::string; +using Integer = int32_t; +using StrArray = string_array; +using Boolean = tribool; + +#endif // DEF_H_INCLUDED diff --git a/src/config/proxygroup.h b/src/config/proxygroup.h new file mode 100644 index 0000000..85baa36 --- /dev/null +++ b/src/config/proxygroup.h @@ -0,0 +1,63 @@ +#ifndef PROXYGROUP_H_INCLUDED +#define PROXYGROUP_H_INCLUDED + +#include "def.h" + +enum ProxyGroupType +{ + Select, + URLTest, + Fallback, + LoadBalance, + Relay, + SSID +}; + +enum BalanceStrategy +{ + ConsistentHashing, + RoundRobin +}; + +struct ProxyGroupConfig +{ + String Name; + ProxyGroupType Type; + StrArray Proxies; + StrArray UsingProvider; + String Url; + Integer Interval = 0; + Integer Timeout = 0; + Integer Tolerance = 0; + BalanceStrategy Strategy = BalanceStrategy::ConsistentHashing; + Boolean Lazy; + Boolean DisableUdp; + + String TypeStr() const + { + switch(Type) + { + case ProxyGroupType::Select: return "select"; + case ProxyGroupType::URLTest: return "url-test"; + case ProxyGroupType::LoadBalance: return "load-balance"; + case ProxyGroupType::Fallback: return "fallback"; + case ProxyGroupType::Relay: return "relay"; + case ProxyGroupType::SSID: return "ssid"; + } + return ""; + } + + String StrategyStr() const + { + switch(Strategy) + { + case BalanceStrategy::ConsistentHashing: return "consistent-hashing"; + case BalanceStrategy::RoundRobin: return "round-robin"; + } + return ""; + } +}; + +using ProxyGroupConfigs = std::vector; + +#endif // PROXYGROUP_H_INCLUDED diff --git a/src/config/regmatch.h b/src/config/regmatch.h new file mode 100644 index 0000000..16d8548 --- /dev/null +++ b/src/config/regmatch.h @@ -0,0 +1,15 @@ +#ifndef REGMATCH_H_INCLUDED +#define REGMATCH_H_INCLUDED + +#include "def.h" + +struct RegexMatchConfig +{ + String Match; + String Replace; + String Script; +}; + +using RegexMatchConfigs = std::vector; + +#endif // REGMATCH_H_INCLUDED diff --git a/src/config/ruleset.h b/src/config/ruleset.h new file mode 100644 index 0000000..4a29a54 --- /dev/null +++ b/src/config/ruleset.h @@ -0,0 +1,29 @@ +#ifndef RULESET_H_INCLUDED +#define RULESET_H_INCLUDED + +#include "def.h" + +enum RulesetType +{ + SurgeRuleset, + QuantumultX, + ClashDomain, + ClashIpCidr, + ClashClassic +}; + +struct RulesetConfig +{ + String Group; + //RulesetType Type = RulesetType::SurgeRuleset; + String Url; + Integer Interval = 86400; + bool operator==(const RulesetConfig &r) const + { + return Group == r.Group && Url == r.Url && Interval == r.Interval; + } +}; + +using RulesetConfigs = std::vector; + +#endif // RULESET_H_INCLUDED diff --git a/src/generator/config/nodemanip.cpp b/src/generator/config/nodemanip.cpp index b5fb84c..24a0378 100644 --- a/src/generator/config/nodemanip.cpp +++ b/src/generator/config/nodemanip.cpp @@ -37,8 +37,8 @@ int addNodes(std::string link, std::vector &allNodes, int groupID, parse_ std::string &proxy = *parse_set.proxy, &subInfo = *parse_set.sub_info; string_array &exclude_remarks = *parse_set.exclude_remarks; string_array &include_remarks = *parse_set.include_remarks; - string_array &stream_rules = *parse_set.stream_rules; - string_array &time_rules = *parse_set.time_rules; + RegexMatchConfigs &stream_rules = *parse_set.stream_rules; + RegexMatchConfigs &time_rules = *parse_set.time_rules; string_icase_map &request_headers = *parse_set.request_header; bool &authorized = parse_set.authorized; diff --git a/src/generator/config/nodemanip.h b/src/generator/config/nodemanip.h index bffeeda..2d965e9 100644 --- a/src/generator/config/nodemanip.h +++ b/src/generator/config/nodemanip.h @@ -6,6 +6,7 @@ #include #include +#include "../../config/regmatch.h" #include "../../parser/config/proxy.h" #include "../../utils/map_extra.h" #include "../../utils/string.h" @@ -15,8 +16,8 @@ struct parse_settings std::string *proxy = nullptr; string_array *exclude_remarks = nullptr; string_array *include_remarks = nullptr; - string_array *stream_rules = nullptr; - string_array *time_rules = nullptr; + RegexMatchConfigs *stream_rules = nullptr; + RegexMatchConfigs *time_rules = nullptr; std::string *sub_info = nullptr; bool authorized = false; string_icase_map *request_header = nullptr; diff --git a/src/generator/config/subexport.cpp b/src/generator/config/subexport.cpp index a6b4ba1..27c6404 100644 --- a/src/generator/config/subexport.cpp +++ b/src/generator/config/subexport.cpp @@ -8,6 +8,7 @@ #include #include +#include "../../config/regmatch.h" #include "../../generator/config/subexport.h" #include "../../generator/template/templates.h" #include "../../parser/config/proxy.h" @@ -63,19 +64,17 @@ std::string vmessLinkConstruct(const std::string &remarks, const std::string &ad return sb.GetString(); } -void nodeRename(Proxy &node, const string_array &rename_array, extra_settings &ext) +void nodeRename(Proxy &node, const RegexMatchConfigs &rename_array, extra_settings &ext) { - string_size pos; - std::string match, rep; std::string &remark = node.Remark, original_remark = node.Remark, returned_remark, real_rule; - for(const std::string &x : rename_array) + for(const RegexMatchConfig &x : rename_array) { - if(startsWith(x, "!!script:")) + if(!x.Script.empty()) { script_safe_runner(ext.js_runtime, ext.js_context, [&](qjs::Context &ctx) { - std::string script = x.substr(9); + std::string script = x.Script; if(startsWith(script, "path:")) script = fileGet(script.substr(5), true); try @@ -93,14 +92,8 @@ void nodeRename(Proxy &node, const string_array &rename_array, extra_settings &e }, gScriptCleanContext); continue; } - pos = x.rfind("@"); - match = x.substr(0, pos); - if(pos != x.npos && pos < x.size()) - rep = x.substr(pos + 1); - else - rep.clear(); - if(applyMatcher(match, real_rule, node) && real_rule.size()) - remark = regReplace(remark, real_rule, rep); + if(applyMatcher(x.Match, real_rule, node) && real_rule.size()) + remark = regReplace(remark, real_rule, x.Replace); } if(remark.empty()) remark = original_remark; @@ -123,19 +116,18 @@ std::string removeEmoji(const std::string &orig_remark) return remark; } -std::string addEmoji(const Proxy &node, const string_array &emoji_array, extra_settings &ext) +std::string addEmoji(const Proxy &node, const RegexMatchConfigs &emoji_array, extra_settings &ext) { std::string real_rule, ret; - string_size pos; - for(const std::string &x : emoji_array) + for(const RegexMatchConfig &x : emoji_array) { - if(startsWith(x, "!!script:")) + if(!x.Script.empty()) { std::string result; script_safe_runner(ext.js_runtime, ext.js_context, [&](qjs::Context &ctx) { - std::string script = x.substr(9); + std::string script = x.Script; if(startsWith(script, "path:")) script = fileGet(script.substr(5), true); try @@ -155,11 +147,10 @@ std::string addEmoji(const Proxy &node, const string_array &emoji_array, extra_s return result; continue; } - pos = x.rfind(","); - if(pos == x.npos) + if(x.Replace.empty()) continue; - if(applyMatcher(x.substr(0, pos), real_rule, node) && real_rule.size() && regFind(node.Remark, real_rule)) - return x.substr(pos + 1) + " " + node.Remark; + if(applyMatcher(x.Match, real_rule, node) && real_rule.size() && regFind(node.Remark, real_rule)) + return x.Replace + " " + node.Remark; } return node.Remark; } @@ -206,7 +197,7 @@ void parseGroupTimes(const std::string &src, int *interval, int *tolerance, int return; } -void groupGenerate(std::string &rule, std::vector &nodelist, string_array &filtered_nodelist, bool add_direct, extra_settings &ext) +void groupGenerate(const std::string &rule, std::vector &nodelist, string_array &filtered_nodelist, bool add_direct, extra_settings &ext) { std::string real_rule; if(startsWith(rule, "[]") && add_direct) @@ -291,11 +282,11 @@ void preprocessNodes(std::vector &nodes, extra_settings &ext) } } -void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const string_array &extra_proxy_group, bool clashR, extra_settings &ext) +void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGroupConfigs &extra_proxy_group, bool clashR, extra_settings &ext) { YAML::Node proxies, singleproxy, singlegroup, original_groups; std::vector nodelist; - string_array vArray, remarks_list, filtered_nodelist; + string_array remarks_list, filtered_nodelist; /// proxies style bool block = false, compact = false; switch(hash_(ext.clash_proxies_style)) @@ -376,11 +367,11 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const string_ break; case "ws"_hash: singleproxy["network"] = x.TransferProtocol; - singleproxy["ws-path"] = x.Path; + singleproxy["ws-opts"]["path"] = x.Path; if(x.Host.size()) - singleproxy["ws-headers"]["Host"] = x.Host; + singleproxy["ws-opts"]["headers"]["Host"] = x.Host; if(x.Edge.size()) - singleproxy["ws-headers"]["Edge"] = x.Edge; + singleproxy["ws-opts"]["headers"]["Edge"] = x.Edge; break; case "http"_hash: singleproxy["network"] = x.TransferProtocol; @@ -472,6 +463,16 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const string_ singleproxy["password"].SetTag("str"); if(!scv.is_undef()) singleproxy["skip-cert-verify"] = scv.get(); + switch(hash_(x.TransferProtocol)) + { + case "tcp"_hash: + break; + case "grpc"_hash: + singleproxy["network"] = x.TransferProtocol; + if(!x.Path.empty()) + singleproxy["grpc-opts"]["grpc-service-name"] = x.Path; + break; + } break; case ProxyType::Snell: singleproxy["type"] = "snell"; @@ -516,60 +517,45 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const string_ else yamlnode["Proxy"] = proxies; - string_array providers; - for(const std::string &x : extra_proxy_group) + for(const ProxyGroupConfig &x : extra_proxy_group) { singlegroup.reset(); eraseElements(filtered_nodelist); - eraseElements(providers); - unsigned int rules_upper_bound = 0; - vArray = split(x, "`"); - if(vArray.size() < 3) - continue; + singlegroup["name"] = x.Name; + singlegroup["type"] = x.TypeStr(); - singlegroup["name"] = vArray[0]; - singlegroup["type"] = vArray[1]; - - int interval = 0, tolerance = 0; - rules_upper_bound = vArray.size(); - switch(hash_(vArray[1])) + switch(x.Type) { - case "select"_hash: - case "relay"_hash: + case ProxyGroupType::Select: + case ProxyGroupType::Relay: break; - case "url-test"_hash: - case "fallback"_hash: - case "load-balance"_hash: - if(rules_upper_bound < 5) - continue; - rules_upper_bound -= 2; - singlegroup["url"] = vArray[rules_upper_bound]; - parseGroupTimes(vArray[rules_upper_bound + 1], &interval, &tolerance, NULL); - if(interval) - singlegroup["interval"] = interval; - if(tolerance) - singlegroup["tolerance"] = tolerance; + case ProxyGroupType::LoadBalance: + singlegroup["strategy"] = x.StrategyStr(); + [[fallthrough]]; + case ProxyGroupType::URLTest: + if(!x.Lazy.is_undef()) + singlegroup["lazy"] = x.Lazy.get(); + [[fallthrough]]; + case ProxyGroupType::Fallback: + singlegroup["url"] = x.Url; + if(x.Interval > 0) + singlegroup["interval"] = x.Interval; + if(x.Tolerance > 0) + singlegroup["tolerance"] = x.Tolerance; break; default: continue; } + if(!x.DisableUdp.is_undef()) + singlegroup["disable-udp"] = x.DisableUdp.get(); - for(unsigned int i = 2; i < rules_upper_bound; i++) - { - if(startsWith(vArray[i], "!!PROVIDER=")) - { - string_array list = split(vArray[i].substr(11), ","); - providers.reserve(providers.size() + list.size()); - std::move(list.begin(), list.end(), std::back_inserter(providers)); - } - else - groupGenerate(vArray[i], nodelist, filtered_nodelist, true, ext); - } + for(const auto& y : x.Proxies) + groupGenerate(y, nodelist, filtered_nodelist, true, ext); - if(providers.size()) - singlegroup["use"] = providers; + if(x.UsingProvider.size()) + singlegroup["use"] = x.UsingProvider; else { if(filtered_nodelist.empty()) @@ -582,7 +568,7 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const string_ bool replace_flag = false; for(unsigned int i = 0; i < original_groups.size(); i++) { - if(original_groups[i]["name"].as() == vArray[0]) + if(original_groups[i]["name"].as() == x.Name) { original_groups[i] = singlegroup; replace_flag = true; @@ -599,7 +585,7 @@ void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const string_ yamlnode["Proxy Group"] = original_groups; } -std::string proxyToClash(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const string_array &extra_proxy_group, bool clashR, extra_settings &ext) +std::string proxyToClash(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, bool clashR, extra_settings &ext) { YAML::Node yamlnode; @@ -647,7 +633,7 @@ std::string proxyToClash(std::vector &nodes, const std::string &base_conf return output_content; } -std::string proxyToSurge(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const string_array &extra_proxy_group, int surge_ver, extra_settings &ext) +std::string proxyToSurge(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, int surge_ver, extra_settings &ext) { INIReader ini; std::string proxy; @@ -656,7 +642,7 @@ std::string proxyToSurge(std::vector &nodes, const std::string &base_conf std::vector nodelist; unsigned short local_port = 1080; - string_array vArray, remarks_list, filtered_nodelist, args; + string_array remarks_list, filtered_nodelist, args; ini.store_any_line = true; // filter out sections that requires direct-save @@ -833,53 +819,35 @@ std::string proxyToSurge(std::vector &nodes, const std::string &base_conf ini.SetCurrentSection("Proxy Group"); ini.EraseSection(); - for(const std::string &x : extra_proxy_group) + for(const ProxyGroupConfig &x : extra_proxy_group) { - //group pref - std::string url; - int interval = 0, tolerance = 0, timeout = 0; eraseElements(filtered_nodelist); - unsigned int rules_upper_bound = 0; - url.clear(); proxy.clear(); - vArray = split(x, "`"); - if(vArray.size() < 3) - continue; - - rules_upper_bound = vArray.size(); - switch(hash_(vArray[1])) + switch(x.Type) { - case "select"_hash: + case ProxyGroupType::Select: + case ProxyGroupType::URLTest: + case ProxyGroupType::Fallback: break; - case "load-balance"_hash: + case ProxyGroupType::LoadBalance: if(surge_ver < 1) continue; [[fallthrough]]; - case "url-test"_hash: - case "fallback"_hash: - if(rules_upper_bound < 5) - continue; - rules_upper_bound -= 2; - url = vArray[rules_upper_bound]; - parseGroupTimes(vArray[rules_upper_bound + 1], &interval, &tolerance, &timeout); - break; - case "ssid"_hash: - if(rules_upper_bound < 4) - continue; - proxy = vArray[1] + ",default=" + vArray[2] + ","; - proxy += std::accumulate(vArray.begin() + 4, vArray.end(), vArray[3], [](std::string a, std::string b) + case ProxyGroupType::SSID: + proxy = x.TypeStr() + ",default=" + x.Proxies[0] + ","; + proxy += std::accumulate(x.Proxies.begin() + 2, x.Proxies.end(), x.Proxies[1], [](std::string a, std::string b) { return std::move(a) + "," + std::move(b); }); - ini.Set("{NONAME}", vArray[0] + " = " + proxy); //insert order + ini.Set("{NONAME}", x.Name + " = " + proxy); //insert order continue; default: continue; } - for(unsigned int i = 2; i < rules_upper_bound; i++) - groupGenerate(vArray[i], nodelist, filtered_nodelist, true, ext); + for(const auto &y : x.Proxies) + groupGenerate(y, nodelist, filtered_nodelist, true, ext); if(!filtered_nodelist.size()) filtered_nodelist.emplace_back("DIRECT"); @@ -892,28 +860,28 @@ std::string proxyToSurge(std::vector &nodes, const std::string &base_conf case "direct"_hash: case "reject"_hash: case "reject-tinygif"_hash: - ini.Set("Proxy", "{NONAME}", vArray[0] + " = " + proxy); + ini.Set("Proxy", "{NONAME}", x.Name + " = " + proxy); continue; } } - proxy = vArray[1] + ","; + proxy = x.TypeStr() + ","; proxy += std::accumulate(std::next(filtered_nodelist.cbegin()), filtered_nodelist.cend(), filtered_nodelist[0], [](std::string a, std::string b) { return std::move(a) + "," + std::move(b); }); - if(vArray[1] == "url-test" || vArray[1] == "fallback") + if(x.Type == ProxyGroupType::URLTest || x.Type == ProxyGroupType::Fallback) { - proxy += ",url=" + url + ",interval=" + std::to_string(interval); - if(tolerance > 0) - proxy += ",tolerance=" + std::to_string(tolerance); - if(timeout > 0) - proxy += ",timeout=" + std::to_string(timeout); + proxy += ",url=" + x.Url + ",interval=" + std::to_string(x.Interval); + if(x.Tolerance > 0) + proxy += ",tolerance=" + std::to_string(x.Tolerance); + if(x.Timeout > 0) + proxy += ",timeout=" + std::to_string(x.Timeout); } - else if(vArray[1] == "load-balance") - proxy += ",url=" + url; + else if(x.Type == ProxyGroupType::LoadBalance) + proxy += ",url=" + x.Url; - ini.Set("{NONAME}", vArray[0] + " = " + proxy); //insert order + ini.Set("{NONAME}", x.Name + " = " + proxy); //insert order } if(ext.enable_rule_generator) @@ -1075,7 +1043,7 @@ std::string proxyToSSSub(std::string base_conf, std::vector &nodes, extra return output_content; } -std::string proxyToQuan(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext) +std::string proxyToQuan(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext) { INIReader ini; ini.store_any_line = true; @@ -1102,7 +1070,7 @@ std::string proxyToQuan(std::vector &nodes, const std::string &base_conf, return ini.ToString(); } -void proxyToQuan(std::vector &nodes, INIReader &ini, std::vector &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext) +void proxyToQuan(std::vector &nodes, INIReader &ini, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext) { std::string type, proxyStr; tribool scv; @@ -1244,47 +1212,27 @@ void proxyToQuan(std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext) +std::string proxyToQuanX(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext) { INIReader ini; ini.store_any_line = true; @@ -1364,7 +1310,7 @@ std::string proxyToQuanX(std::vector &nodes, const std::string &base_conf return ini.ToString(); } -void proxyToQuanX(std::vector &nodes, INIReader &ini, std::vector &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext) +void proxyToQuanX(std::vector &nodes, INIReader &ini, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext) { std::string type; std::string remark, hostname, port, method; @@ -1503,53 +1449,37 @@ void proxyToQuanX(std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, std::string &group, std::strin return "ssd://" + base64Encode(sb.GetString()); } -std::string proxyToMellow(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext) +std::string proxyToMellow(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext) { INIReader ini; ini.store_any_line = true; @@ -1760,7 +1690,7 @@ std::string proxyToMellow(std::vector &nodes, const std::string &base_con return ini.ToString(); } -void proxyToMellow(std::vector &nodes, INIReader &ini, std::vector &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext) +void proxyToMellow(std::vector &nodes, INIReader &ini, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext) { std::string proxy; std::string type, remark, hostname, port, username, password, method; @@ -1849,36 +1779,25 @@ void proxyToMellow(std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, INIReader &ini, std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext) +std::string proxyToLoon(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext) { rapidjson::Document json; INIReader ini; @@ -1924,9 +1843,6 @@ std::string proxyToLoon(std::vector &nodes, const std::string &base_conf, std::string output_nodelist; tribool scv; std::vector nodelist; - //group pref - std::string url; - int interval = 0; string_array vArray, remarks_list, filtered_nodelist; @@ -2039,51 +1955,38 @@ std::string proxyToLoon(std::vector &nodes, const std::string &base_conf, ini.SetCurrentSection("Proxy Group"); ini.EraseSection(); - for(const std::string &x : extra_proxy_group) + for(const ProxyGroupConfig &x : extra_proxy_group) { eraseElements(filtered_nodelist); - unsigned int rules_upper_bound = 0; - url.clear(); proxy.clear(); - vArray = split(x, "`"); - if(vArray.size() < 3) - continue; - - rules_upper_bound = vArray.size(); - switch(hash_(vArray[1])) + switch(x.Type) { - case "select"_hash: + case ProxyGroupType::Select: + case ProxyGroupType::URLTest: + case ProxyGroupType::Fallback: break; - case "url-test"_hash: - case "fallback"_hash: - if(vArray.size() < 5) + case ProxyGroupType::SSID: + if(x.Proxies.size() < 2) continue; - rules_upper_bound -= 2; - url = vArray[rules_upper_bound]; - parseGroupTimes(vArray[rules_upper_bound + 1], &interval, NULL, NULL); - break; - case "ssid"_hash: - if(vArray.size() < 4) - continue; - proxy = vArray[1] + ",default=" + vArray[2] + ","; - proxy += std::accumulate(vArray.begin() + 4, vArray.end(), vArray[3], [](std::string a, std::string b) + proxy = x.TypeStr() + ",default=" + x.Proxies[0] + ","; + proxy += std::accumulate(x.Proxies.begin() + 2, x.Proxies.end(), vArray[1], [](std::string a, std::string b) { return std::move(a) + "," + std::move(b); }); - ini.Set("{NONAME}", vArray[0] + " = " + proxy); //insert order + ini.Set("{NONAME}", x.Name + " = " + proxy); //insert order continue; default: continue; } - for(unsigned int i = 2; i < rules_upper_bound; i++) - groupGenerate(vArray[i], nodelist, filtered_nodelist, true, ext); + for(const auto &y : x.Proxies) + groupGenerate(y, nodelist, filtered_nodelist, true, ext); if(!filtered_nodelist.size()) filtered_nodelist.emplace_back("DIRECT"); - proxy = vArray[1] + ","; + proxy = x.TypeStr() + ","; /* for(std::string &y : filtered_nodelist) proxy += "," + y; @@ -2092,10 +1995,10 @@ std::string proxyToLoon(std::vector &nodes, const std::string &base_conf, { return std::move(a) + "," + std::move(b); }); - if(vArray[1] == "url-test" || vArray[1] == "fallback") - proxy += ",url=" + url + ",interval=" + std::to_string(interval); + if(x.Type == ProxyGroupType::URLTest || x.Type == ProxyGroupType::Fallback) + proxy += ",url=" + x.Url + ",interval=" + std::to_string(x.Interval); - ini.Set("{NONAME}", vArray[0] + " = " + proxy); //insert order + ini.Set("{NONAME}", x.Name + " = " + proxy); //insert order } if(ext.enable_rule_generator) diff --git a/src/generator/config/subexport.h b/src/generator/config/subexport.h index 6c95f9c..6186ae6 100644 --- a/src/generator/config/subexport.h +++ b/src/generator/config/subexport.h @@ -5,6 +5,8 @@ #include +#include "../../config/proxygroup.h" +#include "../../config/regmatch.h" #include "../../parser/config/proxy.h" #include "../../utils/ini_reader/ini_reader.h" #include "../../utils/string.h" @@ -15,8 +17,8 @@ struct extra_settings { bool enable_rule_generator = true; bool overwrite_original_rules = true; - string_array rename_array; - string_array emoji_array; + RegexMatchConfigs rename_array; + RegexMatchConfigs emoji_array; bool add_emoji = false; bool remove_emoji = false; bool append_proxy_type = false; @@ -52,18 +54,18 @@ void rulesetToClash(YAML::Node &base_rule, std::vector &ruleset void rulesetToSurge(INIReader &base_rule, std::vector &ruleset_content_array, int surge_ver, bool overwrite_original_rules, std::string remote_path_prefix); void preprocessNodes(std::vector &nodes, extra_settings &ext); -std::string proxyToClash(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const string_array &extra_proxy_group, bool clashR, extra_settings &ext); -void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const string_array &extra_proxy_group, bool clashR, extra_settings &ext); -std::string proxyToSurge(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const string_array &extra_proxy_group, int surge_ver, extra_settings &ext); -std::string proxyToMellow(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext); -void proxyToMellow(std::vector &nodes, INIReader &ini, std::vector &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext); -std::string proxyToLoon(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext); +std::string proxyToClash(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, bool clashR, extra_settings &ext); +void proxyToClash(std::vector &nodes, YAML::Node &yamlnode, const ProxyGroupConfigs &extra_proxy_group, bool clashR, extra_settings &ext); +std::string proxyToSurge(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, int surge_ver, extra_settings &ext); +std::string proxyToMellow(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext); +void proxyToMellow(std::vector &nodes, INIReader &ini, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext); +std::string proxyToLoon(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext); std::string proxyToSSSub(std::string base_conf, std::vector &nodes, extra_settings &ext); std::string proxyToSingle(std::vector &nodes, int types, extra_settings &ext); -std::string proxyToQuanX(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext); -void proxyToQuanX(std::vector &nodes, INIReader &ini, std::vector &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext); -std::string proxyToQuan(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext); -void proxyToQuan(std::vector &nodes, INIReader &ini, std::vector &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext); +std::string proxyToQuanX(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext); +void proxyToQuanX(std::vector &nodes, INIReader &ini, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext); +std::string proxyToQuan(std::vector &nodes, const std::string &base_conf, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext); +void proxyToQuan(std::vector &nodes, INIReader &ini, std::vector &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext); std::string proxyToSSD(std::vector &nodes, std::string &group, std::string &userinfo, extra_settings &ext); #endif // SUBEXPORT_H_INCLUDED diff --git a/src/handler/interfaces.cpp b/src/handler/interfaces.cpp index 102b242..df412e3 100644 --- a/src/handler/interfaces.cpp +++ b/src/handler/interfaces.cpp @@ -6,6 +6,7 @@ #include #include +#include "../config/binding.h" #include "../generator/config/ruleconvert.h" #include "../generator/config/nodemanip.h" #include "../generator/config/ruleconvert.h" @@ -33,7 +34,9 @@ //common settings std::string gPrefPath = "pref.ini", gDefaultExtConfig; -string_array gExcludeRemarks, gIncludeRemarks, gCustomRulesets, gStreamNodeRules, gTimeNodeRules; +string_array gExcludeRemarks, gIncludeRemarks; +RulesetConfigs gCustomRulesets; +RegexMatchConfigs gStreamNodeRules, gTimeNodeRules; std::vector gRulesetContent; std::string gListenAddress = "127.0.0.1", gDefaultUrls, gInsertUrls, gManagedConfigPrefix; int gListenPort = 25500, gMaxPendingConns = 10, gMaxConcurThreads = 4; @@ -46,9 +49,6 @@ extern int gLogLevel; extern long gMaxAllowedDownloadSize; string_map gAliases; -extern bool gServeFile; -extern std::string gServeFileRoot; - //global variables for template std::string gTemplatePath = "templates"; string_map gTemplateVars; @@ -61,7 +61,7 @@ std::string gGenerateProfiles; std::mutex gMutexConfigure; //preferences -string_array gRenames, gEmojis; +RegexMatchConfigs gRenames, gEmojis; bool gAddEmoji = false, gRemoveEmoji = false, gAppendType = false, gFilterDeprecated = true; tribool gUDP, gTFO, gSkipCertVerify, gTLS13, gEnableInsert; bool gEnableSort = false, gUpdateStrict = false; @@ -72,7 +72,7 @@ int gUpdateInterval = 0; std::string gSortScript, gFilterScript; std::string gClashBase; -string_array gCustomProxyGroups; +ProxyGroupConfigs gCustomProxyGroups; std::string gSurgeBase, gSurfboardBase, gMellowBase, gQuanBase, gQuanXBase, gLoonBase, gSSSubBase; std::string gSurgeSSRPath, gQuanXDevID; @@ -86,11 +86,13 @@ bool gScriptCleanContext = false; //cron system bool gEnableCron = false; -string_array gCronTasks; +CronTaskConfigs gCronTasks; + +extern WebServer webServer; string_array gRegexBlacklist = {"(.*)*"}; -void refreshRulesets(string_array &ruleset_list, std::vector &ruleset_content_array); +void refreshRulesets(RulesetConfigs &ruleset_list, std::vector &ruleset_content_array); std::string parseProxy(const std::string &source) { @@ -222,7 +224,8 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS) for(std::string &x : vArray) x.insert(0, "ruleset,"); std::vector rca; - refreshRulesets(vArray, rca); + RulesetConfigs confs = INIBinding::from::from_ini(vArray); + refreshRulesets(confs, rca); for(ruleset_content &x : rca) { std::string content = x.rule_content.get(); @@ -256,8 +259,7 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS) if(strLine[pose - 1] == '\r') pose--; } - else - pose -= posb; + pose -= posb; return 0; }; @@ -405,6 +407,50 @@ int importItems(string_array &target, bool scope_limit = true) return 0; } +toml::value parseToml(const std::string &content, const std::string &fname) +{ + std::istringstream is(content); + return toml::parse(is, fname); +} + +void importItems(std::vector &root, const std::string &import_key, bool scope_limit = true) +{ + std::string content; + std::vector newRoot; + auto iter = root.begin(); + size_t count = 0; + + std::string proxy = parseProxy(gProxyConfig); + while(iter != root.end()) + { + auto& table = iter->as_table(); + if(table.find("import") == table.end()) + newRoot.emplace_back(std::move(*iter)); + else + { + const std::string &path = toml::get(table.at("import")); + writeLog(0, "Trying to import items from " + path); + if(fileExist(path)) + content = fileGet(path, scope_limit); + else if(isLink(path)) + content = webGet(path, proxy, gCacheConfig); + else + writeLog(0, "File not found or not a valid URL: " + path, LOG_LEVEL_ERROR); + if(content.size()) + { + auto items = parseToml(content, path); + auto list = toml::find>(items, import_key); + count += list.size(); + std::move(list.begin(), list.end(), std::back_inserter(newRoot)); + } + } + iter++; + } + root.swap(newRoot); + writeLog(0, "Imported " + std::to_string(count) + " item(s)."); + return; +} + void readRegexMatch(YAML::Node node, const std::string &delimiter, string_array &dest, bool scope_limit = true) { YAML::Node object; @@ -488,8 +534,8 @@ void readGroup(YAML::Node node, string_array &dest, bool scope_limit = true) std::string url = "http://www.gstatic.com/generate_204", interval = "300", tolerance, timeout; object["name"] >>= name; object["type"] >>= type; - tempArray.emplace_back("name=" + name); - tempArray.emplace_back("type=" + type); + tempArray.emplace_back(name); + tempArray.emplace_back(type); object["url"] >>= url; object["interval"] >>= interval; object["tolerance"] >>= tolerance; @@ -509,13 +555,13 @@ void readGroup(YAML::Node node, string_array &dest, bool scope_limit = true) default: if(tempArray.size() < 3) continue; - tempArray.emplace_back("url=" + url); - tempArray.emplace_back("interval=" + interval + ",timeout=" + timeout + ",tolerance=" + tolerance); + tempArray.emplace_back(url); + tempArray.emplace_back(interval + "," + timeout + "," + tolerance); } strLine = std::accumulate(std::next(tempArray.begin()), tempArray.end(), tempArray[0], [](std::string a, std::string b) -> std::string { - return std::move(a) + "," + std::move(b); + return std::move(a) + "`" + std::move(b); }); dest.emplace_back(std::move(strLine)); } @@ -555,7 +601,7 @@ void readRuleset(YAML::Node node, string_array &dest, bool scope_limit = true) importItems(dest, scope_limit); } -void refreshRulesets(string_array &ruleset_list, std::vector &ruleset_content_array) +void refreshRulesets(RulesetConfigs &ruleset_list, std::vector &ruleset_content_array) { eraseElements(ruleset_content_array); std::string rule_group, rule_url, rule_url_typed, interval; @@ -563,26 +609,18 @@ void refreshRulesets(string_array &ruleset_list, std::vector &r std::string proxy = parseProxy(gProxyRuleset); - for(std::string &x : ruleset_list) + for(RulesetConfig &x : ruleset_list) { - string_size pos = x.find(","); - if(pos == x.npos || pos == x.size() - 1) - continue; - rule_group = trim(x.substr(0, pos)); - if(x.find("[]", pos + 1) == pos + 1) + rule_group = x.Group; + rule_url = x.Url; + std::string::size_type pos = x.Url.find("[]"); + if(pos != std::string::npos) { - rule_url = trim(x.substr(pos + 1)); - writeLog(0, "Adding rule '" + rule_url.substr(2) + "," + rule_group + "'.", LOG_LEVEL_INFO); - rc = {rule_group, "", "", RULESET_SURGE, std::async(std::launch::async, [rule_url](){return rule_url;}), 0}; + writeLog(0, "Adding rule '" + rule_url.substr(pos + 2) + "," + rule_group + "'.", LOG_LEVEL_INFO); + rc = {rule_group, "", "", RULESET_SURGE, std::async(std::launch::async, [=](){return rule_url.substr(pos);}), 0}; } else { - string_size pos2 = x.find(",", pos + 1); - rule_url = trim(x.substr(pos + 1, pos2 - pos - 1)); - if(pos2 != x.npos) - interval = x.substr(pos2 + 1); - else - interval.clear(); ruleset_type type = RULESET_SURGE; rule_url_typed = rule_url; auto iter = std::find_if(RulesetTypes.begin(), RulesetTypes.end(), [rule_url](auto y){ return startsWith(rule_url, y.first); }); @@ -592,7 +630,7 @@ void refreshRulesets(string_array &ruleset_list, std::vector &r type = iter->second; } writeLog(0, "Updating ruleset url '" + rule_url + "' with group '" + rule_group + "'.", LOG_LEVEL_INFO); - rc = {rule_group, rule_url, rule_url_typed, type, fetchFileAsync(rule_url, proxy, gCacheRuleset, gAsyncFetchRuleset), to_int(interval, 0)}; + rc = {rule_group, rule_url, rule_url_typed, type, fetchFileAsync(rule_url, proxy, gCacheRuleset, gAsyncFetchRuleset), x.Interval}; } ruleset_content_array.emplace_back(std::move(rc)); } @@ -662,13 +700,15 @@ void readYAMLConf(YAML::Node &node) if(section["stream_rule"].IsSequence()) { readRegexMatch(section["stream_rule"], "|", tempArray, false); - safe_set_streams(tempArray); + auto configs = INIBinding::from::from_ini(tempArray, "|"); + safe_set_streams(configs); eraseElements(tempArray); } if(section["time_rule"].IsSequence()) { readRegexMatch(section["time_rule"], "|", tempArray, false); - safe_set_times(tempArray); + auto configs = INIBinding::from::from_ini(tempArray, "|"); + safe_set_times(configs); eraseElements(tempArray); } } @@ -696,7 +736,8 @@ void readYAMLConf(YAML::Node &node) if(section["rename_node"].IsSequence()) { readRegexMatch(section["rename_node"], "@", tempArray, false); - safe_set_renames(tempArray); + auto configs = INIBinding::from::from_ini(tempArray, "@"); + safe_set_renames(configs); eraseElements(tempArray); } @@ -724,7 +765,8 @@ void readYAMLConf(YAML::Node &node) if(section["rules"].IsSequence()) { readEmoji(section["rules"], tempArray, false); - safe_set_emojis(tempArray); + auto configs = INIBinding::from::from_ini(tempArray, ","); + safe_set_emojis(configs); eraseElements(tempArray); } } @@ -746,12 +788,20 @@ void readYAMLConf(YAML::Node &node) } const char *ruleset_title = section["rulesets"].IsDefined() ? "rulesets" : "surge_ruleset"; if(section[ruleset_title].IsSequence()) - readRuleset(section[ruleset_title], gCustomRulesets, false); + { + string_array vArray; + readRuleset(section[ruleset_title], vArray, false); + gCustomRulesets = INIBinding::from::from_ini(vArray); + } } const char *groups_title = node["proxy_groups"].IsDefined() ? "proxy_groups" : "proxy_group"; if(node[groups_title].IsDefined() && node[groups_title]["custom_proxy_group"].IsDefined()) - readGroup(node[groups_title]["custom_proxy_group"], gCustomProxyGroups, false); + { + string_array vArray; + readGroup(node[groups_title]["custom_proxy_group"], vArray, false); + gCustomProxyGroups = INIBinding::from::from_ini(vArray); + } if(node["template"].IsDefined()) { @@ -771,26 +821,26 @@ void readYAMLConf(YAML::Node &node) if(node["aliases"].IsSequence()) { - reset_redirect(); + webServer.reset_redirect(); for(size_t i = 0; i < node["aliases"].size(); i++) { std::string uri, target; node["aliases"][i]["uri"] >> uri; node["aliases"][i]["target"] >> target; - append_redirect(uri, target); + webServer.append_redirect(uri, target); } } if(node["tasks"].IsSequence()) { - gCronTasks.clear(); + string_array vArray; for(size_t i = 0; i < node["tasks"].size(); i++) { std::string name, exp, path, timeout; node["tasks"][i]["import"] >> name; if(name.size()) { - gCronTasks.emplace_back("!!import:" + name); + vArray.emplace_back("!!import:" + name); continue; } node["tasks"][i]["name"] >> name; @@ -798,10 +848,11 @@ void readYAMLConf(YAML::Node &node) node["tasks"][i]["path"] >> path; node["tasks"][i]["timeout"] >> timeout; strLine = name + "`" + exp + "`" + path + "`" + timeout; - gCronTasks.push_back(std::move(strLine)); + vArray.emplace_back(std::move(strLine)); } - importItems(gCronTasks, false); - gEnableCron = !gCronTasks.empty(); + importItems(vArray, false); + gEnableCron = !vArray.empty(); + gCronTasks = INIBinding::from::from_ini(vArray); refresh_schedule(); } @@ -809,8 +860,8 @@ void readYAMLConf(YAML::Node &node) { node["server"]["listen"] >> gListenAddress; node["server"]["port"] >> gListenPort; - node["server"]["serve_file_root"] >>= gServeFileRoot; - gServeFile = !gServeFileRoot.empty(); + node["server"]["serve_file_root"] >>= webServer.serve_file_root; + webServer.serve_file = !webServer.serve_file_root.empty(); } if(node["advanced"].IsDefined()) @@ -866,6 +917,224 @@ void readYAMLConf(YAML::Node &node) } } +template +void find_if_exist(const toml::value &v, const toml::key &k, T& target, U&&... args) +{ + if(v.contains(k)) target = toml::find(v, k); + if constexpr (sizeof...(args)) find_if_exist(v, std::forward(args)...); +} + +std::string join(const string_array &arr, const std::string &delimiter = "|") +{ + if(arr.size() == 0) + return ""; + if(arr.size() == 1) + return arr[0]; + return std::accumulate(arr.begin() + 1, arr.end(), arr[0], [&](const std::string &a, const std::string &b) { return a + delimiter + b; }); +} + +void operate_toml_kv_table(const std::vector &arr, const toml::key &key_name, const toml::key &value_name, std::function binary_op) +{ + for(const toml::table &table : arr) + { + const auto &key = table.at(key_name), value = table.at(value_name); + binary_op(key, value); + } +} + +void readTOMLConf(toml::value &root) +{ + const auto §ion_common = toml::find(root, "common"); + string_array default_url, insert_url; + + find_if_exist(section_common, "default_url", default_url, "insert_url", insert_url); + gDefaultUrls = join(default_url); + gInsertUrls = join(insert_url); + + bool filter = false; + find_if_exist(section_common, + "api_mode", gAPIMode, + "api_access_token", gAccessToken, + "exclude_remarks", gExcludeRemarks, + "include_remarks", gIncludeRemarks, + "enable_insert", gEnableInsert, + "prepend_insert_url", gPrependInsert, + "enable_filter", filter, + "default_external_config", gDefaultExtConfig, + "base_path", gBasePath, + "clash_rule_base", gClashBase, + "surge_rule_base", gSurgeBase, + "surfboard_rule_base", gSurfboardBase, + "mellow_rule_base", gMellowBase, + "quan_rule_base", gQuanBase, + "quanx_rule_base", gQuanXBase, + "loon_rule_base", gLoonBase, + "proxy_config", gProxyConfig, + "proxy_ruleset", gProxyRuleset, + "proxy_subscription", gProxySubscription, + "append_proxy_type", gAppendType + ); + + if(filter) + find_if_exist(section_common, "filter_script", gFilterScript); + else + gFilterScript.clear(); + + safe_set_streams(toml::find_or(root, "userinfo", "stream_rule", RegexMatchConfigs{})); + safe_set_times(toml::find_or(root, "userinfo", "time_rule", RegexMatchConfigs{})); + + const auto §ion_node_pref = toml::find(root, "node_pref"); + + find_if_exist(section_node_pref, + "udp_flag", gUDP, + "tcp_fast_open_flag", gTFO, + "skip_cert_verify_flag", gSkipCertVerify, + "tls13_flag", gTLS13, + "sort_flag", gEnableSort, + "sort_script", gSortScript, + "filter_deprecated_nodes", gFilterDeprecated, + "append_sub_userinfo", gAppendUserinfo, + "clash_use_new_field_name", gClashUseNewField, + "clash_proxies_style", gClashProxiesStyle + ); + + auto renameconfs = toml::find_or>(section_node_pref, "rename_node", {}); + importItems(renameconfs, "rename_node", false); + safe_set_renames(toml::get(toml::value(renameconfs))); + + const auto §ion_managed = toml::find(root, "managed_config"); + + find_if_exist(section_managed, + "write_managed_config", gWriteManagedConfig, + "managed_config_prefix", gManagedConfigPrefix, + "config_update_interval", gUpdateInterval, + "config_update_strict", gUpdateStrict, + "quanx_device_id", gQuanXDevID + ); + + const auto §ion_surge_external = toml::find(root, "surge_external_proxy"); + find_if_exist(section_surge_external, + "surge_ssr_path", gSurgeSSRPath, + "resolve_hostname", gSurgeResolveHostname + ); + + const auto §ion_emojis = toml::find(root, "emojis"); + + find_if_exist(section_emojis, + "add_emoji", gAddEmoji, + "remove_old_emoji", gRemoveEmoji + ); + + auto emojiconfs = toml::find_or>(section_emojis, "emoji", {}); + importItems(emojiconfs, "emoji", false); + safe_set_emojis(toml::get(toml::value(emojiconfs))); + + auto groups = toml::find_or>(root, "custom_groups", {}); + importItems(groups, "custom_groups", false); + gCustomProxyGroups = toml::get(toml::value(groups)); + + const auto §ion_ruleset = toml::find(root, "ruleset"); + + find_if_exist(section_ruleset, + "enabled", gEnableRuleGen, + "overwrite_original_rules", gOverwriteOriginalRules, + "update_ruleset_on_request", gUpdateRulesetOnRequest + ); + + auto rulesets = toml::find_or>(root, "rulesets", {}); + importItems(rulesets, "rulesets", false); + gCustomRulesets = toml::get(toml::value(rulesets)); + + const auto §ion_template = toml::find(root, "template"); + + gTemplatePath = toml::find_or(section_template, "template_path", "template"); + + eraseElements(gTemplateVars); + operate_toml_kv_table(toml::find_or>(section_template, "globals", {}), "key", "value", [&](const toml::value &key, const toml::value &value) + { + gTemplateVars[key.as_string()] = value.as_string(); + }); + + webServer.reset_redirect(); + operate_toml_kv_table(toml::find_or>(root, "aliases", {}), "uri", "target", [&](const toml::value &key, const toml::value &value) + { + webServer.append_redirect(key.as_string(), value.as_string()); + }); + + auto tasks = toml::find_or>(root, "tasks", {}); + importItems(tasks, "tasks", false); + gCronTasks = toml::get(toml::value(tasks)); + + const auto §ion_server = toml::find(root, "server"); + + find_if_exist(section_server, + "listen", gListenAddress, + "port", gListenPort, + "serve_file_root", webServer.serve_file_root + ); + webServer.serve_file = !webServer.serve_file_root.empty(); + + const auto §ion_advanced = toml::find(root, "advanced"); + + std::string log_level; + bool enable_cache = true; + int cache_subscription = gCacheSubscription, cache_config = gCacheConfig, cache_ruleset = gCacheRuleset; + + find_if_exist(section_advanced, + "log_level", log_level, + "print_debug_info", gPrintDbgInfo, + "max_pending_connections", gMaxPendingConns, + "max_concurrent_threads", gMaxConcurThreads, + "max_allowed_rulesets", gMaxAllowedRulesets, + "max_allowed_rules", gMaxAllowedRules, + "max_allowed_download_size", gMaxAllowedDownloadSize, + "enable_cache", enable_cache, + "cache_subscription", cache_subscription, + "cache_config", cache_config, + "cache_ruleset", cache_ruleset, + "script_clean_context", gScriptCleanContext, + "async_fetch_ruleset", gAsyncFetchRuleset, + "skip_failed_links", gSkipFailedLinks + ); + + if(gPrintDbgInfo) + gLogLevel = LOG_LEVEL_VERBOSE; + else + { + switch(hash_(log_level)) + { + case "warn"_hash: + gLogLevel = LOG_LEVEL_WARNING; + break; + case "error"_hash: + gLogLevel = LOG_LEVEL_ERROR; + break; + case "fatal"_hash: + gLogLevel = LOG_LEVEL_FATAL; + break; + case "verbose"_hash: + gLogLevel = LOG_LEVEL_VERBOSE; + break; + case "debug"_hash: + gLogLevel = LOG_LEVEL_DEBUG; + break; + default: + gLogLevel = LOG_LEVEL_INFO; + } + } + + if(enable_cache) + { + gCacheSubscription = cache_subscription; + gCacheConfig = cache_config; + gCacheRuleset = cache_ruleset; + } + else + { + gCacheSubscription = gCacheConfig = gCacheRuleset = 0; + } +} + void readConf() { guarded_mutex guard(gMutexConfigure); @@ -886,10 +1155,18 @@ void readConf() if(yaml.size() && yaml["common"]) return readYAMLConf(yaml); } + toml::value conf = parseToml(prefdata, gPrefPath); + if(!conf.is_uninitialized() && toml::find_or(conf, "version", 0)) + return readTOMLConf(conf); } catch (YAML::Exception &e) { - //ignore + //ignore yaml parse error + } + catch (toml::exception &e) + { + //ignore toml parse error + writeLog(0, e.what(), LOG_LEVEL_DEBUG); } INIReader ini; @@ -960,7 +1237,8 @@ void readConf() { ini.GetAll("rename_node", tempArray); importItems(tempArray, false); - safe_set_renames(tempArray); + auto configs = INIBinding::from::from_ini(tempArray, "@"); + safe_set_renames(configs); eraseElements(tempArray); } } @@ -972,14 +1250,16 @@ void readConf() { ini.GetAll("stream_rule", tempArray); importItems(tempArray, false); - safe_set_streams(tempArray); + auto configs = INIBinding::from::from_ini(tempArray, "|"); + safe_set_streams(configs); eraseElements(tempArray); } if(ini.ItemPrefixExist("time_rule")) { ini.GetAll("time_rule", tempArray); importItems(tempArray, false); - safe_set_times(tempArray); + auto configs = INIBinding::from::from_ini(tempArray, "|"); + safe_set_times(configs); eraseElements(tempArray); } } @@ -998,7 +1278,8 @@ void readConf() { ini.GetAll("rule", tempArray); importItems(tempArray, false); - safe_set_emojis(tempArray); + auto configs = INIBinding::from::from_ini(tempArray, ","); + safe_set_emojis(configs); eraseElements(tempArray); } @@ -1013,13 +1294,17 @@ void readConf() ini.GetBoolIfExist("update_ruleset_on_request", gUpdateRulesetOnRequest); if(ini.ItemPrefixExist("ruleset")) { - ini.GetAll("ruleset", gCustomRulesets); - importItems(gCustomRulesets, true); + string_array vArray; + ini.GetAll("ruleset", vArray); + importItems(vArray, false); + gCustomRulesets = INIBinding::from::from_ini(vArray); } else if(ini.ItemPrefixExist("surge_ruleset")) { - ini.GetAll("surge_ruleset", gCustomRulesets); - importItems(gCustomRulesets, false); + string_array vArray; + ini.GetAll("surge_ruleset", vArray); + importItems(vArray, false); + gCustomRulesets = INIBinding::from::from_ini(vArray); } } else @@ -1034,8 +1319,10 @@ void readConf() ini.EnterSection("clash_proxy_group"); if(ini.ItemPrefixExist("custom_proxy_group")) { - ini.GetAll("custom_proxy_group", gCustomProxyGroups); - importItems(gCustomProxyGroups, false); + string_array vArray; + ini.GetAll("custom_proxy_group", vArray); + importItems(vArray, false); + gCustomProxyGroups = INIBinding::from::from_ini(vArray); } ini.EnterSection("template"); @@ -1055,26 +1342,27 @@ void readConf() { ini.EnterSection("aliases"); ini.GetItems(tempmap); - reset_redirect(); + webServer.reset_redirect(); for(auto &x : tempmap) - append_redirect(x.first, x.second); + webServer.append_redirect(x.first, x.second); } if(ini.SectionExist("tasks")) { - gCronTasks.clear(); + string_array vArray; ini.EnterSection("tasks"); - ini.GetAll("task", gCronTasks); - importItems(gCronTasks, false); - gEnableCron = !gCronTasks.empty(); + ini.GetAll("task", vArray); + importItems(vArray, false); + gEnableCron = !vArray.empty(); + gCronTasks = INIBinding::from::from_ini(vArray); refresh_schedule(); } ini.EnterSection("server"); ini.GetIfExist("listen", gListenAddress); ini.GetIntIfExist("port", gListenPort); - gServeFileRoot = ini.Get("serve_file_root"); - gServeFile = !gServeFileRoot.empty(); + webServer.serve_file_root = ini.Get("serve_file_root"); + webServer.serve_file = !webServer.serve_file_root.empty(); ini.EnterSection("advanced"); std::string log_level; @@ -1135,8 +1423,8 @@ void readConf() struct ExternalConfig { - string_array custom_proxy_group; - string_array surge_ruleset; + ProxyGroupConfigs custom_proxy_group; + RulesetConfigs surge_ruleset; std::string clash_rule_base; std::string surge_rule_base; std::string surfboard_rule_base; @@ -1145,8 +1433,8 @@ struct ExternalConfig std::string quanx_rule_base; std::string loon_rule_base; std::string sssub_rule_base; - string_array rename; - string_array emoji; + RegexMatchConfigs rename; + RegexMatchConfigs emoji; string_array include; string_array exclude; template_args *tpl_args = NULL; @@ -1176,28 +1464,41 @@ int loadExternalYAML(YAML::Node &node, ExternalConfig &ext) const char *group_name = section["proxy_groups"].IsDefined() ? "proxy_groups" : "custom_proxy_group"; if(section[group_name].size()) - readGroup(section[group_name], ext.custom_proxy_group, gAPIMode); + { + string_array vArray; + readGroup(section[group_name], vArray, gAPIMode); + ext.custom_proxy_group = INIBinding::from::from_ini(vArray); + } const char *ruleset_name = section["rulesets"].IsDefined() ? "rulesets" : "surge_ruleset"; if(section[ruleset_name].size()) { - readRuleset(section[ruleset_name], ext.surge_ruleset, gAPIMode); - if(gMaxAllowedRulesets && ext.surge_ruleset.size() > gMaxAllowedRulesets) + string_array vArray; + readRuleset(section[ruleset_name], vArray, gAPIMode); + if(gMaxAllowedRulesets && vArray.size() > gMaxAllowedRulesets) { writeLog(0, "Ruleset count in external config has exceeded limit.", LOG_LEVEL_WARNING); - eraseElements(ext.surge_ruleset); return -1; } + ext.surge_ruleset = INIBinding::from::from_ini(vArray); } if(section["rename_node"].size()) - readRegexMatch(section["rename_node"], "@", ext.rename, gAPIMode); + { + string_array vArray; + readRegexMatch(section["rename_node"], "@", vArray, gAPIMode); + ext.rename = INIBinding::from::from_ini(vArray, "@"); + } ext.add_emoji = safe_as(section["add_emoji"]); ext.remove_old_emoji = safe_as(section["remove_old_emoji"]); const char *emoji_name = section["emojis"].IsDefined() ? "emojis" : "emoji"; if(section[emoji_name].size()) - readEmoji(section[emoji_name], ext.emoji, gAPIMode); + { + string_array vArray; + readEmoji(section[emoji_name], vArray, gAPIMode); + ext.emoji = INIBinding::from::from_ini(vArray, ","); + } section["include_remarks"] >> ext.include; section["exclude_remarks"] >> ext.exclude; @@ -1216,6 +1517,57 @@ int loadExternalYAML(YAML::Node &node, ExternalConfig &ext) return 0; } +int loadExternalTOML(toml::value &root, ExternalConfig &ext) +{ + const auto §ion = toml::find(root, "custom"); + + find_if_exist(section, + "enable_rule_generator", ext.enable_rule_generator, + "overwrite_original_rules", ext.overwrite_original_rules, + "clash_rule_base", ext.clash_rule_base, + "surge_rule_base", ext.surge_ruleset, + "surfboard_rule_base", ext.surfboard_rule_base, + "mellow_rule_base", ext.mellow_rule_base, + "quan_rule_base", ext.quan_rule_base, + "quanx_rule_base", ext.quanx_rule_base, + "sssub_rule_base", ext.sssub_rule_base, + "add_emoji", ext.add_emoji, + "remove_old_emoji", ext.remove_old_emoji, + "include_remarks", ext.include, + "exclude_remarks", ext.exclude + ); + + if(ext.tpl_args != nullptr) operate_toml_kv_table(toml::find_or>(section, "template_args", {}), "key", "value", + [&](const toml::value &key, const toml::value &value) + { + std::string val = toml::format(value); + ext.tpl_args->local_vars[key.as_string()] = val; + }); + + auto groups = toml::find_or>(root, "custom_groups", {}); + importItems(groups, "custom_groups", false); + ext.custom_proxy_group = toml::get(toml::value(groups)); + + auto rulesets = toml::find_or>(root, "rulesets", {}); + importItems(rulesets, "rulesets", false); + if(gMaxAllowedRulesets && rulesets.size() > gMaxAllowedRulesets) + { + writeLog(0, "Ruleset count in external config has exceeded limit. ", LOG_LEVEL_WARNING); + return -1; + } + ext.surge_ruleset = toml::get(toml::value(rulesets)); + + auto emojiconfs = toml::find_or>(root, "emoji", {}); + importItems(emojiconfs, "emoji", false); + ext.emoji = toml::get(toml::value(emojiconfs)); + + auto renameconfs = toml::find_or>(root, "rename_node", {}); + importItems(renameconfs, "rename_node", false); + ext.rename = toml::get(toml::value(renameconfs)); + + return 0; +} + int loadExternalConfig(std::string &path, ExternalConfig &ext) { std::string base_content, proxy = parseProxy(gProxyConfig), config = fetchFile(path, proxy, gCacheConfig); @@ -1227,11 +1579,18 @@ int loadExternalConfig(std::string &path, ExternalConfig &ext) YAML::Node yaml = YAML::Load(base_content); if(yaml.size() && yaml["custom"].IsDefined()) return loadExternalYAML(yaml, ext); + toml::value conf = parseToml(base_content, path); + if(!conf.is_uninitialized() && toml::find_or(conf, "version", 0)) + return loadExternalTOML(conf, ext); } catch (YAML::Exception &e) { //ignore } + catch (toml::exception &e) + { + //ignore + } INIReader ini; ini.store_isolated_line = true; @@ -1246,20 +1605,23 @@ int loadExternalConfig(std::string &path, ExternalConfig &ext) ini.EnterSection("custom"); if(ini.ItemPrefixExist("custom_proxy_group")) { - ini.GetAll("custom_proxy_group", ext.custom_proxy_group); - importItems(ext.custom_proxy_group, gAPIMode); + string_array vArray; + ini.GetAll("custom_proxy_group", vArray); + importItems(vArray, gAPIMode); + ext.custom_proxy_group = INIBinding::from::from_ini(vArray); } std::string ruleset_name = ini.ItemPrefixExist("ruleset") ? "ruleset" : "surge_ruleset"; if(ini.ItemPrefixExist(ruleset_name)) { - ini.GetAll(ruleset_name, ext.surge_ruleset); - importItems(ext.surge_ruleset, gAPIMode); - if(gMaxAllowedRulesets && ext.surge_ruleset.size() > gMaxAllowedRulesets) + string_array vArray; + ini.GetAll(ruleset_name, vArray); + importItems(vArray, gAPIMode); + if(gMaxAllowedRulesets && vArray.size() > gMaxAllowedRulesets) { writeLog(0, "Ruleset count in external config has exceeded limit. ", LOG_LEVEL_WARNING); - eraseElements(ext.surge_ruleset); return -1; } + ext.surge_ruleset = INIBinding::from::from_ini(vArray); } ini.GetIfExist("clash_rule_base", ext.clash_rule_base); @@ -1276,22 +1638,26 @@ int loadExternalConfig(std::string &path, ExternalConfig &ext) if(ini.ItemPrefixExist("rename")) { - ini.GetAll("rename", ext.rename); - importItems(ext.rename, gAPIMode); + string_array vArray; + ini.GetAll("rename", vArray); + importItems(vArray, gAPIMode); + ext.rename = INIBinding::from::from_ini(vArray, "@"); } ext.add_emoji = ini.Get("add_emoji"); ext.remove_old_emoji = ini.Get("remove_old_emoji"); if(ini.ItemPrefixExist("emoji")) { - ini.GetAll("emoji", ext.emoji); - importItems(ext.emoji, gAPIMode); + string_array vArray; + ini.GetAll("emoji", vArray); + importItems(vArray, gAPIMode); + ext.emoji = INIBinding::from::from_ini(vArray, ","); } if(ini.ItemPrefixExist("include_remarks")) ini.GetAll("include_remarks", ext.include); if(ini.ItemPrefixExist("exclude_remarks")) ini.GetAll("exclude_remarks", ext.exclude); - if(ini.SectionExist("template") && ext.tpl_args != NULL) + if(ini.SectionExist("template") && ext.tpl_args != nullptr) { ini.EnterSection("template"); string_multimap tempmap; @@ -1354,7 +1720,9 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS) tribool argPrependInsert = getUrlArg(argument, "prepend"), argGenClassicalRuleProvider = getUrlArg(argument, "classic"), argTLS13 = getUrlArg(argument, "tls13"); std::string base_content, output_content; - string_array lCustomProxyGroups = gCustomProxyGroups, lCustomRulesets = gCustomRulesets, lIncludeRemarks = gIncludeRemarks, lExcludeRemarks = gExcludeRemarks; + ProxyGroupConfigs lCustomProxyGroups = gCustomProxyGroups; + RulesetConfigs lCustomRulesets = gCustomRulesets; + string_array lIncludeRemarks = gIncludeRemarks, lExcludeRemarks = gExcludeRemarks; std::vector lRulesetContent; extra_settings ext; std::string subInfo, dummy; @@ -1488,11 +1856,17 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS) { /// loading custom groups if(argCustomGroups.size() && !ext.nodelist) - lCustomProxyGroups = split(argCustomGroups, "@"); + { + string_array vArray = split(argCustomGroups, "@"); + lCustomProxyGroups = INIBinding::from::from_ini(vArray); + } /// loading custom rulesets if(argCustomRulesets.size() && !ext.nodelist) - lCustomRulesets = split(argCustomRulesets, "@"); + { + string_array vArray = split(argCustomRulesets, "@"); + lCustomRulesets = INIBinding::from::from_ini(vArray); + } } } if(ext.enable_rule_generator && !ext.nodelist && !lSimpleSubscription) @@ -1517,7 +1891,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS) if(ext.add_emoji && ext.emoji_array.empty()) ext.emoji_array = safe_get_emojis(); if(argRenames.size()) - ext.rename_array = split(argRenames, "`"); + ext.rename_array = INIBinding::from::from_ini(split(argRenames, "`"), "@"); else if(ext.rename_array.empty()) ext.rename_array = safe_get_renames(); @@ -1537,7 +1911,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS) } //start parsing urls - string_array stream_temp = safe_get_streams(), time_temp = safe_get_times(); + RegexMatchConfigs stream_temp = safe_get_streams(), time_temp = safe_get_times(); //loading urls string_array urls; @@ -1645,7 +2019,8 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS) } } */ - script_safe_runner(ext.js_runtime, ext.js_context, [&](qjs::Context &ctx){ + script_safe_runner(ext.js_runtime, ext.js_context, [&](qjs::Context &ctx) + { try { ctx.eval(filterScript); @@ -1684,7 +2059,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS) } */ - string_array dummy_group; + ProxyGroupConfigs dummy_group; std::vector dummy_ruleset; std::string managed_url = base64Decode(urlDecode(getUrlArg(argument, "profile_data"))); if(managed_url.empty()) @@ -1999,10 +2374,12 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS) proxy = parseProxy(gProxySubscription); eraseElements(dummy_str_array); + RegexMatchConfigs dummy_regex_array; std::string subInfo; parse_settings parse_set; parse_set.proxy = &proxy; - parse_set.exclude_remarks = parse_set.include_remarks = parse_set.stream_rules = parse_set.time_rules = &dummy_str_array; + parse_set.exclude_remarks = parse_set.include_remarks = &dummy_str_array; + parse_set.stream_rules = parse_set.time_rules = &dummy_regex_array; parse_set.request_header = &request.headers; parse_set.sub_info = &subInfo; parse_set.authorized = !gAPIMode; @@ -2039,7 +2416,8 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS) ext.tls13 = gTLS13; ext.clash_proxies_style = gClashProxiesStyle; - proxyToClash(nodes, clash, dummy_str_array, false, ext); + ProxyGroupConfigs dummy_groups; + proxyToClash(nodes, clash, dummy_groups, false, ext); section.clear(); ini.GetItems("Proxy", section); diff --git a/src/handler/interfaces.h b/src/handler/interfaces.h index 09f15e3..1395f64 100644 --- a/src/handler/interfaces.h +++ b/src/handler/interfaces.h @@ -5,10 +5,11 @@ #include #include +#include "../config/ruleset.h" #include "../generator/config/subexport.h" #include "../server/webserver.h" -void refreshRulesets(string_array &ruleset_list, std::vector &rca); +void refreshRulesets(RulesetConfigs &ruleset_list, std::vector &rca); void readConf(); int simpleGenerator(); std::string convertRuleset(const std::string &content, int type); diff --git a/src/handler/multithread.cpp b/src/handler/multithread.cpp index f3fedce..3057dee 100644 --- a/src/handler/multithread.cpp +++ b/src/handler/multithread.cpp @@ -9,52 +9,52 @@ //safety lock for multi-thread std::mutex on_emoji, on_rename, on_stream, on_time; -extern string_array gEmojis, gRenames; -extern string_array gStreamNodeRules, gTimeNodeRules; +extern RegexMatchConfigs gEmojis, gRenames; +extern RegexMatchConfigs gStreamNodeRules, gTimeNodeRules; -string_array safe_get_emojis() +RegexMatchConfigs safe_get_emojis() { guarded_mutex guard(on_emoji); return gEmojis; } -string_array safe_get_renames() +RegexMatchConfigs safe_get_renames() { guarded_mutex guard(on_rename); return gRenames; } -string_array safe_get_streams() +RegexMatchConfigs safe_get_streams() { guarded_mutex guard(on_stream); return gStreamNodeRules; } -string_array safe_get_times() +RegexMatchConfigs safe_get_times() { guarded_mutex guard(on_time); return gTimeNodeRules; } -void safe_set_emojis(string_array &data) +void safe_set_emojis(RegexMatchConfigs data) { guarded_mutex guard(on_emoji); gEmojis.swap(data); } -void safe_set_renames(string_array &data) +void safe_set_renames(RegexMatchConfigs data) { guarded_mutex guard(on_rename); gRenames.swap(data); } -void safe_set_streams(string_array &data) +void safe_set_streams(RegexMatchConfigs data) { guarded_mutex guard(on_stream); gStreamNodeRules.swap(data); } -void safe_set_times(string_array &data) +void safe_set_times(RegexMatchConfigs data) { guarded_mutex guard(on_time); gTimeNodeRules.swap(data); diff --git a/src/handler/multithread.h b/src/handler/multithread.h index 3f8e3c6..394b6fe 100644 --- a/src/handler/multithread.h +++ b/src/handler/multithread.h @@ -6,21 +6,22 @@ #include +#include "../config/regmatch.h" #include "../utils/ini_reader/ini_reader.h" #include "../utils/string.h" using guarded_mutex = std::lock_guard; -string_array safe_get_emojis(); -string_array safe_get_renames(); -string_array safe_get_streams(); -string_array safe_get_times(); +RegexMatchConfigs safe_get_emojis(); +RegexMatchConfigs safe_get_renames(); +RegexMatchConfigs safe_get_streams(); +RegexMatchConfigs safe_get_times(); YAML::Node safe_get_clash_base(); INIReader safe_get_mellow_base(); -void safe_set_emojis(string_array &data); -void safe_set_renames(string_array &data); -void safe_set_streams(string_array &data); -void safe_set_times(string_array &data); +void safe_set_emojis(RegexMatchConfigs data); +void safe_set_renames(RegexMatchConfigs data); +void safe_set_streams(RegexMatchConfigs data); +void safe_set_times(RegexMatchConfigs data); std::shared_future fetchFileAsync(const std::string &path, const std::string &proxy, int cache_ttl, bool async = false); std::string fetchFile(const std::string &path, const std::string &proxy, int cache_ttl); diff --git a/src/handler/webget.cpp b/src/handler/webget.cpp index ccb8a72..55f8108 100644 --- a/src/handler/webget.cpp +++ b/src/handler/webget.cpp @@ -98,7 +98,7 @@ RWLock cache_rw_lock; long gMaxAllowedDownloadSize = 1048576L; //std::string user_agent_str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"; -std::string user_agent_str = "subconverter/" VERSION " cURL/" LIBCURL_VERSION; +static std::string user_agent_str = "subconverter/" VERSION " cURL/" LIBCURL_VERSION; struct curl_progress_data { diff --git a/src/main.cpp b/src/main.cpp index 521c3be..c515044 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include #include +#include "config/ruleset.h" #include "handler/interfaces.h" #include "handler/webget.h" #include "script/cron.h" @@ -25,10 +26,12 @@ extern std::string gPrefPath, gAccessToken, gListenAddress, gGenerateProfiles, gManagedConfigPrefix; extern bool gAPIMode, gGeneratorMode, gCFWChildProcess, gUpdateRulesetOnRequest; extern int gListenPort, gMaxConcurThreads, gMaxPendingConns; -extern string_array gCustomRulesets; +extern RulesetConfigs gCustomRulesets; extern std::vector gRulesetContent; extern bool gEnableCron; +WebServer webServer; + #ifndef _WIN32 void SetConsoleTitle(const std::string &title) { @@ -105,7 +108,7 @@ void signal_handler(int sig) #endif // _WIN32 case SIGTERM: case SIGINT: - stop_web_server(); + webServer.stop_web_server(); break; } } @@ -122,10 +125,17 @@ int main(int argc, char *argv[]) std::string prgpath = argv[0]; setcd(prgpath); //first switch to program directory #endif // _DEBUG - if(fileExist("pref.yml")) + if(fileExist("pref.toml")) + gPrefPath = "pref.toml"; + else if(fileExist("pref.yml")) gPrefPath = "pref.yml"; else if(!fileExist("pref.ini")) { + if(fileExist("pref.example.toml")) + { + fileCopy("pref.example.toml", "pref.toml"); + gPrefPath = "pref.toml"; + } if(fileExist("pref.example.yml")) { fileCopy("pref.example.yml", "pref.yml"); @@ -173,18 +183,18 @@ int main(int argc, char *argv[]) return simpleGenerator(); /* - append_response("GET", "/", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string + webServer.append_response("GET", "/", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string { return "subconverter " VERSION " backend\n"; }); */ - append_response("GET", "/version", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string + webServer.append_response("GET", "/version", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string { return "subconverter " VERSION " backend\n"; }); - append_response("GET", "/refreshrules", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string + webServer.append_response("GET", "/refreshrules", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string { if(gAccessToken.size()) { @@ -199,7 +209,7 @@ int main(int argc, char *argv[]) return "done\n"; }); - append_response("GET", "/readconf", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string + webServer.append_response("GET", "/readconf", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string { if(gAccessToken.size()) { @@ -216,7 +226,7 @@ int main(int argc, char *argv[]) return "done\n"; }); - append_response("POST", "/updateconf", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string + webServer.append_response("POST", "/updateconf", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string { if(gAccessToken.size()) { @@ -244,7 +254,7 @@ int main(int argc, char *argv[]) return "done\n"; }); - append_response("GET", "/flushcache", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string + webServer.append_response("GET", "/flushcache", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string { if(getUrlArg(request.argument, "token") != gAccessToken) { @@ -255,41 +265,41 @@ int main(int argc, char *argv[]) return "done"; }); - append_response("GET", "/sub", "text/plain;charset=utf-8", subconverter); + webServer.append_response("GET", "/sub", "text/plain;charset=utf-8", subconverter); - append_response("GET", "/sub2clashr", "text/plain;charset=utf-8", simpleToClashR); + webServer.append_response("GET", "/sub2clashr", "text/plain;charset=utf-8", simpleToClashR); - append_response("GET", "/surge2clash", "text/plain;charset=utf-8", surgeConfToClash); + webServer.append_response("GET", "/surge2clash", "text/plain;charset=utf-8", surgeConfToClash); - append_response("GET", "/getruleset", "text/plain;charset=utf-8", getRuleset); + webServer.append_response("GET", "/getruleset", "text/plain;charset=utf-8", getRuleset); - append_response("GET", "/getprofile", "text/plain;charset=utf-8", getProfile); + webServer.append_response("GET", "/getprofile", "text/plain;charset=utf-8", getProfile); - append_response("GET", "/qx-script", "text/plain;charset=utf-8", getScript); + webServer.append_response("GET", "/qx-script", "text/plain;charset=utf-8", getScript); - append_response("GET", "/qx-rewrite", "text/plain;charset=utf-8", getRewriteRemote); + webServer.append_response("GET", "/qx-rewrite", "text/plain;charset=utf-8", getRewriteRemote); - append_response("GET", "/render", "text/plain;charset=utf-8", renderTemplate); + webServer.append_response("GET", "/render", "text/plain;charset=utf-8", renderTemplate); - append_response("GET", "/convert", "text/plain;charset=utf-8", getConvertedRuleset); + webServer.append_response("GET", "/convert", "text/plain;charset=utf-8", getConvertedRuleset); if(!gAPIMode) { - append_response("GET", "/get", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string + webServer.append_response("GET", "/get", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string { std::string url = urlDecode(getUrlArg(request.argument, "url")); return webGet(url, ""); }); - append_response("GET", "/getlocal", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string + webServer.append_response("GET", "/getlocal", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string { return fileGet(urlDecode(getUrlArg(request.argument, "path"))); }); } - //append_response("POST", "/create-profile", "text/plain;charset=utf-8", createProfile); + //webServer.append_response("POST", "/create-profile", "text/plain;charset=utf-8", createProfile); - //append_response("GET", "/list-profiles", "text/plain;charset=utf-8", listProfiles); + //webServer.append_response("GET", "/list-profiles", "text/plain;charset=utf-8", listProfiles); std::string env_port = getEnv("PORT"); if(env_port.size()) @@ -297,7 +307,7 @@ int main(int argc, char *argv[]) listener_args args = {gListenAddress, gListenPort, gMaxPendingConns, gMaxConcurThreads, cron_tick_caller, 200}; //std::cout<<"Serving HTTP @ http://"< #include +#include "../config/regmatch.h" #include "../parser/config/proxy.h" #include "../utils/base64/base64.h" #include "../utils/rapidjson_extra.h" @@ -82,9 +83,9 @@ bool getSubInfoFromHeader(const std::string &header, std::string &result) return false; } -bool getSubInfoFromNodes(const std::vector &nodes, const string_array &stream_rules, const string_array &time_rules, std::string &result) +bool getSubInfoFromNodes(const std::vector &nodes, const RegexMatchConfigs &stream_rules, const RegexMatchConfigs &time_rules, std::string &result) { - std::string remarks, pattern, target, stream_info, time_info, retStr; + std::string remarks, stream_info, time_info, retStr; string_size spos; for(const Proxy &x : nodes) @@ -92,16 +93,11 @@ bool getSubInfoFromNodes(const std::vector &nodes, const string_array &st remarks = x.Remark; if(!stream_info.size()) { - for(const std::string &y : stream_rules) + for(const RegexMatchConfig &y : stream_rules) { - spos = y.rfind("|"); - if(spos == y.npos) - continue; - pattern = y.substr(0, spos); - target = y.substr(spos + 1); - if(regMatch(remarks, pattern)) + if(regMatch(remarks, y.Match)) { - retStr = regReplace(remarks, pattern, target); + retStr = regReplace(remarks, y.Match, y.Replace); if(retStr != remarks) { stream_info = retStr; @@ -116,16 +112,11 @@ bool getSubInfoFromNodes(const std::vector &nodes, const string_array &st remarks = x.Remark; if(!time_info.size()) { - for(const std::string &y : time_rules) + for(const RegexMatchConfig &y : time_rules) { - spos = y.rfind("|"); - if(spos == y.npos) - continue; - pattern = y.substr(0, spos); - target = y.substr(spos + 1); - if(regMatch(remarks, pattern)) + if(regMatch(remarks, y.Match)) { - retStr = regReplace(remarks, pattern, target); + retStr = regReplace(remarks, y.Match, y.Replace); if(retStr != remarks) { time_info = retStr; diff --git a/src/parser/infoparser.h b/src/parser/infoparser.h index 96d0fdd..ad90718 100644 --- a/src/parser/infoparser.h +++ b/src/parser/infoparser.h @@ -5,9 +5,10 @@ #include "../utils/string.h" #include "config/proxy.h" +#include "../config/regmatch.h" bool getSubInfoFromHeader(const std::string &header, std::string &result); -bool getSubInfoFromNodes(const std::vector &nodes, const string_array &stream_rules, const string_array &time_rules, std::string &result); +bool getSubInfoFromNodes(const std::vector &nodes, const RegexMatchConfigs &stream_rules, const RegexMatchConfigs &time_rules, std::string &result); bool getSubInfoFromSSD(const std::string &sub, std::string &result); unsigned long long streamToInt(const std::string &stream); diff --git a/src/parser/subparser.cpp b/src/parser/subparser.cpp index d846127..d7cefad 100644 --- a/src/parser/subparser.cpp +++ b/src/parser/subparser.cpp @@ -96,12 +96,14 @@ void httpConstruct(Proxy &node, const std::string &group, const std::string &rem node.TLSSecure = tls; } -void trojanConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &host, bool tlssecure, tribool udp, tribool tfo, tribool scv, tribool tls13) +void trojanConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &network, const std::string &host, const std::string &path, bool tlssecure, tribool udp, tribool tfo, tribool scv, tribool tls13) { commonConstruct(node, ProxyType::Trojan, group, remarks, server, port, udp, tfo, scv, tls13); node.Password = password; node.Host = host; node.TLSSecure = tlssecure; + node.TransferProtocol = network.empty() ? "tcp" : network; + node.Path = path; } void snellConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &obfs, const std::string &host, tribool udp, tribool tfo, tribool scv) @@ -761,7 +763,7 @@ void explodeTrojan(std::string trojan, Proxy &node) if(group.empty()) group = TROJAN_DEFAULT_GROUP; - trojanConstruct(node, group, remark, server, port, psk, host, true, tribool(), tfo, scv); + trojanConstruct(node, group, remark, server, port, psk, "", host, "", true, tribool(), tfo, scv); } void explodeQuan(const std::string &quan, Proxy &node) @@ -911,10 +913,12 @@ void explodeNetch(std::string netch, Proxy &node) break; case "Trojan"_hash: host = GetMember(json, "Host"); + path = GetMember(json, "Path"); + transprot = GetMember(json, "TransferProtocol"); tls = GetMember(json, "TLSSecure"); if(group.empty()) group = TROJAN_DEFAULT_GROUP; - trojanConstruct(node, group, remark, address, port, password, host, tls == "true", udp, tfo, scv); + trojanConstruct(node, group, remark, address, port, password, transprot, host, path, tls == "true", udp, tfo, scv); break; case "Snell"_hash: obfs = GetMember(json, "OBFS"); @@ -968,9 +972,18 @@ void explodeClash(Node yamlnode, std::vector &nodes) edge.clear(); break; case "ws"_hash: - path = singleproxy["ws-path"].IsDefined() ? safe_as(singleproxy["ws-path"]) : "/"; - singleproxy["ws-headers"]["Host"] >>= host; - singleproxy["ws-headers"]["Edge"] >>= edge; + if(singleproxy["ws-opts"].IsDefined()) + { + path = singleproxy["ws-opts"]["path"].IsDefined() ? safe_as(singleproxy["ws-opts"]["path"]) : "/"; + singleproxy["ws-opts"]["headers"]["Host"] >>= host; + singleproxy["ws-opts"]["headers"]["Edge"] >>= edge; + } + else + { + path = singleproxy["ws-path"].IsDefined() ? safe_as(singleproxy["ws-path"]) : "/"; + singleproxy["ws-headers"]["Host"] >>= host; + singleproxy["ws-headers"]["Edge"] >>= edge; + } break; case "h2"_hash: singleproxy["h2-opts"]["path"] >>= path; @@ -1097,8 +1110,18 @@ void explodeClash(Node yamlnode, std::vector &nodes) group = TROJAN_DEFAULT_GROUP; singleproxy["password"] >>= password; singleproxy["sni"] >>= host; + if(safe_as(singleproxy["network"]) == "grpc") + { + net = "grpc"; + singleproxy["grpc-opts"]["grpc-service-name"] >>= path; + } + else + { + net = "tcp"; + path.clear(); + } - trojanConstruct(node, group, ps, server, port, password, host, true, udp, tfo, scv); + trojanConstruct(node, group, ps, server, port, password, net, host, path, true, udp, tfo, scv); break; case "snell"_hash: group = SNELL_DEFAULT_GROUP; @@ -1574,7 +1597,7 @@ bool explodeSurge(std::string surge, std::vector &nodes) if(host.empty() && !isIPv4(server) && !isIPv6(server)) host = server; - trojanConstruct(node, TROJAN_DEFAULT_GROUP, remarks, server, port, password, host, true, udp, tfo, scv); + trojanConstruct(node, TROJAN_DEFAULT_GROUP, remarks, server, port, password, "", host, "", true, udp, tfo, scv); break; case "snell"_hash: server = trim(configs[1]); @@ -1840,7 +1863,7 @@ bool explodeSurge(std::string surge, std::vector &nodes) if(host.empty() && !isIPv4(server) && !isIPv6(server)) host = server; - trojanConstruct(node, TROJAN_DEFAULT_GROUP, remarks, server, port, password, host, tls == "true", udp, tfo, scv, tls13); + trojanConstruct(node, TROJAN_DEFAULT_GROUP, remarks, server, port, password, "", host, "", tls == "true", udp, tfo, scv, tls13); break; case "http"_hash: //quantumult x style http links server = trim(configs[0].substr(0, configs[0].rfind(":"))); diff --git a/src/parser/subparser.h b/src/parser/subparser.h index 5afa71c..3f05414 100644 --- a/src/parser/subparser.h +++ b/src/parser/subparser.h @@ -25,7 +25,7 @@ void ssrConstruct(Proxy &node, const std::string &group, const std::string &rema void ssConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &method, const std::string &plugin, const std::string &pluginopts, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool()); void socksConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &username, const std::string &password, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool()); void httpConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &username, const std::string &password, bool tls, tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool()); -void trojanConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &host, bool tlssecure, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool()); +void trojanConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &network, const std::string &host, const std::string &path, bool tlssecure, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool()); void snellConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &obfs, const std::string &host, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool()); void explodeVmess(std::string vmess, Proxy &node); void explodeSSR(std::string ssr, Proxy &node); diff --git a/src/script/cron.cpp b/src/script/cron.cpp index 05c6c85..0c2680e 100644 --- a/src/script/cron.cpp +++ b/src/script/cron.cpp @@ -2,6 +2,7 @@ #include #include +#include "../config/crontask.h" #include "../handler/interfaces.h" #include "../handler/multithread.h" #include "../server/webserver.h" @@ -11,7 +12,7 @@ #include "script_quickjs.h" extern bool gEnableCron; -extern string_array gCronTasks; +extern CronTaskConfigs gCronTasks; extern std::string gProxyConfig, gAccessToken; extern int gCacheConfig; @@ -48,13 +49,9 @@ int timeout_checker(JSRuntime *rt, void *opaque) void refresh_schedule() { cron.clear_schedules(); - for(std::string &x : gCronTasks) + for(const CronTaskConfig &x : gCronTasks) { - string_array arguments = split(x, "`"); - if(arguments.size() < 3) - continue; - std::string &name = arguments[0], &cronexp = arguments[1], &path = arguments[2]; - cron.add_schedule(name, cronexp, [=](auto &) + cron.add_schedule(x.Name, x.CronExp, [=](auto &) { qjs::Runtime runtime; qjs::Context context(runtime); @@ -64,18 +61,18 @@ void refresh_schedule() script_context_init(context); defer(script_cleanup(context);) std::string proxy = parseProxy(gProxyConfig); - std::string script = fetchFile(path, proxy, gCacheConfig); + std::string script = fetchFile(x.Path, proxy, gCacheConfig); if(script.empty()) { - writeLog(0, "Script '" + name + "' run failed: file is empty or not exist!", LOG_LEVEL_WARNING); + writeLog(0, "Script '" + x.Name + "' run failed: file is empty or not exist!", LOG_LEVEL_WARNING); return; } script_info info; - if(arguments.size() >= 4 && !arguments[3].empty()) + if(x.Timeout > 0) { info.begin_time = time(NULL); - info.timeout = to_int(arguments[3], 0); - info.name = name; + info.timeout = x.Timeout; + info.name = x.Name; JS_SetInterruptHandler(JS_GetRuntime(context.ctx), timeout_checker, &info); } context.eval(script); @@ -109,19 +106,15 @@ std::string list_cron_schedule(RESPONSE_CALLBACK_ARGS) writer.Int(200); writer.Key("tasks"); writer.StartArray(); - for(std::string &x : gCronTasks) + for(const CronTaskConfig &x : gCronTasks) { - string_array arguments = split(x, "`"); - if(arguments.size() < 3) - continue; writer.StartObject(); - std::string &name = arguments[0], &cronexp = arguments[1], &path = arguments[2]; writer.Key("name"); - writer.String(name.data()); + writer.String(x.Name.data()); writer.Key("cronexp"); - writer.String(cronexp.data()); + writer.String(x.CronExp.data()); writer.Key("path"); - writer.String(path.data()); + writer.String(x.Path.data()); writer.EndObject(); } writer.EndArray(); diff --git a/src/server/webserver.h b/src/server/webserver.h index 48783e3..b53a2b6 100644 --- a/src/server/webserver.h +++ b/src/server/webserver.h @@ -3,8 +3,13 @@ #include #include +#include +#include +#include #include "../utils/map_extra.h" +#include "../utils/string.h" +#include "../version.h" struct Request { @@ -36,11 +41,40 @@ struct listener_args uint32_t looper_interval = 200; }; -void append_response(const std::string &method, const std::string &uri, const std::string &content_type, response_callback response); -void append_redirect(const std::string &uri, const std::string &target); -void reset_redirect(); -int start_web_server(void *argv); -int start_web_server_multi(void *argv); -void stop_web_server(); +struct responseRoute +{ + std::string method; + std::string path; + std::string content_type; + response_callback rc; +}; + +class WebServer +{ +public: + std::string user_agent_str = "subconverter/" VERSION " cURL/" LIBCURL_VERSION; + std::atomic_bool SERVER_EXIT_FLAG{false}; + + // file server + bool serve_file = false; + std::string serve_file_root; + + // basic authentication + bool require_auth = false; + std::string auth_user, auth_password, auth_realm = "Please enter username and password:"; + + void append_response(const std::string &method, const std::string &uri, const std::string &content_type, response_callback response); + void append_redirect(const std::string &uri, const std::string &target); + void reset_redirect(); + int start_web_server(void *argv); + int start_web_server_multi(void *argv); + void stop_web_server(); +private: + int serveFile(const std::string &filename, std::string &content_type, std::string &return_data); + inline int process_request(Request &request, Response &response, std::string &return_data); + void on_request(evhttp_request *req, void *args); + std::vector responses; + string_map redirect_map; +}; #endif // WEBSERVER_H_INCLUDED diff --git a/src/server/webserver_libevent.cpp b/src/server/webserver_libevent.cpp index 1b071b3..1318b1f 100644 --- a/src/server/webserver_libevent.cpp +++ b/src/server/webserver_libevent.cpp @@ -14,7 +14,9 @@ #include #include #include +#include +#include "../utils/base64/base64.h" #include "../utils/file_extra.h" #include "../utils/logger.h" #include "../utils/stl_extra.h" @@ -23,12 +25,22 @@ #include "socket.h" #include "webserver.h" -extern std::string user_agent_str; -std::atomic_bool SERVER_EXIT_FLAG(false); +template +Pointer deduced_wrap( + const std::function &func) +{ + static auto saved = func; + static Pointer p = [](Args... args) { + return saved(std::forward(args)...); + }; + return p; +} -// file server -bool gServeFile = false; -std::string gServeFileRoot; +template +auto *wrap(Lambda &&func) +{ + return deduced_wrap(std::function{func}); +} struct MIME_type { @@ -78,9 +90,9 @@ std::string checkMIMEType(const std::string &filename) return "application/octet-stream"; } -int serveFile(const std::string &filename, std::string &content_type, std::string &return_data) +int WebServer::serveFile(const std::string &filename, std::string &content_type, std::string &return_data) { - std::string realname = gServeFileRoot + filename; + std::string realname = serve_file_root + filename; if(filename.compare("/") == 0) realname += "index.html"; if(!fileExist(realname)) @@ -92,17 +104,6 @@ int serveFile(const std::string &filename, std::string &content_type, std::strin return 0; } -struct responseRoute -{ - std::string method; - std::string path; - std::string content_type; - response_callback rc; -}; - -std::vector responses; -string_map redirect_map; - const char *request_header_blacklist[] = {"host", "accept", "accept-encoding"}; static inline void buffer_cleanup(struct evbuffer *eb) @@ -114,7 +115,7 @@ static inline void buffer_cleanup(struct evbuffer *eb) #endif // MALLOC_TRIM } -static inline int process_request(Request &request, Response &response, std::string &return_data) +inline int WebServer::process_request(Request &request, Response &response, std::string &return_data) { writeLog(0, "handle_cmd: " + request.method + " handle_uri: " + request.url, LOG_LEVEL_VERBOSE); @@ -125,13 +126,17 @@ static inline int process_request(Request &request, Response &response, std::str request.url.erase(pos); } + if(request.method == "OPTIONS") + { + for(responseRoute &x : responses) + if(matchSpaceSeparatedList(replaceAllDistinct(request.postdata, ",", ""), x.method) && x.path == request.url) + return 1; + return -1; + } + for(responseRoute &x : responses) { - if(request.method == "OPTIONS" && matchSpaceSeparatedList(replaceAllDistinct(request.postdata, ",", ""), x.method) && x.path == request.url) - { - return 1; - } - else if(x.method == request.method && x.path == request.url) + if(x.method == request.method && x.path == request.url) { response_callback &rc = x.rc; return_data = rc(request, response); @@ -151,11 +156,10 @@ static inline int process_request(Request &request, Response &response, std::str else return_data += "?" + request.argument; } - response.content_type = "REDIRECT"; - return 0; + return 2; } - if(gServeFile) + if(serve_file) { if(request.method.compare("GET") == 0 && serveFile(request.url, response.content_type, return_data) == 0) return 0; @@ -164,9 +168,10 @@ static inline int process_request(Request &request, Response &response, std::str return -1; } -void OnReq(evhttp_request *req, void *args) +void WebServer::on_request(evhttp_request *req, void *args) { (void)args; + static std::string auth_token = "Basic " + base64Encode(auth_user + ":" + auth_password); const char *req_content_type = evhttp_find_header(req->input_headers, "Content-Type"), *req_ac_method = evhttp_find_header(req->input_headers, "Access-Control-Request-Method"); const char *uri = req->uri, *internal_flag = evhttp_find_header(req->input_headers, "SubConverter-Request"); @@ -176,26 +181,41 @@ void OnReq(evhttp_request *req, void *args) //std::cerr<<"Accept connection from client "<input_headers, "Authorization"); + if (auth == nullptr || auth_token != auth) + { + evhttp_add_header(req->output_headers, "WWW-Authenticate", ("Basic realm=\"" + auth_realm + "\"").data()); + auto buffer = evhttp_request_get_output_buffer(req); + evbuffer_add_printf(buffer, "Unauthorized"); + evhttp_send_reply(req, 401, nullptr, buffer); + buffer_cleanup(buffer); + return; + } + } + Request request; Response response; - if(EVBUFFER_LENGTH(req->input_buffer) != 0) + size_t buffer_len = evbuffer_get_length(req->input_buffer); + if (buffer_len != 0) { - request.postdata.assign((char *)EVBUFFER_DATA(req->input_buffer), EVBUFFER_LENGTH(req->input_buffer)); - if(req_content_type != NULL && strcmp(req_content_type, "application/x-www-form-urlencoded") == 0) + request.postdata.assign(reinterpret_cast(evbuffer_pullup(req->input_buffer, -1)), buffer_len); + if(req_content_type != nullptr && strcmp(req_content_type, "application/x-www-form-urlencoded") == 0) request.postdata = urlDecode(request.postdata); } - else if(req_ac_method != NULL) + else if (req_ac_method != nullptr) { request.postdata.assign(req_ac_method); } - switch(req->type) + switch (req->type) { case EVHTTP_REQ_GET: request.method = "GET"; break; case EVHTTP_REQ_POST: request.method = "POST"; break; @@ -218,54 +238,50 @@ void OnReq(evhttp_request *req, void *args) std::string return_data; int retVal = process_request(request, response, return_data); - std::string content_type = response.content_type; + std::string &content_type = response.content_type; - auto *OutBuf = evhttp_request_get_output_buffer(req); - //struct evbuffer *OutBuf = evbuffer_new(); - if (!OutBuf) + auto *output_buffer = evhttp_request_get_output_buffer(req); + if (!output_buffer) + { + evhttp_send_error(req, HTTP_INTERNAL, nullptr); return; + } - for(auto &x : response.headers) + for (auto &x : response.headers) evhttp_add_header(req->output_headers, x.first.data(), x.second.data()); - switch(retVal) + switch (retVal) { case 1: //found OPTIONS evhttp_add_header(req->output_headers, "Access-Control-Allow-Origin", "*"); evhttp_add_header(req->output_headers, "Access-Control-Allow-Headers", "*"); - evhttp_send_reply(req, response.status_code, "", NULL); + evhttp_send_reply(req, response.status_code, nullptr, nullptr); + break; + case 2: //found redirect + evhttp_add_header(req->output_headers, "Location", return_data.c_str()); + evhttp_send_reply(req, HTTP_MOVETEMP, nullptr, nullptr); break; case 0: //found normal - if(content_type.size()) - { - if(content_type == "REDIRECT") - { - evhttp_add_header(req->output_headers, "Location", return_data.c_str()); - evhttp_send_reply(req, HTTP_MOVETEMP, "", NULL); - buffer_cleanup(OutBuf); - return; - } - else - evhttp_add_header(req->output_headers, "Content-Type", content_type.c_str()); - } + if (content_type.size()) + evhttp_add_header(req->output_headers, "Content-Type", content_type.c_str()); evhttp_add_header(req->output_headers, "Access-Control-Allow-Origin", "*"); evhttp_add_header(req->output_headers, "Connection", "close"); - evbuffer_add(OutBuf, return_data.data(), return_data.size()); - evhttp_send_reply(req, response.status_code, "", OutBuf); + evbuffer_add(output_buffer, return_data.data(), return_data.size()); + evhttp_send_reply(req, response.status_code, nullptr, output_buffer); break; case -1: //not found return_data = "File not found."; - evbuffer_add(OutBuf, return_data.data(), return_data.size()); - evhttp_send_reply(req, HTTP_NOTFOUND, "", OutBuf); + evbuffer_add(output_buffer, return_data.data(), return_data.size()); + evhttp_send_reply(req, HTTP_NOTFOUND, nullptr, output_buffer); //evhttp_send_error(req, HTTP_NOTFOUND, "Resource not found"); break; default: //undefined behavior - evhttp_send_error(req, HTTP_INTERNAL, ""); + evhttp_send_error(req, HTTP_INTERNAL, nullptr); } - buffer_cleanup(OutBuf); + buffer_cleanup(output_buffer); } -int start_web_server(void *argv) +int WebServer::start_web_server(void *argv) { struct listener_args *args = reinterpret_cast(argv); std::string listen_address = args->listen_address; @@ -278,17 +294,19 @@ int start_web_server(void *argv) } const char *SrvAddress = listen_address.c_str(); std::uint16_t SrvPort = port; - std::unique_ptr Server(evhttp_start(SrvAddress, SrvPort), &evhttp_free); - if (!Server) + std::unique_ptr server(evhttp_start(SrvAddress, SrvPort), &evhttp_free); + if (!server) { //std::cerr << "Failed to init http server." << std::endl; writeLog(0, "Failed to init http server.", LOG_LEVEL_FATAL); return -1; } - evhttp_set_allowed_methods(Server.get(), EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS | EVHTTP_REQ_PUT | EVHTTP_REQ_PATCH | EVHTTP_REQ_DELETE); - evhttp_set_gencb(Server.get(), OnReq, nullptr); - evhttp_set_timeout(Server.get(), 30); + auto call_on_request = [&](evhttp_request *req, void *args) { on_request(req, args); }; + + evhttp_set_allowed_methods(server.get(), EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS | EVHTTP_REQ_PUT | EVHTTP_REQ_PATCH | EVHTTP_REQ_DELETE); + evhttp_set_gencb(server.get(), wrap(call_on_request), nullptr); + evhttp_set_timeout(server.get(), 30); if (event_dispatch() == -1) { //std::cerr << "Failed to run message loop." << std::endl; @@ -303,7 +321,7 @@ void* httpserver_dispatch(void *arg) { event_base_dispatch(reinterpret_cast(arg)); event_base_free(reinterpret_cast(arg)); //free resources - return NULL; + return nullptr; } int httpserver_bindsocket(std::string listen_address, int listen_port, int backlog) @@ -345,44 +363,45 @@ int httpserver_bindsocket(std::string listen_address, int listen_port, int backl return nfd; } -int start_web_server_multi(void *argv) +int WebServer::start_web_server_multi(void *argv) { struct listener_args *args = reinterpret_cast(argv); std::string listen_address = args->listen_address; - int port = args->port, nthreads = args->max_workers; - int i; + int port = args->port, nthreads = args->max_workers, max_conn = args->max_conn; - int nfd = httpserver_bindsocket(listen_address, port, args->max_conn); + auto call_on_request = [&](evhttp_request *req, void *args) { on_request(req, args); }; + + int nfd = httpserver_bindsocket(listen_address, port, max_conn); if (nfd < 0) return -1; pthread_t ths[nthreads]; struct event_base *base[nthreads]; - for (i = 0; i < nthreads; i++) + for (int i = 0; i < nthreads; i++) { base[i] = event_init(); - if (base[i] == NULL) + if (base[i] == nullptr) return -1; struct evhttp *httpd = evhttp_new(base[i]); - if (httpd == NULL) + if (httpd == nullptr) return -1; if (evhttp_accept_socket(httpd, nfd) != 0) return -1; - evhttp_set_allowed_methods(httpd, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS); - evhttp_set_gencb(httpd, OnReq, nullptr); + evhttp_set_allowed_methods(httpd, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS | EVHTTP_REQ_PUT | EVHTTP_REQ_PATCH | EVHTTP_REQ_DELETE); + evhttp_set_gencb(httpd, wrap(call_on_request), nullptr); evhttp_set_timeout(httpd, 30); - if (pthread_create(&ths[i], NULL, httpserver_dispatch, base[i]) != 0) + if (pthread_create(&ths[i], nullptr, httpserver_dispatch, base[i]) != 0) return -1; } while (!SERVER_EXIT_FLAG) { - if(args->looper_callback != nullptr) + if (args->looper_callback != nullptr) args->looper_callback(); std::this_thread::sleep_for(std::chrono::milliseconds(args->looper_interval)); //block forever until receive stop signal } - for (i = 0; i < nthreads; i++) + for (int i = 0; i < nthreads; i++) event_base_loopbreak(base[i]); //stop the loop shutdown(nfd, SD_BOTH); //stop accept call @@ -391,12 +410,12 @@ int start_web_server_multi(void *argv) return 0; } -void stop_web_server() +void WebServer::stop_web_server() { SERVER_EXIT_FLAG = true; } -void append_response(const std::string &method, const std::string &uri, const std::string &content_type, response_callback response) +void WebServer::append_response(const std::string &method, const std::string &uri, const std::string &content_type, response_callback response) { responseRoute rr; rr.method = method; @@ -406,12 +425,12 @@ void append_response(const std::string &method, const std::string &uri, const st responses.emplace_back(std::move(rr)); } -void append_redirect(const std::string &uri, const std::string &target) +void WebServer::append_redirect(const std::string &uri, const std::string &target) { redirect_map[uri] = target; } -void reset_redirect() +void WebServer::reset_redirect() { eraseElements(redirect_map); } diff --git a/src/utils/tribool.h b/src/utils/tribool.h index b411d1b..03b3634 100644 --- a/src/utils/tribool.h +++ b/src/utils/tribool.h @@ -34,6 +34,8 @@ public: return *this; } + inline bool operator==(const tribool& rhs){ return _M_VALUE == rhs._M_VALUE; } + operator bool() const { return _M_VALUE == 3; } bool is_undef() const { return _M_VALUE <= 1; }