#include #include #include #include #include #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" #include "multithread.h" #include "logger.h" #include "string_hash.h" //common settings std::string pref_path = "pref.ini"; 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, insert_url, managed_config_prefix; int listen_port = 25500, max_pending_connections = 10, max_concurrent_threads = 4; bool api_mode = true, write_managed_config = false, enable_rule_generator = true, update_ruleset_on_request = false, overwrite_original_rules = true; bool print_debug_info = false, cfw_child_process = false, append_userinfo = true, enable_base_gen = false; std::string access_token; extern std::string custom_group; extern int global_log_level; //generator settings bool generator_mode = false; std::string gen_profile; //multi-thread lock std::mutex on_configuring; //preferences string_array renames, emojis; bool add_emoji = false, remove_old_emoji = false, append_proxy_type = false, filter_deprecated = true; bool udp_flag = false, tfo_flag = false, scv_flag = false, do_sort = false, config_update_strict = false; std::string proxy_config, proxy_ruleset, proxy_subscription; int config_update_interval = 0; std::string clash_rule_base; string_array clash_extra_group; std::string surge_rule_base, surfboard_rule_base, mellow_rule_base, quan_rule_base, quanx_rule_base, loon_rule_base; std::string surge_ssr_path, quanx_script_id; //pre-compiled rule bases YAML::Node clash_base; INIReader surge_base, mellow_base; //cache system int cache_subscription = 60, cache_config = 300, cache_ruleset = 21600; string_array regex_blacklist = {"(.*)*"}; template void operator >> (const YAML::Node& node, T& i) { if(node.IsDefined() && !node.IsNull()) //fail-safe i = node.as(); }; std::string getRuleset(RESPONSE_CALLBACK_ARGS) { std::string url = urlsafe_base64_decode(getUrlArg(argument, "url")), type = getUrlArg(argument, "type"), group = urlsafe_base64_decode(getUrlArg(argument, "group")); std::string output_content, dummy; if(!url.size() || !type.size() || (type == "2" && !group.size()) || (type != "1" && type != "2")) { *status_code = 400; return "Invalid request!"; } std::string proxy; if(proxy_ruleset == "SYSTEM") proxy = getSystemProxy(); else if(proxy_ruleset == "NONE") proxy = ""; else proxy = proxy_ruleset; if(fileExist(url)) output_content = fileGet(url, true); else output_content = webGet(url, proxy, dummy, cache_ruleset); if(!output_content.size()) { *status_code = 400; return "Invalid request!"; } if(type == "2") { std::string strLine; std::stringstream ss; const std::string rule_match_regex = "^(.*?,.*?)(,.*)(,.*)$"; ss << output_content; char delimiter = count(output_content.begin(), output_content.end(), '\n') < 1 ? '\r' : '\n'; std::string::size_type lineSize; output_content.clear(); while(getline(ss, strLine, delimiter)) { if(strLine.find("IP-CIDR6") == 0 || strLine.find("URL-REGEX") == 0 || strLine.find("PROCESS-NAME") == 0 || strLine.find("AND") == 0 || strLine.find("OR") == 0) //remove unsupported types continue; lineSize = strLine.size(); if(lineSize && strLine[lineSize - 1] == '\r') //remove line break { strLine.erase(lineSize - 1); lineSize--; } if(!strLine.empty() && (strLine[0] != ';' && strLine[0] != '#' && !(lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/'))) { strLine += "," + group; if(std::count(strLine.begin(), strLine.end(), ',') > 2 && regReplace(strLine, rule_match_regex, "$2") == ",no-resolve") strLine = regReplace(strLine, rule_match_regex, "$1$3$2"); else strLine = regReplace(strLine, rule_match_regex, "$1$3"); } output_content.append(strLine + "\n"); } } return output_content; } int importItems(string_array &target) { string_array result; std::stringstream ss; std::string path, content, strLine, dummy; unsigned int itemCount = 0; for(std::string &x : target) { if(x.find("!!import:") == x.npos) { result.emplace_back(x); continue; } path = x.substr(x.find(":") + 1); writeLog(0, "Trying to import items from " + path); std::string proxy; if(proxy_ruleset == "SYSTEM") proxy = getSystemProxy(); else if(proxy_ruleset == "NONE") proxy = ""; else proxy = proxy_ruleset; if(fileExist(path)) content = fileGet(path, api_mode); else content = webGet(path, proxy, dummy, cache_config); if(!content.size()) return -1; ss << content; char delimiter = count(content.begin(), content.end(), '\n') < 1 ? '\r' : '\n'; std::string::size_type lineSize; while(getline(ss, strLine, delimiter)) { lineSize = strLine.size(); if(lineSize && strLine[lineSize - 1] == '\r') //remove line break { strLine = strLine.substr(0, lineSize - 1); lineSize--; } if(!lineSize || strLine[0] == ';' || strLine[0] == '#' || (lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')) //empty lines and comments are ignored continue; result.emplace_back(strLine); itemCount++; } ss.clear(); } target.swap(result); writeLog(0, "Imported " + std::to_string(itemCount) + " item(s)."); return 0; } void readRegexMatch(YAML::Node node, std::string delimiter, string_array &dest) { YAML::Node object; std::string url, match, rep, strLine; for(unsigned i = 0; i < node.size(); i++) { object = node[i]; object["import"] >> url; if(url.size()) { url = "!!import:" + url; dest.emplace_back(url); continue; } object["match"] >> match; object["replace"] >> rep; if(match.size() && rep.size()) strLine = match + delimiter + rep; else continue; dest.emplace_back(strLine); } importItems(dest); } void readEmoji(YAML::Node node, string_array &dest) { YAML::Node object; std::string url, match, rep, strLine; for(unsigned i = 0; i < node.size(); i++) { object = node[i]; object["import"] >> url; if(url.size()) { url = "!!import:" + url; dest.emplace_back(url); continue; } object["match"] >> match; object["emoji"] >> rep; if(match.size() && rep.size()) strLine = match + "," + rep; else continue; dest.emplace_back(strLine); } importItems(dest); } void readGroup(YAML::Node node, string_array &dest) { std::string strLine, name, type, url, interval; string_array tempArray; YAML::Node object; unsigned int i, j; for(i = 0; i < node.size(); i++) { name.clear(); eraseElements(tempArray); object = node[i]; object["import"] >> name; if(name.size()) { name = "!!import:" + name; dest.emplace_back(name); continue; } url = "http://www.gstatic.com/generate_204", interval = "300"; object["name"] >> name; object["type"] >> type; tempArray.emplace_back(name); tempArray.emplace_back(type); object["url"] >> url; object["interval"] >> interval; for(j = 0; j < object["rule"].size(); j++) tempArray.emplace_back(object["rule"][j].as()); if(type != "select" && type != "ssid") { tempArray.emplace_back(url); tempArray.emplace_back(interval); } if((type == "select" && tempArray.size() < 3) || (type == "ssid" && tempArray.size() < 4) || (type != "select" && type != "ssid" && tempArray.size() < 5)) continue; strLine = std::accumulate(std::next(tempArray.begin()), tempArray.end(), tempArray[0], [](std::string a, std::string b) -> std::string { return std::move(a) + "`" + std::move(b); }); dest.emplace_back(strLine); } importItems(dest); } void readRuleset(YAML::Node node, string_array &dest) { std::string strLine, name, url, group; YAML::Node object; for(unsigned int i = 0; i < node.size(); i++) { url.clear(); name.clear(); object = node[i]; object["import"] >> name; if(name.size()) { name = "!!import:" + name; dest.emplace_back(name); continue; } object["ruleset"] >> url; object["group"] >> group; object["rule"] >> name; if(url.size()) strLine = group + "," + url; else if(name.size()) strLine = group + ",[]" + name; else continue; dest.emplace_back(strLine); } importItems(dest); } void refreshRulesets(string_array &ruleset_list, std::vector &rca) { eraseElements(rca); std::string rule_group, rule_url, dummy; ruleset_content rc; std::string proxy; if(proxy_ruleset == "SYSTEM") proxy = getSystemProxy(); else if(proxy_ruleset == "NONE") proxy = ""; else proxy = proxy_ruleset; for(std::string &x : ruleset_list) { /* vArray = split(x, ","); if(vArray.size() != 2) continue; rule_group = trim(vArray[0]); rule_url = trim(vArray[1]); */ if(x.find(",") == x.npos) continue; rule_group = trim(x.substr(0, x.find(","))); rule_url = trim(x.substr(x.find(",") + 1)); if(rule_url.find("[]") == 0) { writeLog(0, "Adding rule '" + rule_url.substr(2) + "," + rule_group + "'.", LOG_LEVEL_INFO); //std::cerr<<"Adding rule '"<> api_mode; section["api_access_token"] >> access_token; if(section["default_url"].IsSequence()) { section["default_url"] >> tempArray; if(tempArray.size()) strLine = std::accumulate(std::next(tempArray.begin()), tempArray.end(), tempArray[0], [](std::string a, std::string b) { return std::move(a) + "|" + std::move(b); }); default_url = strLine; } if(section["insert_url"].IsSequence()) { section["insert_url"] >> tempArray; if(tempArray.size()) strLine = std::accumulate(std::next(tempArray.begin()), tempArray.end(), tempArray[0], [](std::string a, std::string b) { return std::move(a) + "|" + std::move(b); }); insert_url = strLine; } if(section["exclude_remarks"].IsSequence()) section["exclude_remarks"] >> def_exclude_remarks; if(section["include_remarks"].IsSequence()) section["include_remarks"] >> def_include_remarks; section["clash_rule_base"] >> clash_rule_base; section["surge_rule_base"] >> surge_rule_base; section["surfboard_rule_base"] >> surfboard_rule_base; section["mellow_rule_base"] >> mellow_rule_base; section["quan_rule_base"] >> quan_rule_base; section["quanx_rule_base"] >> quanx_rule_base; section["loon_rule_base"] >> loon_rule_base; section["append_proxy_type"] >> append_proxy_type; section["proxy_config"] >> proxy_config; section["proxy_ruleset"] >> proxy_ruleset; section["proxy_subscription"] >> proxy_subscription; if(node["userinfo"].IsDefined()) { section = node["userinfo"]; if(section["stream_rule"].IsSequence()) { readRegexMatch(section["stream_rule"], "|", tempArray); safe_set_streams(tempArray); eraseElements(tempArray); } if(section["time_rule"].IsSequence()) { readRegexMatch(section["time_rule"], "|", tempArray); safe_set_times(tempArray); eraseElements(tempArray); } } if(node["node_pref"].IsDefined()) { section = node["node_pref"]; section["udp_flag"] >> udp_flag; section["tcp_fast_open_flag"] >> tfo_flag; section["sort_flag"] >> do_sort; section["skip_cert_verify_flag"] >> scv_flag; section["filter_deprecated_nodes"] >> filter_deprecated; section["append_sub_userinfo"] >> append_userinfo; } if(section["rename_node"].IsSequence()) { readRegexMatch(section["rename_node"], "@", tempArray); safe_set_renames(tempArray); eraseElements(tempArray); } if(node["managed_config"].IsDefined()) { section = node["managed_config"]; section["write_managed_config"] >> write_managed_config; section["managed_config_prefix"] >> managed_config_prefix; section["config_update_interval"] >> config_update_interval; section["config_update_strict"] >> config_update_strict; section["quanx_device_id"] >> quanx_script_id; } if(node["surge_external_proxy"].IsDefined()) node["surge_external_proxy"]["surge_ssr_path"] >> surge_ssr_path; if(node["emojis"].IsDefined()) { section = node["emojis"]; section["add_emoji"] >> add_emoji; section["remove_old_emoji"] >> remove_old_emoji; if(section["rules"].IsSequence()) { readEmoji(section["rules"], tempArray); safe_set_emojis(tempArray); eraseElements(tempArray); } } if(node["ruleset"].IsDefined()) { section = node["ruleset"]; section["enabled"] >> enable_rule_generator; if(!enable_rule_generator) { overwrite_original_rules = false; update_ruleset_on_request = false; } else { section["overwrite_original_rules"] >> overwrite_original_rules; section["update_ruleset_on_request"] >> update_ruleset_on_request; } if(section["surge_ruleset"].IsSequence()) readRuleset(section["surge_ruleset"], rulesets); } if(node["proxy_group"].IsDefined() && node["proxy_group"]["custom_proxy_group"].IsDefined()) readGroup(node["proxy_group"]["custom_proxy_group"], clash_extra_group); if(node["server"].IsDefined()) { node["server"]["listen"] >> listen_address; node["server"]["port"] >> listen_port; } if(node["advanced"].IsDefined()) { std::string log_level; node["advanced"]["log_level"] >> log_level; node["advanced"]["print_debug_info"] >> print_debug_info; if(print_debug_info) global_log_level = LOG_LEVEL_VERBOSE; else { switch(hash_(log_level)) { case "warn"_hash: global_log_level = LOG_LEVEL_WARNING; break; case "error"_hash: global_log_level = LOG_LEVEL_ERROR; break; case "fatal"_hash: global_log_level = LOG_LEVEL_FATAL; break; case "verbose"_hash: global_log_level = LOG_LEVEL_VERBOSE; break; case "debug"_hash: global_log_level = LOG_LEVEL_DEBUG; break; default: global_log_level = LOG_LEVEL_INFO; } } node["advanced"]["max_pending_connections"] >> max_pending_connections; node["advanced"]["max_concurrent_threads"] >> max_concurrent_threads; node["advanced"]["enable_base_gen"] >> enable_base_gen; if(node["advanced"]["enable_cache"].IsDefined()) { if(node["advanced"]["enable_cache"].as()) { node["advanced"]["cache_subscription"] >> cache_subscription; node["advanced"]["cache_config"] >> cache_config; node["advanced"]["cache_ruleset"] >> cache_ruleset; } else cache_subscription = cache_config = cache_ruleset = 0; //disable cache } } } void readConf() { guarded_mutex guard(on_configuring); //std::cerr<<"Reading preference settings..."<> ext.clash_rule_base; section["surge_rule_base"] >> ext.surge_rule_base; section["surfboard_rule_base"] >> ext.surfboard_rule_base; section["mellow_rule_base"] >> ext.mellow_rule_base; section["quan_rule_base"] >> ext.quan_rule_base; section["quanx_rule_base"] >> ext.quanx_rule_base; section["loon_rule_base"] >> ext.loon_rule_base; ext.enable_rule_generator = section["enable_rule_generator"].as(); ext.overwrite_original_rules = section["overwrite_original_rules"].as(); if(section["custom_proxy_group"].size()) readGroup(section["custom_proxy_group"], ext.custom_proxy_group); if(section["surge_ruleset"].size()) readRuleset(section["surge_ruleset"], ext.surge_ruleset); if(section["rename_node"].size()) readRegexMatch(section["rename_node"], "@", ext.rename); section["include_remarks"] >> ext.include; section["exclude_remarks"] >> ext.exclude; return 0; } int loadExternalConfig(std::string &path, ExternalConfig &ext) { std::string base_content, dummy; std::string proxy; if(proxy_config == "SYSTEM") proxy = getSystemProxy(); else if(proxy_config == "NONE") proxy = ""; else proxy = proxy_config; if(fileExist(path)) base_content = fileGet(path, api_mode); else base_content = webGet(path, proxy, dummy, cache_config); try { YAML::Node yaml = YAML::Load(base_content); if(yaml.size() && yaml["custom"]) return loadExternalYAML(yaml, ext); } catch (YAML::Exception &e) { //ignore } INIReader ini; ini.store_isolated_line = true; ini.SetIsolatedItemsSection("custom"); if(ini.Parse(base_content) != INIREADER_EXCEPTION_NONE) { //std::cerr<<"Load external configuration failed. Reason: "< rca; extra_settings ext; std::string subInfo, dummy; int interval = interval_str.size() ? to_int(interval_str, config_update_interval) : config_update_interval; bool ruleset_updated = false, authorized = !api_mode || getUrlArg(argument, "token") == access_token, strict = strict_str.size() ? strict_str == "true" : config_update_strict; 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!"; //for external configuration std::string ext_clash_base = clash_rule_base, ext_surge_base = surge_rule_base, ext_mellow_base = mellow_rule_base, ext_surfboard_base = surfboard_rule_base; std::string ext_quan_base = quan_rule_base, ext_quanx_base = quanx_rule_base, ext_loon_base = loon_rule_base; //validate urls if(!url.size() && (!api_mode || authorized)) url = default_url; if(insert_url.size()) url = insert_url + "|" + url; if(!url.size() || !target.size()) { *status_code = 400; return "Invalid request!"; } //check if we need to read configuration if((!api_mode || cfw_child_process) && !generator_mode) readConf(); //check for proxy settings std::string proxy; if(proxy_subscription == "SYSTEM") proxy = getSystemProxy(); else if(proxy_subscription == "NONE") proxy = ""; else proxy = proxy_subscription; ext.emoji_array = safe_get_emojis(); ext.rename_array = safe_get_renames(); //check other flags if(emoji.size()) { ext.add_emoji = emoji == "true"; ext.remove_emoji = true; } else { ext.add_emoji = add_emoji; ext.remove_emoji = remove_old_emoji; } if(append_type.size()) ext.append_proxy_type = append_type == "true"; else ext.append_proxy_type = append_proxy_type; ext.tfo = tfo.size() ? tfo == "true" : tfo_flag; ext.udp = udp.size() ? udp == "true" : udp_flag; ext.sort_flag = sort_flag.size() ? sort_flag == "true" : do_sort; ext.skip_cert_verify = scv.size() ? scv == "true" : scv_flag; ext.filter_deprecated = fdn.size() ? fdn == "true" : filter_deprecated; ext.nodelist = nodelist == "true"; ext.surge_ssr_path = surge_ssr_path; ext.quanx_dev_id = dev_id.size() ? dev_id : quanx_script_id; ext.enable_rule_generator = enable_rule_generator; ext.overwrite_original_rules = overwrite_original_rules; if(expand != "true") ext.managed_config_prefix = managed_config_prefix; //load external configuration if(config.size()) { //std::cerr<<"External configuration file provided. Loading...\n"; writeLog(0, "External configuration file provided. Loading...", LOG_LEVEL_INFO); //read predefined data first extra_group = clash_extra_group; extra_ruleset = rulesets; //then load external configuration ExternalConfig extconf; loadExternalConfig(config, extconf); if(extconf.clash_rule_base.size()) ext_clash_base = extconf.clash_rule_base; if(extconf.surge_rule_base.size()) ext_surge_base = extconf.surge_rule_base; if(extconf.surfboard_rule_base.size()) ext_surfboard_base = extconf.surfboard_rule_base; if(extconf.mellow_rule_base.size()) ext_mellow_base = extconf.mellow_rule_base; if(extconf.quan_rule_base.size()) ext_quan_base = extconf.quan_rule_base; if(extconf.quanx_rule_base.size()) ext_quanx_base = extconf.quanx_rule_base; if(extconf.loon_rule_base.size()) ext_loon_base = extconf.loon_rule_base; if(extconf.rename.size()) ext.rename_array = extconf.rename; if(extconf.emoji.size()) ext.emoji_array = extconf.emoji; ext.enable_rule_generator = extconf.enable_rule_generator; //load custom group if(extconf.custom_proxy_group.size()) extra_group = extconf.custom_proxy_group; //load custom rules ext.overwrite_original_rules = extconf.overwrite_original_rules; if(extconf.surge_ruleset.size() && !ext.nodelist) { extra_ruleset = extconf.surge_ruleset; refreshRulesets(extra_ruleset, rca); ruleset_updated = true; } else { if(ext.enable_rule_generator && !ext.nodelist) { if(update_ruleset_on_request) refreshRulesets(rulesets, ruleset_content_array); rca = ruleset_content_array; } } if(extconf.include.size()) include_remarks = extconf.include; if(extconf.exclude.size()) exclude_remarks = extconf.exclude; } else { //loading custom groups if(groups.size() && !ext.nodelist) { extra_group = split(groups, "@"); if(!extra_group.size()) extra_group = clash_extra_group; } else extra_group = clash_extra_group; //loading custom rulesets if(ruleset.size() && !ext.nodelist) { extra_ruleset = split(ruleset, "@"); if(!extra_ruleset.size()) { if(update_ruleset_on_request) refreshRulesets(rulesets, ruleset_content_array); rca = ruleset_content_array; } else { refreshRulesets(extra_ruleset, rca); ruleset_updated = true; } } else { if(enable_rule_generator && !ext.nodelist) { if(update_ruleset_on_request || cfw_child_process) refreshRulesets(rulesets, ruleset_content_array); rca = ruleset_content_array; } } } //loading urls string_array urls = split(url, "|"); std::vector nodes; int groupID = 0; //check custom include/exclude settings if(include.size() && regValid(include)) include_remarks.emplace_back(include); else include_remarks = def_include_remarks; if(exclude.size() && regValid(exclude)) exclude_remarks.emplace_back(exclude); else exclude_remarks = def_exclude_remarks; //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 '"< dummy_ruleset; //std::cerr<<"Generate target: "; if(target == "clash" || target == "clashr") { //std::cerr<<"Clash"<<((target == "clashr") ? "R" : "")< nodes; string_array extra_group, extra_ruleset, include_remarks, exclude_remarks; std::vector rca; std::string subInfo; if(!url.size() && !api_mode) url = default_url; if(!url.size() || argument.substr(0, 8) != "sublink=") { *status_code = 400; return "Invalid request!"; } if(url == "sublink") { *status_code = 400; return "Please insert your subscription link instead of clicking the default link."; } if(insert_url.size()) url = insert_url + "|" + url; if(!api_mode || cfw_child_process) readConf(); extra_group = clash_extra_group; if(update_ruleset_on_request || cfw_child_process) refreshRulesets(rulesets, ruleset_content_array); rca = ruleset_content_array; extra_settings ext = {true, overwrite_original_rules, safe_get_renames(), safe_get_emojis(), add_emoji, remove_old_emoji, append_proxy_type, udp_flag, tfo_flag, false, do_sort, scv_flag, filter_deprecated, "", "", ""}; std::string proxy; if(proxy_subscription == "SYSTEM") proxy = getSystemProxy(); else if(proxy_subscription == "NONE") proxy = ""; else proxy = proxy_subscription; include_remarks = def_include_remarks; exclude_remarks = def_exclude_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 '"< nodes; std::string base_content, url = argument.size() <= 5 ? "" : argument.substr(5), dummy; ini.store_any_line = true; if(!url.size()) url = default_url; if(!url.size() || argument.substr(0, 5) != "link=") { *status_code = 400; return "Invalid request!"; } if(url == "link") { *status_code = 400; return "Please insert your subscription link instead of clicking the default link."; } std::string proxy; if(proxy_config == "SYSTEM") proxy = getSystemProxy(); else if(proxy_config == "NONE") proxy = ""; else proxy = proxy_config; if(fileExist(url)) base_content = fileGet(url); else base_content = webGet(url, proxy, dummy, cache_config); if(ini.Parse(base_content) != INIREADER_EXCEPTION_NONE) { std::string errmsg = "Parsing Surge config failed! Reason: " + ini.GetLastError(); //std::cerr<= 2 && strLine[0] == '/' && strLine[1] == '/')) //empty lines and comments are ignored continue; else if(strLine.find("USER-AGENT") == 0 || strLine.find("URL-REGEX") == 0 || strLine.find("PROCESS-NAME") == 0 || strLine.find("AND") == 0 || strLine.find("OR") == 0) //remove unsupported types continue; strLine += strArray[2]; if(std::count(strLine.begin(), strLine.end(), ',') > 2) strLine = regReplace(strLine, "^(.*?,.*?)(,.*)(,.*)$", "$1$3$2"); rule.push_back(strLine); } ss.clear(); continue; } rule.push_back(x); } clash["Rule"] = rule; return YAML::Dump(clash); } std::string getProfile(RESPONSE_CALLBACK_ARGS) { std::string name = UrlDecode(getUrlArg(argument, "name")), token = UrlDecode(getUrlArg(argument, "token")), expand = getUrlArg(argument, "expand"); if(token.empty() || name.empty()) { *status_code = 403; return "Forbidden"; } if(!fileExist(name)) { *status_code = 404; return "Profile not found"; } //std::cerr<<"Trying to load profile '" + name + "'.\n"; writeLog(0, "Trying to load profile '" + name + "'.", LOG_LEVEL_INFO); INIReader ini; if(ini.ParseFile(name) != INIREADER_EXCEPTION_NONE && !ini.SectionExist("Profile")) { //std::cerr<<"Load profile failed! Reason: "<second) { *status_code = 403; return "Forbidden"; } token = access_token; } else { if(token != access_token) { *status_code = 403; return "Forbidden"; } } contents.emplace("token", token); contents.emplace("expand", expand); std::string query; for(auto &x : contents) { query += x.first + "=" + UrlEncode(x.second) + "&"; } query.erase(query.size() - 1); return subconverter(query, postdata, status_code, extra_headers); } std::string getScript(RESPONSE_CALLBACK_ARGS) { std::string url = urlsafe_base64_decode(getUrlArg(argument, "url")), dev_id = getUrlArg(argument, "id"); std::string output_content, dummy; std::string proxy; if(proxy_config == "SYSTEM") proxy = getSystemProxy(); else if(proxy_config == "NONE") proxy = ""; else proxy = proxy_config; if(fileExist(url)) output_content = fileGet(url, true); else output_content = webGet(url, proxy, dummy, cache_config); if(!dev_id.size()) dev_id = quanx_script_id; const std::string pattern = "(\\/\\*[\\s\\S]*?)^(.*?@supported )(.*?\\s?)$([\\s\\S]*\\*\\/\\s?"; if(dev_id.size()) { if(regFind(output_content, pattern)) output_content = regReplace(output_content, pattern, "$1$2" + dev_id + "$4"); else output_content = "/**\n * @supported " + dev_id + "\n * THIS COMMENT IS GENERATED BY SUBCONVERTER\n */\n\n" + output_content; } return output_content; } std::string getRewriteRemote(RESPONSE_CALLBACK_ARGS) { std::string url = urlsafe_base64_decode(getUrlArg(argument, "url")), dev_id = getUrlArg(argument, "id"); std::string output_content, dummy; std::string proxy; if(proxy_config == "SYSTEM") proxy = getSystemProxy(); else if(proxy_config == "NONE") proxy = ""; else proxy = proxy_config; if(fileExist(url)) output_content = fileGet(url, true); else output_content = webGet(url, proxy, dummy, cache_config); if(!dev_id.size()) dev_id = quanx_script_id; if(dev_id.size()) { std::stringstream ss; std::string strLine; const std::string pattern = "^(.*? url script-.*? )(.*?)$"; string_size lineSize; char delimiter = count(output_content.begin(), output_content.end(), '\n') < 1 ? '\r' : '\n'; ss << output_content; output_content.clear(); while(getline(ss, strLine, delimiter)) { lineSize = strLine.size(); if(lineSize && strLine[lineSize - 1] == '\r') //remove line break { strLine.erase(lineSize - 1); lineSize--; } if(!strLine.empty() && regMatch(strLine, pattern)) { url = managed_config_prefix + "/qx-script?id=" + dev_id + "&url=" + urlsafe_base64_encode(regReplace(strLine, pattern, "$2")); strLine = regReplace(strLine, pattern, "$1") + url; } output_content.append(strLine + "\n"); } } return output_content; } static inline std::string intToStream(unsigned long long stream) { char chrs[16] = {}; double streamval = stream; int level = 0; while(streamval > 1024.0) { if(level >= 5) break; level++; streamval /= 1024.0; } switch(level) { case 3: snprintf(chrs, 15, "%.2f GB", streamval); break; case 4: snprintf(chrs, 15, "%.2f TB", streamval); break; case 5: snprintf(chrs, 15, "%.2f PB", streamval); break; case 2: snprintf(chrs, 15, "%.2f MB", streamval); break; case 1: snprintf(chrs, 15, "%.2f KB", streamval); break; case 0: snprintf(chrs, 15, "%.2f B", streamval); break; } return std::string(chrs); } std::string subInfoToMessage(std::string subinfo) { typedef unsigned long long ull; subinfo = replace_all_distinct(subinfo, "; ", "&"); std::string retdata, useddata = "N/A", totaldata = "N/A", expirydata = "N/A"; std::string upload = getUrlArg(subinfo, "upload"), download = getUrlArg(subinfo, "download"), total = getUrlArg(subinfo, "total"), expire = getUrlArg(subinfo, "expire"); ull used = to_number(upload, 0) + to_number(download, 0), tot = to_number(total, 0); time_t expiry = to_number(expire, 0); if(used != 0) useddata = intToStream(used); if(tot != 0) totaldata = intToStream(tot); if(expiry != 0) { char buffer[30]; struct tm *dt = localtime(&expiry); strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M", dt); expirydata.assign(buffer); } if(useddata == "N/A" && totaldata == "N/A" && expirydata == "N/A") retdata = "Not Available"; else retdata += "Stream Used: " + useddata + " Stream Total: " + totaldata + " Expiry Time: " + expirydata; return retdata; } int simpleGenerator() { //std::cerr<<"\nReading generator configuration...\n"; writeLog(0, "Reading generator configuration...", LOG_LEVEL_INFO); std::string config = fileGet("generate.ini"), path, profile, arguments, content; if(config.empty()) { //std::cerr<<"Generator configuration not found or empty!\n"; writeLog(0, "Generator configuration not found or empty!", LOG_LEVEL_ERROR); return -1; } INIReader ini; if(ini.Parse(config) != INIREADER_EXCEPTION_NONE) { //std::cerr<<"Generator configuration broken! Reason:"< headers; int ret_code = 200; for(std::string &x : sections) { arguments.clear(); ret_code = 200; //std::cerr<<"Generating artifact '"<