Enhancements

Add Subscription-UserInfo header to exporter, supports extracting from original headers and from info nodes.
Optimize codes.
This commit is contained in:
Tindy X
2020-01-30 17:14:00 +08:00
parent 7c546e9606
commit 4492fcd806
10 changed files with 365 additions and 114 deletions

View File

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

View File

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

View File

@@ -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> 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<ruleset_content> 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 '"<<x<<"'."<<std::endl;
if(addNodes(x, nodes, groupID, proxy, exclude_remarks, include_remarks) == -1)
if(addNodes(x, nodes, groupID, proxy, exclude_remarks, include_remarks, stream_temp, time_temp, subInfo) == -1)
{
*status_code = 400;
return std::string("The following link doesn't contain any valid node info: " + x);
@@ -530,6 +552,9 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
return "No nodes were found!";
}
if(subInfo.size() && groupID == 1)
extra_headers.emplace("Subscription-UserInfo", subInfo);
std::cerr<<"Generate target: ";
if(target == "clash" || target == "clashr")
{
@@ -672,6 +697,7 @@ std::string simpleToClashR(RESPONSE_CALLBACK_ARGS)
std::vector<nodeInfo> nodes;
string_array extra_group, extra_ruleset, include_remarks, exclude_remarks;
std::vector<ruleset_content> 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 '"<<url<<"'.\n";
addNodes(url, nodes, 0, proxy, exclude_remarks, include_remarks);
//start parsing urls
int groupID = 0;
string_array dummy;
string_array urls = split(url, "|");
for(std::string &x : urls)
{
x = trim(x);
std::cerr<<"Fetching node data from url '"<<x<<"'."<<std::endl;
if(addNodes(x, nodes, groupID, proxy, exclude_remarks, include_remarks, dummy, dummy, subInfo) == -1)
{
*status_code = 400;
return std::string("The following link doesn't contain any valid node info: " + x);
}
groupID++;
}
//exit if found nothing
if(!nodes.size())
{
*status_code = 400;

View File

@@ -4,9 +4,10 @@
//safety lock for multi-thread
typedef std::lock_guard<std::mutex> 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);
}

View File

@@ -6,12 +6,13 @@
typedef std::lock_guard<std::mutex> 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

View File

@@ -22,12 +22,12 @@ void copyNodes(std::vector<nodeInfo> &source, std::vector<nodeInfo> &dest)
}
}
int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std::string proxy, string_array &exclude_remarks, string_array &include_remarks)
int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std::string proxy, string_array &exclude_remarks, string_array &include_remarks, string_array &stream_rules, string_array &time_rules, std::string &subInfo)
{
int linkType = -1;
std::vector<nodeInfo> 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<nodeInfo> &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<nodeInfo> &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<nodeInfo> &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);

View File

@@ -6,6 +6,6 @@
#include "nodeinfo.h"
int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std::string proxy, string_array &exclude_remarks, string_array &include_remarks);
int addNodes(std::string link, std::vector<nodeInfo> &allNodes, int groupID, std::string proxy, string_array &exclude_remarks, string_array &include_remarks, string_array &stream_rules, string_array &time_rules, std::string &subInfo);
#endif // NODEMANIP_H_INCLUDED

View File

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

View File

@@ -1,7 +1,10 @@
#include <fstream>
#include <algorithm>
#include <time.h>
#include <rapidjson/document.h>
#include <pcrecpp.h>
#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<nodeInfo> &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<nodeInfo> &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<nodeInfo> &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<nodeInfo> &nodes)
{
unsigned int index = 0;
int filetype = -1;
std::vector<nodeInfo>::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<nodeInfo> &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<nodeInfo> &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<nodeInfo> &nodes, string_array &exclude_remarks, string_array &include_remarks, int groupID)
{
int index = 0;
std::vector<nodeInfo>::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<std::string> 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<nodeInfo> &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;
}

View File

@@ -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<nodeInfo> &nodes);
void explodeSub(std::string sub, bool sslibev, bool ssrlibev, std::string custom_port, int local_port, std::vector<nodeInfo> &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<nodeInfo> &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<nodeInfo> &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<nodeInfo> &nodes);
int explodeConf(std::string filepath, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector<nodeInfo> &nodes);
int explodeConfContent(std::string content, std::string custom_port, int local_port, bool sslibev, bool ssrlibev, std::vector<nodeInfo> &nodes);
bool chkIgnore(const nodeInfo &node, string_array &exclude_remarks, string_array &include_remarks);
void filterNodes(std::vector<nodeInfo> &nodes, string_array &exclude_remarks, string_array &include_remarks, int groupID);
bool getSubInfoFromHeader(std::string &header, std::string &result);
bool getSubInfoFromNodes(std::vector<nodeInfo> &nodes, string_array &stream_rules, string_array &time_rules, std::string &result);
#endif // SPEEDTESTUTIL_H_INCLUDED