mirror of
https://github.com/asdlokj1qpi233/subconverter.git
synced 2025-10-28 04:12:49 +00:00
Enhancements
Change section and key names in configuration files. Add support for using Quantumult X rulesets/Clash rule-providers as rule sources. Optimize codes.
This commit is contained in:
@@ -22,19 +22,19 @@ custom_proxy_group=!!import:snippets/groups_forcerule.txt
|
||||
;Options for custom rulesets
|
||||
enable_rule_generator=false
|
||||
overwrite_original_rules=false
|
||||
;surge_ruleset=DIRECT,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Unbreak.list
|
||||
;surge_ruleset=⛔️ 广告拦截,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Advertising.list
|
||||
;surge_ruleset=🚫 运营劫持,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Hijacking.list
|
||||
;surge_ruleset=🌌 YouTube,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/YouTube.list
|
||||
;surge_ruleset=🎥 NETFLIX,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/Netflix.list
|
||||
;surge_ruleset=HBO,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/HBO.list
|
||||
;surge_ruleset=Fox,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/Fox.list
|
||||
;surge_ruleset=🌍 国外媒体,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/GlobalMedia.list
|
||||
;surge_ruleset=🌏 港台媒体,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/HKMTMedia.list
|
||||
;surge_ruleset=📲 电报信息,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Telegram.list
|
||||
;surge_ruleset=🔰 节点选择,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Global.list
|
||||
;surge_ruleset=🍎 苹果服务,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Apple.list
|
||||
;surge_ruleset=DIRECT,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/China.list
|
||||
;ruleset=DIRECT,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Unbreak.list
|
||||
;ruleset=⛔️ 广告拦截,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Advertising.list
|
||||
;ruleset=🚫 运营劫持,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Hijacking.list
|
||||
;ruleset=🌌 YouTube,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/YouTube.list
|
||||
;ruleset=🎥 NETFLIX,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/Netflix.list
|
||||
;ruleset=HBO,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/HBO.list
|
||||
;ruleset=Fox,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Media/Fox.list
|
||||
;ruleset=🌍 国外媒体,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/GlobalMedia.list
|
||||
;ruleset=🌏 港台媒体,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/HKMTMedia.list
|
||||
;ruleset=📲 电报信息,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Telegram.list
|
||||
;ruleset=🔰 节点选择,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Global.list
|
||||
;ruleset=🍎 苹果服务,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Apple.list
|
||||
;ruleset=DIRECT,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/China.list
|
||||
|
||||
;Options for custom base configuration file
|
||||
clash_rule_base=base/forcerule.yml
|
||||
|
||||
@@ -67,16 +67,21 @@ emojis:
|
||||
# - {match: "(流量|时间|应急)", emoji: "🏳️🌈"}
|
||||
- {import: snippets/emoji.txt}
|
||||
|
||||
ruleset:
|
||||
rulesets:
|
||||
enabled: true
|
||||
overwrite_original_rules: false
|
||||
update_ruleset_on_request: false
|
||||
surge_ruleset:
|
||||
rulesets:
|
||||
# - {rule: "GEOIP,CN", group: "DIRECT"}
|
||||
# - {ruleset: "rules/LocalAreaNetwork.list", group: "DIRECT"}
|
||||
# - {ruleset: "surge:rules/LocalAreaNetwork.list", group: "DIRECT"}
|
||||
# - {ruleset: "quanx:https://raw.githubusercontent.com/ConnersHua/Profiles/master/Quantumult/X/Filter/Advertising.list", group: "Advertising"}
|
||||
# - {ruleset: "clash-domain:https://ruleset.dev/clash_domestic_services_domains", group: "Domestic Services"}
|
||||
# - {ruleset: "clash-ipcidr:https://ruleset.dev/clash_domestic_services_ips", group: "Domestic Services"}
|
||||
# - {ruleset: "clash-classic:https://raw.githubusercontent.com/DivineEngine/Profiles/master/Clash/RuleSet/China.yaml", group: "DIRECT"}
|
||||
- {import: snippets/rulesets.txt}
|
||||
|
||||
proxy_group:
|
||||
proxy_groups:
|
||||
custom_proxy_group:
|
||||
# - {name: UrlTest, type: url-test, rule: [".*"], url: http://www.gstatic.com/generate_204, interval: 300, tolerance: 100, timeout: 5}
|
||||
# - {name: Proxy, type: select, rule: [".*"]}
|
||||
|
||||
@@ -142,7 +142,7 @@ remove_old_emoji=true
|
||||
|
||||
rule=!!import:snippets/emoji.txt
|
||||
|
||||
[ruleset]
|
||||
[rulesets]
|
||||
;Enable generating rules with rulesets
|
||||
enabled=true
|
||||
|
||||
@@ -153,17 +153,24 @@ overwrite_original_rules=false
|
||||
update_ruleset_on_request=false
|
||||
|
||||
;Ruleset addresses, supports local files/URL
|
||||
;Format: Group name,URL
|
||||
;Format: Group name,[type:]URL
|
||||
; Group name,[]Rule
|
||||
;where "type" supports the following value: surge, quanx, clash-domain, clash-ipcidr, clash-classic
|
||||
;type defaults to surge if omitted
|
||||
|
||||
;surge_ruleset=DIRECT,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Unbreak.list
|
||||
;surge_ruleset=🎯 全球直连,rules/LocalAreaNetwork.list
|
||||
;surge_ruleset=🎯 全球直连,[]GEOIP,CN
|
||||
;surge_ruleset=🐟 漏网之鱼,[]FINAL
|
||||
;ruleset=DIRECT,https://raw.githubusercontent.com/ConnersHua/Profiles/master/Surge/Ruleset/Unbreak.list
|
||||
;ruleset=🎯 全球直连,rules/LocalAreaNetwork.list
|
||||
;ruleset=DIRECT,surge:rules/LocalAreaNetwork.list
|
||||
;ruleset=Advertising,quanx:https://raw.githubusercontent.com/ConnersHua/Profiles/master/Quantumult/X/Filter/Advertising.list
|
||||
;ruleset=Domestic Services,clash-domain:https://ruleset.dev/clash_domestic_services_domains
|
||||
;ruleset=Domestic Services,clash-ipcidr:https://ruleset.dev/clash_domestic_services_ips
|
||||
;ruleset=DIRECT,clash-classic:https://raw.githubusercontent.com/DivineEngine/Profiles/master/Clash/RuleSet/China.yaml
|
||||
;ruleset=🎯 全球直连,[]GEOIP,CN
|
||||
;ruleset=🐟 漏网之鱼,[]FINAL
|
||||
|
||||
surge_ruleset=!!import:snippets/rulesets.txt
|
||||
ruleset=!!import:snippets/rulesets.txt
|
||||
|
||||
[clash_proxy_group]
|
||||
[proxy_groups]
|
||||
;Generate Clash Proxy Group with the following patterns. Node filtering rule supports regular expression.
|
||||
;Format: Group_Name`select`Rule_1`Rule_2`...
|
||||
; Group_Name`url-test|fallback|load-balance`Rule_1`Rule_2`...`test_url`interval[,timeout][,tolerance]
|
||||
|
||||
@@ -217,7 +217,7 @@ public:
|
||||
string_multimap itemGroup, existItemGroup;
|
||||
std::stringstream strStrm;
|
||||
unsigned int lineSize = 0;
|
||||
char delimiter = count(content.begin(), content.end(), '\n') < 1 ? '\r' : '\n';
|
||||
char delimiter = getLineBreak(content);
|
||||
|
||||
EraseAll(); //first erase all data
|
||||
if(do_utf8_to_gbk && is_str_utf8(content))
|
||||
|
||||
@@ -75,6 +75,8 @@ size_t max_allowed_rulesets = 64, max_allowed_rules = 32768;
|
||||
|
||||
string_array regex_blacklist = {"(.*)*"};
|
||||
|
||||
void refreshRulesets(string_array &ruleset_list, std::vector<ruleset_content> &rca);
|
||||
|
||||
std::string parseProxy(const std::string &source)
|
||||
{
|
||||
std::string proxy = source;
|
||||
@@ -90,8 +92,80 @@ const string_array clash_rule_type = {basic_types, "IP-CIDR6", "SRC-PORT", "DST-
|
||||
const string_array surge_rule_type = {basic_types, "IP-CIDR6", "USER-AGENT", "URL-REGEX", "AND", "OR", "NOT", "PROCESS-NAME", "IN-PORT", "DEST-PORT", "SRC-IP"};
|
||||
const string_array quanx_rule_type = {basic_types, "USER-AGENT", "HOST", "HOST-SUFFIX", "HOST-KEYWORD"};
|
||||
|
||||
std::string convertRuleset(const std::string &content, int type)
|
||||
{
|
||||
/// Target: Surge type,pattern[,flag]
|
||||
/// Source: QuanX type,pattern[,group]
|
||||
/// Clash payload:\n - 'ipcidr/domain/classic(Surge-like)'
|
||||
|
||||
std::string output, strLine, strTemp;
|
||||
|
||||
if(type == RULESET_SURGE)
|
||||
return content;
|
||||
|
||||
if(startsWith(content, "payload:")) /// Clash
|
||||
{
|
||||
output = regReplace(regReplace(content, "payload:\\r?\\n", "", true), "\\s?^\\s*-\\s+('?)(.*)\\1$", "\n$2", true);
|
||||
if(type == RULESET_CLASH_CLASSICAL) /// classical type
|
||||
return output;
|
||||
std::stringstream ss;
|
||||
ss << output;
|
||||
char delimiter = getLineBreak(output);
|
||||
output.clear();
|
||||
string_size pos, lineSize;
|
||||
while(getline(ss, strLine, delimiter))
|
||||
{
|
||||
lineSize = strLine.size();
|
||||
if(lineSize && strLine[lineSize - 1] == '\r') //remove line break
|
||||
{
|
||||
strLine.erase(lineSize - 1);
|
||||
lineSize--;
|
||||
}
|
||||
|
||||
if(!strLine.empty() && (strLine[0] != ';' && strLine[0] != '#' && !(lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')))
|
||||
{
|
||||
pos = strLine.find("/");
|
||||
if(pos != strLine.npos) /// ipcidr
|
||||
{
|
||||
if(isIPv4(strLine.substr(0, pos)))
|
||||
output += "IP-CIDR,";
|
||||
else
|
||||
output += "IP-CIDR6,";
|
||||
}
|
||||
else
|
||||
{
|
||||
if(strLine[0] == '.' || (lineSize >= 2 && strLine[0] == '+' && strLine[1] == '.')) /// suffix
|
||||
{
|
||||
output += "DOMAIN-SUFFIX,";
|
||||
strLine.erase(0, 2 - (strLine[0] == '.'));
|
||||
}
|
||||
else
|
||||
output += "DOMAIN,";
|
||||
}
|
||||
}
|
||||
output += strLine;
|
||||
output += '\n';
|
||||
}
|
||||
return output;
|
||||
}
|
||||
else /// QuanX
|
||||
{
|
||||
output = regReplace(regReplace(content, "(?i:host)", "DOMAIN", true), "(?i:ip6-cidr)", "IP-CIDR6", true); //translate type
|
||||
output = regReplace(output, "^((?i:DOMAIN(?:-(?:SUFFIX|KEYWORD))?|IP-CIDR6?),.*?)(?:,(?!no-resolve).*?)(,no-resolve)?$", "$1$2", true); //remove group
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
std::string getConvertedRuleset(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
std::string url = UrlDecode(getUrlArg(request.argument, "url")), type = getUrlArg(request.argument, "type");
|
||||
return convertRuleset(fetchFile(url, parseProxy(proxy_ruleset), cache_ruleset), to_int(type));
|
||||
}
|
||||
|
||||
std::string getRuleset(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
int *status_code = &response.status_code;
|
||||
/// type: 1 for Surge, 2 for Quantumult X, 3 for Clash domain rule-provider, 4 for Clash ipcidr rule-provider, 5 for Surge DOMAIN-SET, 6 for Clash classical ruleset
|
||||
std::string url = urlsafe_base64_decode(getUrlArg(argument, "url")), type = getUrlArg(argument, "type"), group = urlsafe_base64_decode(getUrlArg(argument, "group"));
|
||||
std::string output_content, dummy;
|
||||
@@ -106,7 +180,14 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS)
|
||||
std::string proxy = parseProxy(proxy_ruleset);
|
||||
string_array vArray = split(url, "|");
|
||||
for(std::string &x : vArray)
|
||||
output_content += fetchFile(x, proxy, cache_ruleset) + "\n";
|
||||
x.insert(0, "ruleset,");
|
||||
std::vector<ruleset_content> rca;
|
||||
refreshRulesets(vArray, rca);
|
||||
for(ruleset_content &x : rca)
|
||||
{
|
||||
std::string content = x.rule_content.get();
|
||||
output_content += convertRuleset(content, x.rule_type);
|
||||
}
|
||||
|
||||
if(!output_content.size())
|
||||
{
|
||||
@@ -119,7 +200,7 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS)
|
||||
const std::string rule_match_regex = "^(.*?,.*?)(,.*)(,.*)$";
|
||||
|
||||
ss << output_content;
|
||||
char delimiter = count(output_content.begin(), output_content.end(), '\n') < 1 ? '\r' : '\n';
|
||||
char delimiter = getLineBreak(output_content);
|
||||
std::string::size_type lineSize, posb, pose;
|
||||
auto filterLine = [&]()
|
||||
{
|
||||
@@ -136,7 +217,7 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS)
|
||||
pose--;
|
||||
}
|
||||
else
|
||||
pose -= posb + 1;
|
||||
pose -= posb;
|
||||
return 0;
|
||||
};
|
||||
|
||||
@@ -255,7 +336,7 @@ int importItems(string_array &target, bool scope_limit = true)
|
||||
|
||||
if(fileExist(path))
|
||||
content = fileGet(path, scope_limit);
|
||||
else if(startsWith(path, "http://") || startsWith(path, "https://") || startsWith(path, "data:"))
|
||||
else if(isLink(path))
|
||||
content = webGet(path, proxy, dummy, cache_config);
|
||||
else
|
||||
writeLog(0, "File not found or not a valid URL: " + path, LOG_LEVEL_ERROR);
|
||||
@@ -263,7 +344,7 @@ int importItems(string_array &target, bool scope_limit = true)
|
||||
return -1;
|
||||
|
||||
ss << content;
|
||||
char delimiter = count(content.begin(), content.end(), '\n') < 1 ? '\r' : '\n';
|
||||
char delimiter = getLineBreak(content);
|
||||
std::string::size_type lineSize;
|
||||
while(getline(ss, strLine, delimiter))
|
||||
{
|
||||
@@ -427,7 +508,7 @@ void readRuleset(YAML::Node node, string_array &dest, bool scope_limit = true)
|
||||
void refreshRulesets(string_array &ruleset_list, std::vector<ruleset_content> &rca)
|
||||
{
|
||||
eraseElements(rca);
|
||||
std::string rule_group, rule_url, dummy;
|
||||
std::string rule_group, rule_url, rule_url_typed, dummy;
|
||||
ruleset_content rc;
|
||||
|
||||
std::string proxy = parseProxy(proxy_ruleset);
|
||||
@@ -450,12 +531,25 @@ void refreshRulesets(string_array &ruleset_list, std::vector<ruleset_content> &r
|
||||
{
|
||||
writeLog(0, "Adding rule '" + rule_url.substr(2) + "," + rule_group + "'.", LOG_LEVEL_INFO);
|
||||
//std::cerr<<"Adding rule '"<<rule_url.substr(2)<<","<<rule_group<<"'."<<std::endl;
|
||||
rc = {rule_group, "", std::async(std::launch::async, [rule_url](){return rule_url;})};
|
||||
rc = {rule_group, "", "", RULESET_SURGE, std::async(std::launch::async, [rule_url](){return rule_url;})};
|
||||
rca.emplace_back(rc);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
ruleset_type type = RULESET_SURGE;
|
||||
const std::map<std::string, ruleset_type> types = {{"clash-domain:", RULESET_CLASH_DOMAIN}, {"clash-ipcidr:", RULESET_CLASH_IPCIDR}, {"clash-classic:", RULESET_CLASH_CLASSICAL}, \
|
||||
{"quanx:", RULESET_QUANX}, {"surge:", RULESET_SURGE}};
|
||||
rule_url_typed = rule_url;
|
||||
for(auto &y : types)
|
||||
{
|
||||
if(startsWith(rule_url, y.first))
|
||||
{
|
||||
rule_url.erase(0, y.first.size());
|
||||
type = y.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//std::cerr<<"Updating ruleset url '"<<rule_url<<"' with group '"<<rule_group<<"'."<<std::endl;
|
||||
writeLog(0, "Updating ruleset url '" + rule_url + "' with group '" + rule_group + "'.", LOG_LEVEL_INFO);
|
||||
/*
|
||||
@@ -470,7 +564,7 @@ void refreshRulesets(string_array &ruleset_list, std::vector<ruleset_content> &r
|
||||
else
|
||||
continue;
|
||||
*/
|
||||
rc = {rule_group, rule_url, fetchFileAsync(rule_url, proxy, cache_ruleset, async_fetch_ruleset)};
|
||||
rc = {rule_group, rule_url, rule_url_typed, type, fetchFileAsync(rule_url, proxy, cache_ruleset, async_fetch_ruleset)};
|
||||
}
|
||||
rca.emplace_back(rc);
|
||||
/*
|
||||
@@ -610,9 +704,10 @@ void readYAMLConf(YAML::Node &node)
|
||||
}
|
||||
}
|
||||
|
||||
if(node["ruleset"].IsDefined())
|
||||
const char *rulesets_title = node["rulesets"].IsDefined() ? "rulesets" : "ruleset";
|
||||
if(node[rulesets_title].IsDefined())
|
||||
{
|
||||
section = node["ruleset"];
|
||||
section = node[rulesets_title];
|
||||
section["enabled"] >> enable_rule_generator;
|
||||
if(!enable_rule_generator)
|
||||
{
|
||||
@@ -624,12 +719,14 @@ void readYAMLConf(YAML::Node &node)
|
||||
section["overwrite_original_rules"] >> overwrite_original_rules;
|
||||
section["update_ruleset_on_request"] >> update_ruleset_on_request;
|
||||
}
|
||||
if(section["surge_ruleset"].IsSequence())
|
||||
readRuleset(section["surge_ruleset"], rulesets, false);
|
||||
const char *ruleset_title = section["rulesets"].IsDefined() ? "rulesets" : "surge_ruleset";
|
||||
if(section[ruleset_title].IsSequence())
|
||||
readRuleset(section[ruleset_title], rulesets, false);
|
||||
}
|
||||
|
||||
if(node["proxy_group"].IsDefined() && node["proxy_group"]["custom_proxy_group"].IsDefined())
|
||||
readGroup(node["proxy_group"]["custom_proxy_group"], clash_extra_group, false);
|
||||
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"], clash_extra_group, false);
|
||||
|
||||
if(node["template"].IsDefined())
|
||||
{
|
||||
@@ -851,13 +948,21 @@ void readConf()
|
||||
eraseElements(tempArray);
|
||||
}
|
||||
|
||||
ini.EnterSection("ruleset");
|
||||
if(ini.SectionExist("rulesets"))
|
||||
ini.EnterSection("rulesets");
|
||||
else
|
||||
ini.EnterSection("ruleset");
|
||||
enable_rule_generator = ini.GetBool("enabled");
|
||||
if(enable_rule_generator)
|
||||
{
|
||||
ini.GetBoolIfExist("overwrite_original_rules", overwrite_original_rules);
|
||||
ini.GetBoolIfExist("update_ruleset_on_request", update_ruleset_on_request);
|
||||
if(ini.ItemPrefixExist("surge_ruleset"))
|
||||
if(ini.ItemPrefixExist("ruleset"))
|
||||
{
|
||||
ini.GetAll("ruleset", rulesets);
|
||||
importItems(rulesets, true);
|
||||
}
|
||||
else if(ini.ItemPrefixExist("surge_ruleset"))
|
||||
{
|
||||
ini.GetAll("surge_ruleset", rulesets);
|
||||
importItems(rulesets, false);
|
||||
@@ -869,7 +974,10 @@ void readConf()
|
||||
update_ruleset_on_request = false;
|
||||
}
|
||||
|
||||
ini.EnterSection("clash_proxy_group");
|
||||
if(ini.SectionExist("proxy_groups"))
|
||||
ini.EnterSection("proxy_groups");
|
||||
else
|
||||
ini.EnterSection("clash_proxy_group");
|
||||
if(ini.ItemPrefixExist("custom_proxy_group"))
|
||||
{
|
||||
ini.GetAll("custom_proxy_group", clash_extra_group);
|
||||
@@ -1000,9 +1108,10 @@ int loadExternalYAML(YAML::Node &node, ExternalConfig &ext)
|
||||
if(section["custom_proxy_group"].size())
|
||||
readGroup(section["custom_proxy_group"], ext.custom_proxy_group, api_mode);
|
||||
|
||||
if(section["surge_ruleset"].size())
|
||||
const char *ruleset_name = section["rulesets"].IsDefined() ? "rulesets" : "surge_ruleset";
|
||||
if(section[ruleset_name].size())
|
||||
{
|
||||
readRuleset(section["surge_ruleset"], ext.surge_ruleset, api_mode);
|
||||
readRuleset(section[ruleset_name], ext.surge_ruleset, api_mode);
|
||||
if(max_allowed_rulesets && ext.surge_ruleset.size() > max_allowed_rulesets)
|
||||
{
|
||||
writeLog(0, "Ruleset count in external config has exceeded limit.", LOG_LEVEL_WARNING);
|
||||
@@ -1014,8 +1123,9 @@ int loadExternalYAML(YAML::Node &node, ExternalConfig &ext)
|
||||
if(section["rename_node"].size())
|
||||
readRegexMatch(section["rename_node"], "@", ext.rename, api_mode);
|
||||
|
||||
if(section["emoji"].size())
|
||||
readEmoji(section["emoji"], ext.emoji, api_mode);
|
||||
const char *emoji_name = section["emojis"].IsDefined() ? "emojis" : "emoji";
|
||||
if(section[emoji_name].size())
|
||||
readEmoji(section[emoji_name], ext.emoji, api_mode);
|
||||
|
||||
section["include_remarks"] >> ext.include;
|
||||
section["exclude_remarks"] >> ext.exclude;
|
||||
@@ -1067,9 +1177,10 @@ int loadExternalConfig(std::string &path, ExternalConfig &ext)
|
||||
ini.GetAll("custom_proxy_group", ext.custom_proxy_group);
|
||||
importItems(ext.custom_proxy_group, api_mode);
|
||||
}
|
||||
if(ini.ItemPrefixExist("surge_ruleset"))
|
||||
std::string ruleset_name = ini.ItemPrefixExist("ruleset") ? "ruleset" : "surge_ruleset";
|
||||
if(ini.ItemPrefixExist(ruleset_name))
|
||||
{
|
||||
ini.GetAll("surge_ruleset", ext.surge_ruleset);
|
||||
ini.GetAll(ruleset_name, ext.surge_ruleset);
|
||||
importItems(ext.surge_ruleset, api_mode);
|
||||
if(max_allowed_rulesets && ext.surge_ruleset.size() > max_allowed_rulesets)
|
||||
{
|
||||
@@ -1120,7 +1231,7 @@ int loadExternalConfig(std::string &path, ExternalConfig &ext)
|
||||
|
||||
void checkExternalBase(const std::string &path, std::string &dest)
|
||||
{
|
||||
if(startsWith(path, "https://") || startsWith(path, "http://") || startsWith(path, "data:") || (startsWith(path, base_path) && fileExist(path)))
|
||||
if(isLink(path) || (startsWith(path, base_path) && fileExist(path)))
|
||||
dest = path;
|
||||
}
|
||||
|
||||
@@ -1158,6 +1269,9 @@ void generateBase()
|
||||
|
||||
std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
int *status_code = &response.status_code;
|
||||
|
||||
std::string target = getUrlArg(argument, "target");
|
||||
switch(hash_(target))
|
||||
{
|
||||
@@ -1469,7 +1583,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
x.group = group;
|
||||
|
||||
if(subInfo.size() && append_sub_userinfo.get(append_userinfo))
|
||||
extra_headers.emplace("Subscription-UserInfo", subInfo);
|
||||
response.headers.emplace("Subscription-UserInfo", subInfo);
|
||||
|
||||
//do pre-process now
|
||||
preprocessNodes(nodes, ext);
|
||||
@@ -1702,12 +1816,15 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
|
||||
}
|
||||
writeLog(0, "Generate completed.", LOG_LEVEL_INFO);
|
||||
if(filename.size())
|
||||
extra_headers.emplace("Content-Disposition", "attachment; filename=\"" + filename + "\"");
|
||||
response.headers.emplace("Content-Disposition", "attachment; filename=\"" + filename + "\"");
|
||||
return output_content;
|
||||
}
|
||||
|
||||
std::string simpleToClashR(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
int *status_code = &response.status_code;
|
||||
|
||||
std::string url = argument.size() <= 8 ? "" : argument.substr(8);
|
||||
if(!url.size() || argument.substr(0, 8) != "sublink=")
|
||||
{
|
||||
@@ -1719,11 +1836,15 @@ std::string simpleToClashR(RESPONSE_CALLBACK_ARGS)
|
||||
*status_code = 400;
|
||||
return "Please insert your subscription link instead of clicking the default link.";
|
||||
}
|
||||
return subconverter("target=clashr&url=" + UrlEncode(url), postdata, status_code, extra_headers);
|
||||
request.argument = "target=clashr&url=" + UrlEncode(url);
|
||||
return subconverter(request, response);
|
||||
}
|
||||
|
||||
std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
int *status_code = &response.status_code;
|
||||
|
||||
INIReader ini;
|
||||
string_array dummy_str_array;
|
||||
std::vector<nodeInfo> nodes;
|
||||
@@ -1899,7 +2020,7 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
|
||||
continue;
|
||||
|
||||
ss << content;
|
||||
char delimiter = count(content.begin(), content.end(), '\n') < 1 ? '\r' : '\n';
|
||||
char delimiter = getLineBreak(content);
|
||||
|
||||
while(getline(ss, strLine, delimiter))
|
||||
{
|
||||
@@ -1931,6 +2052,9 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
|
||||
|
||||
std::string getProfile(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
int *status_code = &response.status_code;
|
||||
|
||||
std::string name = UrlDecode(getUrlArg(argument, "name")), token = UrlDecode(getUrlArg(argument, "token"));
|
||||
string_array profiles = split(name, "|");
|
||||
name = profiles[0];
|
||||
@@ -2026,11 +2150,14 @@ std::string getProfile(RESPONSE_CALLBACK_ARGS)
|
||||
query += x.first + "=" + UrlEncode(x.second) + "&";
|
||||
}
|
||||
query += argument;
|
||||
return subconverter(query, postdata, status_code, extra_headers);
|
||||
request.argument = query;
|
||||
return subconverter(request, response);
|
||||
}
|
||||
|
||||
std::string getScript(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
|
||||
std::string url = urlsafe_base64_decode(getUrlArg(argument, "url")), dev_id = getUrlArg(argument, "id");
|
||||
std::string output_content, dummy;
|
||||
|
||||
@@ -2054,6 +2181,8 @@ std::string getScript(RESPONSE_CALLBACK_ARGS)
|
||||
|
||||
std::string getRewriteRemote(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
|
||||
std::string url = urlsafe_base64_decode(getUrlArg(argument, "url")), dev_id = getUrlArg(argument, "id");
|
||||
std::string output_content, dummy;
|
||||
|
||||
@@ -2070,7 +2199,7 @@ std::string getRewriteRemote(RESPONSE_CALLBACK_ARGS)
|
||||
std::string strLine;
|
||||
const std::string pattern = "^(.*? url script-.*? )(.*?)$";
|
||||
string_size lineSize;
|
||||
char delimiter = count(output_content.begin(), output_content.end(), '\n') < 1 ? '\r' : '\n';
|
||||
char delimiter = getLineBreak(output_content);
|
||||
|
||||
ss << output_content;
|
||||
output_content.clear();
|
||||
@@ -2228,10 +2357,11 @@ int simpleGenerator()
|
||||
writeLog(0, "Generating all artifacts...", LOG_LEVEL_INFO);
|
||||
|
||||
string_multimap allItems;
|
||||
std::string dummy_str;
|
||||
std::string proxy = parseProxy(proxy_subscription);
|
||||
string_map headers;
|
||||
int ret_code = 200;
|
||||
Request request;
|
||||
Response response;
|
||||
int &ret_code = response.status_code;
|
||||
string_map &headers = response.headers;
|
||||
for(std::string &x : sections)
|
||||
{
|
||||
arguments.clear();
|
||||
@@ -2250,7 +2380,8 @@ int simpleGenerator()
|
||||
if(ini.ItemExist("profile"))
|
||||
{
|
||||
profile = ini.Get("profile");
|
||||
content = getProfile("name=" + UrlEncode(profile) + "&token=" + access_token + "&expand=true", dummy_str, &ret_code, headers);
|
||||
request.argument = "name=" + UrlEncode(profile) + "&token=" + access_token + "&expand=true";
|
||||
content = getProfile(request, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2278,7 +2409,8 @@ int simpleGenerator()
|
||||
arguments += y.first + "=" + UrlEncode(y.second) + "&";
|
||||
}
|
||||
arguments.erase(arguments.size() - 1);
|
||||
content = subconverter(arguments, dummy_str, &ret_code, headers);
|
||||
request.argument = arguments;
|
||||
content = subconverter(request, response);
|
||||
}
|
||||
if(ret_code != 200)
|
||||
{
|
||||
@@ -2308,6 +2440,9 @@ int simpleGenerator()
|
||||
|
||||
std::string renderTemplate(RESPONSE_CALLBACK_ARGS)
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
int *status_code = &response.status_code;
|
||||
|
||||
std::string path = UrlDecode(getUrlArg(argument, "path"));
|
||||
writeLog(0, "Trying to render template '" + path + "'...", LOG_LEVEL_INFO);
|
||||
|
||||
|
||||
@@ -12,7 +12,9 @@ void refreshRulesets(string_array &ruleset_list, std::vector<ruleset_content> &r
|
||||
void readConf();
|
||||
void generateBase();
|
||||
int simpleGenerator();
|
||||
std::string convertRuleset(const std::string &content, int type);
|
||||
|
||||
std::string getConvertedRuleset(RESPONSE_CALLBACK_ARGS);
|
||||
std::string getScript(RESPONSE_CALLBACK_ARGS);
|
||||
std::string getProfile(RESPONSE_CALLBACK_ARGS);
|
||||
std::string getRuleset(RESPONSE_CALLBACK_ARGS);
|
||||
|
||||
14
src/main.cpp
14
src/main.cpp
@@ -156,6 +156,9 @@ int main(int argc, char *argv[])
|
||||
|
||||
append_response("GET", "/refreshrules", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
int *status_code = &response.status_code;
|
||||
|
||||
if(access_token.size())
|
||||
{
|
||||
std::string token = getUrlArg(argument, "token");
|
||||
@@ -172,6 +175,9 @@ int main(int argc, char *argv[])
|
||||
|
||||
append_response("GET", "/readconf", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
int *status_code = &response.status_code;
|
||||
|
||||
if(access_token.size())
|
||||
{
|
||||
std::string token = getUrlArg(argument, "token");
|
||||
@@ -188,6 +194,10 @@ int main(int argc, char *argv[])
|
||||
|
||||
append_response("POST", "/updateconf", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
std::string postdata = request.postdata;
|
||||
int *status_code = &response.status_code;
|
||||
|
||||
if(access_token.size())
|
||||
{
|
||||
std::string token = getUrlArg(argument, "token");
|
||||
@@ -231,16 +241,20 @@ int main(int argc, char *argv[])
|
||||
|
||||
append_response("GET", "/render", "text/plain;charset=utf-8", renderTemplate);
|
||||
|
||||
append_response("GET", "/convert", "text/plain;charset=utf-8", getConvertedRuleset);
|
||||
|
||||
if(!api_mode)
|
||||
{
|
||||
append_response("GET", "/get", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
std::string url = UrlDecode(getUrlArg(argument, "url"));
|
||||
return webGet(url, "");
|
||||
});
|
||||
|
||||
append_response("GET", "/getlocal", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
|
||||
{
|
||||
std::string &argument = request.argument;
|
||||
return fileGet(UrlDecode(getUrlArg(argument, "path")));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -723,7 +723,7 @@ int regGetMatch(const std::string &src, const std::string &match, size_t group_c
|
||||
|
||||
std::string regTrim(const std::string &src)
|
||||
{
|
||||
return regReplace(src, "^\\s*?(.*?)\\s*$", "$1");
|
||||
return regReplace(src, "^\\s*?([\\s\\S]*)\\s*$", "$1", false);
|
||||
}
|
||||
|
||||
std::string speedCalc(double speed)
|
||||
|
||||
165
src/misc.h
165
src/misc.h
@@ -4,6 +4,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
|
||||
#include <yaml-cpp/yaml.h>
|
||||
|
||||
@@ -34,83 +35,6 @@ static const std::string base64_chars =
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
|
||||
class tribool
|
||||
{
|
||||
private:
|
||||
|
||||
int _M_VALUE = -1;
|
||||
|
||||
public:
|
||||
|
||||
tribool() { clear(); }
|
||||
|
||||
template <typename T> tribool(const T &value) { set(value); }
|
||||
|
||||
tribool(const tribool &value) { *this = value; }
|
||||
|
||||
~tribool() = default;
|
||||
|
||||
tribool& operator=(const tribool &src)
|
||||
{
|
||||
_M_VALUE = src._M_VALUE;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T> tribool& operator=(const T &value)
|
||||
{
|
||||
set(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const { return _M_VALUE == 1; }
|
||||
|
||||
bool is_undef() { return _M_VALUE == -1; }
|
||||
|
||||
template <typename T> void define(const T &value)
|
||||
{
|
||||
if(_M_VALUE == -1)
|
||||
*this = value;
|
||||
}
|
||||
|
||||
template <typename T> tribool read(const T &value)
|
||||
{
|
||||
define(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool get(const bool &def_value = false)
|
||||
{
|
||||
if(_M_VALUE == -1)
|
||||
return def_value;
|
||||
return _M_VALUE;
|
||||
}
|
||||
|
||||
template <typename T> bool set(const T &value)
|
||||
{
|
||||
_M_VALUE = value;
|
||||
return _M_VALUE;
|
||||
}
|
||||
|
||||
bool set(const std::string &str)
|
||||
{
|
||||
switch(hash_(str))
|
||||
{
|
||||
case "true"_hash:
|
||||
_M_VALUE = 1;
|
||||
break;
|
||||
case "false"_hash:
|
||||
_M_VALUE = 0;
|
||||
break;
|
||||
default:
|
||||
_M_VALUE = -1;
|
||||
break;
|
||||
}
|
||||
return _M_VALUE;
|
||||
}
|
||||
|
||||
void clear() { _M_VALUE = -1; }
|
||||
};
|
||||
|
||||
std::string UrlEncode(const std::string& str);
|
||||
std::string UrlDecode(const std::string& str);
|
||||
std::string base64_decode(const std::string &encoded_string, bool accept_urlsafe = false);
|
||||
@@ -203,6 +127,93 @@ template <typename T, typename U> static inline T to_number(const U &value, T de
|
||||
|
||||
int to_int(const std::string &str, int def_value = 0);
|
||||
|
||||
static inline char getLineBreak(const std::string &str)
|
||||
{
|
||||
return count(str.begin(), str.end(), '\n') < 1 ? '\r' : '\n';
|
||||
}
|
||||
|
||||
class tribool
|
||||
{
|
||||
private:
|
||||
|
||||
int _M_VALUE = -1;
|
||||
|
||||
public:
|
||||
|
||||
tribool() { clear(); }
|
||||
|
||||
template <typename T> tribool(const T &value) { set(value); }
|
||||
|
||||
tribool(const tribool &value) { *this = value; }
|
||||
|
||||
~tribool() = default;
|
||||
|
||||
tribool& operator=(const tribool &src)
|
||||
{
|
||||
_M_VALUE = src._M_VALUE;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T> tribool& operator=(const T &value)
|
||||
{
|
||||
set(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator bool() const { return _M_VALUE == 1; }
|
||||
|
||||
bool is_undef() { return _M_VALUE == -1; }
|
||||
|
||||
template <typename T> void define(const T &value)
|
||||
{
|
||||
if(_M_VALUE == -1)
|
||||
*this = value;
|
||||
}
|
||||
|
||||
template <typename T> tribool read(const T &value)
|
||||
{
|
||||
define(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool get(const bool &def_value = false)
|
||||
{
|
||||
if(_M_VALUE == -1)
|
||||
return def_value;
|
||||
return _M_VALUE;
|
||||
}
|
||||
|
||||
template <typename T> bool set(const T &value)
|
||||
{
|
||||
_M_VALUE = value;
|
||||
return _M_VALUE;
|
||||
}
|
||||
|
||||
bool set(const std::string &str)
|
||||
{
|
||||
switch(hash_(str))
|
||||
{
|
||||
case "true"_hash:
|
||||
case "1"_hash:
|
||||
_M_VALUE = 1;
|
||||
break;
|
||||
case "false"_hash:
|
||||
case "0"_hash:
|
||||
_M_VALUE = 0;
|
||||
break;
|
||||
default:
|
||||
if(to_int(str, 0) > 1)
|
||||
_M_VALUE = 1;
|
||||
else
|
||||
_M_VALUE = -1;
|
||||
break;
|
||||
}
|
||||
return _M_VALUE;
|
||||
}
|
||||
|
||||
void clear() { _M_VALUE = -1; }
|
||||
};
|
||||
|
||||
#ifndef HAVE_TO_STRING
|
||||
namespace std
|
||||
{
|
||||
|
||||
@@ -76,7 +76,7 @@ std::shared_future<std::string> fetchFileAsync(const std::string &path, const st
|
||||
std::shared_future<std::string> retVal;
|
||||
if(fileExist(path))
|
||||
retVal = std::async(std::launch::async, [path](){return fileGet(path, true);});
|
||||
else if(startsWith(path, "https://") || startsWith(path, "http://") || startsWith(path, "data:"))
|
||||
else if(isLink(path))
|
||||
retVal = std::async(std::launch::async, [path, proxy, cache_ttl](){return webGet(path, proxy, cache_ttl);});
|
||||
else
|
||||
return std::async(std::launch::async, [](){return std::string();});
|
||||
|
||||
@@ -72,7 +72,7 @@ int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std
|
||||
linkType = SPEEDTEST_MESSAGE_FOUNDSOCKS;
|
||||
else if(startsWith(link, "https://t.me/http") || startsWith(link, "tg://http"))
|
||||
linkType = SPEEDTEST_MESSAGE_FOUNDHTTP;
|
||||
else if(startsWith(link, "http://") || startsWith(link, "https://") || startsWith(link, "data:") || startsWith(link, "surge:///install-config"))
|
||||
else if(isLink(link) || startsWith(link, "surge:///install-config"))
|
||||
linkType = SPEEDTEST_MESSAGE_FOUNDSUB;
|
||||
else if(startsWith(link, "Netch://"))
|
||||
linkType = SPEEDTEST_MESSAGE_FOUNDNETCH;
|
||||
|
||||
@@ -1974,7 +1974,7 @@ void explode(std::string link, bool sslibev, bool ssrlibev, const std::string &c
|
||||
explodeNetch(link, sslibev, ssrlibev, custom_port, node);
|
||||
else if(strFind(link, "trojan://"))
|
||||
explodeTrojan(link, custom_port, node);
|
||||
else if(strFind(link, "http://") || strFind(link, "https://"))
|
||||
else if(isLink(link))
|
||||
explodeHTTPSub(link, custom_port, node);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "templates.h"
|
||||
#include "script_duktape.h"
|
||||
#include "yamlcpp_extra.h"
|
||||
#include "interfaces.h"
|
||||
|
||||
extern bool api_mode;
|
||||
extern string_array ss_ciphers, ssr_ciphers;
|
||||
@@ -559,7 +560,7 @@ void processRemark(std::string &oldremark, std::string &newremark, string_array
|
||||
void rulesetToClash(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset_content_array, bool overwrite_original_rules, bool new_field_name)
|
||||
{
|
||||
string_array allRules, vArray;
|
||||
std::string rule_group, retrived_rules, strLine;
|
||||
std::string rule_group, retrieved_rules, strLine;
|
||||
std::stringstream strStrm;
|
||||
const std::string field_name = new_field_name ? "rules" : "Rule";
|
||||
YAML::Node Rules;
|
||||
@@ -573,15 +574,15 @@ void rulesetToClash(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset
|
||||
if(max_allowed_rules && total_rules > max_allowed_rules)
|
||||
break;
|
||||
rule_group = x.rule_group;
|
||||
retrived_rules = x.rule_content.get();
|
||||
if(retrived_rules.empty())
|
||||
retrieved_rules = x.rule_content.get();
|
||||
if(retrieved_rules.empty())
|
||||
{
|
||||
writeLog(0, "Failed to fetch ruleset or ruleset is empty: '" + x.rule_path + "'!", LOG_LEVEL_WARNING);
|
||||
continue;
|
||||
}
|
||||
if(retrived_rules.find("[]") == 0)
|
||||
if(retrieved_rules.find("[]") == 0)
|
||||
{
|
||||
strLine = retrived_rules.substr(2);
|
||||
strLine = retrieved_rules.substr(2);
|
||||
if(strLine.find("FINAL") == 0)
|
||||
strLine.replace(0, 5, "MATCH");
|
||||
strLine += "," + rule_group;
|
||||
@@ -591,10 +592,11 @@ void rulesetToClash(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset
|
||||
total_rules++;
|
||||
continue;
|
||||
}
|
||||
char delimiter = count(retrived_rules.begin(), retrived_rules.end(), '\n') < 1 ? '\r' : '\n';
|
||||
convertRuleset(retrieved_rules, x.rule_type);
|
||||
char delimiter = getLineBreak(retrieved_rules);
|
||||
|
||||
strStrm.clear();
|
||||
strStrm<<retrived_rules;
|
||||
strStrm<<retrieved_rules;
|
||||
std::string::size_type lineSize;
|
||||
while(getline(strStrm, strLine, delimiter))
|
||||
{
|
||||
@@ -646,7 +648,7 @@ void rulesetToClash(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset
|
||||
std::string rulesetToClashStr(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset_content_array, bool overwrite_original_rules, bool new_field_name)
|
||||
{
|
||||
string_array allRules, vArray;
|
||||
std::string rule_group, retrived_rules, strLine;
|
||||
std::string rule_group, retrieved_rules, strLine;
|
||||
std::stringstream strStrm;
|
||||
const std::string field_name = new_field_name ? "rules" : "Rule";
|
||||
std::string output_content = "\n" + field_name + ":\n";
|
||||
@@ -664,15 +666,15 @@ std::string rulesetToClashStr(YAML::Node &base_rule, std::vector<ruleset_content
|
||||
if(max_allowed_rules && total_rules > max_allowed_rules)
|
||||
break;
|
||||
rule_group = x.rule_group;
|
||||
retrived_rules = x.rule_content.get();
|
||||
if(retrived_rules.empty())
|
||||
retrieved_rules = x.rule_content.get();
|
||||
if(retrieved_rules.empty())
|
||||
{
|
||||
writeLog(0, "Failed to fetch ruleset or ruleset is empty: '" + x.rule_path + "'!", LOG_LEVEL_WARNING);
|
||||
continue;
|
||||
}
|
||||
if(retrived_rules.find("[]") == 0)
|
||||
if(retrieved_rules.find("[]") == 0)
|
||||
{
|
||||
strLine = retrived_rules.substr(2);
|
||||
strLine = retrieved_rules.substr(2);
|
||||
if(strLine.find("FINAL") == 0)
|
||||
strLine.replace(0, 5, "MATCH");
|
||||
strLine += "," + rule_group;
|
||||
@@ -682,10 +684,11 @@ std::string rulesetToClashStr(YAML::Node &base_rule, std::vector<ruleset_content
|
||||
total_rules++;
|
||||
continue;
|
||||
}
|
||||
char delimiter = count(retrived_rules.begin(), retrived_rules.end(), '\n') < 1 ? '\r' : '\n';
|
||||
convertRuleset(retrieved_rules, x.rule_type);
|
||||
char delimiter = getLineBreak(retrieved_rules);
|
||||
|
||||
strStrm.clear();
|
||||
strStrm<<retrived_rules;
|
||||
strStrm<<retrieved_rules;
|
||||
std::string::size_type lineSize;
|
||||
while(getline(strStrm, strLine, delimiter))
|
||||
{
|
||||
@@ -714,7 +717,7 @@ std::string rulesetToClashStr(YAML::Node &base_rule, std::vector<ruleset_content
|
||||
void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_content_array, int surge_ver, bool overwrite_original_rules, std::string remote_path_prefix)
|
||||
{
|
||||
string_array allRules;
|
||||
std::string rule_group, rule_path, retrieved_rules, strLine;
|
||||
std::string rule_group, rule_path, rule_path_typed, retrieved_rules, strLine;
|
||||
std::stringstream strStrm;
|
||||
size_t total_rules = 0;
|
||||
|
||||
@@ -755,6 +758,7 @@ void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_
|
||||
break;
|
||||
rule_group = x.rule_group;
|
||||
rule_path = x.rule_path;
|
||||
rule_path_typed = x.rule_path_typed;
|
||||
if(rule_path.empty())
|
||||
{
|
||||
strLine = x.rule_content.get().substr(2);
|
||||
@@ -780,39 +784,54 @@ void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_
|
||||
}
|
||||
else
|
||||
{
|
||||
if(surge_ver == -1 && x.rule_type == RULESET_QUANX && isLink(rule_path))
|
||||
{
|
||||
strLine = rule_path + ", tag=" + rule_group + ", force-policy=" + rule_group + ", enabled=true";
|
||||
base_rule.Set("filter_remote", "{NONAME}", strLine);
|
||||
continue;
|
||||
}
|
||||
if(fileExist(rule_path))
|
||||
{
|
||||
if(surge_ver > 2 && remote_path_prefix.size())
|
||||
{
|
||||
strLine = "RULE-SET," + remote_path_prefix + "/getruleset?type=1&url=" + urlsafe_base64_encode(rule_path) + "," + rule_group;
|
||||
strLine = "RULE-SET," + remote_path_prefix + "/getruleset?type=1&url=" + urlsafe_base64_encode(rule_path_typed) + "," + rule_group;
|
||||
allRules.emplace_back(strLine);
|
||||
continue;
|
||||
}
|
||||
else if(surge_ver == -1 && remote_path_prefix.size())
|
||||
{
|
||||
strLine = remote_path_prefix + "/getruleset?type=2&url=" + urlsafe_base64_encode(rule_path) + "&group=" + urlsafe_base64_encode(rule_group);
|
||||
strLine = remote_path_prefix + "/getruleset?type=2&url=" + urlsafe_base64_encode(rule_path_typed) + "&group=" + urlsafe_base64_encode(rule_group);
|
||||
strLine += ", tag=" + rule_group + ", enabled=true";
|
||||
base_rule.Set("filter_remote", "{NONAME}", strLine);
|
||||
continue;
|
||||
}
|
||||
else if(surge_ver == -4 && remote_path_prefix.size())
|
||||
{
|
||||
strLine = remote_path_prefix + "/getruleset?type=1&url=" + urlsafe_base64_encode(rule_path) + "," + rule_group;
|
||||
strLine = remote_path_prefix + "/getruleset?type=1&url=" + urlsafe_base64_encode(rule_path_typed) + "," + rule_group;
|
||||
base_rule.Set("Remote Rule", "{NONAME}", strLine);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if(startsWith(rule_path, "https://") || startsWith(rule_path, "http://") || startsWith(rule_path, "data:"))
|
||||
else if(isLink(rule_path))
|
||||
{
|
||||
if(surge_ver > 2)
|
||||
{
|
||||
strLine = "RULE-SET," + rule_path + "," + rule_group;
|
||||
if(x.rule_type != RULESET_SURGE)
|
||||
{
|
||||
if(remote_path_prefix.size())
|
||||
strLine = "RULE-SET," + remote_path_prefix + "/getruleset?type=1&url=" + urlsafe_base64_encode(rule_path_typed) + "," + rule_group;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
else
|
||||
strLine = "RULE-SET," + rule_path + "," + rule_group;
|
||||
|
||||
allRules.emplace_back(strLine);
|
||||
continue;
|
||||
}
|
||||
else if(surge_ver == -1 && remote_path_prefix.size())
|
||||
{
|
||||
strLine = remote_path_prefix + "/getruleset?type=2&url=" + urlsafe_base64_encode(rule_path) + "&group=" + urlsafe_base64_encode(rule_group);
|
||||
strLine = remote_path_prefix + "/getruleset?type=2&url=" + urlsafe_base64_encode(rule_path_typed) + "&group=" + urlsafe_base64_encode(rule_group);
|
||||
strLine += ", tag=" + rule_group + ", enabled=true";
|
||||
base_rule.Set("filter_remote", "{NONAME}", strLine);
|
||||
continue;
|
||||
@@ -833,7 +852,8 @@ void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_
|
||||
continue;
|
||||
}
|
||||
|
||||
char delimiter = count(retrieved_rules.begin(), retrieved_rules.end(), '\n') < 1 ? '\r' : '\n';
|
||||
convertRuleset(retrieved_rules, x.rule_type);
|
||||
char delimiter = getLineBreak(retrieved_rules);
|
||||
|
||||
strStrm.clear();
|
||||
strStrm<<retrieved_rules;
|
||||
@@ -2427,7 +2447,7 @@ void netchToQuanX(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<rule
|
||||
if(regMatch(content, pattern))
|
||||
{
|
||||
url = regReplace(content, pattern, "$2");
|
||||
if(startsWith(url, "https://") || startsWith(url, "http://"))
|
||||
if(isLink(url))
|
||||
{
|
||||
url = ext.managed_config_prefix + "/qx-script?id=" + ext.quanx_dev_id + "&url=" + urlsafe_base64_encode(url);
|
||||
content = regReplace(content, pattern, "$1") + url;
|
||||
@@ -2451,7 +2471,7 @@ void netchToQuanX(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<rule
|
||||
else
|
||||
content = x.second;
|
||||
|
||||
if(startsWith(content, "https://") || startsWith(content, "http://"))
|
||||
if(isLink(content))
|
||||
{
|
||||
pos = content.find(",");
|
||||
url = ext.managed_config_prefix + "/qx-rewrite?id=" + ext.quanx_dev_id + "&url=" + urlsafe_base64_encode(content.substr(0, pos));
|
||||
|
||||
@@ -9,10 +9,21 @@
|
||||
#include "ini_reader.h"
|
||||
#include "nodeinfo.h"
|
||||
|
||||
enum ruleset_type
|
||||
{
|
||||
RULESET_SURGE,
|
||||
RULESET_QUANX,
|
||||
RULESET_CLASH_DOMAIN,
|
||||
RULESET_CLASH_IPCIDR,
|
||||
RULESET_CLASH_CLASSICAL
|
||||
};
|
||||
|
||||
struct ruleset_content
|
||||
{
|
||||
std::string rule_group;
|
||||
std::string rule_path;
|
||||
std::string rule_path_typed;
|
||||
int rule_type = RULESET_SURGE;
|
||||
std::shared_future<std::string> rule_content;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "templates.h"
|
||||
#include "logger.h"
|
||||
#include "misc.h"
|
||||
#include "webget.h"
|
||||
|
||||
extern std::string managed_config_prefix;
|
||||
|
||||
@@ -220,12 +221,13 @@ int renderClashScript(YAML::Node &base_rule, std::vector<ruleset_content> &rules
|
||||
{
|
||||
nlohmann::json data;
|
||||
std::string match_group, geoips, retrieved_rules;
|
||||
std::string strLine, rule_group, rule_path, rule_name;
|
||||
std::string strLine, rule_group, rule_path, rule_path_typed, rule_name;
|
||||
std::stringstream strStrm;
|
||||
string_array vArray, groups;
|
||||
string_map keywords, urls, names;
|
||||
std::map<std::string, bool> has_domain, has_ipcidr;
|
||||
string_array rules;
|
||||
int index = 0;
|
||||
|
||||
if(!overwrite_original_rules && base_rule["rules"].IsDefined())
|
||||
rules = safe_as<string_array>(base_rule["rules"]);
|
||||
@@ -234,6 +236,7 @@ int renderClashScript(YAML::Node &base_rule, std::vector<ruleset_content> &rules
|
||||
{
|
||||
rule_group = x.rule_group;
|
||||
rule_path = x.rule_path;
|
||||
rule_path_typed = x.rule_path_typed;
|
||||
if(rule_path.empty())
|
||||
{
|
||||
strLine = x.rule_content.get().substr(2);
|
||||
@@ -260,13 +263,38 @@ int renderClashScript(YAML::Node &base_rule, std::vector<ruleset_content> &rules
|
||||
}
|
||||
else
|
||||
{
|
||||
if(x.rule_type == RULESET_CLASH_IPCIDR || x.rule_type == RULESET_CLASH_DOMAIN || x.rule_type == RULESET_CLASH_CLASSICAL)
|
||||
{
|
||||
rule_name = std::to_string(hash_(rule_group + rule_path));
|
||||
names[rule_name] = rule_group;
|
||||
urls[rule_name] = "*" + rule_path;
|
||||
switch(x.rule_type)
|
||||
{
|
||||
case RULESET_CLASH_IPCIDR:
|
||||
has_ipcidr[rule_name] = true;
|
||||
if(!script)
|
||||
rules.emplace_back("RULE-SET," + rule_group + "_" + rule_name + "_ipcidr," + rule_group);
|
||||
break;
|
||||
case RULESET_CLASH_DOMAIN:
|
||||
has_domain[rule_name] = true;
|
||||
if(!script)
|
||||
rules.emplace_back("RULE-SET," + rule_group + "_" + rule_name + "_domain," + rule_group);
|
||||
break;
|
||||
case RULESET_CLASH_CLASSICAL:
|
||||
if(!script)
|
||||
rules.emplace_back("RULE-SET," + rule_group + "_" + rule_name + "_classical," + rule_group);
|
||||
break;
|
||||
}
|
||||
groups.emplace_back(rule_name);
|
||||
continue;
|
||||
}
|
||||
if(remote_path_prefix.size())
|
||||
{
|
||||
if(fileExist(rule_path) || startsWith(rule_path, "https://") || startsWith(rule_path, "http://") || startsWith(rule_path, "data:"))
|
||||
if(isLink(rule_path) || startsWith(rule_path, "data:"))
|
||||
{
|
||||
rule_name = std::to_string(hash_(rule_path));
|
||||
rule_name = std::to_string(hash_(rule_group + rule_path));
|
||||
names[rule_name] = rule_group;
|
||||
urls[rule_name] = rule_path;
|
||||
urls[rule_name] = rule_path_typed;
|
||||
if(clash_classical_ruleset)
|
||||
{
|
||||
groups.emplace_back(rule_name);
|
||||
@@ -286,7 +314,7 @@ int renderClashScript(YAML::Node &base_rule, std::vector<ruleset_content> &rules
|
||||
continue;
|
||||
}
|
||||
|
||||
char delimiter = std::count(retrieved_rules.begin(), retrieved_rules.end(), '\n') < 1 ? '\r' : '\n';
|
||||
char delimiter = getLineBreak(retrieved_rules);
|
||||
|
||||
strStrm.clear();
|
||||
strStrm<<retrieved_rules;
|
||||
@@ -335,7 +363,6 @@ int renderClashScript(YAML::Node &base_rule, std::vector<ruleset_content> &rules
|
||||
groups.emplace_back(rule_name);
|
||||
}
|
||||
}
|
||||
int index = 0;
|
||||
for(std::string &x : groups)
|
||||
{
|
||||
std::string json_path = "rules." + std::to_string(index) + ".";
|
||||
@@ -345,7 +372,10 @@ int renderClashScript(YAML::Node &base_rule, std::vector<ruleset_content> &rules
|
||||
{
|
||||
base_rule["rule-providers"][name + "_" + x + "_classical"]["type"] = "http";
|
||||
base_rule["rule-providers"][name + "_" + x + "_classical"]["behavior"] = "classical";
|
||||
base_rule["rule-providers"][name + "_" + x + "_classical"]["url"] = remote_path_prefix + "/getruleset?type=6&url=" + urlsafe_base64_encode(url);
|
||||
if(url[0] == '*')
|
||||
base_rule["rule-providers"][name + "_" + x + "_classical"]["url"] = url.substr(1);
|
||||
else
|
||||
base_rule["rule-providers"][name + "_" + x + "_classical"]["url"] = remote_path_prefix + "/getruleset?type=6&url=" + urlsafe_base64_encode(url);
|
||||
base_rule["rule-providers"][name + "_" + x + "_classical"]["path"] = "./providers/rule-provider_" + x + "_classical.yaml";
|
||||
}
|
||||
else
|
||||
@@ -354,14 +384,20 @@ int renderClashScript(YAML::Node &base_rule, std::vector<ruleset_content> &rules
|
||||
{
|
||||
base_rule["rule-providers"][name + "_" + x + "_domain"]["type"] = "http";
|
||||
base_rule["rule-providers"][name + "_" + x + "_domain"]["behavior"] = "domain";
|
||||
base_rule["rule-providers"][name + "_" + x + "_domain"]["url"] = remote_path_prefix + "/getruleset?type=3&url=" + urlsafe_base64_encode(url);
|
||||
if(url[0] == '*')
|
||||
base_rule["rule-providers"][name + "_" + x + "_domain"]["url"] = url.substr(1);
|
||||
else
|
||||
base_rule["rule-providers"][name + "_" + x + "_domain"]["url"] = remote_path_prefix + "/getruleset?type=3&url=" + urlsafe_base64_encode(url);
|
||||
base_rule["rule-providers"][name + "_" + x + "_domain"]["path"] = "./providers/rule-provider_" + x + "_domain.yaml";
|
||||
}
|
||||
if(group_has_ipcidr)
|
||||
{
|
||||
base_rule["rule-providers"][name + "_" + x + "_ipcidr"]["type"] = "http";
|
||||
base_rule["rule-providers"][name + "_" + x + "_ipcidr"]["behavior"] = "ipcidr";
|
||||
base_rule["rule-providers"][name + "_" + x + "_ipcidr"]["url"] = remote_path_prefix + "/getruleset?type=4&url=" + urlsafe_base64_encode(url);
|
||||
if(url[0] == '*')
|
||||
base_rule["rule-providers"][name + "_" + x + "_ipcidr"]["url"] = url.substr(1);
|
||||
else
|
||||
base_rule["rule-providers"][name + "_" + x + "_ipcidr"]["url"] = remote_path_prefix + "/getruleset?type=4&url=" + urlsafe_base64_encode(url);
|
||||
base_rule["rule-providers"][name + "_" + x + "_ipcidr"]["path"] = "./providers/rule-provider_" + x + "_ipcidr.yaml";
|
||||
}
|
||||
}
|
||||
@@ -391,8 +427,9 @@ int renderClashScript(YAML::Node &base_rule, std::vector<ruleset_content> &rules
|
||||
std::string output_content = env.render(tmpl, data);
|
||||
base_rule["script"]["code"] = output_content;
|
||||
}
|
||||
catch (std::exception&)
|
||||
catch (std::exception &e)
|
||||
{
|
||||
writeLog(0, "Error when rendering: " + std::string(e.what()), LOG_TYPE_ERROR);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,4 +16,9 @@ std::string buildSocks5ProxyString(const std::string &addr, int port, const std:
|
||||
std::string httpGet(const std::string &host, const std::string &addr, const std::string &uri);
|
||||
std::string httpsGet(const std::string &host, const std::string &addr, const std::string &uri);
|
||||
|
||||
static inline bool isLink(const std::string &url)
|
||||
{
|
||||
return startsWith(url, "https://") || startsWith(url, "http://") || startsWith(url, "data:");
|
||||
}
|
||||
|
||||
#endif // WEBGET_H_INCLUDED
|
||||
|
||||
@@ -1,9 +1,28 @@
|
||||
#ifndef WEBSERVER_H_INCLUDED
|
||||
#define WEBSERVER_H_INCLUDED
|
||||
|
||||
typedef std::string (*response_callback)(std::string, std::string, int*, std::map<std::string, std::string>&); //process arguments and POST data and return served-content
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#define RESPONSE_CALLBACK_ARGS std::string argument, std::string postdata, int *status_code, std::map<std::string, std::string> &extra_headers
|
||||
struct Request
|
||||
{
|
||||
std::string method;
|
||||
std::string url;
|
||||
std::string argument;
|
||||
std::map<std::string, std::string> headers;
|
||||
std::string postdata;
|
||||
};
|
||||
|
||||
struct Response
|
||||
{
|
||||
int status_code = 200;
|
||||
std::string content_type;
|
||||
std::map<std::string, std::string> headers;
|
||||
};
|
||||
|
||||
typedef std::string (*response_callback)(Request&, Response&); //process arguments and POST data and return served-content
|
||||
|
||||
#define RESPONSE_CALLBACK_ARGS Request &request, Response &response
|
||||
|
||||
struct listener_args
|
||||
{
|
||||
|
||||
@@ -40,47 +40,45 @@ static inline void buffer_cleanup(struct evbuffer *eb)
|
||||
#endif // MALLOC_TRIM
|
||||
}
|
||||
|
||||
static inline int process_request(const char *method_str, std::string uri, std::string &postdata, std::string &content_type, std::string &return_data, int *status_code, std::map<std::string, std::string> &extra_headers)
|
||||
//static inline int process_request(const char *method_str, std::string uri, std::string &postdata, std::string &content_type, std::string &return_data, int *status_code, std::map<std::string, std::string> &extra_headers)
|
||||
static inline int process_request(Request &request, Response &response, std::string &return_data)
|
||||
{
|
||||
std::string path, arguments;
|
||||
//std::cerr << "handle_cmd: " << method_str << std::endl << "handle_uri: " << uri << std::endl;
|
||||
writeLog(0, "handle_cmd: " + std::string(method_str) + " handle_uri: " + uri, LOG_LEVEL_VERBOSE);
|
||||
writeLog(0, "handle_cmd: " + request.method + " handle_uri: " + request.url, LOG_LEVEL_VERBOSE);
|
||||
|
||||
if(strFind(uri, "?"))
|
||||
string_size pos = request.url.find("?");
|
||||
if(pos != request.url.npos)
|
||||
{
|
||||
path = uri.substr(0, uri.find("?"));
|
||||
arguments = uri.substr(uri.find("?") + 1);
|
||||
request.argument = request.url.substr(pos + 1);
|
||||
request.url.erase(pos);
|
||||
}
|
||||
else
|
||||
path = uri;
|
||||
|
||||
for(responseRoute &x : responses)
|
||||
{
|
||||
if(strcmp(method_str, "OPTIONS") == 0 && postdata.find(x.method) != 0 && x.path == path)
|
||||
if(request.method == "OPTIONS" && request.postdata.find(x.method) != 0 && x.path == request.url)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if(x.method.compare(method_str) == 0 && x.path == path)
|
||||
else if(x.method == request.method && x.path == request.url)
|
||||
{
|
||||
response_callback &rc = x.rc;
|
||||
return_data = rc(arguments, postdata, status_code, extra_headers);
|
||||
content_type = x.content_type;
|
||||
return_data = rc(request, response);
|
||||
response.content_type = x.content_type;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto iter = redirect_map.find(path);
|
||||
auto iter = redirect_map.find(request.url);
|
||||
if(iter != redirect_map.end())
|
||||
{
|
||||
return_data = iter->second;
|
||||
if(arguments.size())
|
||||
if(request.argument.size())
|
||||
{
|
||||
if(return_data.find("?") != return_data.npos)
|
||||
return_data += "&" + arguments;
|
||||
return_data += "&" + request.argument;
|
||||
else
|
||||
return_data += "?" + arguments;
|
||||
return_data += "?" + request.argument;
|
||||
}
|
||||
content_type = "REDIRECT";
|
||||
response.content_type = "REDIRECT";
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -117,16 +115,26 @@ void OnReq(evhttp_request *req, void *args)
|
||||
postdata.assign(req_ac_method);
|
||||
}
|
||||
|
||||
int status_code = 200;
|
||||
std::map<std::string, std::string> extra_headers;
|
||||
retVal = process_request(req_method, uri, postdata, content_type, return_data, &status_code, extra_headers);
|
||||
Request request;
|
||||
Response response;
|
||||
request.method = req_method;
|
||||
request.url = uri;
|
||||
|
||||
struct evkeyval* kv = req->input_headers->tqh_first;
|
||||
while (kv)
|
||||
{
|
||||
request.headers.emplace(kv->key, kv->value);
|
||||
kv = kv->next.tqe_next;
|
||||
}
|
||||
retVal = process_request(request, response, return_data);
|
||||
content_type = response.content_type;
|
||||
|
||||
auto *OutBuf = evhttp_request_get_output_buffer(req);
|
||||
//struct evbuffer *OutBuf = evbuffer_new();
|
||||
if (!OutBuf)
|
||||
return;
|
||||
|
||||
for(auto &x : extra_headers)
|
||||
for(auto &x : response.headers)
|
||||
evhttp_add_header(req->output_headers, x.first.data(), x.second.data());
|
||||
|
||||
switch(retVal)
|
||||
@@ -134,7 +142,7 @@ void OnReq(evhttp_request *req, void *args)
|
||||
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, status_code, "", NULL);
|
||||
evhttp_send_reply(req, response.status_code, "", NULL);
|
||||
break;
|
||||
case 0: //found normal
|
||||
if(content_type.size())
|
||||
@@ -152,7 +160,7 @@ void OnReq(evhttp_request *req, void *args)
|
||||
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, status_code, "", OutBuf);
|
||||
evhttp_send_reply(req, response.status_code, "", OutBuf);
|
||||
break;
|
||||
case -1: //not found
|
||||
return_data = "File not found.";
|
||||
|
||||
Reference in New Issue
Block a user