Compare commits

...

146 Commits

Author SHA1 Message Date
2dust
a46a4ad7c1 up 7.12.7 2025-06-19 11:59:20 +08:00
2dust
e46f680651 Optimize the UI for dns settings 2025-06-19 11:24:33 +08:00
2dust
93a20852f5 Optimize the UI for routing settings 2025-06-19 10:48:47 +08:00
2dust
298bb64e66 Routing rules do not determine whether it is version V3 2025-06-19 10:04:48 +08:00
2dust
0e5ac65f55 Improved network speed display when using clash api without proxy and direct 2025-06-16 15:06:18 +08:00
2dust
cb6122f872 First scroll horizontally to the initial position to avoid the control crash bug
https://github.com/2dust/v2rayN/issues/7387
2025-06-16 10:48:35 +08:00
2dust
06500e0218 When testing, start the core and then delay 1s before starting the test
https://github.com/2dust/v2rayN/issues/7391
2025-06-15 17:33:11 +08:00
DHR60
9ddf0b42e7 Fix Observatory (#7437)
* Enables concurrency for observatory

* Adds burst observatory for least load balancer
2025-06-15 14:41:47 +08:00
2dust
2056377f55 up 7.12.6 2025-06-15 14:39:02 +08:00
2dust
7065dabc94 If it is not in China area, no update is required
https://github.com/2dust/v2rayN/issues/7417
2025-06-15 14:35:59 +08:00
2dust
9e2336a71e The notification pop-up position is changed to the top right
https://github.com/2dust/v2rayN/issues/7421
2025-06-15 14:16:30 +08:00
2dust
d239c627f3 Use File.SetUnixFileMode to set the execute permission first. If that fails, use chmod to set the execute permission.
https://github.com/2dust/v2rayN/issues/7416
2025-06-15 12:33:21 +08:00
2dust
984b36d34e Update Directory.Packages.props 2025-06-15 12:33:02 +08:00
DHR60
c81bc2f536 Fix (#7405)
#7404
2025-06-08 09:30:08 +08:00
Miheichev Aleksandr Sergeevich
87f7e65076 docs: improve README.md formatting and readability (#7376)
- Split long line for better readability
- Add consistent spacing between sections
- Remove extra blank lines
- Update sing-box link to point to main repo instead of releases
2025-06-02 09:52:49 +08:00
duolaameng
9985b68b6b Update LICENSE (#7327)
完善版权信息:项目名称、时间、作者
2025-05-28 20:04:12 +08:00
Miheichev Aleksandr Sergeevich
fb4b8b2789 Revision of the Russian translation (#7342)
* Revision of the Russian translation

* Fix: remove duplicate TbSettingsSocksPortTip and refine three Russian strings

* refactor: improve Russian translations and fix punctuation

- Standardized punctuation in tooltips and messages (replaced semicolons with commas where appropriate)
- Improved consistency in server type labels (moved square brackets in "[TUIC] server" and similar)
- Enhanced clarity in "Speed Ping Test URL" to "URL for real ping test"
- Simplified "NeedRebootTips" message
- Fixed minor grammatical and formatting issues in various UI strings
- Removed duplicate "TbSettingsStartBootTip" entry
- Improved tooltip for port settings
2025-05-28 20:03:50 +08:00
Yuri Tukhachevsky
3812ccc780 Add support for DDE and MATE (#7353)
* add support for UKUI

* Add support for DDE and MATE
2025-05-28 09:05:55 +08:00
Yuri Tukhachevsky
608a6c387a add support for UKUI (#7348) 2025-05-26 13:48:31 +08:00
2dust
4875b37f70 up 7.12.5 2025-05-25 19:12:06 +08:00
2dust
2f3fba73de Bug fix
https://github.com/2dust/v2rayN/issues/7333
2025-05-25 19:11:42 +08:00
2dust
2ab1b9068f up 7.12.4 2025-05-21 20:00:41 +08:00
DHR60
b9613875ce Determine .exe suffix based on OS in GenRoutingDirectExe (#7322)
* Determine .exe suffix based on OS in GenRoutingDirectExe

* Uses Utils.GetExeName
2025-05-21 19:57:23 +08:00
2dust
5d2aea6b4f Change the inbound of the xray configuration file from socks to mixed 2025-05-17 14:51:18 +08:00
Pk-web6936
5824e18ed6 Update Persian translate (#7273) 2025-05-15 19:21:53 +08:00
2dust
4f8648cbc9 Fix
https://github.com/2dust/v2rayN/issues/7270
2025-05-12 20:29:34 +08:00
2dust
01b021b2c3 Bug fix
https://github.com/2dust/v2rayN/issues/7279
2025-05-12 20:28:28 +08:00
2dust
331e8ce960 up 7.12.3 2025-05-11 19:19:26 +08:00
2dust
a2cfe6fa51 Added the current connection information test url option
https://github.com/2dust/v2rayN/discussions/7268
2025-05-11 16:59:00 +08:00
2dust
8381fefb78 up 7.12.2 2025-05-11 10:39:02 +08:00
2dust
d3b95d781a Removed the function of displaying the current connection IP
https://github.com/2dust/v2rayN/discussions/7268
2025-05-11 10:38:48 +08:00
2dust
3a4a96f87a Fix
https://github.com/2dust/v2rayN/issues/7258
2025-05-09 14:33:41 +08:00
2dust
3d462c4be3 Fix
https://github.com/2dust/v2rayN/issues/7247
2025-05-08 15:56:52 +08:00
2dust
82b366cd9b Fix
https://github.com/2dust/v2rayN/issues/7244
2025-05-07 14:28:55 +08:00
DHR60
897a4e5635 Move exe direct rule before clash_mode (#7236) 2025-05-05 13:59:14 +08:00
2dust
8ea76fd318 up 7.12.1 2025-05-04 17:44:36 +08:00
2dust
693a96fff2 Update Directory.Packages.props 2025-05-04 17:44:04 +08:00
DHR60
8b4e2f8f23 Fix DNS (#7233)
* Fix DNS

* Removes expectIPs from remote DNS
2025-05-04 17:28:57 +08:00
2dust
13b164acac Enhanced sorting function, can sort by statistics 2025-05-03 11:29:36 +08:00
2dust
e590547b30 Revert "Bug fix"
This reverts commit 5a0fdd971a.
2025-05-02 10:44:20 +08:00
2dust
5a0fdd971a Bug fix
https://github.com/2dust/v2rayN/issues/7211
2025-04-30 14:12:02 +08:00
2dust
514dce960a up 7.12.0 2025-04-28 15:57:09 +08:00
Reza Bakhshi Laktasaraei
6ee6fb1706 Improve Accessibility in StatusBarView and ProfilesView, Update Package Versions (#7199)
* Fix Tab navigation in ToolBar by setting KeyboardNavigation to Continue

* Improve accessibility for ComboBoxes in StatusBarView.xaml

Added ItemContainerStyle to cmbRoutings2 and cmbRoutings to bind AutomationProperties.Name to Remarks, ensuring screen readers announce the correct values instead of the default object type.

* Improve accessibility for cmbServers in StatusBarView.xaml

Added ItemContainerStyle to cmbServers to bind AutomationProperties.Name to Text, ensuring screen readers announce the correct values instead of the default object type.

* Update package versions and fix accessibility in ProfilesView.xaml

- Updated package versions in Directory.Packages.props:
  - Semi.Avalonia and Semi.Avalonia.DataGrid from 11.2.1.6 to 11.2.1.7.
  - ZXing.Net.Bindings.SkiaSharp from 0.16.14 to 0.16.21.
- Fixed MC3024 error in ProfilesView.xaml by creating AccessibleMyChipListBoxItem style:
  - Added AccessibleMyChipListBoxItem style based on MyChipListBoxItem to set AutomationProperties.Name.
  - Replaced ItemContainerStyle with AccessibleMyChipListBoxItem to preserve original appearance.
  - Updated AutomationProperties.Name to use resx:ResUI.menuSubscription for better localization.
  - Removed duplicate AutomationProperties.Name from TextBlock as it's now handled by the style.

* Update Directory.Packages.props

---------

Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
2025-04-28 15:25:57 +08:00
2dust
6985328653 Optimize and improve code 2025-04-28 15:16:58 +08:00
DHR60
41c406b84d Revert "Fix xray wireguard chained proxies not working (2dust#7113)" (#7194) 2025-04-27 09:37:46 +08:00
2dust
30f7f2c563 Update Directory.Packages.props 2025-04-26 10:32:20 +08:00
Reza Bakhshi Laktasaraei
be3dbfb8e3 Fix Tab navigation in ToolBar by setting KeyboardNavigation to Continue (#7185) 2025-04-26 10:31:00 +08:00
Pk-web6936
9b92259e80 Update Persian translation (#7191)
0032a3d27a
2025-04-26 10:30:05 +08:00
2dust
1c04144573 Code clean 2025-04-26 09:53:19 +08:00
2dust
adf3b955d6 Refactor Linux to run Tun as sudo 2025-04-26 09:50:31 +08:00
2dust
0032a3d27a Fix kill process for linux 2025-04-25 16:36:28 +08:00
2dust
c6d347d49a Refactor the Linux version to not store sudo password 2025-04-25 15:19:49 +08:00
2dust
ea42246d1b Improved AmazTool 2025-04-25 14:38:33 +08:00
2dust
3f19958c75 Bug fix
https://github.com/2dust/v2rayN/issues/7179
2025-04-24 11:50:24 +08:00
2dust
35788158bc up 7.11.3 2025-04-21 10:17:37 +08:00
2dust
4fd494ded4 Update Directory.Packages.props 2025-04-21 10:15:49 +08:00
2dust
23eeb8ff55 Try to fix
https://github.com/2dust/v2rayN/issues/7128
2025-04-18 12:31:46 +08:00
2dust
456ffb200a up 7.11.2 2025-04-15 10:54:55 +08:00
2dust
18e0bb194e Enhance Accessibility in MsgView 2025-04-15 09:57:04 +08:00
2dust
392f6111dd Update Directory.Packages.props 2025-04-14 11:17:57 +08:00
DHR60
ce6572af3d Fix xray wireguard chained proxies not working (#7113)
* Fix chained proxies not working

* Add domain field for WireGuard exit node
2025-04-12 19:03:34 +08:00
DHR60
cf59137481 fix dns leak (#7110) 2025-04-12 10:00:18 +08:00
DHR60
519e588124 fix #7105 (#7109) 2025-04-12 09:52:20 +08:00
Reza Bakhshi Laktasaraei
666c874998 Add accessibility labels to improve screen reader support (#7105)
* Add accessibility with AutomationProperties.Name to menus

* Add accessibility labels to StatusBarView using ResUI resources

* Add accessibility labels to ProfilesView using ResUI resources
2025-04-11 09:28:34 +08:00
DHR60
5f9f677467 Adjust menu items (#7100)
* Adjust menu items

* fix #7089
2025-04-11 09:27:34 +08:00
2dust
b06b5779dd up 7.11.1 2025-04-09 17:39:34 +08:00
2dust
e3a3b9c201 Improved language res 2025-04-09 17:39:23 +08:00
2dust
321ec30f39 Internationalized code comments 2025-04-09 16:48:38 +08:00
2dust
5adae2dd2a In Chinese and English, the keyword server is changed to Configuration 2025-04-09 16:21:25 +08:00
2dust
be5e15dfb6 Fix
https://github.com/2dust/v2rayN/pull/7089
2025-04-09 15:25:42 +08:00
DHR60
15d3418c79 add xray wireguard support (#7089)
* add xray wireguard support

* add wireguard core type settings

* Update OptionSettingWindow.axaml
2025-04-09 15:09:36 +08:00
2dust
0efb0228c6 Update Global.cs 2025-04-08 19:06:35 +08:00
DHR60
75b399b48b Updates profile remark based on core type (#7076) 2025-04-07 09:58:16 +08:00
2dust
24ccfb8077 up 7.11.0 2025-04-03 14:24:20 +08:00
2dust
204451db6c Bug fix
https://github.com/2dust/v2rayN/issues/7058
2025-04-03 14:22:12 +08:00
Pk-web6936
f553bbc41e Update Persian Translation (#7053) 2025-04-03 10:19:09 +08:00
2dust
8cb4f2f961 Adjusted the server configuration right-click menu 2025-04-02 15:53:28 +08:00
2dust
4d3db56065 csharp_style_namespace_declarations = file_scoped 2025-04-02 11:44:23 +08:00
NeonSweet
d92540121f Update proxy_set_linux_sh (#7042)
Co-authored-by: neonsweet <neonsweet@126.com>
2025-04-02 09:39:51 +08:00
2dust
17d586ea26 Update Directory.Packages.props 2025-03-31 15:05:07 +08:00
2dust
9a096d31fc Remove ads rules from default routing rules and DNS 2025-03-30 11:07:59 +08:00
2dust
bf83dbdfea Global setting ScrollViewer AllowAutoHide = False for desktop 2025-03-29 20:42:02 +08:00
2dust
e31cd0e199 Update Directory.Packages.props 2025-03-29 20:40:47 +08:00
2dust
1e11477e27 When add a new routing rule, add it to the top 2025-03-29 19:48:15 +08:00
2dust
e0750df96c Update v2rayN.sln 2025-03-29 19:40:32 +08:00
DHR60
e3580b05f7 add xray core leastPing support (#7023)
* add xray core leastPing support

* Refactor multi-server configuration UI logic

* Remove unused functions
2025-03-29 16:44:42 +08:00
patterniha
6ad0762731 set xray.location.cert to asset(bin) path (#7004)
* Update Global.cs

* Update CoreHandler.cs
2025-03-27 09:50:19 +08:00
2dust
70b05d7812 Update ResUI.fa-Ir.resx 2025-03-24 09:50:25 +08:00
Pk-web6936
5403fc9e21 Update ResUI.fa-Ir.resx (#6986)
Update Persian translate
2025-03-24 09:17:28 +08:00
dashi
5bffca9584 Update translate for ResUI.ru.resx (#6983)
It may contain spelling errors.
2025-03-24 09:17:11 +08:00
Pk-web6936
2060539c34 Update Persian translate (#6973)
Update Persian translate
2025-03-23 10:28:21 +08:00
2dust
de3cdb4f7e up Resx 2025-03-23 10:18:36 +08:00
2dust
2a4ba2a751 up Resx 2025-03-21 11:11:53 +08:00
dependabot[bot]
48747aabe0 Bump actions/upload-artifact from 4.6.1 to 4.6.2 (#6950)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.1 to 4.6.2.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4.6.1...v4.6.2)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-20 09:13:01 +08:00
2dust
7182be921d Update proxy_set_linux_sh
https://github.com/2dust/v2rayN/issues/6937
2025-03-19 10:04:06 +08:00
2dust
9d7dcd2c4f Update Directory.Packages.props 2025-03-19 10:03:55 +08:00
2dust
c3e56e84f1 Bug fix
https://github.com/2dust/v2rayN/issues/6932
2025-03-18 16:18:27 +08:00
dependabot[bot]
f1ef5a1f51 Bump actions/setup-dotnet from 4.3.0 to 4.3.1 (#6929)
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 4.3.0 to 4.3.1.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v4.3.0...v4.3.1)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-18 10:01:15 +08:00
2dust
d1e6898290 up 7.10.5 2025-03-17 11:07:31 +08:00
2dust
8597332b21 Fixed warnings 2025-03-17 11:07:01 +08:00
2dust
7cc42ae249 Simply check the file hash value and delete the old pac file 2025-03-17 11:05:04 +08:00
Wydy
e054d4487d Update pac (#6924) 2025-03-17 09:06:21 +08:00
2dust
6ef36f521d Optimize and improve code 2025-03-16 15:48:42 +08:00
2dust
a02a122dd1 Update proxy_set_linux_sh
https://github.com/2dust/v2rayN/issues/6886
2025-03-16 11:01:23 +08:00
2dust
701138617c Update Directory.Packages.props 2025-03-16 10:56:33 +08:00
Avery Lynn
d0e2cc9442 Fix server deduplicaiton failure (#6900)
- Add a local method AreEquel to compare string values, return TRUE when compare between 'null' and 'string.Emtpy'
2025-03-13 20:11:10 +08:00
DHR60
d561f10edc Modify default fallback load balancing rule (#6889)
* Modify default fallback load balancing rule

* Refine default fallback load balancing rule based on domain strategy
2025-03-12 18:36:51 +08:00
2dust
2df412476a If it is a Windows WinGet installation, the configuration file is stored in the user directory
https://github.com/2dust/v2rayN/issues/6803
2025-03-09 19:04:49 +08:00
2dust
e6011cfede AI-optimized code 2025-03-07 12:11:19 +08:00
2dust
bcf43e2928 Enhanced subscription customization configuration
https://github.com/2dust/v2rayN/issues/6875
2025-03-07 11:37:01 +08:00
2dust
3f0f895424 up 7.10.4 2025-03-06 17:35:10 +08:00
2dust
07a3bdc618 AI-optimized code 2025-03-06 16:36:39 +08:00
2dust
7e348c196e AI-optimized code 2025-03-06 14:53:25 +08:00
2dust
51e5885e76 AI-optimized code 2025-03-06 14:42:21 +08:00
2dust
50d7912f62 AI-optimized code 2025-03-06 14:34:31 +08:00
2dust
3869148fc8 AI-optimized code 2025-03-06 14:30:02 +08:00
2dust
a0af4fb30c Update DownloadService.cs
AI-optimized code
2025-03-06 14:08:38 +08:00
2dust
c374b8565b Update SpeedtestService.cs
AI-optimized code
2025-03-06 14:08:29 +08:00
2dust
7e8b405555 AI-optimized code 2025-03-06 12:21:21 +08:00
2dust
c3439c5abe AI-optimized code 2025-03-06 11:10:55 +08:00
2dust
d4a8787356 AI-optimized code 2025-03-06 10:57:39 +08:00
2dust
23b27575a0 AI-optimized code 2025-03-06 10:48:18 +08:00
2dust
8d8a887c42 Update HttpClientHelper.cs
AI-optimized code
2025-03-06 10:47:57 +08:00
2dust
1229c967ba Update SemanticVersion.cs
AI-optimized code
2025-03-06 10:47:31 +08:00
2dust
d35f65f86d Update Utils.cs
AI-optimized code
2025-03-06 10:47:06 +08:00
2dust
0a8ce0f961 Update ProxySettingWindows.cs
AI-optimized code
2025-03-06 10:46:50 +08:00
2dust
8092481d26 Update Job.cs
AI-optimized code
2025-03-06 10:46:41 +08:00
2dust
764014e49a Replace all Utils.ToInt(variable) with variable.ToInt() 2025-03-05 20:26:26 +08:00
2dust
71cc6d7a88 Replace all Utils.IsNullOrEmpty(variable) with variable.IsNullOrEmpty() 2025-03-05 19:44:49 +08:00
2dust
f3af831cf2 Replace all Utils.IsNotEmpty(variable) with variable.IsNotEmpty() 2025-03-05 16:42:43 +08:00
2dust
78fde575d7 up 7.10.3 2025-03-04 17:03:26 +08:00
2dust
6e5af34877 Revert "If the update fails during the upgrade, the update will be retried."
This reverts commit 9748fbb076.
2025-03-04 17:02:05 +08:00
2dust
8d1853e991 up 7.10.2 2025-03-04 15:11:05 +08:00
DHR60
859299c712 Add kcp DNS masquerade support (#6852)
* Add kcp DNS masquerade support

* Update V2rayConfig.cs

* Update CoreConfigV2rayService.cs

---------

Co-authored-by: 2dust <31833384+2dust@users.noreply.github.com>
2025-03-04 10:32:07 +08:00
2dust
7fbb0013b0 Optimizing Task Code 2025-03-03 16:57:55 +08:00
2dust
837cfbd03b Optimize UI prompts 2025-03-03 14:36:30 +08:00
2dust
cdc5d72cfa Update CoreConfigSingboxService.cs 2025-03-03 14:36:01 +08:00
DHR60
8dcf5c5b90 Add Hy2 Port hopping URI support (#6848) 2025-03-03 14:11:53 +08:00
2dust
67fe6ac3d8 Optimizing Task Code, add unified processing of scheduled tasks 2025-03-03 12:20:58 +08:00
2dust
438eaba4d5 up 7.10.1 2025-03-02 10:30:08 +08:00
2dust
3c8baa99d5 Update Directory.Packages.props 2025-03-02 10:29:43 +08:00
2dust
e70658f311 Add Hy2 Port hopping for sing-box 1.11+
https://github.com/2dust/v2rayN/issues/6772
2025-03-01 21:13:37 +08:00
2dust
2dd10cf5a1 Optimize QrcodeView 2025-03-01 19:56:52 +08:00
2dust
96781a784b git submodule update --remote 2025-03-01 15:29:54 +08:00
2dust
9748fbb076 If the update fails during the upgrade, the update will be retried. 2025-03-01 14:23:43 +08:00
2dust
aa5e4378ab Update AutoStartupHandler.cs 2025-03-01 14:14:07 +08:00
242 changed files with 26833 additions and 49335 deletions

View File

@@ -32,7 +32,7 @@ jobs:
fetch-depth: '0'
- name: Setup
uses: actions/setup-dotnet@v4.3.0
uses: actions/setup-dotnet@v4.3.1
with:
dotnet-version: '8.0.x'
@@ -45,7 +45,7 @@ jobs:
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts
uses: actions/upload-artifact@v4.6.1
uses: actions/upload-artifact@v4.6.2
with:
name: v2rayN-linux
path: |

View File

@@ -32,7 +32,7 @@ jobs:
fetch-depth: '0'
- name: Setup
uses: actions/setup-dotnet@v4.3.0
uses: actions/setup-dotnet@v4.3.1
with:
dotnet-version: '8.0.x'
@@ -45,7 +45,7 @@ jobs:
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts
uses: actions/upload-artifact@v4.6.1
uses: actions/upload-artifact@v4.6.2
with:
name: v2rayN-macos
path: |

View File

@@ -32,7 +32,7 @@ jobs:
fetch-depth: '0'
- name: Setup
uses: actions/setup-dotnet@v4.3.0
uses: actions/setup-dotnet@v4.3.1
with:
dotnet-version: '8.0.x'
@@ -45,7 +45,7 @@ jobs:
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts
uses: actions/upload-artifact@v4.6.1
uses: actions/upload-artifact@v4.6.2
with:
name: v2rayN-windows-desktop
path: |

View File

@@ -30,7 +30,7 @@ jobs:
uses: actions/checkout@v4.2.2
- name: Setup
uses: actions/setup-dotnet@v4.3.0
uses: actions/setup-dotnet@v4.3.1
with:
dotnet-version: '8.0.x'
@@ -46,7 +46,7 @@ jobs:
- name: Upload build artifacts
uses: actions/upload-artifact@v4.6.1
uses: actions/upload-artifact@v4.6.2
with:
name: v2rayN-windows
path: |

View File

@@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
Copyright (C) 2019-Present 2dust
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
v2rayN Copyright (C) 2019-Present 2dust
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.

View File

@@ -1,16 +1,18 @@
# v2rayN
A GUI client for Windows, Linux and macOS, support [Xray](https://github.com/XTLS/Xray-core) and [sing-box](https://github.com/SagerNet/sing-box/releases) and [others](https://github.com/2dust/v2rayN/wiki/List-of-supported-cores)
A GUI client for Windows, Linux and macOS, support [Xray](https://github.com/XTLS/Xray-core)
and [sing-box](https://github.com/SagerNet/sing-box)
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)
[![CodeFactor](https://www.codefactor.io/repository/github/2dust/v2rayn/badge)](https://www.codefactor.io/repository/github/2dust/v2rayn)
[![GitHub Releases](https://img.shields.io/github/downloads/2dust/v2rayN/latest/total?logo=github)](https://github.com/2dust/v2rayN/releases)
[![Chat on Telegram](https://img.shields.io/badge/Chat%20on-Telegram-brightgreen.svg)](https://t.me/v2rayn)
## How to use
Read the [Wiki](https://github.com/2dust/v2rayN/wiki) for details.
## Telegram Channel
[github_2dust](https://t.me/github_2dust)

View File

@@ -1,29 +1,87 @@
namespace AmazTool
{
namespace AmazTool;
internal static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
private static void Main(string[] args)
{
try
{
// If no arguments are provided, display usage guidelines and exit
if (args.Length == 0)
{
Console.WriteLine(Resx.Resource.Guidelines);
Thread.Sleep(5000);
ShowHelp();
return;
}
var argData = Uri.UnescapeDataString(string.Join(" ", args));
if (argData.Equals("rebootas"))
// Log all arguments for debugging purposes
foreach (var arg in args)
{
Console.WriteLine(arg);
}
// Parse command based on first argument
switch (args[0].ToLowerInvariant())
{
case "rebootas":
// Handle application restart
HandleRebootAsync();
break;
case "help":
case "--help":
case "-h":
case "/?":
// Display help information
ShowHelp();
break;
default:
// Default behavior: handle as upgrade data
// Maintain backward compatibility with existing usage pattern
var argData = Uri.UnescapeDataString(string.Join(" ", args));
HandleUpgrade(argData);
break;
}
}
catch (Exception ex)
{
// Global exception handling
Console.WriteLine($"An error occurred: {ex.Message}");
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}
/// <summary>
/// Display help information and usage guidelines
/// </summary>
private static void ShowHelp()
{
Console.WriteLine(Resx.Resource.Guidelines);
Console.WriteLine("Available commands:");
Console.WriteLine(" rebootas - Restart the application");
Console.WriteLine(" help - Display this help information");
Thread.Sleep(5000);
}
/// <summary>
/// Handle application restart
/// </summary>
private static void HandleRebootAsync()
{
Console.WriteLine("Restarting application...");
Thread.Sleep(1000);
Utils.StartV2RayN();
return;
}
UpgradeApp.Upgrade(argData);
}
/// <summary>
/// Handle application upgrade with the provided data
/// </summary>
/// <param name="upgradeData">Data for the upgrade process</param>
private static void HandleUpgrade(string upgradeData)
{
Console.WriteLine("Upgrading application...");
UpgradeApp.Upgrade(upgradeData);
}
}

View File

@@ -0,0 +1,156 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<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>正在重啟,請等待...</value>
</data>
<data name="Guidelines" xml:space="preserve">
<value>請從主應用程式運行。</value>
</data>
<data name="UpgradeFileNotFound" xml:space="preserve">
<value>升級失敗,檔案不存在。</value>
</data>
<data name="InProgress" xml:space="preserve">
<value>正在進行中,請等待...</value>
</data>
<data name="TryTerminateProcess" xml:space="preserve">
<value>嘗試結束 v2rayN 進程...</value>
</data>
<data name="FailedTerminateProcess" xml:space="preserve">
<value>請手動關閉正在執行的 v2rayN否則可能會升級失敗。</value>
</data>
<data name="StartUnzipping" xml:space="preserve">
<value>開始解壓縮更新包...</value>
</data>
<data name="SuccessUnzipping" xml:space="preserve">
<value>解壓縮更新包成功。</value>
</data>
<data name="FailedUnzipping" xml:space="preserve">
<value>解壓縮更新包失敗。</value>
</data>
<data name="FailedUpgrade" xml:space="preserve">
<value>升級失敗。</value>
</data>
<data name="SuccessUpgrade" xml:space="preserve">
<value>升級成功。</value>
</data>
<data name="Information" xml:space="preserve">
<value>提示</value>
</data>
</root>

View File

@@ -2,8 +2,8 @@ using System.Diagnostics;
using System.IO.Compression;
using System.Text;
namespace AmazTool
{
namespace AmazTool;
internal class UpgradeApp
{
public static void Upgrade(string fileName)
@@ -60,7 +60,10 @@ namespace AmazTool
var lst = entry.FullName.Split(splitKey);
if (lst.Length == 1)
{
continue;
}
var fullName = string.Join(splitKey, lst[1..lst.Length]);
if (string.Equals(Utils.GetExePath(), Utils.GetPath(fullName), StringComparison.OrdinalIgnoreCase))
@@ -75,7 +78,16 @@ namespace AmazTool
{
continue;
}
try
{
entry.ExtractToFile(entryOutputPath, true);
}
catch
{
Thread.Sleep(1000);
entry.ExtractToFile(entryOutputPath, true);
}
Console.WriteLine(entryOutputPath);
}
@@ -102,4 +114,3 @@ namespace AmazTool
Utils.StartV2RayN();
}
}
}

View File

@@ -1,7 +1,7 @@
using System.Diagnostics;
using System.Diagnostics;
namespace AmazTool;
namespace AmazTool
{
internal class Utils
{
public static string GetExePath()
@@ -16,7 +16,7 @@ namespace AmazTool
public static string GetPath(string fileName)
{
string startupPath = StartupPath();
var startupPath = StartupPath();
if (string.IsNullOrEmpty(fileName))
{
return startupPath;
@@ -49,4 +49,3 @@ namespace AmazTool
}
}
}
}

View File

@@ -1,14 +1,14 @@
<Project>
<PropertyGroup>
<Version>7.10.0</Version>
<Version>7.12.7</Version>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
<NoWarn>CA1031;CS1591;NU1507;CA1416</NoWarn>
<NoWarn>CA1031;CS1591;NU1507;CA1416;IDE0058</NoWarn>
<Nullable>annotations</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Authors>2dust</Authors>

View File

@@ -5,25 +5,25 @@
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.4" />
<PackageVersion Include="Avalonia.Desktop" Version="11.2.4" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.4" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.4" />
<PackageVersion Include="CliWrap" Version="3.8.1" />
<PackageVersion Include="Downloader" Version="3.3.3" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.2.0" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.1" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.1" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.1" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.1" />
<PackageVersion Include="CliWrap" Version="3.9.0" />
<PackageVersion Include="Downloader" Version="3.3.4" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />
<PackageVersion Include="MaterialDesignThemes" Version="5.2.1" />
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
<PackageVersion Include="QRCoder" Version="1.6.0" />
<PackageVersion Include="ReactiveUI" Version="20.1.63" />
<PackageVersion Include="ReactiveUI" Version="20.3.1" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="20.1.63" />
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.4" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.4" />
<PackageVersion Include="ReactiveUI.WPF" Version="20.3.1" />
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.8" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.8" />
<PackageVersion Include="Splat.NLog" Version="15.3.1" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.0" />
<PackageVersion Include="WebDav.Client" Version="2.8.0" />
<PackageVersion Include="TaskScheduler" Version="2.12.1" />
<PackageVersion Include="WebDav.Client" Version="2.9.0" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
</ItemGroup>

View File

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

View File

@@ -1,14 +1,14 @@
using System.Security.Cryptography;
using System.Text;
namespace ServiceLib.Common
{
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 byte[] Salt = Encoding.ASCII.GetBytes("saltysalt".PadRight(16, ' '));
private static readonly string DefaultPassword = Utils.GetMd5(Utils.GetHomePath() + "AesUtils");
/// <summary>
@@ -98,4 +98,3 @@ namespace ServiceLib.Common
return randomNumber;
}
}
}

View File

@@ -1,8 +1,8 @@
using System.Security.Cryptography;
using System.Text;
namespace ServiceLib.Common
{
namespace ServiceLib.Common;
public class DesUtils
{
/// <summary>
@@ -72,4 +72,3 @@ namespace ServiceLib.Common
return Utils.GetMd5(Utils.GetHomePath() + "DesUtils");
}
}
}

View File

@@ -1,8 +1,8 @@
using System.Net;
using Downloader;
namespace ServiceLib.Common
{
namespace ServiceLib.Common;
public class DownloaderHelper
{
private static readonly Lazy<DownloaderHelper> _instance = new(() => new());
@@ -10,7 +10,7 @@ namespace ServiceLib.Common
public async Task<string?> DownloadStringAsync(IWebProxy? webProxy, string url, string? userAgent, int timeout)
{
if (Utils.IsNullOrEmpty(url))
if (url.IsNullOrEmpty())
{
return null;
}
@@ -18,7 +18,7 @@ namespace ServiceLib.Common
Uri uri = new(url);
//Authorization Header
var headers = new WebHeaderCollection();
if (Utils.IsNotEmpty(uri.UserInfo))
if (uri.UserInfo.IsNotEmpty())
{
headers.Add(HttpRequestHeader.Authorization, "Basic " + Utils.Base64Encode(uri.UserInfo));
}
@@ -56,7 +56,7 @@ namespace ServiceLib.Common
public async Task DownloadDataAsync4Speed(IWebProxy webProxy, string url, IProgress<string> progress, int timeout)
{
if (Utils.IsNullOrEmpty(url))
if (url.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(url));
}
@@ -86,7 +86,7 @@ namespace ServiceLib.Common
//};
downloader.DownloadProgressChanged += (sender, value) =>
{
var ts = (DateTime.Now - totalDatetime);
var ts = DateTime.Now - totalDatetime;
if (progress != null && ts.Seconds > totalSecond)
{
hasValue = true;
@@ -119,11 +119,11 @@ namespace ServiceLib.Common
public async Task DownloadFileAsync(IWebProxy? webProxy, string url, string fileName, IProgress<double> progress, int timeout)
{
if (Utils.IsNullOrEmpty(url))
if (url.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(url));
}
if (Utils.IsNullOrEmpty(fileName))
if (fileName.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(fileName));
}
@@ -146,10 +146,7 @@ namespace ServiceLib.Common
var progressPercentage = 0;
var hasValue = false;
await using var downloader = new Downloader.DownloadService(downloadOpt);
downloader.DownloadStarted += (sender, value) =>
{
progress?.Report(0);
};
downloader.DownloadStarted += (sender, value) => progress?.Report(0);
downloader.DownloadProgressChanged += (sender, value) =>
{
hasValue = true;
@@ -181,4 +178,3 @@ namespace ServiceLib.Common
downloadOpt = null;
}
}
}

View File

@@ -1,9 +1,9 @@
using System.Formats.Tar;
using System.Formats.Tar;
using System.IO.Compression;
using System.Text;
namespace ServiceLib.Common
{
namespace ServiceLib.Common;
public static class FileManager
{
private static readonly string _tag = "FileManager";
@@ -99,7 +99,7 @@ namespace ServiceLib.Common
}
try
{
if (Utils.IsNotEmpty(ignoredName) && entry.Name.Contains(ignoredName))
if (ignoredName.IsNotEmpty() && entry.Name.Contains(ignoredName))
{
continue;
}
@@ -163,18 +163,20 @@ namespace ServiceLib.Common
// 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);
_ = 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))
if (ignoredName.IsNotEmpty() && file.Name.Contains(ignoredName))
{
continue;
}
@@ -187,7 +189,7 @@ namespace ServiceLib.Common
{
continue;
}
file.CopyTo(targetFilePath, overwrite);
_ = file.CopyTo(targetFilePath, overwrite);
}
// If recursive and copying subdirectories, recursively call this method
@@ -222,4 +224,3 @@ namespace ServiceLib.Common
}
}
}
}

View File

@@ -2,8 +2,8 @@ using System.Net.Http.Headers;
using System.Net.Mime;
using System.Text;
namespace ServiceLib.Common
{
namespace ServiceLib.Common;
/// <summary>
/// </summary>
public class HttpClientHelper
@@ -18,12 +18,17 @@ namespace ServiceLib.Common
public static HttpClientHelper Instance => _instance.Value;
private readonly HttpClient httpClient;
private HttpClientHelper(HttpClient httpClient) => this.httpClient = httpClient;
private HttpClientHelper(HttpClient httpClient)
{
this.httpClient = httpClient;
}
public async Task<string?> TryGetAsync(string url)
{
if (Utils.IsNullOrEmpty(url))
if (url.IsNullOrEmpty())
{
return null;
}
try
{
@@ -38,15 +43,19 @@ namespace ServiceLib.Common
public async Task<string?> GetAsync(string url)
{
if (Utils.IsNullOrEmpty(url))
if (url.IsNullOrEmpty())
{
return null;
}
return await httpClient.GetStringAsync(url);
}
public async Task<string?> GetAsync(HttpClient client, string url, CancellationToken token = default)
{
if (Utils.IsNullOrEmpty(url))
if (url.IsNullOrEmpty())
{
return null;
}
return await client.GetStringAsync(url, token);
}
@@ -55,13 +64,13 @@ namespace ServiceLib.Common
var jsonContent = JsonUtils.Serialize(headers);
var content = new StringContent(jsonContent, Encoding.UTF8, MediaTypeNames.Application.Json);
var result = await httpClient.PutAsync(url, content);
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 buffer = Encoding.UTF8.GetBytes(myContent);
var byteContent = new ByteArrayContent(buffer);
byteContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
@@ -78,12 +87,16 @@ namespace ServiceLib.Common
ArgumentNullException.ThrowIfNull(url);
ArgumentNullException.ThrowIfNull(fileName);
if (File.Exists(fileName))
{
File.Delete(fileName);
}
using var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token);
if (!response.IsSuccessStatusCode)
{
throw new Exception(response.StatusCode.ToString());
}
var total = response.Content.Headers.ContentLength ?? -1L;
var canReportProgress = total != -1 && progress != null;
@@ -102,7 +115,9 @@ namespace ServiceLib.Common
totalRead += read;
if (read == 0)
{
break;
}
await file.WriteAsync(buffer.AsMemory(0, read), token);
if (canReportProgress)
@@ -123,7 +138,7 @@ namespace ServiceLib.Common
public async Task DownloadDataAsync4Speed(HttpClient client, string url, IProgress<string> progress, CancellationToken token = default)
{
if (Utils.IsNullOrEmpty(url))
if (url.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(url));
}
@@ -173,7 +188,7 @@ namespace ServiceLib.Common
totalRead += read;
var ts = (DateTime.Now - totalDatetime);
var ts = DateTime.Now - totalDatetime;
if (progress != null && ts.Seconds > totalSecond)
{
totalSecond = ts.Seconds;
@@ -188,4 +203,3 @@ namespace ServiceLib.Common
} while (isMoreToRead);
}
}
}

View File

@@ -1,8 +1,7 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ServiceLib.Common
{
namespace ServiceLib.Common;
/*
* See:
* http://stackoverflow.com/questions/6266820/working-example-of-createjobobject-setinformationjobobject-pinvoke-in-net
@@ -15,28 +14,30 @@ namespace ServiceLib.Common
public Job()
{
handle = CreateJobObject(IntPtr.Zero, null);
IntPtr extendedInfoPtr = IntPtr.Zero;
JOBOBJECT_BASIC_LIMIT_INFORMATION info = new()
var extendedInfoPtr = IntPtr.Zero;
var info = new JOBOBJECT_BASIC_LIMIT_INFORMATION
{
LimitFlags = 0x2000
};
JOBOBJECT_EXTENDED_LIMIT_INFORMATION extendedInfo = new()
var extendedInfo = new JOBOBJECT_EXTENDED_LIMIT_INFORMATION
{
BasicLimitInformation = info
};
try
{
int length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
var length = Marshal.SizeOf(typeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
extendedInfoPtr = Marshal.AllocHGlobal(length);
Marshal.StructureToPtr(extendedInfo, extendedInfoPtr, false);
if (!SetInformationJobObject(handle, JobObjectInfoType.ExtendedLimitInformation, extendedInfoPtr,
(uint)length))
{
throw new Exception(string.Format("Unable to set information. Error: {0}",
Marshal.GetLastWin32Error()));
}
}
finally
{
if (extendedInfoPtr != IntPtr.Zero)
@@ -48,7 +49,7 @@ namespace ServiceLib.Common
public bool AddProcess(IntPtr processHandle)
{
bool succ = AssignProcessToJobObject(handle, processHandle);
var succ = AssignProcessToJobObject(handle, processHandle);
if (!succ)
{
@@ -76,7 +77,9 @@ namespace ServiceLib.Common
private void Dispose(bool disposing)
{
if (disposed)
{
return;
}
disposed = true;
if (disposing)
@@ -104,7 +107,7 @@ namespace ServiceLib.Common
private static extern IntPtr CreateJobObject(IntPtr a, string? lpName);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, UInt32 cbJobObjectInfoLength);
private static extern bool SetInformationJobObject(IntPtr hJob, JobObjectInfoType infoType, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool AssignProcessToJobObject(IntPtr job, IntPtr process);
@@ -121,34 +124,34 @@ namespace ServiceLib.Common
[StructLayout(LayoutKind.Sequential)]
internal struct IO_COUNTERS
{
public UInt64 ReadOperationCount;
public UInt64 WriteOperationCount;
public UInt64 OtherOperationCount;
public UInt64 ReadTransferCount;
public UInt64 WriteTransferCount;
public UInt64 OtherTransferCount;
public ulong ReadOperationCount;
public ulong WriteOperationCount;
public ulong OtherOperationCount;
public ulong ReadTransferCount;
public ulong WriteTransferCount;
public ulong OtherTransferCount;
}
[StructLayout(LayoutKind.Sequential)]
internal struct JOBOBJECT_BASIC_LIMIT_INFORMATION
{
public Int64 PerProcessUserTimeLimit;
public Int64 PerJobUserTimeLimit;
public UInt32 LimitFlags;
public long PerProcessUserTimeLimit;
public long PerJobUserTimeLimit;
public uint LimitFlags;
public UIntPtr MinimumWorkingSetSize;
public UIntPtr MaximumWorkingSetSize;
public UInt32 ActiveProcessLimit;
public uint ActiveProcessLimit;
public UIntPtr Affinity;
public UInt32 PriorityClass;
public UInt32 SchedulingClass;
public uint PriorityClass;
public uint SchedulingClass;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public UInt32 nLength;
public uint nLength;
public IntPtr lpSecurityDescriptor;
public Int32 bInheritHandle;
public int bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
@@ -174,4 +177,4 @@ namespace ServiceLib.Common
}
#endregion Helper classes
}

View File

@@ -1,9 +1,10 @@
using System.Text.Json;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace ServiceLib.Common
{
namespace ServiceLib.Common;
public class JsonUtils
{
private static readonly string _tag = "JsonUtils";
@@ -86,7 +87,8 @@ namespace ServiceLib.Common
var options = new JsonSerializerOptions
{
WriteIndented = indented,
DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull
DefaultIgnoreCondition = nullValue ? JsonIgnoreCondition.Never : JsonIgnoreCondition.WhenWritingNull,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
result = JsonSerializer.Serialize(obj, options);
}
@@ -128,4 +130,3 @@ namespace ServiceLib.Common
/// <returns></returns>
public static JsonNode? SerializeToNode(object? obj) => JsonSerializer.SerializeToNode(obj);
}
}

View File

@@ -2,10 +2,13 @@ using NLog;
using NLog.Config;
using NLog.Targets;
namespace ServiceLib.Common
{
namespace ServiceLib.Common;
public class Logging
{
private static readonly Logger _logger1 = LogManager.GetLogger("Log1");
private static readonly Logger _logger2 = LogManager.GetLogger("Log2");
public static void Setup()
{
LoggingConfiguration config = new();
@@ -28,23 +31,25 @@ namespace ServiceLib.Common
public static void SaveLog(string strContent)
{
if (!LogManager.IsLoggingEnabled())
{
return;
}
LogManager.GetLogger("Log1").Info(strContent);
_logger1.Info(strContent);
}
public static void SaveLog(string strTitle, Exception ex)
{
if (!LogManager.IsLoggingEnabled())
{
return;
}
var logger = LogManager.GetLogger("Log2");
logger.Debug($"{strTitle},{ex.Message}");
logger.Debug(ex.StackTrace);
_logger2.Debug($"{strTitle},{ex.Message}");
_logger2.Debug(ex.StackTrace);
if (ex?.InnerException != null)
{
logger.Error(ex.InnerException);
}
_logger2.Error(ex.InnerException);
}
}
}

View File

@@ -8,7 +8,7 @@ public static class ProcUtils
public static void ProcessStart(string? fileName, string arguments = "")
{
ProcessStart(fileName, arguments, null);
_ = ProcessStart(fileName, arguments, null);
}
public static int? ProcessStart(string? fileName, string arguments, string? dir)
@@ -38,7 +38,7 @@ public static class ProcUtils
WorkingDirectory = dir ?? string.Empty
}
};
proc.Start();
_ = proc.Start();
return dir is null ? null : proc.Id;
}
catch (Exception ex)
@@ -60,7 +60,7 @@ public static class ProcUtils
FileName = Utils.GetExePath().AppendQuotes(),
Verb = blAdmin ? "runas" : null,
};
Process.Start(startInfo);
_ = Process.Start(startInfo);
}
catch (Exception ex)
{
@@ -138,7 +138,9 @@ public static class ProcUtils
fileName = null;
processName = null;
if (!review)
{
return;
}
try
{
procId = proc?.Id;

View File

@@ -1,9 +1,9 @@
using QRCoder;
using QRCoder;
using SkiaSharp;
using ZXing.SkiaSharp;
namespace ServiceLib.Common
{
namespace ServiceLib.Common;
public class QRCodeHelper
{
public static byte[]? GenQRCode(string? url)
@@ -60,7 +60,7 @@ namespace ServiceLib.Common
var reader = new BarcodeReader();
var result = reader.Decode(bitmap);
if (result != null && Utils.IsNotEmpty(result.Text))
if (result != null && result.Text.IsNotEmpty())
{
return result.Text;
}
@@ -87,4 +87,3 @@ namespace ServiceLib.Common
return flipped;
}
}
}

View File

@@ -1,29 +1,29 @@
namespace ServiceLib.Common
{
namespace ServiceLib.Common;
public class SemanticVersion
{
private int major;
private int minor;
private int patch;
private string version;
private readonly int major;
private readonly int minor;
private readonly int patch;
private readonly string version;
public SemanticVersion(int major, int minor, int patch)
{
this.major = major;
this.minor = minor;
this.patch = patch;
this.version = $"{major}.{minor}.{patch}";
version = $"{major}.{minor}.{patch}";
}
public SemanticVersion(string? version)
{
try
{
if (version.IsNullOrEmpty())
if (string.IsNullOrEmpty(version))
{
this.major = 0;
this.minor = 0;
this.patch = 0;
major = 0;
minor = 0;
patch = 0;
return;
}
this.version = version.RemovePrefix('v');
@@ -31,15 +31,15 @@
var parts = this.version.Split('.');
if (parts.Length == 2)
{
this.major = int.Parse(parts.First());
this.minor = int.Parse(parts.Last());
this.patch = 0;
major = int.Parse(parts.First());
minor = int.Parse(parts.Last());
patch = 0;
}
else if (parts.Length is 3 or 4)
{
this.major = int.Parse(parts[0]);
this.minor = int.Parse(parts[1]);
this.patch = int.Parse(parts[2]);
major = int.Parse(parts[0]);
minor = int.Parse(parts[1]);
patch = int.Parse(parts[2]);
}
else
{
@@ -48,9 +48,9 @@
}
catch
{
this.major = 0;
this.minor = 0;
this.patch = 0;
major = 0;
minor = 0;
patch = 0;
}
}
@@ -58,7 +58,7 @@
{
if (obj is SemanticVersion other)
{
return this.major == other.major && this.minor == other.minor && this.patch == other.patch;
return major == other.major && minor == other.minor && patch == other.patch;
}
else
{
@@ -68,7 +68,7 @@
public override int GetHashCode()
{
return this.major.GetHashCode() ^ this.minor.GetHashCode() ^ this.patch.GetHashCode();
return major.GetHashCode() ^ minor.GetHashCode() ^ patch.GetHashCode();
}
/// <summary>
@@ -77,18 +77,18 @@
/// <returns>major.minor.patch</returns>
public override string ToString()
{
return this.version;
return version;
}
public string ToVersionString(string? prefix = null)
{
if (prefix == null)
{
return this.version;
return version;
}
else
{
return $"{prefix}{this.version}";
return $"{prefix}{version}";
}
}
@@ -108,31 +108,31 @@
private bool GreaterEquals(SemanticVersion other)
{
if (this.major < other.major)
if (major < other.major)
{
return false;
}
else if (this.major > other.major)
else if (major > other.major)
{
return true;
}
else
{
if (this.minor < other.minor)
if (minor < other.minor)
{
return false;
}
else if (this.minor > other.minor)
else if (minor > other.minor)
{
return true;
}
else
{
if (this.patch < other.patch)
if (patch < other.patch)
{
return false;
}
else if (this.patch > other.patch)
else if (patch > other.patch)
{
return true;
}
@@ -146,31 +146,31 @@
private bool LessEquals(SemanticVersion other)
{
if (this.major < other.major)
if (major < other.major)
{
return true;
}
else if (this.major > other.major)
else if (major > other.major)
{
return false;
}
else
{
if (this.minor < other.minor)
if (minor < other.minor)
{
return true;
}
else if (this.minor > other.minor)
else if (minor > other.minor)
{
return false;
}
else
{
if (this.patch < other.patch)
if (patch < other.patch)
{
return true;
}
else if (this.patch > other.patch)
else if (patch > other.patch)
{
return false;
}
@@ -184,4 +184,3 @@
#endregion Private
}
}

View File

@@ -1,13 +1,13 @@
using System.Collections;
using SQLite;
namespace ServiceLib.Common
{
namespace ServiceLib.Common;
public sealed class SQLiteHelper
{
private static readonly Lazy<SQLiteHelper> _instance = new(() => new());
public static SQLiteHelper Instance => _instance.Value;
private string _connstr;
private readonly string _connstr;
private SQLiteConnection _db;
private SQLiteAsyncConnection _dbAsync;
private readonly string _configDB = "guiNDB.db";
@@ -88,4 +88,3 @@ namespace ServiceLib.Common
});
}
}
}

View File

@@ -1,12 +1,12 @@
using System.Diagnostics.CodeAnalysis;
namespace ServiceLib.Common
{
namespace ServiceLib.Common;
public static class StringEx
{
public static bool IsNullOrEmpty([NotNullWhen(false)] this string? value)
{
return string.IsNullOrEmpty(value);
return string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value);
}
public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? value)
@@ -22,7 +22,9 @@ namespace ServiceLib.Common
public static bool BeginWithAny(this string s, IEnumerable<char> chars)
{
if (s.IsNullOrEmpty())
{
return false;
}
return chars.Contains(s.First());
}
@@ -36,7 +38,9 @@ namespace ServiceLib.Common
while (reader.ReadLine() is { } line)
{
if (line.IsWhiteSpace())
{
continue;
}
yield return line;
}
}
@@ -70,5 +74,9 @@ namespace ServiceLib.Common
{
return string.IsNullOrEmpty(value) ? string.Empty : $"\"{value}\"";
}
public static int ToInt(this string? value, int defaultValue = 0)
{
return int.TryParse(value, out var result) ? result : defaultValue;
}
}

View File

@@ -11,8 +11,8 @@ using System.Text;
using CliWrap;
using CliWrap.Buffered;
namespace ServiceLib.Common
{
namespace ServiceLib.Common;
public class Utils
{
private static readonly string _tag = "Utils";
@@ -20,85 +20,69 @@ namespace ServiceLib.Common
#region
/// <summary>
/// 转逗号分隔的字符串
/// Convert to comma-separated string
/// </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)
if (lst == null || lst.Count == 0)
{
return string.Empty;
}
if (wrap)
var separator = wrap ? "," + Environment.NewLine : ",";
try
{
return string.Join("," + Environment.NewLine, lst);
}
else
{
return string.Join(",", lst);
}
return string.Join(separator, lst);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return string.Empty;
}
}
/// <summary>
/// 逗号分隔的字符串
/// Comma-separated string
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static List<string>? String2List(string? str)
{
try
{
if (str == null)
if (string.IsNullOrWhiteSpace(str))
{
return null;
}
str = str.Replace(Environment.NewLine, "");
try
{
str = str.Replace(Environment.NewLine, string.Empty);
return new List<string>(str.Split(',', StringSplitOptions.RemoveEmptyEntries));
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
}
return null;
}
}
/// <summary>
/// 逗号分隔的字符串,先排序后转List
/// Comma-separated string, sorted and then converted to 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(_tag, ex);
}
return null;
var lst = String2List(str);
lst?.Sort();
return lst;
}
/// <summary>
/// Base64编码
/// Base64 Encode
/// </summary>
/// <param name="plainText"></param>
/// <returns></returns>
@@ -118,7 +102,7 @@ namespace ServiceLib.Common
}
/// <summary>
/// Base64解码
/// Base64 Decode
/// </summary>
/// <param name="plainText"></param>
/// <returns></returns>
@@ -127,7 +111,10 @@ namespace ServiceLib.Common
try
{
if (plainText.IsNullOrEmpty())
{
return "";
}
plainText = plainText.Trim()
.Replace(Environment.NewLine, "")
.Replace("\n", "")
@@ -152,18 +139,6 @@ namespace ServiceLib.Common
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
@@ -188,55 +163,25 @@ namespace ServiceLib.Common
}
}
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}";
if (amount <= 0)
{
return $"{amount:f1} B";
}
string[] units = ["KB", "MB", "GB", "TB", "PB"];
var unitIndex = 0;
double size = amount;
// Loop and divide by 1024 until a suitable unit is found
while (size >= 1024 && unitIndex < units.Length - 1)
{
size /= 1024;
unitIndex++;
}
return $"{size:f1} {units[unitIndex]}";
}
public static string UrlEncode(string url)
@@ -252,7 +197,7 @@ namespace ServiceLib.Common
public static NameValueCollection ParseQueryString(string query)
{
var result = new NameValueCollection(StringComparer.OrdinalIgnoreCase);
if (IsNullOrEmpty(query))
if (query.IsNullOrEmpty())
{
return result;
}
@@ -279,6 +224,13 @@ namespace ServiceLib.Common
}
public static string GetMd5(string str)
{
if (string.IsNullOrEmpty(str))
{
return string.Empty;
}
try
{
var byteOld = Encoding.UTF8.GetBytes(str);
var byteNew = MD5.HashData(byteOld);
@@ -290,6 +242,38 @@ namespace ServiceLib.Common
return sb.ToString();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return string.Empty;
}
}
public static string GetFileHash(string filePath)
{
if (string.IsNullOrEmpty(filePath))
{
return string.Empty;
}
if (!File.Exists(filePath))
{
return string.Empty;
}
try
{
using var md5 = MD5.Create();
using var stream = File.OpenRead(filePath);
var hash = md5.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant();
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
return string.Empty;
}
}
/// <summary>
/// idn to idc
@@ -298,7 +282,7 @@ namespace ServiceLib.Common
/// <returns></returns>
public static string GetPunycode(string url)
{
if (Utils.IsNullOrEmpty(url))
if (url.IsNullOrEmpty())
{
return url;
}
@@ -331,7 +315,7 @@ namespace ServiceLib.Common
public static string Convert2Comma(string text)
{
if (IsNullOrEmpty(text))
if (text.IsNullOrEmpty())
{
return text;
}
@@ -344,7 +328,7 @@ namespace ServiceLib.Common
#region
/// <summary>
/// 判断输入的是否是数字
/// Determine if the input is a number
/// </summary>
/// <param name="oText"></param>
/// <returns></returns>
@@ -353,28 +337,13 @@ namespace ServiceLib.Common
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地址是否合法
/// Validate if the domain address is valid
/// </summary>
/// <param name="domain"></param>
public static bool IsDomain(string? domain)
{
if (IsNullOrEmpty(domain))
if (domain.IsNullOrEmpty())
{
return false;
}
@@ -491,7 +460,7 @@ namespace ServiceLib.Common
}
/// <summary>
/// 取得版本
/// Get version
/// </summary>
/// <returns></returns>
public static string GetVersion(bool blFull = true)
@@ -529,7 +498,7 @@ namespace ServiceLib.Common
}
/// <summary>
/// 取得GUID
/// GUID
/// </summary>
/// <returns></returns>
public static string GetGuid(bool full = true)
@@ -613,7 +582,7 @@ namespace ServiceLib.Common
var result = await cmd.ExecuteBufferedAsync();
if (result.IsSuccess)
{
return result.StandardOutput.ToString();
return result.StandardOutput ?? "";
}
Logging.SaveLog(result.ToString() ?? "");
@@ -634,13 +603,20 @@ namespace ServiceLib.Common
{
try
{
var basePath = GetBaseDirectory();
//When this file exists, it is equivalent to having no permission to read and write
if (File.Exists(Path.Combine(GetBaseDirectory(), "NotStoreConfigHere.txt")))
if (File.Exists(Path.Combine(basePath, "NotStoreConfigHere.txt")))
{
return false;
}
var tempPath = Path.Combine(GetBaseDirectory(), "guiTemps");
//Check if it is installed by Windows WinGet
if (IsWindows() && basePath.Contains("Users") && basePath.Contains("WinGet"))
{
return false;
}
var tempPath = Path.Combine(basePath, "guiTemps");
if (!Directory.Exists(tempPath))
{
Directory.CreateDirectory(tempPath);
@@ -660,7 +636,7 @@ namespace ServiceLib.Common
public static string GetPath(string fileName)
{
var startupPath = StartupPath();
if (IsNullOrEmpty(fileName))
if (fileName.IsNullOrEmpty())
{
return startupPath;
}
@@ -696,7 +672,7 @@ namespace ServiceLib.Common
Directory.CreateDirectory(tempPath);
}
if (IsNullOrEmpty(filename))
if (filename.IsNullOrEmpty())
{
return tempPath;
}
@@ -725,7 +701,7 @@ namespace ServiceLib.Common
Directory.CreateDirectory(tempPath);
}
if (Utils.IsNullOrEmpty(filename))
if (filename.IsNullOrEmpty())
{
return tempPath;
}
@@ -752,7 +728,7 @@ namespace ServiceLib.Common
}
}
if (IsNullOrEmpty(filename))
if (filename.IsNullOrEmpty())
{
return tempPath;
}
@@ -770,7 +746,7 @@ namespace ServiceLib.Common
Directory.CreateDirectory(tempPath);
}
if (Utils.IsNullOrEmpty(filename))
if (filename.IsNullOrEmpty())
{
return tempPath;
}
@@ -788,7 +764,7 @@ namespace ServiceLib.Common
Directory.CreateDirectory(tempPath);
}
if (Utils.IsNullOrEmpty(filename))
if (filename.IsNullOrEmpty())
{
return tempPath;
}
@@ -806,7 +782,7 @@ namespace ServiceLib.Common
Directory.CreateDirectory(tempPath);
}
if (Utils.IsNullOrEmpty(filename))
if (filename.IsNullOrEmpty())
{
return tempPath;
}
@@ -863,14 +839,46 @@ namespace ServiceLib.Common
public static async Task<string?> SetLinuxChmod(string? fileName)
{
if (fileName.IsNullOrEmpty())
{
return null;
}
if (SetUnixFileMode(fileName))
{
Logging.SaveLog($"Successfully set the file execution permission, {fileName}");
return "";
}
if (fileName.Contains(' '))
{
fileName = fileName.AppendQuotes();
//File.SetUnixFileMode(fileName, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute);
}
var arg = new List<string>() { "-c", $"chmod +x {fileName}" };
return await GetCliWrapOutput(Global.LinuxBash, arg);
}
public static bool SetUnixFileMode(string? fileName)
{
try
{
if (fileName.IsNullOrEmpty())
{
return false;
}
if (File.Exists(fileName))
{
var currentMode = File.GetUnixFileMode(fileName);
File.SetUnixFileMode(fileName, currentMode | UnixFileMode.UserExecute | UnixFileMode.GroupExecute | UnixFileMode.OtherExecute);
return true;
}
}
catch (Exception ex)
{
Logging.SaveLog("SetUnixFileMode", ex);
}
return false;
}
public static async Task<string?> GetLinuxFontFamily(string lang)
{
// var arg = new List<string>() { "-c", $"fc-list :lang={lang} family" };
@@ -887,4 +895,3 @@ namespace ServiceLib.Common
#endregion Platform
}
}

View File

@@ -2,8 +2,8 @@ using System.Security.Cryptography;
using System.Text;
using Microsoft.Win32;
namespace ServiceLib.Common
{
namespace ServiceLib.Common;
internal static class WindowsUtils
{
private static readonly string _tag = "WindowsUtils";
@@ -15,7 +15,7 @@ namespace ServiceLib.Common
{
regKey = Registry.CurrentUser.OpenSubKey(path, false);
var value = regKey?.GetValue(name) as string;
return Utils.IsNullOrEmpty(value) ? def : value;
return value.IsNullOrEmpty() ? def : value;
}
catch (Exception ex)
{
@@ -34,7 +34,7 @@ namespace ServiceLib.Common
try
{
regKey = Registry.CurrentUser.CreateSubKey(path);
if (Utils.IsNullOrEmpty(value.ToString()))
if (value.ToString().IsNullOrEmpty())
{
regKey?.DeleteValue(name, false);
}
@@ -63,7 +63,7 @@ namespace ServiceLib.Common
var arg = $$""" /remove-device "SWD\Wintun\{{{guid}}}" """;
// Try to remove the device
await Utils.GetCliWrapOutput(pnpUtilPath, arg);
_ = await Utils.GetCliWrapOutput(pnpUtilPath, arg);
}
catch (Exception ex)
{
@@ -71,4 +71,3 @@ namespace ServiceLib.Common
}
}
}
}

View File

@@ -1,9 +1,9 @@
using YamlDotNet.Core;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace ServiceLib.Common
{
namespace ServiceLib.Common;
public class YamlUtils
{
private static readonly string _tag = "YamlUtils";
@@ -11,7 +11,7 @@ namespace ServiceLib.Common
#region YAML
/// <summary>
/// 反序列化成对象
/// Deserialize
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="str"></param>
@@ -34,7 +34,7 @@ namespace ServiceLib.Common
}
/// <summary>
/// 序列化
/// Serialize
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
@@ -62,9 +62,6 @@ namespace ServiceLib.Common
public static string? PreprocessYaml(string str)
{
var deserializer = new DeserializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance)
.Build();
try
{
var mergingParser = new MergingParser(new Parser(new StringReader(str)));
@@ -80,4 +77,3 @@ namespace ServiceLib.Common
#endregion YAML
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums
{
namespace ServiceLib.Enums;
public enum EConfigType
{
VMess = 1,
@@ -13,4 +13,3 @@
WireGuard = 9,
HTTP = 10
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums
{
namespace ServiceLib.Enums;
public enum ECoreType
{
v2fly = 1,
@@ -16,4 +16,3 @@ namespace ServiceLib.Enums
overtls = 28,
v2rayN = 99
}
}

View File

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

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums
{
namespace ServiceLib.Enums;
public enum EGlobalHotkey
{
ShowForm = 0,
@@ -8,4 +8,3 @@
SystemProxyUnchanged = 3,
SystemProxyPac = 4,
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums
{
namespace ServiceLib.Enums;
public enum EInboundProtocol
{
socks = 0,
@@ -11,4 +11,3 @@
mixed,
speedtest = 21
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums
{
namespace ServiceLib.Enums;
public enum EMove
{
Top = 1,
@@ -8,4 +8,3 @@
Bottom = 4,
Position = 5
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums
{
namespace ServiceLib.Enums;
public enum EMsgCommand
{
ClearMsg,
@@ -8,4 +8,3 @@ namespace ServiceLib.Enums
RefreshProfiles,
AppExit
}
}

View File

@@ -0,0 +1,9 @@
namespace ServiceLib.Enums;
public enum EMultipleLoad
{
Random,
RoundRobin,
LeastPing,
LeastLoad
}

View File

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

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums
{
namespace ServiceLib.Enums;
public enum ERuleMode
{
Rule = 0,
@@ -7,4 +7,3 @@
Direct = 2,
Unchanged = 3
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums
{
namespace ServiceLib.Enums;
public enum EServerColName
{
Def = 0,
@@ -18,4 +18,3 @@
TotalDown,
TotalUp
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums
{
namespace ServiceLib.Enums;
public enum ESpeedActionType
{
Tcping,
@@ -7,4 +7,3 @@
Speedtest,
Mixedtest
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums
{
namespace ServiceLib.Enums;
public enum ESysProxyType
{
ForcedClear = 0,
@@ -7,4 +7,3 @@
Unchanged = 2,
Pac = 3
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums
{
namespace ServiceLib.Enums;
public enum ETheme
{
FollowSystem,
@@ -10,4 +10,3 @@
Dusk,
NightSky
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums
{
namespace ServiceLib.Enums;
public enum ETransport
{
tcp,
@@ -12,4 +12,3 @@
quic,
grpc
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Enums
{
namespace ServiceLib.Enums;
public enum EViewAction
{
CloseWindow,
@@ -20,6 +20,7 @@
BrowseServer,
ImportRulesFromFile,
InitSettingFont,
PasswordInput,
SubEditWindow,
RoutingRuleSettingWindow,
RoutingRuleDetailsWindow,
@@ -43,4 +44,3 @@
DispatcherCheckUpdateFinished,
DispatcherShowMsg,
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib
{
namespace ServiceLib;
public class Global
{
#region const
@@ -8,9 +8,7 @@ namespace ServiceLib
public const string GithubUrl = "https://github.com";
public const string GithubApiUrl = "https://api.github.com/repos";
public const string GeoUrl = "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/latest/download/{0}.dat";
public const string SpeedPingTestUrl = @"https://www.google.com/generate_204";
public const string 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";
@@ -72,6 +70,7 @@ namespace ServiceLib
public const string LocalAppData = "V2RAYN_LOCAL_APPLICATION_DATA_V2";
public const string V2RayLocalAsset = "V2RAY_LOCATION_ASSET";
public const string XrayLocalAsset = "XRAY_LOCATION_ASSET";
public const string XrayLocalCert = "XRAY_LOCATION_CERT";
public const int SpeedTestPageSize = 1000;
public const string LinuxBash = "/bin/bash";
@@ -124,7 +123,7 @@ namespace ServiceLib
[
"",
@"https://github.com/runetfreedom/russia-v2ray-rules-dat/releases/latest/download/{0}.dat",
@"https://cdn.jsdelivr.net/gh/chocolate4u/Iran-v2ray-rules@release/{0}.dat"
@"https://github.com/Chocolate4U/Iran-v2ray-rules/releases/latest/download/{0}.dat"
];
public static readonly List<string> SingboxRulesetSources =
@@ -265,7 +264,8 @@ namespace ServiceLib
"utp",
"wechat-video",
"dtls",
"wireguard"
"wireguard",
"dns"
];
public static readonly List<string> CoreTypes =
@@ -517,6 +517,15 @@ namespace ServiceLib
@"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb"
];
public static readonly List<string> IPAPIUrls =
[
@"https://speed.cloudflare.com/meta",
@"https://api.ip.sb/geoip",
@"https://api-ipv4.ip.sb/geoip",
@"https://api-ipv6.ip.sb/geoip",
@"https://api.ipapi.is",
@""
];
#endregion const
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler
{
namespace ServiceLib.Handler;
public sealed class AppHandler
{
#region Property
@@ -9,7 +9,6 @@ namespace ServiceLib.Handler
private int? _statePort;
private int? _statePort2;
private Job? _processJob;
private bool? _isAdministrator;
public static AppHandler Instance => _instance.Value;
public Config Config => _config;
@@ -31,14 +30,7 @@ namespace ServiceLib.Handler
}
}
public bool IsAdministrator
{
get
{
_isAdministrator ??= Utils.IsAdministrator();
return _isAdministrator.Value;
}
}
public string LinuxSudoPwd { get; set; }
#endregion Property
@@ -80,7 +72,9 @@ namespace ServiceLib.Handler
Logging.SaveLog($"v2rayN start up | {Utils.GetRuntimeInfo()}");
Logging.LoggingEnabled(_config.GuiItem.EnableLog);
ClearExpiredFiles();
//First determine the port value
_ = StatePort;
_ = StatePort2;
return true;
}
@@ -92,16 +86,6 @@ namespace ServiceLib.Handler
return true;
}
private void ClearExpiredFiles()
{
Task.Run(() =>
{
FileManager.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
FileManager.DeleteExpiredFiles(Utils.GetBinConfigPath(), DateTime.Now.AddHours(-1));
});
}
#endregion Init
#region Config
@@ -143,7 +127,7 @@ namespace ServiceLib.Handler
public async Task<List<ProfileItem>?> ProfileItems(string subid)
{
if (Utils.IsNullOrEmpty(subid))
if (subid.IsNullOrEmpty())
{
return await SQLiteHelper.Instance.TableAsync<ProfileItem>().ToListAsync();
}
@@ -165,11 +149,11 @@ namespace ServiceLib.Handler
from ProfileItem a
left join SubItem b on a.subid = b.id
where 1=1 ";
if (Utils.IsNotEmpty(subid))
if (subid.IsNotEmpty())
{
sql += $" and a.subid = '{subid}'";
}
if (Utils.IsNotEmpty(filter))
if (filter.IsNotEmpty())
{
if (filter.Contains('\''))
{
@@ -183,7 +167,7 @@ namespace ServiceLib.Handler
public async Task<ProfileItem?> GetProfileItem(string indexId)
{
if (Utils.IsNullOrEmpty(indexId))
if (indexId.IsNullOrEmpty())
{
return null;
}
@@ -192,7 +176,7 @@ namespace ServiceLib.Handler
public async Task<ProfileItem?> GetProfileItemViaRemarks(string? remarks)
{
if (Utils.IsNullOrEmpty(remarks))
if (remarks.IsNullOrEmpty())
{
return null;
}
@@ -253,4 +237,3 @@ namespace ServiceLib.Handler
#endregion Core Type
}
}

View File

@@ -1,8 +1,8 @@
using System.Security.Principal;
using System.Text.RegularExpressions;
namespace ServiceLib.Handler
{
namespace ServiceLib.Handler;
public static class AutoStartupHandler
{
private static readonly string _tag = "AutoStartupHandler";
@@ -85,7 +85,7 @@ namespace ServiceLib.Handler
/// <exception cref="ArgumentNullException"></exception>
public static void AutoStartTaskService(string taskName, string fileName, string description)
{
if (Utils.IsNullOrEmpty(taskName))
if (taskName.IsNullOrEmpty())
{
return;
}
@@ -93,7 +93,7 @@ namespace ServiceLib.Handler
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))
if (fileName.IsNullOrEmpty())
{
foreach (var t in tasks)
{
@@ -109,7 +109,7 @@ namespace ServiceLib.Handler
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.Triggers.Add(new Microsoft.Win32.TaskScheduler.LogonTrigger { UserId = logonUser, Delay = TimeSpan.FromSeconds(30) });
task.Principal.RunLevel = Microsoft.Win32.TaskScheduler.TaskRunLevel.Highest;
task.Actions.Add(new Microsoft.Win32.TaskScheduler.ExecAction(fileName.AppendQuotes(), null, Path.GetDirectoryName(fileName)));
@@ -239,4 +239,3 @@ namespace ServiceLib.Handler
#endregion macOS
}
}

View File

@@ -1,19 +1,19 @@
using static ServiceLib.Models.ClashProxies;
namespace ServiceLib.Handler
{
namespace ServiceLib.Handler;
public sealed class ClashApiHandler
{
private static readonly Lazy<ClashApiHandler> instance = new(() => new());
public static ClashApiHandler Instance => instance.Value;
private static readonly string _tag = "ClashApiHandler";
private Dictionary<string, ProxiesItem>? _proxies;
public Dictionary<string, object> ProfileContent { get; set; }
private static readonly string _tag = "ClashApiHandler";
public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync(Config config)
public async Task<Tuple<ClashProxies, ClashProviders>?> GetClashProxiesAsync()
{
for (var i = 0; i < 5; i++)
for (var i = 0; i < 3; i++)
{
var url = $"{GetApiUrl()}/proxies";
var result = await HttpClientHelper.Instance.TryGetAsync(url);
@@ -37,38 +37,30 @@ namespace ServiceLib.Handler
public void ClashProxiesDelayTest(bool blAll, List<ClashProxyModel> lstProxy, Action<ClashProxyModel?, string> updateFunc)
{
Task.Run(() =>
Task.Run(async () =>
{
if (blAll)
{
for (var i = 0; i < 5; i++)
{
if (_proxies != null)
{
break;
}
Task.Delay(5000).Wait();
}
if (_proxies == null)
{
return;
await GetClashProxiesAsync();
}
lstProxy = new List<ClashProxyModel>();
foreach (KeyValuePair<string, ProxiesItem> kv in _proxies)
foreach (var kv in _proxies ?? [])
{
if (Global.notAllowTestType.Contains(kv.Value.type.ToLower()))
if (Global.notAllowTestType.Contains(kv.Value.type?.ToLower()))
{
continue;
}
lstProxy.Add(new ClashProxyModel()
{
Name = kv.Value.name,
Type = kv.Value.type.ToLower(),
Type = kv.Value.type?.ToLower(),
});
}
}
if (lstProxy == null)
if (lstProxy is not { Count: > 0 })
{
return;
}
@@ -90,9 +82,8 @@ namespace ServiceLib.Handler
updateFunc?.Invoke(it, result);
}));
}
Task.WaitAll(tasks.ToArray());
Task.Delay(1000).Wait();
await Task.WhenAll(tasks);
await Task.Delay(1000);
updateFunc?.Invoke(null, "");
});
}
@@ -158,7 +149,7 @@ namespace ServiceLib.Handler
}
}
public async Task<ClashConnections?> GetClashConnectionsAsync(Config config)
public async Task<ClashConnections?> GetClashConnectionsAsync()
{
try
{
@@ -194,4 +185,3 @@ namespace ServiceLib.Handler
return $"{Global.HttpProtocol}{Global.Loopback}:{AppHandler.Instance.StatePort2}";
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,42 @@
namespace ServiceLib.Handler;
public class ConnectionHandler
{
private static readonly Lazy<ConnectionHandler> _instance = new(() => new());
public static ConnectionHandler Instance => _instance.Value;
public async Task<string> RunAvailabilityCheck()
{
var downloadHandle = new DownloadService();
var time = await downloadHandle.RunAvailabilityCheck(null);
var ip = time > 0 ? await GetIPInfo(downloadHandle) ?? Global.None : Global.None;
return string.Format(ResUI.TestMeOutput, time, ip);
}
private async Task<string?> GetIPInfo(DownloadService downloadHandle)
{
var url = AppHandler.Instance.Config.SpeedTestItem.IPAPIUrl;
if (url.IsNullOrEmpty())
{
return null;
}
var result = await downloadHandle.TryDownloadString(url, true, "");
if (result == null)
{
return null;
}
var ipInfo = JsonUtils.Deserialize<IPAPIInfo>(result);
if (ipInfo == null)
{
return null;
}
var ip = ipInfo.ip ?? ipInfo.clientIp ?? ipInfo.ip_addr ?? ipInfo.query;
var country = ipInfo.country_code ?? ipInfo.country ?? ipInfo.countryCode ?? ipInfo.location?.country_code;
return $"({country ?? "unknown"}) {ip}";
}
}

View File

@@ -0,0 +1,126 @@
using System.Diagnostics;
using System.Text;
using CliWrap;
namespace ServiceLib.Handler;
public class CoreAdminHandler
{
private static readonly Lazy<CoreAdminHandler> _instance = new(() => new());
public static CoreAdminHandler Instance => _instance.Value;
private Config _config;
private Action<bool, string>? _updateFunc;
private int _linuxSudoPid = -1;
public async Task Init(Config config, Action<bool, string> updateFunc)
{
if (_config != null)
{
return;
}
_config = config;
_updateFunc = updateFunc;
}
private void UpdateFunc(bool notify, string msg)
{
_updateFunc?.Invoke(notify, msg);
}
public async Task<Process?> RunProcessAsLinuxSudo(string fileName, CoreInfo coreInfo, string configPath)
{
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
Process proc = new()
{
StartInfo = new()
{
FileName = shFilePath,
Arguments = "",
WorkingDirectory = Utils.GetBinConfigPath(),
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
StandardInputEncoding = Encoding.UTF8,
StandardOutputEncoding = Encoding.UTF8,
StandardErrorEncoding = Encoding.UTF8,
}
};
proc.OutputDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.ErrorDataReceived += (sender, e) =>
{
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync();
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(AppHandler.Instance.LinuxSudoPwd);
await Task.Delay(100);
if (proc is null or { HasExited: true })
{
throw new Exception(ResUI.FailedToRunCore);
}
_linuxSudoPid = proc.Id;
return proc;
}
public async Task KillProcessAsLinuxSudo()
{
if (_linuxSudoPid < 0)
{
return;
}
var cmdLine = $"pkill -P {_linuxSudoPid} ; kill {_linuxSudoPid}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
await Cli.Wrap(shFilePath)
.WithStandardInputPipe(PipeSource.FromString(AppHandler.Instance.LinuxSudoPwd))
.ExecuteAsync();
_linuxSudoPid = -1;
}
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
{
var shFilePath = Utils.GetBinConfigPath(fileName);
File.Delete(shFilePath);
var sb = new StringBuilder();
sb.AppendLine("#!/bin/sh");
if (Utils.IsAdministrator())
{
sb.AppendLine($"{cmdLine}");
}
else
{
sb.AppendLine($"sudo -S {cmdLine}");
}
await File.WriteAllTextAsync(shFilePath, sb.ToString());
await Utils.SetLinuxChmod(shFilePath);
return shFilePath;
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler
{
namespace ServiceLib.Handler;
/// <summary>
/// Core configuration file processing class
/// </summary>
@@ -33,7 +33,7 @@ namespace ServiceLib.Handler
{
return result;
}
if (Utils.IsNotEmpty(fileName) && result.Data != null)
if (fileName.IsNotEmpty() && result.Data != null)
{
await File.WriteAllTextAsync(fileName, result.Data.ToString());
}
@@ -133,16 +133,16 @@ namespace ServiceLib.Handler
return result;
}
public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType)
public static async Task<RetResult> GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType, EMultipleLoad multipleLoad)
{
var result = new RetResult();
if (coreType == ECoreType.sing_box)
{
result = await new CoreConfigSingboxService(config).GenerateClientMultipleLoadConfig(selecteds);
}
else if (coreType == ECoreType.Xray)
else
{
result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds);
result = await new CoreConfigV2rayService(config).GenerateClientMultipleLoadConfig(selecteds, multipleLoad);
}
if (result.Success != true)
@@ -153,4 +153,3 @@ namespace ServiceLib.Handler
return result;
}
}
}

View File

@@ -1,8 +1,8 @@
using System.Diagnostics;
using System.Text;
namespace ServiceLib.Handler
{
namespace ServiceLib.Handler;
/// <summary>
/// Core process processing class
/// </summary>
@@ -13,7 +13,7 @@ namespace ServiceLib.Handler
private Config _config;
private Process? _process;
private Process? _processPre;
private int _linuxSudoPid = -1;
private bool _linuxSudo = false;
private Action<bool, string>? _updateFunc;
private const string _tag = "CoreHandler";
@@ -24,6 +24,7 @@ namespace ServiceLib.Handler
Environment.SetEnvironmentVariable(Global.V2RayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable(Global.XrayLocalAsset, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
Environment.SetEnvironmentVariable(Global.XrayLocalCert, Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
//Copy the bin folder to the storage location (for init)
if (Environment.GetEnvironmentVariable(Global.LocalAppData) == "1")
@@ -100,7 +101,7 @@ namespace ServiceLib.Handler
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 coreType = selecteds.Exists(t => t.ConfigType is EConfigType.Hysteria2 or EConfigType.TUIC) ? ECoreType.sing_box : ECoreType.Xray;
var fileName = string.Format(Global.CoreSpeedtestConfigFileName, Utils.GetGuid(false));
var configPath = Utils.GetBinConfigPath(fileName);
var result = await CoreConfigHandler.GenerateClientSpeedtestConfig(_config, configPath, selecteds, coreType);
@@ -154,23 +155,23 @@ namespace ServiceLib.Handler
{
try
{
if (_linuxSudo)
{
await CoreAdminHandler.Instance.KillProcessAsLinuxSudo();
_linuxSudo = false;
}
if (_process != null)
{
await ProcUtils.ProcessKill(_process, true);
await ProcUtils.ProcessKill(_process, Utils.IsWindows());
_process = null;
}
if (_processPre != null)
{
await ProcUtils.ProcessKill(_processPre, true);
await ProcUtils.ProcessKill(_processPre, Utils.IsWindows());
_processPre = null;
}
if (_linuxSudoPid > 0)
{
await KillProcessAsLinuxSudo();
}
_linuxSudoPid = -1;
}
catch (Exception ex)
{
@@ -224,15 +225,6 @@ namespace ServiceLib.Handler
_updateFunc?.Invoke(notify, msg);
}
private bool IsNeedSudo(ECoreType eCoreType)
{
return _config.TunModeItem.EnableTun
&& eCoreType == ECoreType.sing_box
&& (Utils.IsNonWindows())
//&& _config.TunModeItem.LinuxSudoPwd.IsNotEmpty()
;
}
#endregion Private
#region Process
@@ -240,13 +232,35 @@ namespace ServiceLib.Handler
private async Task<Process?> RunProcess(CoreInfo? coreInfo, string configPath, bool displayLog, bool mayNeedSudo)
{
var fileName = CoreInfoHandler.Instance.GetCoreExecFile(coreInfo, out var msg);
if (Utils.IsNullOrEmpty(fileName))
if (fileName.IsNullOrEmpty())
{
UpdateFunc(false, msg);
return null;
}
try
{
if (mayNeedSudo
&& _config.TunModeItem.EnableTun
&& coreInfo.CoreType == ECoreType.sing_box
&& Utils.IsNonWindows())
{
_linuxSudo = true;
await CoreAdminHandler.Instance.Init(_config, _updateFunc);
return await CoreAdminHandler.Instance.RunProcessAsLinuxSudo(fileName, coreInfo, configPath);
}
return await RunProcessNormal(fileName, coreInfo, configPath, displayLog);
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
UpdateFunc(mayNeedSudo, ex.Message);
return null;
}
}
private async Task<Process?> RunProcessNormal(string fileName, CoreInfo? coreInfo, string configPath, bool displayLog)
{
Process proc = new()
{
@@ -264,47 +278,32 @@ namespace ServiceLib.Handler
}
};
var isNeedSudo = mayNeedSudo && IsNeedSudo(coreInfo.CoreType);
if (isNeedSudo)
{
await RunProcessAsLinuxSudo(proc, fileName, coreInfo, configPath);
}
if (displayLog)
{
proc.OutputDataReceived += (sender, e) =>
{
if (Utils.IsNullOrEmpty(e.Data))
return;
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, e.Data + Environment.NewLine);
}
};
proc.ErrorDataReceived += (sender, e) =>
{
if (Utils.IsNullOrEmpty(e.Data))
return;
if (e.Data.IsNotEmpty())
{
UpdateFunc(false, 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();
}
await Task.Delay(500);
await Task.Delay(100);
AppHandler.Instance.AddProcess(proc.Handle);
if (proc is null or { HasExited: true })
{
@@ -312,98 +311,6 @@ namespace ServiceLib.Handler
}
return proc;
}
catch (Exception ex)
{
Logging.SaveLog(_tag, ex);
UpdateFunc(mayNeedSudo, ex.Message);
return null;
}
}
#endregion Process
#region Linux
private async Task RunProcessAsLinuxSudo(Process proc, string fileName, CoreInfo coreInfo, string configPath)
{
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "run_as_sudo.sh");
proc.StartInfo.FileName = shFilePath;
proc.StartInfo.Arguments = "";
proc.StartInfo.WorkingDirectory = "";
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{
proc.StartInfo.StandardInputEncoding = Encoding.UTF8;
proc.StartInfo.RedirectStandardInput = true;
}
}
private async Task KillProcessAsLinuxSudo()
{
var cmdLine = $"kill {_linuxSudoPid}";
var shFilePath = await CreateLinuxShellFile(cmdLine, "kill_as_sudo.sh");
Process proc = new()
{
StartInfo = new()
{
FileName = shFilePath,
UseShellExecute = false,
CreateNoWindow = true,
StandardInputEncoding = Encoding.UTF8,
RedirectStandardInput = true
}
};
proc.Start();
if (_config.TunModeItem.LinuxSudoPwd.IsNotEmpty())
{
try
{
var pwd = DesUtils.Decrypt(_config.TunModeItem.LinuxSudoPwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
await Task.Delay(10);
await proc.StandardInput.WriteLineAsync(pwd);
}
catch (Exception)
{
// ignored
}
}
var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
await proc.WaitForExitAsync(timeout.Token);
await Task.Delay(3000);
}
private async Task<string> CreateLinuxShellFile(string cmdLine, string fileName)
{
//Shell scripts
var shFilePath = Utils.GetBinConfigPath(AppHandler.Instance.IsAdministrator ? "root_" + fileName : fileName);
File.Delete(shFilePath);
var sb = new StringBuilder();
sb.AppendLine("#!/bin/sh");
if (AppHandler.Instance.IsAdministrator)
{
sb.AppendLine($"{cmdLine}");
}
else if (_config.TunModeItem.LinuxSudoPwd.IsNullOrEmpty())
{
sb.AppendLine($"pkexec {cmdLine}");
}
else
{
sb.AppendLine($"sudo -S {cmdLine}");
}
await File.WriteAllTextAsync(shFilePath, sb.ToString());
await Utils.SetLinuxChmod(shFilePath);
Logging.SaveLog(shFilePath);
return shFilePath;
}
#endregion Linux
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler
{
namespace ServiceLib.Handler;
public sealed class CoreInfoHandler
{
private static readonly Lazy<CoreInfoHandler> _instance = new(() => new());
@@ -215,4 +215,3 @@ namespace ServiceLib.Handler
return $"{Global.GithubUrl}/{Global.CoreUrls[eCoreType]}/releases";
}
}
}

View File

@@ -1,27 +1,30 @@
using System.Collections.Specialized;
namespace ServiceLib.Handler.Fmt
{
namespace ServiceLib.Handler.Fmt;
public class BaseFmt
{
protected static string GetIpv6(string address)
{
if (Utils.IsIpv6(address))
{
// 检查地址是否已经被方括号包围,如果没有,则添加方括号
// Check if the address is already surrounded by square brackets, if not, add square brackets
return address.StartsWith('[') && address.EndsWith(']') ? address : $"[{address}]";
}
return address; // 如果不是IPv6地址直接返回原地址
else
{
return address;
}
}
protected static int GetStdTransport(ProfileItem item, string? securityDef, ref Dictionary<string, string> dicQuery)
{
if (Utils.IsNotEmpty(item.Flow))
if (item.Flow.IsNotEmpty())
{
dicQuery.Add("flow", item.Flow);
}
if (Utils.IsNotEmpty(item.StreamSecurity))
if (item.StreamSecurity.IsNotEmpty())
{
dicQuery.Add("security", item.StreamSecurity);
}
@@ -32,27 +35,27 @@ namespace ServiceLib.Handler.Fmt
dicQuery.Add("security", securityDef);
}
}
if (Utils.IsNotEmpty(item.Sni))
if (item.Sni.IsNotEmpty())
{
dicQuery.Add("sni", item.Sni);
}
if (Utils.IsNotEmpty(item.Alpn))
if (item.Alpn.IsNotEmpty())
{
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
}
if (Utils.IsNotEmpty(item.Fingerprint))
if (item.Fingerprint.IsNotEmpty())
{
dicQuery.Add("fp", Utils.UrlEncode(item.Fingerprint));
}
if (Utils.IsNotEmpty(item.PublicKey))
if (item.PublicKey.IsNotEmpty())
{
dicQuery.Add("pbk", Utils.UrlEncode(item.PublicKey));
}
if (Utils.IsNotEmpty(item.ShortId))
if (item.ShortId.IsNotEmpty())
{
dicQuery.Add("sid", Utils.UrlEncode(item.ShortId));
}
if (Utils.IsNotEmpty(item.SpiderX))
if (item.SpiderX.IsNotEmpty())
{
dicQuery.Add("spx", Utils.UrlEncode(item.SpiderX));
}
@@ -61,21 +64,21 @@ namespace ServiceLib.Handler.Fmt
dicQuery.Add("allowInsecure", "1");
}
dicQuery.Add("type", Utils.IsNotEmpty(item.Network) ? item.Network : nameof(ETransport.tcp));
dicQuery.Add("type", item.Network.IsNotEmpty() ? 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("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
if (item.RequestHost.IsNotEmpty())
{
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("headerType", item.HeaderType.IsNotEmpty() ? item.HeaderType : Global.None);
if (item.Path.IsNotEmpty())
{
dicQuery.Add("seed", Utils.UrlEncode(item.Path));
}
@@ -83,30 +86,30 @@ namespace ServiceLib.Handler.Fmt
case nameof(ETransport.ws):
case nameof(ETransport.httpupgrade):
if (Utils.IsNotEmpty(item.RequestHost))
if (item.RequestHost.IsNotEmpty())
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
}
if (Utils.IsNotEmpty(item.Path))
if (item.Path.IsNotEmpty())
{
dicQuery.Add("path", Utils.UrlEncode(item.Path));
}
break;
case nameof(ETransport.xhttp):
if (Utils.IsNotEmpty(item.RequestHost))
if (item.RequestHost.IsNotEmpty())
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
}
if (Utils.IsNotEmpty(item.Path))
if (item.Path.IsNotEmpty())
{
dicQuery.Add("path", Utils.UrlEncode(item.Path));
}
if (Utils.IsNotEmpty(item.HeaderType) && Global.XhttpMode.Contains(item.HeaderType))
if (item.HeaderType.IsNotEmpty() && Global.XhttpMode.Contains(item.HeaderType))
{
dicQuery.Add("mode", Utils.UrlEncode(item.HeaderType));
}
if (Utils.IsNotEmpty(item.Extra))
if (item.Extra.IsNotEmpty())
{
dicQuery.Add("extra", Utils.UrlEncode(item.Extra));
}
@@ -115,24 +118,24 @@ namespace ServiceLib.Handler.Fmt
case nameof(ETransport.http):
case nameof(ETransport.h2):
dicQuery["type"] = nameof(ETransport.http);
if (Utils.IsNotEmpty(item.RequestHost))
if (item.RequestHost.IsNotEmpty())
{
dicQuery.Add("host", Utils.UrlEncode(item.RequestHost));
}
if (Utils.IsNotEmpty(item.Path))
if (item.Path.IsNotEmpty())
{
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("headerType", item.HeaderType.IsNotEmpty() ? 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))
if (item.Path.IsNotEmpty())
{
dicQuery.Add("authority", Utils.UrlEncode(item.RequestHost));
dicQuery.Add("serviceName", Utils.UrlEncode(item.Path));
@@ -215,8 +218,10 @@ namespace ServiceLib.Handler.Fmt
foreach (var item in s)
{
if (str.Contains(item, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
@@ -237,4 +242,3 @@ namespace ServiceLib.Handler.Fmt
return $"{Global.ProtocolShares[eConfigType]}{url}{query}{remark}";
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt
{
namespace ServiceLib.Handler.Fmt;
public class ClashFmt : BaseFmt
{
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
@@ -20,4 +20,3 @@
return null;
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt
{
namespace ServiceLib.Handler.Fmt;
public class FmtHandler
{
private static readonly string _tag = "FmtHandler";
@@ -37,7 +37,7 @@
try
{
string str = config.TrimEx();
if (Utils.IsNullOrEmpty(str))
if (str.IsNullOrEmpty())
{
msg = ResUI.FailedReadConfiguration;
return null;
@@ -89,4 +89,3 @@
}
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt
{
namespace ServiceLib.Handler.Fmt;
public class Hysteria2Fmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
@@ -24,6 +24,8 @@ namespace ServiceLib.Handler.Fmt
item.Path = Utils.UrlDecode(query["obfs-password"] ?? "");
item.AllowInsecure = (query["insecure"] ?? "") == "1" ? "true" : "false";
item.Ports = Utils.UrlDecode(query["mport"] ?? "").Replace('-', ':');
return item;
}
@@ -34,25 +36,29 @@ namespace ServiceLib.Handler.Fmt
string url = string.Empty;
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.Sni))
if (item.Sni.IsNotEmpty())
{
dicQuery.Add("sni", item.Sni);
}
if (Utils.IsNotEmpty(item.Alpn))
if (item.Alpn.IsNotEmpty())
{
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
}
if (Utils.IsNotEmpty(item.Path))
if (item.Path.IsNotEmpty())
{
dicQuery.Add("obfs", "salamander");
dicQuery.Add("obfs-password", Utils.UrlEncode(item.Path));
}
dicQuery.Add("insecure", item.AllowInsecure.ToLower() == "true" ? "1" : "0");
if (item.Ports.IsNotEmpty())
{
dicQuery.Add("mport", Utils.UrlEncode(item.Ports.Replace(':', '-')));
}
return ToUri(EConfigType.Hysteria2, item.Address, item.Port, item.Id, dicQuery, remark);
}
@@ -93,4 +99,3 @@ namespace ServiceLib.Handler.Fmt
return null;
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt
{
namespace ServiceLib.Handler.Fmt;
public class NaiveproxyFmt : BaseFmt
{
public static ProfileItem? ResolveFull(string strData, string? subRemarks)
@@ -20,4 +20,3 @@
return null;
}
}
}

View File

@@ -1,7 +1,7 @@
using System.Text.RegularExpressions;
namespace ServiceLib.Handler.Fmt
{
namespace ServiceLib.Handler.Fmt;
public class ShadowsocksFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
@@ -27,11 +27,11 @@ namespace ServiceLib.Handler.Fmt
public static string? ToUri(ProfileItem? item)
{
if (item == null)
{
return null;
string url = string.Empty;
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
}
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
@@ -53,12 +53,14 @@ namespace ServiceLib.Handler.Fmt
{
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))
if (tag.IsNotEmpty())
{
item.Remarks = Utils.UrlDecode(tag);
}
@@ -72,11 +74,13 @@ namespace ServiceLib.Handler.Fmt
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);
item.Port = details.Groups["port"].Value.ToInt();
return item;
}
@@ -84,7 +88,9 @@ namespace ServiceLib.Handler.Fmt
{
var parsedUrl = Utils.TryUri(result);
if (parsedUrl == null)
{
return null;
}
ProfileItem item = new()
{
@@ -96,7 +102,7 @@ namespace ServiceLib.Handler.Fmt
//2022-blake3
if (rawUserInfo.Contains(':'))
{
string[] userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
var userInfoParts = rawUserInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2)
{
return null;
@@ -107,8 +113,8 @@ namespace ServiceLib.Handler.Fmt
else
{
// parse base64 UserInfo
string userInfo = Utils.Base64Decode(rawUserInfo);
string[] userInfoParts = userInfo.Split(new[] { ':' }, 2);
var userInfo = Utils.Base64Decode(rawUserInfo);
var userInfoParts = userInfo.Split(new[] { ':' }, 2);
if (userInfoParts.Length != 2)
{
return null;
@@ -122,7 +128,7 @@ namespace ServiceLib.Handler.Fmt
{
//obfs-host exists
var obfsHost = queryParameters["plugin"]?.Split(';').FirstOrDefault(t => t.Contains("obfs-host"));
if (queryParameters["plugin"].Contains("obfs=http") && Utils.IsNotEmpty(obfsHost))
if (queryParameters["plugin"].Contains("obfs=http") && obfsHost.IsNotEmpty())
{
obfsHost = obfsHost?.Replace("obfs-host=", "");
item.Network = Global.DefaultNetwork;
@@ -162,7 +168,7 @@ namespace ServiceLib.Handler.Fmt
Security = it.method,
Id = it.password,
Address = it.server,
Port = Utils.ToInt(it.server_port)
Port = it.server_port.ToInt()
};
lst.Add(ssItem);
}
@@ -171,4 +177,3 @@ namespace ServiceLib.Handler.Fmt
return null;
}
}
}

View File

@@ -1,44 +1,39 @@
namespace ServiceLib.Handler.Fmt
{
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)
var configObjects = JsonUtils.Deserialize<object[]>(strData);
if (configObjects is not { Length: > 0 })
{
return null;
}
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 profileIt = ResolveFull(objectString, subRemarks);
if (profileIt != 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 config = JsonUtils.ParseJson(strData);
if (config?["inbounds"] == null
|| config["outbounds"] == null
|| config["route"] == null
|| config["dns"] == null)
{
return null;
}
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
@@ -49,7 +44,4 @@
return profileItem;
}
return null;
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt
{
namespace ServiceLib.Handler.Fmt;
public class SocksFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
@@ -24,11 +24,11 @@ namespace ServiceLib.Handler.Fmt
public static string? ToUri(ProfileItem? item)
{
if (item == null)
{
return null;
var url = string.Empty;
}
var remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
@@ -77,7 +77,7 @@ namespace ServiceLib.Handler.Fmt
return null;
}
item.Address = arr1[1][..indexPort];
item.Port = Utils.ToInt(arr1[1][(indexPort + 1)..]);
item.Port = arr1[1][(indexPort + 1)..].ToInt();
item.Security = arr21.First();
item.Id = arr21[1];
@@ -88,7 +88,9 @@ namespace ServiceLib.Handler.Fmt
{
var parsedUrl = Utils.TryUri(result);
if (parsedUrl == null)
{
return null;
}
ProfileItem item = new()
{
@@ -110,4 +112,3 @@ namespace ServiceLib.Handler.Fmt
return item;
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt
{
namespace ServiceLib.Handler.Fmt;
public class TrojanFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
@@ -13,7 +13,9 @@ namespace ServiceLib.Handler.Fmt
var url = Utils.TryUri(str);
if (url == null)
{
return null;
}
item.Address = url.IdnHost;
item.Port = url.Port;
@@ -21,7 +23,7 @@ namespace ServiceLib.Handler.Fmt
item.Id = Utils.UrlDecode(url.UserInfo);
var query = Utils.ParseQueryString(url.Query);
ResolveStdTransport(query, ref item);
_ = ResolveStdTransport(query, ref item);
return item;
}
@@ -29,18 +31,17 @@ namespace ServiceLib.Handler.Fmt
public static string? ToUri(ProfileItem? item)
{
if (item == null)
{
return null;
string url = string.Empty;
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
}
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
GetStdTransport(item, null, ref dicQuery);
_ = GetStdTransport(item, null, ref dicQuery);
return ToUri(EConfigType.Trojan, item.Address, item.Port, item.Id, dicQuery, remark);
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt
{
namespace ServiceLib.Handler.Fmt;
public class TuicFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
@@ -13,7 +13,9 @@ namespace ServiceLib.Handler.Fmt
var url = Utils.TryUri(str);
if (url == null)
{
return null;
}
item.Address = url.IdnHost;
item.Port = url.Port;
@@ -36,20 +38,21 @@ namespace ServiceLib.Handler.Fmt
public static string? ToUri(ProfileItem? item)
{
if (item == null)
{
return null;
string url = string.Empty;
}
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.Sni))
if (item.Sni.IsNotEmpty())
{
dicQuery.Add("sni", item.Sni);
}
if (Utils.IsNotEmpty(item.Alpn))
if (item.Alpn.IsNotEmpty())
{
dicQuery.Add("alpn", Utils.UrlEncode(item.Alpn));
}
@@ -58,4 +61,3 @@ namespace ServiceLib.Handler.Fmt
return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Id}:{item.Security}", dicQuery, remark);
}
}
}

View File

@@ -1,56 +1,48 @@
namespace ServiceLib.Handler.Fmt
{
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)
var configObjects = JsonUtils.Deserialize<object[]>(strData);
if (configObjects is not { Length: > 0 })
{
return null;
}
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 profileIt = ResolveFull(objectString, subRemarks);
if (profileIt != 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 config = JsonUtils.ParseJson(strData);
if (config?["inbounds"] == null
|| config["outbounds"] == null
|| config["routing"] == null)
{
return null;
}
var fileName = WriteAllText(strData);
var profileItem = new ProfileItem
{
CoreType = ECoreType.Xray,
Address = fileName,
Remarks = v2rayConfig.remarks ?? subRemarks ?? "v2ray_custom"
Remarks = config?["remarks"]?.ToString() ?? subRemarks ?? "v2ray_custom"
};
return profileItem;
}
return null;
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt
{
namespace ServiceLib.Handler.Fmt;
public class VLESSFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
@@ -14,7 +14,9 @@ namespace ServiceLib.Handler.Fmt
var url = Utils.TryUri(str);
if (url == null)
{
return null;
}
item.Address = url.IdnHost;
item.Port = url.Port;
@@ -24,7 +26,7 @@ namespace ServiceLib.Handler.Fmt
var query = Utils.ParseQueryString(url.Query);
item.Security = query["encryption"] ?? Global.None;
item.StreamSecurity = query["security"] ?? "";
ResolveStdTransport(query, ref item);
_ = ResolveStdTransport(query, ref item);
return item;
}
@@ -32,16 +34,17 @@ namespace ServiceLib.Handler.Fmt
public static string? ToUri(ProfileItem? item)
{
if (item == null)
{
return null;
string url = string.Empty;
}
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.Security))
if (item.Security.IsNotEmpty())
{
dicQuery.Add("encryption", item.Security);
}
@@ -49,9 +52,8 @@ namespace ServiceLib.Handler.Fmt
{
dicQuery.Add("encryption", Global.None);
}
GetStdTransport(item, Global.None, ref dicQuery);
_ = GetStdTransport(item, Global.None, ref dicQuery);
return ToUri(EConfigType.VLESS, item.Address, item.Port, item.Id, dicQuery, remark);
}
}
}

View File

@@ -1,14 +1,14 @@
namespace ServiceLib.Handler.Fmt
{
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)
if (str.IndexOf('@') > 0)
{
item = ResolveStdVmess(str);
item = ResolveStdVmess(str) ?? ResolveVmess(str, out msg);
}
else
{
@@ -20,10 +20,10 @@ namespace ServiceLib.Handler.Fmt
public static string? ToUri(ProfileItem? item)
{
if (item == null)
{
return null;
string url = string.Empty;
VmessQRCode vmessQRCode = new()
}
var vmessQRCode = new VmessQRCode
{
v = item.ConfigVersion,
ps = item.Remarks.TrimEx(),
@@ -42,7 +42,7 @@ namespace ServiceLib.Handler.Fmt
fp = item.Fingerprint
};
url = JsonUtils.Serialize(vmessQRCode);
var url = JsonUtils.Serialize(vmessQRCode);
url = Utils.Base64Encode(url);
url = $"{Global.ProtocolShares[EConfigType.VMess]}{url}";
@@ -60,7 +60,7 @@ namespace ServiceLib.Handler.Fmt
result = result[Global.ProtocolShares[EConfigType.VMess].Length..];
result = Utils.Base64Decode(result);
VmessQRCode? vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result);
var vmessQRCode = JsonUtils.Deserialize<VmessQRCode>(result);
if (vmessQRCode == null)
{
msg = ResUI.FailedConversionConfiguration;
@@ -78,12 +78,12 @@ namespace ServiceLib.Handler.Fmt
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.Security = vmessQRCode.scy.IsNotEmpty() ? vmessQRCode.scy : Global.DefaultSecurity;
if (vmessQRCode.net.IsNotEmpty())
{
item.Network = vmessQRCode.net;
}
if (Utils.IsNotEmpty(vmessQRCode.type))
if (vmessQRCode.type.IsNotEmpty())
{
item.HeaderType = vmessQRCode.type;
}
@@ -100,7 +100,7 @@ namespace ServiceLib.Handler.Fmt
public static ProfileItem? ResolveStdVmess(string str)
{
ProfileItem item = new()
var item = new ProfileItem
{
ConfigType = EConfigType.VMess,
Security = "auto"
@@ -108,7 +108,9 @@ namespace ServiceLib.Handler.Fmt
var url = Utils.TryUri(str);
if (url == null)
{
return null;
}
item.Address = url.IdnHost;
item.Port = url.Port;
@@ -121,4 +123,3 @@ namespace ServiceLib.Handler.Fmt
return item;
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.Fmt
{
namespace ServiceLib.Handler.Fmt;
public class WireguardFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
@@ -13,7 +13,9 @@ namespace ServiceLib.Handler.Fmt
var url = Utils.TryUri(str);
if (url == null)
{
return null;
}
item.Address = url.IdnHost;
item.Port = url.Port;
@@ -33,33 +35,33 @@ namespace ServiceLib.Handler.Fmt
public static string? ToUri(ProfileItem? item)
{
if (item == null)
{
return null;
string url = string.Empty;
}
string remark = string.Empty;
if (Utils.IsNotEmpty(item.Remarks))
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var dicQuery = new Dictionary<string, string>();
if (Utils.IsNotEmpty(item.PublicKey))
if (item.PublicKey.IsNotEmpty())
{
dicQuery.Add("publickey", Utils.UrlEncode(item.PublicKey));
}
if (Utils.IsNotEmpty(item.Path))
if (item.Path.IsNotEmpty())
{
dicQuery.Add("reserved", Utils.UrlEncode(item.Path));
}
if (Utils.IsNotEmpty(item.RequestHost))
if (item.RequestHost.IsNotEmpty())
{
dicQuery.Add("address", Utils.UrlEncode(item.RequestHost));
}
if (Utils.IsNotEmpty(item.ShortId))
if (item.ShortId.IsNotEmpty())
{
dicQuery.Add("mtu", Utils.UrlEncode(item.ShortId));
}
return ToUri(EConfigType.WireGuard, item.Address, item.Port, item.Id, dicQuery, remark);
}
}
}

View File

@@ -1,7 +1,7 @@
using ReactiveUI;
using ReactiveUI;
namespace ServiceLib.Handler;
namespace ServiceLib.Handler
{
public class NoticeHandler
{
private static readonly Lazy<NoticeHandler> _instance = new(() => new());
@@ -41,4 +41,3 @@ namespace ServiceLib.Handler
SendMessage(msg);
}
}
}

View File

@@ -1,8 +1,8 @@
using System.Net.Sockets;
using System.Text;
namespace ServiceLib.Handler
{
namespace ServiceLib.Handler;
public class PacHandler
{
private static string _configPath;
@@ -15,7 +15,7 @@ namespace ServiceLib.Handler
public static async Task Start(string configPath, int httpPort, int pacPort)
{
_needRestart = (configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning);
_needRestart = configPath != _configPath || httpPort != _httpPort || pacPort != _pacPort || !_isRunning;
_configPath = configPath;
_httpPort = httpPort;
@@ -33,6 +33,13 @@ namespace ServiceLib.Handler
private static async Task InitText()
{
var path = Path.Combine(_configPath, "pac.txt");
// Delete the old pac file
if (File.Exists(path) && Utils.GetFileHash(path).Equals("b590c07280f058ef05d5394aa2f927fe"))
{
File.Delete(path);
}
if (!File.Exists(path))
{
var pac = EmbedUtils.GetEmbedText(Global.PacFileName);
@@ -70,7 +77,7 @@ namespace ServiceLib.Handler
}
var client = await _tcpListener.AcceptTcpClientAsync();
await Task.Run(() => { WriteContent(client); });
await Task.Run(() => WriteContent(client));
}
catch
{
@@ -90,7 +97,9 @@ namespace ServiceLib.Handler
public static void Stop()
{
if (_tcpListener == null)
{
return;
}
try
{
_isRunning = false;
@@ -103,4 +112,3 @@ namespace ServiceLib.Handler
}
}
}
}

View File

@@ -2,8 +2,8 @@ using System.Collections.Concurrent;
//using System.Reactive.Linq;
namespace ServiceLib.Handler
{
namespace ServiceLib.Handler;
public class ProfileExHandler
{
private static readonly Lazy<ProfileExHandler> _instance = new(() => new());
@@ -20,14 +20,6 @@ namespace ServiceLib.Handler
public async Task Init()
{
await InitData();
_ = Task.Run(async () =>
{
while (true)
{
await Task.Delay(1000 * 600);
await SaveQueueIndexIds();
}
});
}
public async Task<ConcurrentBag<ProfileExItem>> GetProfileExs()
@@ -44,7 +36,7 @@ namespace ServiceLib.Handler
private void IndexIdEnqueue(string indexId)
{
if (Utils.IsNotEmpty(indexId) && !_queIndexIds.Contains(indexId))
if (indexId.IsNotEmpty() && !_queIndexIds.Contains(indexId))
{
_queIndexIds.Enqueue(indexId);
}
@@ -187,4 +179,3 @@ namespace ServiceLib.Handler
return _lstProfileEx.Max(t => t == null ? 0 : t.Sort);
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler
{
namespace ServiceLib.Handler;
public class StatisticsHandler
{
private static readonly Lazy<StatisticsHandler> instance = new(() => new());
@@ -161,4 +161,3 @@
}
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.SysProxy
{
namespace ServiceLib.Handler.SysProxy;
public class ProxySettingLinux
{
private static readonly string _proxySetFileName = $"{Global.ProxySetLinuxShellFileName.Replace(Global.NamespaceSample, "")}.sh";
@@ -30,4 +30,3 @@ namespace ServiceLib.Handler.SysProxy
await Utils.GetCliWrapOutput(fileName, args);
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.SysProxy
{
namespace ServiceLib.Handler.SysProxy;
public class ProxySettingOSX
{
private static readonly string _proxySetFileName = $"{Global.ProxySetOSXShellFileName.Replace(Global.NamespaceSample, "")}.sh";
@@ -35,4 +35,3 @@ namespace ServiceLib.Handler.SysProxy
await Utils.GetCliWrapOutput(fileName, args);
}
}
}

View File

@@ -1,8 +1,8 @@
using System.Runtime.InteropServices;
using static ServiceLib.Handler.SysProxy.ProxySettingWindows.InternetConnectionOption;
namespace ServiceLib.Handler.SysProxy
{
namespace ServiceLib.Handler.SysProxy;
public class ProxySettingWindows
{
private const string _regPath = @"Software\Microsoft\Windows\CurrentVersion\Internet Settings";
@@ -60,7 +60,7 @@ namespace ServiceLib.Handler.SysProxy
try
{
// set proxy for LAN
bool result = SetConnectionProxy(null, strProxy, exceptions, type);
var result = SetConnectionProxy(null, strProxy, exceptions, type);
// set proxy for dial up connections
var connections = EnumerateRasEntries();
foreach (var connection in connections)
@@ -71,27 +71,27 @@ namespace ServiceLib.Handler.SysProxy
}
catch
{
SetProxyFallback(strProxy, exceptions, type);
_ = SetProxyFallback(strProxy, exceptions, type);
return false;
}
}
private static bool SetConnectionProxy(string? connectionName, string? strProxy, string? exceptions, int type)
{
InternetPerConnOptionList list = new();
var list = new InternetPerConnOptionList();
int optionCount = 1;
var optionCount = 1;
if (type == 1) // No proxy
{
optionCount = 1;
}
else if (type is 2 or 4) // named proxy or autoproxy script URL
{
optionCount = string.IsNullOrEmpty(exceptions) ? 2 : 3;
optionCount = exceptions.IsNullOrEmpty() ? 2 : 3;
}
int m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT;
PerConnOption m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
var m_Int = (int)PerConnFlags.PROXY_TYPE_DIRECT;
var m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
if (type == 2) // named proxy
{
m_Int = (int)(PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY);
@@ -103,11 +103,9 @@ namespace ServiceLib.Handler.SysProxy
m_Option = PerConnOption.INTERNET_PER_CONN_AUTOCONFIG_URL;
}
//int optionCount = Utile.IsNullOrEmpty(strProxy) ? 1 : (Utile.IsNullOrEmpty(exceptions) ? 2 : 3);
InternetConnectionOption[] options = new InternetConnectionOption[optionCount];
var options = new InternetConnectionOption[optionCount];
// USE a proxy server ...
options[0].m_Option = PerConnOption.INTERNET_PER_CONN_FLAGS;
//options[0].m_Value.m_Int = (int)((optionCount < 2) ? PerConnFlags.PROXY_TYPE_DIRECT : (PerConnFlags.PROXY_TYPE_DIRECT | PerConnFlags.PROXY_TYPE_PROXY));
options[0].m_Value.m_Int = m_Int;
// use THIS proxy server
if (optionCount > 1)
@@ -135,20 +133,20 @@ namespace ServiceLib.Handler.SysProxy
list.dwOptionCount = options.Length;
list.dwOptionError = 0;
int optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
var optSize = Marshal.SizeOf(typeof(InternetConnectionOption));
// make a pointer out of all that ...
nint optionsPtr = Marshal.AllocCoTaskMem(optSize * options.Length); // !! remember to deallocate memory 4
var 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)
for (var i = 0; i < options.Length; ++i)
{
if (Environment.Is64BitOperatingSystem)
{
nint opt = new(optionsPtr.ToInt64() + (i * optSize));
var opt = new nint(optionsPtr.ToInt64() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
else
{
nint opt = new(optionsPtr.ToInt32() + (i * optSize));
var opt = new nint(optionsPtr.ToInt32() + (i * optSize));
Marshal.StructureToPtr(options[i], opt, false);
}
}
@@ -156,14 +154,14 @@ namespace ServiceLib.Handler.SysProxy
list.options = optionsPtr;
// and then make a pointer out of the whole list
nint ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5
var ipcoListPtr = Marshal.AllocCoTaskMem(list.dwSize); // !! remember to deallocate memory 5
Marshal.StructureToPtr(list, ipcoListPtr, false);
// and finally, call the API method!
bool isSuccess = NativeMethods.InternetSetOption(nint.Zero,
var isSuccess = NativeMethods.InternetSetOption(nint.Zero,
InternetOption.INTERNET_OPTION_PER_CONNECTION_OPTION,
ipcoListPtr, list.dwSize);
int returnvalue = 0; // ERROR_SUCCESS
var returnvalue = 0; // ERROR_SUCCESS
if (!isSuccess)
{ // get the error codes, they might be helpful
returnvalue = Marshal.GetLastPInvokeError();
@@ -171,13 +169,15 @@ namespace ServiceLib.Handler.SysProxy
else
{
// Notify the system that the registry settings have been changed and cause them to be refreshed
NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_SETTINGS_CHANGED, nint.Zero, 0);
NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_REFRESH, nint.Zero, 0);
_ = NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_SETTINGS_CHANGED, nint.Zero, 0);
_ = NativeMethods.InternetSetOption(nint.Zero, InternetOption.INTERNET_OPTION_REFRESH, nint.Zero, 0);
}
// FREE the data ASAP
if (list.szConnection != nint.Zero)
{
Marshal.FreeHGlobal(list.szConnection); // release mem 3
}
if (optionCount > 1)
{
Marshal.FreeHGlobal(options[1].m_Value.m_StringPtr); // release mem 1
@@ -204,18 +204,18 @@ namespace ServiceLib.Handler.SysProxy
/// <exception cref="ApplicationException">Error message with win32 error code</exception>
private static IEnumerable<string> EnumerateRasEntries()
{
int entries = 0;
var entries = 0;
// attempt to query with 1 entry buffer
RASENTRYNAME[] rasEntryNames = new RASENTRYNAME[1];
int bufferSize = Marshal.SizeOf(typeof(RASENTRYNAME));
var rasEntryNames = new RASENTRYNAME[1];
var bufferSize = Marshal.SizeOf(typeof(RASENTRYNAME));
rasEntryNames[0].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
uint result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
var result = NativeMethods.RasEnumEntries(null, null, rasEntryNames, ref bufferSize, ref entries);
// increase buffer if the buffer is not large enough
if (result == (uint)ErrorCode.ERROR_BUFFER_TOO_SMALL)
{
rasEntryNames = new RASENTRYNAME[bufferSize / Marshal.SizeOf(typeof(RASENTRYNAME))];
for (int i = 0; i < rasEntryNames.Length; i++)
for (var i = 0; i < rasEntryNames.Length; i++)
{
rasEntryNames[i].dwSize = Marshal.SizeOf(typeof(RASENTRYNAME));
}
@@ -225,7 +225,7 @@ namespace ServiceLib.Handler.SysProxy
if (result == 0)
{
var entryNames = new List<string>();
for (int i = 0; i < entries; i++)
for (var i = 0; i < entries; i++)
{
entryNames.Add(rasEntryNames[i].szEntryName);
}
@@ -357,4 +357,3 @@ namespace ServiceLib.Handler.SysProxy
);
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler.SysProxy
{
namespace ServiceLib.Handler.SysProxy;
public static class SysProxyHandler
{
private static readonly string _tag = "SysProxyHandler";
@@ -75,7 +75,7 @@
}
strProxy = string.Empty;
if (Utils.IsNullOrEmpty(config.SystemProxyItem.SystemProxyAdvancedProtocol))
if (config.SystemProxyItem.SystemProxyAdvancedProtocol.IsNullOrEmpty())
{
strProxy = $"{Global.Loopback}:{port}";
}
@@ -96,4 +96,3 @@
ProxySettingWindows.SetProxy(strProxy, "", 4);
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Handler
{
namespace ServiceLib.Handler;
public class TaskHandler
{
private static readonly Lazy<TaskHandler> _instance = new(() => new());
@@ -7,66 +7,91 @@
public void RegUpdateTask(Config config, Action<bool, string> updateFunc)
{
Task.Run(() => UpdateTaskRunSubscription(config, updateFunc));
Task.Run(() => UpdateTaskRunGeo(config, updateFunc));
Task.Run(() => ScheduledTasks(config, updateFunc));
}
private async Task ScheduledTasks(Config config, Action<bool, string> updateFunc)
{
Logging.SaveLog("Setup Scheduled Tasks");
var numOfExecuted = 1;
while (true)
{
//1 minute
await Task.Delay(1000 * 60);
//Execute once 1 minute
await UpdateTaskRunSubscription(config, updateFunc);
//Execute once 20 minute
if (numOfExecuted % 20 == 0)
{
//Logging.SaveLog("Execute save config");
await ConfigHandler.SaveConfig(config);
await ProfileExHandler.Instance.SaveTo();
}
//Execute once 1 hour
if (numOfExecuted % 60 == 0)
{
//Logging.SaveLog("Execute delete expired files");
FileManager.DeleteExpiredFiles(Utils.GetBinConfigPath(), DateTime.Now.AddHours(-1));
FileManager.DeleteExpiredFiles(Utils.GetLogPath(), DateTime.Now.AddMonths(-1));
FileManager.DeleteExpiredFiles(Utils.GetTempPath(), DateTime.Now.AddMonths(-1));
//Check once 1 hour
await UpdateTaskRunGeo(config, numOfExecuted / 60, updateFunc);
}
numOfExecuted++;
}
}
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())
var lstSubs = (await AppHandler.Instance.SubItems())?
.Where(t => t.AutoUpdateInterval > 0)
.Where(t => updateTime - t.UpdateTime >= t.AutoUpdateInterval * 60)
.ToList();
if (lstSubs is not { Count: > 0 })
{
return;
}
Logging.SaveLog("Execute update subscription");
var updateHandle = new UpdateService();
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);
{
Logging.SaveLog($"Update subscription end. {msg}");
}
});
item.UpdateTime = updateTime;
await ConfigHandler.AddSubItem(config, item);
await Task.Delay(5000);
}
await Task.Delay(60000);
await Task.Delay(1000);
}
}
private async Task UpdateTaskRunGeo(Config config, Action<bool, string> updateFunc)
private async Task UpdateTaskRunGeo(Config config, int hours, Action<bool, string> updateFunc)
{
var autoUpdateGeoTime = DateTime.Now;
//await Task.Delay(1000 * 120);
Logging.SaveLog("UpdateTaskRunGeo");
if (config.GuiItem.AutoUpdateInterval > 0 && hours > 0 && hours % config.GuiItem.AutoUpdateInterval == 0)
{
Logging.SaveLog("Execute update geo files");
var updateHandle = new UpdateService();
while (true)
{
await Task.Delay(1000 * 3600);
var dtNow = DateTime.Now;
if (config.GuiItem.AutoUpdateInterval > 0)
{
if ((dtNow - autoUpdateGeoTime).Hours % config.GuiItem.AutoUpdateInterval == 0)
{
await updateHandle.UpdateGeoFileAll(config, (bool success, string msg) =>
{
updateFunc?.Invoke(false, msg);
});
autoUpdateGeoTime = dtNow;
}
}
}
}
}
}

View File

@@ -1,14 +1,14 @@
using System.Net;
using WebDav;
namespace ServiceLib.Handler
{
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 readonly Config? _config;
private WebDavClient? _client;
private string? _lastDescription;
private string _webDir = Global.AppName + "_backup";
@@ -62,7 +62,9 @@ namespace ServiceLib.Handler
private async Task<bool> TryCreateDir()
{
if (_client is null)
{
return false;
}
try
{
var result2 = await _client.Mkcol(_webDir);
@@ -177,4 +179,3 @@ namespace ServiceLib.Handler
public string GetLastError() => _lastDescription ?? string.Empty;
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models
{
namespace ServiceLib.Models;
public class CheckUpdateModel
{
public bool? IsSelected { get; set; }
@@ -8,4 +8,3 @@
public string? FileName { get; set; }
public bool? IsFinished { get; set; }
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models
{
namespace ServiceLib.Models;
public class ClashConnectionModel
{
public string? Id { get; set; }
@@ -14,4 +14,3 @@
public string? Elapsed { get; set; }
public string? Chain { get; set; }
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models
{
namespace ServiceLib.Models;
public class ClashConnections
{
public ulong downloadTotal { get; set; }
@@ -34,4 +34,3 @@
public string? processPath { get; set; }
public string? remoteDestination { get; set; }
}
}

View File

@@ -1,7 +1,7 @@
using static ServiceLib.Models.ClashProxies;
using static ServiceLib.Models.ClashProxies;
namespace ServiceLib.Models;
namespace ServiceLib.Models
{
public class ClashProviders
{
public Dictionary<string, ProvidersItem>? providers { get; set; }
@@ -14,4 +14,3 @@ namespace ServiceLib.Models
public string? vehicleType { get; set; }
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models
{
namespace ServiceLib.Models;
public class ClashProxies
{
public Dictionary<string, ProxiesItem>? proxies { get; set; }
@@ -21,4 +21,3 @@
public int delay { get; set; }
}
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models
{
namespace ServiceLib.Models;
[Serializable]
public class ClashProxyModel
{
@@ -15,4 +15,3 @@
public bool IsActive { get; set; }
}
}

View File

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

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models
{
namespace ServiceLib.Models;
public class ComboItem
{
public string? ID
@@ -12,4 +12,3 @@
get; set;
}
}
}

View File

@@ -1,8 +1,5 @@
namespace ServiceLib.Models
{
/// <summary>
/// 本软件配置文件实体类
/// </summary>
namespace ServiceLib.Models;
[Serializable]
public class Config
{
@@ -54,4 +51,3 @@
#endregion other entities
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models
{
namespace ServiceLib.Models;
[Serializable]
public class CoreBasicItem
{
@@ -147,7 +147,6 @@ namespace ServiceLib.Models
public int Mtu { get; set; }
public bool EnableExInbound { get; set; }
public bool EnableIPv6Address { get; set; }
public string? LinuxSudoPwd { get; set; }
}
[Serializable]
@@ -157,6 +156,7 @@ namespace ServiceLib.Models
public string SpeedTestUrl { get; set; }
public string SpeedPingTestUrl { get; set; }
public int MixedConcurrencyCount { get; set; }
public string IPAPIUrl { get; set; }
}
[Serializable]
@@ -197,6 +197,7 @@ namespace ServiceLib.Models
{
public int UpMbps { get; set; }
public int DownMbps { get; set; }
public int HopInterval { get; set; } = 30;
}
[Serializable]
@@ -244,4 +245,3 @@ namespace ServiceLib.Models
public string? Length { get; set; }
public string? Interval { get; set; }
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models
{
namespace ServiceLib.Models;
[Serializable]
public class CoreInfo
{
@@ -18,4 +18,3 @@ namespace ServiceLib.Models
public string? VersionArg { get; set; }
public bool AbsolutePath { get; set; }
}
}

View File

@@ -1,7 +1,7 @@
using SQLite;
using SQLite;
namespace ServiceLib.Models;
namespace ServiceLib.Models
{
[Serializable]
public class DNSItem
{
@@ -17,4 +17,3 @@ namespace ServiceLib.Models
public string? DomainStrategy4Freedom { get; set; }
public string? DomainDNSAddress { get; set; }
}
}

View File

@@ -1,7 +1,7 @@
using System.Text.Json.Serialization;
using System.Text.Json.Serialization;
namespace ServiceLib.Models;
namespace ServiceLib.Models
{
public class GitHubReleaseAsset
{
[JsonPropertyName("url")] public string? Url { get; set; }
@@ -65,4 +65,3 @@ namespace ServiceLib.Models
[JsonPropertyName("body")] public string? Body { get; set; }
}
}

View File

@@ -1,13 +1,19 @@
namespace ServiceLib.Models
{
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? clientIp { get; set; }
public string? ip_addr { get; set; }
public string? query { get; set; }
public string? country { get; set; }
public string? country_name { get; set; }
public string? country_code { get; set; }
public string? countryCode { get; set; }
public LocationInfo? location { get; set; }
}
public class LocationInfo
{
public string? country_code { get; set; }
}

View File

@@ -1,7 +1,7 @@
using SQLite;
namespace ServiceLib.Models
{
namespace ServiceLib.Models;
[Serializable]
public class ProfileExItem
{
@@ -13,4 +13,3 @@ namespace ServiceLib.Models
public int Sort { get; set; }
public string? Message { get; set; }
}
}

View File

@@ -1,7 +1,7 @@
using SQLite;
using SQLite;
namespace ServiceLib.Models;
namespace ServiceLib.Models
{
[Serializable]
public class ProfileItem
{
@@ -48,12 +48,12 @@ namespace ServiceLib.Models
public List<string>? GetAlpn()
{
return Utils.IsNullOrEmpty(Alpn) ? null : Utils.String2List(Alpn);
return Alpn.IsNullOrEmpty() ? null : Utils.String2List(Alpn);
}
public string GetNetwork()
{
if (Utils.IsNullOrEmpty(Network) || !Global.Networks.Contains(Network))
if (Network.IsNullOrEmpty() || !Global.Networks.Contains(Network))
{
return Global.DefaultNetwork;
}
@@ -69,6 +69,7 @@ namespace ServiceLib.Models
public int ConfigVersion { get; set; }
public string Address { get; set; }
public int Port { get; set; }
public string Ports { get; set; }
public string Id { get; set; }
public int AlterId { get; set; }
public string Security { get; set; }
@@ -93,4 +94,3 @@ namespace ServiceLib.Models
public string SpiderX { get; set; }
public string Extra { get; set; }
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models
{
namespace ServiceLib.Models;
[Serializable]
public class ProfileItemModel : ProfileItem
{
@@ -15,4 +15,3 @@
public string TotalUp { get; set; }
public string TotalDown { get; set; }
}
}

View File

@@ -1,5 +1,5 @@
namespace ServiceLib.Models
{
namespace ServiceLib.Models;
public class RetResult
{
public bool Success { get; set; }
@@ -24,4 +24,3 @@
Data = data;
}
}
}

View File

@@ -1,7 +1,7 @@
using SQLite;
using SQLite;
namespace ServiceLib.Models;
namespace ServiceLib.Models
{
[Serializable]
public class RoutingItem
{
@@ -20,4 +20,3 @@ namespace ServiceLib.Models
public string DomainStrategy4Singbox { get; set; }
public int Sort { get; set; }
}
}

View File

@@ -1,8 +1,7 @@
namespace ServiceLib.Models
{
namespace ServiceLib.Models;
[Serializable]
public class RoutingItemModel : RoutingItem
{
public bool IsActive { get; set; }
}
}

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