Compare commits

...

439 Commits

Author SHA1 Message Date
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
345 changed files with 58567 additions and 24563 deletions
+10
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
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
+2 -2
View File
@@ -28,7 +28,7 @@ jobs:
- name: Build
run: cd v2rayN &&
.\build.ps1
./build.ps1
# - name: Package
# shell: pwsh
@@ -40,7 +40,7 @@ jobs:
with:
name: v2rayN
path: |
.\v2rayN\v2rayN.zip
./v2rayN/v2rayN.zip
# - name: Release
# uses: softprops/action-gh-release@v1
+12 -6
View File
@@ -1,5 +1,5 @@
# v2rayN
A GUI client for Windows, support [Xray core](https://github.com/XTLS/Xray-core) and [v2fly core](https://github.com/v2fly/v2ray-core) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
A GUI client for Windows and Linux, support [Xray core](https://github.com/XTLS/Xray-core) 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)
@@ -9,13 +9,19 @@ A GUI client for Windows, support [Xray core](https://github.com/XTLS/Xray-core)
## How to use
- If you are new to this, please download v2rayN-With-Core.zip from [releases](https://github.com/2dust/v2rayN/releases)
- Otherwise please download v2rayN.zip (you will also need to download cores in the bin directory)
- Run v2rayN.exe
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+
```
## Requirements
- (6.35 and above)[Microsoft .NET 8.0 Desktop Runtime ](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)
- (6.33 and below)[Microsoft .NET 6.0 Desktop Runtime ](https://dotnet.microsoft.com/en-us/download/dotnet/6.0)
- [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)
+27
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>
+22
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
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);
}
}
}
}
@@ -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>
@@ -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>
+138
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";
}
}
-102
View File
@@ -1,102 +0,0 @@
using System;
using System.IO;
using System.Net.Sockets;
using System.Text;
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(async () =>
{
while (_isRunning)
{
try
{
if (!_tcpListener.Pending())
{
await Task.Delay(10);
continue;
}
var client = _tcpListener.AcceptTcpClient();
await 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)
{
}
}
}
}
-20
View File
@@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</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>
-95
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
-20
View File
@@ -1,20 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Protobuf Include="Statistics.proto" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.26.1" />
<PackageReference Include="Grpc.Net.Client" Version="2.62.0" />
<PackageReference Include="Grpc.Tools" Version="2.62.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>
-53
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 {}
-13
View File
@@ -1,13 +0,0 @@
using ProtosLib.Statistics;
namespace ProtosLib
{
public class Tests
{
private StatsService.StatsServiceClient client_;
public Tests()
{
}
}
}
@@ -0,0 +1,10 @@
using ReactiveUI;
namespace ServiceLib.Base
{
public class MyReactiveObject : ReactiveObject
{
protected static Config? _config;
protected Func<EViewAction, object?, Task<bool>>? _updateView;
}
}
+101
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;
}
}
}
+75
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");
}
}
}
@@ -1,10 +1,9 @@
using Downloader;
using System.IO;
using System.Net;
namespace v2rayN
namespace ServiceLib.Common
{
internal class DownloaderHelper
public class DownloaderHelper
{
private static readonly Lazy<DownloaderHelper> _instance = new(() => new());
public static DownloaderHelper Instance => _instance.Value;
@@ -19,7 +18,7 @@ namespace v2rayN
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));
}
@@ -37,7 +36,7 @@ namespace v2rayN
}
};
using var downloader = new DownloadService(downloadOpt);
await using var downloader = new Downloader.DownloadService(downloadOpt);
downloader.DownloadFileCompleted += (sender, value) =>
{
if (value.Error != null)
@@ -47,12 +46,12 @@ namespace v2rayN
};
using var cts = new CancellationTokenSource();
using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
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)
@@ -73,11 +72,11 @@ namespace v2rayN
}
};
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)
@@ -87,7 +86,7 @@ namespace v2rayN
//};
downloader.DownloadProgressChanged += (sender, value) =>
{
TimeSpan ts = (DateTime.Now - totalDatetime);
var ts = (DateTime.Now - totalDatetime);
if (progress != null && ts.Seconds > totalSecond)
{
hasValue = true;
@@ -113,7 +112,7 @@ namespace v2rayN
//progress.Report("......");
using var cts = new CancellationTokenSource();
cts.CancelAfter(timeout * 1000);
using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token);
await using var stream = await downloader.DownloadFileTaskAsync(address: url, cts.Token);
downloadOpt = null;
}
@@ -146,7 +145,7 @@ namespace v2rayN
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);
@@ -169,11 +168,15 @@ namespace v2rayN
{
progress.Report(101);
}
else if (value.Error != null)
{
throw value.Error;
}
}
};
using var cts = new CancellationTokenSource();
await downloader.DownloadFileTaskAsync(url, fileName, cts.Token).WaitAsync(TimeSpan.FromSeconds(timeout), cts.Token);
await downloader.DownloadFileTaskAsync(url, fileName, cts.Token);
downloadOpt = null;
}
+198
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);
}
}
}
}
}
@@ -1,9 +1,8 @@
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Text;
namespace v2rayN
namespace ServiceLib.Common
{
/// <summary>
/// </summary>
@@ -21,6 +20,22 @@ namespace v2rayN
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 (Utils.IsNullOrEmpty(url)) return null;
@@ -41,6 +56,21 @@ namespace v2rayN
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);
@@ -54,8 +84,8 @@ namespace v2rayN
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;
@@ -68,7 +98,7 @@ namespace v2rayN
totalRead += read;
if (read == 0) break;
file.Write(buffer, 0, read);
await file.WriteAsync(buffer.AsMemory(0, read), token);
if (canReportProgress)
{
@@ -103,13 +133,13 @@ namespace v2rayN
//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
{
@@ -138,7 +168,7 @@ namespace v2rayN
totalRead += read;
TimeSpan ts = (DateTime.Now - totalDatetime);
var ts = (DateTime.Now - totalDatetime);
if (progress != null && ts.Seconds > totalSecond)
{
totalSecond = ts.Seconds;
@@ -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;
@@ -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;
@@ -1,11 +1,10 @@
using System.IO;
using System.Text.Json;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace v2rayN
namespace ServiceLib.Common
{
internal class JsonUtils
public class JsonUtils
{
/// <summary>
/// DeepCopy
@@ -32,7 +31,11 @@ namespace v2rayN
{
return default;
}
return JsonSerializer.Deserialize<T>(strJson);
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
return JsonSerializer.Deserialize<T>(strJson, options);
}
catch
{
@@ -70,7 +73,7 @@ namespace v2rayN
/// <returns></returns>
public static string Serialize(object? obj, bool indented = true)
{
string result = string.Empty;
var result = string.Empty;
try
{
if (obj == null)
@@ -105,12 +108,15 @@ namespace v2rayN
/// <param name="filePath"></param>
/// <param name="nullValue"></param>
/// <returns></returns>
public static int ToFile(object? obj, string filePath, bool nullValue = true)
public static int ToFile(object? obj, string? filePath, bool nullValue = true)
{
int result;
if (filePath is null)
{
return -1;
}
try
{
using FileStream file = File.Create(filePath);
using var file = File.Create(filePath);
var options = new JsonSerializerOptions
{
@@ -119,14 +125,13 @@ namespace v2rayN
};
JsonSerializer.Serialize(file, obj, options);
result = 0;
return 0;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
result = -1;
return -1;
}
return result;
}
}
}
@@ -1,9 +1,8 @@
using NLog;
using NLog.Config;
using NLog.Targets;
using System.IO;
namespace v2rayN
namespace ServiceLib.Common
{
public class Logging
{
@@ -38,40 +37,41 @@ namespace v2rayN
foreach (var filePath in files)
{
var file = new FileInfo(filePath);
if (file.CreationTime < now)
if (file.CreationTime >= now) continue;
try
{
try
{
file.Delete();
}
catch { }
file.Delete();
}
catch
{
// ignored
}
}
}
catch { }
catch
{
// ignored
}
});
}
public static void SaveLog(string strContent)
{
if (LogManager.IsLoggingEnabled())
{
var logger = LogManager.GetLogger("Log1");
logger.Info(strContent);
}
if (!LogManager.IsLoggingEnabled()) return;
LogManager.GetLogger("Log1").Info(strContent);
}
public static void SaveLog(string strTitle, Exception ex)
{
if (LogManager.IsLoggingEnabled())
if (!LogManager.IsLoggingEnabled()) return;
var logger = LogManager.GetLogger("Log2");
logger.Debug($"{strTitle},{ex.Message}");
logger.Debug(ex.StackTrace);
if (ex?.InnerException != null)
{
var logger = LogManager.GetLogger("Log2");
logger.Debug($"{strTitle},{ex.Message}");
logger.Debug(ex.StackTrace);
if (ex?.InnerException != null)
{
logger.Error(ex.InnerException);
}
logger.Error(ex.InnerException);
}
}
}
+90
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;
}
}
}
@@ -1,7 +1,7 @@
using System.Linq.Expressions;
using System.Reflection;
namespace v2rayN
namespace ServiceLib.Common
{
public static class QueryableExtension
{
@@ -17,7 +17,7 @@ namespace v2rayN
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);
@@ -1,4 +1,4 @@
namespace v2rayN
namespace ServiceLib.Common
{
public class SemanticVersion
{
@@ -15,19 +15,27 @@
this.version = $"{major}.{minor}.{patch}";
}
public SemanticVersion(string version)
public SemanticVersion(string? version)
{
this.version = version.RemovePrefix('v');
try
{
string[] parts = this.version.Split('.');
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[0]);
this.minor = int.Parse(parts[1]);
this.patch = 0;
}
else if (parts.Length == 3)
else if (parts.Length is 3 or 4)
{
this.major = int.Parse(parts[0]);
this.minor = int.Parse(parts[1]);
@@ -43,7 +51,6 @@
this.major = 0;
this.minor = 0;
this.patch = 0;
//this.version = "0.0.0";
}
}
@@ -1,7 +1,7 @@
using SQLite;
using System.Collections;
namespace v2rayN
namespace ServiceLib.Common
{
public sealed class SQLiteHelper
{
@@ -10,8 +10,7 @@ namespace v2rayN
private string _connstr;
private SQLiteConnection _db;
private SQLiteAsyncConnection _dbAsync;
private static readonly object objLock = new();
public readonly string _configDB = "guiNDB.db";
private readonly string _configDB = "guiNDB.db";
public SQLiteHelper()
{
@@ -25,17 +24,9 @@ namespace v2rayN
return _db.CreateTable<T>();
}
public int Insert(object model)
public async Task<int> InsertAllAsync(IEnumerable models)
{
return _db.Insert(model);
}
public int InsertAll(IEnumerable models)
{
lock (objLock)
{
return _db.InsertAll(models);
}
return await _dbAsync.InsertAllAsync(models);
}
public async Task<int> InsertAsync(object model)
@@ -43,46 +34,19 @@ namespace v2rayN
return await _dbAsync.InsertAsync(model);
}
public int Replace(object model)
{
lock (objLock)
{
return _db.InsertOrReplace(model);
}
}
public async Task<int> ReplaceAsync(object model)
{
return await _dbAsync.InsertOrReplaceAsync(model);
}
public int Update(object model)
{
lock (objLock)
{
return _db.Update(model);
}
}
public async Task<int> UpdateAsync(object model)
{
return await _dbAsync.UpdateAsync(model);
}
public int UpdateAll(IEnumerable models)
public async Task<int> UpdateAllAsync(IEnumerable models)
{
lock (objLock)
{
return _db.UpdateAll(models);
}
}
public int Delete(object model)
{
lock (objLock)
{
return _db.Delete(model);
}
return await _dbAsync.UpdateAllAsync(models);
}
public async Task<int> DeleteAsync(object model)
@@ -90,19 +54,9 @@ namespace v2rayN
return await _dbAsync.DeleteAsync(model);
}
public List<T> Query<T>(string sql) where T : new()
public async Task<int> DeleteAllAsync<T>()
{
return _db.Query<T>(sql);
}
public async Task<List<T>> QueryAsync<T>(string sql) where T : new()
{
return await _dbAsync.QueryAsync<T>(sql);
}
public int Execute(string sql)
{
return _db.Execute(sql);
return await _dbAsync.DeleteAllAsync<T>();
}
public async Task<int> ExecuteAsync(string sql)
@@ -110,9 +64,9 @@ namespace v2rayN
return await _dbAsync.ExecuteAsync(sql);
}
public TableQuery<T> Table<T>() where T : new()
public async Task<List<T>> QueryAsync<T>(string sql) where T : new()
{
return _db.Table<T>();
return await _dbAsync.QueryAsync<T>(sql);
}
public AsyncTableQuery<T> TableAsync<T>() where T : new()
@@ -1,9 +1,8 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
namespace v2rayN
namespace ServiceLib.Common
{
internal static class StringEx
public static class StringEx
{
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
{
@@ -15,27 +14,25 @@ namespace v2rayN
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[0]);
}
public static bool IsWhiteSpace(this string value)
private static bool IsWhiteSpace(this string value)
{
foreach (char c in value)
{
if (char.IsWhiteSpace(c)) continue;
return false;
}
return true;
return value.All(char.IsWhiteSpace);
}
public static IEnumerable<string> NonWhiteSpaceLines(this TextReader reader)
{
string? line;
while ((line = reader.ReadLine()) != null)
while (reader.ReadLine() is { } line)
{
if (line.IsWhiteSpace()) continue;
yield return line;
@@ -49,26 +46,12 @@ namespace v2rayN
public static string RemovePrefix(this string value, char prefix)
{
if (value.StartsWith(prefix))
{
return value.Substring(1);
}
else
{
return value;
}
return value.StartsWith(prefix) ? value[1..] : value;
}
public static string RemovePrefix(this string value, string prefix)
{
if (value.StartsWith(prefix))
{
return value.Substring(prefix.Length);
}
else
{
return value;
}
return value.StartsWith(prefix) ? value[prefix.Length..] : value;
}
public static string UpperFirstChar(this string value)
@@ -78,17 +61,12 @@ namespace v2rayN
return string.Empty;
}
return char.ToUpper(value[0]) + value.Substring(1);
return char.ToUpper(value[0]) + value[1..];
}
public static string AppendQuotes(this string value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
return $"\"{value}\"";
return string.IsNullOrEmpty(value) ? string.Empty : $"\"{value}\"";
}
}
}
+875
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[0]);
var val = Uri.UnescapeDataString(keyValue[1]);
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[1], hostItem[0]);
}
}
}
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
}
}
+52
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();
}
}
}
}
+81
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
}
}
@@ -1,15 +1,16 @@
namespace v2rayN.Models
namespace ServiceLib.Enums
{
public enum EConfigType
{
VMess = 1,
Custom = 2,
Shadowsocks = 3,
Socks = 4,
SOCKS = 4,
VLESS = 5,
Trojan = 6,
Hysteria2 = 7,
Tuic = 8,
Wireguard = 9
TUIC = 8,
WireGuard = 9,
HTTP = 10
}
}
@@ -1,14 +1,13 @@
namespace v2rayN.Models
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,
@@ -0,0 +1,9 @@
namespace ServiceLib.Enums
{
public enum EGirdOrientation
{
Horizontal,
Vertical,
Tab,
}
}
@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace ServiceLib.Enums
{
public enum EGlobalHotkey
{
@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace ServiceLib.Enums
{
public enum EInboundProtocol
{
@@ -8,6 +8,7 @@
http2,
pac,
api,
api2,
speedtest = 21
}
}
@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace ServiceLib.Enums
{
public enum EMove
{
+11
View File
@@ -0,0 +1,11 @@
namespace ServiceLib.Enums
{
public enum EMsgCommand
{
ClearMsg,
SendMsgView,
SendSnackMsg,
RefreshProfiles,
StopSpeedtest
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace ServiceLib.Enums
{
public enum EPresetType
{
Default = 0,
Russia = 1,
}
}
+10
View File
@@ -0,0 +1,10 @@
namespace ServiceLib.Enums
{
public enum ERuleMode
{
Rule = 0,
Global = 1,
Direct = 2,
Unchanged = 3
}
}
+21
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
}
}
@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace ServiceLib.Enums
{
public enum ESpeedActionType
{
@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace ServiceLib.Enums
{
public enum ESysProxyType
{
@@ -1,4 +1,4 @@
namespace v2rayN.Models
namespace ServiceLib.Enums
{
public enum ETransport
{
@@ -6,6 +6,7 @@
kcp,
ws,
httpupgrade,
xhttp,
h2,
http,
quic,
+46
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,
}
}
+3
View File
@@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ReactiveUI />
</Weavers>
@@ -1,19 +1,15 @@
using v2rayN.Models;
namespace v2rayN
namespace ServiceLib
{
internal class Global
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 SagerNetCoreUrl = "https://github.com/SagerNet/v2ray-core/releases";
public const string NUrl = @"https://github.com/2dust/v2rayN/releases";
public const string ClashCoreUrl = "https://github.com/Dreamacro/clash/releases";
public const string ClashMetaCoreUrl = "https://github.com/MetaCubeX/Clash.Meta/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";
@@ -23,25 +19,35 @@ namespace v2rayN
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 V2raySampleClient = "v2rayN.Sample.SampleClientConfig";
public const string SingboxSampleClient = "v2rayN.Sample.SingboxSampleClientConfig";
public const string V2raySampleHttpRequestFileName = "v2rayN.Sample.SampleHttpRequest";
public const string V2raySampleHttpResponseFileName = "v2rayN.Sample.SampleHttpResponse";
public const string V2raySampleInbound = "v2rayN.Sample.SampleInbound";
public const string V2raySampleOutbound = "v2rayN.Sample.SampleOutbound";
public const string SingboxSampleOutbound = "v2rayN.Sample.SingboxSampleOutbound";
public const string CustomRoutingFileName = "v2rayN.Sample.custom_routing_";
public const string TunSingboxDNSFileName = "v2rayN.Sample.tun_singbox_dns";
public const string TunSingboxInboundFileName = "v2rayN.Sample.tun_singbox_inbound";
public const string TunSingboxRulesFileName = "v2rayN.Sample.tun_singbox_rules";
public const string DNSV2rayNormalFileName = "v2rayN.Sample.dns_v2ray_normal";
public const string DNSSingboxNormalFileName = "v2rayN.Sample.dns_singbox_normal";
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";
@@ -56,6 +62,7 @@ namespace v2rayN
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";
@@ -66,9 +73,6 @@ namespace v2rayN
public const string GrpcGunMode = "gun";
public const string GrpcMultiMode = "multi";
public const int MaxPort = 65536;
public const string CommandClearMsg = "CommandClearMsg";
public const string CommandSendMsgView = "CommandSendMsgView";
public const string CommandStopSpeedTest = "CommandStopSpeedTest";
public const string DelayUnit = "";
public const string SpeedUnit = "";
public const int MinFontSize = 10;
@@ -103,18 +107,38 @@ namespace v2rayN
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",
@"http://cachefly.cachefly.net/50mb.test",
@"http://cachefly.cachefly.net/100mb.test",
@"http://cachefly.cachefly.net/10mb.test"
@"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" },
@@ -130,55 +154,65 @@ namespace v2rayN
{
{EConfigType.VMess,"vmess://"},
{EConfigType.Shadowsocks,"ss://"},
{EConfigType.Socks,"socks://"},
{EConfigType.SOCKS,"socks://"},
{EConfigType.VLESS,"vless://"},
{EConfigType.Trojan,"trojan://"},
{EConfigType.Hysteria2,"hysteria2://"},
{EConfigType.Tuic,"tuic://"},
{EConfigType.Wireguard,"wireguard://"}
{EConfigType.TUIC,"tuic://"},
{EConfigType.WireGuard,"wireguard://"}
};
public static readonly Dictionary<EConfigType, string> ProtocolTypes = new()
{
{EConfigType.VMess,"vmess"},
{EConfigType.Shadowsocks,"shadowsocks"},
{EConfigType.Socks,"socks"},
{EConfigType.SOCKS,"socks"},
{EConfigType.HTTP,"http"},
{EConfigType.VLESS,"vless"},
{EConfigType.Trojan,"trojan"},
{EConfigType.Hysteria2,"hysteria2"},
{EConfigType.Tuic,"tuic"},
{EConfigType.Wireguard,"wireguard"}
{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> SsSecuritiesInSagerNet = new() { "none", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305", "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", "xchacha20-ietf-poly1305", "rc4", "rc4-md5", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-cfb8", "aes-192-cfb8", "aes-256-cfb8", "aes-128-ofb", "aes-192-ofb", "aes-256-ofb", "bf-cfb", "cast5-cfb", "des-cfb", "idea-cfb", "rc2-cfb", "seed-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", "camellia-128-cfb8", "camellia-192-cfb8", "camellia-256-cfb8", "salsa20", "chacha20", "chacha20-ietf", "xchacha20" };
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", "h2", "quic", "grpc" };
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() { "v2fly", "SagerNet", "Xray", "sing_box" };
public static readonly List<string> CoreTypes = new() { "v2fly", "Xray", "sing_box" };
public static readonly List<string> CoreTypes4VLESS = 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" };
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,http/1.1", "h3,h2", "h2,http/1.1", "" };
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", "http", "socks2", "http2" };
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
}
}
+11
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;
+274
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;
}
}
public bool IsAdministrator
{
get
{
_isAdministrator ??= Utils.IsAdministrator();
return _isAdministrator.Value;
}
}
#endregion Property
#region Init
public bool InitApp()
{
_config = ConfigHandler.LoadConfig();
if (_config == null)
{
return false;
}
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.Setup();
Logging.LoggingEnabled(_config.GuiItem.EnableLog);
Logging.SaveLog($"v2rayN start up | {Utils.GetVersion()} | {Utils.GetExePath()}");
Logging.SaveLog($"{Environment.OSVersion} - {(Environment.Is64BitOperatingSystem ? 64 : 32)}");
Logging.ClearLogs();
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;
}
if (_config.CoreTypeItem == null)
{
return ECoreType.Xray;
}
var item = _config.CoreTypeItem.FirstOrDefault(it => it.ConfigType == eConfigType);
if (item == null)
{
return ECoreType.Xray;
}
return item.CoreType;
}
#endregion Core Type
}
}
@@ -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
}
}
@@ -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
@@ -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;
}
}
}
+432
View File
@@ -0,0 +1,432 @@
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)
{
await KillProcess(_process);
_process.Dispose();
_process = null;
}
if (_processPre != null)
{
await KillProcess(_processPre);
_processPre.Dispose();
_processPre = null;
}
if (_linuxSudoPid > 0)
{
await KillProcessAsLinuxSudo();
}
_linuxSudoPid = -1;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
}
}
public async Task CoreStopPid(int pid)
{
try
{
var _p = Process.GetProcessById(pid);
await KillProcess(_p);
}
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 = _config.RunningCoreType = 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 KillProcess(Process? proc)
{
if (proc is null)
{
return;
}
try
{
proc?.Kill();
}
catch (Exception)
{
// ignored
}
await Task.Delay(100);
if (proc?.HasExited == false)
{
try
{
proc?.Kill();
}
catch (Exception)
{
// ignored
}
}
}
#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 -9 {_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())
{
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);
}
var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
await proc.WaitForExitAsync(timeout.Token);
await Task.Delay(1000);
}
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
{
//Shell scripts
var shFilePath = Utils.GetBinPath(fileName);
File.Delete(shFilePath);
var sb = new StringBuilder();
sb.AppendLine("#!/bin/sh");
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
}
}
@@ -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()}";
}
}
}
+239
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}";
}
}
}
+23
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;
}
}
}
@@ -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;
}
}
}
}
@@ -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;
}
}
}
@@ -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;
}
}
}
@@ -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[0];
item.Id = Utils.UrlDecode(userInfoParts[1]);
}
else
{
// parse base64 UserInfo
string userInfo = Utils.Base64Decode(rawUserInfo);
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2)
{
return null;
}
item.Security = userInfoParts[0];
item.Id = userInfoParts[1];
}
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;
}
}
}
@@ -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;
}
}
}
+119
View File
@@ -0,0 +1,119 @@
namespace ServiceLib.Handler.Fmt
{
public class SocksFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
ProfileItem? item;
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;
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
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
int 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
int indexS = result.IndexOf("@");
if (indexS > 0)
{
}
else
{
result = Utils.Base64Decode(result);
}
string[] arr1 = result.Split('@');
if (arr1.Length != 2)
{
return null;
}
string[] arr21 = arr1[0].Split(':');
//string[] arr22 = arr1[1].Split(':');
int indexPort = arr1[1].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[0];
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(new[] { ':' }, 2);
if (userInfoParts.Length == 2)
{
item.Security = userInfoParts[0];
item.Id = userInfoParts[1];
}
return item;
}
}
}
@@ -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);
}
}
}
+59
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[0];
item.Security = userInfoParts[1];
}
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);
}
}
}
+56
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;
}
}
}
+55
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);
}
}
}
+122
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;
}
}
}
@@ -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);
}
}
}
@@ -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);
}
}
}
+105
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
}
}
}
}
@@ -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);
}
}
}
@@ -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;
}
}
}
}
@@ -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;
}
}
}
@@ -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;
}
}
}
@@ -1,10 +1,38 @@
using System.Runtime.InteropServices;
using static v2rayN.Handler.ProxySetting.InternetConnectionOption;
using System.Runtime.InteropServices;
using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption;
namespace v2rayN.Handler
namespace ServiceLib.Handler.SysProxy
{
internal class ProxySetting
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>
@@ -29,15 +57,24 @@ namespace v2rayN.Handler
/// <returns>true: one of connection is successfully updated proxy settings</returns>
public static bool SetProxy(string? strProxy, string? exceptions, int type)
{
// 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)
try
{
result |= SetConnectionProxy(connection, strProxy, exceptions, type);
// 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;
}
return result;
}
private static bool SetConnectionProxy(string? connectionName, string? strProxy, string? exceptions, int type)
@@ -51,7 +88,7 @@ namespace v2rayN.Handler
}
else if (type is 2 or 4) // named proxy or autoproxy script URL
{
optionCount = Utils.IsNullOrEmpty(exceptions) ? 2 : 3;
optionCount = string.IsNullOrEmpty(exceptions) ? 2 : 3;
}
int m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT;
@@ -94,25 +131,25 @@ namespace v2rayN.Handler
}
else
{
list.szConnection = IntPtr.Zero;
list.szConnection = nint.Zero;
}
list.dwOptionCount = options.Length;
list.dwOptionError = 0;
int optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
// make a pointer out of all that ...
IntPtr optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); // !! remember to deallocate memory 4
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)
{
IntPtr opt = new(optionsPtr.ToInt64() + (i * optSize));
nint opt = new(optionsPtr.ToInt64() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
else
{
IntPtr opt = new(optionsPtr.ToInt32() + (i * optSize));
nint opt = new(optionsPtr.ToInt32() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
}
@@ -120,11 +157,11 @@ namespace v2rayN.Handler
list.options = optionsPtr;
// and then make a pointer out of the whole list
IntPtr ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5
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(IntPtr.Zero,
bool isSuccess = NativeMethods.InternetSetOption(nint.Zero,
InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
ipcoListPtr, list.dwSize);
int returnvalue = 0; // ERROR_SUCCESS
@@ -135,12 +172,12 @@ namespace v2rayN.Handler
else
{
// Notify the system that the registry settings have been changed and cause them to be refreshed
NativeMethods.InternetSetOption(IntPtr.Zero, InternetOption.INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0);
NativeMethods.InternetSetOption(IntPtr.Zero, InternetOption.INTERNET_OPTION_REFRESH, IntPtr.Zero, 0);
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 != IntPtr.Zero) Marshal.FreeHGlobal(list.szConnection); // release mem 3
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
@@ -204,13 +241,13 @@ namespace v2rayN.Handler
public struct InternetPerConnOptionList
{
public int dwSize; // size of the INTERNET_PER_CONN_OPTION_LIST struct
public IntPtr szConnection; // connection name to set/query options
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 IntPtr options;
};
public nint options;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct InternetConnectionOption
@@ -236,7 +273,7 @@ namespace v2rayN.Handler
public int m_Int;
[FieldOffset(0)]
public IntPtr m_StringPtr;
public nint m_StringPtr;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
@@ -308,7 +345,7 @@ namespace v2rayN.Handler
{
[DllImport("WinInet.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InternetSetOption(IntPtr hInternet, InternetOption dwOption, IntPtr lpBuffer, int dwBufferLength);
public static extern bool InternetSetOption(nint hInternet, InternetOption dwOption, nint lpBuffer, int dwBufferLength);
[DllImport("Rasapi32.dll", CharSet = CharSet.Auto)]
public static extern uint RasEnumEntries(
@@ -0,0 +1,97 @@
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.http);
var portSocks = AppHandler.Instance.GetLocalPort(EInboundProtocol.socks);
if (port <= 0)
{
return false;
}
switch (type)
{
case ESysProxyType.ForcedChange when Utils.IsWindows():
{
GetWindowsProxyString(config, port, portSocks, 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, int portSocks, out string strProxy, out string strExceptions)
{
strExceptions = "";
if (config.SystemProxyItem.NotProxyLocalAddress)
{
strExceptions = $"<local>;{config.ConstItem.DefIEProxyExceptions};{config.SystemProxyItem.SystemProxyExceptions}";
}
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}", portSocks.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);
}
}
}
+72
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;
}
}
}
}
}
}
+179
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;
}
}
@@ -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; }
}
}
@@ -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; }
}
}
@@ -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; }
}
}
@@ -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; }
}
}
}
+24
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; }
}
}
}
@@ -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; }
}
}
+8
View File
@@ -0,0 +1,8 @@
namespace ServiceLib.Models
{
public class CmdItem
{
public string? Cmd { get; set; }
public List<string>? Arguments { get; set; }
}
}
@@ -1,13 +1,13 @@
namespace v2rayN.Models
namespace ServiceLib.Models
{
public class ComboItem
{
public string ID
public string? ID
{
get; set;
}
public string Text
public string? Text
{
get; set;
}
+56
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
}
}
+254
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; }
}
[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 int ConnectionsSorting { get; set; }
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; }
}
}
+21
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; }
}
}
+20
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; }
}
}
+68
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; }
}
}
+13
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; }
}
}
+15
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; }
}
}
+96
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; }
}
}
@@ -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; }
}
}
+27
View File
@@ -0,0 +1,27 @@
namespace ServiceLib.Models
{
public class RetResult
{
public bool Success { get; set; }
public string? Msg { get; set; }
public object? Data { get; set; }
public RetResult(bool success = false)
{
Success = success;
}
public RetResult(bool success, string? msg)
{
Success = success;
Msg = msg;
}
public RetResult(bool success, string? msg, object? data)
{
Success = success;
Msg = msg;
Data = data;
}
}
}

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