diff --git a/base/pref.ini b/base/pref.ini index b48ef7c..86c8a9e 100644 --- a/base/pref.ini +++ b/base/pref.ini @@ -35,6 +35,21 @@ proxy_subscription=NONE ;Append a proxy type string ([SS] [SSR] [VMess]) to node remark. append_proxy_type=false +[userinfo] +;Rules to extract stream data from node +;Format: full_match_regex|new_format_regex +;where new_format_regex should be like "total=$1&left=$2&used=$3" +stream_rule=^剩余流量:(.*?) (.*)$|total=$1&left=$2 +stream_rule=^Bandwidth: (.*?)/(.*)$|used=$1&total=$2 +stream_rule=^\[.*?\]剩余(.*?)@(?:.*)$|total=$1 + +;Rules to extract expire time data from node +;Format: full_match_regex|new_format_regex +;where new_format_regex should follow this example: yyyy:mm:dd:hh:mm:ss +time_rule=^过期时间:(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)$|$1:$2:$3:$4:$5:$6 +time_rule=^到期时间:(\d+)-(\d+)-(\d+)$|$1:$2:$3:0:0:0 +time_rule=^Smart Access expire: (\d+)/(\d+)/(\d+)$|$1:$2:$3:0:0:0 + [node_pref] udp_flag=false tcp_fast_open_flag=false diff --git a/src/ini_reader.h b/src/ini_reader.h index 0f84c3f..c4b3f23 100644 --- a/src/ini_reader.h +++ b/src/ini_reader.h @@ -9,12 +9,15 @@ #include "misc.h" -#define INIREADER_EXCEPTION_NONE 0 -#define INIREADER_EXCEPTION_EMPTY -1 -#define INIREADER_EXCEPTION_DUPLICATE -2 -#define INIREADER_EXCEPTION_OUTOFBOUND -3 -#define INIREADER_EXCEPTION_NOTEXIST -4 -#define INIREADER_EXCEPTION_NOTPARSED -5 +enum +{ + INIREADER_EXCEPTION_EMPTY = -5, + INIREADER_EXCEPTION_DUPLICATE, + INIREADER_EXCEPTION_OUTOFBOUND, + INIREADER_EXCEPTION_NOTEXIST, + INIREADER_EXCEPTION_NOTPARSED, + INIREADER_EXCEPTION_NONE +}; #define __SAVE_ERROR_AND_RETURN(x) {last_error = x; return last_error;} @@ -80,6 +83,11 @@ public: */ bool allow_dup_section_titles = false; + /** + * @brief Keep an empty section while parsing + */ + bool keep_empty_section = true; + /** * @brief Initialize the reader. */ @@ -224,23 +232,24 @@ public: thisSection = strLine.substr(1, lineSize - 2); //save section title inExcludedSection = chkIgnore(thisSection); //check if this section is excluded - if(curSection.size() && itemGroup.size()) //just finished reading a section + if(curSection.size() && (keep_empty_section || itemGroup.size())) //just finished reading a section { if(ini_content.count(curSection)) //a section with the same name has been inserted { - if(allow_dup_section_titles) + if(allow_dup_section_titles || !ini_content.at(curSection).size()) { eraseElements(existItemGroup); existItemGroup = ini_content.at(curSection); //get the old items for(auto &x : existItemGroup) itemGroup.emplace(x); //insert them all into new section - ini_content.erase(curSection); //remove the old section } - else + else if(ini_content.at(curSection).size()) __SAVE_ERROR_AND_RETURN(INIREADER_EXCEPTION_DUPLICATE); //not allowed, stop + ini_content.erase(curSection); //remove the old section } + else if(itemGroup.size()) + read_sections.push_back(curSection); //add to read sections list ini_content.emplace(curSection, itemGroup); //insert previous section to content map - read_sections.push_back(curSection); //add to read sections list if(std::count(section_order.cbegin(), section_order.cend(), curSection) == 0) section_order.emplace_back(curSection); } @@ -255,7 +264,7 @@ public: if(include_sections.size() && include_sections == read_sections) //all included sections has been read break; //exit now } - if(curSection.size() && itemGroup.size()) //final section + if(curSection.size() && (keep_empty_section || itemGroup.size())) //final section { if(ini_content.count(curSection)) //a section with the same name has been inserted { @@ -265,13 +274,14 @@ public: existItemGroup = ini_content.at(curSection); //get the old items for(auto &x : existItemGroup) itemGroup.emplace(x); //insert them all into new section - ini_content.erase(curSection); //remove the old section } - else + else if(ini_content.at(curSection).size()) __SAVE_ERROR_AND_RETURN(INIREADER_EXCEPTION_DUPLICATE); //not allowed, stop + ini_content.erase(curSection); //remove the old section } + else if(itemGroup.size()) + read_sections.emplace_back(curSection); //add to read sections list ini_content.emplace(curSection, itemGroup); //insert this section to content map - read_sections.emplace_back(curSection); //add to read sections list if(std::count(section_order.cbegin(), section_order.cend(), curSection) == 0) section_order.emplace_back(curSection); } @@ -803,7 +813,7 @@ public: content += "\n"; } - return content.substr(0, content.size() - 2); + return content.erase(content.size() - 2); } /** diff --git a/src/main.cpp b/src/main.cpp index 814e2a5..a044139 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,7 +20,7 @@ //common settings std::string pref_path = "pref.ini"; bool generator_mode = false; -string_array def_exclude_remarks, def_include_remarks, rulesets; +string_array def_exclude_remarks, def_include_remarks, rulesets, stream_rules, time_rules; std::vector ruleset_content_array; 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; @@ -151,7 +151,7 @@ void readConf() eraseElements(def_include_remarks); eraseElements(clash_extra_group); eraseElements(rulesets); - string_array emojis_temp, renames_temp; + string_array tempArray; ini.EnterSection("common"); if(ini.ItemExist("api_mode")) @@ -201,8 +201,26 @@ void readConf() filter_deprecated = ini.GetBool("filter_deprecated_nodes"); if(ini.ItemPrefixExist("rename_node")) { - ini.GetAll("rename_node", renames_temp); - safe_set_renames(renames_temp); + ini.GetAll("rename_node", tempArray); + safe_set_renames(tempArray); + eraseElements(tempArray); + } + } + + if(ini.SectionExist("userinfo")) + { + ini.EnterSection("userinfo"); + if(ini.ItemPrefixExist("stream_rule")) + { + ini.GetAll("stream_rule", tempArray); + safe_set_streams(tempArray); + eraseElements(tempArray); + } + if(ini.ItemPrefixExist("time_rule")) + { + ini.GetAll("time_rule", tempArray); + safe_set_times(tempArray); + eraseElements(tempArray); } } @@ -219,8 +237,9 @@ void readConf() remove_old_emoji = ini.GetBool("remove_old_emoji"); if(ini.ItemPrefixExist("rule")) { - ini.GetAll("rule", emojis_temp); - safe_set_emojis(emojis_temp); + ini.GetAll("rule", tempArray); + safe_set_emojis(tempArray); + eraseElements(tempArray); } ini.EnterSection("ruleset"); @@ -284,6 +303,7 @@ int loadExternalConfig(std::string &path, ExternalConfig &ext, std::string proxy INIReader ini; ini.store_isolated_line = true; + ini.keep_empty_section = false; ini.SetIsolatedItemsSection("custom"); if(ini.Parse(base_content) != INIREADER_EXCEPTION_NONE) { @@ -361,6 +381,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS) std::string groups = urlsafe_base64_decode(getUrlArg(argument, "groups")), ruleset = urlsafe_base64_decode(getUrlArg(argument, "ruleset")), config = UrlDecode(getUrlArg(argument, "config")); std::vector rca; extra_settings ext; + std::string subInfo; if(std::find(regex_blacklist.cbegin(), regex_blacklist.cend(), include) != regex_blacklist.cend() || std::find(regex_blacklist.cbegin(), regex_blacklist.cend(), exclude) != regex_blacklist.cend()) return "Invalid request!"; @@ -512,11 +533,12 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS) custom_group = group; //start parsing urls + string_array stream_temp = safe_get_streams(), time_temp = safe_get_times(); for(std::string &x : urls) { x = trim(x); std::cerr<<"Fetching node data from url '"< nodes; string_array extra_group, extra_ruleset, include_remarks, exclude_remarks; std::vector rca; + std::string subInfo; if(!url.size()) url = default_url; @@ -707,9 +733,22 @@ std::string simpleToClashR(RESPONSE_CALLBACK_ARGS) include_remarks = def_include_remarks; exclude_remarks = def_exclude_remarks; - std::cerr<<"Fetching node data from url '"< guarded_mutex; -std::mutex on_configuring, on_emoji, on_rename; +std::mutex on_configuring, on_emoji, on_rename, on_stream, on_time; extern string_array emojis, renames; +extern string_array stream_rules, time_rules; string_array safe_get_emojis() { @@ -20,6 +21,18 @@ string_array safe_get_renames() return renames; } +string_array safe_get_streams() +{ + guarded_mutex guard(on_stream); + return stream_rules; +} + +string_array safe_get_times() +{ + guarded_mutex guard(on_time); + return time_rules; +} + void safe_set_emojis(string_array &data) { guarded_mutex guard(on_emoji); @@ -31,3 +44,15 @@ void safe_set_renames(string_array &data) guarded_mutex guard(on_rename); renames.swap(data); } + +void safe_set_streams(string_array &data) +{ + guarded_mutex guard(on_stream); + stream_rules.swap(data); +} + +void safe_set_times(string_array &data) +{ + guarded_mutex guard(on_time); + time_rules.swap(data); +} diff --git a/src/multithread.h b/src/multithread.h index 71d033b..26e884b 100644 --- a/src/multithread.h +++ b/src/multithread.h @@ -6,12 +6,13 @@ typedef std::lock_guard guarded_mutex; -void try_config_lock(); -void try_emoji_lock(); -void try_rename_lock(); string_array safe_get_emojis(); string_array safe_get_renames(); +string_array safe_get_streams(); +string_array safe_get_times(); void safe_set_emojis(string_array &data); void safe_set_renames(string_array &data); +void safe_set_streams(string_array &data); +void safe_set_times(string_array &data); #endif // MULTITHREAD_H_INCLUDED diff --git a/src/nodemanip.cpp b/src/nodemanip.cpp index bb5f3a4..50b5d5f 100644 --- a/src/nodemanip.cpp +++ b/src/nodemanip.cpp @@ -22,12 +22,12 @@ void copyNodes(std::vector &source, std::vector &dest) } } -int addNodes(std::string link, std::vector &allNodes, int groupID, std::string proxy, string_array &exclude_remarks, string_array &include_remarks) +int addNodes(std::string link, std::vector &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) { int linkType = -1; std::vector nodes; nodeInfo node; - std::string strSub; + std::string strSub, extra_headers; link = replace_all_distinct(link, "\"", ""); writeLog(LOG_TYPE_INFO, "Received Link."); @@ -52,7 +52,7 @@ int addNodes(std::string link, std::vector &allNodes, int groupID, std writeLog(LOG_TYPE_INFO, "Downloading subscription data..."); if(strFind(link, "surge:///install-config")) //surge config link link = UrlDecode(getUrlArg(link, "url")); - strSub = webGet(link, proxy); + strSub = webGet(link, proxy, extra_headers); /* if(strSub.size() == 0) { @@ -70,11 +70,14 @@ int addNodes(std::string link, std::vector &allNodes, int groupID, std if(strSub.size()) { writeLog(LOG_TYPE_INFO, "Parsing subscription data..."); - if(explodeConfContent(strSub, override_conf_port, socksport, ss_libev, ssr_libev, nodes, exclude_remarks, include_remarks) == SPEEDTEST_ERROR_UNRECOGFILE) + if(explodeConfContent(strSub, override_conf_port, socksport, ss_libev, ssr_libev, nodes) == SPEEDTEST_ERROR_UNRECOGFILE) { writeLog(LOG_TYPE_ERROR, "Invalid subscription!"); return -1; } + if(!getSubInfoFromHeader(extra_headers, subInfo)) + getSubInfoFromNodes(nodes, stream_rules, time_rules, subInfo); + filterNodes(nodes, exclude_remarks, include_remarks, groupID); for(nodeInfo &x : nodes) x.groupID = groupID; copyNodes(nodes, allNodes); @@ -89,11 +92,13 @@ int addNodes(std::string link, std::vector &allNodes, int groupID, std if(api_mode) return -1; writeLog(LOG_TYPE_INFO, "Parsing configuration file data..."); - if(explodeConf(link, override_conf_port, socksport, ss_libev, ssr_libev, nodes, exclude_remarks, include_remarks) == SPEEDTEST_ERROR_UNRECOGFILE) + if(explodeConf(link, override_conf_port, socksport, ss_libev, ssr_libev, nodes) == SPEEDTEST_ERROR_UNRECOGFILE) { writeLog(LOG_TYPE_ERROR, "Invalid configuration file!"); return -1; } + getSubInfoFromNodes(nodes, stream_rules, time_rules, subInfo); + filterNodes(nodes, exclude_remarks, include_remarks, groupID); for(nodeInfo &x : nodes) x.groupID = groupID; copyNodes(nodes, allNodes); diff --git a/src/nodemanip.h b/src/nodemanip.h index 6d7f5ad..bd21ae5 100644 --- a/src/nodemanip.h +++ b/src/nodemanip.h @@ -6,6 +6,6 @@ #include "nodeinfo.h" -int addNodes(std::string link, std::vector &allNodes, int groupID, std::string proxy, string_array &exclude_remarks, string_array &include_remarks); +int addNodes(std::string link, std::vector &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); #endif // NODEMANIP_H_INCLUDED diff --git a/src/printout.h b/src/printout.h index f77c548..2655811 100644 --- a/src/printout.h +++ b/src/printout.h @@ -8,57 +8,63 @@ #include "misc.h" #include "nodeinfo.h" -#define SPEEDTEST_ERROR_NONE 0 -#define SPEEDTEST_ERROR_UNDEFINED 100 -#define SPEEDTEST_ERROR_WSAERR 99 -#define SPEEDTEST_ERROR_SOCKETERR 98 -#define SPEEDTEST_ERROR_NORECOGLINK 97 -#define SPEEDTEST_ERROR_NOCONNECTION 96 -#define SPEEDTEST_ERROR_INVALIDSUB 95 -#define SPEEDTEST_ERROR_NONODES 94 -#define SPEEDTEST_ERROR_NORESOLVE 93 -#define SPEEDTEST_ERROR_RETEST 92 -#define SPEEDTEST_ERROR_NOSPEED 91 -#define SPEEDTEST_ERROR_UNRECOGFILE 90 -#define SPEEDTEST_ERROR_SUBFETCHERR 89 -#define SPEEDTEST_ERROR_GEOIPERR 88 +enum +{ + SPEEDTEST_ERROR_UNDEFINED = -13, + SPEEDTEST_ERROR_WSAERR, + SPEEDTEST_ERROR_SOCKETERR, + SPEEDTEST_ERROR_NORECOGLINK, + SPEEDTEST_ERROR_NOCONNECTION, + SPEEDTEST_ERROR_INVALIDSUB, + SPEEDTEST_ERROR_NONODES, + SPEEDTEST_ERROR_NORESOLVE, + SPEEDTEST_ERROR_RETEST, + SPEEDTEST_ERROR_NOSPEED, + SPEEDTEST_ERROR_UNRECOGFILE, + SPEEDTEST_ERROR_SUBFETCHERR, + SPEEDTEST_ERROR_GEOIPERR, + SPEEDTEST_ERROR_NONE +}; -#define SPEEDTEST_MESSAGE_WELCOME 0 -#define SPEEDTEST_MESSAGE_FOUNDVMESS 1 -#define SPEEDTEST_MESSAGE_FOUNDSS 2 -#define SPEEDTEST_MESSAGE_FOUNDSSR 3 -#define SPEEDTEST_MESSAGE_FOUNDSUB 4 -#define SPEEDTEST_MESSAGE_GOTSERVER 5 -#define SPEEDTEST_MESSAGE_STARTPING 6 -#define SPEEDTEST_MESSAGE_GOTPING 7 -#define SPEEDTEST_MESSAGE_STARTSPEED 8 -#define SPEEDTEST_MESSAGE_GOTSPEED 9 -#define SPEEDTEST_MESSAGE_GOTRESULT 10 -#define SPEEDTEST_MESSAGE_TRAFFIC 11 -#define SPEEDTEST_MESSAGE_PICSAVING 12 -#define SPEEDTEST_MESSAGE_PICSAVED 13 -#define SPEEDTEST_MESSAGE_GROUP 14 -#define SPEEDTEST_MESSAGE_FETCHSUB 15 -#define SPEEDTEST_MESSAGE_BEGIN 16 -#define SPEEDTEST_MESSAGE_FOUNDLOCAL 17 -#define SPEEDTEST_MESSAGE_PARSING 18 -#define SPEEDTEST_MESSAGE_FOUNDUPD 19 -#define SPEEDTEST_MESSAGE_PICDATA 20 -#define SPEEDTEST_MESSAGE_STARTGPING 21 -#define SPEEDTEST_MESSAGE_GOTGPING 22 -#define SPEEDTEST_MESSAGE_FOUNDSOCKS 23 -#define SPEEDTEST_MESSAGE_STARTGEOIP 24 -#define SPEEDTEST_MESSAGE_GOTGEOIP 25 -#define SPEEDTEST_MESSAGE_FOUNDSSCONF 26 -#define SPEEDTEST_MESSAGE_MULTILINK 27 -#define SPEEDTEST_MESSAGE_PICSAVINGMULTI 28 -#define SPEEDTEST_MESSAGE_PICSAVEDMULTI 29 -#define SPEEDTEST_MESSAGE_STARTUPD 30 -#define SPEEDTEST_MESSAGE_GOTUPD 31 -#define SPEEDTEST_MESSAGE_FOUNDSSTAP 32 -#define SPEEDTEST_MESSAGE_FOUNDNETCH 33 -#define SPEEDTEST_MESSAGE_FOUNDHTTP 34 -#define SPEEDTEST_MESSAGE_EOF 49 +enum +{ + SPEEDTEST_MESSAGE_WELCOME = 1, + SPEEDTEST_MESSAGE_FOUNDVMESS, + SPEEDTEST_MESSAGE_FOUNDSS, + SPEEDTEST_MESSAGE_FOUNDSSR, + SPEEDTEST_MESSAGE_FOUNDSUB, + SPEEDTEST_MESSAGE_GOTSERVER, + SPEEDTEST_MESSAGE_STARTPING, + SPEEDTEST_MESSAGE_GOTPING, + SPEEDTEST_MESSAGE_STARTSPEED, + SPEEDTEST_MESSAGE_GOTSPEED, + SPEEDTEST_MESSAGE_GOTRESULT, + SPEEDTEST_MESSAGE_TRAFFIC, + SPEEDTEST_MESSAGE_PICSAVING, + SPEEDTEST_MESSAGE_PICSAVED, + SPEEDTEST_MESSAGE_GROUP, + SPEEDTEST_MESSAGE_FETCHSUB, + SPEEDTEST_MESSAGE_BEGIN, + SPEEDTEST_MESSAGE_FOUNDLOCAL, + SPEEDTEST_MESSAGE_PARSING, + SPEEDTEST_MESSAGE_FOUNDUPD, + SPEEDTEST_MESSAGE_PICDATA, + SPEEDTEST_MESSAGE_STARTGPING, + SPEEDTEST_MESSAGE_GOTGPING, + SPEEDTEST_MESSAGE_FOUNDSOCKS, + SPEEDTEST_MESSAGE_STARTGEOIP, + SPEEDTEST_MESSAGE_GOTGEOIP, + SPEEDTEST_MESSAGE_FOUNDSSCONF, + SPEEDTEST_MESSAGE_MULTILINK, + SPEEDTEST_MESSAGE_PICSAVINGMULTI, + SPEEDTEST_MESSAGE_PICSAVEDMULTI, + SPEEDTEST_MESSAGE_STARTUPD, + SPEEDTEST_MESSAGE_GOTUPD, + SPEEDTEST_MESSAGE_FOUNDSSTAP, + SPEEDTEST_MESSAGE_FOUNDNETCH, + SPEEDTEST_MESSAGE_FOUNDHTTP, + SPEEDTEST_MESSAGE_EOF +}; #define SS_DEFAULT_GROUP "SSProvider" #define SSR_DEFAULT_GROUP "SSRProvider" diff --git a/src/speedtestutil.cpp b/src/speedtestutil.cpp index 9ce32ab..b02e257 100644 --- a/src/speedtestutil.cpp +++ b/src/speedtestutil.cpp @@ -1,7 +1,10 @@ #include #include +#include + #include +#include #include "misc.h" #include "printout.h" @@ -997,6 +1000,7 @@ bool explodeSurge(std::string surge, std::string custom_port, int local_port, st */ ini.store_isolated_line = true; + ini.keep_empty_section = false; ini.SetIsolatedItemsSection("Proxy"); ini.IncludeSection("Proxy"); ini.Parse(surge); @@ -1388,7 +1392,7 @@ bool chkIgnore(const nodeInfo &node, string_array &exclude_remarks, string_array return excluded || !included; } -int explodeConf(std::string filepath, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector &nodes, string_array &exclude_remarks, string_array &include_remarks) +int explodeConf(std::string filepath, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector &nodes) { std::ifstream infile; std::stringstream contentstrm; @@ -1397,12 +1401,11 @@ int explodeConf(std::string filepath, std::string custom_port, int local_port, b contentstrm << infile.rdbuf(); infile.close(); - return explodeConfContent(contentstrm.str(), custom_port, local_port, sslibev, ssrlibev, nodes, exclude_remarks, include_remarks); + return explodeConfContent(contentstrm.str(), custom_port, local_port, sslibev, ssrlibev, nodes); } -int explodeConfContent(std::string content, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector &nodes, string_array &exclude_remarks, string_array &include_remarks) +int explodeConfContent(std::string content, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector &nodes) { - unsigned int index = 0; int filetype = -1; std::vector::iterator iter; @@ -1443,32 +1446,13 @@ int explodeConfContent(std::string content, std::string custom_port, int local_p break; default: //try to parse as a local subscription - explodeSub(content, sslibev, ssrlibev, custom_port, local_port, nodes, exclude_remarks, include_remarks); + explodeSub(content, sslibev, ssrlibev, custom_port, local_port, nodes); } if(nodes.size() == 0) return SPEEDTEST_ERROR_UNRECOGFILE; else return SPEEDTEST_ERROR_NONE; - - iter = nodes.begin(); - while(iter != nodes.end()) - { - if(chkIgnore(*iter, exclude_remarks, include_remarks)) - { - writeLog(LOG_TYPE_INFO, "Node " + iter->group + " - " + iter->remarks + " has been ignored and will not be added."); - nodes.erase(iter); - } - else - { - writeLog(LOG_TYPE_INFO, "Node " + iter->group + " - " + iter->remarks + " has been added."); - iter->id = index; - ++index; - ++iter; - } - } - - return SPEEDTEST_ERROR_NONE; } void explode(std::string link, bool sslibev, bool ssrlibev, std::string custom_port, int local_port, nodeInfo &node) @@ -1485,7 +1469,7 @@ void explode(std::string link, bool sslibev, bool ssrlibev, std::string custom_p explodeNetch(link, sslibev, ssrlibev, custom_port, local_port, node); } -void explodeSub(std::string sub, bool sslibev, bool ssrlibev, std::string custom_port, int local_port, std::vector &nodes, string_array &exclude_remarks, string_array &include_remarks) +void explodeSub(std::string sub, bool sslibev, bool ssrlibev, std::string custom_port, int local_port, std::vector &nodes) { std::stringstream strstream; std::string strLink; @@ -1540,7 +1524,10 @@ void explodeSub(std::string sub, bool sslibev, bool ssrlibev, std::string custom nodes.push_back(node); } } +} +void filterNodes(std::vector &nodes, string_array &exclude_remarks, string_array &include_remarks, int groupID) +{ int index = 0; std::vector::iterator iter = nodes.begin(); while(iter != nodes.end()) @@ -1554,8 +1541,168 @@ void explodeSub(std::string sub, bool sslibev, bool ssrlibev, std::string custom { writeLog(LOG_TYPE_INFO, "Node " + iter->group + " - " + iter->remarks + " has been added."); iter->id = index; + iter->groupID = groupID; ++index; ++iter; } } } + +static inline unsigned long long streamToInt(std::string stream) +{ + if(!stream.size()) + return 0; + double streamval = 1.0; + if(stream.find("GB") != std::string::npos) + streamval = std::pow(1024, 3) * stof(stream.substr(0, stream.size() - 2)); + else if(stream.find("TB") != std::string::npos) + streamval = std::pow(1024, 4) * stof(stream.substr(0, stream.size() - 2)); + else if(stream.find("PB") != std::string::npos) + streamval = std::pow(1024, 5) * stof(stream.substr(0, stream.size() - 2)); + else if(stream.find("MB") != std::string::npos) + streamval = std::pow(1024, 2) * stof(stream.substr(0, stream.size() - 2)); + else if(stream.find("KB") != std::string::npos) + streamval = 1024.0 * stof(stream.substr(0, stream.size() - 2)); + else if(stream.find("B") != std::string::npos) + streamval = 1.0 * stof(stream.substr(0, stream.size() - 1)); + return (unsigned long long)streamval; +} + +static inline double percentToDouble(std::string percent) +{ + return stof(percent.erase(percent.size() - 1)) / 100.0; +} + +time_t dateStringToTimestamp(std::string date) +{ + std::vector date_array = split(date, ":"); + if(date_array.size() != 6) + return 0; + time_t rawtime; + struct tm *expire_time; + time(&rawtime); + expire_time = localtime(&rawtime); + expire_time->tm_year = to_int(date_array[0], 1900) - 1900; + expire_time->tm_mon = to_int(date_array[1], 1) - 1; + expire_time->tm_mday = to_int(date_array[2]); + expire_time->tm_hour = to_int(date_array[3]); + expire_time->tm_min = to_int(date_array[4]); + expire_time->tm_sec = to_int(date_array[5]); + return mktime(expire_time); +} + +bool getSubInfoFromHeader(std::string &header, std::string &result) +{ + bool ret = false; + pcrecpp::RE reg("^(?i:Subscription-UserInfo): (.*?)\\r$", pcrecpp::MULTILINE()); + try + { + ret = reg.PartialMatch(header, &result); + } + catch (std::exception &e) + { + //ignore + } + return ret; +} + +bool getSubInfoFromNodes(std::vector &nodes, string_array &stream_rules, string_array &time_rules, std::string &result) +{ + std::string remarks, pattern, target, stream_info, time_info; + string_array vArray; + + for(nodeInfo &x : nodes) + { + remarks = x.remarks; + if(!stream_info.size()) + { + for(std::string &y : stream_rules) + { + vArray = split(y, "|"); + if(vArray.size() != 2) + continue; + pattern = vArray[0]; + target = vArray[1]; + pcrecpp::RE reg(pattern, pcrecpp::UTF8()); + if(reg.FullMatch(remarks)) + { + if(target.find("$") != target.npos) + target = replace_all_distinct(target, "$", "\\"); + if(reg.GlobalReplace(target, &remarks)) + stream_info = remarks; + } + else + continue; + } + } + + remarks = x.remarks; + if(!time_info.size()) + { + for(std::string &y : time_rules) + { + vArray = split(y, "|"); + if(vArray.size() != 2) + continue; + pattern = vArray[0]; + target = vArray[1]; + pcrecpp::RE reg(pattern, pcrecpp::UTF8()); + if(reg.FullMatch(remarks)) + { + if(target.find("$") != target.npos) + target = replace_all_distinct(target, "$", "\\"); + if(reg.GlobalReplace(target, &remarks)) + time_info = remarks; + } + else + continue; + } + } + + if(stream_info.size() && time_info.size()) + break; + } + + if(!stream_info.size() && !time_info.size()) + return false; + + //calculate how much stream left + unsigned long long total = 0, left = 0, used = 0, expire = 0; + std::string total_str = getUrlArg(stream_info, "total"), left_str = getUrlArg(stream_info, "left"), used_str = getUrlArg(stream_info, "used"); + if(strFind(total_str, "%")) + { + if(used_str.size()) + { + used = streamToInt(used_str); + total = used / (1 - percentToDouble(total_str)); + } + else if(left_str.size()) + { + left = streamToInt(left_str); + total = left / percentToDouble(total_str); + used = total - left; + } + } + else + { + total = streamToInt(total_str); + if(used_str.size()) + { + used = streamToInt(used_str); + } + else if(left_str.size()) + { + left = streamToInt(left_str); + used = total - left; + } + } + + result = "upload=0; download=" + std::to_string(used) + "; total=" + std::to_string(total) + ";"; + + //calculate expire time + expire = dateStringToTimestamp(time_info); + if(expire != 0) + result += " expire=" + std::to_string(expire) + ";"; + + return true; +} diff --git a/src/speedtestutil.h b/src/speedtestutil.h index 5ee8449..cfa089d 100644 --- a/src/speedtestutil.h +++ b/src/speedtestutil.h @@ -20,9 +20,12 @@ void explodeShadowrocket(std::string kit, std::string custom_port, int local_por void explodeKitsunebi(std::string kit, std::string custom_port, int local_port, nodeInfo &node); void explode(std::string link, bool sslibev, bool ssrlibev, std::string custom_port, int local_port, nodeInfo &node); void explodeSSD(std::string link, bool libev, std::string custom_port, int local_port, std::vector &nodes); -void explodeSub(std::string sub, bool sslibev, bool ssrlibev, std::string custom_port, int local_port, std::vector &nodes, string_array &exclude_remarks, string_array &include_remarks); -int explodeConf(std::string filepath, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector &nodes, string_array &exclude_remarks, string_array &include_remarks); -int explodeConfContent(std::string content, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector &nodes, string_array &exclude_remarks, string_array &include_remarks); +void explodeSub(std::string sub, bool sslibev, bool ssrlibev, std::string custom_port, int local_port, std::vector &nodes); +int explodeConf(std::string filepath, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector &nodes); +int explodeConfContent(std::string content, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector &nodes); bool chkIgnore(const nodeInfo &node, string_array &exclude_remarks, string_array &include_remarks); +void filterNodes(std::vector &nodes, string_array &exclude_remarks, string_array &include_remarks, int groupID); +bool getSubInfoFromHeader(std::string &header, std::string &result); +bool getSubInfoFromNodes(std::vector &nodes, string_array &stream_rules, string_array &time_rules, std::string &result); #endif // SPEEDTESTUTIL_H_INCLUDED