Enhancements

Fix potential crash related to the new file reader.
Fix loading local subscriptions requires token when API mode is disabled.
Add support for parsing Clash Proxy Provider.
Update READMEs.
This commit is contained in:
Tindy X 2020-02-22 02:49:41 +08:00
parent 0527f1ddd4
commit e3726192dd
No known key found for this signature in database
GPG Key ID: C6AD413169968D58
5 changed files with 104 additions and 42 deletions

View File

@ -43,7 +43,7 @@
注意: 注意:
1. Shadowrocket 用户可以使用 `ss`、`ssr`以及 `v2ray`参数 1. Shadowrocket 用户可以使用 `ss`、`ssr` 以及 `v2ray` 参数
2. 类 TG 代理的 HTTP/Socks 链接 由于没有命名设定,所以可以在后方插入`&remark=`进行命名,例如 2. 类 TG 代理的 HTTP/Socks 链接 由于没有命名设定,所以可以在后方插入`&remark=`进行命名,例如
@ -256,7 +256,7 @@ http://127.0.0.1:25500/getprofile?name=%NAME%&token=%TOKEN%
| 调用参数 | 必要性 | 示例 | 解释 | | 调用参数 | 必要性 | 示例 | 解释 |
| -------- | :----: | :--------------- | :------------------------ | | -------- | :----: | :--------------- | :------------------------ |
| name | 必要 | profiles/formyairport.ini | 指配置档案的存储位置(可使用基于**主程序**的相对位置) | | name | 必要 | profiles/formyairport.ini | 指配置档案的存储位置(可使用基于**pref 配置文件**的相对位置) |
| token | 必要 | passwd | 为了安全考虑**必须设置token**(详见 [配置文件](#配置文件) 中 `[common] 部分``api_access_token` 的描述) | | token | 必要 | passwd | 为了安全考虑**必须设置token**(详见 [配置文件](#配置文件) 中 `[common] 部分``api_access_token` 的描述) |
应当注意的是,此处文件内的参数**无需进行 URLEncode**,且此处的 `token``api_mode` 的状态无关。 应当注意的是,此处文件内的参数**无需进行 URLEncode**,且此处的 `token``api_mode` 的状态无关。
@ -269,6 +269,7 @@ http://127.0.0.1:25500/getprofile?name=%NAME%&token=%TOKEN%
以上述 [进阶链接](#进阶链接) 的例子而言,`formyairport.ini` 内的内容应当是: 以上述 [进阶链接](#进阶链接) 的例子而言,`formyairport.ini` 内的内容应当是:
```txt ```txt
[Profile]
url=https://dler.cloud/subscribe/ABCDE?clash=vmess url=https://dler.cloud/subscribe/ABCDE?clash=vmess
target=surge target=surge
surge_ver=4 surge_ver=4

105
README.md
View File

@ -16,32 +16,45 @@ Utility to convert between various proxy subscription formats.
- [Quick Usage](#quick-usage) - [Quick Usage](#quick-usage)
- [Access Interface](#access-interface) - [Access Interface](#access-interface)
- [Description](#description) - [Description](#description)
- [Quick Conversion](#quick-conversion)
- [Advanced Usage](#advanced-usage) - [Advanced Usage](#advanced-usage)
- [Read Before Continue](#read-before-continue) - [Read Before Continue](#read-before-continue)
- [Advanced Details](#advanced-details) - [Advanced Details](#advanced-details)
- [Profiles](#profiles)
- [Configuration File](#configuration-file) - [Configuration File](#configuration-file)
- [External Configuration File](#external-configuration-file) - [External Configuration File](#external-configuration-file)
- [Auto Upload](#auto-upload) - [Auto Upload](#auto-upload)
## Supported Types ## Supported Types
| Type | As Source | As Target | Target Name | | Type | As Source | As Target | Target Name |
| ------------ | :---------: | :-----------: | ----------- | | ------------ | :--------: | :----------: | ----------- |
| Clash | ✔ | ✔ | clash | | Clash | ✓ | ✓ | clash |
| ClashR | ✔ | ✔ | clashr | | ClashR | ✓ | ✓ | clashr |
| Quantumult | ✔ | ✔ | quan | | Quantumult | ✓ | ✓ | quan |
| Quantumult X | ✔ | ✔ | quanx | | Quantumult X | ✓ | ✓ | quanx |
| SS (SIP002) | ✔ | ✔ | ss | | Loon | ✓ | ✓ | loon |
| SS Android | ✔ | ✔ | sssub | | SS (SIP002) | ✓ | ✓ | ss |
| SSD | ✔ | ✔ | ssd | | SS Android | ✓ | ✓ | sssub |
| SSR | ✔ | ✔ | ssr | | SSD | ✓ | ✓ | ssd |
| Surfboard | ✔ | ✔ | surfboard | | SSR | ✓ | ✓ | ssr |
| Surge 2 | ✔ | ✔ | surge&ver=2 | | Surfboard | ✓ | ✓ | surfboard |
| Surge 3 | ✔ | ✔ | surge&ver=3 | | Surge 2 | ✓ | ✓ | surge&ver=2 |
| Surge 4 | ✔ | ✔ | surge&ver=4 | | Surge 3 | ✓ | ✓ | surge&ver=3 |
| V2Ray | ✔ | ✔ | v2ray | | Surge 4 | ✓ | ✓ | surge&ver=4 |
| V2Ray | ✓ | ✓ | v2ray |
| Telegram-liked HTTP/Socks 5 links | ✓ | × | Only as source |
Notice:
1. Shadowrocket users should use `ss`, `ssr` or `v2ray` as target.
2. You can add `&remark=` to Telegram-liked HTTP/Socks 5 links to set a remark for this node. For example:
- tg://http?server=1.2.3.4&port=233&user=user&pass=pass&remark=Example
- https://t.me/http?server=1.2.3.4&port=233&user=user&pass=pass&remark=Example
**Notice**Shadowrocket users should use `ss`, `ssr` or `v2ray` as target.
--- ---
@ -51,7 +64,7 @@ Utility to convert between various proxy subscription formats.
### Access Interface ### Access Interface
```TXT ```txt
http://127.0.0.1:25500/sub?target=%TARGET%&url=%URL%&config=%CONFIG% http://127.0.0.1:25500/sub?target=%TARGET%&url=%URL%&config=%CONFIG%
``` ```
@ -67,7 +80,7 @@ If you need to merge two or more subscription, you should connect them with '|'
Example: Example:
```TXT ```txt
You have 2 subscriptions and you want to merge them and generate a Clash subscription: You have 2 subscriptions and you want to merge them and generate a Clash subscription:
1. https://dler.cloud/subscribe/ABCDE?clash=vmess 1. https://dler.cloud/subscribe/ABCDE?clash=vmess
2. https://rich.cloud/subscribe/ABCDE?clash=vmess 2. https://rich.cloud/subscribe/ABCDE?clash=vmess
@ -84,6 +97,16 @@ http://127.0.0.1:25500/sub?target=clash&url=https%3A%2F%2Fdler.cloud%2Fsubscribe
Finally subscribe this link in Clash and you are done! Finally subscribe this link in Clash and you are done!
``` ```
### Quick Conversion
When the Surge configuration file has already meet your requirements, but you also need a same Clash configuration with the same groups and rules, you can use the following method:
```txt
http://127.0.0.1:25500/surge2clash?link=url_to_Surge_subscription
```
Here `url_to_Surge_subscription` **does not need to be URLEncoded** and no other configuration is needed.
--- ---
## Advanced Usage ## Advanced Usage
@ -128,10 +151,11 @@ http://127.0.0.1:25500/sub?target=%TARGET%&url=%URL%&emoji=%EMOJI%····
| sort | No | true / false | Sort nodes in alphabetical order. Default is `false`. | | sort | No | true / false | Sort nodes in alphabetical order. Default is `false`. |
| include | No | See `include_remarks` | Exclude nodes which remarks match the following patterns. Supports regular expression. Process with [URLEncode](https://www.urlencoder.org/) first. **WILL OVERRIDE THE SAME SETTING IN `pref.ini`** | | include | No | See `include_remarks` | Exclude nodes which remarks match the following patterns. Supports regular expression. Process with [URLEncode](https://www.urlencoder.org/) first. **WILL OVERRIDE THE SAME SETTING IN `pref.ini`** |
| exclude | No | See `exclude_remarks` | Only include nodes which remarks match the following patterns. Supports regular expression. Process with [URLEncode](https://www.urlencoder.org/) first. **WILL OVERRIDE THE SAME SETTING IN `pref.ini`** | | exclude | No | See `exclude_remarks` | Only include nodes which remarks match the following patterns. Supports regular expression. Process with [URLEncode](https://www.urlencoder.org/) first. **WILL OVERRIDE THE SAME SETTING IN `pref.ini`** |
| filename | No | MySS | Set the file name while downloading. Can be used as a profile name in Clash for Windows. |
Example: Example:
```TXT ```txt
You have the following subscription: `https://dler.cloud/subscribe/ABCDE?clash=vmess`, and you want to convert it to Surge 4 subscription, set UDP and TFO to enabled, You have the following subscription: `https://dler.cloud/subscribe/ABCDE?clash=vmess`, and you want to convert it to Surge 4 subscription, set UDP and TFO to enabled,
add Emoji to node remarks and filter out unused nodes named "剩余流量1024G" and "官网地址dler.cloud". add Emoji to node remarks and filter out unused nodes named "剩余流量1024G" and "官网地址dler.cloud".
@ -148,6 +172,49 @@ http://127.0.0.1:25500/sub?surge&ver=4&tfo=true&udp=true&emoji=true&exclude=%28%
Finally subscribe this link in Surge and you are done! Finally subscribe this link in Surge and you are done!
``` ```
### Profiles
> After preparing all the arguments for the subscription link, it may be too long and hard to remember. Now you can consider setting up a profile.
For now **only local profiles are allowed.**
#### Interface for Profiles
```txt
http://127.0.0.1:25500/getprofile?name=%NAME%&token=%TOKEN%
```
#### Description
| Argument | Needed | Example | Explanation |
| -------- | :----: | :--------------- | :------------------------ |
| name | Yes | profiles/formyairport.ini | The path to the profile. (relative to the `pref` configuration) |
| token | Yes | passwd | The access token for authorization. (Please check `api_access_token` in the `[common] section` in `pref` configuration. |
Notice that arguments in the profile **does not require URLEncode**.
Create a new text file **only in the same directory or a sub-directory** of the `pref` configuration (a sub-directory named `profiles` is recommended) and name it, for example `formyairport.ini`, then fill the arguments to the file according to the [example profile](https://github.com/tindy2013/subconverter/blob/master/base/profiles/example_profile.ini), then you are good to go.
<details>
<summary>Example:</summary>
Using the same example in [Advanced Usage](#advanced-usage), the content of `formyairport.ini` should be:
```txt
[Profile]
url=https://dler.cloud/subscribe/ABCDE?clash=vmess
target=surge
surge_ver=4
tfo=true
udp=true
emoji=true
exclude=(流量|官网)
```
After saving it to the `profile` directory, you can access `http://127.0.0.1:25500/getprofile?name=profiles/formyairport.ini&token=passwd` to read this profile.
</details>
### Configuration File ### Configuration File
> Check comments inside [pref.ini](https://github.com/tindy2013/subconverter/blob/master/base/pref.ini) for more information. > Check comments inside [pref.ini](https://github.com/tindy2013/subconverter/blob/master/base/pref.ini) for more information.

View File

@ -903,7 +903,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
extra_settings ext; extra_settings ext;
std::string subInfo, dummy; std::string subInfo, dummy;
int interval = interval_str.size() ? to_int(interval_str, config_update_interval) : config_update_interval; int interval = interval_str.size() ? to_int(interval_str, config_update_interval) : config_update_interval;
bool ruleset_updated = false, authorized = getUrlArg(argument, "token") == access_token, strict = strict_str.size() ? strict_str == "true" : config_update_strict; 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()) 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!"; return "Invalid request!";

View File

@ -5,6 +5,7 @@
#include <sstream> #include <sstream>
#include <iosfwd> #include <iosfwd>
#include <iostream> #include <iostream>
#include <cstdio>
//#include <filesystem> //#include <filesystem>
#include <unistd.h> #include <unistd.h>
@ -688,10 +689,8 @@ std::string getMD5(const std::string &data)
// TODO: Add preprocessor option to disable (open web service safety) // TODO: Add preprocessor option to disable (open web service safety)
std::string fileGet(const std::string &path, bool binary, bool scope_limit) std::string fileGet(const std::string &path, bool binary, bool scope_limit)
{ {
std::ifstream infile;
//std::stringstream strstrm;
std::string content; std::string content;
std::ios::openmode mode = binary ? std::ios::binary : std::ios::in; const char *mode = binary ? "rb" : "r";
if(scope_limit) if(scope_limit)
{ {
@ -704,20 +703,14 @@ std::string fileGet(const std::string &path, bool binary, bool scope_limit)
#endif // _WIN32 #endif // _WIN32
} }
infile.open(path, mode); std::FILE *fp = std::fopen(path.c_str(), mode);
if(infile) if(fp)
{ {
//move to a faster method std::fseek(fp, 0, SEEK_END);
infile.seekg(0, std::ios::end); content.resize(std::ftell(fp));
content.resize(infile.tellg()); std::rewind(fp);
infile.seekg(0, std::ios::beg); std::fread(&content[0], 1, content.size(), fp);
infile.read(&content[0], content.size()); std::fclose(fp);
infile.close();
/*
strstrm << infile.rdbuf();
infile.close();
return strstrm.str();
*/
} }
return content; return content;
} }

View File

@ -736,9 +736,10 @@ void explodeClash(Node yamlnode, std::string custom_port, int local_port, std::v
std::string plugin, pluginopts, pluginopts_mode, pluginopts_host, pluginopts_mux; //ss std::string plugin, pluginopts, pluginopts_mode, pluginopts_host, pluginopts_mux; //ss
std::string protocol, protoparam, obfs, obfsparam; //ssr std::string protocol, protoparam, obfs, obfsparam; //ssr
std::string user; //socks std::string user; //socks
for(unsigned int i = 0; i < yamlnode["Proxy"].size(); i++) const std::string section = yamlnode["proxies"].IsDefined() ? "proxies" : "Proxy";
for(unsigned int i = 0; i < yamlnode[section].size(); i++)
{ {
singleproxy = yamlnode["Proxy"][i]; singleproxy = yamlnode[section][i];
singleproxy["type"] >> proxytype; singleproxy["type"] >> proxytype;
singleproxy["name"] >> ps; singleproxy["name"] >> ps;
singleproxy["server"] >> server; singleproxy["server"] >> server;
@ -1521,10 +1522,10 @@ void explodeSub(std::string sub, bool sslibev, bool ssrlibev, std::string custom
//try to parse as clash configuration //try to parse as clash configuration
try try
{ {
if(!processed && strFind(sub, "Proxy")) if(!processed && (strFind(sub, "Proxy:") || strFind(sub, "proxies:")))
{ {
Node yamlnode = Load(sub); Node yamlnode = Load(sub);
if(yamlnode.size() && yamlnode["Proxy"]) if(yamlnode.size() && (yamlnode["Proxy"].IsDefined() || yamlnode["proxies"].IsDefined()))
{ {
explodeClash(yamlnode, custom_port, local_port, nodes, sslibev, ssrlibev); explodeClash(yamlnode, custom_port, local_port, nodes, sslibev, ssrlibev);
processed = true; processed = true;