mirror of
				https://github.com/asdlokj1qpi233/subconverter.git
				synced 2025-10-30 21:32:42 +00:00 
			
		
		
		
	Bug fixes
Fix compatibility issue for some ShadowsocksD subscription. Fix some cross-platform configuration file reader bugs. Add upload to Gist function. Add append MANAGED-CONFIG to Surge configuration function. Clean up codes.
This commit is contained in:
		
							
								
								
									
										3
									
								
								gistconf.ini
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								gistconf.ini
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| [common] | ||||
| ;uncomment the following line and enter your token to enable upload function | ||||
| ;token = your_personal_token_here | ||||
| @@ -129,7 +129,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 = count(content.begin(), content.end(), '\n') < 1 ? '\r' : '\n'; | ||||
|  | ||||
|         EraseAll(); //first erase all data | ||||
|         if(do_utf8_to_gbk && is_str_utf8(content)) | ||||
|   | ||||
							
								
								
									
										380
									
								
								main.cpp
									
									
									
									
									
								
							
							
						
						
									
										380
									
								
								main.cpp
									
									
									
									
									
								
							| @@ -18,9 +18,9 @@ | ||||
| //common settings | ||||
| string_array def_exclude_remarks, def_include_remarks, rulesets; | ||||
| std::vector<ruleset_content> ruleset_content_array; | ||||
| std::string listen_address = "127.0.0.1", default_url; | ||||
| std::string listen_address = "127.0.0.1", default_url, managed_config_prefix; | ||||
| int listen_port = 25500, max_pending_connections = 10, max_concurrent_threads = 4; | ||||
| bool api_mode = true, update_ruleset_on_request = false, overwrite_original_rules = true; | ||||
| bool api_mode = true, write_managed_config = true, update_ruleset_on_request = false, overwrite_original_rules = true; | ||||
| extern std::string custom_group; | ||||
|  | ||||
| //safety lock for multi-thread | ||||
| @@ -40,24 +40,24 @@ std::string surge_rule_base; | ||||
|  | ||||
| void setcd(char *argv[]) | ||||
| { | ||||
|     #ifndef _WIN32 | ||||
| 	std::string path, prgpath; | ||||
| 	char szTmp[1024]; | ||||
| 	prgpath.assign(argv[0]); | ||||
| 	if(prgpath[0] == '/') | ||||
| 	{ | ||||
| 		path = prgpath.substr(0, prgpath.rfind("/") + 1); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		getcwd(szTmp, 1023); | ||||
| 		path.append(szTmp); | ||||
| 		path.append("/"); | ||||
| 		path.append(argv[0]); | ||||
| 		path = path.substr(0, path.rfind("/") + 1); | ||||
| 	} | ||||
| #ifndef _WIN32 | ||||
|     std::string path, prgpath; | ||||
|     char szTmp[1024]; | ||||
|     prgpath.assign(argv[0]); | ||||
|     if(prgpath[0] == '/') | ||||
|     { | ||||
|         path = prgpath.substr(0, prgpath.rfind("/") + 1); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         getcwd(szTmp, 1023); | ||||
|         path.append(szTmp); | ||||
|         path.append("/"); | ||||
|         path.append(argv[0]); | ||||
|         path = path.substr(0, path.rfind("/") + 1); | ||||
|     } | ||||
|     chdir(path.data()); | ||||
|     #endif // _WIN32 | ||||
| #endif // _WIN32 | ||||
| } | ||||
|  | ||||
| std::string refreshRulesets() | ||||
| @@ -101,6 +101,8 @@ std::string refreshRulesets() | ||||
|         } | ||||
|         if(rc.rule_content.size()) | ||||
|             ruleset_content_array.emplace_back(rc); | ||||
|         else | ||||
|             std::cerr<<"Warning: No data was fetched from this link. Skipping..."<<std::endl; | ||||
|     } | ||||
|  | ||||
|     return "done"; | ||||
| @@ -136,6 +138,12 @@ void readConf() | ||||
|     if(ini.ItemPrefixExist("rename_node")) | ||||
|         ini.GetAll("rename_node", renames); | ||||
|  | ||||
|     ini.EnterSection("managed_config"); | ||||
|     if(ini.ItemExist("write_managed_config")) | ||||
|         write_managed_config = ini.GetBool("write_managed_config"); | ||||
|     if(ini.ItemExist("managed_config_prefix")) | ||||
|         managed_config_prefix = ini.Get("managed_config_prefix"); | ||||
|  | ||||
|     ini.EnterSection("emojis"); | ||||
|     if(ini.ItemExist("add_emoji")) | ||||
|         add_emoji = ini.GetBool("add_emoji"); | ||||
| @@ -180,6 +188,122 @@ void readConf() | ||||
|     std::cerr<<"Read preference settings completed."<<std::endl; | ||||
| } | ||||
|  | ||||
| std::string subconverter(RESPONSE_CALLBACK_ARGS) | ||||
| { | ||||
|     if(!api_mode) | ||||
|         readConf(); | ||||
|     std::string target = getUrlArg(argument, "target"), url = UrlDecode(getUrlArg(argument, "url")), include = UrlDecode(getUrlArg(argument, "regex")); | ||||
|     std::string group = UrlDecode(getUrlArg(argument, "group")), upload = getUrlArg(argument, "upload"), version = getUrlArg(argument, "ver"); | ||||
|     std::string base_content, output_content; | ||||
|     if(!url.size()) | ||||
|         url = default_url; | ||||
|     string_array urls = split(url, "|"); | ||||
|     std::vector<nodeInfo> nodes; | ||||
|     int groupID = 0; | ||||
|     if(include.size()) | ||||
|     { | ||||
|         eraseElements(def_include_remarks); | ||||
|         def_include_remarks.emplace_back(include); | ||||
|     } | ||||
|     if(group.size()) | ||||
|         custom_group = group; | ||||
|     for(std::string &x : urls) | ||||
|     { | ||||
|         std::cerr<<"Fetching node data from url '"<<x<<"'."<<std::endl; | ||||
|         addNodes(x, nodes, groupID); | ||||
|         groupID++; | ||||
|     } | ||||
|     if(!target.size()) | ||||
|         return std::string(); | ||||
|  | ||||
|     std::cerr<<"Generate target: "; | ||||
|     if(target == "clash" || target == "clashr") | ||||
|     { | ||||
|         std::cerr<<"Clash"<<((target == "clashr") ? "R" : "")<<std::endl; | ||||
|         if(fileExist(clash_rule_base)) | ||||
|             base_content = fileGet(clash_rule_base, false); | ||||
|         else | ||||
|             base_content = webGet(clash_rule_base, getSystemProxy()); | ||||
|  | ||||
|         if(update_ruleset_on_request) | ||||
|             refreshRulesets(); | ||||
|         output_content = netchToClash(nodes, base_content, ruleset_content_array, clash_extra_group, target == "clashr"); | ||||
|         if(upload == "true") | ||||
|             uploadGist("clash", output_content, false); | ||||
|         return output_content; | ||||
|     } | ||||
|     else if(target == "surge") | ||||
|     { | ||||
|         std::cerr<<"Surge "<<version<<std::endl; | ||||
|         int surge_ver = version.size() ? stoi(version) : 3; | ||||
|         if(fileExist(surge_rule_base)) | ||||
|             base_content = fileGet(surge_rule_base, false); | ||||
|         else | ||||
|             base_content = webGet(surge_rule_base, getSystemProxy()); | ||||
|  | ||||
|         output_content = netchToSurge(nodes, base_content, rulesets, clash_extra_group, surge_ver); | ||||
|         if(upload == "true") | ||||
|             uploadGist("surge" + version, output_content, true); | ||||
|  | ||||
|         if(write_managed_config && managed_config_prefix.size()) | ||||
|             output_content = "#!MANAGED-CONFIG " + managed_config_prefix + "/sub?" + argument + "\n\n" + output_content; | ||||
|         return output_content; | ||||
|     } | ||||
|     else if(target == "ss") | ||||
|     { | ||||
|         std::cerr<<"SS"<<std::endl; | ||||
|         output_content = netchToSS(nodes); | ||||
|         if(upload == "true") | ||||
|             uploadGist("ss", output_content, false); | ||||
|         return output_content; | ||||
|     } | ||||
|     else if(target == "ssr") | ||||
|     { | ||||
|         std::cerr<<"SSR"<<std::endl; | ||||
|         output_content = netchToSSR(nodes); | ||||
|         if(upload == "true") | ||||
|             uploadGist("ssr", output_content, false); | ||||
|         return output_content; | ||||
|     } | ||||
|     else if(target == "v2ray") | ||||
|     { | ||||
|         std::cerr<<"v2rayN"<<std::endl; | ||||
|         output_content = netchToVMess(nodes); | ||||
|         if(upload == "true") | ||||
|             uploadGist("v2ray", output_content, false); | ||||
|         return output_content; | ||||
|     } | ||||
|     else if(target == "quan") | ||||
|     { | ||||
|         std::cerr<<"Quantumult"<<std::endl; | ||||
|         output_content = netchToQuan(nodes); | ||||
|         if(upload == "true") | ||||
|             uploadGist("quan", output_content, false); | ||||
|         return output_content; | ||||
|     } | ||||
|     else if(target == "quanx") | ||||
|     { | ||||
|         std::cerr<<"Quantumult X"<<std::endl; | ||||
|         output_content = netchToQuanX(nodes); | ||||
|         if(upload == "true") | ||||
|             uploadGist("quanx", output_content, false); | ||||
|         return output_content; | ||||
|     } | ||||
|     else if(target == "ssd") | ||||
|     { | ||||
|         std::cerr<<"SSD"<<std::endl; | ||||
|         output_content = netchToSSD(nodes, group); | ||||
|         if(upload == "true") | ||||
|             uploadGist("ssd", output_content, false); | ||||
|         return output_content; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         std::cerr<<"Unspecified"<<std::endl; | ||||
|         return std::string(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| #ifdef _WIN32 | ||||
| @@ -208,243 +332,51 @@ int main(int argc, char *argv[]) | ||||
|         return "done"; | ||||
|     }); | ||||
|  | ||||
|     append_response("GET", "/sub", "text/plain;charset=utf-8", subconverter); | ||||
|  | ||||
|     append_response("GET", "/clash", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string | ||||
|     { | ||||
|         if(!api_mode) | ||||
|             readConf(); | ||||
|         std::string clash_base_content; | ||||
|         std::string url = UrlDecode(getUrlArg(argument, "url")), include = UrlDecode(getUrlArg(argument, "regex")), group = UrlDecode(getUrlArg(argument, "group")); | ||||
|         if(!url.size()) url = default_url; | ||||
|         string_array urls = split(url, "|"); | ||||
|         std::vector<nodeInfo> nodes; | ||||
|         int groupID = 0; | ||||
|         if(include.size()) | ||||
|         { | ||||
|             eraseElements(def_include_remarks); | ||||
|             def_include_remarks.emplace_back(include); | ||||
|         } | ||||
|         if(group.size()) custom_group = group; | ||||
|         for(std::string &x : urls) | ||||
|         { | ||||
|             std::cerr<<"Fetching node data from url '"<<x<<"'. Generate target: Clash"<<std::endl; | ||||
|             addNodes(x, nodes, groupID); | ||||
|             groupID++; | ||||
|         } | ||||
|         if(fileExist(clash_rule_base)) | ||||
|             clash_base_content = fileGet(clash_rule_base, false); | ||||
|         else | ||||
|             clash_base_content = webGet(clash_rule_base, getSystemProxy()); | ||||
|  | ||||
|         if(update_ruleset_on_request) | ||||
|             refreshRulesets(); | ||||
|         return netchToClash(nodes, clash_base_content, ruleset_content_array, clash_extra_group, false); | ||||
|         return subconverter(argument + "&target=clash", postdata); | ||||
|     }); | ||||
|  | ||||
|     append_response("GET", "/clashr", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string | ||||
|     { | ||||
|         if(!api_mode) | ||||
|             readConf(); | ||||
|         std::string clash_base_content; | ||||
|         std::string url = UrlDecode(getUrlArg(argument, "url")), include = UrlDecode(getUrlArg(argument, "regex")), group = UrlDecode(getUrlArg(argument, "group")); | ||||
|         if(!url.size()) url = default_url; | ||||
|         string_array urls = split(url, "|"); | ||||
|         std::vector<nodeInfo> nodes; | ||||
|         int groupID = 0; | ||||
|         if(include.size()) | ||||
|         { | ||||
|             eraseElements(def_include_remarks); | ||||
|             def_include_remarks.emplace_back(include); | ||||
|         } | ||||
|         if(group.size()) custom_group = group; | ||||
|         for(std::string &x : urls) | ||||
|         { | ||||
|             std::cerr<<"Fetching node data from url '"<<x<<"'. Generate target: ClashR"<<std::endl; | ||||
|             addNodes(x, nodes, groupID); | ||||
|             groupID++; | ||||
|         } | ||||
|         if(fileExist(clash_rule_base)) | ||||
|             clash_base_content = fileGet(clash_rule_base, false); | ||||
|         else | ||||
|             clash_base_content = webGet(clash_rule_base, getSystemProxy()); | ||||
|  | ||||
|         if(update_ruleset_on_request) | ||||
|             refreshRulesets(); | ||||
|         return netchToClash(nodes, clash_base_content, ruleset_content_array, clash_extra_group, true); | ||||
|         return subconverter(argument + "&target=clashr", postdata); | ||||
|     }); | ||||
|  | ||||
|     append_response("GET", "/surge", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string | ||||
|     { | ||||
|         if(!api_mode) | ||||
|             readConf(); | ||||
|         std::string surge_base_content; | ||||
|         std::string url = UrlDecode(getUrlArg(argument, "url")), include = UrlDecode(getUrlArg(argument, "regex")), group = UrlDecode(getUrlArg(argument, "group")), version = getUrlArg(argument, "ver"); | ||||
|         int surge_ver = version.size() ? stoi(version) : 3; | ||||
|         if(!url.size()) url = default_url; | ||||
|         string_array urls = split(url, "|"); | ||||
|         std::vector<nodeInfo> nodes; | ||||
|         int groupID = 0; | ||||
|         if(include.size()) | ||||
|         { | ||||
|             eraseElements(def_include_remarks); | ||||
|             def_include_remarks.emplace_back(include); | ||||
|         } | ||||
|         if(group.size()) custom_group = group; | ||||
|         for(std::string &x : urls) | ||||
|         { | ||||
|             std::cerr<<"Fetching node data from url '"<<x<<"'. Generate target: Surge "<<surge_ver<<std::endl; | ||||
|             addNodes(x, nodes, groupID); | ||||
|             groupID++; | ||||
|         } | ||||
|         if(fileExist(surge_rule_base)) | ||||
|             surge_base_content = fileGet(surge_rule_base, false); | ||||
|         else | ||||
|             surge_base_content = webGet(surge_rule_base, getSystemProxy()); | ||||
|  | ||||
|         return netchToSurge(nodes, surge_base_content, rulesets, clash_extra_group, surge_ver); | ||||
|         return subconverter(argument + "&target=surge", postdata); | ||||
|     }); | ||||
|  | ||||
|     append_response("GET", "/ss", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string | ||||
|     { | ||||
|         if(!api_mode) | ||||
|             readConf(); | ||||
|         std::string url = UrlDecode(getUrlArg(argument, "url")), include = UrlDecode(getUrlArg(argument, "regex")); | ||||
|         if(!url.size()) url = default_url; | ||||
|         string_array urls = split(url, "|"); | ||||
|         std::vector<nodeInfo> nodes; | ||||
|         int groupID = 0; | ||||
|         if(include.size()) | ||||
|         { | ||||
|             eraseElements(def_include_remarks); | ||||
|             def_include_remarks.emplace_back(include); | ||||
|         } | ||||
|         for(std::string &x : urls) | ||||
|         { | ||||
|             std::cerr<<"Fetching node data from url '"<<x<<"'. Generate target: SIP002"<<std::endl; | ||||
|             addNodes(x, nodes, groupID); | ||||
|             groupID++; | ||||
|         } | ||||
|  | ||||
|         return netchToSS(nodes); | ||||
|         return subconverter(argument + "&target=ss", postdata); | ||||
|     }); | ||||
|  | ||||
|     append_response("GET", "/ssr", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string | ||||
|     { | ||||
|         if(!api_mode) | ||||
|             readConf(); | ||||
|         std::string url = UrlDecode(getUrlArg(argument, "url")), include = UrlDecode(getUrlArg(argument, "regex")); | ||||
|         if(!url.size()) url = default_url; | ||||
|         string_array urls = split(url, "|"); | ||||
|         std::vector<nodeInfo> nodes; | ||||
|         int groupID = 0; | ||||
|         if(include.size()) | ||||
|         { | ||||
|             eraseElements(def_include_remarks); | ||||
|             def_include_remarks.emplace_back(include); | ||||
|         } | ||||
|         for(std::string &x : urls) | ||||
|         { | ||||
|             std::cerr<<"Fetching node data from url '"<<x<<"'. Generate target: SSR"<<std::endl; | ||||
|             addNodes(x, nodes, groupID); | ||||
|             groupID++; | ||||
|         } | ||||
|  | ||||
|         return netchToSSR(nodes); | ||||
|         return subconverter(argument + "&target=ssr", postdata); | ||||
|     }); | ||||
|  | ||||
|     append_response("GET", "/v2rayn", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string | ||||
|     append_response("GET", "/v2ray", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string | ||||
|     { | ||||
|         if(!api_mode) | ||||
|             readConf(); | ||||
|         std::string url = UrlDecode(getUrlArg(argument, "url")), include = UrlDecode(getUrlArg(argument, "regex")); | ||||
|         if(!url.size()) url = default_url; | ||||
|         string_array urls = split(url, "|"); | ||||
|         std::vector<nodeInfo> nodes; | ||||
|         int groupID = 0; | ||||
|         if(include.size()) | ||||
|         { | ||||
|             eraseElements(def_include_remarks); | ||||
|             def_include_remarks.emplace_back(include); | ||||
|         } | ||||
|         for(std::string &x : urls) | ||||
|         { | ||||
|             std::cerr<<"Fetching node data from url '"<<x<<"'. Generate target: VMess"<<std::endl; | ||||
|             addNodes(x, nodes, groupID); | ||||
|             groupID++; | ||||
|         } | ||||
|  | ||||
|         return netchToVMess(nodes); | ||||
|         return subconverter(argument + "&target=v2ray", postdata); | ||||
|     }); | ||||
|  | ||||
|     append_response("GET", "/v2rayq", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string | ||||
|     append_response("GET", "/quan", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string | ||||
|     { | ||||
|         if(!api_mode) | ||||
|             readConf(); | ||||
|         std::string url = UrlDecode(getUrlArg(argument, "url")), include = UrlDecode(getUrlArg(argument, "regex")); | ||||
|         if(!url.size()) url = default_url; | ||||
|         string_array urls = split(url, "|"); | ||||
|         std::vector<nodeInfo> nodes; | ||||
|         int groupID = 0; | ||||
|         if(include.size()) | ||||
|         { | ||||
|             eraseElements(def_include_remarks); | ||||
|             def_include_remarks.emplace_back(include); | ||||
|         } | ||||
|         for(std::string &x : urls) | ||||
|         { | ||||
|             std::cerr<<"Fetching node data from url '"<<x<<"'. Generate target: VMess"<<std::endl; | ||||
|             addNodes(x, nodes, groupID); | ||||
|             groupID++; | ||||
|         } | ||||
|  | ||||
|         return netchToQuan(nodes); | ||||
|         return subconverter(argument + "&target=quan", postdata); | ||||
|     }); | ||||
|  | ||||
|     append_response("GET", "/quanx", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string | ||||
|     { | ||||
|         if(!api_mode) | ||||
|             readConf(); | ||||
|         std::string url = UrlDecode(getUrlArg(argument, "url")), include = UrlDecode(getUrlArg(argument, "regex")); | ||||
|         if(!url.size()) url = default_url; | ||||
|         string_array urls = split(url, "|"); | ||||
|         std::vector<nodeInfo> nodes; | ||||
|         int groupID = 0; | ||||
|         if(include.size()) | ||||
|         { | ||||
|             eraseElements(def_include_remarks); | ||||
|             def_include_remarks.emplace_back(include); | ||||
|         } | ||||
|         for(std::string &x : urls) | ||||
|         { | ||||
|             std::cerr<<"Fetching node data from url '"<<x<<"'. Generate target: Quantumult X"<<std::endl; | ||||
|             addNodes(x, nodes, groupID); | ||||
|             groupID++; | ||||
|         } | ||||
|  | ||||
|         return netchToQuanX(nodes); | ||||
|         return subconverter(argument + "&target=quanx", postdata); | ||||
|     }); | ||||
|  | ||||
|     append_response("GET", "/ssd", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string | ||||
|     { | ||||
|         if(!api_mode) | ||||
|             readConf(); | ||||
|         std::string url = UrlDecode(getUrlArg(argument, "url")), include = UrlDecode(getUrlArg(argument, "regex")), group = UrlDecode(getUrlArg(argument, "group")); | ||||
|         if(!url.size()) url = default_url; | ||||
|         string_array urls = split(url, "|"); | ||||
|         std::vector<nodeInfo> nodes; | ||||
|         int groupID = 0; | ||||
|         if(include.size()) | ||||
|         { | ||||
|             eraseElements(def_include_remarks); | ||||
|             def_include_remarks.emplace_back(include); | ||||
|         } | ||||
|         for(std::string &x : urls) | ||||
|         { | ||||
|             std::cerr<<"Fetching node data from url '"<<x<<"'. Generate target: SSD"<<std::endl; | ||||
|             addNodes(x, nodes, groupID); | ||||
|             groupID++; | ||||
|         } | ||||
|  | ||||
|         return netchToSSD(nodes, group); | ||||
|         return subconverter(argument + "&target=ssd", postdata); | ||||
|     }); | ||||
|  | ||||
|     if(!api_mode) | ||||
|   | ||||
							
								
								
									
										2
									
								
								misc.cpp
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								misc.cpp
									
									
									
									
									
								
							| @@ -478,7 +478,7 @@ std::string urlsafe_base64_decode(std::string encoded_string) | ||||
|  | ||||
| std::string urlsafe_base64_encode(std::string string_to_encode) | ||||
| { | ||||
|     return replace_all_distinct(replace_all_distinct(base64_encode(string_to_encode), "+", "-"), "/", "_"); | ||||
|     return replace_all_distinct(replace_all_distinct(replace_all_distinct(base64_encode(string_to_encode), "+", "-"), "/", "_"), "=", ""); | ||||
| } | ||||
|  | ||||
| std::string getMD5(std::string data) | ||||
|   | ||||
							
								
								
									
										7
									
								
								pref.ini
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								pref.ini
									
									
									
									
									
								
							| @@ -72,6 +72,13 @@ surge_rule_base=surge.conf | ||||
| ; times RE | ||||
| rename_node=\(?((x|X)?(\d+)(\.?\d+)?)((\s?倍率?)|(x|X))\)?@$1x | ||||
|  | ||||
| [managed_config] | ||||
| ;Append a '#!MANAGED-CONFIG' info to Surge configurations | ||||
| write_managed_config=false | ||||
|  | ||||
| ;Address prefix for MANAGED-CONFIG info, without the trailing "/". | ||||
| managed_config_prefix=http://127.0.0.1:25500 | ||||
|  | ||||
| [emojis] | ||||
| add_emoji=true | ||||
| remove_old_emoji=true | ||||
|   | ||||
| @@ -390,19 +390,24 @@ void explodeSSD(std::string link, bool libev, std::string custom_port, int local | ||||
|     jsondata.Parse(link.c_str()); | ||||
|     if(!jsondata.HasMember("servers")) | ||||
|         return; | ||||
|     GetMember(jsondata, "airport", group); | ||||
|     for(unsigned int i = 0; i < jsondata["servers"].Size(); i++) | ||||
|     { | ||||
|         jsondata["airport"] >> group; | ||||
|         jsondata["servers"][i]["remarks"] >> remarks; | ||||
|         jsondata["port"] >> port; | ||||
|         jsondata["encryption"] >> method; | ||||
|         jsondata["password"] >> password; | ||||
|         //get default info | ||||
|         GetMember(jsondata, "port", port); | ||||
|         GetMember(jsondata, "encryption", method); | ||||
|         GetMember(jsondata, "password", password); | ||||
|         GetMember(jsondata, "plugin", plugin); | ||||
|         GetMember(jsondata, "plugin_options", pluginopts); | ||||
|  | ||||
|         //get server-specific info | ||||
|         jsondata["servers"][i]["server"] >> server; | ||||
|         GetMember(jsondata["servers"][i], "remarks", remarks); | ||||
|         GetMember(jsondata["servers"][i], "port", port); | ||||
|         GetMember(jsondata["servers"][i], "encryption", method); | ||||
|         GetMember(jsondata["servers"][i], "password", password); | ||||
|         GetMember(jsondata["servers"][i], "plugin", plugin); | ||||
|         GetMember(jsondata["servers"][i], "plugin_options", pluginopts); | ||||
|         GetMember(jsondata["servers"][i], "encryption", method); | ||||
|         GetMember(jsondata["servers"][i], "port", port); | ||||
|         GetMember(jsondata["servers"][i], "password", password); | ||||
|  | ||||
|         if(custom_port != "") | ||||
|             port = custom_port; | ||||
| @@ -1550,7 +1555,7 @@ void explodeSub(std::string sub, bool sslibev, bool ssrlibev, std::string custom | ||||
|     sub = urlsafe_base64_decode(sub); | ||||
|     strstream << sub; | ||||
|     unsigned int index = nodes.size(); | ||||
|     char delimiter = split(sub, "\n").size() <= 1 ? split(sub, "\r").size() <= 1 ? ' ' : '\r' : '\n'; | ||||
|     char delimiter = split(sub, "\n").size() < 1 ? split(sub, "\r").size() < 1 ? ' ' : '\r' : '\n'; | ||||
|     while(getline(strstream, strLink, delimiter)) | ||||
|     { | ||||
|         explode(strLink, sslibev, ssrlibev, custom_port, local_port, node); | ||||
|   | ||||
							
								
								
									
										144
									
								
								subexport.cpp
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								subexport.cpp
									
									
									
									
									
								
							| @@ -2,7 +2,6 @@ | ||||
| #include "speedtestutil.h" | ||||
| #include "ini_reader.h" | ||||
| #include "rapidjson_extra.h" | ||||
| #include "yamlcpp_extra.h" | ||||
| #include "webget.h" | ||||
| #include "subexport.h" | ||||
| #include "printout.h" | ||||
| @@ -245,7 +244,7 @@ void rulesetToClash(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset | ||||
|             allRules.emplace_back(retrived_rules.substr(2) + "," + rule_group); | ||||
|             continue; | ||||
|         } | ||||
|         char delimiter = count(retrived_rules.begin(), retrived_rules.end(), '\n') <= 1 ? '\r' : '\n'; | ||||
|         char delimiter = count(retrived_rules.begin(), retrived_rules.end(), '\n') < 1 ? '\r' : '\n'; | ||||
|  | ||||
|         strStrm.clear(); | ||||
|         strStrm<<retrived_rules; | ||||
| @@ -305,7 +304,7 @@ void rulesetToSurge(INIReader &base_rule, string_array &ruleset_array, int surge | ||||
|                 if(api_mode) | ||||
|                     continue; | ||||
|                 retrived_rules = fileGet(rule_path, false); | ||||
|                 char delimiter = count(retrived_rules.begin(), retrived_rules.end(), '\n') <= 1 ? '\r' : '\n'; | ||||
|                 char delimiter = count(retrived_rules.begin(), retrived_rules.end(), '\n') < 1 ? '\r' : '\n'; | ||||
|  | ||||
|                 strStrm.clear(); | ||||
|                 strStrm<<retrived_rules; | ||||
| @@ -527,7 +526,7 @@ std::string netchToClash(std::vector<nodeInfo> &nodes, std::string &baseConf, st | ||||
|  | ||||
|         singlegroup["name"] = vArray[0]; | ||||
|         singlegroup["type"] = vArray[1]; | ||||
|         singlegroup["proxies"] = vectorToNode(filtered_nodelist); | ||||
|         singlegroup["proxies"] = filtered_nodelist; | ||||
|  | ||||
|         for(unsigned int i = 0; i < original_groups.size(); i++) | ||||
|         { | ||||
| @@ -828,7 +827,9 @@ std::string netchToVMess(std::vector<nodeInfo> &nodes) | ||||
| std::string netchToQuan(std::vector<nodeInfo> &nodes) | ||||
| { | ||||
|     rapidjson::Document json; | ||||
|     std::string remark, hostname, port, method; | ||||
|     std::string remark, hostname, port, method, password; | ||||
|     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; | ||||
| @@ -839,17 +840,19 @@ std::string netchToQuan(std::vector<nodeInfo> &nodes) | ||||
|         hostname = GetMember(json, "Hostname"); | ||||
|         port = std::__cxx11::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"; | ||||
|         password = GetMember(json, "Password"); | ||||
|  | ||||
|         switch(x.linkType) | ||||
|         { | ||||
|         case SPEEDTEST_MESSAGE_FOUNDVMESS: | ||||
|             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"; | ||||
|  | ||||
|             if(method == "auto") | ||||
|                 method = "chacha20-ietf-poly1305"; | ||||
|             proxyStr = remark + " = vmess, " + hostname + ", " + port + ", " + method + ", \"" + id + "\", group=" + x.group; | ||||
| @@ -857,11 +860,32 @@ std::string netchToQuan(std::vector<nodeInfo> &nodes) | ||||
|                 proxyStr += ", obfs=ws, obfs-path=" + path + ", obfs-header=\"Host: " + host + "\""; | ||||
|             if(tlssecure) | ||||
|                 proxyStr += ", over-tls=true, tls-host=" + host; | ||||
|             proxyStr = "vmess://" + urlsafe_base64_encode(proxyStr); | ||||
|             break; | ||||
|         case SPEEDTEST_MESSAGE_FOUNDSSR: | ||||
|             protocol = GetMember(json, "Protocol"); | ||||
|             protoparam = GetMember(json, "ProtocolParam"); | ||||
|             obfs = GetMember(json, "OBFS"); | ||||
|             obfsparam = GetMember(json, "OBFSParam"); | ||||
|  | ||||
|             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: | ||||
|             plugin = GetMember(json, "Plugin"); | ||||
|             pluginopts = GetMember(json, "PluginOption"); | ||||
|             proxyStr = "ss://" + urlsafe_base64_encode(method + ":" + password + "@" + hostname + ":" + port); | ||||
|             if(plugin.size() & pluginopts.size()) | ||||
|             { | ||||
|                 proxyStr += "/?plugin=" + UrlEncode(plugin + ";" +pluginopts); | ||||
|             } | ||||
|             proxyStr += "&group=" + urlsafe_base64_encode(x.group) + "#" + UrlEncode(remark); | ||||
|             break; | ||||
|         default: | ||||
|             continue; | ||||
|         } | ||||
|         allLinks += "vmess://" + urlsafe_base64_encode(proxyStr) + "\n"; | ||||
|         allLinks += proxyStr + "\n"; | ||||
|     } | ||||
|  | ||||
|     return allLinks; | ||||
| @@ -992,3 +1016,97 @@ std::string netchToSSD(std::vector<nodeInfo> &nodes, std::string &group) | ||||
|     writer.EndObject(); | ||||
|     return "ssd://" + base64_encode(sb.GetString()); | ||||
| } | ||||
|  | ||||
| std::string buildGistData(std::string name, std::string content) | ||||
| { | ||||
|     rapidjson::StringBuffer sb; | ||||
|     rapidjson::Writer<rapidjson::StringBuffer> writer(sb); | ||||
|     writer.StartObject(); | ||||
|     writer.Key("description"); | ||||
|     writer.String("subconverter"); | ||||
|     writer.Key("public"); | ||||
|     writer.Bool(false); | ||||
|     writer.Key("files"); | ||||
|     writer.StartObject(); | ||||
|     writer.Key(name.data()); | ||||
|     writer.StartObject(); | ||||
|     writer.Key("content"); | ||||
|     writer.String(content.data()); | ||||
|     writer.EndObject(); | ||||
|     writer.EndObject(); | ||||
|     writer.EndObject(); | ||||
|     return sb.GetString(); | ||||
| } | ||||
|  | ||||
| int uploadGist(std::string path, std::string content, bool writeManageURL) | ||||
| { | ||||
|     INIReader ini; | ||||
|     rapidjson::Document json; | ||||
|     std::string token, id, username, retData, url; | ||||
|     int retVal = 0; | ||||
|  | ||||
|     if(!fileExist("gistconf.ini")) | ||||
|     { | ||||
|         std::cerr<<"gistconf.ini not found. Skipping...\n"; | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     ini.ParseFile("gistconf.ini"); | ||||
|     if(ini.EnterSection("common") == 0) | ||||
|     { | ||||
|         token = ini.Get("token"); | ||||
|         if(!token.size()) | ||||
|         { | ||||
|             std::cerr<<"No token is provided. Skipping...\n"; | ||||
|             return -1; | ||||
|         } | ||||
|         id = ini.Get("id"); | ||||
|         username = ini.Get("username"); | ||||
|         if(!id.size()) | ||||
|         { | ||||
|             std::cerr<<"No gist id is provided. Creating new gist...\n"; | ||||
|             retVal = curlPost("https://api.github.com/gists", buildGistData(path, content), "", token, &retData); | ||||
|             if(retVal != 201) | ||||
|             { | ||||
|                 std::cerr<<"Create new Gist failed! Return data:\n"<<retData<<"\n"; | ||||
|                 return -1; | ||||
|             } | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|             url = ini.Get(path, "url"); | ||||
|             if(!url.size()) | ||||
|                 url = "https://gist.githubusercontent.com/" + username + "/" + id + "/raw/" + path; | ||||
|             std::cerr<<"Gist id provided. Modifying gist...\n"; | ||||
|             if(writeManageURL) | ||||
|                 content = "#!MANAGED-CONFIG "+ url + "\n" + content; | ||||
|             retVal = curlPatch("https://api.github.com/gists/" + id, buildGistData(path, content), "", token, &retData); | ||||
|             if(retVal != 200) | ||||
|             { | ||||
|                 std::cerr<<"Modify gist failed! Return data:\n"<<retData<<"\n"; | ||||
|                 return -1; | ||||
|             } | ||||
|         } | ||||
|         json.Parse(retData.data()); | ||||
|         GetMember(json, "id", id); | ||||
|         if(json.HasMember("owner")) | ||||
|             GetMember(json["owner"], "login", username); | ||||
|         std::cerr<<"Writing to Gist success!\nPath: "<<path<<"\nRaw URL: "<<url<<"\nGist owner: "<<username<<"\n"; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         std::cerr<<"gistconf.ini has incorrect format. Skipping...\n"; | ||||
|         return -1; | ||||
|     } | ||||
|     ini.EraseSection(); | ||||
|     ini.Set("token", token); | ||||
|     ini.Set("id", id); | ||||
|     ini.Set("username", username); | ||||
|  | ||||
|     ini.SetCurrentSection(path); | ||||
|     ini.EraseSection(); | ||||
|     ini.Set("url", url); | ||||
|  | ||||
|     ini.ToFile("gistconf.ini"); | ||||
|     return 0; | ||||
| } | ||||
|   | ||||
| @@ -18,5 +18,7 @@ std::string netchToVMess(std::vector<nodeInfo> &nodes); | ||||
| std::string netchToQuanX(std::vector<nodeInfo> &nodes); | ||||
| std::string netchToQuan(std::vector<nodeInfo> &nodes); | ||||
| std::string netchToSSD(std::vector<nodeInfo> &nodes, std::string &group); | ||||
| std::string buildGistData(std::string name, std::string content); | ||||
| int uploadGist(std::string path, std::string content, bool writeManageURL); | ||||
|  | ||||
| #endif // SUBEXPORT_H_INCLUDED | ||||
|   | ||||
							
								
								
									
										45
									
								
								webget.cpp
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								webget.cpp
									
									
									
									
									
								
							| @@ -31,7 +31,7 @@ std::string curlGet(std::string url, std::string proxy) | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 10L); | ||||
|     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_WRITEFUNCTION, writer); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &data); | ||||
| @@ -56,32 +56,42 @@ std::string webGet(std::string url, std::string proxy) | ||||
|     return curlGet(url, proxy); | ||||
| } | ||||
|  | ||||
| long curlPost(std::string url, std::string data, std::string proxy) | ||||
| int curlPost(std::string url, std::string data, std::string proxy, std::string auth_token, std::string *retData) | ||||
| { | ||||
|     CURL *curl_handle; | ||||
|     double retVal = 0.0; | ||||
|     struct curl_slist *list = NULL; | ||||
|     int retVal = 0; | ||||
|  | ||||
|     CURLcode res = curl_global_init(CURL_GLOBAL_ALL); | ||||
|  | ||||
|     curl_handle = curl_easy_init(); | ||||
|     list = curl_slist_append(list, "Content-Type: application/json;charset='utf-8'"); | ||||
|     if(auth_token.size()) | ||||
|         list = curl_slist_append(list, std::string("Authorization: token " + auth_token).data()); | ||||
|  | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_URL, url.data()); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_HEADER, 0L); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 0L); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L); | ||||
|     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()); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 10L); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 15L); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); | ||||
| 	curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writer); | ||||
| 	curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, retData); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, user_agent_str.data()); | ||||
|     if(proxy != "") | ||||
|         curl_easy_setopt(curl_handle, CURLOPT_PROXY, proxy.data()); | ||||
|  | ||||
|     res = curl_easy_perform(curl_handle); | ||||
|     curl_slist_free_all(list); | ||||
|  | ||||
|     if(res == CURLE_OK) | ||||
|     { | ||||
|         res = curl_easy_getinfo(curl_handle, CURLINFO_SPEED_UPLOAD, &retVal); | ||||
|         res = curl_easy_getinfo(curl_handle, CURLINFO_HTTP_CODE, &retVal); | ||||
|     } | ||||
|  | ||||
|     curl_easy_cleanup(curl_handle); | ||||
| @@ -89,30 +99,43 @@ long curlPost(std::string url, std::string data, std::string proxy) | ||||
|     return retVal; | ||||
| } | ||||
|  | ||||
| int curlPatch(std::string url, std::string data, std::string proxy) | ||||
| int curlPatch(std::string url, std::string data, std::string proxy, std::string auth_token, std::string *retData) | ||||
| { | ||||
|     CURL *curl_handle; | ||||
|     int retVal = 0; | ||||
|     struct curl_slist *list = NULL; | ||||
|  | ||||
|     CURLcode res = curl_global_init(CURL_GLOBAL_ALL); | ||||
|  | ||||
|     curl_handle = curl_easy_init(); | ||||
|  | ||||
|     list = curl_slist_append(list, "Content-Type: application/json;charset='utf-8'"); | ||||
|     if(auth_token.size()) | ||||
|         list = curl_slist_append(list, std::string("Authorization: token " + auth_token).data()); | ||||
|  | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_URL, url.data()); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_HEADER, 0L); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 0L); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1L); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L); | ||||
|     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()); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 10L); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, 15L); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L); | ||||
| 	curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, writer); | ||||
| 	curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, retData); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, list); | ||||
|     curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, user_agent_str.data()); | ||||
|     if(proxy != "") | ||||
|         curl_easy_setopt(curl_handle, CURLOPT_PROXY, proxy.data()); | ||||
|  | ||||
|     res = curl_easy_perform(curl_handle); | ||||
|     if(res != CURLE_OK) | ||||
|         retVal = -1; | ||||
|     curl_slist_free_all(list); | ||||
|     if(res == CURLE_OK) | ||||
|     { | ||||
|         res = curl_easy_getinfo(curl_handle, CURLINFO_HTTP_CODE, &retVal); | ||||
|     } | ||||
|  | ||||
|     curl_easy_cleanup(curl_handle); | ||||
|     curl_global_cleanup(); | ||||
|   | ||||
							
								
								
									
										3
									
								
								webget.h
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								webget.h
									
									
									
									
									
								
							| @@ -12,7 +12,8 @@ | ||||
| std::string webGet(std::string url, std::string proxy = ""); | ||||
| std::string httpGet(std::string host, std::string addr, std::string uri); | ||||
| std::string httpsGet(std::string host, std::string addr, std::string uri); | ||||
| long curlPost(std::string url, std::string data, std::string proxy); | ||||
| int curlPost(std::string url, std::string data, std::string proxy, std::string auth_token, std::string *retData); | ||||
| int curlPatch(std::string url, std::string data, std::string proxy, std::string auth_token, std::string *retData); | ||||
| std::string buildSocks5ProxyString(std::string addr, int port, std::string username, std::string password); | ||||
|  | ||||
| #endif // WEBGET_H_INCLUDED | ||||
|   | ||||
| @@ -1,14 +0,0 @@ | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| #include <yaml-cpp/yaml.h> | ||||
|  | ||||
| #include "misc.h" | ||||
|  | ||||
| YAML::Node vectorToNode(string_array data) | ||||
| { | ||||
|     YAML::Node node; | ||||
|     for(std::string &x : data) | ||||
|         node.push_back(x); | ||||
|     return node; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user