Compare commits

...

863 Commits
6.16 ... 7.3.0

Author SHA1 Message Date
2dust
a372d8902e up 7.3.0 2024-12-05 20:08:23 +08:00
2dust
c38c62e4c3 Add automatic batching during speed test and real ping 2024-12-05 19:48:42 +08:00
2dust
8aceff7480 Enhanced testing function
Separate xray-core and sing-box-core testing
2024-12-05 15:21:16 +08:00
2dust
736c450161 Improve the code 2024-12-05 14:05:12 +08:00
2dust
3b63a3d308 Fix
https://github.com/2dust/v2rayN/issues/6216
2024-12-05 14:04:39 +08:00
2dust
4f56174c8f Bug fix
https://github.com/2dust/v2rayN/issues/6219
2024-12-05 12:16:53 +08:00
2dust
4d2eb324f1 Adjust UI
https://github.com/2dust/v2rayN/issues/6217
2024-12-05 10:39:08 +08:00
2dust
3f2ab8ddcb Try to fix
https://github.com/2dust/v2rayN/issues/6186
2024-12-05 10:12:26 +08:00
2dust
fd8f863c5b Add stream-one for xhttp mode 2024-12-03 18:00:55 +08:00
2dust
246f1d7df0 Update README.md 2024-12-03 17:54:13 +08:00
2dust
ccacda9bf5 Using mixed local listening ports 2024-12-03 15:01:36 +08:00
2dust
5494d63878 Remove v2fly-core UI operation support 2024-12-03 13:58:56 +08:00
2dust
c32b9812a7 up PackageReference 2024-12-02 14:43:07 +08:00
2dust
9eb9898b61 Improve the clash connection 2024-12-02 14:40:09 +08:00
2dust
834e05999f Paste import functionality moved to server list
https://github.com/2dust/v2rayN/issues/6201
2024-12-02 11:01:32 +08:00
2dust
b4c37d9906 Bug fix
https://github.com/2dust/v2rayN/issues/6200
2024-12-02 10:16:07 +08:00
2dust
9326b450d7 Delete .github/workflows/winget-pre-release-publish.yml 2024-12-01 14:39:36 +08:00
2dust
019ee8b1ba Update winget-publish.yml 2024-12-01 14:30:39 +08:00
Merrick Zhang
f043645397 Create winget-publish.yml (#6014)
* Create winget-publish.yml

Publish to winget after release

* Update winget-publish.yml

change to released trigger

* Create winget-pre-release-publish.yml

publish pre-release versions.
2024-12-01 14:29:10 +08:00
2dust
3b173f0b3e Improve code 2024-12-01 11:17:36 +08:00
2dust
f36c06389d Improve the clash connection 2024-12-01 10:18:36 +08:00
2dust
d3a0b44247 Bug fix
https://github.com/2dust/v2rayN/issues/6182
2024-11-30 13:49:26 +08:00
2dust
e4d3a98aa8 Fixed the problem of traffic statistics api url 2024-11-30 10:58:53 +08:00
2dust
f685682214 Code clean 2024-11-29 19:21:09 +08:00
2dust
1c6323315b up 7.2.3 2024-11-29 11:43:53 +08:00
2dust
1ff4839be1 Bug fix
https://github.com/2dust/v2rayN/issues/6160
2024-11-29 11:31:22 +08:00
2dust
558e5bb340 Improve tun for linux 2024-11-29 10:31:57 +08:00
2dust
48a9d208e6 Label text optimization 2024-11-28 14:44:24 +08:00
2dust
f29e1f8c45 up 7.2.2 2024-11-27 18:54:16 +08:00
2dust
5d2bc88bb9 up PackageReference 2024-11-27 18:53:32 +08:00
2dust
298fdb1191 Change IPAPI
https://github.com/2dust/v2rayN/issues/6172
2024-11-27 17:03:16 +08:00
2dust
59b34688ea Improve tun for linux 2024-11-27 16:52:25 +08:00
2dust
5ce0bb6e4a Up 2024-11-27 14:40:01 +08:00
2dust
487b1ab182 Update build-osx 2024-11-27 14:37:39 +08:00
2dust
5c144a8ba3 Create build-osx.yml 2024-11-27 14:29:22 +08:00
2dust
ad344356df Create build-osx.sh 2024-11-27 14:23:16 +08:00
2dust
a55c65374d Update build.ps1 2024-11-27 14:22:42 +08:00
2dust
28447a9d43 Update build.ps1 2024-11-27 13:57:51 +08:00
2dust
0c03550c62 Update build.yml 2024-11-27 12:22:40 +08:00
2dust
6d9a84803f Update build.yml 2024-11-27 12:20:26 +08:00
2dust
03b0e4e2bb Update build.yml 2024-11-27 12:18:15 +08:00
2dust
a00e9a6f5e Update build.ps1 2024-11-27 11:25:37 +08:00
2dust
ba17f8fde9 Improve CoreHandler 2024-11-27 11:05:41 +08:00
2dust
01d35456bd Update AesUtils.cs 2024-11-26 20:07:41 +08:00
cryptochecktool
672b8c48ac AesUtils重新修改 (#6165)
* Create AesUtils.cs

新增AesUtils,替换掉DesUtils,提高安全性。
细节:
1.使用AES代替DES
2.正确使用IV调用方式
3.使用SHA256代替MD5进行密钥派生
4.使用PBKDF进行迭代生成密钥。

* Update AesUtils.cs

1.将默认密钥规则设置为与DES一致
2.经过本地测试
2024-11-26 19:54:46 +08:00
2dust
ac1a357740 Delete AesUtils.cs 2024-11-26 19:52:22 +08:00
2dust
504f8d09a6 Tun enable in macos when opening the app 2024-11-26 16:33:19 +08:00
2dust
89ce7c23c9 Fix tun interface name in macos 2024-11-26 15:32:35 +08:00
2dust
a5d99b1eb5 Improved Tun mode function in Linux version 2024-11-26 15:11:02 +08:00
2dust
800d193acb Bug fix 2024-11-26 14:39:33 +08:00
cryptochecktool
7a1d12be76 Update CoreHandler.cs (#6163)
将Desutils替换为Aesutils,提高安全性
2024-11-26 13:54:12 +08:00
cryptochecktool
1b9c95e801 Update OptionSettingViewModel.cs (#6162)
将Desutils替换为Aesutils,提升安全性。
2024-11-26 13:53:50 +08:00
cryptochecktool
9f44815470 Create AesUtils.cs (#6161)
新增AesUtils,替换掉DesUtils,提高安全性。
细节:
1.使用AES代替DES
2.正确使用IV调用方式
3.使用SHA256代替MD5进行密钥派生
4.使用PBKDF进行迭代生成密钥。
2024-11-26 13:52:37 +08:00
2dust
ca38239bce Add Font setting for macos 2024-11-25 21:00:49 +08:00
2dust
d9c22de6b8 Added core update function for macos 2024-11-25 20:31:12 +08:00
2dust
49a3c84fc5 up 7.2.1 2024-11-24 11:31:45 +08:00
2dust
5e0c28438b Refactor Localization for AmazTool 2024-11-23 14:41:19 +08:00
2dust
a6dc801bc4 Merge pac function into the main program 2024-11-23 14:08:08 +08:00
Space Time
22009d1b71 调整订阅分组设置窗口的布局 (#6132)
* 调整订阅分组设置窗口的布局

* 避免添加配置后出现滚动条
2024-11-23 10:45:16 +08:00
2dust
50f39dc40e Delete splithttp 2024-11-22 20:56:34 +08:00
2dust
05d446ed37 up 7.2.0 2024-11-22 18:04:36 +08:00
2dust
7a0b068642 Improve Follow System Theme for windows 2024-11-22 17:37:30 +08:00
2dust
c0ef8c09af Add Follow System Theme for desktop 2024-11-22 17:35:49 +08:00
fonaix
a2db6dd468 修复:TrayIcon不跟随代理模式变化的问题 (#6126)
* 添加:MacOS 代理配置与清除

* 修复:点击表头排序时,超时服务器排在前面的问题

* 添加:MacOS 打开存储所在位置功能

* 修复:删除全部节点时,UI不更新的问题

* 修复:TrayIcon不跟随代理模式变化的问题
2024-11-21 09:02:35 +08:00
2dust
471bc0f65d Update README.md 2024-11-20 15:33:29 +08:00
2dust
80f840a7c2 Fix
https://github.com/2dust/v2rayN/issues/6119
2024-11-20 15:29:06 +08:00
2dust
a2413fdf23 Remove Routing Basic function 2024-11-20 12:01:04 +08:00
2dust
9cbe2b938c Fixed the inbound issue caused by server configuration error during speed test
https://github.com/2dust/v2rayN/issues/6110
2024-11-20 10:34:51 +08:00
2dust
e915726c52 Improved upgrade
https://github.com/2dust/v2rayN/issues/6102
2024-11-20 10:29:04 +08:00
2dust
b11e68cfd8 Remove StatisticsV2rayService
Remove ProtosLib project
2024-11-19 19:20:12 +08:00
2dust
bee66d06dd Added real IP location display
https://ipapi.co/api/?shell#introduction
2024-11-19 16:43:00 +08:00
2dust
945a0add96 Code clean 2024-11-19 15:52:08 +08:00
2dust
294cabcf05 Update README.md 2024-11-18 17:14:42 +08:00
2dust
22eb993ebf up 7.1.3 2024-11-18 16:51:51 +08:00
2dust
b5e1a297ae Optimize Linux start tun mode
You can choose not to store the password and enter it manually each time
2024-11-18 16:44:12 +08:00
2dust
499a16feae Fix LocalizationHelper for AmazTool 2024-11-18 10:13:49 +08:00
Slnanx
1866a59d12 更新程序内嵌资源文件 (#6101)
* fix

* fix

* 移除自述

* 改用单例模式,避免多次初始化。

* 小修正

* 修正

* 更正自述

* 应该完成了

* 内嵌资源

* Update LocalizationHelper.cs
2024-11-18 09:57:38 +08:00
2dust
10513e0f3b Fixed the problem that the statistics column would not be hidden 2024-11-18 09:56:13 +08:00
Slnanx
2e32de2fbb fix (#6099)
* fix

* fix

* 移除自述

* 改用单例模式,避免多次初始化。

* 小修正

* 修正

* 更正自述

* 应该完成了
2024-11-17 19:42:14 +08:00
2dust
30a838df77 Bug fix
https://github.com/2dust/v2rayN/issues/6092
2024-11-17 16:18:46 +08:00
2dust
7cf9b9f57e up 7.1.2 2024-11-17 14:15:37 +08:00
2dust
736d995d4c up PackageReference 2024-11-17 14:11:51 +08:00
2dust
e335b2c0d6 Fix with AppendQuotes 2024-11-17 14:11:17 +08:00
2dust
96ae5517f6 Improve core msg 2024-11-17 09:58:57 +08:00
Slnanx
fb4b8d923a 可以通过判断系统语言来显示对应语言 (#6083) 2024-11-17 09:42:40 +08:00
2dust
2ade705e51 Bug fix 2024-11-16 20:15:05 +08:00
NagisaEfi
e0086b4b79 Update ResUI.zh-Hant.resx (#6080) 2024-11-16 10:34:55 +08:00
2dust
8f6d443104 Hide to tray when closing the window
https://github.com/2dust/v2rayN/issues/6076
2024-11-15 19:50:37 +08:00
2dust
5dbce16895 up 7.1.1 2024-11-15 13:36:44 +08:00
2dust
1016dcb3d1 Improve PAC 2024-11-15 11:59:22 +08:00
2dust
ba5ad12e13 Linux password encryption storage 2024-11-15 09:42:49 +08:00
2dust
57f9c8158e up 7.1.0 2024-11-14 15:20:14 +08:00
2dust
b8a0293b52 Add xhttp extra for xray
https://github.com/XTLS/Xray-core/pull/4000
2024-11-14 14:37:03 +08:00
2dust
59b7daaef5 Improve UI 2024-11-14 14:25:58 +08:00
2dust
d781c768e9 Add xhttp mode for xray
https://github.com/XTLS/Xray-core/pull/3994
2024-11-14 09:48:43 +08:00
2dust
04efed2079 SplitHTTP is now XHTTP for xray 2024-11-13 19:48:44 +08:00
2dust
2de9e14c7d xray-core uses quic to add failure prompts and plans to remove quic 2024-11-13 19:00:35 +08:00
2dust
a849dec6c2 Remove SagerNet 2024-11-13 18:00:31 +08:00
2dust
28019dc511 Migrate xray traffic statistics to Metrics
https://xtls.github.io/config/metrics.html
2024-11-13 16:45:23 +08:00
2dust
9d638968a9 Use tun mode without running with sudo in linux 2024-11-13 16:22:56 +08:00
2dust
f921ff7d77 Update README.md 2024-11-13 16:00:01 +08:00
NagisaEfi
e3ea329795 Update ResUI.zh-Hant.resx (#6049) 2024-11-13 09:49:41 +08:00
TTG
87f1c129b6 Update DNS Routing (#6045)
* Update DNS Routing

* Removed outdated DNS IP
2024-11-12 09:42:10 +08:00
2dust
a1c8bc0e61 Improve the code 2024-11-11 16:56:08 +08:00
fonaix
61e9101851 添加:MacOS 打开存储所在位置功能 (#6038)
* 添加:MacOS 代理配置与清除

* 修复:点击表头排序时,超时服务器排在前面的问题

* 添加:MacOS 打开存储所在位置功能

* 修复:删除全部节点时,UI不更新的问题
2024-11-11 09:48:20 +08:00
2dust
a6289347cc Bug fix
https://github.com/2dust/v2rayN/issues/6037
2024-11-11 09:37:49 +08:00
2dust
9e84de8e76 Migrating the sing-box configuration to 1.10.0
https://sing-box.sagernet.org/migration/#tun-address-fields-are-merged
2024-11-10 17:30:03 +08:00
2dust
d04cc53152 Fill ResUI 2024-11-10 17:25:36 +08:00
2dust
1ff3c5fc07 Fix
https://github.com/2dust/v2rayN/issues/6032
2024-11-10 17:24:41 +08:00
2dust
3db047a12f Fix 2024-11-09 14:37:48 +08:00
TTG
bf4615f2f2 Update Configuration to Optimize (#6027)
* Remove geolocation-cn

* Segment and update DNS lists
2024-11-09 09:28:10 +08:00
2dust
798c7fad9a up 7.0.9 2024-11-08 20:44:28 +08:00
2dust
5797b37262 Bug fix
https://github.com/2dust/v2rayN/issues/6023
2024-11-08 20:41:07 +08:00
2dust
ce22f34cd6 up 7.0.8 2024-11-08 20:01:36 +08:00
2dust
4c49e52e26 Bug fix
https://github.com/2dust/v2rayN/issues/5963
2024-11-08 19:24:36 +08:00
2dust
47318b5d70 Improve the code for Desktop Exit 2024-11-08 11:11:06 +08:00
2dust
d12297909c Improve the code for WPF Global Window 2024-11-08 11:04:03 +08:00
fonaix
5fb4edae2d 添加:MacOS 代理配置与清除 (#6018)
* 添加:MacOS 代理配置与清除

* 修复:点击表头排序时,超时服务器排在前面的问题
2024-11-08 09:21:43 +08:00
DecorativeFamily
c4b490e46d [CodeFactor] Apply fixes (#6005)
Co-authored-by: codefactor-io <support@codefactor.io>
2024-11-07 14:46:27 +08:00
2dust
45febe3fff up 7.0.7
https://github.com/2dust/v2rayN/issues/5970
2024-11-06 15:00:45 +08:00
2dust
689a81a985 Built-in font for Desktop
https://github.com/2dust/v2rayN/issues/5970
2024-11-06 14:52:54 +08:00
2dust
28620b385a Update MsgView.xaml
https://github.com/2dust/v2rayN/issues/5997
2024-11-05 19:49:27 +08:00
2dust
be13446e69 Speed ​​test address maintenance 2024-11-04 14:21:11 +08:00
2dust
ee57d5b8e6 Improve checkup date 2024-11-04 14:15:59 +08:00
2dust
4eda3dd8fa Add Memo attribute to subscription group
https://github.com/2dust/v2rayN/issues/5981
2024-11-04 10:42:42 +08:00
2dust
911dc7f90e Revert
9fd20ff001
2024-11-04 10:37:44 +08:00
fonaix
18005b96e8 修复:双击表头弹出编辑窗口的问题 (#5984)
* 修复:日志文本框垂直显示上下空白问题

* 修复:双击表头弹出编辑窗口的问题
2024-11-04 09:44:03 +08:00
2dust
a3e45d206e Bug fix
https://github.com/2dust/v2rayN/issues/5979
2024-11-03 21:15:50 +08:00
2dust
0accf262dc up 7.0.6 2024-11-03 16:49:06 +08:00
2dust
e0eb73bb0a Code clean 2024-11-03 16:45:48 +08:00
2dust
258e822c13 Add exit function to the main interface for Desktop 2024-11-03 14:48:13 +08:00
2dust
4f05b93d63 Bug fix
MainModule FileName only exists in Win32.
2024-11-03 13:44:12 +08:00
2dust
bb661d4f50 Only one instance is allowed to run 2024-11-03 13:30:32 +08:00
2dust
201cfaa922 Fix
https://github.com/2dust/v2rayN/issues/5966
2024-11-02 15:02:30 +08:00
2dust
c339aa349c up PackageReference 2024-11-02 14:42:26 +08:00
2dust
046421f345 Code clean 2024-11-02 14:42:01 +08:00
fonaix
3a2c9e7aaa 修复:日志文本框垂直显示上下空白问题 (#5967) 2024-11-02 10:28:18 +08:00
2dust
0bb6e6bd86 up 7.0.5 2024-11-01 16:54:16 +08:00
2dust
693e8ab157 Add startup auto-start for desktop 2024-11-01 14:54:23 +08:00
2dust
033c16c992 Refactor to select active nodes when updating subscriptions 2024-11-01 10:19:31 +08:00
2dust
6ec61433fb Only update the stable version of the core
https://github.com/2dust/v2rayN/issues/5950
2024-11-01 09:26:15 +08:00
2dust
072f773245 up 7.0.4 2024-10-31 09:59:59 +08:00
2dust
0086f65a96 Bug fix
b7f4fd7469
2024-10-30 08:57:10 +08:00
2dust
281f14f47e Adjustment of preset rule sets 2024-10-28 13:45:39 +08:00
2dust
9fd20ff001 De-duplication after subscription update 2024-10-28 09:38:20 +08:00
2dust
7df90a6034 Bug fix
https://github.com/2dust/v2rayN/issues/5928
2024-10-28 09:16:24 +08:00
2dust
019869ec28 up 7.0.3 2024-10-27 15:01:14 +08:00
2dust
b7f4fd7469 Add font settings for Desktop 2024-10-27 14:59:06 +08:00
2dust
1273d2aee1 Improved font settings 2024-10-27 14:57:45 +08:00
2dust
88990b4828 Bug fix
0efb0b5e3e
2024-10-26 18:00:22 +08:00
2dust
2f02c2970c UseShellExecute = true 2024-10-26 17:55:36 +08:00
2dust
ade789c6d4 Update UpgradeApp.cs 2024-10-26 17:55:08 +08:00
2dust
9738f90970 up 7.0.2 2024-10-26 15:00:57 +08:00
2dust
6ed0741339 Bug fix
https://github.com/2dust/v2rayN/issues/5923
2024-10-26 14:57:28 +08:00
2dust
e6b1e22245 Update UpgradeApp.cs 2024-10-26 14:42:58 +08:00
2dust
6b922be0c6 Set AssemblyName to v2rayN 2024-10-26 10:42:29 +08:00
2dust
6e35a260e8 Bug fix
https://github.com/2dust/v2rayN/issues/5915
2024-10-26 09:55:22 +08:00
2dust
1106fd8cf1 Give upgrade app execute permission at runtime 2024-10-25 20:32:47 +08:00
2dust
fb92b90d5c Bug fix
https://github.com/2dust/v2rayN/issues/5909
2024-10-25 17:58:54 +08:00
2dust
5effbee50b Fix
https://github.com/2dust/v2rayN/issues/5905
2024-10-25 17:41:01 +08:00
2dust
9bc50a9f34 up 7.0.1 2024-10-25 13:50:17 +08:00
2dust
2a5a339c27 Update UpgradeApp.cs 2024-10-25 11:56:15 +08:00
2dust
78d182fff3 Add a warning about the use of insecure HTTP protocol subscription address 2024-10-25 11:06:04 +08:00
2dust
0efb0b5e3e Give core execute permission at runtime 2024-10-25 10:10:55 +08:00
2dust
a2e8755730 Xray Asset Location use XRAY_LOCATION_ASSET 2024-10-25 09:31:16 +08:00
2dust
06ddedbc4c The core folder is all lowercase letters 2024-10-25 09:27:11 +08:00
2dust
fa148cdf42 Bug fix 2024-10-25 09:01:59 +08:00
2dust
c1f71dee15 up 7.0.0 2024-10-24 17:57:04 +08:00
2dust
871cb81323 Code clean 2024-10-24 17:56:39 +08:00
2dust
34f5c0f910 Adding checks for subscription url 2024-10-24 17:09:07 +08:00
2dust
3fafc6de93 Code optimization 2024-10-24 15:35:37 +08:00
2dust
b013213745 Remove simple routing settings 2024-10-24 10:55:15 +08:00
2dust
40ea454a7c Remove X86 support 2024-10-24 10:32:19 +08:00
2dust
f0bac4b4c8 Code optimization 2024-10-24 10:25:02 +08:00
2dust
ffb38129e2 The first letter of the guiconfig attribute must be capitalized. 2024-10-23 17:19:57 +08:00
2dust
3c550c094a The first letter of the guiconfig attribute must be capitalized. 2024-10-23 17:00:33 +08:00
2dust
2d143687b8 The first letter of the guiconfig attribute must be capitalized. 2024-10-23 10:42:35 +08:00
2dust
1e7284f141 Bug fix 2024-10-23 10:18:55 +08:00
DecorativeFamily
50bdf0927c use Dependabot keep actions updated to latest (#5886)
like: v2-->v3
2024-10-23 09:35:02 +08:00
2dust
10a60b26fa After the Linux version updates the core, set chmod+x 2024-10-22 20:08:33 +08:00
2dust
b3c2084b76 Code optimization, function asynchrony 2024-10-22 17:50:42 +08:00
2dust
3bf2dc711d Improved fontsize 2024-10-22 11:04:12 +08:00
2dust
5a7836115e Fix some code 2024-10-22 09:46:50 +08:00
gush4
0da9cb45bd Add icon for PAC mode (#5876)
Co-authored-by: author <mail@example.com>
2024-10-22 09:32:07 +08:00
2dust
28aa954f8c Improved CheckUpdate 2024-10-21 20:31:19 +08:00
2dust
4fc71fb77e Merge branch 'master' of https://github.com/2dust/v2rayN 2024-10-21 20:29:54 +08:00
2dust
5ea6b1e08a Update RetResult 2024-10-21 18:18:41 +08:00
runetfreedom
0cdfc97460 change ru templates path (#5869) 2024-10-21 13:47:38 +08:00
2dust
3dd54312e7 Code optimization, function asynchrony 2024-10-21 13:46:13 +08:00
2dust
a866017b4c Code optimization, function asynchrony 2024-10-21 09:45:33 +08:00
2dust
394b657fc9 Code optimization, function asynchrony 2024-10-20 11:51:05 +08:00
2dust
50449df08d Improved 2024-10-19 14:30:14 +08:00
2dust
fe3836be14 Bug fix QRCodeHelper 2024-10-19 14:29:53 +08:00
runetfreedom
055cd62dd8 Sing-box srs updating support (#5855) 2024-10-18 20:33:41 +08:00
runetfreedom
4d1f7fa60c External dns configs support + ru translation (#5854)
* Ru translation

* External dns for presets
2024-10-18 17:54:47 +08:00
2dust
5c0fba8744 Add scanning QR code from image 2024-10-18 17:35:32 +08:00
2dust
b74ddc0b43 Adjust Resources 2024-10-17 15:29:23 +08:00
2dust
d004c6860e Add check update for sing-box 2024-10-17 15:28:32 +08:00
2dust
fc3ba6c030 Improved UI 2024-10-16 19:36:38 +08:00
2dust
9c4dc185be Adjust some code
https://github.com/2dust/v2rayN/pull/5840
2024-10-16 15:42:44 +08:00
runetfreedom
2edbbc523a External routing rules templates + regional presets support (#5840)
* External routing rules templates + auto download geo file if repo changed

* Regional presets support
2024-10-16 14:11:20 +08:00
2dust
a5122b656d Bug fix for Dsktop 2024-10-16 10:52:45 +08:00
2dust
74f980aab1 Refactor system proxy 2024-10-16 09:20:05 +08:00
2dust
35e5475255 Add system proxy for kde 2024-10-15 20:22:26 +08:00
2dust
fb649c04cf Code clean 2024-10-15 15:17:05 +08:00
2dust
4ce7ca3c6f Fix
https://github.com/2dust/v2rayN/pull/5835
2024-10-15 13:37:36 +08:00
runetfreedom
eebb6735aa Add sing-box srs custom url support for fix TUN (#5835)
Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
2024-10-15 13:28:51 +08:00
2dust
e96c9abd69 Improved share Uri
https://github.com/2dust/v2rayN/issues/5807
2024-10-15 11:02:17 +08:00
2dust
fd7cf164ff Fix
https://github.com/2dust/v2rayN/issues/5821
2024-10-15 09:31:30 +08:00
2dust
24afe8bde4 Fix
https://github.com/2dust/v2rayN/pull/5829
2024-10-14 19:29:15 +08:00
2dust
9540669f56 prefer_ipv4 for sing-box dns strategy
https://github.com/2dust/v2rayN/issues/5790
2024-10-14 19:27:22 +08:00
runetfreedom
c6caf29b5b Custom geo file source from settings (#5829)
Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
2024-10-14 17:50:44 +08:00
2dust
2698137dea Improved UI
https://github.com/2dust/v2rayN/issues/5814
2024-10-14 17:28:10 +08:00
2dust
bdae08b13b Improved UI 2024-10-14 14:27:53 +08:00
2dust
019c69ecff Refactor Common 2024-10-14 10:57:40 +08:00
2dust
6b99b7eec5 Refactor Utils 2024-10-14 10:42:05 +08:00
2dust
4eb443e547 Bug fix for IsAdministrator
This reverts commit 7618f9f7d4.
2024-10-13 20:15:43 +08:00
2dust
e530789739 Improved Desktop TrayIcon 2024-10-12 19:46:06 +08:00
2dust
7618f9f7d4 Refactor new StatusBarViewModel 2024-10-12 15:45:21 +08:00
2dust
d7bde77977 up PackageReference 2024-10-11 15:06:48 +08:00
2dust
a0286ff810 Improved MessageBus contract 2024-10-11 10:17:44 +08:00
2dust
b172b03927 Improved UI for Desktop 2024-10-11 09:24:15 +08:00
2dust
a556bf9487 Improved ReactiveCommand.CreateFromTask 2024-10-10 18:02:09 +08:00
2dust
4f5362fdc4 Update build.ps1 2024-10-10 14:05:14 +08:00
2dust
176a91e7c5 Update build.ps1 2024-10-09 20:44:37 +08:00
2dust
3c45ef624a Rename v2rayUpgrade 2 AmazTool 2024-10-09 20:13:25 +08:00
2dust
fea7c9fbd7 Improved CliWrap 2024-10-08 20:45:56 +08:00
2dust
aa6b6e3849 Remove Mono.Posix.NETStandard 2024-10-08 17:40:25 +08:00
2dust
f5956e7bf0 Improved await _updateView 2024-10-08 14:55:06 +08:00
2dust
b2669103dc Refactor Upgrade 2024-10-08 14:03:15 +08:00
2dust
53e19ecb1d Improved Action Invoke 2024-10-08 13:47:13 +08:00
2dust
3e74bb65bd Improved models 2024-10-08 09:50:03 +08:00
2dust
f60575b77c Improved CoreInfo 2024-10-07 20:10:00 +08:00
2dust
7a839063d0 Refactor CoreHandler 2024-10-07 11:18:24 +08:00
2dust
4ccc7aa92d Refactor NoticeHandler 2024-10-07 10:59:13 +08:00
2dust
d5c6a42a9a Refactor some service 2024-10-07 10:39:43 +08:00
2dust
3bdef4d6d8 Rename LazyConfig to AppHandler 2024-10-07 09:51:41 +08:00
2dust
f40eb724d7 Add AppViewModel for Desktop 2024-10-06 21:16:17 +08:00
2dust
4d84eede56 Refactor ProxySettingWindows 2024-10-06 20:44:43 +08:00
2dust
53a2fbd0ff Bug fix 2024-10-06 10:42:08 +08:00
2dust
409fe5290e Implementing IsAdministrator for non-Windows 2024-10-06 10:23:44 +08:00
2dust
75d86cf883 Remove SagerNet clash clash_meta 2024-10-05 17:51:31 +08:00
2dust
b33f536dbd Improved UI for Desktop PathIcon 2024-10-05 14:53:34 +08:00
2dust
1f77c56eaf Fix TrayIcon display 2024-10-05 11:12:52 +08:00
2dust
fe895b1b27 Improved v2rayUpgrade 2024-10-05 10:43:37 +08:00
2dust
20bb263b06 Bug fix for check update 2024-10-04 17:10:30 +08:00
2dust
3ecbd3bc10 Improved UI for Desktop CheckUpdate 2024-10-04 16:01:36 +08:00
2dust
fe81b51dcb Add BackupAndRestore 2024-10-04 15:22:32 +08:00
2dust
90ba80436d Improved UI for Desktop version 2024-10-04 14:25:09 +08:00
2dust
2bb5f6afc4 Add DialogHost fro Desktop 2024-10-03 17:22:07 +08:00
2dust
3971318ffb Improved UI for routing rules 2024-10-03 13:56:06 +08:00
2dust
5fbeb4b0fb Improved UI for Desktop version 2024-10-02 21:08:17 +08:00
NagisaEfi
1b2b838bc0 Update ResUI.resx and ResUI.zh-Hant.resx (#5767) 2024-10-01 09:49:02 +08:00
2dust
8e28cc01b8 Fix TextFormattingMode = Display
https://github.com/2dust/v2rayN/discussions/5740
2024-09-30 09:29:06 +08:00
2dust
77a59e9107 StorageUI and RestoreUI 2024-09-29 20:59:53 +08:00
2dust
1411643192 Add system proxy for Linux 2024-09-28 10:41:30 +08:00
2dust
fde2a768cf Improved UI for Desktop version 2024-09-27 20:25:06 +08:00
2dust
b008a58cf8 Code improvements 2024-09-27 09:58:33 +08:00
2dust
8e02394ff4 Adjustments to automatically update GEO files 2024-09-27 09:58:09 +08:00
2dust
60a75d9a31 up 6.60 2024-09-23 17:23:23 +08:00
2dust
24566901fb up PackageReference 2024-09-23 17:22:14 +08:00
2dust
cc428618e5 Adjusting the text display 2024-09-23 17:17:12 +08:00
2dust
4d330cedac Disable auto update geo 2024-09-23 17:05:03 +08:00
2dust
2503583498 System proxy and tun mode
https://github.com/2dust/v2rayN/issues/5651
2024-09-23 17:03:28 +08:00
2dust
1b11916e1e Preparing to remove part of the core
https://github.com/2dust/v2rayN/discussions/5703
2024-09-23 15:16:32 +08:00
2dust
3dfd557265 Fix the problem of showing and hiding statistics columns 2024-09-23 15:00:25 +08:00
2dust
cd6bea28b6 Improved font display
https://github.com/2dust/v2rayN/issues/5713
2024-09-23 14:10:58 +08:00
2dust
054f0f2bc7 Bug fix
https://github.com/2dust/v2rayN/issues/5725
2024-09-23 13:37:18 +08:00
2dust
97347674e7 Bug fix
https://github.com/2dust/v2rayN/issues/5724
2024-09-23 12:09:17 +08:00
2dust
36d15852aa Setting up not to automatically update the GEO file on first run
https://github.com/2dust/v2rayN/issues/5704
2024-09-23 11:04:37 +08:00
2dust
4813610492 When running for the first time, switch the interface language to English when judging that the system language is not Chinese.
https://github.com/2dust/v2rayN/issues/5670
2024-09-23 11:01:03 +08:00
2dust
243dbab06a Fix ?! 2024-09-18 19:54:38 +08:00
2dust
073eaa9a26 Bug fix 2024-09-18 09:31:14 +08:00
2dust
cb1f936d05 Fix Speedtest bug 2024-09-17 18:41:01 +08:00
2dust
a7f3a7b1a7 up 6.59 2024-09-17 18:07:07 +08:00
2dust
71eb5f0813 HyperlinkButton Classes="WithIcon" 2024-09-17 17:26:01 +08:00
2dust
b4f50258a7 Fix style for desktop 2024-09-17 17:20:37 +08:00
2dust
346a9c5fcc Add Connections Host Filter 2024-09-17 16:59:35 +08:00
2dust
61635db5b5 Add IsNotEmpty function 2024-09-17 16:52:41 +08:00
2dust
4c0a59a715 Add Connections Host Filter
https://github.com/2dust/v2rayN/issues/5683
2024-09-17 14:50:31 +08:00
2dust
aa829a66ea Improved Style for Desktop version 2024-09-16 18:21:30 +08:00
2dust
885f193a00 Fix GetVersion 2024-09-16 15:34:35 +08:00
2dust
0a9bbf526d up PackageReference 2024-09-16 15:26:17 +08:00
2dust
ff6716b39d Restore backup file check 2024-09-16 15:23:06 +08:00
2dust
8505f2db96 Optimize backup
https://github.com/2dust/v2rayN/issues/5681
2024-09-16 13:37:38 +08:00
2dust
233d605256 Optimize the information function 2024-09-16 11:09:44 +08:00
2dust
c4e01d20a0 up 6.58 2024-09-08 13:39:13 +08:00
2dust
07156cb55c Bug fix 2024-09-08 10:45:05 +08:00
2dust
8361b4e4a0 Fix 2024-09-07 19:04:19 +08:00
2dust
bb90671979 Fix 2024-09-07 18:08:56 +08:00
2dust
beddc71ed8 Add backup and restore 2024-09-07 14:52:33 +08:00
2dust
d5f1cc99ac Refactor V2rayUpgrade 2024-09-06 18:37:46 +08:00
2dust
d03a86292e Add SaveFileDialog 2024-09-06 15:51:06 +08:00
2dust
c061a24948 Update v2rayUpgrade 2024-09-06 14:07:48 +08:00
2dust
9c7446f820 In-app update alone package 2024-09-06 14:07:26 +08:00
2dust
8be1730719 Bug fix 2024-09-06 09:40:45 +08:00
2dust
7ceaf5c071 Add preprocess of yaml file
https://github.com/2dust/v2rayN/issues/5646
2024-09-06 08:57:42 +08:00
2dust
7901a59aa4 Code clean 2024-09-05 13:29:51 +08:00
2dust
8343a1002f Improvement check updates for Desktop 2024-09-05 13:29:25 +08:00
2dust
c83dce3cc0 Fix
https://github.com/2dust/v2rayN/issues/5644
2024-09-05 10:02:26 +08:00
2dust
14afeab2bb Fix
https://github.com/2dust/v2rayN/issues/5640
2024-09-05 09:55:36 +08:00
2dust
c22b57927c up 6.57 2024-09-04 16:23:10 +08:00
2dust
81b84d235c Code clean 2024-09-04 16:15:31 +08:00
2dust
488e8aab00 Improvement check updates 2024-09-04 15:47:17 +08:00
Wydy
fc43a9a726 Update pac.txt (#5636) 2024-09-04 09:42:54 +08:00
2dust
31947fdcb3 Bug fix
https://github.com/2dust/v2rayN/issues/5635
2024-09-03 19:30:23 +08:00
2dust
34c7963d28 Bug fix
https://github.com/2dust/v2rayN/issues/5627
2024-09-03 16:14:49 +08:00
2dust
d91b0afb9a Improved log message display 2024-09-03 16:03:26 +08:00
2dust
82eb3fd6bd Unified processing version 2024-09-03 10:16:50 +08:00
2dust
3d3d3f83df Bug fix
https://github.com/2dust/v2rayN/issues/5619#issuecomment-2323968966
2024-09-02 15:10:34 +08:00
2dust
6879c75bc8 Using Queues to Improve Message Display 2024-09-02 14:20:09 +08:00
2dust
b02ad6cdab up 6.56 2024-09-01 19:56:11 +08:00
2dust
f7b16952ea PackageReference 2024-09-01 19:51:18 +08:00
2dust
f0d05a7d4e Refactor check updates 2024-09-01 16:39:45 +08:00
2dust
d6ca317b20 xray mux add more option
https://github.com/2dust/v2rayN/issues/5565
2024-08-31 10:12:21 +08:00
2dust
2879fddfd9 sinb-box mux add padding option
https://github.com/2dust/v2rayN/issues/5544
2024-08-31 09:38:15 +08:00
2dust
b6c09470fc Add global exception catching 2024-08-30 13:47:53 +08:00
2dust
9fdf6c6c32 Bug fix 2024-08-30 13:15:25 +08:00
2dust
79085af994 Bug fix 2024-08-30 12:59:43 +08:00
2dust
d189f4b443 Add custom config Socks port to the group
https://github.com/2dust/v2rayN/issues/5576
2024-08-30 10:12:40 +08:00
2dust
c9d65e5cd9 Fix
https://github.com/2dust/v2rayN/issues/5595
2024-08-30 09:31:44 +08:00
2dust
639d62588a Refactor click event 2024-08-29 20:32:00 +08:00
2dust
6c9db51fd5 Creating a new desktop app with avaloniaui 2024-08-29 15:48:51 +08:00
2dust
f0dbb6b22c Bug fix 2024-08-29 15:19:03 +08:00
2dust
f10f7b6268 Add Portable Mode for mihomo 2024-08-29 10:10:55 +08:00
2dust
48a5cbc552 Check for standalone windows .Net version 2024-08-29 10:09:45 +08:00
2dust
6721d150e0 Refactor QRCodeHelper 2024-08-28 10:20:37 +08:00
2dust
54c16cad7d Adding OSPlatform Processing 2024-08-27 19:44:50 +08:00
2dust
ae3ab15245 Add Socks Protocol string 2024-08-27 16:09:07 +08:00
2dust
d3c0f50fec Adjustment of server binding 2024-08-27 13:18:18 +08:00
2dust
43753b1b7a Add linux download url 2024-08-27 13:06:39 +08:00
2dust
6f3e4b3682 Adding OSPlatform Processing 2024-08-26 18:45:41 +08:00
2dust
b57cdd31bd Refactor_updateView to add task 2024-08-22 19:51:10 +08:00
2dust
b936c194e4 Refactor v2rayUpgrade 2024-08-22 15:23:29 +08:00
2dust
064431421a Bug fix 2024-08-21 21:12:01 +08:00
TTG
9d49c7aad0 Update custom_routing_white (#5566) 2024-08-21 11:18:15 +08:00
2dust
a6f27e5071 Code clean 2024-08-20 14:30:45 +08:00
2dust
8d1d10b783 Refactoring Project 2024-08-20 14:15:29 +08:00
2dust
61bea05f63 Refactoring Project 2024-08-19 18:15:54 +08:00
2dust
bbe7c7b884 Optimized code 2024-08-18 15:04:56 +08:00
Milinda Brantini
a432852b78 Update cidr for direct outboundTag (#5550)
Signed-off-by: Milinda Brantini <C_A_T_T_E_R_Y@outlook.com>
2024-08-18 09:59:33 +08:00
2dust
770e8b8cfa >Export Base64-encoded Share Links to Clipboard 2024-08-17 20:53:42 +08:00
2dust
7faabdc375 Refactor code to decouple view and viewmodel 2024-08-17 10:53:02 +08:00
2dust
a9860418ba Optimized code 2024-08-15 21:03:00 +08:00
2dust
30ff9d0ea9 Optimized code
CoreInfoHandler
2024-08-15 20:13:59 +08:00
2dust
bbc2298939 Bug fix
https://github.com/2dust/v2rayN/issues/5517
2024-08-15 13:58:07 +08:00
2dust
3286e8e24d Refactor code to decouple view and viewmodel 2024-08-15 13:51:30 +08:00
2dust
372f3991e1 Refactor code to decouple view and viewmodel 2024-08-12 20:25:31 +08:00
2dust
9aa5c0d135 Refactor code to decouple view and viewmodel 2024-08-11 20:44:29 +08:00
2dust
9e9808e489 Refactor code to decouple view and viewmodel 2024-08-11 15:45:55 +08:00
2dust
3dd75b17cf Clean code 2024-08-10 19:20:31 +08:00
2dust
2504b4737b Refactor code to decouple view and viewmodel 2024-08-10 14:44:25 +08:00
2dust
8ff04dca0d Refactor code to decouple view and viewmodel 2024-08-10 10:02:00 +08:00
2dust
d893ee4829 up 6.55 2024-08-05 14:41:54 +08:00
2dust
dfc5ec0705 Improve UI 2024-08-05 14:39:04 +08:00
2dust
8023eb74c9 Bug fix 2024-08-04 19:26:01 +08:00
2dust
bb4d3997ad Bug fix
https://github.com/2dust/v2rayN/issues/5467
2024-08-04 09:40:33 +08:00
2dust
7ede3af762 up 6.54 2024-08-03 16:02:42 +08:00
2dust
97ea1c7a9e Add toolTip 2024-08-03 15:59:00 +08:00
2dust
43bb2c0fb8 Bug fix
https://github.com/2dust/v2rayN/issues/5462
2024-08-03 15:37:47 +08:00
2dust
6e7196bb27 Bug fix
https://github.com/2dust/v2rayN/issues/5437
2024-07-31 20:13:41 +08:00
2dust
87a71d58e8 Fix
https://github.com/2dust/v2rayN/issues/5438
2024-07-30 16:22:54 +08:00
2dust
32ffd43fe3 Bug fix
https://github.com/2dust/v2rayN/issues/5425
2024-07-30 16:06:43 +08:00
2dust
6500c8d85e up 6.53 2024-07-26 11:05:53 +08:00
2dust
9866d436da Add Outbound DNS address
https://github.com/2dust/v2rayN/issues/5387
2024-07-26 11:00:07 +08:00
2dust
0f4884d9d8 Improve and refactor the code 2024-07-25 10:26:39 +08:00
2dust
5a81441351 Adjust resx 2024-07-24 20:14:07 +08:00
2dust
e8721bfb6b Default dns changed to whitelist 2024-07-24 20:01:26 +08:00
2dust
1b09d95209 Improve UI 2024-07-24 19:51:13 +08:00
Jabin Kong
35f3b5a50e 1 (#5394) 2024-07-23 20:41:00 +08:00
2dust
ff5203a561 alpn h2,h3 2024-07-23 20:33:02 +08:00
2dust
c3c1ced309 up 6.52 2024-07-21 15:15:51 +08:00
2dust
08b1c8ec83 Improve latency testing 2024-07-21 15:15:00 +08:00
NagisaEfi
1dd3ee4e0a Update English translations (#5381)
* Update ResUI.resx

* Update ResUI.resx
2024-07-21 11:48:55 +08:00
Random Guy
06d0c6517d fix: add real delay test for custom config (#5377) 2024-07-21 11:44:12 +08:00
2dust
4938ce6364 Code clean 2024-07-21 11:37:11 +08:00
2dust
25f3fc354f Add multi-server load balancing 2024-07-21 11:26:10 +08:00
2dust
ad1a1f8015 Main window layout orientation setting 2024-07-19 20:31:07 +08:00
2dust
5c070e2ca8 Improve UI 2024-07-19 10:51:14 +08:00
2dust
70ea21fca2 Improve UI 2024-07-18 19:59:46 +08:00
2dust
bc3593871b Refactor the main interface 2024-07-18 17:39:11 +08:00
2dust
355a424be2 Code clean 2024-07-18 17:31:49 +08:00
2dust
0ffa9a0cc8 Remove unused resx
Remove Batch export subscription to clipboard
2024-07-15 20:17:55 +08:00
2dust
72fecb2b9a Add clash_mode to rule 2024-07-14 20:17:48 +08:00
2dust
4e9dfe5478 Improve 2024-07-14 17:16:07 +08:00
2dust
c79e2e3ad4 Add functionality multi-server set as active
Implemented using sing-box Selector, which can be switched using the clash api
2024-07-14 16:13:28 +08:00
2dust
7c33c1c322 Add reload for ClashProxiesView 2024-07-14 14:28:45 +08:00
2dust
ea32f75925 Code clean 2024-07-14 11:00:22 +08:00
2dust
7eaed21b9a Improve 2024-07-14 10:59:36 +08:00
2dust
cfe8fcd28d up 6.51 2024-07-12 14:23:29 +08:00
2dust
9f4718af70 Code clean
Add allowInsecure to Share
2024-07-11 14:42:01 +08:00
2dust
39faccfd0b Bug fix 2024-07-10 17:32:26 +08:00
2dust
7545763dae Default domain strategy for resolving the outbound domain names -- singbox 2024-07-08 18:45:29 +08:00
2dust
c2928be35d Log messages are not displayed when in the background 2024-07-08 17:16:49 +08:00
2dust
449bb40d48 sing-box prioritizes the use of local srs 2024-07-08 14:20:41 +08:00
2dust
4caf1a1e63 Improve and refactor the code 2024-07-08 09:52:11 +08:00
2dust
01b205e6f1 Adjust information DispatcherPriority 2024-07-07 21:11:04 +08:00
2dust
160d3843a6 Bug fix 2024-07-05 15:28:28 +08:00
2dust
5efb784115 up 6.50 2024-07-05 11:18:36 +08:00
2dust
acde5b8384 Bug fix 2024-07-05 11:18:14 +08:00
2dust
373ee6586a Optimize system proxy code 2024-07-04 17:07:05 +08:00
2dust
215308c329 Change srs source
https://github.com/2dust/sing-box-rules
2024-07-04 16:27:44 +08:00
2dust
478cadba5e Bug fix 2024-06-30 20:43:06 +08:00
2dust
f4af4da791 Adjust res 2024-06-30 10:00:28 +08:00
2dust
22d4f435de Add tun and mixin functions to clash 2024-06-28 20:29:44 +08:00
2dust
ccb8cd5c04 up 6.49 2024-06-28 16:14:45 +08:00
2dust
e963f9e349 Add clash display in the main interface 2024-06-28 16:14:19 +08:00
2dust
e3c2a4b8da Bug fix 2024-06-28 10:41:17 +08:00
2dust
324f46cdad Bug fix
https://github.com/2dust/v2rayN/discussions/5268
2024-06-27 17:59:39 +08:00
2dust
691b19b5fd Adding support for the clash proxies interface 2024-06-27 16:36:36 +08:00
2dust
63ed2311dc Bug fix 2024-06-26 16:00:37 +08:00
2dust
d59e59ca40 up 6.48 2024-06-24 17:42:37 +08:00
2dust
c9a278b4a2 Check for updates mihomo 2024-06-24 17:41:53 +08:00
2dust
8ee5304148 Bug fix 2024-06-24 16:42:17 +08:00
2dust
3beaa8145f Bug fix 2024-06-24 16:19:00 +08:00
2dust
e30c55a0af up 6.47 2024-06-24 15:34:41 +08:00
2dust
b583590a64 Update ConfigHandler.cs
https://github.com/2dust/v2rayN/pull/5264
2024-06-24 15:25:00 +08:00
OnceUponATimeInAmerica
a28c63168a Support for setting subs-group remarks (group's name) from the (optional) "remark" query parameter in the pasted subs url (#5264) 2024-06-24 15:20:16 +08:00
2dust
620422350f Improved tun mode
Enabling tun mode.
Only one core will be run when using sing-box core;
When using a non-sing-box, the sing-box will be used to start an additional front Socks service to provide a tun entry, which will then run two cores
2024-06-24 15:19:42 +08:00
2dust
b77cc3c33b Add network attribute to route rule
https://github.com/2dust/v2rayN/discussions/5256
2024-06-22 16:44:15 +08:00
2dust
71bf9b4887 up 6.46 2024-06-21 16:02:08 +08:00
2dust
9039d401da set DOTNET_EnableWriteXorExecute=0 under win10
https://github.com/2dust/v2rayN/issues/5186
2024-06-21 14:09:57 +08:00
2dust
74a0a93201 Improve and refactor the code 2024-06-21 11:15:35 +08:00
2dust
522571f0b3 Add splithttp transport for xray
https://github.com/XTLS/Xray-core/releases/tag/v1.8.15
2024-06-20 16:20:05 +08:00
Lsyx-Good
123c49c22d Adjust singbox DNS server and rule position (#5229) 2024-06-20 16:08:08 +08:00
OnceUponATimeInAmerica
1ff88d29be Extend stats reporting to support custom configs with several load-balanced outbounds in them, with tags starting with "proxy" e.g. proxy1, proxy2 (fully compatible with existing configs) (#5225) 2024-06-20 15:32:10 +08:00
Lsyx-Good
63d5a2a1be Fix bug (#5213) 2024-06-10 15:12:39 +08:00
2dust
1e9a6cb06b Refactor code 2024-06-04 09:48:04 +08:00
2dust
31748aa660 Refactor code 2024-06-03 17:50:18 +08:00
2dust
d0c6ea6a63 remove http://cachefly.cachefly.net/100mb.test 2024-06-03 14:06:59 +08:00
2dust
7ffe286a56 Up PackageReference 2024-06-03 07:37:49 +08:00
2dust
b99b30163b Bug fix 2024-06-03 07:37:26 +08:00
kirilllivanov
83b4f1e660 Fix incorrect filenames (#5122) 2024-05-18 17:11:38 +08:00
2dust
b57ba6a98b up 6.45 2024-05-18 09:53:15 +08:00
2dust
d748e6eff4 Add destOverride 2024-05-14 15:31:19 +08:00
2dust
315d4b75b2 Optimize code 2024-05-14 13:42:42 +08:00
2dust
31267cbc33 Adjust Scan screen qrcode 2024-05-12 17:07:56 +08:00
2dust
c23379b3b6 Adjust sing-box dns 2024-05-12 15:42:28 +08:00
2dust
568144d6a2 TransitionAssist DisableTransitions 2024-05-12 14:59:23 +08:00
2dust
5857042963 Up 6.44 2024-05-08 13:40:24 +08:00
2dust
28e41dc621 Add open the file location 2024-05-08 11:43:03 +08:00
2dust
baef3b364b Bug fix 2024-05-08 11:42:01 +08:00
2dust
4b9ddb803f Bug fix 2024-05-07 14:15:08 +08:00
2dust
c07c7ad82f Added reminder that a reboot is required to modify settings 2024-05-06 17:19:29 +08:00
2dust
e223b80b95 Merge pull request #5066 from sincereliu/master
Optimize code for better performance
2024-05-06 09:55:30 +08:00
sincere liu
5fe468fa1b Optimize code for better performance 2024-05-05 22:07:51 +08:00
2dust
60068d8d16 Themes migrate 2024-05-05 16:19:47 +08:00
2dust
b6c5b46afe Up PackageReference 2024-05-05 10:10:19 +08:00
2dust
124cbfadb4 Add custom sing-box rule-set support 2024-05-04 13:54:22 +08:00
2dust
8d21f9b900 Bug fix 2024-05-04 13:24:13 +08:00
2dust
398dbbd2e5 Bug fix
https://github.com/2dust/v2rayN/issues/5035
2024-05-03 10:29:32 +08:00
2dust
e9b392d1c0 Optimized sorted storage 2024-04-30 14:44:02 +08:00
2dust
9d7c7e3225 Adjust the experimental and mux of sing-box 2024-04-27 10:05:13 +08:00
2dust
855fd4f0b7 Merge pull request #5026 from GibMeMyPacket/feat/singbox/exp_cachefile
Add `Enable cache file for sing-box`
2024-04-27 09:42:31 +08:00
GibMeMyPacket
807839929d Add Enable cache file for sing-box 2024-04-25 12:07:53 +00:00
2dust
870955fee1 Bug fix 2024-04-11 08:07:21 +08:00
2dust
e0cea929ea Up 6.43 2024-04-10 13:52:38 +08:00
2dust
315f4c35f0 Code clean 2024-04-10 13:32:50 +08:00
2dust
d961ea22a6 Built-in routing rule set upgraded to V2 2024-04-10 10:50:52 +08:00
2dust
5c0c07c78f Migrate geosite & geoip to rule sets (sing-box) 2024-04-10 08:42:53 +08:00
2dust
bba93a0fb7 Added tls fragment support (Xray-core)
https://github.com/2dust/v2rayN/issues/4900
2024-04-08 17:06:48 +08:00
2dust
5683df2fc0 Added outbound HTTP support (support group prefix proxy)
https://github.com/2dust/v2rayN/discussions/4550
2024-04-08 14:02:56 +08:00
2dust
b5cb9ce67e Set autoHideStartup default value is false 2024-04-05 17:24:53 +08:00
2dust
06a32dc895 Adjust sing-box dns default example 2024-04-05 11:05:45 +08:00
2dust
dff12a8a3a fix
https://github.com/2dust/v2rayN/issues/4928
2024-04-05 11:04:35 +08:00
2dust
dc3f07ee84 Bug fix 2024-04-03 09:51:07 +08:00
2dust
1aef49ee11 Custom configuration file to use Xray prefix in non-Tun mode
https://github.com/2dust/v2rayN/issues/4855
2024-04-01 08:22:14 +08:00
2dust
ee3159b00e Update 6.42 2024-03-31 17:26:02 +08:00
2dust
86d2c307f1 Bug fix 2024-03-31 17:23:33 +08:00
2dust
7823a217b4 Update 6.41 2024-03-31 08:09:13 +08:00
2dust
2004df8337 Rename Model to Models 2024-03-31 08:08:06 +08:00
2dust
13bc2d0340 Bug fix
https://github.com/2dust/v2rayN/issues/4902
2024-03-31 07:54:22 +08:00
2dust
edbc850346 Update 6.40 2024-03-30 18:12:37 +08:00
2dust
901d3c85ca Up PackageReference 2024-03-30 16:15:13 +08:00
2dust
abbe83f3c2 Fixed issues with multi-core traffic processing 2024-03-30 16:00:19 +08:00
2dust
bb3a04a9c8 Bug fix for exit app 2024-03-30 15:26:12 +08:00
2dust
bac13e8b71 Rename Utile to Utils 2024-03-26 14:26:03 +08:00
2dust
3871681de3 Improve remove Application.Current.Shutdown 2024-03-26 14:22:53 +08:00
2dust
ae6f2b6df6 Up PackageReference 2024-03-26 14:19:14 +08:00
2dust
f59bbc9981 Add authority for grpc 2024-03-23 18:26:44 +08:00
2dust
eac0c84e11 Improve IsNullOrEmpty 2024-03-23 18:26:04 +08:00
2dust
f814cc443d Improve 2024-03-23 10:47:06 +08:00
2dust
262466303b Import Multiple Custom v2ray-core Json Configs from Subscription
https://github.com/2dust/v2rayN/issues/4745
2024-03-23 10:46:45 +08:00
2dust
3b4cc2c5f2 Merge pull request #4848 from 1208nn/patch-2
Update schdtask related code
2024-03-16 16:25:58 +08:00
ShH Y
023d3fbf6f Update Utile.cs 2024-03-16 11:38:41 +08:00
2dust
fe01f290bc Improve 2024-03-13 16:38:37 +08:00
2dust
5805ac9f7f Update 6.39 2024-03-12 09:19:19 +08:00
2dust
4e2f38a343 Clean 2024-03-12 09:14:21 +08:00
2dust
572c924e5c Add httpupgrade 2024-03-12 09:05:59 +08:00
2dust
e90353e550 Bug fix 2024-03-11 14:46:24 +08:00
2dust
f8ae1b8c49 Bug fix 2024-03-11 08:34:07 +08:00
2dust
186d995919 Improve 2024-03-09 14:30:39 +08:00
2dust
70584aff9d Refactor 2024-03-09 10:12:45 +08:00
2dust
dcdc63785a HTTP port=SOCKS port+1;Pac port=SOCKS port+4;API port=SOCKS port+5; 2024-03-09 09:40:19 +08:00
2dust
0e7bfa65de Refactor 2024-03-09 09:27:55 +08:00
2dust
9427340ab7 Update 6.38 2024-03-08 09:55:13 +08:00
2dust
63af5bae8a Bug fix 2024-03-08 09:41:38 +08:00
2dust
9232f1fa40 Merge pull request #4802 from Array-Cats/master
Update README.md
2024-03-08 09:40:28 +08:00
Array-Cats
15bdb551f4 Update README.md 2024-03-07 23:56:58 +08:00
2dust
2cda2b53ed Improve 2024-03-07 11:51:45 +08:00
2dust
028c9ea0b5 Bug fix 2024-03-07 09:06:35 +08:00
2dust
315b51e7ca Up PackageReference 2024-03-05 09:45:27 +08:00
2dust
0185b3b145 Remove some pop-up messages 2024-03-04 10:41:42 +08:00
2dust
afaad49879 Bug fix 2024-03-02 10:35:33 +08:00
2dust
4b2b45979b Return api port=socks port +11 2024-03-02 10:00:28 +08:00
2dust
66e40edd0e Bug fix singbox transport type http 2024-03-02 09:58:50 +08:00
2dust
3d5168885f up 6.37 2024-02-29 17:25:50 +08:00
2dust
6eee2c2342 Add obfs=http support for sing-box ss 2024-02-29 10:11:27 +08:00
2dust
11ec27147a Use Global.None instead of none 2024-02-29 09:43:04 +08:00
2dust
a522a02ba2 Temporarily add compatibility to VLESS 2024-02-29 09:39:12 +08:00
2dust
5ff4d35a30 Added termination speed test 2024-02-25 18:28:59 +08:00
2dust
4c5546bf52 Merge pull request #4757 from NagisaEfi/master
Update translation
2024-02-25 09:28:06 +08:00
NagisaEfi
ab37a46e83 Update translation 2024-02-23 23:46:44 +08:00
2dust
0b3635b5c5 Bug fix 2024-02-21 10:17:28 +08:00
2dust
2a338d9a83 Merge pull request #4743 from NagisaEfi/master
Localization (CHT and ENG)
2024-02-20 16:46:17 +08:00
NagisaEfi
7031a4c26c Localization (CHT and ENG) 2024-02-20 15:59:35 +08:00
2dust
2c5ca97476 Merge pull request #4738 from NimaZare/master
spell check and update
2024-02-20 13:07:28 +08:00
NimaZare
fc0c8f6bb1 spell check and update 2024-02-19 13:13:36 +03:30
2dust
643547704e Up 6.36 2024-02-18 11:09:00 +08:00
2dust
4717b63775 Bug fix 4 mihomo 2024-02-18 11:02:50 +08:00
2dust
4af148f480 Bug fix 2024-02-15 10:34:43 +08:00
2dust
a9c59693ee Bug fix 2024-02-13 10:11:17 +08:00
2dust
9b3ac159c1 Merge pull request #4712 from hxdhttk/hxdhttk/statePortConf
Fix the state ports conflicts when multiple `v2rayN` instances start in a short period.
2024-02-13 09:57:59 +08:00
2dust
c03c98157f Merge pull request #4711 from hxdhttk/hxdhttk/mixedStdErr
Fix misleading "Cannot mix synchronous and asynchronous operation on process stream." logs.
2024-02-13 09:47:19 +08:00
Minghao Hu
74149b761f Resolve the StatePort when the other components call it. 2024-02-12 22:49:57 +09:00
Minghao Hu
44cfa2d8dc Add a start up error receiver. 2024-02-12 22:12:57 +09:00
Minghao Hu
b0fb00d597 Merge new commits. 2024-02-12 21:20:16 +09:00
2dust
af3c1dc039 Bug fix
https://github.com/2dust/v2rayN/issues/4707
2024-02-12 08:49:22 +08:00
2dust
1c94b5d5f0 Up 6.35 2024-02-10 17:07:30 +08:00
2dust
fbb00c0f85 Improve 2024-02-10 16:57:26 +08:00
2dust
4a72e63d1c When updating a subscription, only determine whether the alias already exists 2024-02-09 09:57:28 +08:00
2dust
ce94e0ba4d Refactor 2024-02-08 16:22:38 +08:00
2dust
e2c836794b Refactor 2024-02-08 14:01:33 +08:00
2dust
05e424f805 Bug fix 2024-02-08 09:32:01 +08:00
2dust
bf30819a4a Bug fix
https://github.com/2dust/v2rayN/issues/4334
2024-02-05 20:24:30 +08:00
2dust
52e9d30b46 Improve 2024-02-04 10:02:02 +08:00
2dust
1fa2d55e1d Up PackageReference 2024-02-03 10:27:40 +08:00
2dust
4d0ee652d8 Bug fix 2024-02-03 10:26:37 +08:00
2dust
ff215bc9aa Remove ping test 2024-02-02 10:39:07 +08:00
2dust
e1cadc878a Improve VmessQRCode json 2024-02-02 10:21:13 +08:00
2dust
57d9d8ddb9 Improve 2024-02-01 12:44:47 +08:00
2dust
f76b1a5363 Add editing subscription in the main interface 2024-01-31 10:16:21 +08:00
2dust
9b579be2be Try to remove the tun device when turning on 2024-01-31 09:56:47 +08:00
2dust
9d43465eab Improve 2024-01-30 18:29:52 +08:00
2dust
fc52870431 Add setting 4 SpeedPingTestUrl 2024-01-30 18:04:10 +08:00
2dust
c466ea100a Refactor 2024-01-30 17:18:00 +08:00
2dust
4054369611 Up PackageReference 2024-01-29 17:20:15 +08:00
2dust
11106937a3 Improve 2024-01-29 11:50:24 +08:00
2dust
fdddf901f3 Remove the main interface Encryption Method 2024-01-29 11:15:11 +08:00
2dust
2e30c04238 Migrate from Newtonsoft.Json to System.Text.Json 2024-01-28 19:53:20 +08:00
2dust
bd838094cd bug fix 2024-01-27 18:29:17 +08:00
2dust
6f22b74154 Improve 2024-01-27 10:57:40 +08:00
2dust
e71525db0e Refactor 2024-01-25 17:28:06 +08:00
2dust
d7c1516ec2 bug fix for test shadowsocks 2024-01-23 12:30:11 +08:00
2dust
ff91a5dad9 Add obfs 4 hysteria2 2024-01-22 18:45:22 +08:00
2dust
d76679c2b6 Fill alpns & bug fix 2024-01-20 17:43:42 +08:00
2dust
d7cb88f0a4 Bug fix 2024-01-20 09:45:00 +08:00
2dust
8a3eb41314 Add Wireguard uri 2024-01-16 11:05:51 +08:00
2dust
e2f9aac1c9 remove System.Web 2024-01-16 11:05:02 +08:00
2dust
08c7396404 Add mtu setting 4 Wireguard 2024-01-15 17:11:00 +08:00
2dust
69cbdbd20e Added Wireguard support (using sing-box) 2024-01-14 17:33:41 +08:00
2dust
005f26ba7c bug fix 2024-01-14 10:23:46 +08:00
2dust
a1dc0c8104 Update ResUI.resx 2024-01-14 10:04:02 +08:00
2dust
75435b8fe0 Merge pull request #4621 from yscjr/master
修改修改非管理员模式下tun模式启动逻辑;缩短自动隐藏时间
2024-01-14 10:00:09 +08:00
John Edwerd
6d7385788e 修改修改非管理员模式下tun模式启动逻辑;缩短自动隐藏时间 2024-01-13 21:54:03 +08:00
2dust
726d9d410f Refactor 2024-01-13 16:52:42 +08:00
2dust
ee2a61bb61 bug fix 2024-01-12 18:22:23 +08:00
2dust
40748d6e42 Update ResUI.zh-Hant.resx 2024-01-12 18:06:35 +08:00
2dust
02225ad0b8 Refactor 2024-01-11 18:34:33 +08:00
2dust
3c4703ad85 Optimize code 2024-01-11 18:24:32 +08:00
2dust
6762f35ade Optimize code 2024-01-10 17:44:55 +08:00
2dust
5135afcf8f Refactor 2024-01-10 16:32:26 +08:00
2dust
4aae82e0cc Adjust Tool code 2024-01-10 10:56:10 +08:00
2dust
ea95c6de79 Refactor Utils 2024-01-10 10:43:48 +08:00
2dust
4a67e963ed Up TargetFramework to net8.0 2024-01-10 10:01:59 +08:00
2dust
720f177d65 Merge pull request #4603 from ShiinaRinne/hysteria2_uri
fix https://github.com/2dust/v2rayN/issues/4590
2024-01-10 09:55:51 +08:00
ShiinaRinne
2b4a043a92 fix https://github.com/2dust/v2rayN/issues/4590 2024-01-09 23:20:30 +08:00
2dust
77b20bc562 Merge pull request #4594 from yin1999/bump-action
build(ci): bump actions/upload-artifact from v3 to v4
2024-01-09 09:57:45 +08:00
Allo
063449448a build(ci): bump actions/upload-artifact from v3 to v4 2024-01-08 21:35:28 +08:00
2dust
559ffcbab5 up 6.33 2024-01-06 13:47:03 +08:00
2dust
80ed8346be Bug fix 2024-01-06 10:41:07 +08:00
2dust
a49455f330 Code clean 2024-01-05 13:48:42 +08:00
2dust
c3de6f6d02 Merge pull request #4568 from ShiinaRinne/theme
fix: 当关闭"是否跟随系统主题"时,无法应用"暗黑模式"的设置
2024-01-03 20:07:43 +08:00
2dust
ee416b1bf2 Merge pull request #4567 from ShiinaRinne/patch-2
添加ico格式的自定义图标支持
2024-01-03 20:06:54 +08:00
2dust
784928ffc8 Merge pull request #4571 from canmengxian/patch-2
禁用删除工作流运行
2024-01-03 20:06:34 +08:00
残梦
609360a1d0 禁用删除工作流运行 2024-01-02 23:52:14 +08:00
ShiinaRinne
18ce2f7b1c fix: 当关闭"是否跟随系统主题"时,无法应用"暗黑模式"的设置 2024-01-02 15:34:42 +08:00
ShiinaRinne
78625d82ae 添加ico格式的自定义图标支持 2024-01-02 15:03:21 +08:00
2dust
2ca34fb9ad Bug fix 2023-12-29 18:16:02 +08:00
2dust
f5f944aa50 up 6.32 2023-12-29 10:02:24 +08:00
2dust
83b4ab83d1 Improve speed test,add Hy2 and Tuic test 2023-12-29 09:39:55 +08:00
2dust
34f63f9006 Add ipv6 option for Tun mode 2023-12-26 18:13:12 +08:00
2dust
b74188d904 Previous and next proxy 4 sing-box 2023-12-25 16:42:23 +08:00
Minghao Hu
ba246e99e8 Fix mixed sync/async ops on Process streams. 2023-12-24 16:21:21 +09:00
2dust
51576b54c3 Previous and next proxy setting 2023-12-24 10:34:55 +08:00
2dust
0d5a9fcf4a Previous and next proxy 2023-12-23 20:57:31 +08:00
2dust
b0c5f74edb Refactor 2023-12-23 17:49:39 +08:00
2dust
1ac7661593 Refactor 2023-12-23 10:19:41 +08:00
2dust
caa2c523f4 Remove singbox update geo file 2023-12-22 16:56:09 +08:00
2dust
7b9f1e6788 Refactor 2023-12-22 16:03:25 +08:00
2dust
86f3ed29f9 Adjust cores 2023-12-22 12:13:36 +08:00
2dust
655d411afe Fix something 2023-12-22 09:46:55 +08:00
2dust
f5deb8e168 Code clean 2023-12-19 18:09:11 +08:00
2dust
fcdb61bfbf Code clean 2023-12-19 16:49:45 +08:00
2dust
bd9a4ca094 Added Tuic v5 support (using sing-box) 2023-12-19 16:03:30 +08:00
2dust
e2657e746d UI adjust 2023-12-16 17:45:52 +08:00
2dust
6f5a174231 Update Package Reference 2023-12-16 17:45:29 +08:00
2dust
91e9c0b02b up 6.31 2023-12-03 17:03:33 +08:00
2dust
658ea26ab7 Bug fix 2023-12-03 17:02:34 +08:00
2dust
77b736541e Bug fix 2023-12-01 17:03:49 +08:00
2dust
843b881da6 Merge pull request #4464 from ShiinaRinne/host
feat: 支持导入系统hosts追加至dns设置
2023-12-01 16:21:37 +08:00
2dust
3ca8acc805 Merge pull request #4457 from canmengxian/patch-1
优化自动化编译
2023-12-01 16:02:48 +08:00
ShiinaRinne
63809d4f16 Added: 支持导入系统hosts添加至dns设置中 2023-12-01 12:37:49 +08:00
残梦
557660b16b 优化自动化编译 2023-11-28 23:56:44 +08:00
2dust
12be8bda52 Code clean 2023-11-28 16:07:53 +08:00
2dust
81efd25e0a Added Hysteria2 support (using sing-box) 2023-11-28 15:45:53 +08:00
2dust
7037009ce4 Added Hysteria2 support (using sing-box) 2023-11-27 17:58:34 +08:00
2dust
4a0902d4c8 Bug fix 2023-11-26 10:39:03 +08:00
2dust
ed25ac1748 Merge pull request #4444 from flyoutsea/master
Fixed Issue #3875 that ProxySetting doesn't support PPPoE connection. Remove unnecessary sysproxy.exe.
2023-11-26 10:03:27 +08:00
flyoutsea
55e09ed062 Fix build warnings: unused variable and obsolete dpiAware settings. 2023-11-25 21:48:33 +08:00
flyoutsea
babb548c22 Fixed Issue #3875 that ProxySetting doesn't support PPPoE connection. Remove sysproxy.exe, which is the unnecessary workaround. 2023-11-25 21:29:59 +08:00
2dust
1d0e8434ad up 6.30 2023-11-22 16:47:57 +08:00
2dust
e7b2deb941 In tun mode, do a delay check and restart the core 2023-11-22 16:37:46 +08:00
2dust
2a37a6a28e Merge pull request #4432 from The-Mojoo/master
Add Update CoreTypeName
2023-11-22 16:31:56 +08:00
Jimmy Mo
735ebbe92a Add Update CoreTypeName 2023-11-22 11:48:49 +08:00
2dust
2031729656 Merge pull request #4409 from Dts0/master
Fix the issue of readonly custom config  and add github workflow
2023-11-18 18:06:57 +08:00
zhaojingshi
f39fdc38cb add github workflow
The output can be found in actions->workflow->Artifacts
2023-11-18 10:22:47 +08:00
zhaojingshi
6b62623615 Fixed the issue of readonly custom config
Fixed the issue where importing read-only custom files would cause the service to fail to start
2023-11-18 10:22:37 +08:00
2dust
f3f3dc1951 Adjust some core 2023-11-17 13:38:50 +08:00
2dust
f4dd970f5e Add core hysteria2 2023-11-16 20:08:17 +08:00
2dust
74332c58c5 Adjust some core 2023-11-16 19:56:14 +08:00
2dust
f7ce49020f Update Package Reference 2023-11-16 17:47:32 +08:00
2dust
aaba1f7209 Merge pull request #4405 from ShiinaRinne/patch-1
fix https://github.com/2dust/v2rayN/pull/4287#discussion_r1394166976
2023-11-16 17:37:46 +08:00
ShiinaRinne
a9c7916b3d fix https://github.com/2dust/v2rayN/pull/4287#discussion_r1394166976 2023-11-15 21:01:38 +08:00
2dust
e3c8ccfafe Merge pull request #4385 from ShiinaRinne/font-ttc
Add: 添加TTC格式字体支持
2023-11-10 15:09:34 +08:00
2dust
2a22b65b2d Merge pull request #4321 from hxdhttk/hxdhttk/keyDown
Fix that editing the basic routing settings will trigger unexpected error messages.
2023-11-10 15:06:18 +08:00
2dust
ce5728e2df Merge pull request #4287 from MichaelHinrichs/Switch
Use a switch
2023-11-10 15:03:03 +08:00
2dust
cba82e0e8e Adjust speedtest addr 2023-11-10 15:00:48 +08:00
ShiinaRinne
be30064f9a Merge branch 'master' into font-ttc 2023-11-09 14:07:16 +08:00
ShiinaRinne
1798981380 Update ResUI.zh-Hant.resx 2023-11-09 13:56:27 +08:00
ShiinaRinne
06586d3405 Add: 添加TTC格式字体支持
https://github.com/2dust/v2rayN/issues/4089
2023-11-09 13:03:45 +08:00
Minghao Hu
31bcbd0e13 Do not execute keyboard shorcut on basic routing setting window. 2023-10-12 05:05:30 +08:00
2dust
2652beed88 Merge pull request #4299 from hxdhttk/hxdhttk/notifyUpd
Update the H.NotifyIcon to show default system tooltip for sys tray icon.
2023-09-30 14:25:03 +08:00
Minghao Hu
a2b87b862f Merge branch '2dust:master' into hxdhttk/notifyUpd 2023-09-29 14:38:06 +08:00
NintenHero
85e8d9479e Fix switch 2023-09-25 10:05:56 -05:00
NintenHero
119508a4e3 Use a switch 2023-09-25 04:45:50 -04:00
2dust
3c2da0a225 up 6.29 2023-09-24 16:17:54 +08:00
Minghao Hu
ef79e6adf9 Use latest update. 2023-09-24 02:05:04 +08:00
2dust
528400579a Merge pull request #4255 from crazypeace/master
解决当订阅数量较多时,占用窗口空间太大的问题
2023-09-22 14:57:07 +08:00
2dust
521ce1a487 Merge pull request #4235 from YheonYeung/patch-1
修正繁中用詞
2023-09-22 14:56:17 +08:00
Minghao Hu
014cc9bc16 Update H.NotifyIcon. 2023-09-17 16:10:05 +08:00
crazypeace
34c6c953ac 考虑在更大的屏幕上可以接受更高尺寸的占用
https://zelikk.blogspot.com/2023/09/v2rayn-lstgroup-maxheight.html
2023-09-09 15:59:08 +08:00
crazypeace
40fbbc7c58 设置 订阅/分组 显示空间的最大高度
避免占用过大的窗口面积
https://github.com/2dust/v2rayN/issues/4224
2023-09-09 15:00:59 +08:00
YheonYeung
534c329970 Update ResUI.zh-Hant.resx 2023-09-01 23:42:07 +08:00
YheonYeung
328d728257 Update ResUI.zh-Hant.resx 2023-09-01 23:39:16 +08:00
YheonYeung
fae6d42758 Update ResUI.zh-Hant.resx 2023-09-01 23:33:39 +08:00
YheonYeung
27693f6d23 Update ResUI.zh-Hant.resx 2023-09-01 23:31:24 +08:00
YheonYeung
89c4bab5b9 Update ResUI.zh-Hant.resx 2023-09-01 23:29:19 +08:00
YheonYeung
9d2ff04838 Update ResUI.zh-Hant.resx 2023-09-01 23:27:15 +08:00
YheonYeung
cf00243107 Update ResUI.zh-Hant.resx 2023-09-01 23:25:12 +08:00
YheonYeung
e8019ba7a5 修正繁中用詞 2023-09-01 23:21:10 +08:00
2dust
4f30e3f0e3 Merge pull request #4149 from eltociear/patch-1
fix typo in MainForm.cs
2023-08-02 10:15:55 +08:00
Ikko Eltociear Ashimine
047d08470f fix typo in MainForm.cs
entryOuputPath -> entryOutputPath
2023-08-02 00:33:10 +09:00
2dust
9643695389 Merge pull request #4144 from chika0801/master
修正 juicity 启动参数少了 run
2023-07-31 09:21:33 +08:00
chika0801
5cabec86e7 修正 juicity 启动参数少了 run
它的客户端启动参数是 ./juicity-client run -c config.json

https://github.com/juicity/juicity/blob/main/cmd/client/README.md#run
2023-07-30 21:33:12 +08:00
2dust
f2d0e37255 Merge pull request #4141 from chika0801/master
添加 juicity 作为自定义 core
2023-07-30 18:26:36 +08:00
chika0801
9560851a3f add juicity 2023-07-30 16:37:44 +08:00
chika0801
4aeec1caa1 add juicity 2023-07-30 16:36:48 +08:00
chika0801
ae45b1ef44 Update Global.cs 2023-07-30 16:33:11 +08:00
2dust
aad1b087c9 Up 6.28 2023-07-30 14:30:20 +08:00
2dust
b69c581fa1 Merge pull request #4123 from openGiraffes/master
繁体字典添加
2023-07-30 11:08:24 +08:00
2dust
9eb4d90cd6 Merge pull request #4122 from yu0A/master
Fix timestamps in log TextBox
2023-07-30 11:08:14 +08:00
Ding
958567c2c5 繁體支持 2023-07-24 10:55:45 +08:00
yu0A
f1fc4583ed fix #4121 2023-07-23 21:20:36 +08:00
Ding
f7bfd5f766 upload part1 2023-07-21 17:57:16 +08:00
2dust
aadc3c216a Merge pull request #4055 from meaqese/meaqese-patch-1
Russian lang improvements
2023-07-10 15:27:43 +08:00
2dust
6fd2499daf Merge pull request #4048 from crazypeace/master
解决窗口尺寸大于屏幕尺寸的问题 #
2023-07-10 15:26:20 +08:00
meaqese
f0405f1c5d imporvements 2 2023-07-04 21:16:01 +03:00
crazypeace
1914cb3994 Delete dotnet-desktop.yml 2023-07-03 01:50:27 +08:00
crazypeace
ab38fb572d Update dotnet-desktop.yml 2023-07-02 19:52:42 +08:00
crazypeace
46d7128a6e Update dotnet-desktop.yml 2023-07-02 19:49:09 +08:00
crazypeace
0821884ff3 Create dotnet-desktop.yml 2023-07-02 19:45:15 +08:00
crazypeace
62233281ef 与上游一致 2023-07-02 12:40:01 +08:00
crazypeace
39504502af Update AddServerWindow.xaml.cs 2023-07-02 12:22:17 +08:00
crazypeace
f97530a671 设置窗口的尺寸不大于屏幕的尺寸 2023-07-02 12:15:51 +08:00
crazypeace
0fe7d5e598 Update AddServerWindow.xaml.cs 2023-07-01 04:53:42 +08:00
crazypeace
527362dc9f 初始化窗口宽和高的最大值, 依据工作区的宽和高.
解决在笔记本屏幕上因为DPI较高导致窗口显示不全的问题.
2023-06-30 13:29:43 +08:00
meaqese
1023426c8d Russian imporvements 2023-06-18 12:34:53 +03:00
2dust
4458fd7541 Up 6.27 2023-06-18 16:44:03 +08:00
2dust
f0b03f59ff Up PackageReference 2023-06-18 16:33:18 +08:00
2dust
d5b17b3cfb Update MainWindowViewModel.cs 2023-06-18 16:23:03 +08:00
2dust
9664d1d77f Fix update the pre version 2023-06-18 16:18:50 +08:00
2dust
d329646a52 Merge pull request #3989 from hvvvvvvv/v2rayN_2
Update HotkeyHandler.cs
2023-06-18 09:43:50 +08:00
2dust
8eafe72bb0 Merge pull request #3943 from hsbtr/master
feat: pac rule add chat.openai.com
2023-06-18 09:43:31 +08:00
2dust
c38a0bde65 Optimize interface 2023-06-18 09:42:45 +08:00
2dust
eef5d3cc16 Update DNSSettingWindow.xaml.cs
https://github.com/2dust/v2rayN/issues/3992
2023-06-18 08:49:04 +08:00
咸鱼而已
59ada594a5 Merge pull request #1 from hvvvvvvv/hvvvvvvv-patch-1
Update HotkeyHandler.cs
2023-06-15 17:12:03 +08:00
咸鱼而已
5ec1d18143 Update HotkeyHandler.cs
fix bug
2023-06-15 16:58:04 +08:00
2dust
fd1e1bb6bf AddServerView CanResize 2023-06-11 19:57:00 +08:00
2dust
5d545d8a85 bug fixes 2023-06-08 20:36:13 +08:00
liuyan
e38bc45527 feat: pac rule add chat.openai.com 2023-06-05 17:42:06 +08:00
2dust
1b11425acd up 6.26 2023-06-04 20:31:05 +08:00
2dust
1e67850a80 Add inet6_address for sing-box 2023-06-04 20:30:00 +08:00
2dust
975f89456f Update ProtosLib.csproj 2023-06-04 17:13:37 +08:00
2dust
e267b4b379 Update v2rayN.csproj 2023-06-04 17:13:34 +08:00
2dust
2fd21690a5 Enable extra inbound for tun mode 2023-06-04 17:11:37 +08:00
2dust
b176ad03aa Remove ToolTipText 2023-06-04 16:30:42 +08:00
2dust
152c4802d0 Fix the bug for flow is empty 2023-06-04 16:24:48 +08:00
2dust
8adbc57f23 remove statisticsFreshRate 2023-06-04 16:14:17 +08:00
2dust
d263a78db8 bug fixes 2023-06-04 15:50:38 +08:00
2dust
b94a065c06 Optimize Subscription 2023-06-04 15:50:25 +08:00
2dust
bc957fea71 up 6.25 2023-05-21 16:31:48 +08:00
2dust
566f056149 use Task.Delay instead of Thread.Sleep 2023-05-21 16:30:05 +08:00
2dust
abe484b0df use Task.Delay instead of Thread.Sleep 2023-05-21 16:29:57 +08:00
2dust
982c865245 Fix subscription ordering 2023-05-21 16:03:29 +08:00
2dust
07d2a27b5f optimize sing-box DNS 2023-05-21 15:22:30 +08:00
2dust
e4c65deda8 Add fakeip for sing-box 2023-05-21 15:05:15 +08:00
2dust
4930646e05 Tum mode forcibly closes the system proxy 2023-05-21 14:55:48 +08:00
2dust
01dd1ff56f Fix cannot insert routing rule with only process name 2023-05-21 14:43:07 +08:00
2dust
9433213fe5 Fill sing-box server_name 2023-05-21 14:35:18 +08:00
2dust
82dea829f1 Temporarily disable sing-box multiplex 2023-05-21 14:30:48 +08:00
2dust
dafc83aa53 Code clean 2023-05-14 17:25:05 +08:00
2dust
7ab1cd6612 Merge pull request #3852 from qhy040404/master
Fix download url
2023-05-14 17:21:33 +08:00
qhy040404
d0e3b3ffbd Fix download url 2023-05-14 17:16:55 +08:00
2dust
50bcc88700 up 6.24 2023-05-14 16:01:57 +08:00
2dust
8fb8575f4b Update v2rayN.csproj 2023-05-13 20:45:02 +08:00
2dust
e63c113b8a Update ProtosLib.csproj 2023-05-13 20:39:24 +08:00
2dust
43d892dbf7 Fix follow system theme 2023-05-13 20:39:16 +08:00
2dust
d9bf31d4f0 Merge pull request #3844 from JasonWues/master
add follow system theme func
2023-05-13 20:04:41 +08:00
2dust
0bde282448 Merge pull request #3842 from ShiinaRinne/hotkey
修复部分键位无法录入热键
2023-05-13 19:33:07 +08:00
2dust
6098132fdf Merge pull request #3834 from qhy040404/master
Avoid to update core if current version is newer than remote version
2023-05-13 19:32:55 +08:00
Metatron
a6bb8947f3 add follow system theme func 2023-05-12 16:19:09 +08:00
ShiinaRinne
48880328b0 Merge branch '2dust:master' into hotkey 2023-05-11 23:32:20 +08:00
ShiinaRinne
25776a788e https://github.com/2dust/v2rayN/issues/3678 2023-05-11 23:31:47 +08:00
qhy040404
f5e8f2831e Cleanup 2023-05-09 23:08:14 +08:00
qhy040404
b3fe13c97a Avoid to update core if current version is newer than remote version 2023-05-09 21:54:56 +08:00
2dust
7845569319 Add ws headers for sing-box 2023-05-09 18:44:04 +08:00
2dust
69b3178c13 remove tun template 2023-05-09 16:14:07 +08:00
2dust
1dce0f0366 Add speed display for sing-box , using clash api 2023-05-09 15:51:32 +08:00
2dust
db1b3fdaad Remove TunHandler 2023-05-07 19:09:51 +08:00
2dust
032d12a592 Add process name (Tun mode) to the rule
Add Tun DNS in the DNS settings of sing-box
Remove Tun mode settings process name and dns
2023-05-07 19:09:13 +08:00
2dust
94cc36a08f Rename the v2ray configuration class 2023-05-07 16:35:46 +08:00
2dust
4fcae44fb7 The default color is Blue 2023-05-07 15:54:40 +08:00
2dust
83719dfe17 vless reality mux can be used when the flow is empty 2023-05-07 09:01:50 +08:00
2dust
c8b01a5530 bug fixes 2023-05-04 20:28:38 +08:00
2dust
b469d73385 add mux for sing-box vless 2023-05-04 20:23:54 +08:00
2dust
69468b1770 Sing2sing 2023-05-04 20:18:21 +08:00
2dust
7e4f66d533 Add Multiplex protocol settings for sing-box 2023-05-04 16:30:35 +08:00
2dust
758d0d82d4 Tun mode force start sing-box core 2023-05-04 11:44:51 +08:00
2dust
a01b934fdb Fix update geoip 2023-04-30 16:46:49 +08:00
2dust
e9b0372174 Enable pre tun mode for other cores 2023-04-29 19:38:00 +08:00
2dust
288c02e092 Adjust some settings 2023-04-29 16:41:31 +08:00
2dust
33322e8795 Improved true connection test 2023-04-28 21:10:13 +08:00
2dust
ec8f6478df update geo files for sing-box 2023-04-28 15:54:51 +08:00
2dust
59d397a3a0 fix some text 2023-04-28 14:15:03 +08:00
2dust
b06540f2ec Custom config pre-service uses sing-box instead of v2ray 2023-04-28 12:09:02 +08:00
2dust
419f5458b4 Add domainStrategy4Singbox in rule set settings 2023-04-28 10:41:50 +08:00
2dust
ca4e8960d2 Delete shortcut keys for routing rules 2023-04-28 10:23:48 +08:00
2dust
7b3bed015a Add the ip and domain separator convert it to a comma 2023-04-28 10:22:55 +08:00
2dust
c97fa3a767 format dns config 2023-04-28 10:02:42 +08:00
2dust
42f7c7cb43 Adjust some config for sing-box 2023-04-27 16:20:13 +08:00
2dust
c8ae834f50 bug fixes 2023-04-27 14:17:27 +08:00
2dust
b8644268b3 Enter to set active routing rules 2023-04-27 10:28:20 +08:00
2dust
75742d8a9e Convert rule domain newLine to comma 2023-04-27 10:21:06 +08:00
2dust
6b6169e248 Adjust some config 2023-04-27 10:15:22 +08:00
2dust
3c4ae902fd Add dns for tun mode 2023-04-26 20:53:43 +08:00
2dust
bfc8bf91ee Add def dns for normal 2023-04-26 20:32:01 +08:00
2dust
768d5cce27 Add Sing-box domain strategy 2023-04-26 15:11:57 +08:00
2dust
d54a1c2754 Adjust some def configs 2023-04-26 14:39:04 +08:00
2dust
282ad3ab2d Add import default DNS config 2023-04-26 10:43:05 +08:00
2dust
c692dd3a66 add sing-box new LAN port 2023-04-26 10:00:51 +08:00
2dust
e461fce2cb add sing-box tun bypassMode 2023-04-26 09:36:30 +08:00
2dust
4d92caabf8 Trojan add reality 2023-04-25 20:34:37 +08:00
2dust
07c07ec60a sniff_override_destination = RouteOnly ? false 2023-04-25 20:34:18 +08:00
2dust
4020213729 generate a single file for sing-box tun mode 2023-04-25 20:27:21 +08:00
2dust
b9beb4be9d Add registry settings system proxy 2023-04-25 10:56:07 +08:00
2dust
f747416d3b Merge pull request #3756 from MohammadHadi2031/dev/ma/perf
use WaitAsync for Timeout
2023-04-25 10:03:40 +08:00
MohammadHadi Attarieh
41241b694f use WaitAsync instead of CTS 2023-04-24 18:34:30 +03:30
2dust
26c5c1ac01 bug fixes 2023-04-24 14:27:17 +08:00
2dust
dedd5b197c DNS independent setting, V2ray and Sing-box 2023-04-24 11:55:26 +08:00
2dust
6340764a6c Add basic rules for sing-box 2023-04-24 09:40:38 +08:00
2dust
a262c1e98d Add sing-box core config file support 2023-04-23 20:21:52 +08:00
2dust
7b9c64c5a5 remove export to server config 2023-04-23 15:06:51 +08:00
2dust
4283404f35 bug fixes 2023-04-22 20:29:06 +08:00
2dust
f612ce5db8 up 6.23 2023-04-21 15:55:05 +08:00
2dust
831a20ff91 Code clean 2023-04-21 15:15:35 +08:00
2dust
38ae669070 bug fixes for ErrorDataReceived 2023-04-21 14:49:33 +08:00
2dust
fed7e7764e bug fixes 2023-04-21 14:09:55 +08:00
2dust
f3804fce40 Add subscription conversion
https://github.com/tindy2013/subconverter/blob/master/README-cn.md
2023-04-21 12:15:24 +08:00
2dust
88e7cd09d7 The skip delay is -1 speed test in multi-threaded test 2023-04-21 12:11:09 +08:00
2dust
1ce5571ac6 Hide tray tip text 2023-04-21 10:32:41 +08:00
2dust
355bf84af4 bug fixes 2023-04-19 16:33:08 +08:00
2dust
6e8a3a4867 Code clean 2023-04-16 20:45:47 +08:00
2dust
bf6dcdf2bd Code clean 2023-04-14 20:49:36 +08:00
2dust
165454dc57 Migrate from Grpc.Core to Grpc.Net.Client 2023-04-14 15:51:34 +08:00
2dust
34b4b9d099 up 6.22 2023-04-13 16:00:30 +08:00
2dust
0808ec1612 Add Check update for sing-box core 2023-04-13 15:50:28 +08:00
2dust
6b12549cd9 bug fixes 2023-04-12 20:15:17 +08:00
2dust
92baf9025a Update StatisticsHandler.cs 2023-04-12 15:30:07 +08:00
2dust
d58861ab88 Adjust some style 2023-04-12 14:26:37 +08:00
2dust
142230e56a Fix description 2023-04-12 14:08:30 +08:00
2dust
b8959c8f12 Optimize server filter 2023-04-12 11:34:42 +08:00
2dust
3fa22fdc55 bug fixes 2023-04-11 20:18:49 +08:00
2dust
a1c743e59c bug fixes 2023-04-10 17:30:27 +08:00
2dust
ebd0f45c10 Adjust VLESS flow config 2023-04-10 16:21:19 +08:00
2dust
fce6c46250 Can set the update interval for each subscription 2023-04-10 16:00:32 +08:00
2dust
6114e4ff55 Temporary fix for arm64 grpc issues 2023-04-10 11:28:53 +08:00
2dust
cc3d1ff7de Update Package Reference 2023-04-09 09:13:28 +08:00
2dust
2f721c8535 Merge pull request #3635 from kidfruit/core-arm
Core arm
2023-04-09 08:26:33 +08:00
kidfruit
ddb790104f delete .vs 2023-04-08 12:57:11 +08:00
liqi
181dfd2f51 add arm64 download path for core 2023-04-07 21:32:28 +08:00
liqi
fca062f75a add arm64 download path for core 2023-04-07 18:13:37 +08:00
2dust
52b84c3201 bug fixes 2023-04-07 15:38:30 +08:00
2dust
e1021a6832 Merge pull request #3631 from fonaix/master
fix bug #3630
2023-04-07 15:37:28 +08:00
fonaix
aa338d2da5 fix bug #3630 2023-04-07 15:21:09 +08:00
2dust
44471c4926 Update README.md 2023-04-07 13:56:16 +08:00
2dust
930c7e4ce5 Update README.md 2023-04-07 13:55:32 +08:00
2dust
2e64f23ef9 up 6.21 2023-04-07 11:03:23 +08:00
2dust
801d1fd320 clean up res 2023-04-07 10:16:05 +08:00
2dust
cfffb5756e Update ResUI.zh-Hans.resx 2023-04-06 20:39:40 +08:00
2dust
b30daf8e56 Add more subscription url for one group 2023-04-06 15:09:49 +08:00
2dust
84eba671f0 Optimize add batch servers 2023-04-06 11:40:28 +08:00
2dust
f28b93daa0 Use H.NotifyIcon.Wpf 2023-04-06 10:44:58 +08:00
2dust
dc16136c59 Add Reboot as administrator 2023-04-06 10:44:29 +08:00
2dust
e47caf8c4f VLESS flow check 2023-04-05 21:07:47 +08:00
2dust
8ad80bb75d Remove System.Windows.Forms references 2023-04-05 20:53:05 +08:00
2dust
22475e459d Remove the function of minimizing at startup, because it affects the startup position 2023-04-05 09:43:33 +08:00
2dust
73ad33dfc9 Merge pull request #3611 from fonaix/master
fix bug #3218  and optimize the background color display effect of the active row
2023-04-05 08:23:33 +08:00
fonaix
f1782c78f4 fix bug #3218 2023-04-05 00:57:30 +08:00
fonaix
b544dde2c6 Optimize the background color display effect of the active row 2023-04-04 23:39:56 +08:00
2dust
1a7dca8e58 Fix subscription update duplicate server bug 2023-04-04 20:19:52 +08:00
2dust
23c7d7fb46 Remove some System.Windows.Forms references 2023-04-04 15:59:28 +08:00
2dust
65fda407dc RemoveSystem.Windows.Forms references 2023-04-04 15:59:08 +08:00
2dust
12ebad436a Autofit Column Width 2023-04-04 13:30:29 +08:00
2dust
c7297dba7e Merge pull request #3598 from JasonWues/master
当前选中节点标识改为背景颜色更改
2023-04-04 10:31:56 +08:00
2dust
e61368a26e Remove some System.Windows.Forms references 2023-04-04 10:22:13 +08:00
2dust
b98da3a5dc Optimized move to group 2023-04-04 10:21:12 +08:00
2dust
12f3400894 Subscription multi-select delete 2023-04-04 10:20:19 +08:00
Metatron
4b5508fc3c Style change 2023-04-03 14:43:30 +08:00
Metatron
5f40e6e0b7 fix 2023-04-03 10:50:56 +08:00
Metatron
080d660cfa Select row background change 2023-04-03 10:45:21 +08:00
2dust
790209efbc up 6.20 2023-04-01 18:57:24 +08:00
2dust
f1679e444c Add skip subscription tips 2023-03-31 20:07:39 +08:00
2dust
9dc6ba182a Fix delete duplicate server 2023-03-30 14:09:14 +08:00
2dust
785a30e623 Update CoreConfigHandler.cs 2023-03-26 20:05:51 +08:00
2dust
f09efdad66 Add tip for custom log 2023-03-25 18:05:01 +08:00
2dust
4e73b3ae28 Adjust some style 2023-03-25 17:50:32 +08:00
2dust
6213f86f81 up 6.19 2023-03-21 17:20:24 +08:00
2dust
0a6955cd59 Add log start core 2023-03-21 14:22:13 +08:00
2dust
6ee8f03ec0 Gui's log can be turned off 2023-03-20 15:25:58 +08:00
2dust
b1a82d95c2 Hide title when title width is equal to 0 2023-03-20 15:23:29 +08:00
2dust
30bc9ded29 Add REALITY share link 2023-03-20 15:20:55 +08:00
2dust
5a32892e94 bug fixes 2023-03-20 14:03:50 +08:00
2dust
e33de896b6 Merge pull request #3507 from ShiinaRinne/dev
Update: 默认关闭硬件加速
2023-03-20 13:23:58 +08:00
ShiinaRinne
3ba92444a5 Update: 默认关闭硬件加速
开启硬件加速时可能会导致奇奇怪怪的问题,因此修改为默认关闭
https://github.com/2dust/v2rayN/issues/3379
https://github.com/2dust/v2rayN/issues/3279
2023-03-18 20:49:02 +08:00
2dust
4f120e8eb4 bug fixes 2023-03-17 17:20:40 +08:00
2dust
08aebf5736 up 6.18 2023-03-17 13:53:14 +08:00
2dust
5a4966ba8d Adjust speed test 2023-03-17 10:32:56 +08:00
2dust
9ba963fc45 bug fixes 2023-03-16 20:11:10 +08:00
2dust
ccc10dbae4 Merge pull request #3493 from ShiinaRinne/AddEnableHWA
Add: 设置中添加"启用硬件加速"的选项
2023-03-16 14:10:20 +08:00
ShiinaRinne
d858342269 Add: 设置中添加"启用硬件加速"的选项 2023-03-15 23:27:51 +08:00
2dust
63f251d1fd up 6.17 2023-03-10 20:38:08 +08:00
2dust
83efe66f3e bug fixes 2023-03-10 11:25:34 +08:00
360 changed files with 64320 additions and 26120 deletions

10
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
# Set update schedule for GitHub Actions
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
# Check for updates to GitHub Actions every daily
interval: "daily"

32
.github/workflows/build-osx.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: release macos
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
strategy:
matrix:
configuration: [Release]
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build
run: cd v2rayN &&
./build-osx.sh
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: v2rayN-osx
path: |
./v2rayN/v2rayN-osx.zip

60
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,60 @@
name: release
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
strategy:
matrix:
configuration: [Release]
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v4
# - name: 删除工作流运行
# uses: Mattraks/delete-workflow-runs@v2
# with:
# token: ${{ github.token }}
# repository: ${{ github.repository }}
# retain_days: 0
# keep_minimum_runs: 1
- name: Build
run: cd v2rayN &&
./build.ps1
# - name: Package
# shell: pwsh
# run: |
# 7z a -mx9 ..\v2rayN.7z $env:Wap_Project_Directory
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: v2rayN
path: |
./v2rayN/v2rayN.zip
# - name: Release
# uses: softprops/action-gh-release@v1
# env:
# GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
# with:
# prerelease: ${{ contains(github.ref, '-') }}
# draft: false
# files: |
# .\v2rayN\v2rayN.zip
# body: |
# [![](https://img.shields.io/badge/Telegram-Channel-blue)](https://t.me/netch_channel) [![](https://img.shields.io/badge/Telegram-Group-green)](https://t.me/netch_group)
# ## Changelogs
# * This is an automated deployment of GitHub Actions, the change log should be updated manually soon
# ## 更新日志
# * 这是 GitHub Actions 自动化部署,更新日志应该很快会手动更新

31
.github/workflows/winget-publish.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: WinGet submission on release
# based off of https://github.com/nushell/nushell/blob/main/.github/workflows/winget-submission.yml
# inspired by https://github.com/microsoft/PowerToys/blob/main/.github/workflows/package-submissions.yml
# Modified by @MerrickZ https://github.com/anpho
on:
workflow_dispatch:
release:
types: [released]
jobs:
winget:
name: Publish winget package
runs-on: windows-latest
steps:
- name: Submit v2ray package to Windows Package Manager Community Repository
run: |
$wingetPackage = "2dust.v2rayN"
$gitToken = "${{ secrets.PT_WINGET }}"
$github = Invoke-RestMethod -uri "https://api.github.com/repos/2dust/v2rayN/releases"
$targetRelease = $github | Where-Object -Property prerelease -match 'False' | Select -First 1
$installerUrl = $targetRelease | Select -ExpandProperty assets -First 1 | Where-Object -Property name -match 'v2rayN-windows-64-With-Core\.zip*' | Select -ExpandProperty browser_download_url
$ver = $targetRelease.tag_name
# getting latest wingetcreate file
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
.\wingetcreate.exe update $wingetPackage -s -v $ver -u "$installerUrl|x64" -t $gitToken

View File

@@ -1,5 +1,5 @@
# v2rayN
A V2Ray client for Windows, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core)
A GUI client for Windows and Linux, support [Xray core](https://github.com/XTLS/Xray-core) and [sing-box-core](https://github.com/SagerNet/sing-box/releases) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/2dust/v2rayN)](https://github.com/2dust/v2rayN/commits/master)
@@ -7,15 +7,23 @@ A V2Ray client for Windows, support [Xray core](https://github.com/XTLS/Xray-cor
[![GitHub Releases](https://img.shields.io/github/downloads/2dust/v2rayN/latest/total?logo=github)](https://github.com/2dust/v2rayN/releases)
[![Chat on Telegram](https://img.shields.io/badge/Chat%20on-Telegram-brightgreen.svg)](https://t.me/v2rayn)
### How to use
- If you are new to this, please download v2rayN-Core.zip from [releases](https://github.com/2dust/v2rayN/releases)
- Otherwise please download v2rayN.zip (you will also need to download v2ray core into the same folder with v2rayN.exe)
- Run v2rayN.exe
### Requirements
- Microsoft [.NET Framework 4.8 Runtime](https://dotnet.microsoft.com/zh-cn/download/dotnet-framework/thank-you/net48-offline-installer)
- v2fly core [https://github.com/v2fly/v2ray-core/releases](https://github.com/v2fly/v2ray-core/releases)
- Xray core [https://github.com/XTLS/Xray-core/releases](https://github.com/XTLS/Xray-core/releases)
## How to use
Check [Release files introduction](https://github.com/2dust/v2rayN/wiki/Release-files-introduction) and select the version you need to download
### Windows
- Run `v2rayN.exe`
### Linux
- `chmod +x v2rayN` Run `./v2rayN`
```
Debian 9+
Ubuntu 16.04+
Fedora 30+
```
### Telegram Channel
## Requirements
- [Microsoft .NET 8.0 Desktop Runtime ](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
- [Supported cores](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
## Telegram Channel
[github_2dust](https://t.me/github_2dust)

View File

@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Copyright>Copyright © 2017-2024 (GPLv3)</Copyright>
<FileVersion>1.3.0</FileVersion>
</PropertyGroup>
<ItemGroup>
<Compile Update="Resx\Resource.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resource.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resx\Resource.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resource.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,22 @@
namespace AmazTool
{
internal static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
private static void Main(string[] args)
{
if (args.Length == 0)
{
Console.WriteLine(Resx.Resource.Guidelines);
Thread.Sleep(5000);
return;
}
var fileName = Uri.UnescapeDataString(string.Join(" ", args));
UpgradeApp.Upgrade(fileName);
}
}
}

171
v2rayN/AmazTool/Resx/Resource.Designer.cs generated Normal file
View File

@@ -0,0 +1,171 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace AmazTool.Resx {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resource {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resource() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AmazTool.Resx.Resource", typeof(Resource).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// 查找类似 Failed to terminate the v2rayN.Close it manually,or the upgrade may fail. 的本地化字符串。
/// </summary>
internal static string FailedTerminateProcess {
get {
return ResourceManager.GetString("FailedTerminateProcess", resourceCulture);
}
}
/// <summary>
/// 查找类似 Failed to extract the update package. 的本地化字符串。
/// </summary>
internal static string FailedUnzipping {
get {
return ResourceManager.GetString("FailedUnzipping", resourceCulture);
}
}
/// <summary>
/// 查找类似 Upgrade failed. 的本地化字符串。
/// </summary>
internal static string FailedUpgrade {
get {
return ResourceManager.GetString("FailedUpgrade", resourceCulture);
}
}
/// <summary>
/// 查找类似 Please run it from the main application. 的本地化字符串。
/// </summary>
internal static string Guidelines {
get {
return ResourceManager.GetString("Guidelines", resourceCulture);
}
}
/// <summary>
/// 查找类似 Information 的本地化字符串。
/// </summary>
internal static string Information {
get {
return ResourceManager.GetString("Information", resourceCulture);
}
}
/// <summary>
/// 查找类似 In progress, please wait... 的本地化字符串。
/// </summary>
internal static string InProgress {
get {
return ResourceManager.GetString("InProgress", resourceCulture);
}
}
/// <summary>
/// 查找类似 Start v2rayN, please wait... 的本地化字符串。
/// </summary>
internal static string Restartv2rayN {
get {
return ResourceManager.GetString("Restartv2rayN", resourceCulture);
}
}
/// <summary>
/// 查找类似 Start extracting the update package... 的本地化字符串。
/// </summary>
internal static string StartUnzipping {
get {
return ResourceManager.GetString("StartUnzipping", resourceCulture);
}
}
/// <summary>
/// 查找类似 Successfully extracted the update package. 的本地化字符串。
/// </summary>
internal static string SuccessUnzipping {
get {
return ResourceManager.GetString("SuccessUnzipping", resourceCulture);
}
}
/// <summary>
/// 查找类似 Upgrade success. 的本地化字符串。
/// </summary>
internal static string SuccessUpgrade {
get {
return ResourceManager.GetString("SuccessUpgrade", resourceCulture);
}
}
/// <summary>
/// 查找类似 Try to terminate the v2rayN process... 的本地化字符串。
/// </summary>
internal static string TryTerminateProcess {
get {
return ResourceManager.GetString("TryTerminateProcess", resourceCulture);
}
}
/// <summary>
/// 查找类似 Upgrade failed, file not found. 的本地化字符串。
/// </summary>
internal static string UpgradeFileNotFound {
get {
return ResourceManager.GetString("UpgradeFileNotFound", resourceCulture);
}
}
}
}

View File

@@ -117,4 +117,40 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Restartv2rayN" xml:space="preserve">
<value>Start v2rayN, please wait...</value>
</data>
<data name="Guidelines" xml:space="preserve">
<value>Please run it from the main application.</value>
</data>
<data name="UpgradeFileNotFound" xml:space="preserve">
<value>Upgrade failed, file not found.</value>
</data>
<data name="InProgress" xml:space="preserve">
<value>In progress, please wait...</value>
</data>
<data name="TryTerminateProcess" xml:space="preserve">
<value>Try to terminate the v2rayN process...</value>
</data>
<data name="FailedTerminateProcess" xml:space="preserve">
<value>Failed to terminate the v2rayN.Close it manually,or the upgrade may fail.</value>
</data>
<data name="StartUnzipping" xml:space="preserve">
<value>Start extracting the update package...</value>
</data>
<data name="SuccessUnzipping" xml:space="preserve">
<value>Successfully extracted the update package.</value>
</data>
<data name="FailedUnzipping" xml:space="preserve">
<value>Failed to extract the update package.</value>
</data>
<data name="FailedUpgrade" xml:space="preserve">
<value>Upgrade failed.</value>
</data>
<data name="SuccessUpgrade" xml:space="preserve">
<value>Upgrade success.</value>
</data>
<data name="Information" xml:space="preserve">
<value>Information</value>
</data>
</root>

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
@@ -117,8 +117,40 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="pac" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Resources\pac.txt;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;gb2312</value>
<data name="Restartv2rayN" xml:space="preserve">
<value>正在重启,请等待...</value>
</data>
<data name="Guidelines" xml:space="preserve">
<value>请从主应用运行。</value>
</data>
<data name="UpgradeFileNotFound" xml:space="preserve">
<value>升级失败,文件不存在。</value>
</data>
<data name="InProgress" xml:space="preserve">
<value>正在进行中,请等待...</value>
</data>
<data name="TryTerminateProcess" xml:space="preserve">
<value>尝试结束 v2rayN 进程...</value>
</data>
<data name="FailedTerminateProcess" xml:space="preserve">
<value>请手动关闭正在运行的v2rayN否则可能升级失败。</value>
</data>
<data name="StartUnzipping" xml:space="preserve">
<value>开始解压缩更新包...</value>
</data>
<data name="SuccessUnzipping" xml:space="preserve">
<value>解压缩更新包成功。</value>
</data>
<data name="FailedUnzipping" xml:space="preserve">
<value>解压缩更新包失败。</value>
</data>
<data name="FailedUpgrade" xml:space="preserve">
<value>升级失败。</value>
</data>
<data name="SuccessUpgrade" xml:space="preserve">
<value>升级成功。</value>
</data>
<data name="Information" xml:space="preserve">
<value>提示</value>
</data>
</root>

View File

@@ -0,0 +1,138 @@
using System.Diagnostics;
using System.IO.Compression;
using System.Text;
namespace AmazTool
{
internal class UpgradeApp
{
public static void Upgrade(string fileName)
{
Console.WriteLine($"{Resx.Resource.StartUnzipping}\n{fileName}");
Waiting(9);
if (!File.Exists(fileName))
{
Console.WriteLine(Resx.Resource.UpgradeFileNotFound);
return;
}
Console.WriteLine(Resx.Resource.TryTerminateProcess);
try
{
var existing = Process.GetProcessesByName(V2rayN);
foreach (var pp in existing)
{
var path = pp.MainModule?.FileName ?? "";
if (path.StartsWith(GetPath(V2rayN)))
{
pp?.Kill();
pp?.WaitForExit(1000);
}
}
}
catch (Exception ex)
{
// Access may be denied without admin right. The user may not be an administrator.
Console.WriteLine(Resx.Resource.FailedTerminateProcess + ex.StackTrace);
}
Console.WriteLine(Resx.Resource.StartUnzipping);
StringBuilder sb = new();
try
{
string thisAppOldFile = $"{GetExePath()}.tmp";
File.Delete(thisAppOldFile);
string splitKey = "/";
using ZipArchive archive = ZipFile.OpenRead(fileName);
foreach (ZipArchiveEntry entry in archive.Entries)
{
try
{
if (entry.Length == 0)
{
continue;
}
Console.WriteLine(entry.FullName);
var lst = entry.FullName.Split(splitKey);
if (lst.Length == 1) continue;
string fullName = string.Join(splitKey, lst[1..lst.Length]);
if (string.Equals(GetExePath(), GetPath(fullName), StringComparison.OrdinalIgnoreCase))
{
File.Move(GetExePath(), thisAppOldFile);
}
string entryOutputPath = GetPath(fullName);
Directory.CreateDirectory(Path.GetDirectoryName(entryOutputPath)!);
entry.ExtractToFile(entryOutputPath, true);
Console.WriteLine(entryOutputPath);
}
catch (Exception ex)
{
sb.Append(ex.StackTrace);
}
}
}
catch (Exception ex)
{
Console.WriteLine(Resx.Resource.FailedUpgrade + ex.StackTrace);
//return;
}
if (sb.Length > 0)
{
Console.WriteLine(Resx.Resource.FailedUpgrade + sb.ToString());
//return;
}
Console.WriteLine(Resx.Resource.Restartv2rayN);
Waiting(9);
Process process = new()
{
StartInfo = new()
{
UseShellExecute = true,
FileName = V2rayN,
WorkingDirectory = StartupPath()
}
};
process.Start();
}
private static string GetExePath()
{
return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty;
}
private static string StartupPath()
{
return AppDomain.CurrentDomain.BaseDirectory;
}
private static string GetPath(string fileName)
{
string startupPath = StartupPath();
if (string.IsNullOrEmpty(fileName))
{
return startupPath;
}
return Path.Combine(startupPath, fileName);
}
private static void Waiting(int second)
{
for (var i = second; i > 0; i--)
{
Console.WriteLine(i);
Thread.Sleep(1000);
}
}
private static string V2rayN => "v2rayN";
}
}

View File

@@ -1,106 +0,0 @@
using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace PacLib;
public class PacHandler
{
private static string _configPath;
private static int _httpPort;
private static int _pacPort;
private static TcpListener? _tcpListener;
private static string _pacText;
private static bool _isRunning;
private static bool _needRestart = true;
public static void Start(string configPath, int httpPort, int pacPort)
{
_needRestart = (configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning);
_configPath = configPath;
_httpPort = httpPort;
_pacPort = pacPort;
InitText();
if (_needRestart)
{
Stop();
RunListener();
}
}
private static void InitText()
{
var path = Path.Combine(_configPath, "pac.txt");
if (!File.Exists(path))
{
File.AppendAllText(path, Resources.ResourceManager.GetString("pac"));
}
_pacText = File.ReadAllText(path).Replace("__PROXY__", $"PROXY 127.0.0.1:{_httpPort};DIRECT;");
}
private static void RunListener()
{
_tcpListener = TcpListener.Create(_pacPort);
_isRunning = true;
_tcpListener.Start();
Task.Factory.StartNew(() =>
{
while (_isRunning)
{
try
{
if (!_tcpListener.Pending())
{
Thread.Sleep(10);
continue;
}
var client = _tcpListener.AcceptTcpClient();
Task.Run(() =>
{
var stream = client.GetStream();
var sb = new StringBuilder();
sb.AppendLine("HTTP/1.0 200 OK");
sb.AppendLine("Content-type:application/x-ns-proxy-autoconfig");
sb.AppendLine("Connection:close");
sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(_pacText));
sb.AppendLine();
sb.Append(_pacText);
var content = Encoding.UTF8.GetBytes(sb.ToString());
stream.Write(content, 0, content.Length);
stream.Flush();
});
}
catch (Exception e)
{
}
}
}, TaskCreationOptions.LongRunning);
}
public static void Stop()
{
if (_tcpListener != null)
{
try
{
_isRunning = false;
_tcpListener.Stop();
_tcpListener = null;
}
catch (Exception e)
{
}
}
}
}

View File

@@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Compile Update="Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<EmbeddedResource Update="Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@@ -1,95 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 此代码由工具生成。
// 运行时版本:4.0.30319.42000
//
// 对此文件的更改可能会导致不正确的行为,并且如果
// 重新生成代码,这些更改将会丢失。
// </auto-generated>
//------------------------------------------------------------------------------
namespace PacLib {
using System;
/// <summary>
/// 一个强类型的资源类,用于查找本地化的字符串等。
/// </summary>
// 此类是由 StronglyTypedResourceBuilder
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// 返回此类使用的缓存的 ResourceManager 实例。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("PacLib.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// 重写当前线程的 CurrentUICulture 属性,对
/// 使用此强类型资源类的所有资源查找执行重写。
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// 查找类似 var proxy = &apos;__PROXY__&apos;;
///var rules = [
/// [
/// [],
/// []
/// ],
/// [
/// [
/// &quot;aftygh.gov.tw&quot;,
/// &quot;aide.gov.tw&quot;,
/// &quot;aliyun.com&quot;,
/// &quot;arte.gov.tw&quot;,
/// &quot;baidu.com&quot;,
/// &quot;chinaso.com&quot;,
/// &quot;chinaz.com&quot;,
/// &quot;chukuang.gov.tw&quot;,
/// &quot;cycab.gov.tw&quot;,
/// &quot;dbnsa.gov.tw&quot;,
/// &quot;df.gov.tw&quot;,
/// &quot;eastcoast-nsa.gov.tw&quot;,
/// &quot;erv-nsa.gov.tw&quot;,
/// &quot;grb.gov.tw&quot;,
/// &quot;haosou.com&quot;,
/// [字符串的其余部分被截断]&quot;; 的本地化字符串。
/// </summary>
internal static string pac {
get {
return ResourceManager.GetString("pac", resourceCulture);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,24 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Protobuf Include="Statistics.proto" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf">
<Version>3.21.9</Version>
</PackageReference>
<PackageReference Include="Grpc.Core">
<Version>2.46.5</Version>
</PackageReference>
<PackageReference Include="Grpc.Tools" Version="2.50.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -1,53 +0,0 @@
syntax = "proto3";
package v2ray.core.app.stats.command;
option csharp_namespace = "ProtosLib.Statistics";
message GetStatsRequest {
// Name of the stat counter.
string name = 1;
// Whether or not to reset the counter to fetching its value.
bool reset = 2;
}
message Stat {
string name = 1;
int64 value = 2;
}
message GetStatsResponse {
Stat stat = 1;
}
message QueryStatsRequest {
string pattern = 1;
bool reset = 2;
}
message QueryStatsResponse {
repeated Stat stat = 1;
}
message SysStatsRequest {
}
message SysStatsResponse {
uint32 NumGoroutine = 1;
uint32 NumGC = 2;
uint64 Alloc = 3;
uint64 TotalAlloc = 4;
uint64 Sys = 5;
uint64 Mallocs = 6;
uint64 Frees = 7;
uint64 LiveObjects = 8;
uint64 PauseTotalNs = 9;
uint32 Uptime = 10;
}
service StatsService {
rpc GetStats(GetStatsRequest) returns (GetStatsResponse) {}
rpc QueryStats(QueryStatsRequest) returns (QueryStatsResponse) {}
rpc GetSysStats(SysStatsRequest) returns (SysStatsResponse) {}
}
message Config {}

View File

@@ -1,15 +0,0 @@
using ProtosLib.Statistics;
namespace ProtosLib
{
public class Tests
{
private StatsService.StatsServiceClient client_;
public Tests()
{
}
}
}

View File

@@ -0,0 +1,10 @@
using ReactiveUI;
namespace ServiceLib.Base
{
public class MyReactiveObject : ReactiveObject
{
protected static Config? _config;
protected Func<EViewAction, object?, Task<bool>>? _updateView;
}
}

View File

@@ -0,0 +1,101 @@
using System.Security.Cryptography;
using System.Text;
namespace ServiceLib.Common
{
public class AesUtils
{
private const int KeySize = 256; // AES-256
private const int IvSize = 16; // AES block size
private const int Iterations = 10000;
private static readonly byte[] Salt = Encoding.ASCII.GetBytes("saltysalt".PadRight(16, ' ')); // google浏览器默认盐值
private static readonly string DefaultPassword = Utils.GetMd5(Utils.GetHomePath() + "AesUtils");
/// <summary>
/// Encrypt
/// </summary>
/// <param name="text">Plain text</param>
/// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
/// <returns>Base64 encoded cipher text with IV</returns>
public static string Encrypt(string text, string? password = null)
{
if (string.IsNullOrEmpty(text))
return string.Empty;
var plaintext = Encoding.UTF8.GetBytes(text);
var key = GetKey(password);
var iv = GenerateIv();
using var aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
using var ms = new MemoryStream();
ms.Write(iv, 0, iv.Length);
using (var cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write))
{
cs.Write(plaintext, 0, plaintext.Length);
cs.FlushFinalBlock();
}
var cipherTextWithIv = ms.ToArray();
return Convert.ToBase64String(cipherTextWithIv);
}
/// <summary>
/// Decrypt
/// </summary>
/// <param name="cipherTextWithIv">Base64 encoded cipher text with IV</param>
/// <param name="password">Password for key derivation or direct key in ASCII bytes</param>
/// <returns>Plain text</returns>
public static string Decrypt(string cipherTextWithIv, string? password = null)
{
if (string.IsNullOrEmpty(cipherTextWithIv))
return string.Empty;
var cipherTextWithIvBytes = Convert.FromBase64String(cipherTextWithIv);
var key = GetKey(password);
var iv = new byte[IvSize];
Buffer.BlockCopy(cipherTextWithIvBytes, 0, iv, 0, IvSize);
var cipherText = new byte[cipherTextWithIvBytes.Length - IvSize];
Buffer.BlockCopy(cipherTextWithIvBytes, IvSize, cipherText, 0, cipherText.Length);
using var aes = Aes.Create();
aes.Key = key;
aes.IV = iv;
using var ms = new MemoryStream();
using (var cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write))
{
cs.Write(cipherText, 0, cipherText.Length);
cs.FlushFinalBlock();
}
var plainText = ms.ToArray();
return Encoding.UTF8.GetString(plainText);
}
private static byte[] GetKey(string? password)
{
if (password.IsNullOrEmpty())
{
password = DefaultPassword;
}
using var pbkdf2 = new Rfc2898DeriveBytes(password, Salt, Iterations, HashAlgorithmName.SHA256);
return pbkdf2.GetBytes(KeySize / 8);
}
private static byte[] GenerateIv()
{
var randomNumber = new byte[IvSize];
using var rng = RandomNumberGenerator.Create();
rng.GetBytes(randomNumber);
return randomNumber;
}
}
}

View File

@@ -0,0 +1,75 @@
using System.Security.Cryptography;
using System.Text;
namespace ServiceLib.Common
{
public class DesUtils
{
/// <summary>
/// Encrypt
/// </summary>
/// <param name="text"></param>
/// /// <param name="key"></param>
/// <returns></returns>
public static string Encrypt(string? text, string? key = null)
{
if (text.IsNullOrEmpty())
{
return string.Empty;
}
GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv);
var dsp = DES.Create();
using var memStream = new MemoryStream();
using var cryStream = new CryptoStream(memStream, dsp.CreateEncryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
using var sWriter = new StreamWriter(cryStream);
sWriter.Write(text);
sWriter.Flush();
cryStream.FlushFinalBlock();
memStream.Flush();
return Convert.ToBase64String(memStream.GetBuffer(), 0, (int)memStream.Length);
}
/// <summary>
/// Decrypt
/// </summary>
/// <param name="encryptText"></param>
/// <param name="key"></param>
/// <returns></returns>
public static string Decrypt(string? encryptText, string? key = null)
{
if (encryptText.IsNullOrEmpty())
{
return string.Empty;
}
GetKeyIv(key ?? GetDefaultKey(), out var rgbKey, out var rgbIv);
var dsp = DES.Create();
var buffer = Convert.FromBase64String(encryptText);
using var memStream = new MemoryStream();
using var cryStream = new CryptoStream(memStream, dsp.CreateDecryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
cryStream.Write(buffer, 0, buffer.Length);
cryStream.FlushFinalBlock();
return Encoding.UTF8.GetString(memStream.ToArray());
}
private static void GetKeyIv(string key, out byte[] rgbKey, out byte[] rgbIv)
{
if (key.IsNullOrEmpty())
{
throw new ArgumentNullException("The key cannot be null");
}
if (key.Length <= 8)
{
throw new ArgumentNullException("The key length cannot be less than 8 characters.");
}
rgbKey = Encoding.ASCII.GetBytes(key.Substring(0, 8));
rgbIv = Encoding.ASCII.GetBytes(key.Insert(0, "w").Substring(0, 8));
}
private static string GetDefaultKey()
{
return Utils.GetMd5(Utils.GetHomePath() + "DesUtils");
}
}
}

View File

@@ -1,28 +1,24 @@
using Downloader;
using System.IO;
using System.Net;
namespace v2rayN.Base
namespace ServiceLib.Common
{
internal class DownloaderHelper
public class DownloaderHelper
{
private static readonly Lazy<DownloaderHelper> _instance = new(() => new());
public static DownloaderHelper Instance => _instance.Value;
public async Task<string?> DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout)
{
if (string.IsNullOrEmpty(url))
if (Utils.IsNullOrEmpty(url))
{
return null;
}
var cancellationToken = new CancellationTokenSource();
cancellationToken.CancelAfter(timeout * 1000);
Uri uri = new(url);
//Authorization Header
var headers = new WebHeaderCollection();
if (!Utils.IsNullOrEmpty(uri.UserInfo))
if (Utils.IsNotEmpty(uri.UserInfo))
{
headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo));
}
@@ -40,7 +36,7 @@ namespace v2rayN.Base
}
};
using var downloader = new DownloadService(downloadOpt);
await using var downloader = new Downloader.DownloadService(downloadOpt);
downloader.DownloadFileCompleted += (sender, value) =>
{
if (value.Error != null)
@@ -48,25 +44,23 @@ namespace v2rayN.Base
throw value.Error;
}
};
using var stream = await downloader.DownloadFileTaskAsync(address: url, cancellationToken: cancellationToken.Token);
using var cts = new CancellationTokenSource();
await using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
using StreamReader reader = new(stream);
downloadOpt = null;
return reader.ReadToEnd();
return await reader.ReadToEndAsync(cts.Token);
}
public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress<string> progress, int timeout)
{
if (string.IsNullOrEmpty(url))
if (Utils.IsNullOrEmpty(url))
{
throw new ArgumentNullException(nameof(url));
}
var cancellationToken = new CancellationTokenSource();
cancellationToken.CancelAfter(timeout * 1000);
var downloadOpt = new DownloadConfiguration()
{
Timeout = timeout * 1000,
@@ -78,11 +72,11 @@ namespace v2rayN.Base
}
};
DateTime totalDatetime = DateTime.Now;
int totalSecond = 0;
var totalDatetime = DateTime.Now;
var totalSecond = 0;
var hasValue = false;
double maxSpeed = 0;
using var downloader = new DownloadService(downloadOpt);
await using var downloader = new Downloader.DownloadService(downloadOpt);
//downloader.DownloadStarted += (sender, value) =>
//{
// if (progress != null)
@@ -92,7 +86,7 @@ namespace v2rayN.Base
//};
downloader.DownloadProgressChanged += (sender, value) =>
{
TimeSpan ts = (DateTime.Now - totalDatetime);
var ts = (DateTime.Now - totalDatetime);
if (progress != null && ts.Seconds > totalSecond)
{
hasValue = true;
@@ -115,20 +109,21 @@ namespace v2rayN.Base
}
}
};
progress.Report("......");
using var stream = await downloader.DownloadFileTaskAsync(address: url, cancellationToken: cancellationToken.Token);
//progress.Report("......");
using var cts = new CancellationTokenSource();
cts.CancelAfter(timeout * 1000);
await using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token);
downloadOpt = null;
}
public async Task DownloadFileAsync(IWebProxy? webProxy, string url, string fileName, IProgress<double> progress, int timeout)
{
if (string.IsNullOrEmpty(url))
if (Utils.IsNullOrEmpty(url))
{
throw new ArgumentNullException(nameof(url));
}
if (string.IsNullOrEmpty(fileName))
if (Utils.IsNullOrEmpty(fileName))
{
throw new ArgumentNullException(nameof(fileName));
}
@@ -137,9 +132,6 @@ namespace v2rayN.Base
File.Delete(fileName);
}
var cancellationToken = new CancellationTokenSource();
cancellationToken.CancelAfter(timeout * 1000);
var downloadOpt = new DownloadConfiguration()
{
Timeout = timeout * 1000,
@@ -153,7 +145,7 @@ namespace v2rayN.Base
var progressPercentage = 0;
var hasValue = false;
using var downloader = new DownloadService(downloadOpt);
await using var downloader = new Downloader.DownloadService(downloadOpt);
downloader.DownloadStarted += (sender, value) =>
{
progress?.Report(0);
@@ -176,10 +168,15 @@ namespace v2rayN.Base
{
progress.Report(101);
}
else if (value.Error != null)
{
throw value.Error;
}
}
};
await downloader.DownloadFileTaskAsync(url, fileName, cancellationToken: cancellationToken.Token);
using var cts = new CancellationTokenSource();
await downloader.DownloadFileTaskAsync(url, fileName, cts.Token);
downloadOpt = null;
}

View File

@@ -0,0 +1,198 @@
using System.Formats.Tar;
using System.IO.Compression;
using System.Text;
namespace ServiceLib.Common
{
public static class FileManager
{
public static bool ByteArrayToFile(string fileName, byte[] content)
{
try
{
File.WriteAllBytes(fileName, content);
return true;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return false;
}
public static void DecompressFile(string fileName, byte[] content)
{
try
{
using var fs = File.Create(fileName);
using GZipStream input = new(new MemoryStream(content), CompressionMode.Decompress, false);
input.CopyTo(fs);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public static void DecompressFile(string fileName, string toPath, string? toName)
{
try
{
FileInfo fileInfo = new(fileName);
using var originalFileStream = fileInfo.OpenRead();
using var decompressedFileStream = File.Create(toName != null ? Path.Combine(toPath, toName) : toPath);
using GZipStream decompressionStream = new(originalFileStream, CompressionMode.Decompress);
decompressionStream.CopyTo(decompressedFileStream);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public static void DecompressTarFile(string fileName, string toPath)
{
try
{
using var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
using var gz = new GZipStream(fs, CompressionMode.Decompress, leaveOpen: true);
TarFile.ExtractToDirectory(gz, toPath, overwriteFiles: true);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public static string NonExclusiveReadAllText(string path)
{
return NonExclusiveReadAllText(path, Encoding.Default);
}
private static string NonExclusiveReadAllText(string path, Encoding encoding)
{
try
{
using FileStream fs = new(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using StreamReader sr = new(fs, encoding);
return sr.ReadToEnd();
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
throw;
}
}
public static bool ZipExtractToFile(string fileName, string toPath, string ignoredName)
{
try
{
using var archive = ZipFile.OpenRead(fileName);
foreach (var entry in archive.Entries)
{
if (entry.Length == 0)
{
continue;
}
try
{
if (Utils.IsNotEmpty(ignoredName) && entry.Name.Contains(ignoredName))
{
continue;
}
entry.ExtractToFile(Path.Combine(toPath, entry.Name), true);
}
catch (IOException ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
return false;
}
return true;
}
public static List<string>? GetFilesFromZip(string fileName)
{
if (!File.Exists(fileName))
{
return null;
}
try
{
using var archive = ZipFile.OpenRead(fileName);
return archive.Entries.Select(entry => entry.FullName).ToList();
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
return null;
}
}
public static bool CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName)
{
try
{
if (File.Exists(destinationArchiveFileName))
{
File.Delete(destinationArchiveFileName);
}
ZipFile.CreateFromDirectory(sourceDirectoryName, destinationArchiveFileName, CompressionLevel.SmallestSize, true);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
return false;
}
return true;
}
public static void CopyDirectory(string sourceDir, string destinationDir, bool recursive, string? ignoredName)
{
// Get information about the source directory
var dir = new DirectoryInfo(sourceDir);
// Check if the source directory exists
if (!dir.Exists)
throw new DirectoryNotFoundException($"Source directory not found: {dir.FullName}");
// Cache directories before we start copying
var dirs = dir.GetDirectories();
// Create the destination directory
Directory.CreateDirectory(destinationDir);
// Get the files in the source directory and copy to the destination directory
foreach (var file in dir.GetFiles())
{
if (Utils.IsNotEmpty(ignoredName) && file.Name.Contains(ignoredName))
{
continue;
}
if (file.Extension == file.Name)
{
continue;
}
var targetFilePath = Path.Combine(destinationDir, file.Name);
file.CopyTo(targetFilePath, true);
}
// If recursive and copying subdirectories, recursively call this method
if (recursive)
{
foreach (var subDir in dirs)
{
var newDestinationDir = Path.Combine(destinationDir, subDir.Name);
CopyDirectory(subDir.FullName, newDestinationDir, true, ignoredName);
}
}
}
}
}

View File

@@ -1,45 +1,76 @@
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Text;
namespace v2rayN.Base
namespace ServiceLib.Common
{
/// <summary>
/// </summary>
public class HttpClientHelper
{
private readonly static Lazy<HttpClientHelper> _instance = new(() =>
private static readonly Lazy<HttpClientHelper> _instance = new(() =>
{
HttpClientHandler handler = new() { UseCookies = false };
HttpClientHelper helper = new(new HttpClient(handler));
return helper;
});
public static HttpClientHelper Instance => _instance.Value;
private readonly HttpClient httpClient;
private HttpClientHelper(HttpClient httpClient) => this.httpClient = httpClient;
public async Task<string?> TryGetAsync(string url)
{
if (Utils.IsNullOrEmpty(url))
return null;
try
{
var response = await httpClient.GetAsync(url);
return await response.Content.ReadAsStringAsync();
}
catch
{
return null;
}
}
public async Task<string?> GetAsync(string url)
{
if (string.IsNullOrEmpty(url)) return null;
if (Utils.IsNullOrEmpty(url)) return null;
return await httpClient.GetStringAsync(url);
}
public async Task<string?> GetAsync(HttpClient client, string url, CancellationToken token = default)
{
if (string.IsNullOrWhiteSpace(url)) return null;
if (Utils.IsNullOrEmpty(url)) return null;
return await client.GetStringAsync(url, token);
}
public async Task PutAsync(string url, Dictionary<string, string> headers)
{
var jsonContent = Utils.ToJson(headers);
var jsonContent = JsonUtils.Serialize(headers);
var content = new StringContent(jsonContent, Encoding.UTF8, MediaTypeNames.Application.Json);
var result = await httpClient.PutAsync(url, content);
}
public async Task PatchAsync(string url, Dictionary<string, string> headers)
{
var myContent = JsonUtils.Serialize(headers);
var buffer = System.Text.Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
await httpClient.PatchAsync(url, byteContent);
}
public async Task DeleteAsync(string url)
{
await httpClient.DeleteAsync(url);
}
public static async Task DownloadFileAsync(HttpClient client, string url, string fileName, IProgress<double>? progress, CancellationToken token = default)
{
ArgumentNullException.ThrowIfNull(url);
@@ -53,8 +84,8 @@ namespace v2rayN.Base
var total = response.Content.Headers.ContentLength ?? -1L;
var canReportProgress = total != -1 && progress != null;
using var stream = await response.Content.ReadAsStreamAsync(token);
using var file = File.Create(fileName);
await using var stream = await response.Content.ReadAsStreamAsync(token);
await using var file = File.Create(fileName);
var totalRead = 0L;
var buffer = new byte[1024 * 1024];
var progressPercentage = 0;
@@ -67,7 +98,7 @@ namespace v2rayN.Base
totalRead += read;
if (read == 0) break;
file.Write(buffer, 0, read);
await file.WriteAsync(buffer.AsMemory(0, read), token);
if (canReportProgress)
{
@@ -75,20 +106,19 @@ namespace v2rayN.Base
//if (progressPercentage != percent && percent % 10 == 0)
{
progressPercentage = percent;
progress!.Report(percent);
progress?.Report(percent);
}
}
}
if (canReportProgress)
{
progress!.Report(101);
progress?.Report(101);
}
}
public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> progress, CancellationToken token = default)
{
if (string.IsNullOrEmpty(url))
if (Utils.IsNullOrEmpty(url))
{
throw new ArgumentNullException(nameof(url));
}
@@ -103,13 +133,13 @@ namespace v2rayN.Base
//var total = response.Content.Headers.ContentLength.HasValue ? response.Content.Headers.ContentLength.Value : -1L;
//var canReportProgress = total != -1 && progress != null;
using var stream = await response.Content.ReadAsStreamAsync(token);
await using var stream = await response.Content.ReadAsStreamAsync(token);
var totalRead = 0L;
var buffer = new byte[1024 * 64];
var isMoreToRead = true;
string progressSpeed = string.Empty;
DateTime totalDatetime = DateTime.Now;
int totalSecond = 0;
var progressSpeed = string.Empty;
var totalDatetime = DateTime.Now;
var totalSecond = 0;
do
{
@@ -136,10 +166,9 @@ namespace v2rayN.Base
var data = new byte[read];
buffer.ToList().CopyTo(0, data, 0, read);
// TODO:
totalRead += read;
TimeSpan ts = (DateTime.Now - totalDatetime);
var ts = (DateTime.Now - totalDatetime);
if (progress != null && ts.Seconds > totalSecond)
{
totalSecond = ts.Seconds;
@@ -153,6 +182,5 @@ namespace v2rayN.Base
}
} while (isMoreToRead);
}
}
}
}

View File

@@ -1,14 +1,14 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace v2rayN
namespace ServiceLib.Common
{
/*
* See:
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
*/
public class Job : IDisposable
public sealed class Job : IDisposable
{
private IntPtr handle = IntPtr.Zero;
@@ -52,7 +52,7 @@ namespace v2rayN
if (!succ)
{
Utils.SaveLog("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
Logging.SaveLog("Failed to call AssignProcessToJobObject! GetLastError=" + Marshal.GetLastWin32Error());
}
return succ;
@@ -73,7 +73,7 @@ namespace v2rayN
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
private void Dispose(bool disposing)
{
if (disposed) return;
disposed = true;
@@ -95,7 +95,7 @@ namespace v2rayN
Dispose(false);
}
#endregion
#endregion IDisposable
#region Interop
@@ -112,13 +112,13 @@ namespace v2rayN
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr hObject);
#endregion
#endregion Interop
}
#region Helper classes
[StructLayout(LayoutKind.Sequential)]
struct IO_COUNTERS
internal struct IO_COUNTERS
{
public UInt64 ReadOperationCount;
public UInt64 WriteOperationCount;
@@ -128,9 +128,8 @@ namespace v2rayN
public UInt64 OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_BASIC_LIMIT_INFORMATION
internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public Int64 PerProcessUserTimeLimit;
public Int64 PerJobUserTimeLimit;
@@ -152,7 +151,7 @@ namespace v2rayN
}
[StructLayout(LayoutKind.Sequential)]
struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
internal struct JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
public JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
public IO_COUNTERS IoInfo;
@@ -173,5 +172,5 @@ namespace v2rayN
GroupInformation = 11
}
#endregion
}
#endregion Helper classes
}

View File

@@ -0,0 +1,137 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace ServiceLib.Common
{
public class JsonUtils
{
/// <summary>
/// DeepCopy
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public static T DeepCopy<T>(T obj)
{
return Deserialize<T>(Serialize(obj, false))!;
}
/// <summary>
/// Deserialize to object
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="strJson"></param>
/// <returns></returns>
public static T? Deserialize<T>(string? strJson)
{
try
{
if (string.IsNullOrWhiteSpace(strJson))
{
return default;
}
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
return JsonSerializer.Deserialize<T>(strJson, options);
}
catch
{
return default;
}
}
/// <summary>
/// parse
/// </summary>
/// <param name="strJson"></param>
/// <returns></returns>
public static JsonNode? ParseJson(string strJson)
{
try
{
if (string.IsNullOrWhiteSpace(strJson))
{
return null;
}
return JsonNode.Parse(strJson);
}
catch
{
//SaveLog(ex.Message, ex);
return null;
}
}
/// <summary>
/// Serialize Object to Json string
/// </summary>
/// <param name="obj"></param>
/// <param name="indented"></param>
/// <returns></returns>
public static string Serialize(object? obj, bool indented = true)
{
var result = string.Empty;
try
{
if (obj == null)
{
return result;
}
var options = new JsonSerializerOptions
{
WriteIndented = indented ? true : false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
result = JsonSerializer.Serialize(obj, options);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return result;
}
/// <summary>
/// SerializeToNode
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static JsonNode? SerializeToNode(object? obj) => JsonSerializer.SerializeToNode(obj);
/// <summary>
/// Save as json file
/// </summary>
/// <param name="obj"></param>
/// <param name="filePath"></param>
/// <param name="nullValue"></param>
/// <returns></returns>
public static int ToFile(object? obj, string? filePath, bool nullValue = true)
{
if (filePath is null)
{
return -1;
}
try
{
using var file = File.Create(filePath);
var options = new JsonSerializerOptions
{
WriteIndented = true,
DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull
};
JsonSerializer.Serialize(file, obj, options);
return 0;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
return -1;
}
}
}
}

View File

@@ -0,0 +1,78 @@
using NLog;
using NLog.Config;
using NLog.Targets;
namespace ServiceLib.Common
{
public class Logging
{
public static void Setup()
{
LoggingConfiguration config = new();
FileTarget fileTarget = new();
config.AddTarget("file", fileTarget);
fileTarget.Layout = "${longdate}-${level:uppercase=true} ${message}";
fileTarget.FileName = Utils.GetLogPath("${shortdate}.txt");
config.LoggingRules.Add(new LoggingRule("*", LogLevel.Debug, fileTarget));
LogManager.Configuration = config;
}
public static void LoggingEnabled(bool enable)
{
if (!enable)
{
LogManager.SuspendLogging();
}
}
public static void ClearLogs()
{
Task.Run(() =>
{
try
{
var now = DateTime.Now.AddMonths(-1);
var dir = Utils.GetLogPath();
var files = Directory.GetFiles(dir, "*.txt");
foreach (var filePath in files)
{
var file = new FileInfo(filePath);
if (file.CreationTime >= now) continue;
try
{
file.Delete();
}
catch
{
// ignored
}
}
}
catch
{
// ignored
}
});
}
public static void SaveLog(string strContent)
{
if (!LogManager.IsLoggingEnabled()) return;
LogManager.GetLogger("Log1").Info(strContent);
}
public static void SaveLog(string strTitle, Exception ex)
{
if (!LogManager.IsLoggingEnabled()) return;
var logger = LogManager.GetLogger("Log2");
logger.Debug($"{strTitle},{ex.Message}");
logger.Debug(ex.StackTrace);
if (ex?.InnerException != null)
{
logger.Error(ex.InnerException);
}
}
}
}

View File

@@ -0,0 +1,90 @@
using QRCoder;
using SkiaSharp;
using ZXing.SkiaSharp;
namespace ServiceLib.Common
{
public class QRCodeHelper
{
public static byte[]? GenQRCode(string? url)
{
using QRCodeGenerator qrGenerator = new();
using var qrCodeData = qrGenerator.CreateQrCode(url ?? string.Empty, QRCodeGenerator.ECCLevel.Q);
using PngByteQRCode qrCode = new(qrCodeData);
return qrCode.GetGraphic(20);
}
public static string? ParseBarcode(string? fileName)
{
if (fileName == null || !File.Exists(fileName))
{
return null;
}
try
{
var image = SKImage.FromEncodedData(fileName);
var bitmap = SKBitmap.FromImage(image);
return ReaderBarcode(bitmap);
}
catch
{
// ignored
}
return null;
}
public static string? ParseBarcode(byte[]? bytes)
{
try
{
var bitmap = SKBitmap.Decode(bytes);
//using var stream = new FileStream("test2.png", FileMode.Create, FileAccess.Write);
//using var image = SKImage.FromBitmap(bitmap);
//using var encodedImage = image.Encode();
//encodedImage.SaveTo(stream);
return ReaderBarcode(bitmap);
}
catch
{
// ignored
}
return null;
}
private static string? ReaderBarcode(SKBitmap? bitmap)
{
var reader = new BarcodeReader();
var result = reader.Decode(bitmap);
if (result != null && Utils.IsNotEmpty(result.Text))
{
return result.Text;
}
//FlipBitmap
var result2 = reader.Decode(FlipBitmap(bitmap));
return result2?.Text;
}
private static SKBitmap FlipBitmap(SKBitmap bmp)
{
// Create a bitmap (to return)
var flipped = new SKBitmap(bmp.Width, bmp.Height, bmp.Info.ColorType, bmp.Info.AlphaType);
// Create a canvas to draw into the bitmap
using var canvas = new SKCanvas(flipped);
// Set a transform matrix which moves the bitmap to the right,
// and then "scales" it by -1, which just flips the pixels
// horizontally
canvas.Translate(bmp.Width, 0);
canvas.Scale(-1, 1);
canvas.DrawBitmap(bmp, 0, 0);
return flipped;
}
}
}

View File

@@ -1,7 +1,7 @@
using System.Linq.Expressions;
using System.Reflection;
namespace v2rayN.Tool
namespace ServiceLib.Common
{
public static class QueryableExtension
{
@@ -9,14 +9,15 @@ namespace v2rayN.Tool
{
return _OrderBy<T>(query, propertyName, false);
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName)
{
return _OrderBy<T>(query, propertyName, true);
}
static IOrderedQueryable<T> _OrderBy<T>(IQueryable<T> query, string propertyName, bool isDesc)
private static IOrderedQueryable<T> _OrderBy<T>(IQueryable<T> query, string propertyName, bool isDesc)
{
string methodname = (isDesc) ? "OrderByDescendingInternal" : "OrderByInternal";
var methodname = (isDesc) ? "OrderByDescendingInternal" : "OrderByInternal";
var memberProp = typeof(T).GetProperty(propertyName);
@@ -25,22 +26,25 @@ namespace v2rayN.Tool
return (IOrderedQueryable<T>)method.Invoke(null, new object[] { query, memberProp });
}
public static IOrderedQueryable<T> OrderByInternal<T, TProp>(IQueryable<T> query, PropertyInfo memberProperty)
{//public
return query.OrderBy(_GetLamba<T, TProp>(memberProperty));
return query.OrderBy(_GetLambda<T, TProp>(memberProperty));
}
public static IOrderedQueryable<T> OrderByDescendingInternal<T, TProp>(IQueryable<T> query, PropertyInfo memberProperty)
{//public
return query.OrderByDescending(_GetLamba<T, TProp>(memberProperty));
return query.OrderByDescending(_GetLambda<T, TProp>(memberProperty));
}
static Expression<Func<T, TProp>> _GetLamba<T, TProp>(PropertyInfo memberProperty)
private static Expression<Func<T, TProp>> _GetLambda<T, TProp>(PropertyInfo memberProperty)
{
if (memberProperty.PropertyType != typeof(TProp)) throw new Exception();
var thisArg = Expression.Parameter(typeof(T));
var lamba = Expression.Lambda<Func<T, TProp>>(Expression.Property(thisArg, memberProperty), thisArg);
var lambda = Expression.Lambda<Func<T, TProp>>(Expression.Property(thisArg, memberProperty), thisArg);
return lamba;
return lambda;
}
}
}
}

View File

@@ -0,0 +1,187 @@
namespace ServiceLib.Common
{
public class SemanticVersion
{
private int major;
private int minor;
private int patch;
private string version;
public SemanticVersion(int major, int minor, int patch)
{
this.major = major;
this.minor = minor;
this.patch = patch;
this.version = $"{major}.{minor}.{patch}";
}
public SemanticVersion(string? version)
{
try
{
if (version.IsNullOrEmpty())
{
this.major = 0;
this.minor = 0;
this.patch = 0;
return;
}
this.version = version.RemovePrefix('v');
var parts = this.version.Split('.');
if (parts.Length == 2)
{
this.major = int.Parse(parts.First());
this.minor = int.Parse(parts.Last());
this.patch = 0;
}
else if (parts.Length is 3 or 4)
{
this.major = int.Parse(parts[0]);
this.minor = int.Parse(parts[1]);
this.patch = int.Parse(parts[2]);
}
else
{
throw new ArgumentException("Invalid version string");
}
}
catch
{
this.major = 0;
this.minor = 0;
this.patch = 0;
}
}
public override bool Equals(object? obj)
{
if (obj is SemanticVersion other)
{
return this.major == other.major && this.minor == other.minor && this.patch == other.patch;
}
else
{
return false;
}
}
public override int GetHashCode()
{
return this.major.GetHashCode() ^ this.minor.GetHashCode() ^ this.patch.GetHashCode();
}
/// <summary>
/// Use ToVersionString(string? prefix) instead if possible.
/// </summary>
/// <returns>major.minor.patch</returns>
public override string ToString()
{
return this.version;
}
public string ToVersionString(string? prefix = null)
{
if (prefix == null)
{
return this.version;
}
else
{
return $"{prefix}{this.version}";
}
}
public static bool operator ==(SemanticVersion v1, SemanticVersion v2)
{ return v1.Equals(v2); }
public static bool operator !=(SemanticVersion v1, SemanticVersion v2)
{ return !v1.Equals(v2); }
public static bool operator >=(SemanticVersion v1, SemanticVersion v2)
{ return v1.GreaterEquals(v2); }
public static bool operator <=(SemanticVersion v1, SemanticVersion v2)
{ return v1.LessEquals(v2); }
#region Private
private bool GreaterEquals(SemanticVersion other)
{
if (this.major < other.major)
{
return false;
}
else if (this.major > other.major)
{
return true;
}
else
{
if (this.minor < other.minor)
{
return false;
}
else if (this.minor > other.minor)
{
return true;
}
else
{
if (this.patch < other.patch)
{
return false;
}
else if (this.patch > other.patch)
{
return true;
}
else
{
return true;
}
}
}
}
private bool LessEquals(SemanticVersion other)
{
if (this.major < other.major)
{
return true;
}
else if (this.major > other.major)
{
return false;
}
else
{
if (this.minor < other.minor)
{
return true;
}
else if (this.minor > other.minor)
{
return false;
}
else
{
if (this.patch < other.patch)
{
return true;
}
else if (this.patch > other.patch)
{
return false;
}
else
{
return true;
}
}
}
}
#endregion Private
}
}

View File

@@ -0,0 +1,77 @@
using SQLite;
using System.Collections;
namespace ServiceLib.Common
{
public sealed class SQLiteHelper
{
private static readonly Lazy<SQLiteHelper> _instance = new(() => new());
public static SQLiteHelper Instance => _instance.Value;
private string _connstr;
private SQLiteConnection _db;
private SQLiteAsyncConnection _dbAsync;
private readonly string _configDB = "guiNDB.db";
public SQLiteHelper()
{
_connstr = Utils.GetConfigPath(_configDB);
_db = new SQLiteConnection(_connstr, false);
_dbAsync = new SQLiteAsyncConnection(_connstr, false);
}
public CreateTableResult CreateTable<T>()
{
return _db.CreateTable<T>();
}
public async Task<int> InsertAllAsync(IEnumerable models)
{
return await _dbAsync.InsertAllAsync(models);
}
public async Task<int> InsertAsync(object model)
{
return await _dbAsync.InsertAsync(model);
}
public async Task<int> ReplaceAsync(object model)
{
return await _dbAsync.InsertOrReplaceAsync(model);
}
public async Task<int> UpdateAsync(object model)
{
return await _dbAsync.UpdateAsync(model);
}
public async Task<int> UpdateAllAsync(IEnumerable models)
{
return await _dbAsync.UpdateAllAsync(models);
}
public async Task<int> DeleteAsync(object model)
{
return await _dbAsync.DeleteAsync(model);
}
public async Task<int> DeleteAllAsync<T>()
{
return await _dbAsync.DeleteAllAsync<T>();
}
public async Task<int> ExecuteAsync(string sql)
{
return await _dbAsync.ExecuteAsync(sql);
}
public async Task<List<T>> QueryAsync<T>(string sql) where T : new()
{
return await _dbAsync.QueryAsync<T>(sql);
}
public AsyncTableQuery<T> TableAsync<T>() where T : new()
{
return _dbAsync.Table<T>();
}
}
}

View File

@@ -0,0 +1,72 @@
using System.Diagnostics.CodeAnalysis;
namespace ServiceLib.Common
{
public static class StringEx
{
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
{
return string.IsNullOrEmpty(value);
}
public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value)
{
return string.IsNullOrWhiteSpace(value);
}
public static bool IsNotEmpty([NotNullWhen(false)] this string? value)
{
return !string.IsNullOrEmpty(value);
}
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
{
if (s.IsNullOrEmpty()) return false;
return chars.Contains(s.First());
}
private static bool IsWhiteSpace(this string value)
{
return value.All(char.IsWhiteSpace);
}
public static IEnumerable<string> NonWhiteSpaceLines(this TextReader reader)
{
while (reader.ReadLine() is { } line)
{
if (line.IsWhiteSpace()) continue;
yield return line;
}
}
public static string TrimEx(this string? value)
{
return value == null ? string.Empty : value.Trim();
}
public static string RemovePrefix(this string value, char prefix)
{
return value.StartsWith(prefix) ? value[1..] : value;
}
public static string RemovePrefix(this string value, string prefix)
{
return value.StartsWith(prefix) ? value[prefix.Length..] : value;
}
public static string UpperFirstChar(this string value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
return char.ToUpper(value.First()) + value[1..];
}
public static string AppendQuotes(this string value)
{
return string.IsNullOrEmpty(value) ? string.Empty : $"\"{value}\"";
}
}
}

View File

@@ -0,0 +1,875 @@
using CliWrap;
using CliWrap.Buffered;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;
namespace ServiceLib.Common
{
public class Utils
{
#region
/// <summary>
/// 获取嵌入文本资源
/// </summary>
/// <param name="res"></param>
/// <returns></returns>
public static string GetEmbedText(string res)
{
var result = string.Empty;
try
{
var assembly = Assembly.GetExecutingAssembly();
using var stream = assembly.GetManifestResourceStream(res);
ArgumentNullException.ThrowIfNull(stream);
using StreamReader reader = new(stream);
result = reader.ReadToEnd();
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return result;
}
/// <summary>
/// 取得存储资源
/// </summary>
/// <returns></returns>
public static string? LoadResource(string? res)
{
try
{
if (File.Exists(res))
{
return File.ReadAllText(res);
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return null;
}
#endregion
#region
/// <summary>
/// 转逗号分隔的字符串
/// </summary>
/// <param name="lst"></param>
/// <param name="wrap"></param>
/// <returns></returns>
public static string List2String(List<string>? lst, bool wrap = false)
{
try
{
if (lst == null)
{
return string.Empty;
}
if (wrap)
{
return string.Join("," + Environment.NewLine, lst);
}
else
{
return string.Join(",", lst);
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return string.Empty;
}
/// <summary>
/// 逗号分隔的字符串
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static List<string>? String2List(string? str)
{
try
{
if (str == null)
{
return null;
}
str = str.Replace(Environment.NewLine, "");
return new List<string>(str.Split(',', StringSplitOptions.RemoveEmptyEntries));
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return null;
}
/// <summary>
/// 逗号分隔的字符串,先排序后转List
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static List<string>? String2ListSorted(string str)
{
try
{
str = str.Replace(Environment.NewLine, "");
List<string> list = new(str.Split(',', StringSplitOptions.RemoveEmptyEntries));
list.Sort();
return list;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return null;
}
/// <summary>
/// Base64编码
/// </summary>
/// <param name="plainText"></param>
/// <returns></returns>
public static string Base64Encode(string plainText)
{
try
{
var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
return Convert.ToBase64String(plainTextBytes);
}
catch (Exception ex)
{
Logging.SaveLog("Base64Encode", ex);
}
return string.Empty;
}
/// <summary>
/// Base64解码
/// </summary>
/// <param name="plainText"></param>
/// <returns></returns>
public static string Base64Decode(string? plainText)
{
try
{
if (plainText.IsNullOrEmpty()) return "";
plainText = plainText.Trim()
.Replace(Environment.NewLine, "")
.Replace("\n", "")
.Replace("\r", "")
.Replace('_', '/')
.Replace('-', '+')
.Replace(" ", "");
if (plainText.Length % 4 > 0)
{
plainText = plainText.PadRight(plainText.Length + 4 - (plainText.Length % 4), '=');
}
var data = Convert.FromBase64String(plainText);
return Encoding.UTF8.GetString(data);
}
catch (Exception ex)
{
Logging.SaveLog("Base64Decode", ex);
}
return string.Empty;
}
public static int ToInt(object? obj)
{
try
{
return Convert.ToInt32(obj ?? string.Empty);
}
catch
{
return 0;
}
}
public static bool ToBool(object obj)
{
try
{
return Convert.ToBoolean(obj);
}
catch
{
return false;
}
}
public static string ToString(object? obj)
{
try
{
return obj?.ToString() ?? string.Empty;
}
catch
{
return string.Empty;
}
}
private static void ToHumanReadable(long amount, out double result, out string unit)
{
var factor = 1024u;
//long KBs = amount / factor;
var KBs = amount;
if (KBs > 0)
{
// multi KB
var MBs = KBs / factor;
if (MBs > 0)
{
// multi MB
var GBs = MBs / factor;
if (GBs > 0)
{
// multi GB
var TBs = GBs / factor;
if (TBs > 0)
{
result = TBs + ((GBs % factor) / (factor + 0.0));
unit = "TB";
return;
}
result = GBs + ((MBs % factor) / (factor + 0.0));
unit = "GB";
return;
}
result = MBs + ((KBs % factor) / (factor + 0.0));
unit = "MB";
return;
}
result = KBs + ((amount % factor) / (factor + 0.0));
unit = "KB";
return;
}
else
{
result = amount;
unit = "B";
}
}
public static string HumanFy(long amount)
{
ToHumanReadable(amount, out var result, out var unit);
return $"{result:f1} {unit}";
}
public static string UrlEncode(string url)
{
return Uri.EscapeDataString(url);
}
public static string UrlDecode(string url)
{
return Uri.UnescapeDataString(url);
}
public static NameValueCollection ParseQueryString(string query)
{
var result = new NameValueCollection(StringComparer.OrdinalIgnoreCase);
if (IsNullOrEmpty(query))
{
return result;
}
var parts = query[1..].Split('&', StringSplitOptions.RemoveEmptyEntries);
foreach (var part in parts)
{
var keyValue = part.Split('=');
if (keyValue.Length != 2)
{
continue;
}
var key = Uri.UnescapeDataString(keyValue.First());
var val = Uri.UnescapeDataString(keyValue.Last());
if (result[key] is null)
{
result.Add(key, val);
}
}
return result;
}
public static string GetMd5(string str)
{
var byteOld = Encoding.UTF8.GetBytes(str);
var byteNew = MD5.HashData(byteOld);
StringBuilder sb = new(32);
foreach (var b in byteNew)
{
sb.Append(b.ToString("x2"));
}
return sb.ToString();
}
/// <summary>
/// idn to idc
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static string GetPunycode(string url)
{
if (Utils.IsNullOrEmpty(url))
{
return url;
}
try
{
Uri uri = new(url);
if (uri.Host == uri.IdnHost || uri.Host == $"[{uri.IdnHost}]")
{
return url;
}
else
{
return url.Replace(uri.Host, uri.IdnHost);
}
}
catch
{
return url;
}
}
public static bool IsBase64String(string? plainText)
{
if (plainText.IsNullOrEmpty()) return false;
var buffer = new Span<byte>(new byte[plainText.Length]);
return Convert.TryFromBase64String(plainText, buffer, out var _);
}
public static string Convert2Comma(string text)
{
if (IsNullOrEmpty(text))
{
return text;
}
return text.Replace("", ",").Replace(Environment.NewLine, ",");
}
#endregion
#region
/// <summary>
/// 判断输入的是否是数字
/// </summary>
/// <param name="oText"></param>
/// <returns></returns>
public static bool IsNumeric(string oText)
{
return oText.All(char.IsNumber);
}
public static bool IsNullOrEmpty(string? text)
{
if (string.IsNullOrWhiteSpace(text))
{
return true;
}
return text == "null";
}
public static bool IsNotEmpty(string? text)
{
return !string.IsNullOrEmpty(text);
}
/// <summary>
/// 验证Domain地址是否合法
/// </summary>
/// <param name="domain"></param>
public static bool IsDomain(string? domain)
{
if (IsNullOrEmpty(domain))
{
return false;
}
return Uri.CheckHostName(domain) == UriHostNameType.Dns;
}
public static bool IsIpv6(string ip)
{
if (IPAddress.TryParse(ip, out var address))
{
return address.AddressFamily switch
{
AddressFamily.InterNetwork => false,
AddressFamily.InterNetworkV6 => true,
_ => false,
};
}
return false;
}
public static Uri? TryUri(string url)
{
try
{
return new Uri(url);
}
catch (UriFormatException)
{
return null;
}
}
public static bool IsPrivateNetwork(string ip)
{
if (IPAddress.TryParse(ip, out var address))
{
var ipBytes = address.GetAddressBytes();
if (ipBytes[0] == 10) return true;
if (ipBytes[0] == 172 && ipBytes[1] >= 16 && ipBytes[1] <= 31) return true;
if (ipBytes[0] == 192 && ipBytes[1] == 168) return true;
}
return false;
}
#endregion
#region
private static bool PortInUse(int port)
{
try
{
var ipProperties = IPGlobalProperties.GetIPGlobalProperties();
var ipEndPoints = ipProperties.GetActiveTcpListeners();
//var lstIpEndPoints = new List<IPEndPoint>(IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners());
return ipEndPoints.Any(endPoint => endPoint.Port == port);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return false;
}
public static int GetFreePort(int defaultPort = 9090)
{
try
{
if (!Utils.PortInUse(defaultPort))
{
return defaultPort;
}
TcpListener l = new(IPAddress.Loopback, 0);
l.Start();
var port = ((IPEndPoint)l.LocalEndpoint).Port;
l.Stop();
return port;
}
catch
{
}
return 59090;
}
#endregion
#region
public static bool UpgradeAppExists(out string fileName)
{
fileName = Path.Combine(Utils.StartupPath(), GetExeName("AmazTool"));
return File.Exists(fileName);
}
/// <summary>
/// 取得版本
/// </summary>
/// <returns></returns>
public static string GetVersion(bool blFull = true)
{
try
{
if (blFull)
{
return
$"{Global.AppName} - V{GetVersionInfo()} - {RuntimeInformation.ProcessArchitecture} - {File.GetLastWriteTime(GetExePath()):yyyy/MM/dd}";
}
else
{
return $"{Global.AppName}/{GetVersionInfo()}";
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return Global.AppName;
}
public static string GetVersionInfo()
{
try
{
return Assembly.GetExecutingAssembly()?.GetName()?.Version?.ToString(3) ?? "0.0";
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
return "0.0";
}
}
/// <summary>
/// 取得GUID
/// </summary>
/// <returns></returns>
public static string GetGuid(bool full = true)
{
try
{
if (full)
{
return Guid.NewGuid().ToString("D");
}
else
{
return BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0).ToString();
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return string.Empty;
}
public static bool IsGuidByParse(string strSrc)
{
return Guid.TryParse(strSrc, out _);
}
public static void ProcessStart(string? fileName, string arguments = "")
{
try
{
if (fileName.IsNullOrEmpty())
{
return;
}
Process.Start(new ProcessStartInfo(fileName, arguments) { UseShellExecute = true });
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public static Dictionary<string, string> GetSystemHosts()
{
var systemHosts = new Dictionary<string, string>();
var hostFile = @"C:\Windows\System32\drivers\etc\hosts";
try
{
if (File.Exists(hostFile))
{
var hosts = File.ReadAllText(hostFile).Replace("\r", "");
var hostsList = hosts.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var host in hostsList)
{
if (host.StartsWith("#")) continue;
var hostItem = host.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
if (hostItem.Length != 2) continue;
systemHosts.Add(hostItem.Last(), hostItem.First());
}
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return systemHosts;
}
public static async Task<string?> GetCliWrapOutput(string filePath, string? arg)
{
return await GetCliWrapOutput(filePath, arg != null ? new List<string>() { arg } : null);
}
public static async Task<string?> GetCliWrapOutput(string filePath, IEnumerable<string>? args)
{
try
{
var cmd = Cli.Wrap(filePath);
if (args != null)
{
if (args.Count() == 1)
{
cmd = cmd.WithArguments(args.First());
}
else
{
cmd = cmd.WithArguments(args);
}
}
var result = await cmd.ExecuteBufferedAsync();
if (result.IsSuccess)
{
return result.StandardOutput.ToString();
}
Logging.SaveLog(result.ToString() ?? "");
}
catch (Exception ex)
{
Logging.SaveLog("GetCliWrapOutput", ex);
}
return null;
}
#endregion
#region TempPath
public static string GetPath(string fileName)
{
var startupPath = StartupPath();
if (IsNullOrEmpty(fileName))
{
return startupPath;
}
return Path.Combine(startupPath, fileName);
}
public static string GetExePath()
{
return Environment.ProcessPath ?? Process.GetCurrentProcess().MainModule?.FileName ?? string.Empty;
}
public static string StartupPath()
{
return AppDomain.CurrentDomain.BaseDirectory;
}
public static string GetTempPath(string filename = "")
{
var tempPath = Path.Combine(StartupPath(), "guiTemps");
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
if (IsNullOrEmpty(filename))
{
return tempPath;
}
else
{
return Path.Combine(tempPath, filename);
}
}
public static string GetBackupPath(string filename)
{
var tempPath = Path.Combine(StartupPath(), "guiBackups");
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
return Path.Combine(tempPath, filename);
}
public static string GetConfigPath(string filename = "")
{
var tempPath = Path.Combine(StartupPath(), "guiConfigs");
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
if (Utils.IsNullOrEmpty(filename))
{
return tempPath;
}
else
{
return Path.Combine(tempPath, filename);
}
}
public static string GetBinPath(string filename, string? coreType = null)
{
var tempPath = Path.Combine(StartupPath(), "bin");
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
if (coreType != null)
{
tempPath = Path.Combine(tempPath, coreType.ToLower().ToString());
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
}
if (IsNullOrEmpty(filename))
{
return tempPath;
}
else
{
return Path.Combine(tempPath, filename);
}
}
public static string GetLogPath(string filename = "")
{
var tempPath = Path.Combine(StartupPath(), "guiLogs");
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
if (Utils.IsNullOrEmpty(filename))
{
return tempPath;
}
else
{
return Path.Combine(tempPath, filename);
}
}
public static string GetFontsPath(string filename = "")
{
var tempPath = Path.Combine(StartupPath(), "guiFonts");
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
}
if (Utils.IsNullOrEmpty(filename))
{
return tempPath;
}
else
{
return Path.Combine(tempPath, filename);
}
}
#endregion TempPath
#region Platform
public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
public static bool IsOSX() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
public static string GetExeName(string name)
{
return IsWindows() ? $"{name}.exe" : name;
}
public static bool IsAdministrator()
{
if (IsWindows())
{
return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
}
else
{
var id = GetLinuxUserId().Result ?? "1000";
if (int.TryParse(id, out var userId))
{
return userId == 0;
}
else
{
return false;
}
}
}
private static async Task<string?> GetLinuxUserId()
{
var arg = new List<string>() { "-c", "id -u" };
return await GetCliWrapOutput("/bin/bash", arg);
}
public static async Task<string?> SetLinuxChmod(string? fileName)
{
if (fileName.IsNullOrEmpty()) return null;
var arg = new List<string>() { "-c", $"chmod +x {fileName}" };
return await GetCliWrapOutput("/bin/bash", arg);
}
public static async Task<string?> GetLinuxFontFamily(string lang)
{
// var arg = new List<string>() { "-c", $"fc-list :lang={lang} family" };
var arg = new List<string>() { "-c", $"fc-list : family" };
return await GetCliWrapOutput("/bin/bash", arg);
}
public static string? GetHomePath()
{
return IsWindows()
? Environment.ExpandEnvironmentVariables("%HOMEDRIVE%%HOMEPATH%")
: Environment.GetEnvironmentVariable("HOME");
}
#endregion Platform
}
}

View File

@@ -0,0 +1,52 @@
using Microsoft.Win32;
namespace ServiceLib.Common
{
internal static class WindowsUtils
{
public static string? RegReadValue(string path, string name, string def)
{
RegistryKey? regKey = null;
try
{
regKey = Registry.CurrentUser.OpenSubKey(path, false);
var value = regKey?.GetValue(name) as string;
return Utils.IsNullOrEmpty(value) ? def : value;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
finally
{
regKey?.Close();
}
return def;
}
public static void RegWriteValue(string path, string name, object value)
{
RegistryKey? regKey = null;
try
{
regKey = Registry.CurrentUser.CreateSubKey(path);
if (Utils.IsNullOrEmpty(value.ToString()))
{
regKey?.DeleteValue(name, false);
}
else
{
regKey?.SetValue(name, value);
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
finally
{
regKey?.Close();
}
}
}
}

View File

@@ -0,0 +1,81 @@
using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace ServiceLib.Common
{
public class YamlUtils
{
#region YAML
/// <summary>
/// 反序列化成对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="str"></param>
/// <returns></returns>
public static T FromYaml<T>(string str)
{
var deserializer = new DeserializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance)
.Build();
try
{
var obj = deserializer.Deserialize<T>(str);
return obj;
}
catch (Exception ex)
{
Logging.SaveLog("FromYaml", ex);
return deserializer.Deserialize<T>("");
}
}
/// <summary>
/// 序列化
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string ToYaml(object? obj)
{
var result = string.Empty;
if (obj == null)
{
return result;
}
var serializer = new SerializerBuilder()
.WithNamingConvention(HyphenatedNamingConvention.Instance)
.Build();
try
{
result = serializer.Serialize(obj);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return result;
}
public static string? PreprocessYaml(string str)
{
var deserializer = new DeserializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance)
.Build();
try
{
var mergingParser = new MergingParser(new Parser(new StringReader(str)));
var obj = new DeserializerBuilder().Build().Deserialize(mergingParser);
return ToYaml(obj);
}
catch (Exception ex)
{
Logging.SaveLog("PreprocessYaml", ex);
return null;
}
}
#endregion YAML
}
}

View File

@@ -0,0 +1,16 @@
namespace ServiceLib.Enums
{
public enum EConfigType
{
VMess = 1,
Custom = 2,
Shadowsocks = 3,
SOCKS = 4,
VLESS = 5,
Trojan = 6,
Hysteria2 = 7,
TUIC = 8,
WireGuard = 9,
HTTP = 10
}
}

View File

@@ -1,18 +1,17 @@

namespace v2rayN.Mode
namespace ServiceLib.Enums
{
public enum ECoreType
{
v2fly = 1,
Xray = 2,
SagerNet = 3,
v2fly_v5 = 4,
clash = 11,
clash_meta = 12,
mihomo = 13,
hysteria = 21,
naiveproxy = 22,
tuic = 23,
sing_box = 24,
juicity = 25,
hysteria2 = 26,
v2rayN = 99
}
}
}

View File

@@ -0,0 +1,9 @@
namespace ServiceLib.Enums
{
public enum EGirdOrientation
{
Horizontal,
Vertical,
Tab,
}
}

View File

@@ -1,5 +1,4 @@

namespace v2rayN.Mode
namespace ServiceLib.Enums
{
public enum EGlobalHotkey
{
@@ -9,4 +8,4 @@ namespace v2rayN.Mode
SystemProxyUnchanged = 3,
SystemProxyPac = 4,
}
}
}

View File

@@ -0,0 +1,13 @@
namespace ServiceLib.Enums
{
public enum EInboundProtocol
{
socks = 0,
socks2,
pac,
api,
api2,
mixed,
speedtest = 21
}
}

View File

@@ -1,5 +1,4 @@

namespace v2rayN.Mode
namespace ServiceLib.Enums
{
public enum EMove
{
@@ -9,4 +8,4 @@ namespace v2rayN.Mode
Bottom = 4,
Position = 5
}
}
}

View File

@@ -0,0 +1,11 @@
namespace ServiceLib.Enums
{
public enum EMsgCommand
{
ClearMsg,
SendMsgView,
SendSnackMsg,
RefreshProfiles,
StopSpeedtest
}
}

View File

@@ -0,0 +1,8 @@
namespace ServiceLib.Enums
{
public enum EPresetType
{
Default = 0,
Russia = 1,
}
}

View File

@@ -0,0 +1,10 @@
namespace ServiceLib.Enums
{
public enum ERuleMode
{
Rule = 0,
Global = 1,
Direct = 2,
Unchanged = 3
}
}

View File

@@ -0,0 +1,21 @@
namespace ServiceLib.Enums
{
public enum EServerColName
{
Def = 0,
ConfigType,
Remarks,
Address,
Port,
Network,
StreamSecurity,
SubRemarks,
DelayVal,
SpeedVal,
TodayDown,
TodayUp,
TotalDown,
TotalUp
}
}

View File

@@ -1,12 +1,10 @@

namespace v2rayN.Mode
namespace ServiceLib.Enums
{
public enum ESpeedActionType
{
Ping,
Tcping,
Realping,
Speedtest,
Mixedtest
}
}
}

View File

@@ -1,5 +1,4 @@

namespace v2rayN.Mode
namespace ServiceLib.Enums
{
public enum ESysProxyType
{
@@ -8,4 +7,4 @@ namespace v2rayN.Mode
Unchanged = 2,
Pac = 3
}
}
}

View File

@@ -0,0 +1,15 @@
namespace ServiceLib.Enums
{
public enum ETransport
{
tcp,
kcp,
ws,
httpupgrade,
xhttp,
h2,
http,
quic,
grpc
}
}

View File

@@ -0,0 +1,46 @@
namespace ServiceLib.Enums
{
public enum EViewAction
{
CloseWindow,
ShowYesNo,
SaveFileDialog,
AddBatchRoutingRulesYesNo,
AdjustMainLvColWidth,
SetClipboardData,
AddServerViaClipboard,
ImportRulesFromClipboard,
ProfilesFocus,
ShareSub,
ShareServer,
ShowHideWindow,
ScanScreenTask,
ScanImageTask,
Shutdown,
BrowseServer,
ImportRulesFromFile,
InitSettingFont,
SubEditWindow,
RoutingRuleSettingWindow,
RoutingRuleDetailsWindow,
AddServerWindow,
AddServer2Window,
DNSSettingWindow,
RoutingSettingWindow,
OptionSettingWindow,
GlobalHotkeySettingWindow,
SubSettingWindow,
DispatcherSpeedTest,
DispatcherRefreshConnections,
DispatcherRefreshProxyGroups,
DispatcherProxiesDelayTest,
DispatcherStatistics,
DispatcherServerAvailability,
DispatcherReload,
DispatcherRefreshServersBiz,
DispatcherRefreshIcon,
DispatcherCheckUpdate,
DispatcherCheckUpdateFinished,
DispatcherShowMsg,
}
}

View File

@@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ReactiveUI />
</Weavers>

217
v2rayN/ServiceLib/Global.cs Normal file
View File

@@ -0,0 +1,217 @@
namespace ServiceLib
{
public class Global
{
#region const
public const string AppName = "v2rayN";
public const string GithubUrl = "https://github.com";
public const string GithubApiUrl = "https://api.github.com/repos";
public const string V2flyCoreUrl = "https://github.com/v2fly/v2ray-core/releases";
public const string XrayCoreUrl = "https://github.com/XTLS/Xray-core/releases";
public const string NUrl = @"https://github.com/2dust/v2rayN/releases";
public const string MihomoCoreUrl = "https://github.com/MetaCubeX/mihomo/releases";
public const string HysteriaCoreUrl = "https://github.com/apernet/hysteria/releases";
public const string NaiveproxyCoreUrl = "https://github.com/klzgrad/naiveproxy/releases";
public const string TuicCoreUrl = "https://github.com/EAimTY/tuic/releases";
public const string SingboxCoreUrl = "https://github.com/SagerNet/sing-box/releases";
public const string GeoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat";
public const string SpeedPingTestUrl = @"https://www.google.com/generate_204";
public const string JuicityCoreUrl = "https://github.com/juicity/juicity/releases";
public const string CustomRoutingListUrl = @"https://raw.githubusercontent.com/2dust/v2rayCustomRoutingList/master/";
public const string SingboxRulesetUrl = @"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-{0}/{1}.srs";
public const string IPAPIUrl = "https://api.ip.sb/geoip";
public const string PromotionUrl = @"aHR0cHM6Ly85LjIzNDQ1Ni54eXovYWJjLmh0bWw=";
public const string ConfigFileName = "guiNConfig.json";
public const string CoreConfigFileName = "config.json";
public const string CorePreConfigFileName = "configPre.json";
public const string CoreSpeedtestConfigFileName = "configSpeedtest.json";
public const string CoreMultipleLoadConfigFileName = "configMultipleLoad.json";
public const string ClashMixinConfigFileName = "Mixin.yaml";
public const string NamespaceSample = "ServiceLib.Sample.";
public const string V2raySampleClient = NamespaceSample + "SampleClientConfig";
public const string SingboxSampleClient = NamespaceSample + "SingboxSampleClientConfig";
public const string V2raySampleHttpRequestFileName = NamespaceSample + "SampleHttpRequest";
public const string V2raySampleHttpResponseFileName = NamespaceSample + "SampleHttpResponse";
public const string V2raySampleInbound = NamespaceSample + "SampleInbound";
public const string V2raySampleOutbound = NamespaceSample + "SampleOutbound";
public const string SingboxSampleOutbound = NamespaceSample + "SingboxSampleOutbound";
public const string CustomRoutingFileName = NamespaceSample + "custom_routing_";
public const string TunSingboxDNSFileName = NamespaceSample + "tun_singbox_dns";
public const string TunSingboxInboundFileName = NamespaceSample + "tun_singbox_inbound";
public const string TunSingboxRulesFileName = NamespaceSample + "tun_singbox_rules";
public const string DNSV2rayNormalFileName = NamespaceSample + "dns_v2ray_normal";
public const string DNSSingboxNormalFileName = NamespaceSample + "dns_singbox_normal";
public const string ClashMixinYaml = NamespaceSample + "clash_mixin_yaml";
public const string ClashTunYaml = NamespaceSample + "clash_tun_yaml";
public const string LinuxAutostartConfig = NamespaceSample + "linux_autostart_config";
public const string PacFileName = NamespaceSample + "pac";
public const string DefaultSecurity = "auto";
public const string DefaultNetwork = "tcp";
public const string TcpHeaderHttp = "http";
public const string None = "none";
public const string ProxyTag = "proxy";
public const string DirectTag = "direct";
public const string BlockTag = "block";
public const string StreamSecurity = "tls";
public const string StreamSecurityReality = "reality";
public const string Loopback = "127.0.0.1";
public const string InboundAPIProtocol = "dokodemo-door";
public const string HttpProtocol = "http://";
public const string HttpsProtocol = "https://";
public const string SocksProtocol = "socks://";
public const string UserEMail = "t@t.tt";
public const string AutoRunRegPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
public const string AutoRunName = "v2rayNAutoRun";
public const string CustomIconName = "v2rayN.ico";
public const string IEProxyExceptions = "localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;192.168.*";
public const string RoutingRuleComma = "<COMMA>";
public const string GrpcGunMode = "gun";
public const string GrpcMultiMode = "multi";
public const int MaxPort = 65536;
public const string DelayUnit = "";
public const string SpeedUnit = "";
public const int MinFontSize = 10;
public const string RebootAs = "rebootas";
public static readonly List<string> IEProxyProtocols = new() {
"{ip}:{http_port}",
"socks={ip}:{socks_port}",
"http={ip}:{http_port};https={ip}:{http_port};ftp={ip}:{http_port};socks={ip}:{socks_port}",
"http=http://{ip}:{http_port};https=http://{ip}:{http_port}",
""
};
public static readonly List<string> SubConvertUrls = new List<string> {
@"https://sub.xeton.dev/sub?url={0}",
@"https://api.dler.io/sub?url={0}",
@"http://127.0.0.1:25500/sub?url={0}",
""
};
public static readonly List<string> SubConvertConfig = new List<string> {
@"https://raw.githubusercontent.com/ACL4SSR/ACL4SSR/master/Clash/config/ACL4SSR_Online.ini"
};
public static readonly List<string> SubConvertTargets = new List<string> {
"",
"mixed",
"v2ray",
"clash",
"ss",
};
public static readonly List<string> SpeedTestUrls = new() {
@"https://speed.cloudflare.com/__down?bytes=100000000",
@"https://speed.cloudflare.com/__down?bytes=50000000",
@"https://speed.cloudflare.com/__down?bytes=10000000",
@"https://cachefly.cachefly.net/50mb.test",
};
public static readonly List<string> SpeedPingTestUrls = new() {
@"https://www.google.com/generate_204",
@"https://www.gstatic.com/generate_204",
@"https://www.apple.com/library/test/success.html",
@"http://www.msftconnecttest.com/connecttest.txt",
};
public static readonly List<string> GeoFilesSources = new() {
"",
@"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/{0}.dat",
};
public static readonly List<string> SingboxRulesetSources = new() {
"",
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-rules-dat@release/sing-box/rule-set-{0}/{1}.srs",
};
public static readonly List<string> RoutingRulesSources = new() {
"",
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/template.json",
};
public static readonly List<string> DNSTemplateSources = new() {
"",
@"https://cdn.jsdelivr.net/gh/runetfreedom/russia-v2ray-custom-routing-list@main/v2rayN/",
};
public static readonly Dictionary<string, string> UserAgentTexts = new()
{
{"chrome","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36" },
{"firefox","Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0" },
{"safari","Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15" },
{"edge","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70" },
{"none",""}
};
public const string Hysteria2ProtocolShare = "hy2://";
public static readonly Dictionary<EConfigType, string> ProtocolShares = new()
{
{EConfigType.VMess,"vmess://"},
{EConfigType.Shadowsocks,"ss://"},
{EConfigType.SOCKS,"socks://"},
{EConfigType.VLESS,"vless://"},
{EConfigType.Trojan,"trojan://"},
{EConfigType.Hysteria2,"hysteria2://"},
{EConfigType.TUIC,"tuic://"},
{EConfigType.WireGuard,"wireguard://"}
};
public static readonly Dictionary<EConfigType, string> ProtocolTypes = new()
{
{EConfigType.VMess,"vmess"},
{EConfigType.Shadowsocks,"shadowsocks"},
{EConfigType.SOCKS,"socks"},
{EConfigType.HTTP,"http"},
{EConfigType.VLESS,"vless"},
{EConfigType.Trojan,"trojan"},
{EConfigType.Hysteria2,"hysteria2"},
{EConfigType.TUIC,"tuic"},
{EConfigType.WireGuard,"wireguard"}
};
public static readonly List<string> VmessSecurities = new() { "aes-128-gcm", "chacha20-poly1305", "auto", "none", "zero" };
public static readonly List<string> SsSecurities = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "none", "plain" };
public static readonly List<string> SsSecuritiesInXray = new() { "aes-256-gcm", "aes-128-gcm", "chacha20-poly1305", "chacha20-ietf-poly1305", "xchacha20-poly1305", "xchacha20-ietf-poly1305", "none", "plain", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" };
public static readonly List<string> SsSecuritiesInSingbox = new() { "aes-256-gcm", "aes-192-gcm", "aes-128-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "rc4-md5", "chacha20-ietf", "xchacha20" };
public static readonly List<string> Flows = new() { "", "xtls-rprx-vision", "xtls-rprx-vision-udp443" };
public static readonly List<string> Networks = new() { "tcp", "kcp", "ws", "httpupgrade", "xhttp", "h2", "quic", "grpc" };
public static readonly List<string> KcpHeaderTypes = new() { "srtp", "utp", "wechat-video", "dtls", "wireguard" };
public static readonly List<string> CoreTypes = new() { "Xray", "sing_box" };
public static readonly List<string> DomainStrategies = new() { "AsIs", "IPIfNonMatch", "IPOnDemand" };
public static readonly List<string> DomainStrategies4Singbox = new() { "ipv4_only", "ipv6_only", "prefer_ipv4", "prefer_ipv6", "" };
public static readonly List<string> DomainMatchers = new() { "linear", "mph", "" };
public static readonly List<string> Fingerprints = new() { "chrome", "firefox", "safari", "ios", "android", "edge", "360", "qq", "random", "randomized", "" };
public static readonly List<string> UserAgent = new() { "chrome", "firefox", "safari", "edge", "none" };
public static readonly List<string> XhttpMode = new() { "auto", "packet-up", "stream-up", "stream-one" };
public static readonly List<string> AllowInsecure = new() { "true", "false", "" };
public static readonly List<string> DomainStrategy4Freedoms = new() { "AsIs", "UseIP", "UseIPv4", "UseIPv6", "" };
public static readonly List<string> SingboxDomainStrategy4Out = new() { "ipv4_only", "prefer_ipv4", "prefer_ipv6", "ipv6_only", "" };
public static readonly List<string> DomainDNSAddress = ["223.5.5.5", "223.6.6.6", "localhost"];
public static readonly List<string> SingboxDomainDNSAddress = ["223.5.5.5", "223.6.6.6", "dhcp://auto"];
public static readonly List<string> Languages = new() { "zh-Hans", "zh-Hant", "en", "fa-Ir", "ru" };
public static readonly List<string> Alpns = new() { "h3", "h2", "http/1.1", "h3,h2", "h2,http/1.1", "h3,h2,http/1.1", "" };
public static readonly List<string> LogLevels = new() { "debug", "info", "warning", "error", "none" };
public static readonly List<string> InboundTags = new() { "socks", "socks2" };
public static readonly List<string> RuleProtocols = new() { "http", "tls", "bittorrent" };
public static readonly List<string> RuleNetworks = new() { "", "tcp", "udp", "tcp,udp" };
public static readonly List<string> destOverrideProtocols = ["http", "tls", "quic", "fakedns", "fakedns+others"];
public static readonly List<string> TunMtus = new() { "1280", "1408", "1500", "9000" };
public static readonly List<string> TunStacks = new() { "gvisor", "system" };
public static readonly List<string> PresetMsgFilters = new() { "proxy", "direct", "block", "" };
public static readonly List<string> SingboxMuxs = new() { "h2mux", "smux", "yamux", "" };
public static readonly List<string> TuicCongestionControls = new() { "cubic", "new_reno", "bbr" };
public static readonly List<string> allowSelectType = new() { "selector", "urltest", "loadbalance", "fallback" };
public static readonly List<string> notAllowTestType = new() { "selector", "urltest", "direct", "reject", "compatible", "pass", "loadbalance", "fallback" };
public static readonly List<string> proxyVehicleType = new() { "file", "http" };
#endregion const
}
}

View File

@@ -0,0 +1,11 @@
global using ServiceLib.Base;
global using ServiceLib.Common;
global using ServiceLib.Enums;
global using ServiceLib.Handler;
global using ServiceLib.Handler.Fmt;
global using ServiceLib.Services;
global using ServiceLib.Services.Statistics;
global using ServiceLib.Services.CoreConfig;
global using ServiceLib.Models;
global using ServiceLib.Resx;
global using ServiceLib.Handler.SysProxy;

View File

@@ -0,0 +1,274 @@
namespace ServiceLib.Handler
{
public sealed class AppHandler
{
#region Property
private static readonly Lazy<AppHandler> _instance = new(() => new());
private Config _config;
private int? _statePort;
private int? _statePort2;
private Job? _processJob;
private bool? _isAdministrator;
public static AppHandler Instance => _instance.Value;
public Config Config => _config;
public int StatePort
{
get
{
_statePort ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api));
return _statePort.Value;
}
}
public int StatePort2
{
get
{
_statePort2 ??= Utils.GetFreePort(GetLocalPort(EInboundProtocol.api2));
return _statePort2.Value + (_config.TunModeItem.EnableTun ? 1 : 0);
}
}
public bool IsAdministrator
{
get
{
_isAdministrator ??= Utils.IsAdministrator();
return _isAdministrator.Value;
}
}
#endregion Property
#region Init
public bool InitApp()
{
Logging.Setup();
var config = ConfigHandler.LoadConfig();
if (config == null)
{
return false;
}
_config = config;
Thread.CurrentThread.CurrentUICulture = new(_config.UiItem.CurrentLanguage);
//Under Win10
if (Utils.IsWindows() && Environment.OSVersion.Version.Major < 10)
{
Environment.SetEnvironmentVariable("DOTNET_EnableWriteXorExecute", "0", EnvironmentVariableTarget.User);
}
SQLiteHelper.Instance.CreateTable<SubItem>();
SQLiteHelper.Instance.CreateTable<ProfileItem>();
SQLiteHelper.Instance.CreateTable<ServerStatItem>();
SQLiteHelper.Instance.CreateTable<RoutingItem>();
SQLiteHelper.Instance.CreateTable<ProfileExItem>();
SQLiteHelper.Instance.CreateTable<DNSItem>();
return true;
}
public bool InitComponents()
{
Logging.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}");
Logging.SaveLog($"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
Logging.LoggingEnabled(_config.GuiItem.EnableLog);
Logging.ClearLogs();
return true;
}
public bool Reset()
{
_statePort = null;
_statePort2 = null;
return true;
}
#endregion Init
#region Config
public int GetLocalPort(EInboundProtocol protocol)
{
var localPort = _config.Inbound.FirstOrDefault(t => t.Protocol == nameof(EInboundProtocol.socks))?.LocalPort ?? 10808;
return localPort + (int)protocol;
}
public void AddProcess(IntPtr processHandle)
{
if (Utils.IsWindows())
{
_processJob ??= new();
_processJob?.AddProcess(processHandle);
}
}
#endregion Config
#region SqliteHelper
public async Task<List<SubItem>?> SubItems()
{
return await SQLiteHelper.Instance.TableAsync<SubItem>().OrderBy(t => t.Sort).ToListAsync();
}
public async Task<SubItem?> GetSubItem(string subid)
{
return await SQLiteHelper.Instance.TableAsync<SubItem>().FirstOrDefaultAsync(t => t.Id == subid);
}
public async Task<List<ProfileItem>?> ProfileItems(string subid)
{
if (Utils.IsNullOrEmpty(subid))
{
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().ToListAsync();
}
else
{
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().Where(t => t.Subid == subid).ToListAsync();
}
}
public async Task<List<string>?> ProfileItemIndexes(string subid)
{
return (await ProfileItems(subid))?.Select(t => t.IndexId)?.ToList();
}
public async Task<List<ProfileItemModel>?> ProfileItems(string subid, string filter)
{
var sql = @$"select a.*
,b.remarks subRemarks
from ProfileItem a
left join SubItem b on a.subid = b.id
where 1=1 ";
if (Utils.IsNotEmpty(subid))
{
sql += $" and a.subid = '{subid}'";
}
if (Utils.IsNotEmpty(filter))
{
if (filter.Contains('\''))
{
filter = filter.Replace("'", "");
}
sql += string.Format(" and (a.remarks like '%{0}%' or a.address like '%{0}%') ", filter);
}
return await SQLiteHelper.Instance.QueryAsync<ProfileItemModel>(sql);
}
public async Task<List<ProfileItemModel>?> ProfileItemsEx(string subid, string filter)
{
var lstModel = await ProfileItems(_config.SubIndexId, filter);
await ConfigHandler.SetDefaultServer(_config, lstModel);
var lstServerStat = (_config.GuiItem.EnableStatistics ? StatisticsHandler.Instance.ServerStat : null) ?? [];
var lstProfileExs = await ProfileExHandler.Instance.GetProfileExs();
lstModel = (from t in lstModel
join t2 in lstServerStat on t.IndexId equals t2.IndexId into t2b
from t22 in t2b.DefaultIfEmpty()
join t3 in lstProfileExs on t.IndexId equals t3.IndexId into t3b
from t33 in t3b.DefaultIfEmpty()
select new ProfileItemModel
{
IndexId = t.IndexId,
ConfigType = t.ConfigType,
Remarks = t.Remarks,
Address = t.Address,
Port = t.Port,
Security = t.Security,
Network = t.Network,
StreamSecurity = t.StreamSecurity,
Subid = t.Subid,
SubRemarks = t.SubRemarks,
IsActive = t.IndexId == _config.IndexId,
Sort = t33 == null ? 0 : t33.Sort,
Delay = t33 == null ? 0 : t33.Delay,
DelayVal = t33?.Delay != 0 ? $"{t33?.Delay} {Global.DelayUnit}" : string.Empty,
SpeedVal = t33?.Speed != 0 ? $"{t33?.Speed} {Global.SpeedUnit}" : string.Empty,
TodayDown = t22 == null ? "" : Utils.HumanFy(t22.TodayDown),
TodayUp = t22 == null ? "" : Utils.HumanFy(t22.TodayUp),
TotalDown = t22 == null ? "" : Utils.HumanFy(t22.TotalDown),
TotalUp = t22 == null ? "" : Utils.HumanFy(t22.TotalUp)
}).OrderBy(t => t.Sort).ToList();
return lstModel;
}
public async Task<ProfileItem?> GetProfileItem(string indexId)
{
if (Utils.IsNullOrEmpty(indexId))
{
return null;
}
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.IndexId == indexId);
}
public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks)
{
if (Utils.IsNullOrEmpty(remarks))
{
return null;
}
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().FirstOrDefaultAsync(it => it.Remarks == remarks);
}
public async Task<List<RoutingItem>?> RoutingItems()
{
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().OrderBy(t => t.Sort).ToListAsync();
}
public async Task<RoutingItem?> GetRoutingItem(string id)
{
return await SQLiteHelper.Instance.TableAsync<RoutingItem>().FirstOrDefaultAsync(it => it.Id == id);
}
public async Task<List<DNSItem>?> DNSItems()
{
return await SQLiteHelper.Instance.TableAsync<DNSItem>().ToListAsync();
}
public async Task<DNSItem?> GetDNSItem(ECoreType eCoreType)
{
return await SQLiteHelper.Instance.TableAsync<DNSItem>().FirstOrDefaultAsync(it => it.CoreType == eCoreType);
}
#endregion SqliteHelper
#region Core Type
public List<string> GetShadowsocksSecurities(ProfileItem profileItem)
{
var coreType = GetCoreType(profileItem, EConfigType.Shadowsocks);
switch (coreType)
{
case ECoreType.v2fly:
return Global.SsSecurities;
case ECoreType.Xray:
return Global.SsSecuritiesInXray;
case ECoreType.sing_box:
return Global.SsSecuritiesInSingbox;
}
return Global.SsSecuritiesInSingbox;
}
public ECoreType GetCoreType(ProfileItem profileItem, EConfigType eConfigType)
{
if (profileItem?.CoreType != null)
{
return (ECoreType)profileItem.CoreType;
}
var item = _config.CoreTypeItem?.FirstOrDefault(it => it.ConfigType == eConfigType);
return item?.CoreType ?? ECoreType.Xray;
}
#endregion Core Type
}
}

View File

@@ -0,0 +1,159 @@
using System.Security.Principal;
using System.Text.RegularExpressions;
namespace ServiceLib.Handler
{
public static class AutoStartupHandler
{
public static async Task<bool> UpdateTask(Config config)
{
if (Utils.IsWindows())
{
await ClearTaskWindows();
if (config.GuiItem.AutoRun)
{
await SetTaskWindows();
}
}
else if (Utils.IsLinux())
{
await ClearTaskLinux();
if (config.GuiItem.AutoRun)
{
await SetTaskLinux();
}
}
else if (Utils.IsOSX())
{
//TODO
}
return true;
}
#region Windows
private static async Task ClearTaskWindows()
{
var autoRunName = GetAutoRunNameWindows();
WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, "");
if (Utils.IsAdministrator())
{
AutoStartTaskService(autoRunName, "", "");
}
}
private static async Task SetTaskWindows()
{
try
{
var autoRunName = GetAutoRunNameWindows();
var exePath = Utils.GetExePath();
if (Utils.IsAdministrator())
{
AutoStartTaskService(autoRunName, exePath, "");
}
else
{
WindowsUtils.RegWriteValue(Global.AutoRunRegPath, autoRunName, exePath.AppendQuotes());
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
/// <summary>
/// Auto Start via TaskService
/// </summary>
/// <param name="taskName"></param>
/// <param name="fileName"></param>
/// <param name="description"></param>
/// <exception cref="ArgumentNullException"></exception>
public static void AutoStartTaskService(string taskName, string fileName, string description)
{
if (Utils.IsNullOrEmpty(taskName))
{
return;
}
var logonUser = WindowsIdentity.GetCurrent().Name;
using var taskService = new Microsoft.Win32.TaskScheduler.TaskService();
var tasks = taskService.RootFolder.GetTasks(new Regex(taskName));
if (Utils.IsNullOrEmpty(fileName))
{
foreach (var t in tasks)
{
taskService.RootFolder.DeleteTask(t.Name);
}
return;
}
var task = taskService.NewTask();
task.RegistrationInfo.Description = description;
task.Settings.DisallowStartIfOnBatteries = false;
task.Settings.StopIfGoingOnBatteries = false;
task.Settings.RunOnlyIfIdle = false;
task.Settings.IdleSettings.StopOnIdleEnd = false;
task.Settings.ExecutionTimeLimit = TimeSpan.Zero;
task.Triggers.Add(new Microsoft.Win32.TaskScheduler.LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(10) });
task.Principal.RunLevel = Microsoft.Win32.TaskScheduler.TaskRunLevel.Highest;
task.Actions.Add(new Microsoft.Win32.TaskScheduler.ExecAction(fileName.AppendQuotes(), null, Path.GetDirectoryName(fileName)));
taskService.RootFolder.RegisterTaskDefinition(taskName, task);
}
private static string GetAutoRunNameWindows()
{
return $"{Global.AutoRunName}_{Utils.GetMd5(Utils.StartupPath())}";
}
#endregion Windows
#region Linux
private static async Task ClearTaskLinux()
{
try
{
File.Delete(GetHomePathLinux());
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
private static async Task SetTaskLinux()
{
try
{
var linuxConfig = Utils.GetEmbedText(Global.LinuxAutostartConfig);
if (linuxConfig.IsNotEmpty())
{
linuxConfig = linuxConfig.Replace("$ExecPath$", Utils.GetExePath());
Logging.SaveLog(linuxConfig);
var homePath = GetHomePathLinux();
await File.WriteAllTextAsync(homePath, linuxConfig);
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
private static string GetHomePathLinux()
{
var homePath = Path.Combine(Utils.GetHomePath(), ".config", "autostart", $"{Global.AppName}.desktop");
Directory.CreateDirectory(Path.GetDirectoryName(homePath));
return homePath;
}
#endregion Linux
}
}

View File

@@ -0,0 +1,196 @@
using static ServiceLib.Models.ClashProxies;
namespace ServiceLib.Handler
{
public sealed class ClashApiHandler
{
private static readonly Lazy<ClashApiHandler> instance = new(() => new());
public static ClashApiHandler Instance => instance.Value;
private Dictionary<string, ProxiesItem>? _proxies;
public Dictionary<string, object> ProfileContent { get; set; }
public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync(Config config)
{
for (var i = 0; i < 5; i++)
{
var url = $"{GetApiUrl()}/proxies";
var result = await HttpClientHelper.Instance.TryGetAsync(url);
var clashProxies = JsonUtils.Deserialize<ClashProxies>(result);
var url2 = $"{GetApiUrl()}/providers/proxies";
var result2 = await HttpClientHelper.Instance.TryGetAsync(url2);
var clashProviders = JsonUtils.Deserialize<ClashProviders>(result2);
if (clashProxies != null || clashProviders != null)
{
_proxies = clashProxies?.proxies;
return new Tuple<ClashProxies, ClashProviders>(clashProxies, clashProviders);
}
await Task.Delay(2000);
}
return null;
}
public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> updateFunc)
{
Task.Run(() =>
{
if (blAll)
{
for (int i = 0; i < 5; i++)
{
if (_proxies != null)
{
break;
}
Task.Delay(5000).Wait();
}
if (_proxies == null)
{
return;
}
lstProxy = new List<ClashProxyModel>();
foreach (KeyValuePair<string, ProxiesItem> kv in _proxies)
{
if (Global.notAllowTestType.Contains(kv.Value.type.ToLower()))
{
continue;
}
lstProxy.Add(new ClashProxyModel()
{
Name = kv.Value.name,
Type = kv.Value.type.ToLower(),
});
}
}
if (lstProxy == null)
{
return;
}
var urlBase = $"{GetApiUrl()}/proxies";
urlBase += @"/{0}/delay?timeout=10000&url=" + AppHandler.Instance.Config.SpeedTestItem.SpeedPingTestUrl;
List<Task> tasks = new List<Task>();
foreach (var it in lstProxy)
{
if (Global.notAllowTestType.Contains(it.Type.ToLower()))
{
continue;
}
var name = it.Name;
var url = string.Format(urlBase, name);
tasks.Add(Task.Run(async () =>
{
var result = await HttpClientHelper.Instance.TryGetAsync(url);
updateFunc?.Invoke(it, result);
}));
}
Task.WaitAll(tasks.ToArray());
Task.Delay(1000).Wait();
updateFunc?.Invoke(null, "");
});
}
public List<ProxiesItem>? GetClashProxyGroups()
{
try
{
var fileContent = ProfileContent;
if (fileContent is null || fileContent?.ContainsKey("proxy-groups") == false)
{
return null;
}
return JsonUtils.Deserialize<List<ProxiesItem>>(JsonUtils.Serialize(fileContent["proxy-groups"]));
}
catch (Exception ex)
{
Logging.SaveLog("GetClashProxyGroups", ex);
return null;
}
}
public async Task ClashSetActiveProxy(string name, string nameNode)
{
try
{
var url = $"{GetApiUrl()}/proxies/{name}";
Dictionary<string, string> headers = new Dictionary<string, string>();
headers.Add("name", nameNode);
await HttpClientHelper.Instance.PutAsync(url, headers);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public async Task ClashConfigUpdate(Dictionary<string, string> headers)
{
if (_proxies == null)
{
return;
}
var urlBase = $"{GetApiUrl()}/configs";
await HttpClientHelper.Instance.PatchAsync(urlBase, headers);
}
public async Task ClashConfigReload(string filePath)
{
await ClashConnectionClose("");
try
{
var url = $"{GetApiUrl()}/configs?force=true";
Dictionary<string, string> headers = new Dictionary<string, string>();
headers.Add("path", filePath);
await HttpClientHelper.Instance.PutAsync(url, headers);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public async Task<ClashConnections?> GetClashConnectionsAsync(Config config)
{
try
{
var url = $"{GetApiUrl()}/connections";
var result = await HttpClientHelper.Instance.TryGetAsync(url);
var clashConnections = JsonUtils.Deserialize<ClashConnections>(result);
return clashConnections;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return null;
}
public async Task ClashConnectionClose(string id)
{
try
{
var url = $"{GetApiUrl()}/connections/{id}";
await HttpClientHelper.Instance.DeleteAsync(url);
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
private string GetApiUrl()
{
return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}";
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,130 @@
namespace ServiceLib.Handler
{
/// <summary>
/// Core configuration file processing class
/// </summary>
public class CoreConfigHandler
{
public static async Task<RetResult> GenerateClientConfig(ProfileItem node, string? fileName)
{
var config = AppHandler.Instance.Config;
var result = new RetResult();
if (node.ConfigType == EConfigType.Custom)
{
result = node.CoreType switch
{
ECoreType.mihomo => await new CoreConfigClashService(config).GenerateClientCustomConfig(node, fileName),
ECoreType.sing_box => await new CoreConfigSingboxService(config).GenerateClientCustomConfig(node, fileName),
_ => await GenerateClientCustomConfig(node, fileName)
};
}
else if (AppHandler.Instance.GetCoreType(node, node.ConfigType) == ECoreType.sing_box)
{
result = await new CoreConfigSingboxService(config).GenerateClientConfigContent(node);
}
else
{
result = await new CoreConfigV2rayService(config).GenerateClientConfigContent(node);
}
if (result.Success != true)
{
return result;
}
if (Utils.IsNotEmpty(fileName) && result.Data != null)
{
await File.WriteAllTextAsync(fileName, result.Data.ToString());
}
return result;
}
private static async Task<RetResult> GenerateClientCustomConfig(ProfileItem node, string? fileName)
{
var ret = new RetResult();
try
{
if (node == null || fileName is null)
{
ret.Msg = ResUI.CheckServerSettings;
return ret;
}
if (File.Exists(fileName))
{
File.SetAttributes(fileName, FileAttributes.Normal); //If the file has a read-only attribute, direct deletion will fail
File.Delete(fileName);
}
string addressFileName = node.Address;
if (!File.Exists(addressFileName))
{
addressFileName = Utils.GetConfigPath(addressFileName);
}
if (!File.Exists(addressFileName))
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
File.Copy(addressFileName, fileName);
File.SetAttributes(fileName, FileAttributes.Normal); //Copy will keep the attributes of addressFileName, so we need to add write permissions to fileName just in case of addressFileName is a read-only file.
//check again
if (!File.Exists(fileName))
{
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
ret.Msg = string.Format(ResUI.SuccessfulConfiguration, "");
ret.Success = true;
return ret;
}
catch (Exception ex)
{
Logging.SaveLog("GenerateClientCustomConfig", ex);
ret.Msg = ResUI.FailedGenDefaultConfiguration;
return ret;
}
}
public static async Task<RetResult> GenerateClientSpeedtestConfig(Config config, string fileName, List<ServerTestItem> selecteds, ECoreType coreType)
{
var result = new RetResult();
if (coreType == ECoreType.sing_box)
{
result = await new CoreConfigSingboxService(config).GenerateClientSpeedtestConfig(selecteds);
}
else if (coreType == ECoreType.Xray)
{
result = await new CoreConfigV2rayService(config).GenerateClientSpeedtestConfig(selecteds);
}
if (result.Success != true)
{
return result;
}
await File.WriteAllTextAsync(fileName, result.Data.ToString());
return result;
}
public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType)
{
var result = new RetResult();
if (coreType == ECoreType.sing_box)
{
result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds);
}
else if (coreType == ECoreType.Xray)
{
result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds);
}
if (result.Success != true)
{
return result;
}
await File.WriteAllTextAsync(fileName, result.Data.ToString());
return result;
}
}
}

View File

@@ -0,0 +1,424 @@
using System.Diagnostics;
using System.Text;
namespace ServiceLib.Handler
{
/// <summary>
/// Core process processing class
/// </summary>
public class CoreHandler
{
private static readonly Lazy<CoreHandler> _instance = new(() => new());
public static CoreHandler Instance => _instance.Value;
private Config _config;
private Process? _process;
private Process? _processPre;
private int _linuxSudoPid = -1;
private Action<bool, string>? _updateFunc;
public async Task Init(Config config, Action<bool, string> updateFunc)
{
_config = config;
_updateFunc = updateFunc;
Environment.SetEnvironmentVariable("V2RAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable("XRAY_LOCATION_ASSET", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
if (Utils.IsLinux() || Utils.IsOSX())
{
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo();
foreach (var it in coreInfo)
{
if (it.CoreType == ECoreType.v2rayN)
{
if (Utils.UpgradeAppExists(out var fileName))
{
await Utils.SetLinuxChmod(fileName);
}
continue;
}
foreach (var name in it.CoreExes)
{
var exe = Utils.GetBinPath(Utils.GetExeName(name), it.CoreType.ToString());
if (File.Exists(exe))
{
await Utils.SetLinuxChmod(exe);
}
}
}
}
}
public async Task LoadCore(ProfileItem? node)
{
if (node == null)
{
ShowMsg(false, ResUI.CheckServerSettings);
return;
}
var fileName = Utils.GetConfigPath(Global.CoreConfigFileName);
var result = await CoreConfigHandler.GenerateClientConfig(node, fileName);
if (result.Success != true)
{
ShowMsg(true, result.Msg);
return;
}
ShowMsg(true, $"{node.GetSummary()}");
ShowMsg(false, $"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
await CoreStop();
await Task.Delay(100);
await CoreStart(node);
await CoreStartPreService(node);
}
public async Task<int> LoadCoreConfigSpeedtest(List<ServerTestItem> selecteds)
{
var coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC or EConfigType.WireGuard) ? ECoreType.sing_box : ECoreType.Xray;
var configPath = Utils.GetConfigPath(Global.CoreSpeedtestConfigFileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
ShowMsg(false, result.Msg);
if (result.Success != true)
{
return -1;
}
ShowMsg(false, string.Format(ResUI.StartService, DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss")));
ShowMsg(false, configPath);
return await CoreStartSpeedtest(configPath, coreType);
}
public async Task CoreStop()
{
try
{
if (_process != null)
{
_process = await KillProcess(_process);
}
if (_processPre != null)
{
_processPre = await KillProcess(_processPre);
}
if (_linuxSudoPid > 0)
{
await KillProcessAsLinuxSudo();
}
_linuxSudoPid = -1;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public async Task CoreStopPid(int pid)
{
try
{
await KillProcess(Process.GetProcessById(pid));
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
#region Private
private string CoreFindExe(CoreInfo coreInfo)
{
var fileName = string.Empty;
foreach (var name in coreInfo.CoreExes)
{
var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
if (File.Exists(vName))
{
fileName = vName;
break;
}
}
if (Utils.IsNullOrEmpty(fileName))
{
var msg = string.Format(ResUI.NotFoundCore, Utils.GetBinPath("", coreInfo.CoreType.ToString()), string.Join(", ", coreInfo.CoreExes.ToArray()), coreInfo.Url);
Logging.SaveLog(msg);
ShowMsg(false, msg);
}
return fileName;
}
private async Task CoreStart(ProfileItem node)
{
var coreType = _config.RunningCoreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var displayLog = node.ConfigType != EConfigType.Custom || node.DisplayLog;
var proc = await RunProcess(coreInfo, Global.CoreConfigFileName, displayLog, true);
if (proc is null)
{
return;
}
_process = proc;
}
private async Task CoreStartPreService(ProfileItem node)
{
if (_process != null && !_process.HasExited)
{
var coreType = AppHandler.Instance.GetCoreType(node, node.ConfigType);
var itemSocks = await ConfigHandler.GetPreSocksItem(_config, node, coreType);
if (itemSocks != null)
{
var preCoreType = itemSocks.CoreType ?? ECoreType.sing_box;
var fileName = Utils.GetConfigPath(Global.CorePreConfigFileName);
var result = await CoreConfigHandler.GenerateClientConfig(itemSocks, fileName);
if (result.Success)
{
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(preCoreType);
var proc = await RunProcess(coreInfo, Global.CorePreConfigFileName, true, true);
if (proc is null)
{
return;
}
_processPre = proc;
}
}
}
}
private async Task<int> CoreStartSpeedtest(string configPath, ECoreType coreType)
{
try
{
var coreInfo = CoreInfoHandler.Instance.GetCoreInfo(coreType);
var proc = await RunProcess(coreInfo, Global.CoreSpeedtestConfigFileName, true, false);
if (proc is null)
{
return -1;
}
return proc.Id;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
ShowMsg(false, ex.Message);
return -1;
}
}
private void ShowMsg(bool notify, string msg)
{
_updateFunc?.Invoke(notify, msg);
}
private bool IsNeedSudo(ECoreType eCoreType)
{
return _config.TunModeItem.EnableTun
&& eCoreType == ECoreType.sing_box
&& (Utils.IsLinux() || Utils.IsOSX())
//&& _config.TunModeItem.LinuxSudoPwd.IsNotEmpty()
;
}
#endregion Private
#region Process
private async Task<Process?> RunProcess(CoreInfo coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
{
var fileName = CoreFindExe(coreInfo);
if (Utils.IsNullOrEmpty(fileName))
{
return null;
}
try
{
Process proc = new()
{
StartInfo = new()
{
FileName = fileName,
Arguments = string.Format(coreInfo.Arguments, configPath),
WorkingDirectory = Utils.GetConfigPath(),
UseShellExecute = false,
RedirectStandardOutput = displayLog,
RedirectStandardError = displayLog,
CreateNoWindow = true,
StandardOutputEncoding = displayLog ? Encoding.UTF8 : null,
StandardErrorEncoding = displayLog ? Encoding.UTF8 : null,
}
};
var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType);
if (isNeedSudo)
{
await RunProcessAsLinuxSudo(proc, fileName, coreInfo, configPath);
}
var startUpErrorMessage = new StringBuilder();
var startUpSuccessful = false;
if (displayLog)
{
proc.OutputDataReceived += (sender, e) =>
{
if (Utils.IsNullOrEmpty(e.Data)) return;
ShowMsg(false, e.Data + Environment.NewLine);
};
proc.ErrorDataReceived += (sender, e) =>
{
if (Utils.IsNullOrEmpty(e.Data)) return;
ShowMsg(false, e.Data + Environment.NewLine);
if (!startUpSuccessful)
{
startUpErrorMessage.Append(e.Data + Environment.NewLine);
}
};
}
proc.Start();
if (isNeedSudo && _config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
}
if (isNeedSudo) _linuxSudoPid = proc.Id;
if (displayLog)
{
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
}
if (proc.WaitForExit(1000))
{
proc.CancelErrorRead();
throw new Exception(displayLog ? startUpErrorMessage.ToString() : "启动进程失败并退出 (Failed to start the process and exited)");
}
else
{
startUpSuccessful = true;
}
AppHandler.Instance.AddProcess(proc.Handle);
return proc;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
ShowMsg(true, ex.Message);
return null;
}
}
private async Task<Process?> KillProcess(Process? proc)
{
if (proc is null)
{
return null;
}
try { proc?.Kill(true); } catch { }
try { proc?.Close(); } catch { }
try { proc?.Dispose(); } catch { }
await Task.Delay(100);
return null;
}
#endregion Process
#region Linux
private async Task RunProcessAsLinuxSudo(Process proc, string fileName, CoreInfo coreInfo, string configPath)
{
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetConfigPath(configPath).AppendQuotes())}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
proc.StartInfo.FileName = shFilePath;
proc.StartInfo.Arguments = "";
proc.StartInfo.WorkingDirectory = "";
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{
proc.StartInfo.StandardInputEncoding = Encoding.UTF8;
proc.StartInfo.RedirectStandardInput = true;
}
}
private async Task KillProcessAsLinuxSudo()
{
var cmdLine = $"kill {_linuxSudoPid}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
Process proc = new()
{
StartInfo = new()
{
FileName = shFilePath,
UseShellExecute = false,
CreateNoWindow = true,
StandardInputEncoding = Encoding.UTF8,
RedirectStandardInput = true
}
};
proc.Start();
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{
try
{
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
}
catch (Exception)
{
// ignored
}
}
var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
await proc.WaitForExitAsync(timeout.Token);
await Task.Delay(3000);
}
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
{
//Shell scripts
var shFilePath = Utils.GetBinPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName);
File.Delete(shFilePath);
var sb = new StringBuilder();
sb.AppendLine("#!/bin/sh");
if (AppHandler.Instance.IsAdministrator)
{
sb.AppendLine($"{cmdLine}");
}
else if (_config.TunModeItem.LinuxSudoPwd.IsNullOrEmpty())
{
sb.AppendLine($"pkexec {cmdLine}");
}
else
{
sb.AppendLine($"sudo -S {cmdLine}");
}
await File.WriteAllTextAsync(shFilePath, sb.ToString());
await Utils.SetLinuxChmod(shFilePath);
Logging.SaveLog(shFilePath);
return shFilePath;
}
#endregion Linux
}
}

View File

@@ -0,0 +1,179 @@
namespace ServiceLib.Handler
{
public sealed class CoreInfoHandler
{
private static readonly Lazy<CoreInfoHandler> _instance = new(() => new());
private List<CoreInfo>? _coreInfo;
public static CoreInfoHandler Instance => _instance.Value;
public CoreInfoHandler()
{
InitCoreInfo();
}
public CoreInfo? GetCoreInfo(ECoreType coreType)
{
if (_coreInfo == null)
{
InitCoreInfo();
}
return _coreInfo?.FirstOrDefault(t => t.CoreType == coreType);
}
public List<CoreInfo> GetCoreInfo()
{
if (_coreInfo == null)
{
InitCoreInfo();
}
return _coreInfo ?? [];
}
private void InitCoreInfo()
{
_coreInfo = [];
_coreInfo.Add(new CoreInfo
{
CoreType = ECoreType.v2rayN,
Url = Global.NUrl,
ReleaseApiUrl = Global.NUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
DownloadUrlWin64 = Global.NUrl + "/download/{0}/v2rayN-windows-64.zip",
DownloadUrlWinArm64 = Global.NUrl + "/download/{0}/v2rayN-windows-arm64.zip",
DownloadUrlLinux64 = Global.NUrl + "/download/{0}/v2rayN-linux-64.zip",
DownloadUrlLinuxArm64 = Global.NUrl + "/download/{0}/v2rayN-linux-arm64.zip",
DownloadUrlOSX64 = Global.NUrl + "/download/{0}/v2rayN-macos-64.zip",
DownloadUrlOSXArm64 = Global.NUrl + "/download/{0}/v2rayN-macos-arm64.zip",
});
_coreInfo.Add(new CoreInfo
{
CoreType = ECoreType.v2fly,
CoreExes = new List<string> { "wv2ray", "v2ray" },
Arguments = "",
Url = Global.V2flyCoreUrl,
ReleaseApiUrl = Global.V2flyCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
Match = "V2Ray",
VersionArg = "-version",
RedirectInfo = true,
});
_coreInfo.Add(new CoreInfo
{
CoreType = ECoreType.v2fly_v5,
CoreExes = new List<string> { "v2ray" },
Arguments = "run -c config.json -format jsonv5",
Url = Global.V2flyCoreUrl,
ReleaseApiUrl = Global.V2flyCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
Match = "V2Ray",
VersionArg = "version",
RedirectInfo = true,
});
_coreInfo.Add(new CoreInfo
{
CoreType = ECoreType.Xray,
CoreExes = new List<string> { "xray", "wxray" },
Arguments = "run -c {0}",
Url = Global.XrayCoreUrl,
ReleaseApiUrl = Global.XrayCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
DownloadUrlWin64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-64.zip",
DownloadUrlWinArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-windows-arm64-v8a.zip",
DownloadUrlLinux64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-64.zip",
DownloadUrlLinuxArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-linux-arm64-v8a.zip",
DownloadUrlOSX64 = Global.XrayCoreUrl + "/download/{0}/Xray-macos-64.zip",
DownloadUrlOSXArm64 = Global.XrayCoreUrl + "/download/{0}/Xray-macos-arm64-v8a.zip",
Match = "Xray",
VersionArg = "-version",
RedirectInfo = true,
});
_coreInfo.Add(new CoreInfo
{
CoreType = ECoreType.mihomo,
CoreExes = new List<string> { "mihomo-windows-amd64-compatible", "mihomo-windows-amd64", "mihomo-linux-amd64", "mihomo", "clash" },
Arguments = "-f config.json" + PortableMode(),
Url = Global.MihomoCoreUrl,
ReleaseApiUrl = Global.MihomoCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
DownloadUrlWin64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-windows-amd64-compatible-{0}.zip",
DownloadUrlWinArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-windows-arm64-{0}.zip",
DownloadUrlLinux64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-amd64-compatible-{0}.gz",
DownloadUrlLinuxArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-linux-arm64-{0}.gz",
DownloadUrlOSX64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-darwin-amd64-compatible-{0}.gz",
DownloadUrlOSXArm64 = Global.MihomoCoreUrl + "/download/{0}/mihomo-darwin-arm64-{0}.gz",
Match = "Mihomo",
VersionArg = "-v",
RedirectInfo = true,
});
_coreInfo.Add(new CoreInfo
{
CoreType = ECoreType.hysteria,
CoreExes = new List<string> { "hysteria-windows-amd64", "hysteria" },
Arguments = "",
Url = Global.HysteriaCoreUrl,
ReleaseApiUrl = Global.HysteriaCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
RedirectInfo = true,
});
_coreInfo.Add(new CoreInfo
{
CoreType = ECoreType.naiveproxy,
CoreExes = new List<string> { "naiveproxy", "naive" },
Arguments = "config.json",
Url = Global.NaiveproxyCoreUrl,
RedirectInfo = false,
});
_coreInfo.Add(new CoreInfo
{
CoreType = ECoreType.tuic,
CoreExes = new List<string> { "tuic-client", "tuic" },
Arguments = "-c config.json",
Url = Global.TuicCoreUrl,
RedirectInfo = true,
});
_coreInfo.Add(new CoreInfo
{
CoreType = ECoreType.sing_box,
CoreExes = new List<string> { "sing-box-client", "sing-box" },
Arguments = "run -c {0} --disable-color",
Url = Global.SingboxCoreUrl,
RedirectInfo = true,
ReleaseApiUrl = Global.SingboxCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
DownloadUrlWin64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-amd64.zip",
DownloadUrlWinArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-windows-arm64.zip",
DownloadUrlLinux64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-amd64.tar.gz",
DownloadUrlLinuxArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-linux-arm64.tar.gz",
DownloadUrlOSX64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-darwin-amd64.tar.gz",
DownloadUrlOSXArm64 = Global.SingboxCoreUrl + "/download/{0}/sing-box-{1}-darwin-arm64.tar.gz",
Match = "sing-box",
VersionArg = "version",
});
_coreInfo.Add(new CoreInfo
{
CoreType = ECoreType.juicity,
CoreExes = new List<string> { "juicity-client", "juicity" },
Arguments = "run -c config.json",
Url = Global.JuicityCoreUrl
});
_coreInfo.Add(new CoreInfo
{
CoreType = ECoreType.hysteria2,
CoreExes = new List<string> { "hysteria-windows-amd64", "hysteria-linux-amd64", "hysteria" },
Arguments = "",
Url = Global.HysteriaCoreUrl,
ReleaseApiUrl = Global.HysteriaCoreUrl.Replace(Global.GithubUrl, Global.GithubApiUrl),
RedirectInfo = true,
});
}
private string PortableMode()
{
return $" -d {Utils.GetBinPath("").AppendQuotes()}";
}
}
}

View File

@@ -0,0 +1,239 @@
using System.Collections.Specialized;
namespace ServiceLib.Handler.Fmt
{
public class BaseFmt
{
protected static string GetIpv6(string address)
{
if (Utils.IsIpv6(address))
{
// 检查地址是否已经被方括号包围,如果没有,则添加方括号
return address.StartsWith('[') && address.EndsWith(']') ? address : $"[{address}]";
}
return address; // 如果不是IPv6地址直接返回原地址
}
protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
{
if (Utils.IsNotEmpty(item.Flow))
{
dicQuery.Add("flow", item.Flow);
}
if (Utils.IsNotEmpty(item.StreamSecurity))
{
dicQuery.Add("security", item.StreamSecurity);
}
else
{
if (securityDef != null)
{
dicQuery.Add("security", securityDef);
}
}
if (Utils.IsNotEmpty(item.Sni))
{
dicQuery.Add("sni", item.Sni);
}
if (Utils.IsNotEmpty(item.Alpn))
{
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
}
if (Utils.IsNotEmpty(item.Fingerprint))
{
dicQuery.Add("fp", Utils.UrlEncode(item.Fingerprint));
}
if (Utils.IsNotEmpty(item.PublicKey))
{
dicQuery.Add("pbk", Utils.UrlEncode(item.PublicKey));
}
if (Utils.IsNotEmpty(item.ShortId))
{
dicQuery.Add("sid", Utils.UrlEncode(item.ShortId));
}
if (Utils.IsNotEmpty(item.SpiderX))
{
dicQuery.Add("spx", Utils.UrlEncode(item.SpiderX));
}
if (item.AllowInsecure.Equals("true"))
{
dicQuery.Add("allowInsecure", "1");
}
dicQuery.Add("type", Utils.IsNotEmpty(item.Network) ? item.Network : nameof(ETransport.tcp));
switch (item.Network)
{
case nameof(ETransport.tcp):
dicQuery.Add("headerType", Utils.IsNotEmpty(item.HeaderType) ? item.HeaderType : Global.None);
if (Utils.IsNotEmpty(item.RequestHost))
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
}
break;
case nameof(ETransport.kcp):
dicQuery.Add("headerType", Utils.IsNotEmpty(item.HeaderType) ? item.HeaderType : Global.None);
if (Utils.IsNotEmpty(item.Path))
{
dicQuery.Add("seed", Utils.UrlEncode(item.Path));
}
break;
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
if (Utils.IsNotEmpty(item.RequestHost))
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
}
if (Utils.IsNotEmpty(item.Path))
{
dicQuery.Add("path", Utils.UrlEncode(item.Path));
}
break;
case nameof(ETransport.xhttp):
if (Utils.IsNotEmpty(item.RequestHost))
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
}
if (Utils.IsNotEmpty(item.Path))
{
dicQuery.Add("path", Utils.UrlEncode(item.Path));
}
if (Utils.IsNotEmpty(item.HeaderType) && Global.XhttpMode.Contains(item.HeaderType))
{
dicQuery.Add("mode", Utils.UrlEncode(item.HeaderType));
}
if (Utils.IsNotEmpty(item.Extra))
{
dicQuery.Add("extra", Utils.UrlEncode(item.Extra));
}
break;
case nameof(ETransport.http):
case nameof(ETransport.h2):
dicQuery["type"] = nameof(ETransport.http);
if (Utils.IsNotEmpty(item.RequestHost))
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
}
if (Utils.IsNotEmpty(item.Path))
{
dicQuery.Add("path", Utils.UrlEncode(item.Path));
}
break;
case nameof(ETransport.quic):
dicQuery.Add("headerType", Utils.IsNotEmpty(item.HeaderType) ? item.HeaderType : Global.None);
dicQuery.Add("quicSecurity", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("key", Utils.UrlEncode(item.Path));
break;
case nameof(ETransport.grpc):
if (Utils.IsNotEmpty(item.Path))
{
dicQuery.Add("authority", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("serviceName", Utils.UrlEncode(item.Path));
if (item.HeaderType is Global.GrpcGunMode or Global.GrpcMultiMode)
{
dicQuery.Add("mode", Utils.UrlEncode(item.HeaderType));
}
}
break;
}
return 0;
}
protected static int ResolveStdTransport(NameValueCollection query, ref ProfileItem item)
{
item.Flow = query["flow"] ?? "";
item.StreamSecurity = query["security"] ?? "";
item.Sni = query["sni"] ?? "";
item.Alpn = Utils.UrlDecode(query["alpn"] ?? "");
item.Fingerprint = Utils.UrlDecode(query["fp"] ?? "");
item.PublicKey = Utils.UrlDecode(query["pbk"] ?? "");
item.ShortId = Utils.UrlDecode(query["sid"] ?? "");
item.SpiderX = Utils.UrlDecode(query["spx"] ?? "");
item.AllowInsecure = (query["allowInsecure"] ?? "") == "1" ? "true" : "";
item.Network = query["type"] ?? nameof(ETransport.tcp);
switch (item.Network)
{
case nameof(ETransport.tcp):
item.HeaderType = query["headerType"] ?? Global.None;
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
break;
case nameof(ETransport.kcp):
item.HeaderType = query["headerType"] ?? Global.None;
item.Path = Utils.UrlDecode(query["seed"] ?? "");
break;
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
item.Path = Utils.UrlDecode(query["path"] ?? "/");
break;
case nameof(ETransport.xhttp):
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
item.Path = Utils.UrlDecode(query["path"] ?? "/");
item.HeaderType = Utils.UrlDecode(query["mode"] ?? "");
item.Extra = Utils.UrlDecode(query["extra"] ?? "");
break;
case nameof(ETransport.http):
case nameof(ETransport.h2):
item.Network = nameof(ETransport.h2);
item.RequestHost = Utils.UrlDecode(query["host"] ?? "");
item.Path = Utils.UrlDecode(query["path"] ?? "/");
break;
case nameof(ETransport.quic):
item.HeaderType = query["headerType"] ?? Global.None;
item.RequestHost = query["quicSecurity"] ?? Global.None;
item.Path = Utils.UrlDecode(query["key"] ?? "");
break;
case nameof(ETransport.grpc):
item.RequestHost = Utils.UrlDecode(query["authority"] ?? "");
item.Path = Utils.UrlDecode(query["serviceName"] ?? "");
item.HeaderType = Utils.UrlDecode(query["mode"] ?? Global.GrpcGunMode);
break;
default:
break;
}
return 0;
}
protected static bool Contains(string str, params string[] s)
{
foreach (var item in s)
{
if (str.Contains(item, StringComparison.OrdinalIgnoreCase)) return true;
}
return false;
}
protected static string WriteAllText(string strData, string ext = "json")
{
var fileName = Utils.GetTempPath($"{Utils.GetGuid(false)}.{ext}");
File.WriteAllText(fileName, strData);
return fileName;
}
protected static string ToUri(EConfigType eConfigType, string address, object port, string userInfo, Dictionary<string, string>? dicQuery, string? remark)
{
var query = dicQuery != null
? ("?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()))
: string.Empty;
var url = $"{Utils.UrlEncode(userInfo)}@{GetIpv6(address)}:{port}";
return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}";
}
}
}

View File

@@ -0,0 +1,23 @@
namespace ServiceLib.Handler.Fmt
{
public class ClashFmt : BaseFmt
{
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
if (Contains(strData, "port", "socks-port", "proxies"))
{
var fileName = WriteAllText(strData, "yaml");
var profileItem = new ProfileItem
{
CoreType = ECoreType.mihomo,
Address = fileName,
Remarks = subRemarks ?? "clash_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,90 @@
namespace ServiceLib.Handler.Fmt
{
public class FmtHandler
{
public static string? GetShareUri(ProfileItem item)
{
try
{
var url = item.ConfigType switch
{
EConfigType.VMess => VmessFmt.ToUri(item),
EConfigType.Shadowsocks => ShadowsocksFmt.ToUri(item),
EConfigType.SOCKS => SocksFmt.ToUri(item),
EConfigType.Trojan => TrojanFmt.ToUri(item),
EConfigType.VLESS => VLESSFmt.ToUri(item),
EConfigType.Hysteria2 => Hysteria2Fmt.ToUri(item),
EConfigType.TUIC => TuicFmt.ToUri(item),
EConfigType.WireGuard => WireguardFmt.ToUri(item),
_ => null,
};
return url;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
return "";
}
}
public static ProfileItem? ResolveConfig(string config, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
try
{
string str = config.TrimEx();
if (Utils.IsNullOrEmpty(str))
{
msg = ResUI.FailedReadConfiguration;
return null;
}
if (str.StartsWith(Global.ProtocolShares[EConfigType.VMess]))
{
return VmessFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Shadowsocks]))
{
return ShadowsocksFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.SOCKS]))
{
return SocksFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Trojan]))
{
return TrojanFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.VLESS]))
{
return VLESSFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Hysteria2]) || str.StartsWith(Global.Hysteria2ProtocolShare))
{
return Hysteria2Fmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.TUIC]))
{
return TuicFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.WireGuard]))
{
return WireguardFmt.Resolve(str, out msg);
}
else
{
msg = ResUI.NonvmessOrssProtocol;
return null;
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
msg = ResUI.Incorrectconfiguration;
return null;
}
}
}
}

View File

@@ -0,0 +1,94 @@
namespace ServiceLib.Handler.Fmt
{
public class Hysteria2Fmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{
ConfigType = EConfigType.Hysteria2
};
var url = Utils.TryUri(str);
if (url == null) return null;
item.Address = url.IdnHost;
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
item.Path = Utils.UrlDecode(query["obfs-password"] ?? "");
item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false";
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.Sni))
{
dicQuery.Add("sni", item.Sni);
}
if (Utils.IsNotEmpty(item.Alpn))
{
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
}
if (Utils.IsNotEmpty(item.Path))
{
dicQuery.Add("obfs", "salamander");
dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path));
}
dicQuery.Add("insecure", item.AllowInsecure.ToLower() == "true" ? "1" : "0");
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
if (Contains(strData, "server", "up", "down", "listen", "<html>", "<body>"))
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
CoreType = ECoreType.hysteria,
Address = fileName,
Remarks = subRemarks ?? "hysteria_custom"
};
return profileItem;
}
return null;
}
public static ProfileItem? ResolveFull2(string strData, string? subRemarks)
{
if (Contains(strData, "server", "auth", "up", "down", "listen"))
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
CoreType = ECoreType.hysteria2,
Address = fileName,
Remarks = subRemarks ?? "hysteria2_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,23 @@
namespace ServiceLib.Handler.Fmt
{
public class NaiveproxyFmt : BaseFmt
{
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
if (Contains(strData, "listen", "proxy", "<html>", "<body>"))
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
CoreType = ECoreType.naiveproxy,
Address = fileName,
Remarks = subRemarks ?? "naiveproxy_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,172 @@
using System.Text.RegularExpressions;
namespace ServiceLib.Handler.Fmt
{
public class ShadowsocksFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem? item;
item = ResolveSSLegacy(str) ?? ResolveSip002(str);
if (item == null)
{
return null;
}
if (item.Address.Length == 0 || item.Port == 0 || item.Security.Length == 0 || item.Id.Length == 0)
{
return null;
}
item.ConfigType = EConfigType.Shadowsocks;
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
//url = string.Format("{0}:{1}@{2}:{3}",
// item.security,
// item.id,
// item.address,
// item.port);
//url = Utile.Base64Encode(url);
//new Sip002
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
return ToUri(EConfigType.Shadowsocks, item.Address, item.Port, pw, null, remark);
}
private static readonly Regex UrlFinder = new(@"ss://(?<base64>[A-Za-z0-9+-/=_]+)(?:#(?<tag>\S+))?", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static readonly Regex DetailsParser = new(@"^((?<method>.+?):(?<password>.*)@(?<hostname>.+?):(?<port>\d+?))$", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private static ProfileItem? ResolveSSLegacy(string result)
{
var match = UrlFinder.Match(result);
if (!match.Success)
return null;
ProfileItem item = new();
var base64 = match.Groups["base64"].Value.TrimEnd('/');
var tag = match.Groups["tag"].Value;
if (Utils.IsNotEmpty(tag))
{
item.Remarks = Utils.UrlDecode(tag);
}
Match details;
try
{
details = DetailsParser.Match(Utils.Base64Decode(base64));
}
catch (FormatException)
{
return null;
}
if (!details.Success)
return null;
item.Security = details.Groups["method"].Value;
item.Id = details.Groups["password"].Value;
item.Address = details.Groups["hostname"].Value;
item.Port = Utils.ToInt(details.Groups["port"].Value);
return item;
}
private static ProfileItem? ResolveSip002(string result)
{
var parsedUrl = Utils.TryUri(result);
if (parsedUrl == null) return null;
ProfileItem item = new()
{
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
Address = parsedUrl.IdnHost,
Port = parsedUrl.Port,
};
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
//2022-blake3
if (rawUserInfo.Contains(':'))
{
string[] userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2)
{
return null;
}
item.Security = userInfoParts.First();
item.Id = Utils.UrlDecode(userInfoParts.Last());
}
else
{
// parse base64 UserInfo
string userInfo = Utils.Base64Decode(rawUserInfo);
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2)
{
return null;
}
item.Security = userInfoParts.First();
item.Id = userInfoParts.Last();
}
var queryParameters = Utils.ParseQueryString(parsedUrl.Query);
if (queryParameters["plugin"] != null)
{
//obfs-host exists
var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host"));
if (queryParameters["plugin"].Contains("obfs=http") && Utils.IsNotEmpty(obfsHost))
{
obfsHost = obfsHost?.Replace("obfs-host=", "");
item.Network = Global.DefaultNetwork;
item.HeaderType = Global.TcpHeaderHttp;
item.RequestHost = obfsHost ?? "";
}
else
{
return null;
}
}
return item;
}
public static List<ProfileItem>? ResolveSip008(string result)
{
//SsSIP008
var lstSsServer = JsonUtils.Deserialize<List<SsServer>>(result);
if (lstSsServer?.Count <= 0)
{
var ssSIP008 = JsonUtils.Deserialize<SsSIP008>(result);
if (ssSIP008?.servers?.Count > 0)
{
lstSsServer = ssSIP008.servers;
}
}
if (lstSsServer?.Count > 0)
{
List<ProfileItem> lst = [];
foreach (var it in lstSsServer)
{
var ssItem = new ProfileItem()
{
Remarks = it.remarks,
Security = it.method,
Id = it.password,
Address = it.server,
Port = Utils.ToInt(it.server_port)
};
lst.Add(ssItem);
}
return lst;
}
return null;
}
}
}

View File

@@ -0,0 +1,55 @@
namespace ServiceLib.Handler.Fmt
{
public class SingboxFmt : BaseFmt
{
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
{
var configObjects = JsonUtils.Deserialize<Object[]>(strData);
if (configObjects != null && configObjects.Length > 0)
{
List<ProfileItem> lstResult = [];
foreach (var configObject in configObjects)
{
var objectString = JsonUtils.Serialize(configObject);
var singboxCon = JsonUtils.Deserialize<SingboxConfig>(objectString);
if (singboxCon?.inbounds?.Count > 0
&& singboxCon.outbounds?.Count > 0
&& singboxCon.route != null)
{
var fileName = WriteAllText(objectString);
var profileIt = new ProfileItem
{
CoreType = ECoreType.sing_box,
Address = fileName,
Remarks = subRemarks ?? "singbox_custom",
};
lstResult.Add(profileIt);
}
}
return lstResult;
}
return null;
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
var singboxConfig = JsonUtils.Deserialize<SingboxConfig>(strData);
if (singboxConfig?.inbounds?.Count > 0
&& singboxConfig.outbounds?.Count > 0
&& singboxConfig.route != null)
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
CoreType = ECoreType.sing_box,
Address = fileName,
Remarks = subRemarks ?? "singbox_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,111 @@
namespace ServiceLib.Handler.Fmt
{
public class SocksFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
var item = ResolveSocksNew(str) ?? ResolveSocks(str);
if (item == null)
{
return null;
}
if (item.Address.Length == 0 || item.Port == 0)
{
return null;
}
item.ConfigType = EConfigType.SOCKS;
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
var url = string.Empty;
var remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
//new
var pw = Utils.Base64Encode($"{item.Security}:{item.Id}");
return ToUri(EConfigType.SOCKS, item.Address, item.Port, pw, null, remark);
}
private static ProfileItem? ResolveSocks(string result)
{
ProfileItem item = new()
{
ConfigType = EConfigType.SOCKS
};
result = result[Global.ProtocolShares[EConfigType.SOCKS].Length..];
//remark
var indexRemark = result.IndexOf("#");
if (indexRemark > 0)
{
try
{
item.Remarks = Utils.UrlDecode(result.Substring(indexRemark + 1, result.Length - indexRemark - 1));
}
catch { }
result = result[..indexRemark];
}
//part decode
var indexS = result.IndexOf("@");
if (indexS > 0)
{
}
else
{
result = Utils.Base64Decode(result);
}
var arr1 = result.Split('@');
if (arr1.Length != 2)
{
return null;
}
var arr21 = arr1.First().Split(':');
var indexPort = arr1.Last().LastIndexOf(":");
if (arr21.Length != 2 || indexPort < 0)
{
return null;
}
item.Address = arr1[1][..indexPort];
item.Port = Utils.ToInt(arr1[1][(indexPort + 1)..]);
item.Security = arr21.First();
item.Id = arr21[1];
return item;
}
private static ProfileItem? ResolveSocksNew(string result)
{
var parsedUrl = Utils.TryUri(result);
if (parsedUrl == null) return null;
ProfileItem item = new()
{
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
Address = parsedUrl.IdnHost,
Port = parsedUrl.Port,
};
// parse base64 UserInfo
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
var userInfo = Utils.Base64Decode(rawUserInfo);
var userInfoParts = userInfo.Split([':'], 2);
if (userInfoParts.Length == 2)
{
item.Security = userInfoParts.First();
item.Id = userInfoParts[1];
}
return item;
}
}
}

View File

@@ -0,0 +1,44 @@
namespace ServiceLib.Handler.Fmt
{
public class TrojanFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{
ConfigType = EConfigType.Trojan
};
var url = Utils.TryUri(str);
if (url == null) return null;
item.Address = url.IdnHost;
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
GetStdTransport(item, null, ref dicQuery);
return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Id, dicQuery, remark);
}
}
}

View File

@@ -0,0 +1,59 @@
namespace ServiceLib.Handler.Fmt
{
public class TuicFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{
ConfigType = EConfigType.TUIC
};
var url = Utils.TryUri(str);
if (url == null) return null;
item.Address = url.IdnHost;
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
var rawUserInfo = Utils.UrlDecode(url.UserInfo);
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length == 2)
{
item.Id = userInfoParts.First();
item.Security = userInfoParts.Last();
}
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
item.HeaderType = query["congestion_control"] ?? "";
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.Sni))
{
dicQuery.Add("sni", item.Sni);
}
if (Utils.IsNotEmpty(item.Alpn))
{
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
}
dicQuery.Add("congestion_control", item.HeaderType);
return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark);
}
}
}

View File

@@ -0,0 +1,56 @@
namespace ServiceLib.Handler.Fmt
{
public class V2rayFmt : BaseFmt
{
public static List<ProfileItem>? ResolveFullArray(string strData, string? subRemarks)
{
var configObjects = JsonUtils.Deserialize<Object[]>(strData);
if (configObjects != null && configObjects.Length > 0)
{
List<ProfileItem> lstResult = [];
foreach (var configObject in configObjects)
{
var objectString = JsonUtils.Serialize(configObject);
var v2rayCon = JsonUtils.Deserialize<V2rayConfig>(objectString);
if (v2rayCon?.inbounds?.Count > 0
&& v2rayCon.outbounds?.Count > 0
&& v2rayCon.routing != null)
{
var fileName = WriteAllText(objectString);
var profileIt = new ProfileItem
{
CoreType = ECoreType.Xray,
Address = fileName,
Remarks = v2rayCon.remarks ?? subRemarks ?? "v2ray_custom",
};
lstResult.Add(profileIt);
}
}
return lstResult;
}
return null;
}
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
{
var v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(strData);
if (v2rayConfig?.inbounds?.Count > 0
&& v2rayConfig.outbounds?.Count > 0
&& v2rayConfig.routing != null)
{
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
CoreType = ECoreType.Xray,
Address = fileName,
Remarks = v2rayConfig.remarks ?? subRemarks ?? "v2ray_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -0,0 +1,55 @@
namespace ServiceLib.Handler.Fmt
{
public class VLESSFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{
ConfigType = EConfigType.VLESS,
Security = Global.None
};
var url = Utils.TryUri(str);
if (url == null) return null;
item.Address = url.IdnHost;
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
item.Security = query["encryption"] ?? Global.None;
item.StreamSecurity = query["security"] ?? "";
ResolveStdTransport(query, ref item);
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.Security))
{
dicQuery.Add("encryption", item.Security);
}
else
{
dicQuery.Add("encryption", Global.None);
}
GetStdTransport(item, Global.None, ref dicQuery);
return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Id, dicQuery, remark);
}
}
}

View File

@@ -0,0 +1,122 @@
namespace ServiceLib.Handler.Fmt
{
public class VmessFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem? item;
if (str.IndexOf('?') > 0 && str.IndexOf('&') > 0)
{
item = ResolveStdVmess(str);
}
else
{
item = ResolveVmess(str, out msg);
}
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
VmessQRCode vmessQRCode = new()
{
v = item.ConfigVersion,
ps = item.Remarks.TrimEx(),
add = item.Address,
port = item.Port,
id = item.Id,
aid = item.AlterId,
scy = item.Security,
net = item.Network,
type = item.HeaderType,
host = item.RequestHost,
path = item.Path,
tls = item.StreamSecurity,
sni = item.Sni,
alpn = item.Alpn,
fp = item.Fingerprint
};
url = JsonUtils.Serialize(vmessQRCode);
url = Utils.Base64Encode(url);
url = $"{Global.ProtocolShares[EConfigType.VMess]}{url}";
return url;
}
private static ProfileItem? ResolveVmess(string result, out string msg)
{
msg = string.Empty;
var item = new ProfileItem
{
ConfigType = EConfigType.VMess
};
result = result[Global.ProtocolShares[EConfigType.VMess].Length..];
result = Utils.Base64Decode(result);
VmessQRCode? vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result);
if (vmessQRCode == null)
{
msg = ResUI.FailedConversionConfiguration;
return null;
}
item.Network = Global.DefaultNetwork;
item.HeaderType = Global.None;
item.ConfigVersion = vmessQRCode.v;
item.Remarks = Utils.ToString(vmessQRCode.ps);
item.Address = Utils.ToString(vmessQRCode.add);
item.Port = vmessQRCode.port;
item.Id = Utils.ToString(vmessQRCode.id);
item.AlterId = vmessQRCode.aid;
item.Security = Utils.ToString(vmessQRCode.scy);
item.Security = Utils.IsNotEmpty(vmessQRCode.scy) ? vmessQRCode.scy : Global.DefaultSecurity;
if (Utils.IsNotEmpty(vmessQRCode.net))
{
item.Network = vmessQRCode.net;
}
if (Utils.IsNotEmpty(vmessQRCode.type))
{
item.HeaderType = vmessQRCode.type;
}
item.RequestHost = Utils.ToString(vmessQRCode.host);
item.Path = Utils.ToString(vmessQRCode.path);
item.StreamSecurity = Utils.ToString(vmessQRCode.tls);
item.Sni = Utils.ToString(vmessQRCode.sni);
item.Alpn = Utils.ToString(vmessQRCode.alpn);
item.Fingerprint = Utils.ToString(vmessQRCode.fp);
return item;
}
public static ProfileItem? ResolveStdVmess(string str)
{
ProfileItem item = new()
{
ConfigType = EConfigType.VMess,
Security = "auto"
};
var url = Utils.TryUri(str);
if (url == null) return null;
item.Address = url.IdnHost;
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
return item;
}
}
}

View File

@@ -0,0 +1,63 @@
namespace ServiceLib.Handler.Fmt
{
public class WireguardFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem item = new()
{
ConfigType = EConfigType.WireGuard
};
var url = Utils.TryUri(str);
if (url == null) return null;
item.Address = url.IdnHost;
item.Port = url.Port;
item.Remarks = url.GetComponents(UriComponents.Fragment, UriFormat.Unescaped);
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
item.PublicKey = Utils.UrlDecode(query["publickey"] ?? "");
item.Path = Utils.UrlDecode(query["reserved"] ?? "");
item.RequestHost = Utils.UrlDecode(query["address"] ?? "");
item.ShortId = Utils.UrlDecode(query["mtu"] ?? "");
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null) return null;
string url = string.Empty;
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.PublicKey))
{
dicQuery.Add("publickey", Utils.UrlEncode(item.PublicKey));
}
if (Utils.IsNotEmpty(item.Path))
{
dicQuery.Add("reserved", Utils.UrlEncode(item.Path));
}
if (Utils.IsNotEmpty(item.RequestHost))
{
dicQuery.Add("address", Utils.UrlEncode(item.RequestHost));
}
if (Utils.IsNotEmpty(item.ShortId))
{
dicQuery.Add("mtu", Utils.UrlEncode(item.ShortId));
}
return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Id, dicQuery, remark);
}
}
}

View File

@@ -0,0 +1,44 @@
using ReactiveUI;
namespace ServiceLib.Handler
{
public class NoticeHandler
{
private static readonly Lazy<NoticeHandler> _instance = new(() => new());
public static NoticeHandler Instance => _instance.Value;
public void Enqueue(string? content)
{
if (content.IsNullOrEmpty())
{
return;
}
MessageBus.Current.SendMessage(content, EMsgCommand.SendSnackMsg.ToString());
}
public void SendMessage(string? content)
{
if (content.IsNullOrEmpty())
{
return;
}
MessageBus.Current.SendMessage(content, EMsgCommand.SendMsgView.ToString());
}
public void SendMessageEx(string? content)
{
if (content.IsNullOrEmpty())
{
return;
}
content = $"{DateTime.Now:yyyy/MM/dd HH:mm:ss} {content}";
SendMessage(content);
}
public void SendMessageAndEnqueue(string? msg)
{
Enqueue(msg);
SendMessage(msg);
}
}
}

View File

@@ -0,0 +1,105 @@
using System.Net.Sockets;
using System.Text;
namespace ServiceLib.Handler
{
public class PacHandler
{
private static string _configPath;
private static int _httpPort;
private static int _pacPort;
private static TcpListener? _tcpListener;
private static byte[] _writeContent;
private static bool _isRunning;
private static bool _needRestart = true;
public static async Task Start(string configPath, int httpPort, int pacPort)
{
_needRestart = (configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning);
_configPath = configPath;
_httpPort = httpPort;
_pacPort = pacPort;
await InitText();
if (_needRestart)
{
Stop();
RunListener();
}
}
private static async Task InitText()
{
var path = Path.Combine(_configPath, "pac.txt");
if (!File.Exists(path))
{
var pac = Utils.GetEmbedText(Global.PacFileName);
await File.AppendAllTextAsync(path, pac);
}
var pacText =
(await File.ReadAllTextAsync(path)).Replace("__PROXY__", $"PROXY 127.0.0.1:{_httpPort};DIRECT;");
var sb = new StringBuilder();
sb.AppendLine("HTTP/1.0 200 OK");
sb.AppendLine("Content-type:application/x-ns-proxy-autoconfig");
sb.AppendLine("Connection:close");
sb.AppendLine("Content-Length:" + Encoding.UTF8.GetByteCount(pacText));
sb.AppendLine();
sb.Append(pacText);
_writeContent = Encoding.UTF8.GetBytes(sb.ToString());
}
private static void RunListener()
{
_tcpListener = TcpListener.Create(_pacPort);
_isRunning = true;
_tcpListener.Start();
Task.Factory.StartNew(async () =>
{
while (_isRunning)
{
try
{
if (!_tcpListener.Pending())
{
await Task.Delay(10);
continue;
}
var client = await _tcpListener.AcceptTcpClientAsync();
await Task.Run(() => { WriteContent(client); });
}
catch
{
// ignored
}
}
}, TaskCreationOptions.LongRunning);
}
private static void WriteContent(TcpClient client)
{
var stream = client.GetStream();
stream.Write(_writeContent, 0, _writeContent.Length);
stream.Flush();
}
public static void Stop()
{
if (_tcpListener == null) return;
try
{
_isRunning = false;
_tcpListener.Stop();
_tcpListener = null;
}
catch
{
// ignored
}
}
}
}

View File

@@ -0,0 +1,182 @@
using System.Collections.Concurrent;
//using System.Reactive.Linq;
namespace ServiceLib.Handler
{
public class ProfileExHandler
{
private static readonly Lazy<ProfileExHandler> _instance = new(() => new());
private ConcurrentBag<ProfileExItem> _lstProfileEx = [];
private Queue<string> _queIndexIds = new();
public static ProfileExHandler Instance => _instance.Value;
public ProfileExHandler()
{
//Init();
}
public async Task Init()
{
await InitData();
Task.Run(async () =>
{
while (true)
{
await Task.Delay(1000 * 600);
await SaveQueueIndexIds();
}
});
}
public async Task<ConcurrentBag<ProfileExItem>> GetProfileExs()
{
return _lstProfileEx;
}
private async Task InitData()
{
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileExItem where indexId not in ( select indexId from ProfileItem )");
_lstProfileEx = new(await SQLiteHelper.Instance.TableAsync<ProfileExItem>().ToListAsync());
}
private void IndexIdEnqueue(string indexId)
{
if (Utils.IsNotEmpty(indexId) && !_queIndexIds.Contains(indexId))
{
_queIndexIds.Enqueue(indexId);
}
}
private async Task SaveQueueIndexIds()
{
var cnt = _queIndexIds.Count;
if (cnt > 0)
{
var lstExists = await SQLiteHelper.Instance.TableAsync<ProfileExItem>().ToListAsync();
List<ProfileExItem> lstInserts = [];
List<ProfileExItem> lstUpdates = [];
for (int i = 0; i < cnt; i++)
{
var id = _queIndexIds.Dequeue();
var item = lstExists.FirstOrDefault(t => t.IndexId == id);
var itemNew = _lstProfileEx?.FirstOrDefault(t => t.IndexId == id);
if (itemNew is null)
{
continue;
}
if (item is not null)
{
lstUpdates.Add(itemNew);
}
else
{
lstInserts.Add(itemNew);
}
}
try
{
if (lstInserts.Count() > 0)
await SQLiteHelper.Instance.InsertAllAsync(lstInserts);
if (lstUpdates.Count() > 0)
await SQLiteHelper.Instance.UpdateAllAsync(lstUpdates);
}
catch (Exception ex)
{
Logging.SaveLog("ProfileExHandler", ex);
}
}
}
private void AddProfileEx(string indexId, ref ProfileExItem? profileEx)
{
profileEx = new()
{
IndexId = indexId,
Delay = 0,
Speed = 0,
Sort = 0
};
_lstProfileEx.Add(profileEx);
IndexIdEnqueue(indexId);
}
public async Task ClearAll()
{
await SQLiteHelper.Instance.ExecuteAsync($"delete from ProfileExItem ");
_lstProfileEx = new();
}
public async Task SaveTo()
{
try
{
await SaveQueueIndexIds();
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public void SetTestDelay(string indexId, string delayVal)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
int.TryParse(delayVal, out int delay);
profileEx.Delay = delay;
IndexIdEnqueue(indexId);
}
public void SetTestSpeed(string indexId, string speedVal)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
decimal.TryParse(speedVal, out decimal speed);
profileEx.Speed = speed;
IndexIdEnqueue(indexId);
}
public void SetSort(string indexId, int sort)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
if (profileEx == null)
{
AddProfileEx(indexId, ref profileEx);
}
profileEx.Sort = sort;
IndexIdEnqueue(indexId);
}
public int GetSort(string indexId)
{
var profileEx = _lstProfileEx.FirstOrDefault(t => t.IndexId == indexId);
if (profileEx == null)
{
return 0;
}
return profileEx.Sort;
}
public int GetMaxSort()
{
if (_lstProfileEx.Count <= 0)
{
return 0;
}
return _lstProfileEx.Max(t => t == null ? 0 : t.Sort);
}
}
}

View File

@@ -0,0 +1,161 @@
namespace ServiceLib.Handler
{
public class StatisticsHandler
{
private static readonly Lazy<StatisticsHandler> instance = new(() => new());
public static StatisticsHandler Instance => instance.Value;
private Config _config;
private ServerStatItem? _serverStatItem;
private List<ServerStatItem> _lstServerStat;
private Action<ServerSpeedItem>? _updateFunc;
private StatisticsXrayService? _statisticsXray;
private StatisticsSingboxService? _statisticsSingbox;
public List<ServerStatItem> ServerStat => _lstServerStat;
public async Task Init(Config config, Action<ServerSpeedItem> updateFunc)
{
_config = config;
_updateFunc = updateFunc;
if (!config.GuiItem.EnableStatistics)
{
return;
}
await InitData();
_statisticsXray = new StatisticsXrayService(config, UpdateServerStatHandler);
_statisticsSingbox = new StatisticsSingboxService(config, UpdateServerStatHandler);
}
public void Close()
{
try
{
_statisticsXray?.Close();
_statisticsSingbox?.Close();
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public async Task ClearAllServerStatistics()
{
await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem ");
_serverStatItem = null;
_lstServerStat = new();
}
public async Task SaveTo()
{
try
{
if (_lstServerStat != null)
{
await SQLiteHelper.Instance.UpdateAllAsync(_lstServerStat);
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public async Task CloneServerStatItem(string indexId, string toIndexId)
{
if (_lstServerStat == null)
{
return;
}
var stat = _lstServerStat.FirstOrDefault(t => t.IndexId == indexId);
if (stat == null)
{
return;
}
var toStat = JsonUtils.DeepCopy(stat);
toStat.IndexId = toIndexId;
await SQLiteHelper.Instance.ReplaceAsync(toStat);
_lstServerStat.Add(toStat);
}
private async Task InitData()
{
await SQLiteHelper.Instance.ExecuteAsync($"delete from ServerStatItem where indexId not in ( select indexId from ProfileItem )");
long ticks = DateTime.Now.Date.Ticks;
await SQLiteHelper.Instance.ExecuteAsync($"update ServerStatItem set todayUp = 0,todayDown=0,dateNow={ticks} where dateNow<>{ticks}");
_lstServerStat = await SQLiteHelper.Instance.TableAsync<ServerStatItem>().ToListAsync();
}
private void UpdateServerStatHandler(ServerSpeedItem server)
{
UpdateServerStat(server);
}
private async Task UpdateServerStat(ServerSpeedItem server)
{
await GetServerStatItem(_config.IndexId);
if (_serverStatItem is null)
{
return;
}
if (server.ProxyUp != 0 || server.ProxyDown != 0)
{
_serverStatItem.TodayUp += server.ProxyUp;
_serverStatItem.TodayDown += server.ProxyDown;
_serverStatItem.TotalUp += server.ProxyUp;
_serverStatItem.TotalDown += server.ProxyDown;
}
server.IndexId = _config.IndexId;
server.TodayUp = _serverStatItem.TodayUp;
server.TodayDown = _serverStatItem.TodayDown;
server.TotalUp = _serverStatItem.TotalUp;
server.TotalDown = _serverStatItem.TotalDown;
_updateFunc?.Invoke(server);
}
private async Task GetServerStatItem(string indexId)
{
long ticks = DateTime.Now.Date.Ticks;
if (_serverStatItem != null && _serverStatItem.IndexId != indexId)
{
_serverStatItem = null;
}
if (_serverStatItem == null)
{
_serverStatItem = _lstServerStat.FirstOrDefault(t => t.IndexId == indexId);
if (_serverStatItem == null)
{
_serverStatItem = new ServerStatItem
{
IndexId = indexId,
TotalUp = 0,
TotalDown = 0,
TodayUp = 0,
TodayDown = 0,
DateNow = ticks
};
await SQLiteHelper.Instance.ReplaceAsync(_serverStatItem);
_lstServerStat.Add(_serverStatItem);
}
}
if (_serverStatItem.DateNow != ticks)
{
_serverStatItem.TodayUp = 0;
_serverStatItem.TodayDown = 0;
_serverStatItem.DateNow = ticks;
}
}
}
}

View File

@@ -0,0 +1,152 @@
namespace ServiceLib.Handler.SysProxy
{
public class ProxySettingLinux
{
public static async Task SetProxy(string host, int port)
{
var lstCmd = GetSetCmds(host, port);
await ExecCmd(lstCmd);
}
public static async Task UnsetProxy()
{
var lstCmd = GetUnsetCmds();
await ExecCmd(lstCmd);
}
private static async Task ExecCmd(List<CmdItem> lstCmd)
{
foreach (var cmd in lstCmd)
{
if (cmd is null || cmd.Cmd.IsNullOrEmpty() || cmd.Arguments is null)
{
continue;
}
await Task.Delay(10);
await Utils.GetCliWrapOutput(cmd.Cmd, cmd.Arguments);
}
}
private static List<CmdItem> GetSetCmds(string host, int port)
{
var isKde = IsKde(out var configDir);
List<string> lstType = ["", "http", "https", "socks", "ftp"];
List<CmdItem> lstCmd = [];
if (isKde)
{
foreach (var type in lstType)
{
lstCmd.AddRange(GetSetCmd4Kde(type, host, port, configDir));
}
}
else
{
foreach (var type in lstType)
{
lstCmd.AddRange(GetSetCmd4Gnome(type, host, port));
}
}
return lstCmd;
}
private static List<CmdItem> GetUnsetCmds()
{
var isKde = IsKde(out var configDir);
List<CmdItem> lstCmd = [];
if (isKde)
{
lstCmd.Add(new CmdItem()
{
Cmd = "kwriteconfig5",
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "0"]
});
}
else
{
lstCmd.Add(new CmdItem()
{
Cmd = "gsettings",
Arguments = ["set", "org.gnome.system.proxy", "mode", "none"]
});
}
return lstCmd;
}
private static List<CmdItem> GetSetCmd4Kde(string type, string host, int port, string configDir)
{
List<CmdItem> lstCmd = [];
if (type.IsNullOrEmpty())
{
lstCmd.Add(new()
{
Cmd = "kwriteconfig5",
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", "ProxyType", "1"]
});
}
else
{
var type2 = type.Equals("https") ? "http" : type;
lstCmd.Add(new CmdItem()
{
Cmd = "kwriteconfig5",
Arguments = ["--file", $"{configDir}/kioslaverc", "--group", "Proxy Settings", "--key", $"{type}Proxy", $"{type2}://{host}:{port}"]
});
}
return lstCmd;
}
private static List<CmdItem> GetSetCmd4Gnome(string type, string host, int port)
{
List<CmdItem> lstCmd = [];
if (type.IsNullOrEmpty())
{
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", "org.gnome.system.proxy", "mode", "manual"]
});
}
else
{
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", $"org.gnome.system.proxy.{type}", "host", host]
});
lstCmd.Add(new()
{
Cmd = "gsettings",
Arguments = ["set", $"org.gnome.system.proxy.{type}", "port", $"{port}"]
});
}
return lstCmd;
}
private static bool IsKde(out string configDir)
{
configDir = "/home";
var desktop = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP");
var isKde = string.Equals(desktop, "KDE", StringComparison.OrdinalIgnoreCase);
if (isKde)
{
var homeDir = Environment.GetEnvironmentVariable("HOME");
if (homeDir != null)
{
configDir = Path.Combine(homeDir, ".config");
}
}
return isKde;
}
}
}

View File

@@ -0,0 +1,81 @@
namespace ServiceLib.Handler.SysProxy
{
public class ProxySettingOSX
{
/*
* 仅测试了MacOS 13.7.1 x86 版本,其他版本有待确认
*/
/// <summary>
/// 应用接口类型
/// </summary>
private static readonly List<string> LstInterface = ["Ethernet", "Wi-Fi", "Thunderbolt Bridge"];
/// <summary>
/// 代理类型,对应 http,https,socks
/// </summary>
private static readonly List<string> LstTypes = ["setwebproxy", "setsecurewebproxy", "setsocksfirewallproxy"];
public static async Task SetProxy(string host, int port)
{
var lstCmd = GetSetCmds(host, port);
await ExecCmd(lstCmd);
}
public static async Task UnsetProxy()
{
var lstCmd = GetUnsetCmds();
await ExecCmd(lstCmd);
}
private static async Task ExecCmd(List<CmdItem> lstCmd)
{
foreach (var cmd in lstCmd)
{
if (cmd is null || cmd.Cmd.IsNullOrEmpty() || cmd.Arguments is null)
{
continue;
}
await Task.Delay(10);
await Utils.GetCliWrapOutput(cmd.Cmd, cmd.Arguments);
}
}
private static List<CmdItem> GetSetCmds(string host, int port)
{
List<CmdItem> lstCmd = [];
foreach (var interf in LstInterface)
{
foreach (var type in LstTypes)
{
lstCmd.Add(new CmdItem()
{
Cmd = "networksetup",
Arguments = [$"-{type}", interf, host, (type.Contains("socks") ? (port - 1) : port).ToString()]
});
}
}
return lstCmd;
}
private static List<CmdItem> GetUnsetCmds()
{
List<CmdItem> lstCmd = [];
foreach (var interf in LstInterface)
{
foreach (var type in LstTypes)
{
lstCmd.Add(new CmdItem()
{
Cmd = "networksetup",
Arguments = [$"-{type}state", interf, "off"]
});
}
}
return lstCmd;
}
}
}

View File

@@ -0,0 +1,360 @@
using System.Runtime.InteropServices;
using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption;
namespace ServiceLib.Handler.SysProxy
{
public class ProxySettingWindows
{
private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
private static bool SetProxyFallback(string? strProxy, string? exceptions, int type)
{
if (type == 1)
{
WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0);
WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
}
if (type == 2)
{
WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 1);
WindowsUtils.RegWriteValue(_regPath, "ProxyServer", strProxy ?? string.Empty);
WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", exceptions ?? string.Empty);
WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", string.Empty);
}
else if (type == 4)
{
WindowsUtils.RegWriteValue(_regPath, "ProxyEnable", 0);
WindowsUtils.RegWriteValue(_regPath, "ProxyServer", string.Empty);
WindowsUtils.RegWriteValue(_regPath, "ProxyOverride", string.Empty);
WindowsUtils.RegWriteValue(_regPath, "AutoConfigURL", strProxy ?? string.Empty);
}
return true;
}
/// <summary>
// set to use no proxy
/// </summary>
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
public static bool UnsetProxy()
{
return SetProxy(null, null, 1);
}
/// <summary>
/// Set system proxy settings
/// </summary>
/// <param name="strProxy"> proxy address</param>
/// <param name="exceptions">exception addresses that do not use proxy</param>
/// <param name="type">type of proxy defined in PerConnFlags
/// PROXY_TYPE_DIRECT = 0x00000001, // direct connection (no proxy)
/// PROXY_TYPE_PROXY = 0x00000002, // via named proxy
/// PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy script URL
/// PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
/// </param>
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
/// <returns>true: one of connection is successfully updated proxy settings</returns>
public static bool SetProxy(string? strProxy, string? exceptions, int type)
{
try
{
// set proxy for LAN
bool result = SetConnectionProxy(null, strProxy, exceptions, type);
// set proxy for dial up connections
var connections = EnumerateRasEntries();
foreach (var connection in connections)
{
result |= SetConnectionProxy(connection, strProxy, exceptions, type);
}
return result;
}
catch (Exception ex)
{
SetProxyFallback(strProxy, exceptions, type);
//Logging.SaveLog(ex.Message, ex);
return false;
}
}
private static bool SetConnectionProxy(string? connectionName, string? strProxy, string? exceptions, int type)
{
InternetPerConnOptionList list = new();
int optionCount = 1;
if (type == 1) // No proxy
{
optionCount = 1;
}
else if (type is 2 or 4) // named proxy or autoproxy script URL
{
optionCount = string.IsNullOrEmpty(exceptions) ? 2 : 3;
}
int m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT;
PerConnOption m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
if (type == 2) // named proxy
{
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY);
m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_SERVER;
}
else if (type == 4) // autoproxy script url
{
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_AUTO_PROXY_URL);
m_Option = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL;
}
//int optionCount = Utile.IsNullOrEmpty(strProxy) ? 1 : (Utile.IsNullOrEmpty(exceptions) ? 2 : 3);
InternetConnectionOption[] options = new InternetConnectionOption[optionCount];
// USE a proxy server ...
options[0].m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
//options[0].m_Value.m_Int = (int)((optionCount < 2) ? PerConnFlags.PROXY_TYPE_DIRECT : (PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY));
options[0].m_Value.m_Int = m_Int;
// use THIS proxy server
if (optionCount > 1)
{
options[1].m_Option = m_Option;
options[1].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(strProxy); // !! remember to deallocate memory 1
// except for these addresses ...
if (optionCount > 2)
{
options[2].m_Option = PerConnOption.INTERNET_PER_CONN_PROXY_BYPASS;
options[2].m_Value.m_StringPtr = Marshal.StringToHGlobalAuto(exceptions); // !! remember to deallocate memory 2
}
}
// default stuff
list.dwSize = Marshal.SizeOf(list);
if (connectionName != null)
{
list.szConnection = Marshal.StringToHGlobalAuto(connectionName); // !! remember to deallocate memory 3
}
else
{
list.szConnection = nint.Zero;
}
list.dwOptionCount = options.Length;
list.dwOptionError = 0;
int optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
// make a pointer out of all that ...
nint optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); // !! remember to deallocate memory 4
// copy the array over into that spot in memory ...
for (int i = 0; i < options.Length; ++i)
{
if (Environment.Is64BitOperatingSystem)
{
nint opt = new(optionsPtr.ToInt64() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
else
{
nint opt = new(optionsPtr.ToInt32() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
}
list.options = optionsPtr;
// and then make a pointer out of the whole list
nint ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5
Marshal.StructureToPtr(list, ipcoListPtr, false);
// and finally, call the API method!
bool isSuccess = NativeMethods.InternetSetOption(nint.Zero,
InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
ipcoListPtr, list.dwSize);
int returnvalue = 0; // ERROR_SUCCESS
if (!isSuccess)
{ // get the error codes, they might be helpful
returnvalue = Marshal.GetLastPInvokeError();
}
else
{
// Notify the system that the registry settings have been changed and cause them to be refreshed
NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_SETTINGS_CHANGED, nint.Zero, 0);
NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_REFRESH, nint.Zero, 0);
}
// FREE the data ASAP
if (list.szConnection != nint.Zero) Marshal.FreeHGlobal(list.szConnection); // release mem 3
if (optionCount > 1)
{
Marshal.FreeHGlobal(options[1].m_Value.m_StringPtr); // release mem 1
if (optionCount > 2)
{
Marshal.FreeHGlobal(options[2].m_Value.m_StringPtr); // release mem 2
}
}
Marshal.FreeCoTaskMem(optionsPtr); // release mem 4
Marshal.FreeCoTaskMem(ipcoListPtr); // release mem 5
if (returnvalue != 0)
{
// throw the error codes, they might be helpful
throw new ApplicationException($"Set Internet Proxy failed with error code: {Marshal.GetLastWin32Error()}");
}
return true;
}
/// <summary>
/// Retrieve list of connections including LAN and WAN to support PPPoE connection
/// </summary>
/// <returns>A list of RAS connection names. May be empty list if no dial up connection.</returns>
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
private static IEnumerable<string> EnumerateRasEntries()
{
int entries = 0;
// attempt to query with 1 entry buffer
RASENTRYNAME[] rasEntryNames = new RASENTRYNAME[1];
int bufferSize = Marshal.SizeOf(typeof(RASENTRYNAME));
rasEntryNames[0].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
uint result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
// increase buffer if the buffer is not large enough
if (result == (uint)ErrorCode.ERROR_BUFFER_TOO_SMALL)
{
rasEntryNames = new RASENTRYNAME[bufferSize / Marshal.SizeOf(typeof(RASENTRYNAME))];
for (int i = 0; i < rasEntryNames.Length; i++)
{
rasEntryNames[i].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
}
result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
}
if (result == 0)
{
var entryNames = new List<string>();
for (int i = 0; i < entries; i++)
{
entryNames.Add(rasEntryNames[i].szEntryName);
}
return entryNames;
}
throw new ApplicationException($"RasEnumEntries failed with error code: {result}");
}
#region WinInet structures
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct InternetPerConnOptionList
{
public int dwSize; // size of the INTERNET_PER_CONN_OPTION_LIST struct
public nint szConnection; // connection name to set/query options
public int dwOptionCount; // number of options to set/query
public int dwOptionError; // on error, which option failed
//[MarshalAs(UnmanagedType.)]
public nint options;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct InternetConnectionOption
{
private static readonly int Size;
public PerConnOption m_Option;
public InternetConnectionOptionValue m_Value;
static InternetConnectionOption()
{
Size = Marshal.SizeOf(typeof(InternetConnectionOption));
}
// Nested Types
[StructLayout(LayoutKind.Explicit)]
public struct InternetConnectionOptionValue
{
// Fields
[FieldOffset(0)]
public System.Runtime.InteropServices.ComTypes.FILETIME m_FileTime;
[FieldOffset(0)]
public int m_Int;
[FieldOffset(0)]
public nint m_StringPtr;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct RASENTRYNAME
{
public int dwSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = RAS_MaxEntryName + 1)]
public string szEntryName;
public int dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAX_PATH + 1)]
public string szPhonebookPath;
}
// Constants
public const int RAS_MaxEntryName = 256;
public const int MAX_PATH = 260; // Standard MAX_PATH value in Windows
}
#endregion WinInet structures
#region WinInet enums
//
// options manifests for Internet{Query|Set}Option
//
public enum InternetOption : uint
{
INTERNET_OPTION_PER_CONNECTION_OPTION = 75,
INTERNET_OPTION_REFRESH = 37,
INTERNET_OPTION_SETTINGS_CHANGED = 39
}
//
// Options used in INTERNET_PER_CONN_OPTON struct
//
public enum PerConnOption
{
INTERNET_PER_CONN_FLAGS = 1, // Sets or retrieves the connection type. The Value member will contain one or more of the values from PerConnFlags
INTERNET_PER_CONN_PROXY_SERVER = 2, // Sets or retrieves a string containing the proxy servers.
INTERNET_PER_CONN_PROXY_BYPASS = 3, // Sets or retrieves a string containing the URLs that do not use the proxy server.
INTERNET_PER_CONN_AUTOCONFIG_URL = 4//, // Sets or retrieves a string containing the URL to the automatic configuration script.
}
//
// PER_CONN_FLAGS
//
[Flags]
public enum PerConnFlags
{
PROXY_TYPE_DIRECT = 0x00000001, // direct to net
PROXY_TYPE_PROXY = 0x00000002, // via named proxy
PROXY_TYPE_AUTO_PROXY_URL = 0x00000004, // autoproxy URL
PROXY_TYPE_AUTO_DETECT = 0x00000008 // use autoproxy detection
}
public enum ErrorCode : uint
{
ERROR_BUFFER_TOO_SMALL = 603,
ERROR_INVALID_SIZE = 632
}
#endregion WinInet enums
internal static class NativeMethods
{
[DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InternetSetOption(nint hInternet, InternetOption dwOption, nint lpBuffer, int dwBufferLength);
[DllImport("Rasapi32.dll", CharSet = CharSet.Auto)]
public static extern uint RasEnumEntries(
string? reserved, // Reserved, must be null
string? lpszPhonebook, // Pointer to full path and filename of phone-book file. If this parameter is NULL, the entries are enumerated from all the remote access phone-book files
[In, Out] RASENTRYNAME[]? lprasentryname, // Buffer to receive RAS entry names
ref int lpcb, // Size of the buffer
ref int lpcEntries // Number of entries written to the buffer
);
}
}
}

View File

@@ -0,0 +1,96 @@
namespace ServiceLib.Handler.SysProxy
{
public static class SysProxyHandler
{
public static async Task<bool> UpdateSysProxy(Config config, bool forceDisable)
{
var type = config.SystemProxyItem.SysProxyType;
if (forceDisable && type != ESysProxyType.Unchanged)
{
type = ESysProxyType.ForcedClear;
}
try
{
var port = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
if (port <= 0)
{
return false;
}
switch (type)
{
case ESysProxyType.ForcedChange when Utils.IsWindows():
{
GetWindowsProxyString(config, port, out var strProxy, out var strExceptions);
ProxySettingWindows.SetProxy(strProxy, strExceptions, 2);
break;
}
case ESysProxyType.ForcedChange when Utils.IsLinux():
await ProxySettingLinux.SetProxy(Global.Loopback, port);
break;
case ESysProxyType.ForcedChange when Utils.IsOSX():
await ProxySettingOSX.SetProxy(Global.Loopback, port);
break;
case ESysProxyType.ForcedClear when Utils.IsWindows():
ProxySettingWindows.UnsetProxy();
break;
case ESysProxyType.ForcedClear when Utils.IsLinux():
await ProxySettingLinux.UnsetProxy();
break;
case ESysProxyType.ForcedClear when Utils.IsOSX():
await ProxySettingOSX.UnsetProxy();
break;
case ESysProxyType.Pac when Utils.IsWindows():
await SetWindowsProxyPac(port);
break;
}
if (type != ESysProxyType.Pac && Utils.IsWindows())
{
PacHandler.Stop();
}
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
return true;
}
private static void GetWindowsProxyString(Config config, int port, out string strProxy, out string strExceptions)
{
strExceptions = $"{config.ConstItem.DefIEProxyExceptions};{config.SystemProxyItem.SystemProxyExceptions}";
if (config.SystemProxyItem.NotProxyLocalAddress)
{
strExceptions = $"<local>;{strExceptions}";
}
strProxy = string.Empty;
if (Utils.IsNullOrEmpty(config.SystemProxyItem.SystemProxyAdvancedProtocol))
{
strProxy = $"{Global.Loopback}:{port}";
}
else
{
strProxy = config.SystemProxyItem.SystemProxyAdvancedProtocol
.Replace("{ip}", Global.Loopback)
.Replace("{http_port}", port.ToString())
.Replace("{socks_port}", port.ToString());
}
}
private static async Task SetWindowsProxyPac(int port)
{
var portPac = AppHandler.Instance.GetLocalPort(EInboundProtocol.pac);
await PacHandler.Start(Utils.GetConfigPath(), port, portPac);
var strProxy = $"{Global.HttpProtocol}{Global.Loopback}:{portPac}/pac?t={DateTime.Now.Ticks}";
ProxySettingWindows.SetProxy(strProxy, "", 4);
}
}
}

View File

@@ -0,0 +1,72 @@
namespace ServiceLib.Handler
{
public class TaskHandler
{
private static readonly Lazy<TaskHandler> _instance = new(() => new());
public static TaskHandler Instance => _instance.Value;
public void RegUpdateTask(Config config, Action<bool, string> updateFunc)
{
Task.Run(() => UpdateTaskRunSubscription(config, updateFunc));
Task.Run(() => UpdateTaskRunGeo(config, updateFunc));
}
private async Task UpdateTaskRunSubscription(Config config, Action<bool, string> updateFunc)
{
await Task.Delay(60000);
Logging.SaveLog("UpdateTaskRunSubscription");
var updateHandle = new UpdateService();
while (true)
{
var updateTime = ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds();
var lstSubs = (await AppHandler.Instance.SubItems())
.Where(t => t.AutoUpdateInterval > 0)
.Where(t => updateTime - t.UpdateTime >= t.AutoUpdateInterval * 60)
.ToList();
foreach (var item in lstSubs)
{
await updateHandle.UpdateSubscriptionProcess(config, item.Id, true, (bool success, string msg) =>
{
updateFunc?.Invoke(success, msg);
if (success)
Logging.SaveLog("subscription" + msg);
});
item.UpdateTime = updateTime;
await ConfigHandler.AddSubItem(config, item);
await Task.Delay(5000);
}
await Task.Delay(60000);
}
}
private async Task UpdateTaskRunGeo(Config config, Action<bool, string> updateFunc)
{
var autoUpdateGeoTime = DateTime.Now;
//await Task.Delay(1000 * 120);
Logging.SaveLog("UpdateTaskRunGeo");
var updateHandle = new UpdateService();
while (true)
{
await Task.Delay(1000 * 3600);
var dtNow = DateTime.Now;
if (config.GuiItem.AutoUpdateInterval > 0)
{
if ((dtNow - autoUpdateGeoTime).Hours % config.GuiItem.AutoUpdateInterval == 0)
{
await updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
{
updateFunc?.Invoke(false, msg);
});
autoUpdateGeoTime = dtNow;
}
}
}
}
}
}

View File

@@ -0,0 +1,179 @@
using System.Net;
using WebDav;
namespace ServiceLib.Handler
{
public sealed class WebDavHandler
{
private static readonly Lazy<WebDavHandler> _instance = new(() => new());
public static WebDavHandler Instance => _instance.Value;
private Config? _config;
private WebDavClient? _client;
private string? _lastDescription;
private string _webDir = Global.AppName + "_backup";
private readonly string _webFileName = "backup.zip";
private string _logTitle = "WebDav--";
public WebDavHandler()
{
_config = AppHandler.Instance.Config;
}
private async Task<bool> GetClient()
{
try
{
if (_config.WebDavItem.Url.IsNullOrEmpty()
|| _config.WebDavItem.UserName.IsNullOrEmpty()
|| _config.WebDavItem.Password.IsNullOrEmpty())
{
throw new ArgumentException("webdav parameter error or null");
}
if (_client != null)
{
_client?.Dispose();
_client = null;
}
if (_config.WebDavItem.DirName.IsNullOrEmpty())
{
_webDir = Global.AppName + "_backup";
}
else
{
_webDir = _config.WebDavItem.DirName.TrimEx();
}
var clientParams = new WebDavClientParams
{
BaseAddress = new Uri(_config.WebDavItem.Url),
Credentials = new NetworkCredential(_config.WebDavItem.UserName, _config.WebDavItem.Password)
};
_client = new WebDavClient(clientParams);
}
catch (Exception ex)
{
SaveLog(ex);
return false;
}
return await Task.FromResult(true);
}
private async Task<bool> TryCreateDir()
{
if (_client is null) return false;
try
{
var result2 = await _client.Mkcol(_webDir);
if (result2.IsSuccessful)
{
return true;
}
SaveLog(result2.Description);
}
catch (Exception ex)
{
SaveLog(ex);
}
return false;
}
private void SaveLog(string desc)
{
_lastDescription = desc;
Logging.SaveLog(_logTitle + desc);
}
private void SaveLog(Exception ex)
{
_lastDescription = ex.Message;
Logging.SaveLog(_logTitle, ex);
}
public async Task<bool> CheckConnection()
{
if (await GetClient() == false)
{
return false;
}
await TryCreateDir();
try
{
var testName = "readme_test";
var myContent = new StringContent(testName);
var result = await _client.PutFile($"{_webDir}/{testName}", myContent);
if (result.IsSuccessful)
{
await _client.Delete($"{_webDir}/{testName}");
return true;
}
else
{
SaveLog(result.Description);
}
}
catch (Exception ex)
{
SaveLog(ex);
}
return false;
}
public async Task<bool> PutFile(string fileName)
{
if (await GetClient() == false)
{
return false;
}
await TryCreateDir();
try
{
await using var fs = File.OpenRead(fileName);
var result = await _client.PutFile($"{_webDir}/{_webFileName}", fs); // upload a resource
if (result.IsSuccessful)
{
return true;
}
SaveLog(result.Description);
}
catch (Exception ex)
{
SaveLog(ex);
}
return false;
}
public async Task<bool> GetRawFile(string fileName)
{
if (await GetClient() == false)
{
return false;
}
await TryCreateDir();
try
{
var response = await _client.GetRawFile($"{_webDir}/{_webFileName}");
if (!response.IsSuccessful)
{
SaveLog(response.Description);
return false;
}
await using var outputFileStream = new FileStream(fileName, FileMode.Create);
await response.Stream.CopyToAsync(outputFileStream);
return true;
}
catch (Exception ex)
{
SaveLog(ex);
}
return false;
}
public string GetLastError() => _lastDescription ?? string.Empty;
}
}

View File

@@ -0,0 +1,11 @@
namespace ServiceLib.Models
{
public class CheckUpdateModel
{
public bool? IsSelected { get; set; }
public string? CoreType { get; set; }
public string? Remarks { get; set; }
public string? FileName { get; set; }
public bool? IsFinished { get; set; }
}
}

View File

@@ -0,0 +1,17 @@
namespace ServiceLib.Models
{
public class ClashConnectionModel
{
public string? Id { get; set; }
public string? Network { get; set; }
public string? Type { get; set; }
public string? Host { get; set; }
public ulong Upload { get; set; }
public ulong Download { get; set; }
public string? UploadTraffic { get; set; }
public string? DownloadTraffic { get; set; }
public double Time { get; set; }
public string? Elapsed { get; set; }
public string? Chain { get; set; }
}
}

View File

@@ -0,0 +1,37 @@
namespace ServiceLib.Models
{
public class ClashConnections
{
public ulong downloadTotal { get; set; }
public ulong uploadTotal { get; set; }
public List<ConnectionItem>? connections { get; set; }
}
public class ConnectionItem
{
public string? id { get; set; }
public MetadataItem? metadata { get; set; }
public ulong upload { get; set; }
public ulong download { get; set; }
public DateTime start { get; set; }
public List<string>? chains { get; set; }
public string? rule { get; set; }
public string? rulePayload { get; set; }
}
public class MetadataItem
{
public string? network { get; set; }
public string? type { get; set; }
public string? sourceIP { get; set; }
public string? destinationIP { get; set; }
public string? sourcePort { get; set; }
public string? destinationPort { get; set; }
public string? host { get; set; }
public string? nsMode { get; set; }
public object uid { get; set; }
public string? process { get; set; }
public string? processPath { get; set; }
public string? remoteDestination { get; set; }
}
}

View File

@@ -0,0 +1,17 @@
using static ServiceLib.Models.ClashProxies;
namespace ServiceLib.Models
{
public class ClashProviders
{
public Dictionary<string, ProvidersItem>? providers { get; set; }
public class ProvidersItem
{
public string? name { get; set; }
public List<ProxiesItem>? proxies { get; set; }
public string? type { get; set; }
public string? vehicleType { get; set; }
}
}
}

View File

@@ -0,0 +1,24 @@
namespace ServiceLib.Models
{
public class ClashProxies
{
public Dictionary<string, ProxiesItem>? proxies { get; set; }
public class ProxiesItem
{
public List<string>? all { get; set; }
public List<HistoryItem>? history { get; set; }
public string? name { get; set; }
public string? type { get; set; }
public bool udp { get; set; }
public string? now { get; set; }
public int delay { get; set; }
}
public class HistoryItem
{
public string? time { get; set; }
public int delay { get; set; }
}
}
}

View File

@@ -0,0 +1,18 @@
namespace ServiceLib.Models
{
[Serializable]
public class ClashProxyModel
{
public string? Name { get; set; }
public string? Type { get; set; }
public string? Now { get; set; }
public int Delay { get; set; }
public string? DelayName { get; set; }
public bool IsActive { get; set; }
}
}

View File

@@ -0,0 +1,8 @@
namespace ServiceLib.Models
{
public class CmdItem
{
public string? Cmd { get; set; }
public List<string>? Arguments { get; set; }
}
}

View File

@@ -1,14 +1,15 @@
namespace v2rayN.Mode
namespace ServiceLib.Models
{
public class ComboItem
{
public string ID
public string? ID
{
get; set;
}
public string Text
public string? Text
{
get; set;
}
}
}
}

View File

@@ -0,0 +1,56 @@
namespace ServiceLib.Models
{
/// <summary>
/// 本软件配置文件实体类
/// </summary>
[Serializable]
public class Config
{
#region property
public string IndexId { get; set; }
public string SubIndexId { get; set; }
public ECoreType RunningCoreType { get; set; }
public bool IsRunningCore(ECoreType type)
{
switch (type)
{
case ECoreType.Xray when RunningCoreType is ECoreType.Xray or ECoreType.v2fly or ECoreType.v2fly_v5:
case ECoreType.sing_box when RunningCoreType is ECoreType.sing_box or ECoreType.mihomo:
return true;
default:
return false;
}
}
#endregion property
#region other entities
public CoreBasicItem CoreBasicItem { get; set; }
public TunModeItem TunModeItem { get; set; }
public KcpItem KcpItem { get; set; }
public GrpcItem GrpcItem { get; set; }
public RoutingBasicItem RoutingBasicItem { get; set; }
public GUIItem GuiItem { get; set; }
public MsgUIItem MsgUIItem { get; set; }
public UIItem UiItem { get; set; }
public ConstItem ConstItem { get; set; }
public SpeedTestItem SpeedTestItem { get; set; }
public Mux4RayItem Mux4RayItem { get; set; }
public Mux4SboxItem Mux4SboxItem { get; set; }
public HysteriaItem HysteriaItem { get; set; }
public ClashUIItem ClashUIItem { get; set; }
public SystemProxyItem SystemProxyItem { get; set; }
public WebDavItem WebDavItem { get; set; }
public CheckUpdateItem CheckUpdateItem { get; set; }
public List<InItem> Inbound { get; set; }
public List<KeyEventItem> GlobalHotkeys { get; set; }
public List<CoreTypeItem> CoreTypeItem { get; set; }
#endregion other entities
}
}

View File

@@ -0,0 +1,254 @@
namespace ServiceLib.Models
{
[Serializable]
public class CoreBasicItem
{
public bool LogEnabled { get; set; }
public string Loglevel { get; set; }
public bool MuxEnabled { get; set; }
public bool DefAllowInsecure { get; set; }
public string DefFingerprint { get; set; }
public string DefUserAgent { get; set; }
public bool EnableFragment { get; set; }
public bool EnableCacheFile4Sbox { get; set; } = true;
}
[Serializable]
public class InItem
{
public int LocalPort { get; set; }
public string Protocol { get; set; }
public bool UdpEnabled { get; set; }
public bool SniffingEnabled { get; set; } = true;
public List<string>? DestOverride { get; set; } = ["http", "tls"];
public bool RouteOnly { get; set; }
public bool AllowLANConn { get; set; }
public bool NewPort4LAN { get; set; }
public string User { get; set; }
public string Pass { get; set; }
}
[Serializable]
public class KcpItem
{
public int Mtu { get; set; }
public int Tti { get; set; }
public int UplinkCapacity { get; set; }
public int DownlinkCapacity { get; set; }
public bool Congestion { get; set; }
public int ReadBufferSize { get; set; }
public int WriteBufferSize { get; set; }
}
[Serializable]
public class GrpcItem
{
public int? IdleTimeout { get; set; }
public int? HealthCheckTimeout { get; set; }
public bool? PermitWithoutStream { get; set; }
public int? InitialWindowsSize { get; set; }
}
[Serializable]
public class GUIItem
{
public bool AutoRun { get; set; }
public bool EnableStatistics { get; set; }
public bool KeepOlderDedupl { get; set; }
public bool IgnoreGeoUpdateCore { get; set; } = true;
public int AutoUpdateInterval { get; set; }
public bool EnableSecurityProtocolTls13 { get; set; }
public int TrayMenuServersLimit { get; set; } = 20;
public bool EnableHWA { get; set; } = false;
public bool EnableLog { get; set; } = true;
}
[Serializable]
public class MsgUIItem
{
public string? MainMsgFilter { get; set; }
public bool? AutoRefresh { get; set; }
}
[Serializable]
public class UIItem
{
public bool EnableAutoAdjustMainLvColWidth { get; set; }
public bool EnableUpdateSubOnlyRemarksExist { get; set; }
public double MainWidth { get; set; }
public double MainHeight { get; set; }
public double MainGirdHeight1 { get; set; }
public double MainGirdHeight2 { get; set; }
public EGirdOrientation MainGirdOrientation { get; set; } = EGirdOrientation.Vertical;
public bool ColorModeDark { get; set; }
public bool FollowSystemTheme { get; set; }
public string? ColorPrimaryName { get; set; }
public string CurrentLanguage { get; set; }
public string CurrentFontFamily { get; set; }
public int CurrentFontSize { get; set; }
public bool EnableDragDropSort { get; set; }
public bool DoubleClick2Activate { get; set; }
public bool AutoHideStartup { get; set; }
public bool Hide2TrayWhenClose { get; set; }
public List<ColumnItem> MainColumnItem { get; set; }
public bool ShowInTaskbar { get; set; }
}
[Serializable]
public class ConstItem
{
public string DefIEProxyExceptions { get; set; }
public string SubConvertUrl { get; set; } = string.Empty;
public string? GeoSourceUrl { get; set; }
public string? SrsSourceUrl { get; set; }
public string? RouteRulesTemplateSourceUrl { get; set; }
}
[Serializable]
public class KeyEventItem
{
public EGlobalHotkey EGlobalHotkey { get; set; }
public bool Alt { get; set; }
public bool Control { get; set; }
public bool Shift { get; set; }
public int? KeyCode { get; set; }
}
[Serializable]
public class CoreTypeItem
{
public EConfigType ConfigType { get; set; }
public ECoreType CoreType { get; set; }
}
[Serializable]
public class TunModeItem
{
public bool EnableTun { get; set; }
public bool StrictRoute { get; set; } = true;
public string Stack { get; set; }
public int Mtu { get; set; }
public bool EnableExInbound { get; set; }
public bool EnableIPv6Address { get; set; }
public string? LinuxSudoPwd { get; set; }
}
[Serializable]
public class SpeedTestItem
{
public int SpeedTestTimeout { get; set; }
public string SpeedTestUrl { get; set; }
public string SpeedPingTestUrl { get; set; }
public int SpeedTestPageSize { get; set; }
}
[Serializable]
public class RoutingBasicItem
{
public string DomainStrategy { get; set; }
public string DomainStrategy4Singbox { get; set; }
public string DomainMatcher { get; set; }
public string RoutingIndexId { get; set; }
}
[Serializable]
public class ColumnItem
{
public string Name { get; set; }
public int Width { get; set; }
public int Index { get; set; }
}
[Serializable]
public class Mux4RayItem
{
public int? Concurrency { get; set; }
public int? XudpConcurrency { get; set; }
public string? XudpProxyUDP443 { get; set; }
}
[Serializable]
public class Mux4SboxItem
{
public string Protocol { get; set; }
public int MaxConnections { get; set; }
public bool? Padding { get; set; }
}
[Serializable]
public class HysteriaItem
{
public int UpMbps { get; set; }
public int DownMbps { get; set; }
}
[Serializable]
public class ClashUIItem
{
public ERuleMode RuleMode { get; set; }
public bool EnableIPv6 { get; set; }
public bool EnableMixinContent { get; set; }
public int ProxiesSorting { get; set; }
public bool ProxiesAutoRefresh { get; set; }
public int ProxiesAutoDelayTestInterval { get; set; } = 10;
public bool ConnectionsAutoRefresh { get; set; }
public int ConnectionsRefreshInterval { get; set; } = 2;
}
[Serializable]
public class SystemProxyItem
{
public ESysProxyType SysProxyType { get; set; }
public string SystemProxyExceptions { get; set; }
public bool NotProxyLocalAddress { get; set; } = true;
public string SystemProxyAdvancedProtocol { get; set; }
}
[Serializable]
public class WebDavItem
{
public string? Url { get; set; }
public string? UserName { get; set; }
public string? Password { get; set; }
public string? DirName { get; set; }
}
[Serializable]
public class CheckUpdateItem
{
public bool CheckPreReleaseUpdate { get; set; }
public List<string>? SelectedCoreTypes { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
namespace ServiceLib.Models
{
[Serializable]
public class CoreInfo
{
public ECoreType CoreType { get; set; }
public List<string>? CoreExes { get; set; }
public string? Arguments { get; set; }
public string? Url { get; set; }
public string? ReleaseApiUrl { get; set; }
public string? DownloadUrlWin64 { get; set; }
public string? DownloadUrlWinArm64 { get; set; }
public string? DownloadUrlLinux64 { get; set; }
public string? DownloadUrlLinuxArm64 { get; set; }
public string? DownloadUrlOSX64 { get; set; }
public string? DownloadUrlOSXArm64 { get; set; }
public string? Match { get; set; }
public string? VersionArg { get; set; }
public bool RedirectInfo { get; set; }
}
}

View File

@@ -0,0 +1,20 @@
using SQLite;
namespace ServiceLib.Models
{
[Serializable]
public class DNSItem
{
[PrimaryKey]
public string Id { get; set; }
public string Remarks { get; set; }
public bool Enabled { get; set; } = true;
public ECoreType CoreType { get; set; }
public bool UseSystemHosts { get; set; }
public string? NormalDNS { get; set; }
public string? TunDNS { get; set; }
public string? DomainStrategy4Freedom { get; set; }
public string? DomainDNSAddress { get; set; }
}
}

View File

@@ -0,0 +1,68 @@
using System.Text.Json.Serialization;
namespace ServiceLib.Models
{
public class GitHubReleaseAsset
{
[JsonPropertyName("url")] public string? Url { get; set; }
[JsonPropertyName("id")] public int Id { get; set; }
[JsonPropertyName("node_id")] public string? NodeId { get; set; }
[JsonPropertyName("name")] public string? Name { get; set; }
[JsonPropertyName("label")] public object Label { get; set; }
[JsonPropertyName("content_type")] public string? ContentType { get; set; }
[JsonPropertyName("state")] public string? State { get; set; }
[JsonPropertyName("size")] public int Size { get; set; }
[JsonPropertyName("download_count")] public int DownloadCount { get; set; }
[JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; }
[JsonPropertyName("updated_at")] public DateTime UpdatedAt { get; set; }
[JsonPropertyName("browser_download_url")] public string? BrowserDownloadUrl { get; set; }
}
public class GitHubRelease
{
[JsonPropertyName("url")] public string? Url { get; set; }
[JsonPropertyName("assets_url")] public string? AssetsUrl { get; set; }
[JsonPropertyName("upload_url")] public string? UploadUrl { get; set; }
[JsonPropertyName("html_url")] public string? HtmlUrl { get; set; }
[JsonPropertyName("id")] public int Id { get; set; }
[JsonPropertyName("node_id")] public string? NodeId { get; set; }
[JsonPropertyName("tag_name")] public string? TagName { get; set; }
[JsonPropertyName("target_commitish")] public string? TargetCommitish { get; set; }
[JsonPropertyName("name")] public string? Name { get; set; }
[JsonPropertyName("draft")] public bool Draft { get; set; }
[JsonPropertyName("prerelease")] public bool Prerelease { get; set; }
[JsonPropertyName("created_at")] public DateTime CreatedAt { get; set; }
[JsonPropertyName("published_at")] public DateTime PublishedAt { get; set; }
[JsonPropertyName("assets")] public List<GitHubReleaseAsset> Assets { get; set; }
[JsonPropertyName("tarball_url")] public string? TarballUrl { get; set; }
[JsonPropertyName("zipball_url")] public string? ZipballUrl { get; set; }
[JsonPropertyName("body")] public string? Body { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
namespace ServiceLib.Models
{
internal class IPAPIInfo
{
public string? ip { get; set; }
public string? city { get; set; }
public string? region { get; set; }
public string? region_code { get; set; }
public string? country { get; set; }
public string? country_name { get; set; }
public string? country_code { get; set; }
}
}

View File

@@ -0,0 +1,15 @@
using SQLite;
namespace ServiceLib.Models
{
[Serializable]
public class ProfileExItem
{
[PrimaryKey]
public string IndexId { get; set; }
public int Delay { get; set; }
public decimal Speed { get; set; }
public int Sort { get; set; }
}
}

View File

@@ -0,0 +1,96 @@
using SQLite;
namespace ServiceLib.Models
{
[Serializable]
public class ProfileItem
{
public ProfileItem()
{
IndexId = string.Empty;
ConfigType = EConfigType.VMess;
ConfigVersion = 2;
Address = string.Empty;
Port = 0;
Id = string.Empty;
AlterId = 0;
Security = string.Empty;
Network = string.Empty;
Remarks = string.Empty;
HeaderType = string.Empty;
RequestHost = string.Empty;
Path = string.Empty;
StreamSecurity = string.Empty;
AllowInsecure = string.Empty;
Subid = string.Empty;
Flow = string.Empty;
}
#region function
public string GetSummary()
{
var summary = $"[{(ConfigType).ToString()}] ";
var arrAddr = Address.Split('.');
var addr = arrAddr.Length switch
{
> 2 => $"{arrAddr.First()}***{arrAddr.Last()}",
> 1 => $"***{arrAddr.Last()}",
_ => Address
};
summary += ConfigType switch
{
EConfigType.Custom => $"[{CoreType.ToString()}]{Remarks}",
_ => $"{Remarks}({addr}:{Port})"
};
return summary;
}
public List<string>? GetAlpn()
{
return Utils.IsNullOrEmpty(Alpn) ? null : Utils.String2List(Alpn);
}
public string GetNetwork()
{
if (Utils.IsNullOrEmpty(Network) || !Global.Networks.Contains(Network))
{
return Global.DefaultNetwork;
}
return Network.TrimEx();
}
#endregion function
[PrimaryKey]
public string IndexId { get; set; }
public EConfigType ConfigType { get; set; }
public int ConfigVersion { get; set; }
public string Address { get; set; }
public int Port { get; set; }
public string Id { get; set; }
public int AlterId { get; set; }
public string Security { get; set; }
public string Network { get; set; }
public string Remarks { get; set; }
public string HeaderType { get; set; }
public string RequestHost { get; set; }
public string Path { get; set; }
public string StreamSecurity { get; set; }
public string AllowInsecure { get; set; }
public string Subid { get; set; }
public bool IsSub { get; set; } = true;
public string Flow { get; set; }
public string Sni { get; set; }
public string Alpn { get; set; } = string.Empty;
public ECoreType? CoreType { get; set; }
public int? PreSocksPort { get; set; }
public string Fingerprint { get; set; }
public bool DisplayLog { get; set; } = true;
public string PublicKey { get; set; }
public string ShortId { get; set; }
public string SpiderX { get; set; }
public string Extra { get; set; }
}
}

View File

@@ -0,0 +1,18 @@
namespace ServiceLib.Models
{
[Serializable]
public class ProfileItemModel : ProfileItem
{
public bool IsActive { get; set; }
public string SubRemarks { get; set; }
public int Delay { get; set; }
public decimal Speed { get; set; }
public int Sort { get; set; }
public string DelayVal { get; set; }
public string SpeedVal { get; set; }
public string TodayUp { get; set; }
public string TodayDown { get; set; }
public string TotalUp { get; set; }
public string TotalDown { get; set; }
}
}

Some files were not shown because too many files have changed in this diff Show More