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:
Tindy X
2020-06-19 23:57:10 +08:00
parent ee6de60223
commit 605e273d69
18 changed files with 475 additions and 201 deletions

View File

@@ -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

View File

@@ -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: [".*"]}

View File

@@ -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]

View File

@@ -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))

View File

@@ -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);

View File

@@ -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);

View File

@@ -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")));
});
}

View File

@@ -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)

View File

@@ -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
{

View File

@@ -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();});

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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));

View File

@@ -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;
};

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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
{

View File

@@ -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.";