Enhancements

Fix ruleset types other than Surge Ruleset not correctly expanded to rules.
Fix trying to limit upload file size.
Fix when no GEOIP rule is added, the Clash Script will not be able to generate.
Add "mixed" target to generate subscriptions with all types of standard subscriptions.
Add passing X-User-Agent and X-Client-IP header to subscription downloader.
Optimize codes.
This commit is contained in:
Tindy X committed 2020-06-22 21:40:18 +08:00
1 parent 06eb71d1c4
commit 34b19507db
10 files changed
+191 -212

No files matched your search

+28 -37
View File
@@ -330,7 +330,7 @@ int importItems(string_array &target, bool scope_limit = true)
{
string_array result;
std::stringstream ss;
std::string path, content, strLine, dummy;
std::string path, content, strLine;
unsigned int itemCount = 0;
for(std::string &x : target)
{
@@ -347,7 +347,7 @@ int importItems(string_array &target, bool scope_limit = true)
if(fileExist(path))
content = fileGet(path, scope_limit);
else if(isLink(path))
content = webGet(path, proxy, dummy, cache_config);
content = webGet(path, proxy, cache_config);
else
writeLog(0, "File not found or not a valid URL: " + path, LOG_LEVEL_ERROR);
if(!content.size())
@@ -1286,7 +1286,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
std::string target = getUrlArg(argument, "target");
switch(hash_(target))
{
case "clash"_hash: case "clashr"_hash: case "surge"_hash: case "quan"_hash: case "quanx"_hash: case "loon"_hash: case "surfboard"_hash: case "mellow"_hash: case "ss"_hash: case "ssd"_hash: case "ssr"_hash: case "sssub"_hash: case "v2ray"_hash: case "trojan"_hash:
case "clash"_hash: case "clashr"_hash: case "surge"_hash: case "quan"_hash: case "quanx"_hash: case "loon"_hash: case "surfboard"_hash: case "mellow"_hash: case "ss"_hash: case "ssd"_hash: case "ssr"_hash: case "sssub"_hash: case "v2ray"_hash: case "trojan"_hash: case "mixed"_hash:
break;
default:
*status_code = 400;
@@ -1526,7 +1526,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
{
x = regTrim(x);
writeLog(0, "Fetching node data from url '" + x + "'.", LOG_LEVEL_INFO);
if(addNodes(x, insert_nodes, groupID, proxy, exclude_remarks, include_remarks, stream_temp, time_temp, subInfo, authorized) == -1)
if(addNodes(x, insert_nodes, groupID, proxy, exclude_remarks, include_remarks, stream_temp, time_temp, subInfo, authorized, request.headers) == -1)
{
*status_code = 400;
return std::string("The following link doesn't contain any valid node info: " + x);
@@ -1541,7 +1541,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
x = regTrim(x);
//std::cerr<<"Fetching node data from url '"<<x<<"'."<<std::endl;
writeLog(0, "Fetching node data from url '" + x + "'.", LOG_LEVEL_INFO);
if(addNodes(x, nodes, groupID, proxy, exclude_remarks, include_remarks, stream_temp, time_temp, subInfo, authorized) == -1)
if(addNodes(x, nodes, groupID, proxy, exclude_remarks, include_remarks, stream_temp, time_temp, subInfo, authorized, request.headers) == -1)
{
*status_code = 400;
return std::string("The following link doesn't contain any valid node info: " + x);
@@ -1611,7 +1611,6 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
switch(hash_(target))
{
case "clash"_hash: case "clashr"_hash:
//std::cerr<<"Clash"<<((target == "clashr") ? "R" : "")<<std::endl;
writeLog(0, target == "clashr" ? "Generate target: ClashR" : "Generate target: Clash", LOG_LEVEL_INFO);
tpl_args.local_vars["clash.new_field_name"] = ext.clash_new_field_name ? "true" : "false";
if(ext.nodelist)
@@ -1642,7 +1641,6 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
break;
case "surge"_hash:
surge_ver = version.size() ? to_int(version, 3) : 3;
//std::cerr<<"Surge "<<surge_ver<<std::endl;
writeLog(0, "Generate target: Surge " + std::to_string(surge_ver), LOG_LEVEL_INFO);
if(ext.nodelist)
@@ -1671,7 +1669,6 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
}
break;
case "surfboard"_hash:
//std::cerr<<"Surfboard"<<std::endl;
writeLog(0, "Generate target: Surfboard", LOG_LEVEL_INFO);
if(render_template(fetchFile(ext_surfboard_base, proxy, cache_config), tpl_args, base_content, template_path) != 0)
@@ -1689,7 +1686,6 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
+ " strict=" + std::string(strict ? "true" : "false") + "\n\n" + output_content;
break;
case "mellow"_hash:
//std::cerr<<"Mellow"<<std::endl;
writeLog(0, "Generate target: Mellow", LOG_LEVEL_INFO);
// mellow base generator removed for now
//if(ruleset_updated || update_ruleset_on_request || ext_mellow_base != mellow_rule_base || !enable_base_gen)
@@ -1715,15 +1711,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
if(upload)
uploadGist("mellow", upload_path, output_content, true);
break;
case "ss"_hash:
//std::cerr<<"SS"<<std::endl;
writeLog(0, "Generate target: SS", LOG_LEVEL_INFO);
output_content = netchToSS(nodes, ext);
if(upload)
uploadGist("ss", upload_path, output_content, false);
break;
case "sssub"_hash:
//std::cerr<<"SS Subscription"<<std::endl;
writeLog(0, "Generate target: SS Subscription", LOG_LEVEL_INFO);
if(render_template(fetchFile(ext_sssub_base, proxy, cache_config), tpl_args, base_content, template_path) != 0)
@@ -1736,22 +1724,37 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
if(upload)
uploadGist("sssub", upload_path, output_content, false);
break;
case "ss"_hash:
writeLog(0, "Generate target: SS", LOG_LEVEL_INFO);
output_content = netchToSingle(nodes, 1, ext);
if(upload)
uploadGist("ss", upload_path, output_content, false);
break;
case "ssr"_hash:
//std::cerr<<"SSR"<<std::endl;
writeLog(0, "Generate target: SSR", LOG_LEVEL_INFO);
output_content = netchToSSR(nodes, ext);
output_content = netchToSingle(nodes, 2, ext);
if(upload)
uploadGist("ssr", upload_path, output_content, false);
break;
case "v2ray"_hash:
//std::cerr<<"v2rayN"<<std::endl;
writeLog(0, "Generate target: v2rayN", LOG_LEVEL_INFO);
output_content = netchToVMess(nodes, ext);
output_content = netchToSingle(nodes, 4, ext);
if(upload)
uploadGist("v2ray", upload_path, output_content, false);
break;
case "trojan"_hash:
writeLog(0, "Generate target: Trojan", LOG_LEVEL_INFO);
output_content = netchToSingle(nodes, 8, ext);
if(upload)
uploadGist("trojan", upload_path, output_content, false);
break;
case "mixed"_hash:
writeLog(0, "Generate target: Standard Subscription", LOG_LEVEL_INFO);
output_content = netchToSingle(nodes, 15, ext);
if(upload)
uploadGist("sub", upload_path, output_content, false);
break;
case "quan"_hash:
//std::cerr<<"Quantumult"<<std::endl;
writeLog(0, "Generate target: Quantumult", LOG_LEVEL_INFO);
if(!ext.nodelist)
{
@@ -1769,7 +1772,6 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
uploadGist("quan", upload_path, output_content, false);
break;
case "quanx"_hash:
//std::cerr<<"Quantumult X"<<std::endl;
writeLog(0, "Generate target: Quantumult X", LOG_LEVEL_INFO);
if(!ext.nodelist)
{
@@ -1781,14 +1783,12 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
//base_content = fetchFile(ext_quanx_base, proxy, cache_config);
}
output_content = netchToQuanX(nodes, base_content, rca, extra_group, ext);
if(upload)
uploadGist("quanx", upload_path, output_content, false);
break;
case "loon"_hash:
//std::cerr<<"Loon"<<std::endl;
writeLog(0, "Generate target: Loon", LOG_LEVEL_INFO);
if(!ext.nodelist)
{
@@ -1806,21 +1806,12 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
uploadGist("loon", upload_path, output_content, false);
break;
case "ssd"_hash:
//std::cerr<<"SSD"<<std::endl;
writeLog(0, "Generate target: SSD", LOG_LEVEL_INFO);
output_content = netchToSSD(nodes, group, subInfo, ext);
if(upload)
uploadGist("ssd", upload_path, output_content, false);
break;
case "trojan"_hash:
//std::cerr<<"Trojan"<<std::endl;
writeLog(0, "Generate target: Trojan", LOG_LEVEL_INFO);
output_content = netchToTrojan(nodes, ext);
if(upload)
uploadGist("trojan", upload_path, output_content, false);
break;
default:
//std::cerr<<"Unspecified"<<std::endl;
writeLog(0, "Generate target: Unspecified", LOG_LEVEL_INFO);
*status_code = 500;
return "Unrecognized target";
@@ -1859,7 +1850,7 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
INIReader ini;
string_array dummy_str_array;
std::vector<nodeInfo> nodes;
std::string base_content, url = argument.size() <= 5 ? "" : argument.substr(5), dummy;
std::string base_content, url = argument.size() <= 5 ? "" : argument.substr(5);
const std::string proxygroup_name = clash_use_new_field_name ? "proxy-groups" : "Proxy Group", rule_name = clash_use_new_field_name ? "rules" : "Rule";
ini.store_any_line = true;
@@ -1962,7 +1953,7 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
{
//std::cerr<<"Fetching node data from url '"<<x<<"'."<<std::endl;
writeLog(0, "Fetching node data from url '" + x + "'.", LOG_LEVEL_INFO);
if(addNodes(x, nodes, 0, proxy, dummy_str_array, dummy_str_array, dummy_str_array, dummy_str_array, subInfo, !api_mode) == -1)
if(addNodes(x, nodes, 0, proxy, dummy_str_array, dummy_str_array, dummy_str_array, dummy_str_array, subInfo, !api_mode, request.headers) == -1)
{
*status_code = 400;
return std::string("The following link doesn't contain any valid node info: " + x);
@@ -2026,7 +2017,7 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
strArray = split(x, ",");
if(strArray.size() != 3)
continue;
content = webGet(strArray[1], proxy, dummy, cache_ruleset);
content = webGet(strArray[1], proxy, cache_ruleset);
if(!content.size())
continue;
+3
View File
@@ -25,6 +25,9 @@ template <typename T> class __defer_struct final {private: T fn; bool __cancelle
//#define defer(x) std::unique_ptr<void> DO_CONCAT(__defer_deleter_,__LINE__) (nullptr, [&](...){x});
#define defer(x) __defer_struct DO_CONCAT(__defer_deleter,__COUNTER__) ([&](...){x;});
#define getbit(x,n) (((int)x < 1) ? 0 : ((x >> (n - 1)) & 1))
#define setbit(x,n,v) x ^= (-v ^ x) & (1UL << (n - 1))
typedef std::string::size_type string_size;
typedef std::vector<std::string> string_array;
typedef std::map<std::string, std::string> string_map;
+2 -2
View File
@@ -22,7 +22,7 @@ void copyNodes(std::vector<nodeInfo> &source, std::vector<nodeInfo> &dest)
}
}
int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std::string proxy, string_array &exclude_remarks, string_array &include_remarks, string_array &stream_rules, string_array &time_rules, std::string &subInfo, bool authorized)
int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std::string proxy, string_array &exclude_remarks, string_array &include_remarks, string_array &stream_rules, string_array &time_rules, std::string &subInfo, bool authorized, string_map &request_headers)
{
int linkType = -1;
std::vector<nodeInfo> nodes;
@@ -85,7 +85,7 @@ int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std
writeLog(LOG_TYPE_INFO, "Downloading subscription data...");
if(startsWith(link, "surge:///install-config")) //surge config link
link = UrlDecode(getUrlArg(link, "url"));
strSub = webGet(link, proxy, extra_headers, cache_subscription);
strSub = webGet(link, proxy, cache_subscription, &extra_headers, &request_headers);
/*
if(strSub.size() == 0)
{
+1 -1
View File
@@ -6,6 +6,6 @@
#include "nodeinfo.h"
int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std::string proxy, string_array &exclude_remarks, string_array &include_remarks, string_array &stream_rules, string_array &time_rules, std::string &subInfo, bool authorized);
int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std::string proxy, string_array &exclude_remarks, string_array &include_remarks, string_array &stream_rules, string_array &time_rules, std::string &subInfo, bool authorized, string_map &request_headers);
#endif // NODEMANIP_H_INCLUDED
+52 -119
View File
@@ -651,7 +651,7 @@ void rulesetToClash(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset
total_rules++;
continue;
}
convertRuleset(retrieved_rules, x.rule_type);
retrieved_rules = convertRuleset(retrieved_rules, x.rule_type);
char delimiter = getLineBreak(retrieved_rules);
strStrm.clear();
@@ -743,7 +743,7 @@ std::string rulesetToClashStr(YAML::Node &base_rule, std::vector<ruleset_content
total_rules++;
continue;
}
convertRuleset(retrieved_rules, x.rule_type);
retrieved_rules = convertRuleset(retrieved_rules, x.rule_type);
char delimiter = getLineBreak(retrieved_rules);
strStrm.clear();
@@ -911,7 +911,7 @@ void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_
continue;
}
convertRuleset(retrieved_rules, x.rule_type);
retrieved_rules = convertRuleset(retrieved_rules, x.rule_type);
char delimiter = getLineBreak(retrieved_rules);
strStrm.clear();
@@ -1735,13 +1735,16 @@ std::string netchToSurge(std::vector<nodeInfo> &nodes, std::string &base_conf, s
return ini.ToString();
}
std::string netchToSS(std::vector<nodeInfo> &nodes, extra_settings &ext)
std::string netchToSingle(std::vector<nodeInfo> &nodes, int types, extra_settings &ext)
{
/// types: SS=1 SSR=2 VMess=4 Trojan=8
rapidjson::Document json;
std::string remark, hostname, port, password, method;
std::string plugin, pluginopts;
std::string protocol, protoparam, obfs, obfsparam;
std::string id, aid, transproto, faketype, host, path, quicsecure, quicsecret;
std::string proxyStr, allLinks;
bool tlssecure, ss = getbit(types, 1), ssr = getbit(types, 2), vmess = getbit(types, 3), trojan = getbit(types, 4);
for(nodeInfo &x : nodes)
{
@@ -1762,16 +1765,55 @@ std::string netchToSS(std::vector<nodeInfo> &nodes, extra_settings &ext)
switch(x.linkType)
{
case SPEEDTEST_MESSAGE_FOUNDSS:
proxyStr = "ss://" + urlsafe_base64_encode(method + ":" + password) + "@" + hostname + ":" + port;
if(plugin.size() && pluginopts.size())
if(ss)
{
proxyStr += "/?plugin=" + UrlEncode(plugin + ";" + pluginopts);
proxyStr = "ss://" + urlsafe_base64_encode(method + ":" + password) + "@" + hostname + ":" + port;
if(plugin.size() && pluginopts.size())
{
proxyStr += "/?plugin=" + UrlEncode(plugin + ";" + pluginopts);
}
proxyStr += "#" + UrlEncode(remark);
}
proxyStr += "#" + UrlEncode(remark);
else if(ssr)
{
if(std::count(ssr_ciphers.begin(), ssr_ciphers.end(), method) > 0 && !GetMember(json, "Plugin").size() && !GetMember(json, "Plugin").size())
proxyStr = "ssr://" + urlsafe_base64_encode(hostname + ":" + port + ":origin:" + method + ":plain:" + urlsafe_base64_encode(password) \
+ "/?group=" + urlsafe_base64_encode(x.group) + "&remarks=" + urlsafe_base64_encode(remark));
}
else
continue;
break;
case SPEEDTEST_MESSAGE_FOUNDSSR:
if(std::count(ss_ciphers.begin(), ss_ciphers.end(), method) > 0 && protocol == "origin" && obfs == "plain")
proxyStr = "ss://" + urlsafe_base64_encode(method + ":" + password) + "@" + hostname + ":" + port + "#" + UrlEncode(remark);
if(ssr)
{
proxyStr = "ssr://" + urlsafe_base64_encode(hostname + ":" + port + ":" + protocol + ":" + method + ":" + obfs + ":" + urlsafe_base64_encode(password) \
+ "/?group=" + urlsafe_base64_encode(x.group) + "&remarks=" + urlsafe_base64_encode(remark) \
+ "&obfsparam=" + urlsafe_base64_encode(obfsparam) + "&protoparam=" + urlsafe_base64_encode(protoparam));
}
else if(ss)
{
if(std::count(ss_ciphers.begin(), ss_ciphers.end(), method) > 0 && protocol == "origin" && obfs == "plain")
proxyStr = "ss://" + urlsafe_base64_encode(method + ":" + password) + "@" + hostname + ":" + port + "#" + UrlEncode(remark);
}
else
continue;
break;
case SPEEDTEST_MESSAGE_FOUNDVMESS:
if(!vmess)
continue;
id = GetMember(json, "UserID");
aid = GetMember(json, "AlterID");
transproto = GetMember(json, "TransferProtocol");
host = GetMember(json, "Host");
path = GetMember(json, "Path");
faketype = GetMember(json, "FakeType");
tlssecure = GetMember(json, "TLSSecure") == "true";
proxyStr = "vmess://" + base64_encode(vmessLinkConstruct(remark, hostname, port, faketype, id, aid, transproto, path, host, tlssecure ? "tls" : ""));
break;
case SPEEDTEST_MESSAGE_FOUNDTROJAN:
if(!trojan)
continue;
proxyStr = "trojan://" + password + "@" + hostname + ":" + port + "#" + UrlEncode(remark);
break;
default:
continue;
@@ -1857,115 +1899,6 @@ std::string netchToSSSub(std::string &base_conf, std::vector<nodeInfo> &nodes, e
return output_content;
}
std::string netchToSSR(std::vector<nodeInfo> &nodes, extra_settings &ext)
{
rapidjson::Document json;
std::string remark, hostname, port, password, method;
std::string protocol, protoparam, obfs, obfsparam;
std::string proxyStr, allLinks;
for(nodeInfo &x : nodes)
{
json.Parse(x.proxyStr.data());
remark = x.remarks;
hostname = GetMember(json, "Hostname");
port = std::to_string((unsigned short)stoi(GetMember(json, "Port")));
password = GetMember(json, "Password");
method = GetMember(json, "EncryptMethod");
protocol = GetMember(json, "Protocol");
protoparam = GetMember(json, "ProtocolParam");
obfs = GetMember(json, "OBFS");
obfsparam = GetMember(json, "OBFSParam");
switch(x.linkType)
{
case SPEEDTEST_MESSAGE_FOUNDSSR:
proxyStr = "ssr://" + urlsafe_base64_encode(hostname + ":" + port + ":" + protocol + ":" + method + ":" + obfs + ":" + urlsafe_base64_encode(password) \
+ "/?group=" + urlsafe_base64_encode(x.group) + "&remarks=" + urlsafe_base64_encode(remark) \
+ "&obfsparam=" + urlsafe_base64_encode(obfsparam) + "&protoparam=" + urlsafe_base64_encode(protoparam));
break;
case SPEEDTEST_MESSAGE_FOUNDSS:
if(std::count(ssr_ciphers.begin(), ssr_ciphers.end(), method) > 0 && !GetMember(json, "Plugin").size() && !GetMember(json, "Plugin").size())
proxyStr = "ssr://" + urlsafe_base64_encode(hostname + ":" + port + ":origin:" + method + ":plain:" + urlsafe_base64_encode(password) \
+ "/?group=" + urlsafe_base64_encode(x.group) + "&remarks=" + urlsafe_base64_encode(remark));
break;
default:
continue;
}
allLinks += proxyStr + "\n";
}
return base64_encode(allLinks);
}
std::string netchToVMess(std::vector<nodeInfo> &nodes, extra_settings &ext)
{
rapidjson::Document json;
std::string remark, hostname, port, method;
std::string id, aid, transproto, faketype, host, path, quicsecure, quicsecret;
std::string proxyStr, allLinks;
bool tlssecure;
for(nodeInfo &x : nodes)
{
json.Parse(x.proxyStr.data());
remark = x.remarks;
hostname = GetMember(json, "Hostname");
port = std::to_string((unsigned short)stoi(GetMember(json, "Port")));
method = GetMember(json, "EncryptMethod");
id = GetMember(json, "UserID");
aid = GetMember(json, "AlterID");
transproto = GetMember(json, "TransferProtocol");
host = GetMember(json, "Host");
path = GetMember(json, "Path");
faketype = GetMember(json, "FakeType");
tlssecure = GetMember(json, "TLSSecure") == "true";
switch(x.linkType)
{
case SPEEDTEST_MESSAGE_FOUNDVMESS:
proxyStr = "vmess://" + base64_encode(vmessLinkConstruct(remark, hostname, port, faketype, id, aid, transproto, path, host, tlssecure ? "tls" : ""));
break;
default:
continue;
}
allLinks += proxyStr + "\n";
}
return base64_encode(allLinks);
}
std::string netchToTrojan(std::vector<nodeInfo> &nodes, extra_settings &ext)
{
rapidjson::Document json;
std::string server, port, psk, remark;
std::string proxyStr, allLinks;
for(nodeInfo &x : nodes)
{
json.Parse(x.proxyStr.data());
remark = x.remarks;
server = GetMember(json, "Hostname");
port = std::to_string((unsigned short)stoi(GetMember(json, "Port")));
psk = GetMember(json, "Password");
switch(x.linkType)
{
case SPEEDTEST_MESSAGE_FOUNDTROJAN:
proxyStr = "trojan://" + psk + "@" + server + ":" + port + "#" + UrlEncode(remark);
break;
default:
continue;
}
allLinks += proxyStr + "\n";
}
return base64_encode(allLinks);
}
std::string netchToQuan(std::vector<nodeInfo> &nodes, std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, extra_settings &ext)
{
INIReader ini;
+1 -4
View File
@@ -61,11 +61,8 @@ std::string netchToSurge(std::vector<nodeInfo> &nodes, std::string &base_conf, s
std::string netchToMellow(std::vector<nodeInfo> &nodes, std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, extra_settings &ext);
void netchToMellow(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, extra_settings &ext);
std::string netchToLoon(std::vector<nodeInfo> &nodes, std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, extra_settings &ext);
std::string netchToSS(std::vector<nodeInfo> &nodes, extra_settings &ext);
std::string netchToSSSub(std::string &base_conf, std::vector<nodeInfo> &nodes, extra_settings &ext);
std::string netchToSSR(std::vector<nodeInfo> &nodes, extra_settings &ext);
std::string netchToVMess(std::vector<nodeInfo> &nodes, extra_settings &ext);
std::string netchToTrojan(std::vector<nodeInfo> &nodes, extra_settings &ext);
std::string netchToSingle(std::vector<nodeInfo> &nodes, int types, extra_settings &ext);
std::string netchToQuanX(std::vector<nodeInfo> &nodes, std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, extra_settings &ext);
void netchToQuanX(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, extra_settings &ext);
std::string netchToQuan(std::vector<nodeInfo> &nodes, std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, extra_settings &ext);
+5 -3
View File
@@ -190,7 +190,7 @@ const std::string clash_script_template = R"(def main(ctx, md):
{% if not rule.keyword == "" %}{% include "keyword_template" %}{% endif %}
{% endfor %}
geoips = { {{ geoips }} }
{% if exists("geoips") %} geoips = { {{ geoips }} }
ip = md.dst_ip
if ip == "":
ip = ctx.resolve_ip(md.host)
@@ -199,7 +199,7 @@ const std::string clash_script_template = R"(def main(ctx, md):
return "{{ match_group }}"
for key in geoips:
if ctx.geoip(ip) == key:
return geoips[key]
return geoips[key]{% endif %}
return "{{ match_group }}")";
const std::string clash_script_group_template = R"({% if rule.has_domain == "false" and rule.has_ipcidr == "false" %} if ctx.rule_providers["{{ rule.name }}_classical"].match(md):
@@ -414,7 +414,9 @@ int renderClashScript(YAML::Node &base_rule, std::vector<ruleset_content> &rules
}
if(script)
{
parse_json_pointer(data, "geoips", geoips.erase(geoips.size() - 1));
if(geoips.size())
parse_json_pointer(data, "geoips", geoips.erase(geoips.size() - 1));
parse_json_pointer(data, "match_group", match_group);
inja::Environment env;
+70 -40
View File
@@ -45,6 +45,14 @@ static int writer(char *data, size_t size, size_t nmemb, std::string *writerData
return size * nmemb;
}
static int dummy_writer(char *data, size_t size, size_t nmemb, void *writerData)
{
/// dummy writer, do not save anything
(void)data;
(void)writerData;
return size * nmemb;
}
static int size_checker(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow)
{
if(dltotal > 1048576.0)
@@ -52,7 +60,7 @@ static int size_checker(void *clientp, double dltotal, double dlnow, double ulto
return 0;
}
static inline void curl_set_common_options(CURL *curl_handle, const char *url)
static inline void curl_set_common_options(CURL *curl_handle, const char *url, long max_file_size = 1048576L)
{
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, global_log_level == LOG_LEVEL_VERBOSE ? 1L : 0L);
@@ -64,42 +72,63 @@ static inline void curl_set_common_options(CURL *curl_handle, const char *url)
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 15L);
curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, user_agent_str.data());
curl_easy_setopt(curl_handle, CURLOPT_MAXFILESIZE, 1048576L);
if(max_file_size)
curl_easy_setopt(curl_handle, CURLOPT_MAXFILESIZE, max_file_size);
curl_easy_setopt(curl_handle, CURLOPT_PROGRESSFUNCTION, size_checker);
}
static std::string curlGet(const std::string &url, const std::string &proxy, std::string &response_headers, CURLcode &return_code)
//static std::string curlGet(const std::string &url, const std::string &proxy, std::string &response_headers, CURLcode &return_code, const string_map &request_headers)
static int curlGet(const FetchArgument argument, FetchResult &result)
{
CURL *curl_handle;
std::string data, new_url = url;
std::string *data = result.content, new_url = argument.url;
struct curl_slist *list = NULL;
defer(curl_slist_free_all(list);)
long retVal = 0;
curl_init();
curl_handle = curl_easy_init();
if(proxy.size())
if(argument.proxy.size())
{
if(startsWith(proxy, "cors:"))
if(startsWith(argument.proxy, "cors:"))
{
list = curl_slist_append(list, "X-Requested-With: subconverter " VERSION);
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list);
new_url = proxy.substr(5) + url;
new_url = argument.proxy.substr(5) + argument.url;
}
else
curl_easy_setopt(curl_handle, CURLOPT_PROXY, proxy.data());
curl_easy_setopt(curl_handle, CURLOPT_PROXY, argument.proxy.data());
}
curl_set_common_options(curl_handle, new_url.data());
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writer);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &data);
curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, writer);
curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, &response_headers);
if(argument.request_headers)
{
for(auto &x : *argument.request_headers)
list = curl_slist_append(list, (x.first + ": " + x.second).data());
}
if(list)
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list);
if(result.content)
{
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writer);
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, result.content);
}
else
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, dummy_writer);
if(result.response_headers)
{
curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, writer);
curl_easy_setopt(curl_handle, CURLOPT_HEADERDATA, result.response_headers);
}
else
curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, dummy_writer);
unsigned int fail_count = 0, max_fails = 1;
while(true)
{
return_code = curl_easy_perform(curl_handle);
if(return_code == CURLE_OK || max_fails >= fail_count)
*result.status_code = curl_easy_perform(curl_handle);
if(*result.status_code == CURLE_OK || max_fails >= fail_count)
break;
else
fail_count++;
@@ -108,11 +137,14 @@ static std::string curlGet(const std::string &url, const std::string &proxy, std
curl_easy_getinfo(curl_handle, CURLINFO_HTTP_CODE, &retVal);
curl_easy_cleanup(curl_handle);
if(return_code != CURLE_OK || retVal != 200)
data.clear();
data.shrink_to_fit();
if(data)
{
if(*result.status_code != CURLE_OK || retVal != 200)
data->clear();
data->shrink_to_fit();
}
return data;
return *result.status_code;
}
// data:[<mediatype>][;base64],<data>
@@ -139,10 +171,14 @@ std::string buildSocks5ProxyString(const std::string &addr, int port, const std:
return proxystr;
}
std::string webGet(const std::string &url, const std::string &proxy, std::string &response_headers, unsigned int cache_ttl)
std::string webGet(const std::string &url, const std::string &proxy, unsigned int cache_ttl, std::string *response_headers, string_map *request_headers)
{
int return_code = 0;
std::string content;
CURLcode return_code;
FetchArgument argument {url, proxy, request_headers, cache_ttl};
FetchResult fetch_res {&return_code, &content, response_headers};
if (startsWith(url, "data:"))
return dataGet(url);
// cache system
@@ -159,19 +195,22 @@ std::string webGet(const std::string &url, const std::string &proxy, std::string
{
writeLog(0, "CACHE HIT: '" + url + "', using local cache.");
guarded_mutex guard(cache_rw_lock);
response_headers = fileGet(path_header, true);
if(response_headers)
*response_headers = fileGet(path_header, true);
return fileGet(path, true);
}
writeLog(0, "CACHE MISS: '" + url + "', TTL timeout, creating new cache."); // out of TTL
}
else
writeLog(0, "CACHE NOT EXIST: '" + url + "', creating new cache.");
content = curlGet(url, proxy, response_headers, return_code); // try to fetch data
//content = curlGet(url, proxy, response_headers, return_code); // try to fetch data
curlGet(argument, fetch_res);
if(return_code == CURLE_OK) // success, save new cache
{
guarded_mutex guard(cache_rw_lock);
fileWrite(path, content, true);
fileWrite(path_header, response_headers, true);
if(response_headers)
fileWrite(path_header, *response_headers, true);
}
else
{
@@ -180,26 +219,17 @@ std::string webGet(const std::string &url, const std::string &proxy, std::string
writeLog(0, "Fetch failed. Serving cached content."); // cache exist, serving cache
guarded_mutex guard(cache_rw_lock);
content = fileGet(path, true);
response_headers = fileGet(path_header, true);
if(response_headers)
*response_headers = fileGet(path_header, true);
}
else
writeLog(0, "Fetch failed. No local cache available."); // cache not exist or not allow to serve cache, serving nothing
}
return content;
}
return curlGet(url, proxy, response_headers, return_code);
}
std::string webGet(const std::string &url, const std::string &proxy)
{
std::string dummy;
return webGet(url, proxy, dummy);
}
std::string webGet(const std::string &url, const std::string &proxy, unsigned int cache_ttl)
{
std::string dummy;
return webGet(url, proxy, dummy, cache_ttl);
//return curlGet(url, proxy, response_headers, return_code);
content = curlGet(argument, fetch_res);
return content;
}
int curlPost(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData)
@@ -215,7 +245,7 @@ int curlPost(const std::string &url, const std::string &data, const std::string
for(const std::string &x : request_headers)
list = curl_slist_append(list, x.data());
curl_set_common_options(curl_handle, url.data());
curl_set_common_options(curl_handle, url.data(), 0L);
curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, data.data());
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, data.size());
@@ -259,7 +289,7 @@ int curlPatch(const std::string &url, const std::string &data, const std::string
for(const std::string &x : request_headers)
list = curl_slist_append(list, x.data());
curl_set_common_options(curl_handle, url.data());
curl_set_common_options(curl_handle, url.data(), 0L);
curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, "PATCH");
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, data.data());
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, data.size());
+17 -3
View File
@@ -2,12 +2,26 @@
#define WEBGET_H_INCLUDED
#include <string>
#include <map>
#include "misc.h"
std::string webGet(const std::string &url, const std::string &proxy = "");
std::string webGet(const std::string &url, const std::string &proxy, unsigned int cache_ttl);
std::string webGet(const std::string &url, const std::string &proxy, std::string &response_headers, unsigned int cache_ttl = 0);
struct FetchArgument
{
const std::string url;
const std::string proxy;
string_map *request_headers = NULL;
const unsigned int cache_ttl = 0;
};
struct FetchResult
{
int *status_code;
std::string *content = NULL;
std::string *response_headers = NULL;
};
std::string webGet(const std::string &url, const std::string &proxy = "", unsigned int cache_ttl = 0, std::string *response_headers = NULL, string_map *request_headers = NULL);
int webPost(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData);
int webPatch(const std::string &url, const std::string &data, const std::string &proxy, const string_array &request_headers, std::string *retData);
std::string buildSocks5ProxyString(const std::string &addr, int port, const std::string &username, const std::string &password);
+12 -3
View File
@@ -32,6 +32,8 @@ struct responseRoute
std::vector<responseRoute> responses;
string_map redirect_map;
const char *request_header_whitelist[] = {"user-agent"};
static inline void buffer_cleanup(struct evbuffer *eb)
{
//evbuffer_free(eb);
@@ -40,7 +42,6 @@ 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(Request &request, Response &response, std::string &return_data)
{
writeLog(0, "handle_cmd: " + request.method + " handle_uri: " + request.url, LOG_LEVEL_VERBOSE);
@@ -123,9 +124,17 @@ void OnReq(evhttp_request *req, void *args)
struct evkeyval* kv = req->input_headers->tqh_first;
while (kv)
{
request.headers.emplace(kv->key, kv->value);
kv = kv->next.tqe_next;
for(auto &x : request_header_whitelist)
{
if(strcmp(kv->key, x) == 0)
request.headers.emplace(kv->key, kv->value);
kv = kv->next.tqe_next;
}
}
if(user_agent)
request.headers.emplace("X-User-Agent", user_agent);
request.headers.emplace("X-Client-IP", client_ip);
retVal = process_request(request, response, return_data);
content_type = response.content_type;