Przeglądaj źródła

Enhancements

Fix support for parsing some Surge configurations.
Fix adding extra blank line to section end when exporting some configurations.
Fix adding real internal link as MANAGED-CONFIG URL when using profile.
Fix not filtering load-balance group in Surfboard configurations. (Issue [#128](https://github.com/tindy2013/subconverter/issues/128)).
Fix rule type filter not working properly.
Add support for using template in external configurations.
Add relay group support for Clash configurations.
Optimize codes.
Tindy X 5 lat temu
rodzic
commit
025d0eb4e1
5 zmienionych plików z 70 dodań i 49 usunięć
  1. 11 6
      src/ini_reader.h
  2. 27 33
      src/interfaces.cpp
  3. 2 1
      src/speedtestutil.cpp
  4. 28 7
      src/subexport.cpp
  5. 2 2
      src/templates.cpp

+ 11 - 6
src/ini_reader.h

@@ -22,6 +22,7 @@ enum
 typedef std::map<std::string, std::multimap<std::string, std::string>> ini_data_struct;
 typedef std::multimap<std::string, std::string> string_multimap;
 typedef std::vector<std::string> string_array;
+typedef std::string::size_type string_size;
 
 class INIReader
 {
@@ -898,6 +899,7 @@ public:
     std::string ToString()
     {
         std::string content;
+        string_size strsize;
 
         if(!parsed)
             return std::string();
@@ -907,16 +909,19 @@ public:
             content += "[" + x + "]\n";
             if(ini_content.find(x) != ini_content.end())
             {
-                for(auto &y : ini_content.at(x))
+                auto section = ini_content.at(x);
+                for(auto iter = section.begin(); iter != section.end(); iter++)
                 {
-                    if(y.first != "{NONAME}")
-                        content += y.first + "=";
-                    content += y.second + "\n";
+                    if(iter->first != "{NONAME}")
+                        content += iter->first + "=";
+                    content += iter->second + "\n";
+                    if(std::next(iter) == section.end())
+                        strsize = iter->second.size();
                 }
             }
-            content += "\n";
+            if(strsize)
+                content += "\n";
         }
-
         return content.erase(content.size() - 2);
     }
 

+ 27 - 33
src/interfaces.cpp

@@ -125,9 +125,12 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS)
 
     while(getline(ss, strLine, delimiter))
     {
-        if(type_int == 2 && !std::any_of(quanx_rule_type.begin(), quanx_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
-            continue;
-        else if(!std::any_of(surge_rule_type.begin(), surge_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
+        if(type_int == 2)
+        {
+            if(!std::any_of(quanx_rule_type.begin(), quanx_rule_type.end(), [&strLine](std::string type){return startsWith(strLine, type);}))
+                continue;
+        }
+        else if(!std::any_of(surge_rule_type.begin(), surge_rule_type.end(), [&strLine](std::string type){return startsWith(strLine, type);}))
             continue;
 
         lineSize = strLine.size();
@@ -175,8 +178,10 @@ int importItems(string_array &target, bool scope_limit = true)
 
         if(fileExist(path))
             content = fileGet(path, scope_limit);
-        else
+        else if(startsWith(path, "http://") || startsWith(path, "https://") || startsWith(path, "data:"))
             content = webGet(path, proxy, dummy, cache_config);
+        else
+            writeLog(0, "File not found or not a valid URL: " + path, LOG_LEVEL_ERROR);
         if(!content.size())
             return -1;
 
@@ -831,7 +836,7 @@ struct ExternalConfig
     string_array emoji;
     string_array include;
     string_array exclude;
-    string_map template_args;
+    template_args *tpl_args = NULL;
     bool overwrite_original_rules = false;
     bool enable_rule_generator = true;
 };
@@ -866,14 +871,14 @@ int loadExternalYAML(YAML::Node &node, ExternalConfig &ext)
     section["include_remarks"] >> ext.include;
     section["exclude_remarks"] >> ext.exclude;
 
-    if(node["template_args"].IsSequence())
+    if(node["template_args"].IsSequence() && ext.tpl_args != NULL)
     {
         std::string key, value;
         for(size_t i = 0; i < node["template_args"].size(); i++)
         {
             node["template_args"][i]["key"] >> key;
             node["template_args"][i]["value"] >> value;
-            ext.template_args[key] = value;
+            ext.tpl_args->local_vars[key] = value;
         }
     }
 
@@ -882,9 +887,9 @@ int loadExternalYAML(YAML::Node &node, ExternalConfig &ext)
 
 int loadExternalConfig(std::string &path, ExternalConfig &ext)
 {
-    std::string base_content, dummy;
-    std::string proxy = parseProxy(proxy_config);
-    base_content = fetchFile(path, proxy, cache_config);
+    std::string base_content, dummy, proxy = parseProxy(proxy_config), config = fetchFile(path, proxy, cache_config);
+    if(render_template(config, *ext.tpl_args, base_content, template_path) != 0)
+        base_content = config;
 
     try
     {
@@ -946,13 +951,13 @@ int loadExternalConfig(std::string &path, ExternalConfig &ext)
     if(ini.ItemPrefixExist("exclude_remarks"))
         ini.GetAll("exclude_remarks", ext.exclude);
 
-    if(ini.SectionExist("template"))
+    if(ini.SectionExist("template") && ext.tpl_args != NULL)
     {
         ini.EnterSection("template");
         string_multimap tempmap;
         ini.GetItems(tempmap);
         for(auto &x : tempmap)
-            ext.template_args[x.first] = x.second;
+            ext.tpl_args->local_vars[x.first] = x.second;
     }
 
     return 0;
@@ -1094,6 +1099,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
         extra_ruleset = rulesets;
         //then load external configuration
         ExternalConfig extconf;
+        extconf.tpl_args = &tpl_args;
         loadExternalConfig(config, extconf);
         if(!ext.nodelist)
         {
@@ -1118,8 +1124,6 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
             ext.rename_array = extconf.rename;
         if(extconf.emoji.size())
             ext.emoji_array = extconf.emoji;
-        if(extconf.template_args.size())
-            tpl_args.local_vars = extconf.template_args;
         ext.enable_rule_generator = extconf.enable_rule_generator;
         //load custom group
         if(extconf.custom_proxy_group.size())
@@ -1230,6 +1234,9 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
 
     string_array dummy_group;
     std::vector<ruleset_content> dummy_ruleset;
+    std::string managed_url = base64_decode(UrlDecode(getUrlArg(argument, "profile_data")));
+    if(managed_url.empty())
+        managed_url = managed_config_prefix + "/sub?" + argument;
 
     //std::cerr<<"Generate target: ";
     int surge_ver;
@@ -1292,7 +1299,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
                 uploadGist("surge" + version, upload_path, output_content, true);
 
             if(write_managed_config && managed_config_prefix.size())
-                output_content = "#!MANAGED-CONFIG " + managed_config_prefix + "/sub?" + argument + (interval ? " interval=" + std::to_string(interval) : "") \
+                output_content = "#!MANAGED-CONFIG " + managed_url + (interval ? " interval=" + std::to_string(interval) : "") \
                  + " strict=" + std::string(strict ? "true" : "false") + "\n\n" + output_content;
         }
         break;
@@ -1311,7 +1318,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
             uploadGist("surfboard", upload_path, output_content, true);
 
         if(write_managed_config && managed_config_prefix.size())
-            output_content = "#!MANAGED-CONFIG " + managed_config_prefix + "/sub?" + argument + (interval ? " interval=" + std::to_string(interval) : "") \
+            output_content = "#!MANAGED-CONFIG " + managed_url + (interval ? " interval=" + std::to_string(interval) : "") \
                  + " strict=" + std::string(strict ? "true" : "false") + "\n\n" + output_content;
         break;
     case "mellow"_hash:
@@ -1801,27 +1808,14 @@ std::string getProfile(RESPONSE_CALLBACK_ARGS)
         }
     }
 
-    string_map sub_args;
-    for(auto &x : contents)
-    {
-        sub_args[x.first] = x.second;
-    }
-    string_array args = split(argument, "&");
-    string_size pos;
-    for(std::string &x : args)
-    {
-        pos = x.find("=");
-        if(pos == x.npos)
-            continue;
-        sub_args[x.substr(0, pos)] = UrlDecode(x.substr(pos + 1));
-    }
-    sub_args["token"] = token;
+    contents.emplace("token", token);
+    contents.emplace("profile_data", base64_encode(managed_config_prefix + "/getprofile?" + argument));
     std::string query;
-    for(auto &x : sub_args)
+    for(auto &x : contents)
     {
         query += x.first + "=" + UrlEncode(x.second) + "&";
     }
-    query.erase(query.size() - 1);
+    query += argument;
     return subconverter(query, postdata, status_code, extra_headers);
 }
 

+ 2 - 1
src/speedtestutil.cpp

@@ -1211,7 +1211,8 @@ bool explodeSurge(std::string surge, const std::string &custom_port, int local_p
     ini.SetIsolatedItemsSection("Proxy");
     ini.IncludeSection("Proxy");
     ini.AddDirectSaveSection("Proxy");
-    surge = regReplace(surge, "^#!.*$\\r?\\n", "");
+    if(surge.find("[Proxy]") != surge.npos)
+        surge = regReplace(surge, "^[\\S\\s]*?\\[", "[");
     ini.Parse(surge);
 
     if(!ini.SectionExist("Proxy"))

+ 28 - 7
src/subexport.cpp

@@ -28,6 +28,7 @@ const string_array clashr_obfs = {"plain", "http_simple", "http_post", "tls1.2_t
 /// rule type lists
 #define basic_types "DOMAIN", "DOMAIN-SUFFIX", "DOMAIN-KEYWORD", "IP-CIDR", "SRC-IP-CIDR", "GEOIP", "MATCH", "FINAL"
 const string_array clash_rule_type = {basic_types, "IP-CIDR6", "SRC-PORT", "DST-PORT"};
+const string_array surge2_rule_type = {basic_types, "IP-CIDR6", "USER-AGENT", "URL-REGEX", "PROCESS-NAME", "IN-PORT", "DEST-PORT", "SRC-IP"};
 const string_array surge_rule_type = {basic_types, "IP-CIDR6", "USER-AGENT", "URL-REGEX", "AND", "OR", "NOT", "PROCESS-NAME", "IN-PORT", "DEST-PORT", "SRC-IP"};
 const string_array quanx_rule_type = {basic_types, "USER-AGENT", "URL-REGEX", "PROCESS-NAME", "HOST", "HOST-SUFFIX", "HOST-KEYWORD"};
 const string_array surfb_rule_type = {basic_types, "IP-CIDR6", "PROCESS-NAME", "IN-PORT", "DEST-PORT", "SRC-IP"};
@@ -688,12 +689,29 @@ void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_
                     continue;
 
                 /// remove unsupported types
-                if((surge_ver == -1 || surge_ver == -2) && !std::any_of(quanx_rule_type.begin(), quanx_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
-                    continue;
-                else if(surge_ver == -3 && !std::any_of(surfb_rule_type.begin(), surfb_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
-                    continue;
-                else if(!std::any_of(surge_rule_type.begin(), surge_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
-                    continue;
+                switch(surge_ver)
+                {
+                case -1:
+                case -2:
+                    if(!std::any_of(quanx_rule_type.begin(), quanx_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
+                        continue;
+                    break;
+                case -3:
+                    if(!std::any_of(surfb_rule_type.begin(), surfb_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
+                        continue;
+                    break;
+                default:
+                    if(surge_ver > 2)
+                    {
+                        if(!std::any_of(surge_rule_type.begin(), surge_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
+                            continue;
+                    }
+                    else
+                    {
+                        if(!std::any_of(surge2_rule_type.begin(), surge2_rule_type.end(), [strLine](std::string type){return startsWith(strLine, type);}))
+                            continue;
+                    }
+                }
 
                 strLine += "," + rule_group;
                 if(surge_ver == -1 || surge_ver == -2)
@@ -1036,6 +1054,7 @@ void netchToClash(std::vector<nodeInfo> &nodes, YAML::Node &yamlnode, string_arr
         switch(hash_(vArray[1]))
         {
         case "select"_hash:
+        case "relay"_hash:
             break;
         case "url-test"_hash:
         case "fallback"_hash:
@@ -1298,9 +1317,11 @@ std::string netchToSurge(std::vector<nodeInfo> &nodes, std::string &base_conf, s
         {
         case "select"_hash:
             break;
+        case "load-balance"_hash:
+            if(surge_ver < 1)
+                continue;
         case "url-test"_hash:
         case "fallback"_hash:
-        case "load-balance"_hash:
             if(rules_upper_bound < 5)
                 continue;
             rules_upper_bound -= 2;

+ 2 - 2
src/templates.cpp

@@ -70,8 +70,8 @@ int render_template(const std::string &content, const template_args &vars, std::
     });
     m_callbacks.add_callback("join", 3, [](inja::Arguments &args)
     {
-        std::string str1 = args.at(0)->get<std::string>(), str2 = args.at(1)->get<std::string>(), delim = args.at(2)->get<std::string>();
-        return std::move(str1) + std::move(delim) + std::move(str2);
+        std::string str1 = args.at(0)->get<std::string>(), str2 = args.at(1)->get<std::string>(), str3 = args.at(2)->get<std::string>();
+        return std::move(str1) + std::move(str2) + std::move(str3);
     });
     m_callbacks.add_callback("fetch", 1, template_webGet);
     m_callbacks.add_callback("parseHostname", 1, parseHostname);