From 2b649ec25d92c870cb3a25ea9e2645dab680c59a Mon Sep 17 00:00:00 2001 From: Tindy X <49061470+tindy2013@users.noreply.github.com> Date: Fri, 15 Nov 2019 14:10:27 +0800 Subject: [PATCH] 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. --- gistconf.ini | 3 + ini_reader.h | 2 +- main.cpp | 380 +++++++++++++++++++--------------------------- misc.cpp | 2 +- pref.ini | 7 + speedtestutil.cpp | 23 +-- subexport.cpp | 144 ++++++++++++++++-- subexport.h | 2 + webget.cpp | 45 ++++-- webget.h | 3 +- yamlcpp_extra.h | 14 -- 11 files changed, 351 insertions(+), 274 deletions(-) create mode 100644 gistconf.ini delete mode 100644 yamlcpp_extra.h diff --git a/gistconf.ini b/gistconf.ini new file mode 100644 index 0000000..03c68b6 --- /dev/null +++ b/gistconf.ini @@ -0,0 +1,3 @@ +[common] +;uncomment the following line and enter your token to enable upload function +;token = your_personal_token_here diff --git a/ini_reader.h b/ini_reader.h index 580efa3..d531f6c 100644 --- a/ini_reader.h +++ b/ini_reader.h @@ -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)) diff --git a/main.cpp b/main.cpp index 0868ecf..2e16760 100644 --- a/main.cpp +++ b/main.cpp @@ -18,9 +18,9 @@ //common settings string_array def_exclude_remarks, def_include_remarks, rulesets; std::vector 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..."< 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 '"< 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 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 '"< 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 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 '"< 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 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 '"< 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 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 '"< 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 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 '"< 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 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 '"< 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 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 '"< 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 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 '"< 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 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 '"<> 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); diff --git a/subexport.cpp b/subexport.cpp index f7bc716..cd8dac1 100644 --- a/subexport.cpp +++ b/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 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< &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 &nodes) std::string netchToQuan(std::vector &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 &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 &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 &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 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"< &nodes); std::string netchToQuanX(std::vector &nodes); std::string netchToQuan(std::vector &nodes); std::string netchToSSD(std::vector &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 diff --git a/webget.cpp b/webget.cpp index 11a1a02..cac204c 100644 --- a/webget.cpp +++ b/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(); diff --git a/webget.h b/webget.h index 1fa659b..dcfd664 100644 --- a/webget.h +++ b/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 diff --git a/yamlcpp_extra.h b/yamlcpp_extra.h deleted file mode 100644 index 7162ee8..0000000 --- a/yamlcpp_extra.h +++ /dev/null @@ -1,14 +0,0 @@ -#include -#include - -#include - -#include "misc.h" - -YAML::Node vectorToNode(string_array data) -{ - YAML::Node node; - for(std::string &x : data) - node.push_back(x); - return node; -}