subconverter/main.cpp
2019-11-08 23:22:50 +08:00

301 lines
10 KiB
C++

#include <iostream>
#include <string>
#include <mutex>
#include <yaml-cpp/yaml.h>
#include "socket.h"
#include "misc.h"
#include "nodeinfo.h"
#include "speedtestutil.h"
#include "nodemanip.h"
#include "ini_reader.h"
#include "webget.h"
#include "webserver.h"
#include "subexport.h"
//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;
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;
extern std::string custom_group;
//safety lock for multi-thread
typedef std::lock_guard<std::mutex> guarded_mutex;
std::mutex on_configuring;
//preferences
string_array renames, emojis;
bool add_emoji = false, remove_old_emoji = false;
//clash custom
std::string clash_rule_base;
string_array clash_extra_group;
//surge custom
std::string surge_rule_base;
std::string refreshRulesets()
{
guarded_mutex guard(on_configuring);
eraseElements(ruleset_content_array);
string_array vArray;
std::string rule_group, rule_url, proxy = getSystemProxy();
ruleset_content rc;
for(std::string &x : rulesets)
{
vArray = split(x, ",");
if(vArray.size() != 2)
continue;
rule_group = trim(vArray[0]);
rule_url = trim(vArray[1]);
if(rule_url.find("[]") == 0)
{
std::cerr<<"Adding rule '"<<rule_url.substr(2)<<","<<rule_group<<"'."<<std::endl;
rc = {rule_group, rule_url};
ruleset_content_array.emplace_back(rc);
continue;
}
else
{
std::cerr<<"Updating ruleset url '"<<rule_url<<"' with group '"<<rule_group<<"'."<<std::endl;
if(fileExist(rule_url))
{
rc = {rule_group, fileGet(rule_url, false)};
}
else
{
rc = {rule_group, webGet(rule_url, proxy)};
}
}
if(rc.rule_content.size())
ruleset_content_array.emplace_back(rc);
}
return "done";
}
void readConf()
{
guarded_mutex guard(on_configuring);
eraseElements(def_exclude_remarks);
eraseElements(def_include_remarks);
eraseElements(clash_extra_group);
eraseElements(rulesets);
eraseElements(renames);
eraseElements(emojis);
INIReader ini;
//ini.do_utf8_to_gbk = true;
ini.ParseFile("pref.ini");
ini.EnterSection("common");
if(ini.ItemExist("api_mode"))
api_mode = ini.GetBool("api_mode");
if(ini.ItemExist("default_url"))
default_url = ini.Get("default_url");
if(ini.ItemPrefixExist("exclude_remarks"))
ini.GetAll("exclude_remarks", def_exclude_remarks);
if(ini.ItemPrefixExist("include_remarks"))
ini.GetAll("include_remarks", def_exclude_remarks);
if(ini.ItemExist("clash_rule_base"))
clash_rule_base = ini.Get("clash_rule_base");
if(ini.ItemExist("surge_rule_base"))
surge_rule_base = ini.Get("surge_rule_base");
if(ini.ItemPrefixExist("rename_node"))
ini.GetAll("rename_node", renames);
ini.EnterSection("emojis");
if(ini.ItemExist("add_emoji"))
add_emoji = ini.GetBool("add_emoji");
if(ini.ItemExist("remove_old_emoji"))
remove_old_emoji = ini.GetBool("remove_old_emoji");
if(ini.ItemPrefixExist("rule"))
ini.GetAll("rule", emojis);
ini.EnterSection("ruleset");
if(ini.GetBool("enabled"))
{
if(ini.ItemExist("overwrite_original_rules"))
overwrite_original_rules = ini.GetBool("overwrite_original_rules");
if(ini.ItemExist("update_ruleset_on_request"))
update_ruleset_on_request = ini.GetBool("update_ruleset_on_request");
if(ini.ItemPrefixExist("surge_ruleset"))
ini.GetAll("surge_ruleset", rulesets);
}
else
{
overwrite_original_rules = false;
update_ruleset_on_request = false;
}
ini.EnterSection("clash_proxy_group");
if(ini.ItemPrefixExist("custom_proxy_group"))
ini.GetAll("custom_proxy_group", clash_extra_group);
ini.EnterSection("server");
if(ini.ItemExist("listen"))
listen_address = ini.Get("listen");
if(ini.ItemExist("port"))
listen_port = ini.GetInt("port");
ini.EnterSection("advanced");
if(ini.ItemExist("max_pending_connections"))
max_pending_connections = ini.GetInt("max_pending_connections");
if(ini.ItemExist("max_concurrent_threads"))
max_concurrent_threads = ini.GetInt("max_concurrent_threads");
remarksInit(def_exclude_remarks, def_include_remarks);
}
std::string netchToClash(std::vector<nodeInfo> &nodes, std::string &baseConf, std::vector<ruleset_content> &ruleset_content_array, string_array &extra_proxy_group, bool clashR);
std::string netchToSurge(std::vector<nodeInfo> &nodes, std::string &base_conf, string_array &ruleset_array, string_array &extra_proxy_group, int surge_ver);
int main()
{
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
{
fprintf(stderr, "WSAStartup failed.\n");
return 1;
}
SetConsoleOutputCP(65001);
#endif // _WIN32
readConf();
if(!update_ruleset_on_request)
refreshRulesets();
append_response("GET", "/refreshrules", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
return refreshRulesets();
});
append_response("GET", "/readconf", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
readConf();
return "done";
});
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);
});
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);
});
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());
if(update_ruleset_on_request)
refreshRulesets();
return netchToSurge(nodes, surge_base_content, rulesets, clash_extra_group, surge_ver);
});
if(!api_mode)
{
append_response("GET", "/get", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
std::string url = UrlDecode(getUrlArg(argument, "url"));
return webGet(url, "");
});
append_response("GET", "/getlocal", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
return fileGet(UrlDecode(getUrlArg(argument, "path")));
});
}
listener_args args = {listen_address, listen_port, max_pending_connections, max_concurrent_threads};
std::cout<<"Serving HTTP @ http://"<<listen_address<<":"<<listen_port<<std::endl;
start_web_server_multi(&args);
#ifdef _WIN32
WSACleanup();
#endif // _WIN32
return 0;
}