Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e1d70c29e | ||
|
|
a47395cc6c | ||
|
|
e3bc370019 | ||
|
|
825b7bd6a6 | ||
|
|
2705f5c160 | ||
|
|
fd8185b83e | ||
|
|
b3866d1d90 | ||
|
|
bb2fbfaafa | ||
|
|
efa1b2ed54 | ||
|
|
60aa010096 | ||
|
|
68880f0c9b | ||
|
|
7310126a39 | ||
|
|
389ac32094 | ||
|
|
2676e97ca0 | ||
|
|
1c231ee897 | ||
|
|
549988b2de | ||
|
|
18963fa14b | ||
|
|
81da544108 | ||
|
|
d88d1b2e0c | ||
|
|
a4081b1a7f | ||
|
|
f76a1f1a8e | ||
|
|
d60716cf4f | ||
|
|
91a0ad5424 | ||
|
|
bb3bfaaffc | ||
|
|
1a19f59a6b | ||
|
|
e68929a32a | ||
|
|
c274360cc2 | ||
|
|
5755230000 | ||
|
|
388896f161 | ||
|
|
9b4304f016 | ||
|
|
b061a00057 | ||
|
|
41751db06f | ||
|
|
e4927c4d0c | ||
|
|
aef01473ce | ||
|
|
a280391ba9 | ||
|
|
fe3b69a267 | ||
|
|
35589138d5 | ||
|
|
31c000db62 | ||
|
|
ca62e2200d | ||
|
|
6c1c2a2b44 | ||
|
|
2dfc23bbac | ||
|
|
8d2890790e | ||
|
|
638714804b | ||
|
|
d90a48577c | ||
|
|
bd5cf8b70d | ||
|
|
d2c911da51 | ||
|
|
c30c554c8f | ||
|
|
21ad84beb3 | ||
|
|
9943207e1f | ||
|
|
4116daf63c | ||
|
|
a2838b8217 | ||
|
|
c490e9f802 | ||
|
|
1f520b2f8f | ||
|
|
41c9269c9a | ||
|
|
be2ee1a5df | ||
|
|
6f1025c73e | ||
|
|
24f8691cf9 | ||
|
|
3933319e32 | ||
|
|
e8fbde707b | ||
|
|
e60f8e7c4a | ||
|
|
91a484dfcd |
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Telegram Channel
|
||||
url: https://t.me/Netch
|
||||
about: Telegram Channel
|
||||
- name: Telegram Group
|
||||
url: https://t.me/Netch_Discuss_Group
|
||||
about: Telegram Group
|
||||
24
.github/workflows/build.yml
vendored
@@ -1,5 +1,5 @@
|
||||
name: Netch CI
|
||||
on: [push]
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
@@ -13,9 +13,8 @@ jobs:
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Update submodules
|
||||
uses: snickerbockers/submodules-init@v4
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Restore NuGet Packages Cache
|
||||
uses: actions/cache@v2
|
||||
@@ -28,17 +27,18 @@ jobs:
|
||||
- name: Restore NuGet Package
|
||||
run: nuget restore Netch.sln
|
||||
|
||||
- name: Build .NET 4.8
|
||||
- name: Build Solution
|
||||
shell: pwsh
|
||||
run: |
|
||||
.\BUILD.ps1
|
||||
New-Item -ItemType Directory -Path C:\builtfiles -Force > $null
|
||||
Compress-Archive -Path Netch\bin\x64\Release\win-x64\* -DestinationPath C:\builtfiles\Netch.zip
|
||||
echo "::set-env name=Netch_SHA256::$(.\GetSHA256.ps1 C:\builtfiles\Netch.zip)"
|
||||
7z a -mx9 C:\builtfiles\Netch.7z .\Netch\bin\x64\Release\win-x64\
|
||||
7z rn C:\builtfiles\Netch.7z win-x64 Netch
|
||||
echo "::set-env name=Netch_SHA256::$(.\GetSHA256.ps1 C:\builtfiles\Netch.7z)"
|
||||
echo "::set-env name=Netch_EXE_SHA256::$(.\GetSHA256.ps1 Netch\bin\x64\Release\win-x64\Netch.exe)"
|
||||
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v1
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: Netch
|
||||
path: Netch\bin\x64\Release\win-x64
|
||||
@@ -53,17 +53,17 @@ jobs:
|
||||
prerelease: true
|
||||
draft: false
|
||||
files: |
|
||||
C:\builtfiles\Netch.zip
|
||||
C:\builtfiles\Netch.7z
|
||||
body: |
|
||||
[](https://t.me/Netch) [](https://t.me/Netch_Discuss_Group)
|
||||
|
||||
## 更新日志
|
||||
* 这是 GitHub Action 自动化部署,更新日志应该很快会手动更新
|
||||
* 这是 GitHub Actions 自动化部署,更新日志应该很快会手动更新
|
||||
|
||||
## 校验和
|
||||
| 文件名 | SHA256 |
|
||||
| :- | :- |
|
||||
| Netch.zip | ${{ env.Netch_SHA256 }} |
|
||||
| Netch.7z | ${{ env.Netch_SHA256 }} |
|
||||
|
||||
# Deploy:
|
||||
# needs: [build]
|
||||
|
||||
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
/.vs
|
||||
/packages
|
||||
/packages
|
||||
.idea/
|
||||
|
||||
14
BUILD.ps1
@@ -16,19 +16,17 @@ function Build-NetFrameworkx64
|
||||
|
||||
$outdir = "$net_baseoutput\x64"
|
||||
|
||||
msbuild -v:m -m -t:Build /p:Configuration="Release" /p:Platform="x64" /p:TargetFramework=net48 /p:Runtimeidentifier=win-x64 /restore
|
||||
msbuild -v:n -m:1 /p:Configuration="Release" `
|
||||
/p:Platform="x64" `
|
||||
/p:TargetFramework=net48 `
|
||||
/p:Runtimeidentifier=win-x64 `
|
||||
/restore
|
||||
if ($LASTEXITCODE) { cd $mainDir ; exit $LASTEXITCODE }
|
||||
|
||||
Write-Host 'Build x64 Completed, start copy bin, mode, i18n file'
|
||||
Remove-Item -Recurse -Force "$net_baseoutput\x64\Release\win-x64\bin\tap-driver"
|
||||
Copy-Item -Recurse "$mainDir\binaries\*" "$net_baseoutput\x64\Release\win-x64\bin"
|
||||
Copy-Item -Recurse "$mainDir\modes\mode\*" "$net_baseoutput\x64\Release\win-x64\mode"
|
||||
Copy-Item -Recurse "$mainDir\translations\i18n\*" "$net_baseoutput\x64\Release\win-x64\i18n"
|
||||
|
||||
Write-Host 'Build done'
|
||||
}
|
||||
|
||||
cd $mainDir\Netch
|
||||
cd $mainDir
|
||||
Build-NetFrameworkx64
|
||||
cd $mainDir
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Netch", "Netch\Netch.csproj
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetchLib", "NetchLib\NetchLib.csproj", "{A8715AF4-ACC6-43F9-9381-4294C5360623}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetchUpdater", "NetchUpdater\NetchUpdater.csproj", "{828318A8-9B90-4A5F-BD6B-E632CC9D8933}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
@@ -21,6 +23,10 @@ Global
|
||||
{A8715AF4-ACC6-43F9-9381-4294C5360623}.Debug|x64.Build.0 = Debug|x64
|
||||
{A8715AF4-ACC6-43F9-9381-4294C5360623}.Release|x64.ActiveCfg = Release|x64
|
||||
{A8715AF4-ACC6-43F9-9381-4294C5360623}.Release|x64.Build.0 = Release|x64
|
||||
{828318A8-9B90-4A5F-BD6B-E632CC9D8933}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{828318A8-9B90-4A5F-BD6B-E632CC9D8933}.Debug|x64.Build.0 = Debug|x64
|
||||
{828318A8-9B90-4A5F-BD6B-E632CC9D8933}.Release|x64.ActiveCfg = Release|x64
|
||||
{828318A8-9B90-4A5F-BD6B-E632CC9D8933}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Netch.Controllers
|
||||
{
|
||||
Name = "DNS Service";
|
||||
MainFile = "unbound.exe";
|
||||
RedirectStd = false;
|
||||
// RedirectStd = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -7,6 +6,8 @@ namespace Netch.Controllers
|
||||
{
|
||||
public class SSController : EncryptedProxy
|
||||
{
|
||||
private bool dllFlag = false;
|
||||
|
||||
public SSController()
|
||||
{
|
||||
Name = "Shadowsocks";
|
||||
@@ -20,8 +21,9 @@ namespace Netch.Controllers
|
||||
//从DLL启动Shaowsocks
|
||||
if (Global.Settings.BootShadowsocksFromDLL && (mode.Type == 0 || mode.Type == 1 || mode.Type == 2))
|
||||
{
|
||||
dllFlag = true;
|
||||
State = State.Starting;
|
||||
var client = Encoding.UTF8.GetBytes($"0.0.0.0:{Global.Settings.Socks5LocalPort}");
|
||||
var client = Encoding.UTF8.GetBytes($"{LocalAddress}:{Socks5LocalPort}");
|
||||
var remote = Encoding.UTF8.GetBytes($"{server.Hostname}:{server.Port}");
|
||||
var passwd = Encoding.UTF8.GetBytes($"{server.Password}");
|
||||
var method = Encoding.UTF8.GetBytes($"{server.EncryptMethod}");
|
||||
@@ -49,7 +51,8 @@ namespace Netch.Controllers
|
||||
#region Argument
|
||||
|
||||
var argument = new StringBuilder();
|
||||
argument.Append($"-s {server.Hostname} -p {server.Port} -b {Global.Settings.LocalAddress} -l {Global.Settings.Socks5LocalPort} -m {server.EncryptMethod} -k \"{server.Password}\" -u");
|
||||
argument.Append(
|
||||
$"-s {server.Hostname} -p {server.Port} -b {LocalAddress} -l {Socks5LocalPort} -m {server.EncryptMethod} -k \"{server.Password}\" -u");
|
||||
if (!string.IsNullOrWhiteSpace(server.Plugin) && !string.IsNullOrWhiteSpace(server.PluginOption))
|
||||
argument.Append($" --plugin {server.Plugin} --plugin-opts \"{server.PluginOption}\"");
|
||||
if (mode.BypassChina) argument.Append(" --acl default.acl");
|
||||
@@ -64,7 +67,7 @@ namespace Netch.Controllers
|
||||
/// </summary>
|
||||
public override void Stop()
|
||||
{
|
||||
if (Global.Settings.BootShadowsocksFromDLL) NativeMethods.Shadowsocks.Stop();
|
||||
if (dllFlag) NativeMethods.Shadowsocks.Stop();
|
||||
else
|
||||
StopInstance();
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Netch.Controllers
|
||||
if (!string.IsNullOrEmpty(server.OBFSParam)) argument.Append($" -g \"{server.OBFSParam}\"");
|
||||
}
|
||||
|
||||
argument.Append($" -b {Global.Settings.LocalAddress} -l {Global.Settings.Socks5LocalPort} -u");
|
||||
argument.Append($" -b {LocalAddress} -l {Socks5LocalPort} -u");
|
||||
if (mode.BypassChina) argument.Append(" --acl default.acl");
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -22,8 +22,8 @@ namespace Netch.Controllers
|
||||
{
|
||||
File.WriteAllText("data\\last.json", JsonConvert.SerializeObject(new Trojan
|
||||
{
|
||||
local_addr = Global.Settings.LocalAddress,
|
||||
local_port = Global.Settings.Socks5LocalPort,
|
||||
local_addr = LocalAddress,
|
||||
local_port = Socks5LocalPort,
|
||||
remote_addr = server.Hostname,
|
||||
remote_port = server.Port,
|
||||
password = new List<string>
|
||||
|
||||
@@ -27,8 +27,8 @@ namespace Netch.Controllers
|
||||
new VMess.Inbounds
|
||||
{
|
||||
settings = new VMess.InboundSettings(),
|
||||
port = Global.Settings.Socks5LocalPort,
|
||||
listen = Global.Settings.LocalAddress
|
||||
port = Socks5LocalPort,
|
||||
listen = LocalAddress
|
||||
}
|
||||
},
|
||||
outbounds = new List<VMess.Outbounds>
|
||||
|
||||
@@ -1,9 +1,26 @@
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public abstract class EncryptedProxy : Controller
|
||||
{
|
||||
private int? _socks5Port;
|
||||
|
||||
public int Socks5LocalPort
|
||||
{
|
||||
get => _socks5Port ?? Global.Settings.Socks5LocalPort;
|
||||
set => _socks5Port = value;
|
||||
}
|
||||
|
||||
private string _localAddress;
|
||||
|
||||
public string LocalAddress
|
||||
{
|
||||
get => _localAddress ?? Global.Settings.LocalAddress;
|
||||
set => _localAddress = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动
|
||||
/// </summary>
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace Netch.Controllers
|
||||
/// </summary>
|
||||
private string _logPath;
|
||||
|
||||
private FileStream _logFileStream;
|
||||
private readonly StringBuilder _logBuffer = new StringBuilder();
|
||||
|
||||
/// <summary>
|
||||
/// 程序输出的编码,
|
||||
@@ -92,8 +92,7 @@ namespace Netch.Controllers
|
||||
RedirectStandardOutput = RedirectStd,
|
||||
UseShellExecute = !RedirectStd,
|
||||
WindowStyle = ProcessWindowStyle.Hidden
|
||||
},
|
||||
EnableRaisingEvents = true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -111,13 +110,13 @@ namespace Netch.Controllers
|
||||
{
|
||||
// 初始化程序
|
||||
InitInstance(argument);
|
||||
Instance.EnableRaisingEvents = true;
|
||||
if (RedirectStd)
|
||||
{
|
||||
// 清理日志
|
||||
_logPath ??= Path.Combine(Global.NetchDir, $"logging\\{Name}.log");
|
||||
if (_logFileStream == null && File.Exists(_logPath))
|
||||
if (File.Exists(_logPath))
|
||||
File.Delete(_logPath);
|
||||
_logFileStream = new FileStream(_logPath, FileMode.Create, FileAccess.Write);
|
||||
|
||||
Instance.OutputDataReceived += OnOutputDataReceived;
|
||||
Instance.ErrorDataReceived += OnOutputDataReceived;
|
||||
@@ -129,12 +128,13 @@ namespace Netch.Controllers
|
||||
Instance.Start();
|
||||
if (priority != ProcessPriorityClass.Normal)
|
||||
Instance.PriorityClass = priority;
|
||||
if (!RedirectStd || StartedKeywords.Count == 0) return true;
|
||||
if (!RedirectStd) return true;
|
||||
// 启动日志重定向
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
_writeStreamTimer.Elapsed += SaveStreamTimerEvent;
|
||||
_writeStreamTimer.Enabled = true;
|
||||
SaveBufferTimer.Elapsed += SaveBufferTimerEvent;
|
||||
SaveBufferTimer.Enabled = true;
|
||||
if (StartedKeywords.Count == 0) return true;
|
||||
// 等待启动
|
||||
for (var i = 0; i < 1000; i++)
|
||||
{
|
||||
@@ -161,18 +161,17 @@ namespace Netch.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
private static System.Timers.Timer _writeStreamTimer = new System.Timers.Timer(300) {AutoReset = true};
|
||||
private static readonly System.Timers.Timer SaveBufferTimer = new System.Timers.Timer(300) {AutoReset = true};
|
||||
|
||||
private void OnExited(object sender, EventArgs e)
|
||||
{
|
||||
if (RedirectStd)
|
||||
{
|
||||
_writeStreamTimer.Enabled = false;
|
||||
Thread.Sleep(100); // 等待 Write() 写入流
|
||||
_logFileStream.Close();
|
||||
_logFileStream = null;
|
||||
SaveBufferTimer.Enabled = false;
|
||||
}
|
||||
|
||||
SaveBufferTimerEvent(null, null);
|
||||
|
||||
State = State.Stopped;
|
||||
}
|
||||
|
||||
@@ -187,9 +186,9 @@ namespace Netch.Controllers
|
||||
if (e.Data == null)
|
||||
return;
|
||||
|
||||
var info = Encoding.GetEncoding(InstanceOutputEncoding).GetBytes(e.Data + Global.EOF);
|
||||
Task.Run(() => Write(info));
|
||||
var info = Encoding.GetEncoding(InstanceOutputEncoding).GetBytes(e.Data);
|
||||
var str = Encoding.UTF8.GetString(info);
|
||||
Write(str);
|
||||
// 检查启动
|
||||
if (State == State.Starting)
|
||||
{
|
||||
@@ -205,42 +204,30 @@ namespace Netch.Controllers
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private async void SaveStreamTimerEvent(object sender, EventArgs e)
|
||||
private void SaveBufferTimerEvent(object sender, EventArgs e)
|
||||
{
|
||||
if (_logFileStream == null) return;
|
||||
try
|
||||
{
|
||||
await _logFileStream.FlushAsync();
|
||||
if (_logPath != null && _logBuffer != null)
|
||||
{
|
||||
File.AppendAllText(_logPath, _logBuffer.ToString());
|
||||
_logBuffer.Clear();
|
||||
}
|
||||
}
|
||||
catch
|
||||
catch (Exception exception)
|
||||
{
|
||||
// ignored
|
||||
Logging.Warning($"写入 {Name} 日志错误:\n" + exception.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly object _fileLocker = new object();
|
||||
|
||||
/// <summary>
|
||||
/// 写入日志文件流
|
||||
/// 写入日志文件缓冲
|
||||
/// </summary>
|
||||
/// <param name="info"></param>
|
||||
/// <returns>转码后的字符串</returns>
|
||||
private void Write(byte[] info)
|
||||
private void Write(string info)
|
||||
{
|
||||
if (info == null)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
lock (_fileLocker)
|
||||
{
|
||||
_logFileStream.Write(info, 0, info.Length);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error($"写入 {Name} 日志错误:\n" + e.Message);
|
||||
}
|
||||
_logBuffer.Append(info + Global.EOF);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,7 @@ using Netch.Utils;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class MainController
|
||||
public static class MainController
|
||||
{
|
||||
/// <summary>
|
||||
/// 记录当前使用的端口
|
||||
@@ -20,14 +20,14 @@ namespace Netch.Controllers
|
||||
/// </summary>
|
||||
public static readonly List<int> UsingPorts = new List<int>();
|
||||
|
||||
public EncryptedProxy pEncryptedProxyController { get; private set; }
|
||||
public static EncryptedProxy EncryptedProxyController { get; private set; }
|
||||
|
||||
public ModeController pModeController { get; private set; }
|
||||
public static ModeController ModeController { get; private set; }
|
||||
|
||||
private Server _savedServer;
|
||||
private Mode _savedMode;
|
||||
private static Server _savedServer;
|
||||
private static Mode _savedMode;
|
||||
|
||||
public string PortInfo
|
||||
public static string PortInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -48,7 +48,11 @@ namespace Netch.Controllers
|
||||
|
||||
if (_savedMode.Type == 3 || _savedMode.Type == 5)
|
||||
// 有HTTP
|
||||
text.Append($" | HTTP {i18N.Translate("Local Port", ": ")}{_httpPort}");
|
||||
{
|
||||
if (_savedServer.Type != "Socks5")
|
||||
text.Append(" | ");
|
||||
text.Append($"HTTP {i18N.Translate("Local Port", ": ")}{_httpPort}");
|
||||
}
|
||||
|
||||
return $" ({text})";
|
||||
}
|
||||
@@ -57,12 +61,13 @@ namespace Netch.Controllers
|
||||
/// <summary>
|
||||
/// NTT 控制器
|
||||
/// </summary>
|
||||
public NTTController pNTTController = new NTTController();
|
||||
public static readonly NTTController NTTController = new NTTController();
|
||||
|
||||
private static string _localAddress;
|
||||
private static int _redirectorTCPPort;
|
||||
private static int _httpPort;
|
||||
private static int _socks5Port;
|
||||
|
||||
private string _localAddress;
|
||||
private int _redirectorTCPPort;
|
||||
private int _httpPort;
|
||||
private int _socks5Port;
|
||||
|
||||
[DllImport("dnsapi", EntryPoint = "DnsFlushResolverCache")]
|
||||
public static extern uint FlushDNSResolverCache();
|
||||
@@ -73,16 +78,16 @@ namespace Netch.Controllers
|
||||
/// <param name="server">服务器</param>
|
||||
/// <param name="mode">模式</param>
|
||||
/// <returns>是否启动成功</returns>
|
||||
public async Task<bool> Start(Server server, Mode mode)
|
||||
public static async Task<bool> Start(Server server, Mode mode)
|
||||
{
|
||||
Logging.Info($"启动主控制器: {server.Type} [{mode.Type}]{mode.Remark}");
|
||||
|
||||
#region Record Settings
|
||||
|
||||
_httpPort = Global.Settings.HTTPLocalPort;
|
||||
_socks5Port = Global.Settings.Socks5LocalPort;
|
||||
_socks5Port = server.Type != "Socks5" ? Global.Settings.Socks5LocalPort : server.Port;
|
||||
_redirectorTCPPort = Global.Settings.RedirectorTCPPort;
|
||||
_localAddress = Global.Settings.LocalAddress;
|
||||
_localAddress = server.Type != "Socks5" ? Global.Settings.LocalAddress : "127.0.0.1";
|
||||
_savedServer = server;
|
||||
_savedMode = mode;
|
||||
|
||||
@@ -98,45 +103,48 @@ namespace Netch.Controllers
|
||||
}
|
||||
else
|
||||
{
|
||||
pEncryptedProxyController = server.Type switch
|
||||
EncryptedProxyController = server.Type switch
|
||||
{
|
||||
"SS" => new SSController(),
|
||||
"SSR" => new SSRController(),
|
||||
"VMess" => new VMessController(),
|
||||
"Trojan" => new TrojanController(),
|
||||
_ => pEncryptedProxyController
|
||||
_ => EncryptedProxyController
|
||||
};
|
||||
|
||||
KillProcessByName(pEncryptedProxyController.MainFile);
|
||||
KillProcessByName(EncryptedProxyController.MainFile);
|
||||
|
||||
// 检查端口是否被占用
|
||||
var isPortNotAvailable = false;
|
||||
#region 检查端口是否被占用
|
||||
|
||||
var portNotAvailable = false;
|
||||
if (_savedServer.Type != "Socks5")
|
||||
{
|
||||
isPortNotAvailable |= PortCheckAndShowMessageBox(_socks5Port, "Socks5");
|
||||
portNotAvailable |= PortCheckAndShowMessageBox(_socks5Port, "Socks5");
|
||||
}
|
||||
|
||||
switch (_savedMode.Type)
|
||||
{
|
||||
case 0:
|
||||
isPortNotAvailable |= PortCheckAndShowMessageBox(_redirectorTCPPort, "Redirector TCP");
|
||||
portNotAvailable |= PortCheckAndShowMessageBox(_redirectorTCPPort, "Redirector TCP");
|
||||
break;
|
||||
case 3:
|
||||
case 5:
|
||||
isPortNotAvailable |= PortCheckAndShowMessageBox(_httpPort, "HTTP");
|
||||
portNotAvailable |= PortCheckAndShowMessageBox(_httpPort, "HTTP");
|
||||
break;
|
||||
}
|
||||
|
||||
if (isPortNotAvailable)
|
||||
if (portNotAvailable)
|
||||
{
|
||||
Logging.Error("主控制器启动失败: 端口被占用");
|
||||
return false;
|
||||
}
|
||||
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting ", pEncryptedProxyController.Name));
|
||||
#endregion
|
||||
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting ", EncryptedProxyController.Name));
|
||||
try
|
||||
{
|
||||
result = await Task.Run(() => pEncryptedProxyController.Start(server, mode));
|
||||
result = await Task.Run(() => EncryptedProxyController.Start(server, mode));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -148,32 +156,32 @@ namespace Netch.Controllers
|
||||
if (result)
|
||||
{
|
||||
// 加密代理成功启动
|
||||
UsingPorts.Add(Global.Settings.Socks5LocalPort); // 记录Socks5使用端口
|
||||
UsingPorts.Add(_socks5Port);
|
||||
|
||||
switch (mode.Type)
|
||||
{
|
||||
case 0: // 进程代理模式
|
||||
pModeController = new NFController();
|
||||
ModeController = new NFController();
|
||||
break;
|
||||
case 1: // TUN/TAP 黑名单代理模式
|
||||
case 2: // TUN/TAP 白名单代理模式
|
||||
pModeController = new TUNTAPController();
|
||||
ModeController = new TUNTAPController();
|
||||
break;
|
||||
case 3:
|
||||
case 5:
|
||||
pModeController = new HTTPController();
|
||||
ModeController = new HTTPController();
|
||||
break;
|
||||
case 4: // Socks5 代理模式,不需要启动额外的Server
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (pModeController != null)
|
||||
if (ModeController != null)
|
||||
{
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting ", pModeController.Name));
|
||||
Global.MainForm.StatusText(i18N.Translate("Starting ", ModeController.Name));
|
||||
try
|
||||
{
|
||||
result = await Task.Run(() => pModeController.Start(server, mode));
|
||||
result = await Task.Run(() => ModeController.Start(server, mode));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -205,15 +213,7 @@ namespace Netch.Controllers
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
Global.MainForm.NatTypeStatusText(i18N.Translate("Starting NatTester"));
|
||||
// Thread.Sleep(1000);
|
||||
var (nttResult, natType, localEnd, publicEnd) = pNTTController.Start();
|
||||
var country = Utils.Utils.GetCityCode(publicEnd);
|
||||
|
||||
if (nttResult) Global.MainForm.NatTypeStatusText(natType, country);
|
||||
});
|
||||
NatTest();
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -235,17 +235,48 @@ namespace Netch.Controllers
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool NttTested;
|
||||
|
||||
/// <summary>
|
||||
/// 测试 NAT
|
||||
/// </summary>
|
||||
public static void NatTest()
|
||||
{
|
||||
NttTested = false;
|
||||
Task.Run(() =>
|
||||
{
|
||||
Global.MainForm.NatTypeStatusText(i18N.Translate("Starting NatTester"));
|
||||
// Thread.Sleep(1000);
|
||||
var (nttResult, natType, localEnd, publicEnd) = NTTController.Start();
|
||||
|
||||
if (nttResult)
|
||||
{
|
||||
var country = Utils.Utils.GetCityCode(publicEnd);
|
||||
Global.MainForm.NatTypeStatusText(natType, country);
|
||||
}
|
||||
else
|
||||
Global.MainForm.NatTypeStatusText(natType);
|
||||
|
||||
NttTested = true;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止
|
||||
/// </summary>
|
||||
public async Task Stop()
|
||||
public static async Task Stop()
|
||||
{
|
||||
_httpPort = _socks5Port = _redirectorTCPPort = 0;
|
||||
_localAddress = null;
|
||||
_savedMode = null;
|
||||
_savedServer = null;
|
||||
UsingPorts.Clear();
|
||||
|
||||
var tasks = new Task[]
|
||||
{
|
||||
Task.Run(() => pEncryptedProxyController?.Stop()),
|
||||
Task.Run(() => UsingPorts.Clear()),
|
||||
Task.Run(() => pModeController?.Stop()),
|
||||
Task.Run(() => pNTTController.Stop())
|
||||
Task.Run(() => EncryptedProxyController?.Stop()),
|
||||
Task.Run(() => ModeController?.Stop()),
|
||||
Task.Run(() => NTTController.Stop())
|
||||
};
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
@@ -77,12 +78,6 @@ namespace Netch.Controllers
|
||||
Logging.Info("绕行 → 服务器 IP");
|
||||
_directIPs.AddRange(_serverAddresses.Where(address => !IPAddress.IsLoopback(address)).Select(address => IPNetwork.Parse(address.ToString(), 32)));
|
||||
|
||||
if (_savedMode.BypassChina)
|
||||
{
|
||||
Logging.Info("绕行 → 中国 IP");
|
||||
_directIPs.AddRange(Encoding.UTF8.GetString(Resources.CNIP).Split('\n').Select(IPNetwork.Parse));
|
||||
}
|
||||
|
||||
Logging.Info("绕行 → 局域网 IP");
|
||||
_directIPs.AddRange(_bypassLanIPs.Select(IPNetwork.Parse));
|
||||
|
||||
@@ -146,6 +141,19 @@ namespace Netch.Controllers
|
||||
}
|
||||
);
|
||||
|
||||
if (_savedMode.BypassChina)
|
||||
{
|
||||
Logging.Info("绕行 → 中国 IP");
|
||||
|
||||
foreach (var str in File.ReadAllLines("bin\\china_ip_list", Encoding.UTF8))
|
||||
{
|
||||
if (IPNetwork.TryParse(str, out var entry))
|
||||
{
|
||||
_directIPs.Add(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logging.Info("绕行 → 规则 IP");
|
||||
_directIPs.AddRange(_savedMode.Rule.Select(IPNetwork.Parse));
|
||||
|
||||
@@ -204,14 +212,7 @@ namespace Netch.Controllers
|
||||
//if (Global.Settings.TUNTAP.UseCustomDNS || server.Type.Equals("VMess"))
|
||||
if (Global.Settings.TUNTAP.UseCustomDNS)
|
||||
{
|
||||
dns = string.Empty;
|
||||
foreach (var value in Global.Settings.TUNTAP.DNS)
|
||||
{
|
||||
dns += value;
|
||||
dns += ',';
|
||||
}
|
||||
|
||||
dns = dns.Trim();
|
||||
dns = Global.Settings.TUNTAP.DNS.Aggregate(string.Empty, (current, value) => current + (value + ',')).Trim();
|
||||
dns = dns.Substring(0, dns.Length - 1);
|
||||
}
|
||||
else
|
||||
@@ -231,8 +232,7 @@ namespace Netch.Controllers
|
||||
argument.Append($"-tunAddr {Global.Settings.TUNTAP.Address} -tunMask {Global.Settings.TUNTAP.Netmask} -tunGw {Global.Settings.TUNTAP.Gateway} -tunDns {dns} -tunName \"{adapterName}\"");
|
||||
|
||||
State = State.Starting;
|
||||
if (!StartInstanceAuto(argument.ToString(), ProcessPriorityClass.RealTime)) return false;
|
||||
return true;
|
||||
return StartInstanceAuto(argument.ToString(), ProcessPriorityClass.RealTime);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -285,58 +285,63 @@ namespace Netch.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
// 根据 IP Index 寻找 出口适配器
|
||||
var errorAdaptersId = new List<string>();
|
||||
try
|
||||
{
|
||||
try
|
||||
var adapter = NetworkInterface.GetAllNetworkInterfaces().First(_ =>
|
||||
{
|
||||
var adapter = NetworkInterface.GetAllNetworkInterfaces().First(_ => _.GetIPProperties().GetIPv4Properties().Index == Global.Adapter.Index);
|
||||
Global.Adapter.Address = adapter.GetIPProperties().UnicastAddresses.First(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork).Address;
|
||||
Global.Adapter.Gateway = new IPAddress(pRoute.dwForwardNextHop);
|
||||
Logging.Info($"出口 IPv4 地址:{Global.Adapter.Address}");
|
||||
Logging.Info($"出口 网关 地址:{Global.Adapter.Gateway}");
|
||||
Logging.Info($"出口适配器:{adapter.Name} {adapter.Id} {adapter.Description}, index: {Global.Adapter.Index}");
|
||||
// Ex NetworkInformationException: 此接口不支持 IPv4 协议。
|
||||
|
||||
// Ex NetworkInformationException: Windows 系统函数调用失败。
|
||||
// Ex System.ArgumentNullException: source 或 predicate 为 null。
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InvalidOperationException)
|
||||
Logging.Error($"找不到网络接口索引为 {Global.Adapter.Index} 的出口适配器");
|
||||
throw;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var adapter = NetworkInterface.GetAllNetworkInterfaces().First(_ => _.Id == Global.TUNTAP.ComponentID);
|
||||
Global.TUNTAP.Adapter = adapter;
|
||||
Global.TUNTAP.Index = adapter.GetIPProperties().GetIPv4Properties().Index;
|
||||
Logging.Info($"TAP 适配器:{adapter.Name} {adapter.Id} {adapter.Description}, index: {Global.TUNTAP.Index}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InvalidOperationException)
|
||||
Logging.Error($"找不到标识符为 {Global.TUNTAP.ComponentID} 的 TAP 适配器");
|
||||
throw;
|
||||
}
|
||||
|
||||
return true;
|
||||
try
|
||||
{
|
||||
return _.GetIPProperties().GetIPv4Properties().Index == Global.Adapter.Index;
|
||||
}
|
||||
catch (NetworkInformationException)
|
||||
{
|
||||
errorAdaptersId.Add(_.Id);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
Global.Adapter.Address = adapter.GetIPProperties().UnicastAddresses.First(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork).Address;
|
||||
Global.Adapter.Gateway = new IPAddress(pRoute.dwForwardNextHop);
|
||||
Logging.Info($"出口 IPv4 地址:{Global.Adapter.Address}");
|
||||
Logging.Info($"出口 网关 地址:{Global.Adapter.Gateway}");
|
||||
Logging.Info($"出口适配器:{adapter.Name} {adapter.Id} {adapter.Description}, index: {Global.Adapter.Index}");
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error($"找不到 IP Index 为 {Global.Adapter.Index} 的出口适配器: {e.Message}");
|
||||
PrintAdapters();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 根据 ComponentID 寻找 Tap适配器
|
||||
try
|
||||
{
|
||||
var adapter = NetworkInterface.GetAllNetworkInterfaces().First(_ => _.Id == Global.TUNTAP.ComponentID);
|
||||
Global.TUNTAP.Adapter = adapter;
|
||||
Global.TUNTAP.Index = adapter.GetIPProperties().GetIPv4Properties().Index;
|
||||
Logging.Info($"TAP 适配器:{adapter.Name} {adapter.Id} {adapter.Description}, index: {Global.TUNTAP.Index}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var msg = e switch
|
||||
{
|
||||
InvalidOperationException _ => $"找不到标识符为 {Global.TUNTAP.ComponentID} 的 TAP 适配器: {e.Message}",
|
||||
NetworkInformationException _ => $"获取 Tap 适配器信息错误: {e.Message}",
|
||||
_ => $"Tap 适配器其他异常: {e}"
|
||||
};
|
||||
Logging.Error(msg);
|
||||
PrintAdapters();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
void PrintAdapters()
|
||||
{
|
||||
// 理论上如果得到了网络接口的索引/网络适配器的标识符不会找不到网卡
|
||||
// 只是异常处理
|
||||
Logging.Info("所有适配器:\n" +
|
||||
NetworkInterface.GetAllNetworkInterfaces().Aggregate(string.Empty, (current, adapter)
|
||||
=> current + $"{adapter.Name} {adapter.Id} {adapter.Description}, index: {Global.TUNTAP.Index}{Global.EOF}"));
|
||||
return false;
|
||||
}
|
||||
catch (NetworkInformationException e)
|
||||
{
|
||||
if (e.ErrorCode == 10043)
|
||||
MessageBoxX.Show("适配器未开启IPv4协议", LogLevel.ERROR, owner: Global.MainForm);
|
||||
return false;
|
||||
=> current + $"{ /*如果加了 ’*‘ 代表遍历中出现异常的适配器 */(errorAdaptersId.Contains(adapter.Id) ? "*" : "")}{adapter.Name} {adapter.Id} {adapter.Description}{Global.EOF}"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -353,9 +358,12 @@ namespace Netch.Controllers
|
||||
Delete
|
||||
}
|
||||
|
||||
private static bool RouteAction(Action action, IEnumerable<IPNetwork> ipNetworks, RouteType routeType, int metric = 0)
|
||||
private static void RouteAction(Action action, IEnumerable<IPNetwork> ipNetworks, RouteType routeType, int metric = 0)
|
||||
{
|
||||
return ipNetworks.All(address => RouteAction(action, address, routeType, metric));
|
||||
foreach (var address in ipNetworks)
|
||||
{
|
||||
RouteAction(action, address, routeType, metric);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool RouteAction(Action action, string address, byte cidr, RouteType routeType, int metric = 0)
|
||||
@@ -381,12 +389,19 @@ namespace Netch.Controllers
|
||||
throw new ArgumentOutOfRangeException(nameof(routeType), routeType, null);
|
||||
}
|
||||
|
||||
return action switch
|
||||
var result = action switch
|
||||
{
|
||||
Action.Create => NativeMethods.CreateRoute(ipNetwork.Network.ToString(), ipNetwork.Cidr, gateway, index, metric),
|
||||
Action.Delete => NativeMethods.DeleteRoute(ipNetwork.Network.ToString(), ipNetwork.Cidr, gateway, index, metric),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(action), action, null)
|
||||
};
|
||||
|
||||
if (!result)
|
||||
{
|
||||
Logging.Warning($"{action} Route on {routeType} Adapter failed: {ipNetwork} metric {metric}");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Controllers
|
||||
{
|
||||
public class NTTController : Controller
|
||||
{
|
||||
private string _lastResult;
|
||||
private string _localEnd;
|
||||
private string _publicEnd;
|
||||
private string _natType;
|
||||
private bool _nttResult;
|
||||
|
||||
public NTTController()
|
||||
{
|
||||
@@ -21,6 +25,9 @@ namespace Netch.Controllers
|
||||
/// <returns></returns>
|
||||
public (bool, string, string, string) Start()
|
||||
{
|
||||
_nttResult = false;
|
||||
_natType = _localEnd = _publicEnd = null;
|
||||
|
||||
try
|
||||
{
|
||||
InitInstance($" {Global.Settings.STUN_Server} {Global.Settings.STUN_Server_Port}");
|
||||
@@ -30,13 +37,7 @@ namespace Netch.Controllers
|
||||
Instance.BeginOutputReadLine();
|
||||
Instance.BeginErrorReadLine();
|
||||
Instance.WaitForExit();
|
||||
|
||||
var result = _lastResult.Split('#');
|
||||
var natType = result[0];
|
||||
var localEnd = result[1];
|
||||
var publicEnd = result[2];
|
||||
|
||||
return (true, natType, localEnd, publicEnd);
|
||||
return (_nttResult, _natType, _localEnd, _publicEnd);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -56,8 +57,35 @@ namespace Netch.Controllers
|
||||
|
||||
private new void OnOutputDataReceived(object sender, DataReceivedEventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Data))
|
||||
_lastResult = e.Data;
|
||||
if (string.IsNullOrEmpty(e.Data)) return;
|
||||
Logging.Info($"[NTT] {e.Data}");
|
||||
|
||||
var str = e.Data.Split(':').Select(s => s.Trim()).ToArray();
|
||||
if (str.Length < 2)
|
||||
return;
|
||||
var key = str[0];
|
||||
var value = str[1];
|
||||
switch (key)
|
||||
{
|
||||
case "Other address is":
|
||||
case "Binding test":
|
||||
case "Nat mapping behavior":
|
||||
case "Nat filtering behavior":
|
||||
break;
|
||||
case "Local address":
|
||||
_localEnd = value;
|
||||
break;
|
||||
case "Mapped address":
|
||||
_publicEnd = value;
|
||||
break;
|
||||
case "result":
|
||||
_natType = value;
|
||||
_nttResult = true;
|
||||
break;
|
||||
default:
|
||||
_natType = str.Last();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using System.Net;
|
||||
using Netch.Models.GitHubRelease;
|
||||
using Netch.Utils;
|
||||
using Newtonsoft.Json;
|
||||
@@ -15,43 +15,53 @@ namespace Netch.Controllers
|
||||
|
||||
public const string Name = @"Netch";
|
||||
public const string Copyright = @"Copyright © 2019 - 2020";
|
||||
public const string Version = @"1.4.12";
|
||||
public const string Version = @"1.5.1";
|
||||
|
||||
public string LatestVersionNumber;
|
||||
public string LatestVersionUrl;
|
||||
public string LatestVersionDownloadUrl;
|
||||
|
||||
public event EventHandler NewVersionFound;
|
||||
public event EventHandler NewVersionFoundFailed;
|
||||
public event EventHandler NewVersionNotFound;
|
||||
|
||||
public void Check(bool notifyNoFound, bool isPreRelease)
|
||||
public async void Check(bool isPreRelease)
|
||||
{
|
||||
try
|
||||
{
|
||||
var updater = new GitHubRelease(Owner, Repo);
|
||||
var url = updater.AllReleaseUrl;
|
||||
|
||||
var json = WebUtil.DownloadString(WebUtil.CreateRequest(url));
|
||||
var json = await WebUtil.DownloadStringAsync(WebUtil.CreateRequest(url));
|
||||
|
||||
var releases = JsonConvert.DeserializeObject<List<Release>>(json);
|
||||
var latestRelease = VersionUtil.GetLatestRelease(releases, isPreRelease);
|
||||
LatestVersionNumber = latestRelease.tag_name;
|
||||
LatestVersionUrl = latestRelease.html_url;
|
||||
LatestVersionDownloadUrl = latestRelease.assets[0].browser_download_url;
|
||||
Logging.Info($"Github 最新发布版本: {latestRelease.tag_name}");
|
||||
if (VersionUtil.CompareVersion(latestRelease.tag_name, Version) > 0)
|
||||
{
|
||||
LatestVersionNumber = latestRelease.tag_name;
|
||||
LatestVersionUrl = latestRelease.html_url;
|
||||
Logging.Info($"发现新版本");
|
||||
NewVersionFound?.Invoke(this, new EventArgs());
|
||||
}
|
||||
else
|
||||
{
|
||||
LatestVersionNumber = latestRelease.tag_name;
|
||||
if (notifyNoFound) NewVersionNotFound?.Invoke(this, new EventArgs());
|
||||
Logging.Info("目前是最新版本");
|
||||
NewVersionNotFound?.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logging.Error(e.ToString());
|
||||
if (notifyNoFound) NewVersionFoundFailed?.Invoke(this, new EventArgs());
|
||||
if (e is WebException)
|
||||
Logging.Warning($"获取新版本失败: {e.Message}");
|
||||
else
|
||||
{
|
||||
Logging.Warning(e.ToString());
|
||||
}
|
||||
|
||||
NewVersionFoundFailed?.Invoke(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1
Netch/Forms/AboutForm.Designer.cs
generated
@@ -99,7 +99,6 @@
|
||||
this.Name = "AboutForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "About";
|
||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.AboutForm_FormClosing);
|
||||
this.Load += new System.EventHandler(this.AboutForm_Load);
|
||||
this.SponsorGroupBox.ResumeLayout(false);
|
||||
((System.ComponentModel.ISupportInitialize)(this.SponsorPictureBox)).EndInit();
|
||||
|
||||
@@ -19,11 +19,6 @@ namespace Netch.Forms
|
||||
SponsorGroupBox.Text = i18N.Translate(SponsorGroupBox.Text);
|
||||
}
|
||||
|
||||
private void AboutForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
Global.MainForm.Show();
|
||||
}
|
||||
|
||||
private void NetchPictureBox_Click(object sender, EventArgs e)
|
||||
{
|
||||
Process.Start("https://github.com/NetchX/Netch");
|
||||
|
||||
1
Netch/Forms/GlobalBypassIPForm.Designer.cs
generated
@@ -125,7 +125,6 @@
|
||||
this.Name = "GlobalBypassIPForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "Global Bypass IPs";
|
||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.GlobalBypassIPForm_FormClosing);
|
||||
this.Load += new System.EventHandler(this.GlobalBypassIPForm_Load);
|
||||
this.IPGroupBox.ResumeLayout(false);
|
||||
this.IPGroupBox.PerformLayout();
|
||||
|
||||
@@ -28,11 +28,6 @@ namespace Netch.Forms
|
||||
PrefixComboBox.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
private void GlobalBypassIPForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
Global.SettingForm.Show();
|
||||
}
|
||||
|
||||
private void AddButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(IPTextBox.Text))
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -18,6 +20,7 @@ namespace Netch.Forms
|
||||
|
||||
private async void ControlFun()
|
||||
{
|
||||
Configuration.Save();
|
||||
if (State == State.Waiting || State == State.Stopped)
|
||||
{
|
||||
// 服务器、模式 需选择
|
||||
@@ -41,10 +44,10 @@ namespace Netch.Forms
|
||||
var server = ServerComboBox.SelectedItem as Models.Server;
|
||||
var mode = ModeComboBox.SelectedItem as Models.Mode;
|
||||
|
||||
if (await _mainController.Start(server, mode))
|
||||
if (await MainController.Start(server, mode))
|
||||
{
|
||||
State = State.Started;
|
||||
_ = Task.Run(() => { Bandwidth.NetTraffic(server, mode, ref _mainController); });
|
||||
_ = Task.Run(() => { Bandwidth.NetTraffic(server, mode); });
|
||||
// 如果勾选启动后最小化
|
||||
if (Global.Settings.MinimizeWhenStarted)
|
||||
{
|
||||
@@ -68,8 +71,8 @@ namespace Netch.Forms
|
||||
while (State == State.Started)
|
||||
{
|
||||
server.Test();
|
||||
// 重载服务器列表
|
||||
InitServer();
|
||||
// 重绘 ServerComboBox
|
||||
ServerComboBox.Invalidate();
|
||||
|
||||
Thread.Sleep(Global.Settings.StartedTcping_Interval * 1000);
|
||||
}
|
||||
@@ -86,17 +89,17 @@ namespace Netch.Forms
|
||||
{
|
||||
// 停止
|
||||
State = State.Stopping;
|
||||
await _mainController.Stop();
|
||||
await MainController.Stop();
|
||||
State = State.Stopped;
|
||||
_ = Task.Run(TestServer);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnBandwidthUpdated(long download)
|
||||
public void OnBandwidthUpdated(ulong download)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action<long>(OnBandwidthUpdated), download);
|
||||
BeginInvoke(new Action<ulong>(OnBandwidthUpdated), download);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -116,44 +119,14 @@ namespace Netch.Forms
|
||||
}
|
||||
}
|
||||
|
||||
public void OnBandwidthUpdated(long upload, long download)
|
||||
{
|
||||
if (InvokeRequired)
|
||||
{
|
||||
BeginInvoke(new Action<long, long>(OnBandwidthUpdated), upload, download);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (upload < 1 || download < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UsedBandwidthLabel.Text =
|
||||
$"{i18N.Translate("Used", ": ")}{Bandwidth.Compute(upload + download)}";
|
||||
UploadSpeedLabel.Text = $"↑: {Bandwidth.Compute(upload - LastUploadBandwidth)}/s";
|
||||
DownloadSpeedLabel.Text = $"↓: {Bandwidth.Compute(download - LastDownloadBandwidth)}/s";
|
||||
|
||||
LastUploadBandwidth = upload;
|
||||
LastDownloadBandwidth = download;
|
||||
Refresh();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上一次上传的流量
|
||||
/// </summary>
|
||||
public long LastUploadBandwidth;
|
||||
public ulong LastUploadBandwidth;
|
||||
|
||||
/// <summary>
|
||||
/// 上一次下载的流量
|
||||
/// </summary>
|
||||
public long LastDownloadBandwidth;
|
||||
public ulong LastDownloadBandwidth;
|
||||
}
|
||||
}
|
||||
60
Netch/Forms/MainForm.Designer.cs
generated
@@ -41,6 +41,7 @@ namespace Netch.Forms
|
||||
this.AddVMessServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.AddTrojanServerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.HelpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.CreateProcessModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ReloadModesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.SubscribeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
@@ -53,8 +54,10 @@ namespace Netch.Forms
|
||||
this.updateACLWithProxyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.UninstallServiceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.reinstallTapDriverToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.CheckForUpdatesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.AboutToolStripButton = new System.Windows.Forms.ToolStripButton();
|
||||
this.VersionLabel = new System.Windows.Forms.ToolStripLabel();
|
||||
this.NewVersionLabel = new System.Windows.Forms.ToolStripLabel();
|
||||
this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
|
||||
this.configLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
|
||||
@@ -113,9 +116,11 @@ namespace Netch.Forms
|
||||
this.ModeToolStripMenuItem,
|
||||
this.SubscribeToolStripMenuItem,
|
||||
this.OptionsToolStripMenuItem,
|
||||
this.HelpToolStripMenuItem,
|
||||
this.exitToolStripMenuItem,
|
||||
this.AboutToolStripButton,
|
||||
this.VersionLabel,
|
||||
this.exitToolStripMenuItem});
|
||||
this.NewVersionLabel,
|
||||
this.VersionLabel});
|
||||
this.MenuStrip.Location = new System.Drawing.Point(0, 0);
|
||||
this.MenuStrip.Name = "MenuStrip";
|
||||
this.MenuStrip.RenderMode = System.Windows.Forms.ToolStripRenderMode.Professional;
|
||||
@@ -148,35 +153,35 @@ namespace Netch.Forms
|
||||
this.AddSocks5ServerToolStripMenuItem.Name = "AddSocks5ServerToolStripMenuItem";
|
||||
this.AddSocks5ServerToolStripMenuItem.Size = new System.Drawing.Size(259, 22);
|
||||
this.AddSocks5ServerToolStripMenuItem.Text = "Add [Socks5] Server";
|
||||
this.AddSocks5ServerToolStripMenuItem.Click += new System.EventHandler(this.AddSocks5ServerToolStripMenuItem_Click);
|
||||
this.AddSocks5ServerToolStripMenuItem.Click += new System.EventHandler(this.AddServerToolStripMenuItem_Click);
|
||||
//
|
||||
// AddShadowsocksServerToolStripMenuItem
|
||||
//
|
||||
this.AddShadowsocksServerToolStripMenuItem.Name = "AddShadowsocksServerToolStripMenuItem";
|
||||
this.AddShadowsocksServerToolStripMenuItem.Size = new System.Drawing.Size(259, 22);
|
||||
this.AddShadowsocksServerToolStripMenuItem.Text = "Add [Shadowsocks] Server";
|
||||
this.AddShadowsocksServerToolStripMenuItem.Click += new System.EventHandler(this.AddShadowsocksServerToolStripMenuItem_Click);
|
||||
this.AddShadowsocksServerToolStripMenuItem.Click += new System.EventHandler(this.AddServerToolStripMenuItem_Click);
|
||||
//
|
||||
// AddShadowsocksRServerToolStripMenuItem
|
||||
//
|
||||
this.AddShadowsocksRServerToolStripMenuItem.Name = "AddShadowsocksRServerToolStripMenuItem";
|
||||
this.AddShadowsocksRServerToolStripMenuItem.Size = new System.Drawing.Size(259, 22);
|
||||
this.AddShadowsocksRServerToolStripMenuItem.Text = "Add [ShadowsocksR] Server";
|
||||
this.AddShadowsocksRServerToolStripMenuItem.Click += new System.EventHandler(this.AddShadowsocksRServerToolStripMenuItem_Click);
|
||||
this.AddShadowsocksRServerToolStripMenuItem.Click += new System.EventHandler(this.AddServerToolStripMenuItem_Click);
|
||||
//
|
||||
// AddVMessServerToolStripMenuItem
|
||||
//
|
||||
this.AddVMessServerToolStripMenuItem.Name = "AddVMessServerToolStripMenuItem";
|
||||
this.AddVMessServerToolStripMenuItem.Size = new System.Drawing.Size(259, 22);
|
||||
this.AddVMessServerToolStripMenuItem.Text = "Add [VMess] Server";
|
||||
this.AddVMessServerToolStripMenuItem.Click += new System.EventHandler(this.AddVMessServerToolStripMenuItem_Click);
|
||||
this.AddVMessServerToolStripMenuItem.Click += new System.EventHandler(this.AddServerToolStripMenuItem_Click);
|
||||
//
|
||||
// AddTrojanServerToolStripMenuItem
|
||||
//
|
||||
this.AddTrojanServerToolStripMenuItem.Name = "AddTrojanServerToolStripMenuItem";
|
||||
this.AddTrojanServerToolStripMenuItem.Size = new System.Drawing.Size(259, 22);
|
||||
this.AddTrojanServerToolStripMenuItem.Text = "Add [Trojan] Server";
|
||||
this.AddTrojanServerToolStripMenuItem.Click += new System.EventHandler(this.AddTrojanServerToolStripMenuItem_Click);
|
||||
this.AddTrojanServerToolStripMenuItem.Click += new System.EventHandler(this.AddServerToolStripMenuItem_Click);
|
||||
//
|
||||
// ModeToolStripMenuItem
|
||||
//
|
||||
@@ -188,6 +193,15 @@ namespace Netch.Forms
|
||||
this.ModeToolStripMenuItem.Size = new System.Drawing.Size(55, 21);
|
||||
this.ModeToolStripMenuItem.Text = "Mode";
|
||||
//
|
||||
// HelpToolStripMenuItem
|
||||
//
|
||||
this.HelpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.CheckForUpdatesToolStripMenuItem});
|
||||
this.HelpToolStripMenuItem.Margin = new System.Windows.Forms.Padding(0, 0, 0, 1);
|
||||
this.HelpToolStripMenuItem.Name = "HelpToolStripMenuItem";
|
||||
this.HelpToolStripMenuItem.Size = new System.Drawing.Size(55, 21);
|
||||
this.HelpToolStripMenuItem.Text = "Help";
|
||||
//
|
||||
// CreateProcessModeToolStripMenuItem
|
||||
//
|
||||
this.CreateProcessModeToolStripMenuItem.Name = "CreateProcessModeToolStripMenuItem";
|
||||
@@ -282,6 +296,13 @@ namespace Netch.Forms
|
||||
this.reinstallTapDriverToolStripMenuItem.Text = "Reinstall TUN/TAP driver";
|
||||
this.reinstallTapDriverToolStripMenuItem.Click += new System.EventHandler(this.reinstallTapDriverToolStripMenuItem_Click);
|
||||
//
|
||||
// CheckForUpdatesToolStripMenuItem
|
||||
//
|
||||
this.CheckForUpdatesToolStripMenuItem.Name = "CheckForUpdatesToolStripMenuItem";
|
||||
this.CheckForUpdatesToolStripMenuItem.Size = new System.Drawing.Size(219, 22);
|
||||
this.CheckForUpdatesToolStripMenuItem.Text = "Check for updates";
|
||||
this.CheckForUpdatesToolStripMenuItem.Click += new System.EventHandler(this.CheckForUpdatesToolStripMenuItem_Click);
|
||||
//
|
||||
// AboutToolStripButton
|
||||
//
|
||||
this.AboutToolStripButton.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
|
||||
@@ -292,6 +313,20 @@ namespace Netch.Forms
|
||||
this.AboutToolStripButton.Text = "About";
|
||||
this.AboutToolStripButton.Click += new System.EventHandler(this.AboutToolStripButton_Click);
|
||||
//
|
||||
// NewVersionLabel
|
||||
//
|
||||
this.NewVersionLabel.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
|
||||
this.NewVersionLabel.BackColor = System.Drawing.Color.Transparent;
|
||||
this.NewVersionLabel.ForeColor = System.Drawing.Color.Red;
|
||||
this.NewVersionLabel.LinkColor = System.Drawing.Color.Red;
|
||||
this.NewVersionLabel.IsLink = true;
|
||||
this.NewVersionLabel.Visible = false;
|
||||
this.NewVersionLabel.LinkBehavior = System.Windows.Forms.LinkBehavior.NeverUnderline;
|
||||
this.NewVersionLabel.Name = "NewVersionLabel";
|
||||
this.NewVersionLabel.Size = new System.Drawing.Size(26, 19);
|
||||
this.NewVersionLabel.Text = "New version available";
|
||||
this.NewVersionLabel.Click += new System.EventHandler(this.NewVersionLabel_Click);
|
||||
//
|
||||
// VersionLabel
|
||||
//
|
||||
this.VersionLabel.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right;
|
||||
@@ -570,6 +605,7 @@ namespace Netch.Forms
|
||||
this.NatTypeStatusLabel.Size = new System.Drawing.Size(36, 17);
|
||||
this.NatTypeStatusLabel.Text = "NAT:";
|
||||
this.NatTypeStatusLabel.TextAlign = System.Drawing.ContentAlignment.BottomLeft;
|
||||
this.NatTypeStatusLabel.Click += new System.EventHandler(this.NatTypeStatusLabel_Click);
|
||||
//
|
||||
// NatTypeStatusLightLabel
|
||||
//
|
||||
@@ -581,6 +617,7 @@ namespace Netch.Forms
|
||||
this.NatTypeStatusLightLabel.Size = new System.Drawing.Size(18, 21);
|
||||
this.NatTypeStatusLightLabel.Text = "⬤";
|
||||
this.NatTypeStatusLightLabel.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
|
||||
this.NatTypeStatusLightLabel.Click += new System.EventHandler(this.NatTypeStatusLabel_Click);
|
||||
//
|
||||
// ControlButton
|
||||
//
|
||||
@@ -683,7 +720,6 @@ namespace Netch.Forms
|
||||
this.Text = "Netch";
|
||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing);
|
||||
this.Load += new System.EventHandler(this.MainForm_Load);
|
||||
this.VisibleChanged += new System.EventHandler(this.MainForm_VisibleChanged);
|
||||
this.MenuStrip.ResumeLayout(false);
|
||||
this.MenuStrip.PerformLayout();
|
||||
this.ConfigurationGroupBox.ResumeLayout(false);
|
||||
@@ -732,6 +768,7 @@ namespace Netch.Forms
|
||||
private System.Windows.Forms.SearchComboBox ModeComboBox;
|
||||
private System.Windows.Forms.Label ModeLabel;
|
||||
private System.Windows.Forms.ToolStripMenuItem ModeToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem HelpToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripStatusLabel NatTypeStatusLabel;
|
||||
private System.Windows.Forms.NotifyIcon NotifyIcon;
|
||||
private System.Windows.Forms.ContextMenuStrip NotifyMenu;
|
||||
@@ -742,6 +779,7 @@ namespace Netch.Forms
|
||||
private System.Windows.Forms.TextBox ProfileNameText;
|
||||
private System.Windows.Forms.TableLayoutPanel ProfileTable;
|
||||
private System.Windows.Forms.ToolStripMenuItem reinstallTapDriverToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem CheckForUpdatesToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripMenuItem ReloadModesToolStripMenuItem;
|
||||
private System.Windows.Forms.ComboBox ServerComboBox;
|
||||
private System.Windows.Forms.Label ServerLabel;
|
||||
@@ -760,11 +798,11 @@ namespace Netch.Forms
|
||||
private System.Windows.Forms.ToolStripMenuItem UpdateServersFromSubscribeLinksToolStripMenuItem;
|
||||
private System.Windows.Forms.ToolStripStatusLabel UploadSpeedLabel;
|
||||
private System.Windows.Forms.ToolStripStatusLabel UsedBandwidthLabel;
|
||||
private System.Windows.Forms.ToolStripLabel NewVersionLabel;
|
||||
private System.Windows.Forms.ToolStripLabel VersionLabel;
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.ToolStripStatusLabel NatTypeStatusLightLabel;
|
||||
private System.Windows.Forms.ToolStripStatusLabel blankToolStripStatusLabel;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,10 @@ namespace Netch.Forms
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
Global.Settings.Server.AddRange(result);
|
||||
foreach (var server in result)
|
||||
{
|
||||
Global.Settings.Server.Add(server);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -47,34 +50,23 @@ namespace Netch.Forms
|
||||
}
|
||||
}
|
||||
|
||||
private void AddSocks5ServerToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
private void AddServerToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
new Socks5().Show();
|
||||
Hide();
|
||||
}
|
||||
Form form = ((ToolStripMenuItem)sender).Name switch
|
||||
{
|
||||
"AddSocks5ServerToolStripMenuItem" => new Socks5(),
|
||||
"AddShadowsocksServerToolStripMenuItem" => new Shadowsocks(),
|
||||
"AddShadowsocksRServerToolStripMenuItem" => new ShadowsocksR(),
|
||||
"AddVMessServerToolStripMenuItem" => new VMess(),
|
||||
"AddTrojanServerToolStripMenuItem" => new Trojan(),
|
||||
_ => null
|
||||
};
|
||||
|
||||
private void AddShadowsocksServerToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
new Shadowsocks().Show();
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void AddShadowsocksRServerToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
new ShadowsocksR().Show();
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void AddVMessServerToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
new VMess().Show();
|
||||
Hide();
|
||||
}
|
||||
|
||||
private void AddTrojanServerToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
new Trojan().Show();
|
||||
Hide();
|
||||
form?.ShowDialog();
|
||||
InitServer();
|
||||
Configuration.Save();
|
||||
Show();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -83,20 +75,18 @@ namespace Netch.Forms
|
||||
|
||||
private void CreateProcessModeToolStripButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
new Process().Show();
|
||||
Hide();
|
||||
new Process().ShowDialog();
|
||||
Show();
|
||||
}
|
||||
|
||||
private async void ReloadModesToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
private void ReloadModesToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Enabled = false;
|
||||
try
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
SaveConfigs();
|
||||
InitMode();
|
||||
});
|
||||
Modes.Load();
|
||||
InitMode();
|
||||
NotifyTip(i18N.Translate("Modes have been reload"));
|
||||
}
|
||||
catch (Exception)
|
||||
@@ -115,12 +105,20 @@ namespace Netch.Forms
|
||||
|
||||
private void ManageSubscribeLinksToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
new SubscribeForm().Show();
|
||||
Hide();
|
||||
new SubscribeForm().ShowDialog();
|
||||
InitServer();
|
||||
Show();
|
||||
}
|
||||
|
||||
private async void UpdateServersFromSubscribeLinksToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
UpdateServersFromSubscribe();
|
||||
}
|
||||
|
||||
public async void UpdateServersFromSubscribe()
|
||||
{
|
||||
|
||||
void DisableItems(bool v)
|
||||
{
|
||||
MenuStrip.Enabled = ConfigurationGroupBox.Enabled = ProfileGroupBox.Enabled = ControlButton.Enabled = v;
|
||||
@@ -152,7 +150,7 @@ namespace Netch.Forms
|
||||
Remark = "ProxyUpdate",
|
||||
Type = 5
|
||||
};
|
||||
await _mainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
await MainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
}
|
||||
|
||||
var serverLock = new object();
|
||||
@@ -180,15 +178,19 @@ namespace Netch.Forms
|
||||
|
||||
lock (serverLock)
|
||||
{
|
||||
Global.Settings.Server = Global.Settings.Server.Where(server => server.Group != item.Remark).ToList();
|
||||
Global.Settings.Server.RemoveAll(server => server.Group == item.Remark);
|
||||
|
||||
var result = ShareLink.Parse(str);
|
||||
if (result != null)
|
||||
{
|
||||
foreach (var x in result) x.Group = item.Remark;
|
||||
|
||||
Global.Settings.Server.AddRange(result);
|
||||
NotifyTip(i18N.TranslateFormat("Update {1} server(s) from {0}", item.Remark, result.Count));
|
||||
foreach (var server in result)
|
||||
{
|
||||
server.Group = item.Remark;
|
||||
Global.Settings.Server.Add(server);
|
||||
}
|
||||
}
|
||||
|
||||
NotifyTip(i18N.TranslateFormat("Update {1} server(s) from {0}", item.Remark, result?.Count ?? 0));
|
||||
}
|
||||
}
|
||||
catch (WebException e)
|
||||
@@ -201,8 +203,8 @@ namespace Netch.Forms
|
||||
}
|
||||
})).ToArray());
|
||||
|
||||
InitServer();
|
||||
Configuration.Save();
|
||||
await Task.Run(InitServer);
|
||||
StatusText(i18N.Translate("Subscription updated"));
|
||||
}
|
||||
catch (Exception)
|
||||
@@ -213,7 +215,7 @@ namespace Netch.Forms
|
||||
{
|
||||
if (Global.Settings.UseProxyToUpdateSubscription)
|
||||
{
|
||||
await _mainController.Stop();
|
||||
await MainController.Stop();
|
||||
}
|
||||
|
||||
DisableItems(true);
|
||||
@@ -224,6 +226,28 @@ namespace Netch.Forms
|
||||
|
||||
#region 选项
|
||||
|
||||
private void CheckForUpdatesToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
void OnNewVersionNotFound(object o, EventArgs args)
|
||||
{
|
||||
_updater.NewVersionNotFound -= OnNewVersionNotFound;
|
||||
NotifyTip(i18N.Translate("Already latest version"));
|
||||
}
|
||||
|
||||
void OnNewVersionFoundFailed(object o, EventArgs args)
|
||||
{
|
||||
_updater.NewVersionFoundFailed -= OnNewVersionFoundFailed;
|
||||
NotifyTip(i18N.Translate("New version found failed"), info: false);
|
||||
}
|
||||
|
||||
_updater.NewVersionNotFound += OnNewVersionNotFound;
|
||||
_updater.NewVersionFoundFailed += OnNewVersionFoundFailed;
|
||||
CheckUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
private void OpenDirectoryToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
Utils.Utils.Open(".\\");
|
||||
@@ -279,7 +303,7 @@ namespace Netch.Forms
|
||||
Type = 5
|
||||
};
|
||||
State = State.Starting;
|
||||
await _mainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
await MainController.Start(ServerComboBox.SelectedItem as Models.Server, mode);
|
||||
}
|
||||
|
||||
var req = WebUtil.CreateRequest(Global.Settings.ACL);
|
||||
@@ -298,7 +322,7 @@ namespace Netch.Forms
|
||||
{
|
||||
if (useProxy)
|
||||
{
|
||||
await _mainController.Stop();
|
||||
await MainController.Stop();
|
||||
State = State.Stopped;
|
||||
}
|
||||
|
||||
@@ -365,8 +389,9 @@ namespace Netch.Forms
|
||||
|
||||
private void AboutToolStripButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
new AboutForm().Show();
|
||||
Hide();
|
||||
new AboutForm().ShowDialog();
|
||||
Show();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
using System.ComponentModel;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Controllers;
|
||||
using Netch.Forms.Mode;
|
||||
using Netch.Utils;
|
||||
using Process = System.Diagnostics.Process;
|
||||
|
||||
namespace Netch.Forms
|
||||
{
|
||||
@@ -18,11 +25,66 @@ namespace Netch.Forms
|
||||
|
||||
partial class MainForm
|
||||
{
|
||||
private readonly UpdateChecker _updater = new UpdateChecker();
|
||||
|
||||
private void CheckUpdate()
|
||||
{
|
||||
var updater = new UpdateChecker();
|
||||
updater.NewVersionFound += (o, args) => { NotifyTip($"{i18N.Translate(@"New version available", ": ")}{updater.LatestVersionNumber}"); };
|
||||
updater.Check(false, Global.Settings.CheckBetaUpdate);
|
||||
_updater.NewVersionFound += (o, args) =>
|
||||
{
|
||||
if (_updater.LatestVersionDownloadUrl.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
NotifyTip($"{i18N.Translate(@"New version available", ": ")}{_updater.LatestVersionNumber}");
|
||||
NewVersionLabel.Visible = true;
|
||||
}
|
||||
};
|
||||
_updater.Check(Global.Settings.CheckBetaUpdate);
|
||||
}
|
||||
|
||||
private async void NewVersionLabel_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (MessageBoxX.Show(i18N.Translate("Download and install now?"), confirm: true) != DialogResult.OK)
|
||||
return;
|
||||
NotifyTip(i18N.Translate("Start downloading new version"));
|
||||
var fileName = $"Netch{_updater.LatestVersionNumber}.zip";
|
||||
var fileFullPath = Path.Combine(Global.NetchDir, "data", fileName);
|
||||
var updateFileValid = false;
|
||||
|
||||
try
|
||||
{
|
||||
if (File.Exists(fileFullPath))
|
||||
{
|
||||
if (!(updateFileValid = Utils.Utils.IsZipValid(fileFullPath)))
|
||||
{
|
||||
File.Delete(fileFullPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!File.Exists(fileFullPath))
|
||||
await WebUtil.DownloadFileAsync(WebUtil.CreateRequest(_updater.LatestVersionDownloadUrl), fileFullPath);
|
||||
if (updateFileValid || Utils.Utils.IsZipValid(fileFullPath))
|
||||
RunUpdater();
|
||||
else
|
||||
throw new InvalidDataException($"{fileFullPath} invalid");
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
NotifyTip($"{i18N.Translate("Download update failed")}\n{exception.Message}");
|
||||
Logging.Error($"下载更新失败 {exception}");
|
||||
}
|
||||
|
||||
void RunUpdater()
|
||||
{
|
||||
// if debugging process stopped, debugger will kill child processes!!!!
|
||||
// 调试进程结束,调试器将会杀死子进程
|
||||
// uncomment if(!Debugger.isAttach) block in NetchUpdater Project's main() method and attach to NetchUpdater process to debug
|
||||
// 在 NetchUpdater 项目的 main() 方法中取消注释 if(!Debugger.isAttach)块,并附加到 NetchUpdater 进程进行调试
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = Path.Combine(Global.NetchDir, "NetchUpdater.exe"),
|
||||
Arguments =
|
||||
$"{Global.Settings.UDPSocketPort} {fileFullPath} {Global.NetchDir}"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Utils;
|
||||
@@ -15,6 +16,17 @@ namespace Netch.Forms
|
||||
{
|
||||
#region Server
|
||||
|
||||
private void InitServer()
|
||||
{
|
||||
var comboBoxInitialized = _comboBoxInitialized;
|
||||
_comboBoxInitialized = false;
|
||||
|
||||
ServerComboBox.Items.Clear();
|
||||
ServerComboBox.Items.AddRange(Global.Settings.Server.ToArray());
|
||||
SelectLastServer();
|
||||
_comboBoxInitialized = comboBoxInitialized;
|
||||
}
|
||||
|
||||
private static void TestServer()
|
||||
{
|
||||
try
|
||||
@@ -28,11 +40,8 @@ namespace Netch.Forms
|
||||
}
|
||||
}
|
||||
|
||||
public void InitServer()
|
||||
public void SelectLastServer()
|
||||
{
|
||||
ServerComboBox.Items.Clear();
|
||||
ServerComboBox.Items.AddRange(Global.Settings.Server.ToArray());
|
||||
|
||||
// 如果值合法,选中该位置
|
||||
if (Global.Settings.ServerComboBoxSelectedIndex > 0 &&
|
||||
Global.Settings.ServerComboBoxSelectedIndex < ServerComboBox.Items.Count)
|
||||
@@ -52,95 +61,18 @@ namespace Netch.Forms
|
||||
|
||||
#region Mode
|
||||
|
||||
private void InitMode()
|
||||
public void InitMode()
|
||||
{
|
||||
var comboBoxInitialized = _comboBoxInitialized;
|
||||
_comboBoxInitialized = false;
|
||||
|
||||
ModeComboBox.Items.Clear();
|
||||
Global.ModeFiles.Clear();
|
||||
|
||||
if (Directory.Exists("mode"))
|
||||
{
|
||||
foreach (var name in Directory.GetFiles("mode", "*.txt"))
|
||||
{
|
||||
var ok = true;
|
||||
var mode = new Models.Mode();
|
||||
|
||||
using (var sr = new StringReader(File.ReadAllText(name)))
|
||||
{
|
||||
var i = 0;
|
||||
string text;
|
||||
|
||||
while ((text = sr.ReadLine()) != null)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
var splited = text.Trim().Substring(1).Split(',');
|
||||
|
||||
if (splited.Length == 0)
|
||||
{
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (splited.Length >= 1)
|
||||
{
|
||||
mode.Remark = i18N.Translate(splited[0].Trim());
|
||||
}
|
||||
|
||||
if (splited.Length >= 2)
|
||||
{
|
||||
if (int.TryParse(splited[1], out var result))
|
||||
{
|
||||
mode.Type = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (splited.Length >= 3)
|
||||
{
|
||||
if (int.TryParse(splited[2], out var result))
|
||||
{
|
||||
mode.BypassChina = result == 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!text.StartsWith("#") && !string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
mode.Rule.Add(text.Trim());
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok)
|
||||
{
|
||||
mode.FileName = Path.GetFileNameWithoutExtension(name);
|
||||
Global.ModeFiles.Add(mode);
|
||||
}
|
||||
}
|
||||
|
||||
var array = Global.ModeFiles.ToArray();
|
||||
Array.Sort(array, (a, b) => string.Compare(a.Remark, b.Remark, StringComparison.Ordinal));
|
||||
|
||||
ModeComboBox.Items.AddRange(array);
|
||||
|
||||
SelectLastMode();
|
||||
}
|
||||
ModeComboBox.Items.AddRange(Global.Modes.ToArray());
|
||||
SelectLastMode();
|
||||
_comboBoxInitialized = comboBoxInitialized;
|
||||
}
|
||||
|
||||
private void SelectLastMode()
|
||||
public void SelectLastMode()
|
||||
{
|
||||
// 如果值合法,选中该位置
|
||||
if (Global.Settings.ModeComboBoxSelectedIndex > 0 &&
|
||||
@@ -157,29 +89,6 @@ namespace Netch.Forms
|
||||
// 如果当前 ModeComboBox 中没元素,不做处理
|
||||
}
|
||||
|
||||
public void AddMode(Models.Mode mode)
|
||||
{
|
||||
ModeComboBox.Items.Clear();
|
||||
Global.ModeFiles.Add(mode);
|
||||
var array = Global.ModeFiles.ToArray();
|
||||
Array.Sort(array, (a, b) => string.Compare(a.Remark, b.Remark, StringComparison.Ordinal));
|
||||
ModeComboBox.Items.AddRange(array);
|
||||
|
||||
SelectLastMode();
|
||||
}
|
||||
|
||||
public void UpdateMode(Models.Mode NewMode, Models.Mode OldMode)
|
||||
{
|
||||
ModeComboBox.Items.Clear();
|
||||
Global.ModeFiles.Remove(OldMode);
|
||||
Global.ModeFiles.Add(NewMode);
|
||||
var array = Global.ModeFiles.ToArray();
|
||||
Array.Sort(array, (a, b) => string.Compare(a.Remark, b.Remark, StringComparison.Ordinal));
|
||||
ModeComboBox.Items.AddRange(array);
|
||||
|
||||
SelectLastMode();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using Netch.Controllers;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -67,11 +69,11 @@ namespace Netch.Forms
|
||||
ControlButton.Enabled = true;
|
||||
ControlButton.Text = i18N.Translate("Stop");
|
||||
|
||||
StatusTextAppend(_mainController.PortInfo);
|
||||
StatusTextAppend(MainController.PortInfo);
|
||||
|
||||
ProfileGroupBox.Enabled = true;
|
||||
|
||||
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = true;
|
||||
UsedBandwidthLabel.Visible /*= UploadSpeedLabel.Visible*/ = DownloadSpeedLabel.Visible = Bandwidth.NetTrafficAvailable;
|
||||
break;
|
||||
case State.Stopping:
|
||||
ControlButton.Enabled = false;
|
||||
@@ -87,6 +89,7 @@ namespace Netch.Forms
|
||||
|
||||
LastUploadBandwidth = 0;
|
||||
LastDownloadBandwidth = 0;
|
||||
Bandwidth.Stop();
|
||||
|
||||
ProfileGroupBox.Enabled = true;
|
||||
StartDisableItems(true);
|
||||
@@ -125,10 +128,17 @@ namespace Netch.Forms
|
||||
NatTypeStatusLabel.Text = String.Format("NAT{0}{1}", i18N.Translate(": "), text);
|
||||
}
|
||||
|
||||
if (Enum.TryParse(text, false, out STUN_Client.NatType natType))
|
||||
if (int.TryParse(text, out int natType))
|
||||
{
|
||||
NatTypeStatusLightLabel.Visible = true;
|
||||
UpdateNatTypeLight(natType);
|
||||
if (natType > 0 && natType < 5)
|
||||
{
|
||||
NatTypeStatusLightLabel.Visible = true;
|
||||
UpdateNatTypeLight(natType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NatTypeStatusLightLabel.Visible = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -169,6 +179,35 @@ namespace Netch.Forms
|
||||
NatTypeStatusLightLabel.ForeColor = c;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新 NAT指示灯颜色
|
||||
/// </summary>
|
||||
/// <param name="natType"></param>
|
||||
private void UpdateNatTypeLight(int natType)
|
||||
{
|
||||
Color c;
|
||||
switch (natType)
|
||||
{
|
||||
case 1:
|
||||
c = Color.LimeGreen;
|
||||
break;
|
||||
case 2:
|
||||
c = Color.Yellow;
|
||||
break;
|
||||
case 3:
|
||||
c = Color.Red;
|
||||
break;
|
||||
case 4:
|
||||
c = Color.Black;
|
||||
break;
|
||||
default:
|
||||
c = Color.Black;
|
||||
break;
|
||||
}
|
||||
|
||||
NatTypeStatusLightLabel.ForeColor = c;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新状态栏文本
|
||||
/// </summary>
|
||||
|
||||
@@ -16,10 +16,6 @@ namespace Netch.Forms
|
||||
{
|
||||
public partial class MainForm : Form
|
||||
{
|
||||
/// <summary>
|
||||
/// 主控制器
|
||||
/// </summary>
|
||||
private MainController _mainController = new MainController();
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
@@ -31,35 +27,16 @@ namespace Netch.Forms
|
||||
CheckForIllegalCrossThreadCalls = false;
|
||||
}
|
||||
|
||||
private void SaveConfigs()
|
||||
{
|
||||
Global.Settings.ServerComboBoxSelectedIndex = ServerComboBox.SelectedIndex;
|
||||
if (ModeComboBox.Items.Count != 0 && ModeComboBox.SelectedItem != null)
|
||||
{
|
||||
if (ModeComboBox.Tag is object[] list)
|
||||
{
|
||||
Global.Settings.ModeComboBoxSelectedIndex = list.ToList().IndexOf(ModeComboBox.SelectedItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
Global.Settings.ModeComboBoxSelectedIndex = ModeComboBox.Items.IndexOf(ModeComboBox.SelectedItem);
|
||||
}
|
||||
}
|
||||
|
||||
Configuration.Save();
|
||||
}
|
||||
|
||||
private void MainForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
OnlyInstance.Called += OnCalled;
|
||||
// 计算 ComboBox绘制 目标宽度
|
||||
_eWidth = ServerComboBox.Width / 10;
|
||||
|
||||
// 加载服务器
|
||||
InitServer();
|
||||
|
||||
// 加载模式
|
||||
Modes.Load();
|
||||
InitMode();
|
||||
InitServer();
|
||||
_comboBoxInitialized = true;
|
||||
|
||||
// 加载翻译
|
||||
InitText();
|
||||
@@ -106,6 +83,16 @@ namespace Netch.Forms
|
||||
CheckUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
// 检查订阅更新
|
||||
if (Global.Settings.UpdateSubscribeatWhenOpened)
|
||||
{
|
||||
UpdateServersFromSubscribe();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void OnCalled(object sender, OnlyInstance.Commands e)
|
||||
@@ -149,7 +136,7 @@ namespace Netch.Forms
|
||||
// 如果勾选了关闭时退出,自动点击退出按钮
|
||||
else
|
||||
{
|
||||
Exit(true);
|
||||
Exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,15 +149,8 @@ namespace Netch.Forms
|
||||
|
||||
private void SettingsButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
(Global.SettingForm = new SettingForm()).Show();
|
||||
Hide();
|
||||
}
|
||||
|
||||
|
||||
private void MainForm_VisibleChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (!Visible)
|
||||
return;
|
||||
new SettingForm().ShowDialog();
|
||||
|
||||
if (i18N.LangCode != Global.Settings.Language)
|
||||
{
|
||||
@@ -181,6 +161,8 @@ namespace Netch.Forms
|
||||
|
||||
if (ProfileButtons.Count != Global.Settings.ProfileCount)
|
||||
InitProfile();
|
||||
|
||||
Show();
|
||||
}
|
||||
|
||||
private void InitText()
|
||||
@@ -193,6 +175,7 @@ namespace Netch.Forms
|
||||
AddVMessServerToolStripMenuItem.Text = i18N.Translate("Add [VMess] Server");
|
||||
AddTrojanServerToolStripMenuItem.Text = i18N.Translate("Add [Trojan] Server");
|
||||
ModeToolStripMenuItem.Text = i18N.Translate("Mode");
|
||||
HelpToolStripMenuItem.Text = i18N.Translate("Help");
|
||||
CreateProcessModeToolStripMenuItem.Text = i18N.Translate("Create Process Mode");
|
||||
SubscribeToolStripMenuItem.Text = i18N.Translate("Subscribe");
|
||||
ManageSubscribeLinksToolStripMenuItem.Text = i18N.Translate("Manage Subscribe Links");
|
||||
@@ -204,8 +187,10 @@ namespace Netch.Forms
|
||||
UpdateACLToolStripMenuItem.Text = i18N.Translate("Update ACL");
|
||||
updateACLWithProxyToolStripMenuItem.Text = i18N.Translate("Update ACL with proxy");
|
||||
reinstallTapDriverToolStripMenuItem.Text = i18N.Translate("Reinstall TUN/TAP driver");
|
||||
CheckForUpdatesToolStripMenuItem.Text = i18N.Translate("Check for updates");
|
||||
OpenDirectoryToolStripMenuItem.Text = i18N.Translate("Open Directory");
|
||||
AboutToolStripButton.Text = i18N.Translate("About");
|
||||
NewVersionLabel.Text = i18N.Translate("New version available");
|
||||
// VersionLabel.Text = i18N.Translate("xxx");
|
||||
exitToolStripMenuItem.Text = i18N.Translate("Exit");
|
||||
ConfigurationGroupBox.Text = i18N.Translate("Configuration");
|
||||
@@ -234,9 +219,7 @@ namespace Netch.Forms
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please press Stop button first"));
|
||||
|
||||
Visible = true;
|
||||
ShowInTaskbar = true; // 显示在系统任务栏
|
||||
WindowState = FormWindowState.Normal; // 还原窗体
|
||||
NotifyIcon_MouseDoubleClick(null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -248,14 +231,7 @@ namespace Netch.Forms
|
||||
ControlFun();
|
||||
}
|
||||
|
||||
for (var i = 0; i < 16; i++)
|
||||
{
|
||||
if (State == State.Waiting || State == State.Stopped)
|
||||
break;
|
||||
Thread.Sleep(250);
|
||||
}
|
||||
|
||||
SaveConfigs();
|
||||
Configuration.Save();
|
||||
State = State.Terminating;
|
||||
}
|
||||
|
||||
@@ -287,144 +263,130 @@ namespace Netch.Forms
|
||||
|
||||
private void EditServerPictureBox_Click(object sender, EventArgs e)
|
||||
{
|
||||
SaveConfigs();
|
||||
// 当前ServerComboBox中至少有一项
|
||||
if (ServerComboBox.SelectedIndex != -1)
|
||||
{
|
||||
switch (Global.Settings.Server[ServerComboBox.SelectedIndex].Type)
|
||||
{
|
||||
case "Socks5":
|
||||
new Socks5(ServerComboBox.SelectedIndex).Show();
|
||||
break;
|
||||
case "SS":
|
||||
new Shadowsocks(ServerComboBox.SelectedIndex).Show();
|
||||
break;
|
||||
case "SSR":
|
||||
new ShadowsocksR(ServerComboBox.SelectedIndex).Show();
|
||||
break;
|
||||
case "VMess":
|
||||
new VMess(ServerComboBox.SelectedIndex).Show();
|
||||
break;
|
||||
case "Trojan":
|
||||
new Trojan(ServerComboBox.SelectedIndex).Show();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
Hide();
|
||||
}
|
||||
else
|
||||
if (ServerComboBox.SelectedIndex == -1)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select a server first"));
|
||||
return;
|
||||
}
|
||||
|
||||
Form server = Global.Settings.Server[ServerComboBox.SelectedIndex].Type switch
|
||||
{
|
||||
"Socks5" => new Socks5(Global.Settings.Server[ServerComboBox.SelectedIndex]),
|
||||
"SS" => new Shadowsocks(Global.Settings.Server[ServerComboBox.SelectedIndex]),
|
||||
"SSR" => new ShadowsocksR(Global.Settings.Server[ServerComboBox.SelectedIndex]),
|
||||
"VMess" => new VMess(Global.Settings.Server[ServerComboBox.SelectedIndex]),
|
||||
"Trojan" => new Trojan(Global.Settings.Server[ServerComboBox.SelectedIndex]),
|
||||
_ => null
|
||||
};
|
||||
Hide();
|
||||
server?.ShowDialog();
|
||||
InitServer();
|
||||
Configuration.Save();
|
||||
Show();
|
||||
}
|
||||
|
||||
private void SpeedPictureBox_Click(object sender, EventArgs e)
|
||||
private async void SpeedPictureBox_Click(object sender, EventArgs e)
|
||||
{
|
||||
Enabled = false;
|
||||
StatusText(i18N.Translate("Testing"));
|
||||
|
||||
Task.Run(() =>
|
||||
try
|
||||
{
|
||||
await Task.Run(TestServer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
TestServer();
|
||||
|
||||
Enabled = true;
|
||||
StatusText(i18N.Translate("Test done"));
|
||||
Refresh();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void EditModePictureBox_Click(object sender, EventArgs e)
|
||||
{
|
||||
// 当前ModeComboBox中至少有一项
|
||||
if (ModeComboBox.Items.Count > 0 && ModeComboBox.SelectedIndex != -1)
|
||||
{
|
||||
SaveConfigs();
|
||||
var selectedMode = (Models.Mode) ModeComboBox.SelectedItem;
|
||||
// 只允许修改进程加速的模式
|
||||
if (selectedMode.Type == 0)
|
||||
{
|
||||
//Process.Start(Environment.CurrentDirectory + "\\mode\\" + selectedMode.FileName + ".txt");
|
||||
var process = new Process(selectedMode);
|
||||
process.Text = "Edit Process Mode";
|
||||
process.Show();
|
||||
Hide();
|
||||
}
|
||||
}
|
||||
else
|
||||
if (ModeComboBox.Items.Count <= 0 || ModeComboBox.SelectedIndex == -1)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedMode = (Models.Mode) ModeComboBox.SelectedItem;
|
||||
switch (selectedMode.Type)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
Hide();
|
||||
new Process(selectedMode).ShowDialog();
|
||||
InitMode();
|
||||
Show();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
MessageBoxX.Show($"Current not support editing {selectedMode.TypeToString()} Mode");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteModePictureBox_Click(object sender, EventArgs e)
|
||||
{
|
||||
// 当前ModeComboBox中至少有一项
|
||||
if (ModeComboBox.Items.Count > 0 && ModeComboBox.SelectedIndex != -1)
|
||||
{
|
||||
var selectedMode = (Models.Mode) ModeComboBox.SelectedItem;
|
||||
|
||||
//删除模式文件
|
||||
selectedMode.DeleteFile("mode");
|
||||
|
||||
ModeComboBox.Items.Clear();
|
||||
Global.ModeFiles.Remove(selectedMode);
|
||||
var array = Global.ModeFiles.ToArray();
|
||||
Array.Sort(array, (a, b) => string.Compare(a.Remark, b.Remark, StringComparison.Ordinal));
|
||||
ModeComboBox.Items.AddRange(array);
|
||||
|
||||
SelectLastMode();
|
||||
Configuration.Save();
|
||||
}
|
||||
else
|
||||
if (ModeComboBox.Items.Count <= 0 || ModeComboBox.SelectedIndex == -1)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select a mode first"));
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedMode = (Models.Mode) ModeComboBox.SelectedItem;
|
||||
this.ModeComboBox.Items.Remove(selectedMode);
|
||||
Modes.Delete(selectedMode);
|
||||
|
||||
SelectLastMode();
|
||||
}
|
||||
|
||||
private void CopyLinkPictureBox_Click(object sender, EventArgs e)
|
||||
{
|
||||
// 当前ServerComboBox中至少有一项
|
||||
if (ServerComboBox.SelectedIndex != -1)
|
||||
{
|
||||
var selectedMode = (Models.Server) ServerComboBox.SelectedItem;
|
||||
try
|
||||
{
|
||||
//听说巨硬BUG经常会炸,所以Catch一下 :D
|
||||
Clipboard.SetText(ShareLink.GetShareLink(selectedMode));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
else
|
||||
if (ServerComboBox.SelectedIndex == -1)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select a server first"));
|
||||
return;
|
||||
}
|
||||
|
||||
var selectedMode = (Models.Server) ServerComboBox.SelectedItem;
|
||||
try
|
||||
{
|
||||
//听说巨硬BUG经常会炸,所以Catch一下 :D
|
||||
Clipboard.SetText(ShareLink.GetShareLink(selectedMode));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteServerPictureBox_Click(object sender, EventArgs e)
|
||||
{
|
||||
// 当前 ServerComboBox 中至少有一项
|
||||
if (ServerComboBox.SelectedIndex != -1)
|
||||
{
|
||||
var index = ServerComboBox.SelectedIndex;
|
||||
|
||||
Global.Settings.Server.Remove(ServerComboBox.SelectedItem as Models.Server);
|
||||
ServerComboBox.Items.RemoveAt(index);
|
||||
|
||||
if (ServerComboBox.Items.Count > 0)
|
||||
{
|
||||
ServerComboBox.SelectedIndex = index != 0 ? index - 1 : index;
|
||||
}
|
||||
|
||||
Configuration.Save();
|
||||
}
|
||||
else
|
||||
if (ServerComboBox.SelectedIndex == -1)
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please select a server first"));
|
||||
return;
|
||||
}
|
||||
|
||||
var index = ServerComboBox.SelectedIndex;
|
||||
|
||||
Global.Settings.Server.Remove(ServerComboBox.SelectedItem as Models.Server);
|
||||
InitServer();
|
||||
|
||||
Configuration.Save();
|
||||
|
||||
if (ServerComboBox.Items.Count > 0)
|
||||
{
|
||||
ServerComboBox.SelectedIndex = index != 0 ? index - 1 : index;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -472,14 +434,26 @@ namespace Netch.Forms
|
||||
|
||||
#endregion
|
||||
|
||||
private bool _comboBoxInitialized = false;
|
||||
|
||||
private void ModeComboBox_SelectedIndexChanged(object sender, EventArgs o)
|
||||
{
|
||||
if (!_comboBoxInitialized) return;
|
||||
Global.Settings.ModeComboBoxSelectedIndex = ModeComboBox.SelectedIndex;
|
||||
}
|
||||
|
||||
private void ServerComboBox_SelectedIndexChanged(object sender, EventArgs o)
|
||||
{
|
||||
if (!_comboBoxInitialized) return;
|
||||
Global.Settings.ServerComboBoxSelectedIndex = ServerComboBox.SelectedIndex;
|
||||
}
|
||||
|
||||
private void NatTypeStatusLabel_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (_state == State.Started && MainController.NttTested)
|
||||
{
|
||||
MainController.NatTest();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
72
Netch/Forms/Mode/Process.Designer.cs
generated
@@ -1,4 +1,7 @@
|
||||
namespace Netch.Forms.Mode
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace Netch.Forms.Mode
|
||||
{
|
||||
partial class Process
|
||||
{
|
||||
@@ -28,11 +31,10 @@
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.components = new System.ComponentModel.Container();
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Process));
|
||||
this.ConfigurationGroupBox = new System.Windows.Forms.GroupBox();
|
||||
this.UseCustomFilenameBox = new System.Windows.Forms.CheckBox();
|
||||
this.TimeDataButton = new System.Windows.Forms.RadioButton();
|
||||
this.StaySameButton = new System.Windows.Forms.RadioButton();
|
||||
this.FilenameLabel = new System.Windows.Forms.Label();
|
||||
this.FilenameTextBox = new System.Windows.Forms.TextBox();
|
||||
this.ScanButton = new System.Windows.Forms.Button();
|
||||
@@ -42,16 +44,17 @@
|
||||
this.RuleListBox = new System.Windows.Forms.ListBox();
|
||||
this.RemarkTextBox = new System.Windows.Forms.TextBox();
|
||||
this.RemarkLabel = new System.Windows.Forms.Label();
|
||||
this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
|
||||
this.ControlButton = new System.Windows.Forms.Button();
|
||||
this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.ConfigurationGroupBox.SuspendLayout();
|
||||
this.ProcessGroupBox.SuspendLayout();
|
||||
this.contextMenuStrip.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// ConfigurationGroupBox
|
||||
//
|
||||
this.ConfigurationGroupBox.Controls.Add(this.UseCustomFilenameBox);
|
||||
this.ConfigurationGroupBox.Controls.Add(this.TimeDataButton);
|
||||
this.ConfigurationGroupBox.Controls.Add(this.StaySameButton);
|
||||
this.ConfigurationGroupBox.Controls.Add(this.FilenameLabel);
|
||||
this.ConfigurationGroupBox.Controls.Add(this.FilenameTextBox);
|
||||
this.ConfigurationGroupBox.Controls.Add(this.ScanButton);
|
||||
@@ -69,35 +72,13 @@
|
||||
// UseCustomFilenameBox
|
||||
//
|
||||
this.UseCustomFilenameBox.AutoSize = true;
|
||||
this.UseCustomFilenameBox.Location = new System.Drawing.Point(84, 82);
|
||||
this.UseCustomFilenameBox.Location = new System.Drawing.Point(84, 76);
|
||||
this.UseCustomFilenameBox.Name = "UseCustomFilenameBox";
|
||||
this.UseCustomFilenameBox.Size = new System.Drawing.Size(152, 21);
|
||||
this.UseCustomFilenameBox.TabIndex = 9;
|
||||
this.UseCustomFilenameBox.Text = "Use Custom Filename";
|
||||
this.UseCustomFilenameBox.UseVisualStyleBackColor = true;
|
||||
this.UseCustomFilenameBox.CheckedChanged += new System.EventHandler(this.UseCustomFileNameBox_CheckedChanged);
|
||||
//
|
||||
// TimeDataButton
|
||||
//
|
||||
this.TimeDataButton.AutoSize = true;
|
||||
this.TimeDataButton.Location = new System.Drawing.Point(197, 106);
|
||||
this.TimeDataButton.Name = "TimeDataButton";
|
||||
this.TimeDataButton.Size = new System.Drawing.Size(84, 21);
|
||||
this.TimeDataButton.TabIndex = 8;
|
||||
this.TimeDataButton.TabStop = true;
|
||||
this.TimeDataButton.Text = "Time data";
|
||||
this.TimeDataButton.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// StaySameButton
|
||||
//
|
||||
this.StaySameButton.AutoSize = true;
|
||||
this.StaySameButton.Location = new System.Drawing.Point(84, 106);
|
||||
this.StaySameButton.Name = "StaySameButton";
|
||||
this.StaySameButton.Size = new System.Drawing.Size(107, 21);
|
||||
this.StaySameButton.TabIndex = 7;
|
||||
this.StaySameButton.TabStop = true;
|
||||
this.StaySameButton.Text = "Stay the same";
|
||||
this.StaySameButton.UseVisualStyleBackColor = true;
|
||||
this.UseCustomFilenameBox.CheckedChanged += new System.EventHandler(this.UseCustomFilenameBox_CheckedChanged);
|
||||
//
|
||||
// FilenameLabel
|
||||
//
|
||||
@@ -156,9 +137,9 @@
|
||||
//
|
||||
this.RuleListBox.FormattingEnabled = true;
|
||||
this.RuleListBox.ItemHeight = 17;
|
||||
this.RuleListBox.Location = new System.Drawing.Point(6, 134);
|
||||
this.RuleListBox.Location = new System.Drawing.Point(6, 100);
|
||||
this.RuleListBox.Name = "RuleListBox";
|
||||
this.RuleListBox.Size = new System.Drawing.Size(328, 123);
|
||||
this.RuleListBox.Size = new System.Drawing.Size(328, 157);
|
||||
this.RuleListBox.TabIndex = 2;
|
||||
this.RuleListBox.MouseUp += new System.Windows.Forms.MouseEventHandler(this.RuleListBox_MouseUp);
|
||||
//
|
||||
@@ -168,6 +149,7 @@
|
||||
this.RemarkTextBox.Name = "RemarkTextBox";
|
||||
this.RemarkTextBox.Size = new System.Drawing.Size(250, 23);
|
||||
this.RemarkTextBox.TabIndex = 1;
|
||||
this.RemarkTextBox.TextChanged += new System.EventHandler(this.RemarkTextBox_TextChanged);
|
||||
//
|
||||
// RemarkLabel
|
||||
//
|
||||
@@ -178,6 +160,12 @@
|
||||
this.RemarkLabel.TabIndex = 0;
|
||||
this.RemarkLabel.Text = "Remark";
|
||||
//
|
||||
// contextMenuStrip
|
||||
//
|
||||
this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {this.DeleteToolStripMenuItem});
|
||||
this.contextMenuStrip.Name = "contextMenuStrip";
|
||||
this.contextMenuStrip.Size = new System.Drawing.Size(153, 48);
|
||||
//
|
||||
// ControlButton
|
||||
//
|
||||
this.ControlButton.Location = new System.Drawing.Point(277, 362);
|
||||
@@ -188,6 +176,13 @@
|
||||
this.ControlButton.UseVisualStyleBackColor = true;
|
||||
this.ControlButton.Click += new System.EventHandler(this.ControlButton_Click);
|
||||
//
|
||||
// DeleteToolStripMenuItem
|
||||
//
|
||||
this.DeleteToolStripMenuItem.Name = "DeleteToolStripMenuItem";
|
||||
this.DeleteToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
|
||||
this.DeleteToolStripMenuItem.Text = "Delete";
|
||||
this.DeleteToolStripMenuItem.Click += new System.EventHandler(this.deleteRule_Click);
|
||||
//
|
||||
// Process
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
|
||||
@@ -195,27 +190,28 @@
|
||||
this.ClientSize = new System.Drawing.Size(364, 397);
|
||||
this.Controls.Add(this.ControlButton);
|
||||
this.Controls.Add(this.ConfigurationGroupBox);
|
||||
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte) (134)));
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Icon = ((System.Drawing.Icon) (resources.GetObject("$this.Icon")));
|
||||
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.MaximizeBox = false;
|
||||
this.Name = "Process";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "Create Process Mode";
|
||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ModeForm_FormClosing);
|
||||
this.Load += new System.EventHandler(this.ModeForm_Load);
|
||||
this.ConfigurationGroupBox.ResumeLayout(false);
|
||||
this.ConfigurationGroupBox.PerformLayout();
|
||||
this.ProcessGroupBox.ResumeLayout(false);
|
||||
this.ProcessGroupBox.PerformLayout();
|
||||
this.contextMenuStrip.ResumeLayout(false);
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.GroupBox ConfigurationGroupBox;
|
||||
private System.Windows.Forms.ContextMenuStrip contextMenuStrip;
|
||||
private System.Windows.Forms.ToolStripMenuItem DeleteToolStripMenuItem;
|
||||
public System.Windows.Forms.GroupBox ConfigurationGroupBox;
|
||||
private System.Windows.Forms.Label RemarkLabel;
|
||||
private System.Windows.Forms.GroupBox ProcessGroupBox;
|
||||
private System.Windows.Forms.ListBox RuleListBox;
|
||||
@@ -223,11 +219,9 @@
|
||||
private System.Windows.Forms.TextBox ProcessNameTextBox;
|
||||
private System.Windows.Forms.Button AddButton;
|
||||
private System.Windows.Forms.Button ScanButton;
|
||||
private System.Windows.Forms.Button ControlButton;
|
||||
public System.Windows.Forms.Button ControlButton;
|
||||
private System.Windows.Forms.Label FilenameLabel;
|
||||
private System.Windows.Forms.TextBox FilenameTextBox;
|
||||
private System.Windows.Forms.RadioButton StaySameButton;
|
||||
private System.Windows.Forms.RadioButton TimeDataButton;
|
||||
private System.Windows.Forms.CheckBox UseCustomFilenameBox;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Microsoft.WindowsAPICodePack.Dialogs;
|
||||
using Netch.Utils;
|
||||
@@ -9,11 +12,15 @@ namespace Netch.Forms.Mode
|
||||
{
|
||||
public partial class Process : Form
|
||||
{
|
||||
//用于判断当前窗口是否为编辑模式
|
||||
private bool EditMode;
|
||||
/// <summary>
|
||||
/// 被编辑的模式
|
||||
/// </summary>
|
||||
private readonly Models.Mode _mode;
|
||||
|
||||
//被编辑模式坐标
|
||||
private Models.Mode EditMode_Old;
|
||||
/// <summary>
|
||||
/// 是否被编辑过
|
||||
/// </summary>
|
||||
public bool Edited { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 编辑模式
|
||||
@@ -21,21 +28,25 @@ namespace Netch.Forms.Mode
|
||||
/// <param name="mode">模式</param>
|
||||
public Process(Models.Mode mode)
|
||||
{
|
||||
InitializeComponent();
|
||||
if (mode.Type != 0)
|
||||
{
|
||||
throw new Exception("请传入进程模式");
|
||||
}
|
||||
|
||||
InitializeComponent();
|
||||
CheckForIllegalCrossThreadCalls = false;
|
||||
|
||||
EditMode_Old = mode;
|
||||
Text = "Edit Process Mode";
|
||||
//循环填充已有规则
|
||||
mode.Rule.ForEach(i => RuleListBox.Items.Add(i));
|
||||
this._mode = mode;
|
||||
RuleListBox.Items.AddRange(mode.Rule.ToArray());
|
||||
|
||||
EditMode = true;
|
||||
StaySameButton.Enabled = false;
|
||||
TimeDataButton.Enabled = false;
|
||||
FilenameTextBox.Enabled = false;
|
||||
FilenameLabel.Enabled = false;
|
||||
UseCustomFilenameBox.Enabled = false;
|
||||
#region 禁用文件名更改
|
||||
|
||||
RemarkTextBox.TextChanged -= RemarkTextBox_TextChanged;
|
||||
FilenameTextBox.Enabled =
|
||||
UseCustomFilenameBox.Enabled = false;
|
||||
|
||||
#endregion
|
||||
|
||||
FilenameTextBox.Text = mode.FileName;
|
||||
RemarkTextBox.Text = mode.Remark;
|
||||
@@ -44,11 +55,9 @@ namespace Netch.Forms.Mode
|
||||
public Process()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
CheckForIllegalCrossThreadCalls = false;
|
||||
|
||||
EditMode = false;
|
||||
EditMode_Old = null;
|
||||
FilenameTextBox.Enabled = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -88,6 +97,7 @@ namespace Netch.Forms.Mode
|
||||
if (FileChildInfo.Name.EndsWith(".exe") && !RuleListBox.Items.Contains(FileChildInfo.Name))
|
||||
{
|
||||
RuleListBox.Items.Add(FileChildInfo.Name);
|
||||
Edited = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,42 +108,17 @@ namespace Netch.Forms.Mode
|
||||
}
|
||||
}
|
||||
|
||||
private void ModeForm_Load(object sender, EventArgs e)
|
||||
public void ModeForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
Text = i18N.Translate(Text);
|
||||
ConfigurationGroupBox.Text = i18N.Translate(ConfigurationGroupBox.Text);
|
||||
RemarkLabel.Text = i18N.Translate(RemarkLabel.Text);
|
||||
FilenameLabel.Text = i18N.Translate(FilenameLabel.Text);
|
||||
UseCustomFilenameBox.Text = i18N.Translate(UseCustomFilenameBox.Text);
|
||||
StaySameButton.Text = i18N.Translate(StaySameButton.Text);
|
||||
TimeDataButton.Text = i18N.Translate(TimeDataButton.Text);
|
||||
AddButton.Text = i18N.Translate(AddButton.Text);
|
||||
ScanButton.Text = i18N.Translate(ScanButton.Text);
|
||||
ControlButton.Text = i18N.Translate(ControlButton.Text);
|
||||
|
||||
if (Global.Settings.ModeFileNameType == 0)
|
||||
{
|
||||
UseCustomFilenameBox.Checked = true;
|
||||
StaySameButton.Enabled = false;
|
||||
TimeDataButton.Enabled = false;
|
||||
}
|
||||
else if (Global.Settings.ModeFileNameType == 1)
|
||||
{
|
||||
FilenameTextBox.Enabled = false;
|
||||
FilenameLabel.Enabled = false;
|
||||
StaySameButton.Checked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
FilenameTextBox.Enabled = false;
|
||||
FilenameLabel.Enabled = false;
|
||||
TimeDataButton.Checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void ModeForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
Global.MainForm.Show();
|
||||
DeleteToolStripMenuItem.Text = i18N.Translate(DeleteToolStripMenuItem.Text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -141,44 +126,48 @@ namespace Netch.Forms.Mode
|
||||
/// </summary>
|
||||
private void RuleListBox_MouseUp(object sender, MouseEventArgs e)
|
||||
{
|
||||
var strip = new ContextMenuStrip();
|
||||
strip.Items.Add(i18N.Translate("Delete"));
|
||||
RuleListBox.SelectedIndex = RuleListBox.IndexFromPoint(e.X, e.Y);
|
||||
if (RuleListBox.SelectedIndex == -1)
|
||||
return;
|
||||
if (e.Button == MouseButtons.Right)
|
||||
{
|
||||
strip.Show(RuleListBox, e.Location); //鼠标右键按下弹出菜单
|
||||
strip.MouseClick += deleteRule_Click;
|
||||
contextMenuStrip.Show(RuleListBox, e.Location);
|
||||
}
|
||||
}
|
||||
|
||||
void deleteRule_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (RuleListBox.SelectedIndex != -1)
|
||||
{
|
||||
RuleListBox.Items.RemoveAt(RuleListBox.SelectedIndex);
|
||||
}
|
||||
if (RuleListBox.SelectedIndex == -1) return;
|
||||
RuleListBox.Items.RemoveAt(RuleListBox.SelectedIndex);
|
||||
Edited = true;
|
||||
}
|
||||
|
||||
private void AddButton_Click(object sender, EventArgs e)
|
||||
private async void AddButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(ProcessNameTextBox.Text))
|
||||
await Task.Run(() =>
|
||||
{
|
||||
var process = ProcessNameTextBox.Text;
|
||||
if (!process.EndsWith(".exe"))
|
||||
if (!string.IsNullOrWhiteSpace(ProcessNameTextBox.Text))
|
||||
{
|
||||
process += ".exe";
|
||||
}
|
||||
var process = ProcessNameTextBox.Text;
|
||||
if (!process.EndsWith(".exe"))
|
||||
{
|
||||
process += ".exe";
|
||||
}
|
||||
|
||||
if (!RuleListBox.Items.Contains(process))
|
||||
if (!RuleListBox.Items.Contains(process))
|
||||
{
|
||||
RuleListBox.Items.Add(process);
|
||||
}
|
||||
|
||||
Edited = true;
|
||||
RuleListBox.SelectedIndex = RuleListBox.Items.IndexOf(process);
|
||||
ProcessNameTextBox.Text = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
RuleListBox.Items.Add(process);
|
||||
MessageBoxX.Show(i18N.Translate("Please enter an process name (xxx.exe)"));
|
||||
}
|
||||
|
||||
ProcessNameTextBox.Text = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please enter an process name (xxx.exe)"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void ScanButton_Click(object sender, EventArgs e)
|
||||
@@ -199,148 +188,83 @@ namespace Netch.Forms.Mode
|
||||
}
|
||||
}
|
||||
|
||||
private void ControlButton_Click(object sender, EventArgs e)
|
||||
public void ControlButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (EditMode)
|
||||
if (RuleListBox.Items.Count == 0)
|
||||
{
|
||||
// 编辑模式
|
||||
MessageBoxX.Show(i18N.Translate("Unable to add empty rule"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (RuleListBox.Items.Count != 0)
|
||||
{
|
||||
var mode = new Models.Mode
|
||||
{
|
||||
BypassChina = false,
|
||||
FileName = FilenameTextBox.Text,
|
||||
Type = 0,
|
||||
Remark = RemarkTextBox.Text
|
||||
};
|
||||
if (string.IsNullOrWhiteSpace(RemarkTextBox.Text))
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please enter a mode remark"));
|
||||
return;
|
||||
}
|
||||
|
||||
var text = $"# {RemarkTextBox.Text}, 0\r\n";
|
||||
foreach (var item in RuleListBox.Items)
|
||||
{
|
||||
var process = item as string;
|
||||
mode.Rule.Add(process);
|
||||
text += process + "\r\n";
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(FilenameTextBox.Text))
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please enter a mode filename"));
|
||||
return;
|
||||
}
|
||||
|
||||
text = text.Substring(0, text.Length - 2);
|
||||
if (_mode != null)
|
||||
{
|
||||
_mode.Remark = RemarkTextBox.Text;
|
||||
_mode.Rule.Clear();
|
||||
_mode.Rule.AddRange(RuleListBox.Items.Cast<string>());
|
||||
|
||||
if (!Directory.Exists("mode"))
|
||||
{
|
||||
Directory.CreateDirectory("mode");
|
||||
}
|
||||
|
||||
File.WriteAllText(Path.Combine("mode", FilenameTextBox.Text) + ".txt", text);
|
||||
|
||||
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
|
||||
|
||||
Global.MainForm.UpdateMode(mode, EditMode_Old);
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Unable to add empty rule"));
|
||||
}
|
||||
Modes.WriteFile(_mode);
|
||||
Edited = false;
|
||||
MessageBoxX.Show(i18N.Translate("Mode updated successfully"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 自定义文件名
|
||||
if (UseCustomFilenameBox.Checked)
|
||||
var fullName = Modes.GetFullPath(FilenameTextBox.Text + ".txt");
|
||||
if (File.Exists(fullName))
|
||||
{
|
||||
Global.Settings.ModeFileNameType = 0;
|
||||
}
|
||||
// 使用和备注一致的文件名
|
||||
else if (StaySameButton.Checked)
|
||||
{
|
||||
Global.Settings.ModeFileNameType = 1;
|
||||
FilenameTextBox.Text = RemarkTextBox.Text;
|
||||
}
|
||||
// 使用时间数据作为文件名
|
||||
else
|
||||
{
|
||||
Global.Settings.ModeFileNameType = 2;
|
||||
FilenameTextBox.Text = ((long) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds).ToString();
|
||||
MessageBoxX.Show(i18N.Translate("File already exists.\n Please Change the filename"));
|
||||
return;
|
||||
}
|
||||
|
||||
Configuration.Save();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(RemarkTextBox.Text))
|
||||
var mode = new Models.Mode
|
||||
{
|
||||
if (Global.Settings.ModeFileNameType == 0 && string.IsNullOrWhiteSpace(FilenameTextBox.Text))
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please enter a mode filename"));
|
||||
return;
|
||||
}
|
||||
BypassChina = false,
|
||||
FileName = FilenameTextBox.Text,
|
||||
Type = 0,
|
||||
Remark = RemarkTextBox.Text
|
||||
};
|
||||
mode.Rule.AddRange(RuleListBox.Items.Cast<string>());
|
||||
|
||||
var ModeFilename = Path.Combine("mode", FilenameTextBox.Text);
|
||||
|
||||
// 如果文件已存在,返回
|
||||
if (File.Exists(ModeFilename + ".txt"))
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("File already exists.\n Please Change the filename"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (RuleListBox.Items.Count != 0)
|
||||
{
|
||||
var mode = new Models.Mode
|
||||
{
|
||||
BypassChina = false,
|
||||
FileName = FilenameTextBox.Text,
|
||||
Type = 0,
|
||||
Remark = RemarkTextBox.Text
|
||||
};
|
||||
|
||||
var text = $"# {RemarkTextBox.Text}, 0\r\n";
|
||||
foreach (var item in RuleListBox.Items)
|
||||
{
|
||||
var process = item as string;
|
||||
mode.Rule.Add(process);
|
||||
text += process + "\r\n";
|
||||
}
|
||||
|
||||
text = text.Substring(0, text.Length - 2);
|
||||
|
||||
if (!Directory.Exists("mode"))
|
||||
{
|
||||
Directory.CreateDirectory("mode");
|
||||
}
|
||||
|
||||
File.WriteAllText(ModeFilename + ".txt", text);
|
||||
|
||||
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
|
||||
|
||||
Global.MainForm.AddMode(mode);
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Unable to add empty rule"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please enter a mode remark"));
|
||||
}
|
||||
Modes.WriteFile(mode);
|
||||
Modes.Add(mode);
|
||||
MessageBoxX.Show(i18N.Translate("Mode added successfully"));
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
private void UseCustomFileNameBox_CheckedChanged(object sender, EventArgs e)
|
||||
private async void RemarkTextBox_TextChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (UseCustomFilenameBox.Checked)
|
||||
await Task.Run(() =>
|
||||
{
|
||||
StaySameButton.Enabled = false;
|
||||
TimeDataButton.Enabled = false;
|
||||
FilenameTextBox.Enabled = true;
|
||||
FilenameLabel.Enabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
StaySameButton.Enabled = true;
|
||||
TimeDataButton.Enabled = true;
|
||||
FilenameTextBox.Enabled = false;
|
||||
FilenameLabel.Enabled = false;
|
||||
}
|
||||
if (!UseCustomFilenameBox.Checked)
|
||||
{
|
||||
var invalidFileChars = Path.GetInvalidFileNameChars();
|
||||
var fileName = new StringBuilder(RemarkTextBox.Text);
|
||||
foreach (var c in invalidFileChars)
|
||||
{
|
||||
fileName.Replace(c, '_');
|
||||
}
|
||||
|
||||
FilenameTextBox.Text = fileName.ToString();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void UseCustomFilenameBox_CheckedChanged(object sender, EventArgs e)
|
||||
{
|
||||
FilenameTextBox.Enabled = UseCustomFilenameBox.Checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,6 +117,9 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="contextMenuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="$this.Icon" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
|
||||
1
Netch/Forms/Server/Shadowsocks.Designer.cs
generated
@@ -218,7 +218,6 @@
|
||||
this.Name = "Shadowsocks";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "Shadowsocks";
|
||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Shadowsocks_FormClosing);
|
||||
this.Load += new System.EventHandler(this.Shadowsocks_Load);
|
||||
this.ConfigurationGroupBox.ResumeLayout(false);
|
||||
this.ConfigurationGroupBox.PerformLayout();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -8,21 +7,19 @@ namespace Netch.Forms.Server
|
||||
{
|
||||
public partial class Shadowsocks : Form
|
||||
{
|
||||
public int Index;
|
||||
private readonly Models.Server _server;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <param name="index">需要编辑的索引</param>
|
||||
public Shadowsocks(int index = -1)
|
||||
public Shadowsocks(Models.Server server = default)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Index = index;
|
||||
_server = server ?? new Models.Server {EncryptMethod = Global.EncryptMethods.SS[0]};
|
||||
}
|
||||
|
||||
private void Shadowsocks_Load(object sender, EventArgs e)
|
||||
{
|
||||
#region InitText
|
||||
|
||||
ConfigurationGroupBox.Text = i18N.Translate(ConfigurationGroupBox.Text);
|
||||
RemarkLabel.Text = i18N.Translate(RemarkLabel.Text);
|
||||
AddressLabel.Text = i18N.Translate(AddressLabel.Text);
|
||||
@@ -32,44 +29,32 @@ namespace Netch.Forms.Server
|
||||
PluginOptionsLabel.Text = i18N.Translate(PluginOptionsLabel.Text);
|
||||
ControlButton.Text = i18N.Translate(ControlButton.Text);
|
||||
|
||||
foreach (var encrypt in Global.EncryptMethods.SS)
|
||||
{
|
||||
EncryptMethodComboBox.Items.Add(encrypt);
|
||||
}
|
||||
EncryptMethodComboBox.Items.AddRange(Global.EncryptMethods.SS.ToArray());
|
||||
|
||||
if (Index != -1)
|
||||
{
|
||||
RemarkTextBox.Text = Global.Settings.Server[Index].Remark;
|
||||
AddressTextBox.Text = Global.Settings.Server[Index].Hostname;
|
||||
PortTextBox.Text = Global.Settings.Server[Index].Port.ToString();
|
||||
PasswordTextBox.Text = Global.Settings.Server[Index].Password;
|
||||
EncryptMethodComboBox.SelectedIndex = Global.EncryptMethods.SS.IndexOf(Global.Settings.Server[Index].EncryptMethod);
|
||||
PluginTextBox.Text = Global.Settings.Server[Index].Plugin;
|
||||
PluginOptionsTextBox.Text = Global.Settings.Server[Index].PluginOption;
|
||||
}
|
||||
else
|
||||
{
|
||||
EncryptMethodComboBox.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void Shadowsocks_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
Global.MainForm.Show();
|
||||
RemarkTextBox.Text = _server.Remark;
|
||||
AddressTextBox.Text = _server.Hostname;
|
||||
PortTextBox.Text = _server.Port.ToString();
|
||||
PasswordTextBox.Text = _server.Password;
|
||||
EncryptMethodComboBox.SelectedIndex = Global.EncryptMethods.SS.IndexOf(_server.EncryptMethod);
|
||||
PluginTextBox.Text = _server.Plugin;
|
||||
PluginOptionsTextBox.Text = _server.PluginOption;
|
||||
}
|
||||
|
||||
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
|
||||
{
|
||||
var cbx = sender as ComboBox;
|
||||
if (cbx != null)
|
||||
if (sender is ComboBox cbx)
|
||||
{
|
||||
e.DrawBackground();
|
||||
|
||||
if (e.Index >= 0)
|
||||
{
|
||||
var sf = new StringFormat();
|
||||
sf.LineAlignment = StringAlignment.Center;
|
||||
sf.Alignment = StringAlignment.Center;
|
||||
var sf = new StringFormat
|
||||
{
|
||||
LineAlignment = StringAlignment.Center,
|
||||
Alignment = StringAlignment.Center
|
||||
};
|
||||
|
||||
var brush = new SolidBrush(cbx.ForeColor);
|
||||
|
||||
@@ -85,45 +70,28 @@ namespace Netch.Forms.Server
|
||||
|
||||
private void ControlButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!Regex.Match(PortTextBox.Text, "^[0-9]+$").Success)
|
||||
if (!int.TryParse(PortTextBox.Text, out var port))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Index == -1)
|
||||
|
||||
_server.Remark = RemarkTextBox.Text;
|
||||
_server.Type = "SS";
|
||||
_server.Hostname = AddressTextBox.Text;
|
||||
_server.Port = port;
|
||||
_server.Password = PasswordTextBox.Text;
|
||||
_server.EncryptMethod = EncryptMethodComboBox.Text;
|
||||
_server.Plugin = PluginTextBox.Text;
|
||||
_server.PluginOption = PluginOptionsTextBox.Text;
|
||||
_server.Country = null;
|
||||
|
||||
if (Global.Settings.Server.IndexOf(_server) == -1)
|
||||
{
|
||||
Global.Settings.Server.Add(new Models.Server
|
||||
{
|
||||
Remark = RemarkTextBox.Text,
|
||||
Type = "SS",
|
||||
Hostname = AddressTextBox.Text,
|
||||
Port = int.Parse(PortTextBox.Text),
|
||||
Password = PasswordTextBox.Text,
|
||||
EncryptMethod = EncryptMethodComboBox.Text,
|
||||
Plugin = PluginTextBox.Text,
|
||||
PluginOption = PluginOptionsTextBox.Text
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Global.Settings.Server[Index] = new Models.Server
|
||||
{
|
||||
Remark = RemarkTextBox.Text,
|
||||
Group = Global.Settings.Server[Index].Group,
|
||||
Type = "SS",
|
||||
Hostname = AddressTextBox.Text,
|
||||
Port = int.Parse(PortTextBox.Text),
|
||||
Password = PasswordTextBox.Text,
|
||||
EncryptMethod = EncryptMethodComboBox.Text,
|
||||
Plugin = PluginTextBox.Text,
|
||||
PluginOption = PluginOptionsTextBox.Text,
|
||||
Country = null
|
||||
};
|
||||
Global.Settings.Server.Add(_server);
|
||||
}
|
||||
|
||||
Configuration.Save();
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
Global.MainForm.InitServer();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
Netch/Forms/Server/ShadowsocksR.Designer.cs
generated
@@ -266,7 +266,6 @@
|
||||
this.Name = "ShadowsocksR";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "ShadowsocksR";
|
||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.ShadowsocksR_FormClosing);
|
||||
this.Load += new System.EventHandler(this.ShadowsocksR_Load);
|
||||
this.ConfigurationGroupBox.ResumeLayout(false);
|
||||
this.ConfigurationGroupBox.PerformLayout();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -8,21 +7,19 @@ namespace Netch.Forms.Server
|
||||
{
|
||||
public partial class ShadowsocksR : Form
|
||||
{
|
||||
public int Index;
|
||||
private readonly Models.Server _server;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <param name="index">需要编辑的索引</param>
|
||||
public ShadowsocksR(int index = -1)
|
||||
public ShadowsocksR(Models.Server server = default)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Index = index;
|
||||
_server = server ?? new Models.Server {EncryptMethod = Global.EncryptMethods.SSR[0]};
|
||||
}
|
||||
|
||||
private void ShadowsocksR_Load(object sender, EventArgs e)
|
||||
{
|
||||
#region InitText
|
||||
|
||||
ConfigurationGroupBox.Text = i18N.Translate(ConfigurationGroupBox.Text);
|
||||
RemarkLabel.Text = i18N.Translate(RemarkLabel.Text);
|
||||
AddressLabel.Text = i18N.Translate(AddressLabel.Text);
|
||||
@@ -34,58 +31,37 @@ namespace Netch.Forms.Server
|
||||
OBFSParamLabel.Text = i18N.Translate(OBFSParamLabel.Text);
|
||||
ControlButton.Text = i18N.Translate(ControlButton.Text);
|
||||
|
||||
foreach (var encrypt in Global.EncryptMethods.SSR)
|
||||
{
|
||||
EncryptMethodComboBox.Items.Add(encrypt);
|
||||
}
|
||||
EncryptMethodComboBox.Items.AddRange(Global.EncryptMethods.SSR.ToArray());
|
||||
|
||||
foreach (var protocol in Global.Protocols)
|
||||
{
|
||||
ProtocolComboBox.Items.Add(protocol);
|
||||
}
|
||||
ProtocolComboBox.Items.AddRange(Global.Protocols.ToArray());
|
||||
OBFSComboBox.Items.AddRange(Global.OBFSs.ToArray());
|
||||
|
||||
foreach (var obfs in Global.OBFSs)
|
||||
{
|
||||
OBFSComboBox.Items.Add(obfs);
|
||||
}
|
||||
#endregion
|
||||
|
||||
if (Index != -1)
|
||||
{
|
||||
RemarkTextBox.Text = Global.Settings.Server[Index].Remark;
|
||||
AddressTextBox.Text = Global.Settings.Server[Index].Hostname;
|
||||
PortTextBox.Text = Global.Settings.Server[Index].Port.ToString();
|
||||
PasswordTextBox.Text = Global.Settings.Server[Index].Password;
|
||||
EncryptMethodComboBox.SelectedIndex = Global.EncryptMethods.SSR.IndexOf(Global.Settings.Server[Index].EncryptMethod);
|
||||
ProtocolComboBox.SelectedIndex = Global.Protocols.IndexOf(Global.Settings.Server[Index].Protocol);
|
||||
ProtocolParamTextBox.Text = Global.Settings.Server[Index].ProtocolParam;
|
||||
OBFSComboBox.SelectedIndex = Global.OBFSs.IndexOf(Global.Settings.Server[Index].OBFS);
|
||||
OBFSOptionParamTextBox.Text = Global.Settings.Server[Index].OBFSParam;
|
||||
}
|
||||
else
|
||||
{
|
||||
EncryptMethodComboBox.SelectedIndex = 0;
|
||||
ProtocolComboBox.SelectedIndex = 0;
|
||||
OBFSComboBox.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void ShadowsocksR_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
Global.MainForm.Show();
|
||||
RemarkTextBox.Text = _server.Remark;
|
||||
AddressTextBox.Text = _server.Hostname;
|
||||
PortTextBox.Text = _server.Port.ToString();
|
||||
PasswordTextBox.Text = _server.Password;
|
||||
EncryptMethodComboBox.SelectedIndex = Global.EncryptMethods.SSR.IndexOf(_server.EncryptMethod);
|
||||
ProtocolComboBox.SelectedIndex = Global.Protocols.IndexOf(_server.Protocol);
|
||||
ProtocolParamTextBox.Text = _server.ProtocolParam;
|
||||
OBFSComboBox.SelectedIndex = Global.OBFSs.IndexOf(_server.OBFS);
|
||||
OBFSOptionParamTextBox.Text = _server.OBFSParam;
|
||||
}
|
||||
|
||||
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
|
||||
{
|
||||
var cbx = sender as ComboBox;
|
||||
if (cbx != null)
|
||||
if (sender is ComboBox cbx)
|
||||
{
|
||||
e.DrawBackground();
|
||||
|
||||
if (e.Index >= 0)
|
||||
{
|
||||
var sf = new StringFormat();
|
||||
sf.LineAlignment = StringAlignment.Center;
|
||||
sf.Alignment = StringAlignment.Center;
|
||||
var sf = new StringFormat
|
||||
{
|
||||
LineAlignment = StringAlignment.Center,
|
||||
Alignment = StringAlignment.Center
|
||||
};
|
||||
|
||||
var brush = new SolidBrush(cbx.ForeColor);
|
||||
|
||||
@@ -101,49 +77,30 @@ namespace Netch.Forms.Server
|
||||
|
||||
private void ControlButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!Regex.Match(PortTextBox.Text, "^[0-9]+$").Success)
|
||||
if (!int.TryParse(PortTextBox.Text, out var port))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Index == -1)
|
||||
|
||||
_server.Remark = RemarkTextBox.Text;
|
||||
_server.Type = "SSR";
|
||||
_server.Hostname = AddressTextBox.Text;
|
||||
_server.Port = port;
|
||||
_server.Password = PasswordTextBox.Text;
|
||||
_server.EncryptMethod = EncryptMethodComboBox.Text;
|
||||
_server.Protocol = ProtocolComboBox.Text;
|
||||
_server.ProtocolParam = ProtocolParamTextBox.Text;
|
||||
_server.OBFS = OBFSComboBox.Text;
|
||||
_server.OBFSParam = OBFSOptionParamTextBox.Text;
|
||||
_server.Country = null;
|
||||
|
||||
if (Global.Settings.Server.IndexOf(_server) == -1)
|
||||
{
|
||||
Global.Settings.Server.Add(new Models.Server
|
||||
{
|
||||
Remark = RemarkTextBox.Text,
|
||||
Type = "SSR",
|
||||
Hostname = AddressTextBox.Text,
|
||||
Port = int.Parse(PortTextBox.Text),
|
||||
Password = PasswordTextBox.Text,
|
||||
EncryptMethod = EncryptMethodComboBox.Text,
|
||||
Protocol = ProtocolComboBox.Text,
|
||||
ProtocolParam = ProtocolParamTextBox.Text,
|
||||
OBFS = OBFSComboBox.Text,
|
||||
OBFSParam = OBFSOptionParamTextBox.Text
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Global.Settings.Server[Index] = new Models.Server
|
||||
{
|
||||
Remark = RemarkTextBox.Text,
|
||||
Group = Global.Settings.Server[Index].Group,
|
||||
Type = "SSR",
|
||||
Hostname = AddressTextBox.Text,
|
||||
Port = int.Parse(PortTextBox.Text),
|
||||
Password = PasswordTextBox.Text,
|
||||
EncryptMethod = EncryptMethodComboBox.Text,
|
||||
Protocol = ProtocolComboBox.Text,
|
||||
ProtocolParam = ProtocolParamTextBox.Text,
|
||||
OBFS = OBFSComboBox.Text,
|
||||
OBFSParam = OBFSOptionParamTextBox.Text,
|
||||
Country = null
|
||||
};
|
||||
Global.Settings.Server.Add(_server);
|
||||
}
|
||||
|
||||
Configuration.Save();
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
Global.MainForm.InitServer();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
Netch/Forms/Server/Socks5.Designer.cs
generated
@@ -173,7 +173,6 @@
|
||||
this.Name = "Socks5";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "Socks5";
|
||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Shadowsocks_FormClosing);
|
||||
this.Load += new System.EventHandler(this.Shadowsocks_Load);
|
||||
this.ConfigurationGroupBox.ResumeLayout(false);
|
||||
this.ConfigurationGroupBox.PerformLayout();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -8,21 +7,19 @@ namespace Netch.Forms.Server
|
||||
{
|
||||
public partial class Socks5 : Form
|
||||
{
|
||||
public int Index;
|
||||
private readonly Models.Server _server;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <param name="index">需要编辑的索引</param>
|
||||
public Socks5(int index = -1)
|
||||
public Socks5(Models.Server server = default)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Index = index;
|
||||
_server = server ?? new Models.Server();
|
||||
}
|
||||
|
||||
private void Shadowsocks_Load(object sender, EventArgs e)
|
||||
{
|
||||
#region InitText
|
||||
|
||||
ConfigurationGroupBox.Text = i18N.Translate(ConfigurationGroupBox.Text);
|
||||
RemarkLabel.Text = i18N.Translate(RemarkLabel.Text);
|
||||
AddressLabel.Text = i18N.Translate(AddressLabel.Text);
|
||||
@@ -30,33 +27,28 @@ namespace Netch.Forms.Server
|
||||
PasswordLabel.Text = i18N.Translate(PasswordLabel.Text);
|
||||
ControlButton.Text = i18N.Translate(ControlButton.Text);
|
||||
|
||||
if (Index != -1)
|
||||
{
|
||||
RemarkTextBox.Text = Global.Settings.Server[Index].Remark;
|
||||
AddressTextBox.Text = Global.Settings.Server[Index].Hostname;
|
||||
PortTextBox.Text = Global.Settings.Server[Index].Port.ToString();
|
||||
UsernameTextBox.Text = Global.Settings.Server[Index].Username;
|
||||
PasswordTextBox.Text = Global.Settings.Server[Index].Password;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void Shadowsocks_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
Global.MainForm.Show();
|
||||
RemarkTextBox.Text = _server.Remark;
|
||||
AddressTextBox.Text = _server.Hostname;
|
||||
PortTextBox.Text = _server.Port.ToString();
|
||||
UsernameTextBox.Text = _server.Username;
|
||||
PasswordTextBox.Text = _server.Password;
|
||||
}
|
||||
|
||||
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
|
||||
{
|
||||
var cbx = sender as ComboBox;
|
||||
if (cbx != null)
|
||||
if (sender is ComboBox cbx)
|
||||
{
|
||||
e.DrawBackground();
|
||||
|
||||
if (e.Index >= 0)
|
||||
{
|
||||
var sf = new StringFormat();
|
||||
sf.LineAlignment = StringAlignment.Center;
|
||||
sf.Alignment = StringAlignment.Center;
|
||||
var sf = new StringFormat
|
||||
{
|
||||
LineAlignment = StringAlignment.Center,
|
||||
Alignment = StringAlignment.Center
|
||||
};
|
||||
|
||||
var brush = new SolidBrush(cbx.ForeColor);
|
||||
|
||||
@@ -72,41 +64,26 @@ namespace Netch.Forms.Server
|
||||
|
||||
private void ControlButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!Regex.Match(PortTextBox.Text, "^[0-9]+$").Success)
|
||||
if (!int.TryParse(PortTextBox.Text, out var port))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Index == -1)
|
||||
|
||||
_server.Remark = RemarkTextBox.Text;
|
||||
_server.Type = "Socks5";
|
||||
_server.Hostname = AddressTextBox.Text;
|
||||
_server.Port = port;
|
||||
_server.Username = UsernameTextBox.Text;
|
||||
_server.Password = PasswordTextBox.Text;
|
||||
_server.Country = null;
|
||||
|
||||
if (Global.Settings.Server.IndexOf(_server) == -1)
|
||||
{
|
||||
Global.Settings.Server.Add(new Models.Server
|
||||
{
|
||||
Remark = RemarkTextBox.Text,
|
||||
Type = "Socks5",
|
||||
Hostname = AddressTextBox.Text,
|
||||
Port = int.Parse(PortTextBox.Text),
|
||||
Username = UsernameTextBox.Text,
|
||||
Password = PasswordTextBox.Text
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Global.Settings.Server[Index] = new Models.Server
|
||||
{
|
||||
Remark = RemarkTextBox.Text,
|
||||
Group = Global.Settings.Server[Index].Group,
|
||||
Type = "Socks5",
|
||||
Hostname = AddressTextBox.Text,
|
||||
Port = int.Parse(PortTextBox.Text),
|
||||
Username = UsernameTextBox.Text,
|
||||
Password = PasswordTextBox.Text,
|
||||
Country = null
|
||||
};
|
||||
Global.Settings.Server.Add(_server);
|
||||
}
|
||||
|
||||
Configuration.Save();
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
Global.MainForm.InitServer();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
Netch/Forms/Server/Trojan.Designer.cs
generated
@@ -152,7 +152,6 @@
|
||||
this.Name = "Trojan";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "Trojan";
|
||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Trojan_FormClosing);
|
||||
this.Load += new System.EventHandler(this.Trojan_Load);
|
||||
this.ConfigurationGroupBox.ResumeLayout(false);
|
||||
this.ConfigurationGroupBox.PerformLayout();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -8,17 +7,13 @@ namespace Netch.Forms.Server
|
||||
{
|
||||
public partial class Trojan : Form
|
||||
{
|
||||
public int Index;
|
||||
private readonly Models.Server _server;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <param name="index">需要编辑的索引</param>
|
||||
public Trojan(int index = -1)
|
||||
public Trojan(Models.Server server = default)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Index = index;
|
||||
_server = server ?? new Models.Server();
|
||||
}
|
||||
|
||||
private void Trojan_Load(object sender, EventArgs e)
|
||||
@@ -29,32 +24,25 @@ namespace Netch.Forms.Server
|
||||
PasswordLabel.Text = i18N.Translate(PasswordLabel.Text);
|
||||
ControlButton.Text = i18N.Translate(ControlButton.Text);
|
||||
|
||||
if (Index != -1)
|
||||
{
|
||||
RemarkTextBox.Text = Global.Settings.Server[Index].Remark;
|
||||
AddressTextBox.Text = Global.Settings.Server[Index].Hostname;
|
||||
PortTextBox.Text = Global.Settings.Server[Index].Port.ToString();
|
||||
PasswordTextBox.Text = Global.Settings.Server[Index].Password;
|
||||
}
|
||||
}
|
||||
|
||||
private void Trojan_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
Global.MainForm.Show();
|
||||
RemarkTextBox.Text = _server.Remark;
|
||||
AddressTextBox.Text = _server.Hostname;
|
||||
PortTextBox.Text = _server.Port.ToString();
|
||||
PasswordTextBox.Text = _server.Password;
|
||||
}
|
||||
|
||||
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
|
||||
{
|
||||
var cbx = sender as ComboBox;
|
||||
if (cbx != null)
|
||||
if (sender is ComboBox cbx)
|
||||
{
|
||||
e.DrawBackground();
|
||||
|
||||
if (e.Index >= 0)
|
||||
{
|
||||
var sf = new StringFormat();
|
||||
sf.LineAlignment = StringAlignment.Center;
|
||||
sf.Alignment = StringAlignment.Center;
|
||||
var sf = new StringFormat
|
||||
{
|
||||
LineAlignment = StringAlignment.Center,
|
||||
Alignment = StringAlignment.Center
|
||||
};
|
||||
|
||||
var brush = new SolidBrush(cbx.ForeColor);
|
||||
|
||||
@@ -70,39 +58,26 @@ namespace Netch.Forms.Server
|
||||
|
||||
private void ControlButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!Regex.Match(PortTextBox.Text, "^[0-9]+$").Success)
|
||||
if (!int.TryParse(PortTextBox.Text, out var port))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Index == -1)
|
||||
|
||||
|
||||
_server.Remark = RemarkTextBox.Text;
|
||||
_server.Type = "Trojan";
|
||||
_server.Hostname = AddressTextBox.Text;
|
||||
_server.Port = port;
|
||||
_server.Password = PasswordTextBox.Text;
|
||||
_server.Country = null;
|
||||
|
||||
if (Global.Settings.Server.IndexOf(_server) == -1)
|
||||
{
|
||||
Global.Settings.Server.Add(new Models.Server
|
||||
{
|
||||
Remark = RemarkTextBox.Text,
|
||||
Type = "Trojan",
|
||||
Hostname = AddressTextBox.Text,
|
||||
Port = int.Parse(PortTextBox.Text),
|
||||
Password = PasswordTextBox.Text
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Global.Settings.Server[Index] = new Models.Server
|
||||
{
|
||||
Remark = RemarkTextBox.Text,
|
||||
Group = Global.Settings.Server[Index].Group,
|
||||
Type = "Trojan",
|
||||
Hostname = AddressTextBox.Text,
|
||||
Port = int.Parse(PortTextBox.Text),
|
||||
Password = PasswordTextBox.Text,
|
||||
Country = null
|
||||
};
|
||||
Global.Settings.Server.Add(_server);
|
||||
}
|
||||
|
||||
Configuration.Save();
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
Global.MainForm.InitServer();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
Netch/Forms/Server/Vmess.Designer.cs
generated
@@ -385,7 +385,6 @@
|
||||
this.Name = "VMess";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "VMess";
|
||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.VMess_FormClosing);
|
||||
this.Load += new System.EventHandler(this.VMess_Load);
|
||||
this.ConfigurationGroupBox.ResumeLayout(false);
|
||||
this.ConfigurationGroupBox.PerformLayout();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Utils;
|
||||
|
||||
@@ -8,27 +7,28 @@ namespace Netch.Forms.Server
|
||||
{
|
||||
public partial class VMess : Form
|
||||
{
|
||||
public int Index;
|
||||
private static Models.Server _server;
|
||||
|
||||
public VMess(int index = -1)
|
||||
public VMess(Models.Server server = default)
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
Index = index;
|
||||
_server = server ?? new Models.Server {EncryptMethod = Global.EncryptMethods.VMess[0]};
|
||||
}
|
||||
|
||||
private void ComboBox_DrawItem(object sender, DrawItemEventArgs e)
|
||||
{
|
||||
var cbx = sender as ComboBox;
|
||||
if (cbx != null)
|
||||
if (sender is ComboBox cbx)
|
||||
{
|
||||
e.DrawBackground();
|
||||
|
||||
if (e.Index >= 0)
|
||||
{
|
||||
var sf = new StringFormat();
|
||||
sf.LineAlignment = StringAlignment.Center;
|
||||
sf.Alignment = StringAlignment.Center;
|
||||
var sf = new StringFormat
|
||||
{
|
||||
LineAlignment = StringAlignment.Center,
|
||||
Alignment = StringAlignment.Center
|
||||
};
|
||||
|
||||
var brush = new SolidBrush(cbx.ForeColor);
|
||||
|
||||
@@ -44,6 +44,8 @@ namespace Netch.Forms.Server
|
||||
|
||||
private void VMess_Load(object sender, EventArgs e)
|
||||
{
|
||||
#region InitText
|
||||
|
||||
ConfigurationGroupBox.Text = i18N.Translate(ConfigurationGroupBox.Text);
|
||||
RemarkLabel.Text = i18N.Translate(RemarkLabel.Text);
|
||||
AddressLabel.Text = i18N.Translate(AddressLabel.Text);
|
||||
@@ -60,116 +62,65 @@ namespace Netch.Forms.Server
|
||||
UseMuxCheckBox.Text = i18N.Translate(UseMuxCheckBox.Text);
|
||||
ControlButton.Text = i18N.Translate(ControlButton.Text);
|
||||
|
||||
foreach (var encrypt in Global.EncryptMethods.VMess)
|
||||
{
|
||||
EncryptMethodComboBox.Items.Add(encrypt);
|
||||
}
|
||||
EncryptMethodComboBox.Items.AddRange(Global.EncryptMethods.VMess.ToArray());
|
||||
TransferProtocolComboBox.Items.AddRange(Global.TransferProtocols.ToArray());
|
||||
FakeTypeComboBox.Items.AddRange(Global.FakeTypes.ToArray());
|
||||
QUICSecurityComboBox.Items.AddRange(Global.EncryptMethods.VMessQUIC.ToArray());
|
||||
|
||||
foreach (var protocol in Global.TransferProtocols)
|
||||
{
|
||||
TransferProtocolComboBox.Items.Add(protocol);
|
||||
}
|
||||
#endregion
|
||||
|
||||
foreach (var fake in Global.FakeTypes)
|
||||
{
|
||||
FakeTypeComboBox.Items.Add(fake);
|
||||
}
|
||||
|
||||
foreach (var security in Global.EncryptMethods.VMessQUIC)
|
||||
{
|
||||
QUICSecurityComboBox.Items.Add(security);
|
||||
}
|
||||
|
||||
if (Index != -1)
|
||||
{
|
||||
RemarkTextBox.Text = Global.Settings.Server[Index].Remark;
|
||||
AddressTextBox.Text = Global.Settings.Server[Index].Hostname;
|
||||
PortTextBox.Text = Global.Settings.Server[Index].Port.ToString();
|
||||
UserIDTextBox.Text = Global.Settings.Server[Index].UserID;
|
||||
AlterIDTextBox.Text = Global.Settings.Server[Index].AlterID.ToString();
|
||||
EncryptMethodComboBox.SelectedIndex = Global.EncryptMethods.VMess.IndexOf(Global.Settings.Server[Index].EncryptMethod);
|
||||
TransferProtocolComboBox.SelectedIndex = Global.TransferProtocols.IndexOf(Global.Settings.Server[Index].TransferProtocol);
|
||||
FakeTypeComboBox.SelectedIndex = Global.FakeTypes.IndexOf(Global.Settings.Server[Index].FakeType);
|
||||
HostTextBox.Text = Global.Settings.Server[Index].Host;
|
||||
PathTextBox.Text = Global.Settings.Server[Index].Path;
|
||||
QUICSecurityComboBox.SelectedIndex = Global.EncryptMethods.VMessQUIC.IndexOf(Global.Settings.Server[Index].QUICSecure);
|
||||
QUICSecretTextBox.Text = Global.Settings.Server[Index].QUICSecret;
|
||||
TLSSecureCheckBox.Checked = Global.Settings.Server[Index].TLSSecure;
|
||||
UseMuxCheckBox.Checked = Global.Settings.Server[Index].UseMux;
|
||||
}
|
||||
else
|
||||
{
|
||||
EncryptMethodComboBox.SelectedIndex = 0;
|
||||
TransferProtocolComboBox.SelectedIndex = 0;
|
||||
FakeTypeComboBox.SelectedIndex = 0;
|
||||
QUICSecurityComboBox.SelectedIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void VMess_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
Global.MainForm.Show();
|
||||
RemarkTextBox.Text = _server.Remark;
|
||||
AddressTextBox.Text = _server.Hostname;
|
||||
PortTextBox.Text = _server.Port.ToString();
|
||||
UserIDTextBox.Text = _server.UserID;
|
||||
AlterIDTextBox.Text = _server.AlterID.ToString();
|
||||
EncryptMethodComboBox.SelectedIndex = Global.EncryptMethods.VMess.IndexOf(_server.EncryptMethod);
|
||||
TransferProtocolComboBox.SelectedIndex = Global.TransferProtocols.IndexOf(_server.TransferProtocol);
|
||||
FakeTypeComboBox.SelectedIndex = Global.FakeTypes.IndexOf(_server.FakeType);
|
||||
HostTextBox.Text = _server.Host;
|
||||
PathTextBox.Text = _server.Path;
|
||||
QUICSecurityComboBox.SelectedIndex = Global.EncryptMethods.VMessQUIC.IndexOf(_server.QUICSecure);
|
||||
QUICSecretTextBox.Text = _server.QUICSecret;
|
||||
TLSSecureCheckBox.Checked = _server.TLSSecure;
|
||||
UseMuxCheckBox.Checked = _server.UseMux;
|
||||
}
|
||||
|
||||
private void ControlButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!Regex.Match(PortTextBox.Text, "^[0-9]+$").Success)
|
||||
if (!int.TryParse(PortTextBox.Text, out var port))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (AlterIDTextBox.Text == "")
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Please fill in alterID"));
|
||||
return;
|
||||
}
|
||||
if (Index == -1)
|
||||
{
|
||||
Global.Settings.Server.Add(new Models.Server
|
||||
{
|
||||
Remark = RemarkTextBox.Text,
|
||||
Type = "VMess",
|
||||
Hostname = AddressTextBox.Text,
|
||||
Port = int.Parse(PortTextBox.Text),
|
||||
UserID = UserIDTextBox.Text,
|
||||
AlterID = int.Parse(AlterIDTextBox.Text),
|
||||
EncryptMethod = EncryptMethodComboBox.Text,
|
||||
TransferProtocol = TransferProtocolComboBox.Text,
|
||||
FakeType = FakeTypeComboBox.Text,
|
||||
Host = HostTextBox.Text,
|
||||
Path = PathTextBox.Text,
|
||||
QUICSecure = QUICSecurityComboBox.Text,
|
||||
QUICSecret = QUICSecretTextBox.Text,
|
||||
TLSSecure = TLSSecureCheckBox.Checked,
|
||||
UseMux = UseMuxCheckBox.Checked
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Global.Settings.Server[Index] = new Models.Server
|
||||
{
|
||||
Remark = RemarkTextBox.Text,
|
||||
Type = "VMess",
|
||||
Hostname = AddressTextBox.Text,
|
||||
Port = int.Parse(PortTextBox.Text),
|
||||
UserID = UserIDTextBox.Text,
|
||||
AlterID = int.Parse(AlterIDTextBox.Text),
|
||||
EncryptMethod = EncryptMethodComboBox.Text,
|
||||
TransferProtocol = TransferProtocolComboBox.Text,
|
||||
FakeType = FakeTypeComboBox.Text,
|
||||
Host = HostTextBox.Text,
|
||||
Path = PathTextBox.Text,
|
||||
QUICSecure = QUICSecurityComboBox.Text,
|
||||
QUICSecret = QUICSecretTextBox.Text,
|
||||
TLSSecure = TLSSecureCheckBox.Checked,
|
||||
UseMux = UseMuxCheckBox.Checked,
|
||||
Country = null
|
||||
};
|
||||
}
|
||||
|
||||
Configuration.Save();
|
||||
if (!int.TryParse(AlterIDTextBox.Text, out var alterId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_server.Remark = RemarkTextBox.Text;
|
||||
_server.Type = "VMess";
|
||||
_server.Hostname = AddressTextBox.Text;
|
||||
_server.Port = port;
|
||||
_server.UserID = UserIDTextBox.Text;
|
||||
_server.AlterID = alterId;
|
||||
_server.EncryptMethod = EncryptMethodComboBox.Text;
|
||||
_server.TransferProtocol = TransferProtocolComboBox.Text;
|
||||
_server.FakeType = FakeTypeComboBox.Text;
|
||||
_server.Host = HostTextBox.Text;
|
||||
_server.Path = PathTextBox.Text;
|
||||
_server.QUICSecure = QUICSecurityComboBox.Text;
|
||||
_server.QUICSecret = QUICSecretTextBox.Text;
|
||||
_server.TLSSecure = TLSSecureCheckBox.Checked;
|
||||
_server.UseMux = UseMuxCheckBox.Checked;
|
||||
_server.Country = null;
|
||||
|
||||
if (Global.Settings.Server.IndexOf(_server) == -1)
|
||||
{
|
||||
Global.Settings.Server.Add(_server);
|
||||
}
|
||||
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
Global.MainForm.InitServer();
|
||||
Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
57
Netch/Forms/SettingForm.Designer.cs
generated
@@ -61,11 +61,9 @@
|
||||
this.DetectionIntervalLabel = new System.Windows.Forms.Label();
|
||||
this.DetectionIntervalTextBox = new System.Windows.Forms.TextBox();
|
||||
this.TcpingAtStartedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.STUN_ServerPortTextBox = new System.Windows.Forms.TextBox();
|
||||
this.StunTextBoxSplitLabel = new System.Windows.Forms.Label();
|
||||
this.STUNServerLabel = new System.Windows.Forms.Label();
|
||||
this.RunAtStartupCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.STUN_ServerTextBox = new System.Windows.Forms.TextBox();
|
||||
this.STUN_ServerComboBox = new System.Windows.Forms.ComboBox();
|
||||
this.MinimizeWhenStartedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.ProfileCountLabel = new System.Windows.Forms.Label();
|
||||
this.ProfileCountTextBox = new System.Windows.Forms.TextBox();
|
||||
@@ -74,6 +72,7 @@
|
||||
this.StartWhenOpenedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.StopWhenExitedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.ExitWhenClosedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.PortGroupBox.SuspendLayout();
|
||||
this.TUNTAPGroupBox.SuspendLayout();
|
||||
this.BehaviorGroupBox.SuspendLayout();
|
||||
@@ -300,6 +299,7 @@
|
||||
//
|
||||
// BehaviorGroupBox
|
||||
//
|
||||
this.BehaviorGroupBox.Controls.Add(this.UpdateSubscribeatWhenOpenedCheckBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.LanguageLabel);
|
||||
this.BehaviorGroupBox.Controls.Add(this.LanguageComboBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.ModifySystemDNSCheckBox);
|
||||
@@ -309,11 +309,9 @@
|
||||
this.BehaviorGroupBox.Controls.Add(this.DetectionIntervalLabel);
|
||||
this.BehaviorGroupBox.Controls.Add(this.DetectionIntervalTextBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.TcpingAtStartedCheckBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.STUN_ServerPortTextBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.StunTextBoxSplitLabel);
|
||||
this.BehaviorGroupBox.Controls.Add(this.STUNServerLabel);
|
||||
this.BehaviorGroupBox.Controls.Add(this.RunAtStartupCheckBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.STUN_ServerTextBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.STUN_ServerComboBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.MinimizeWhenStartedCheckBox);
|
||||
this.BehaviorGroupBox.Controls.Add(this.ProfileCountLabel);
|
||||
this.BehaviorGroupBox.Controls.Add(this.ProfileCountTextBox);
|
||||
@@ -412,23 +410,6 @@
|
||||
this.TcpingAtStartedCheckBox.Text = "Delay test after start";
|
||||
this.TcpingAtStartedCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// STUN_ServerPortTextBox
|
||||
//
|
||||
this.STUN_ServerPortTextBox.Location = new System.Drawing.Point(366, 241);
|
||||
this.STUN_ServerPortTextBox.Name = "STUN_ServerPortTextBox";
|
||||
this.STUN_ServerPortTextBox.Size = new System.Drawing.Size(68, 23);
|
||||
this.STUN_ServerPortTextBox.TabIndex = 8;
|
||||
this.STUN_ServerPortTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
//
|
||||
// StunTextBoxSplitLabel
|
||||
//
|
||||
this.StunTextBoxSplitLabel.AutoSize = true;
|
||||
this.StunTextBoxSplitLabel.Location = new System.Drawing.Point(353, 244);
|
||||
this.StunTextBoxSplitLabel.Name = "StunTextBoxSplitLabel";
|
||||
this.StunTextBoxSplitLabel.Size = new System.Drawing.Size(11, 17);
|
||||
this.StunTextBoxSplitLabel.TabIndex = 12;
|
||||
this.StunTextBoxSplitLabel.Text = ":";
|
||||
//
|
||||
// STUNServerLabel
|
||||
//
|
||||
this.STUNServerLabel.AutoSize = true;
|
||||
@@ -448,13 +429,13 @@
|
||||
this.RunAtStartupCheckBox.Text = "Run at startup";
|
||||
this.RunAtStartupCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// STUN_ServerTextBox
|
||||
// STUN_ServerComboBox
|
||||
//
|
||||
this.STUN_ServerTextBox.Location = new System.Drawing.Point(120, 241);
|
||||
this.STUN_ServerTextBox.Name = "STUN_ServerTextBox";
|
||||
this.STUN_ServerTextBox.Size = new System.Drawing.Size(233, 23);
|
||||
this.STUN_ServerTextBox.TabIndex = 11;
|
||||
this.STUN_ServerTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
|
||||
this.STUN_ServerComboBox.AutoCompleteMode = System.Windows.Forms.AutoCompleteMode.Suggest;
|
||||
this.STUN_ServerComboBox.Location = new System.Drawing.Point(120, 241);
|
||||
this.STUN_ServerComboBox.Name = "STUN_ServerComboBox";
|
||||
this.STUN_ServerComboBox.Size = new System.Drawing.Size(314, 25);
|
||||
this.STUN_ServerComboBox.TabIndex = 11;
|
||||
//
|
||||
// MinimizeWhenStartedCheckBox
|
||||
//
|
||||
@@ -538,6 +519,17 @@
|
||||
this.ExitWhenClosedCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||
this.ExitWhenClosedCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// UpdateSubscribeatWhenOpenedCheckBox
|
||||
//
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.AutoSize = true;
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.Location = new System.Drawing.Point(206, 129);
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.Name = "UpdateSubscribeatWhenOpenedCheckBox";
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.Size = new System.Drawing.Size(224, 21);
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.TabIndex = 24;
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.Text = "Update subscribeat when opened";
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight;
|
||||
this.UpdateSubscribeatWhenOpenedCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// SettingForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
|
||||
@@ -556,7 +548,6 @@
|
||||
this.Name = "SettingForm";
|
||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
|
||||
this.Text = "Settings";
|
||||
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.SettingForm_FormClosing);
|
||||
this.Load += new System.EventHandler(this.SettingForm_Load);
|
||||
this.PortGroupBox.ResumeLayout(false);
|
||||
this.PortGroupBox.PerformLayout();
|
||||
@@ -565,7 +556,6 @@
|
||||
this.BehaviorGroupBox.ResumeLayout(false);
|
||||
this.BehaviorGroupBox.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -597,10 +587,8 @@
|
||||
private System.Windows.Forms.TextBox ProfileCountTextBox;
|
||||
private System.Windows.Forms.CheckBox MinimizeWhenStartedCheckBox;
|
||||
private System.Windows.Forms.CheckBox RunAtStartupCheckBox;
|
||||
private System.Windows.Forms.Label StunTextBoxSplitLabel;
|
||||
private System.Windows.Forms.Label STUNServerLabel;
|
||||
private System.Windows.Forms.TextBox STUN_ServerTextBox;
|
||||
private System.Windows.Forms.TextBox STUN_ServerPortTextBox;
|
||||
private System.Windows.Forms.ComboBox STUN_ServerComboBox;
|
||||
private System.Windows.Forms.CheckBox ProxyDNSCheckBox;
|
||||
private System.Windows.Forms.TextBox DetectionIntervalTextBox;
|
||||
private System.Windows.Forms.CheckBox TcpingAtStartedCheckBox;
|
||||
@@ -615,5 +603,6 @@
|
||||
private System.Windows.Forms.ComboBox LanguageComboBox;
|
||||
private System.Windows.Forms.CheckBox ModifySystemDNSCheckBox;
|
||||
private System.Windows.Forms.CheckBox CheckBetaUpdateCheckBox;
|
||||
private System.Windows.Forms.CheckBox UpdateSubscribeatWhenOpenedCheckBox;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Models;
|
||||
@@ -107,15 +109,15 @@ namespace Netch.Forms
|
||||
BootShadowsocksFromDLLCheckBox.Checked = Global.Settings.BootShadowsocksFromDLL;
|
||||
ModifySystemDNSCheckBox.Checked = Global.Settings.ModifySystemDNS;
|
||||
CheckBetaUpdateCheckBox.Checked = Global.Settings.CheckBetaUpdate;
|
||||
UpdateSubscribeatWhenOpenedCheckBox.Checked = Global.Settings.UpdateSubscribeatWhenOpened;
|
||||
|
||||
ProfileCountTextBox.Text = Global.Settings.ProfileCount.ToString();
|
||||
TcpingAtStartedCheckBox.Checked = Global.Settings.StartedTcping;
|
||||
DetectionIntervalTextBox.Text = Global.Settings.StartedTcping_Interval.ToString();
|
||||
STUN_ServerTextBox.Text = Global.Settings.STUN_Server;
|
||||
STUN_ServerPortTextBox.Text = Global.Settings.STUN_Server_Port.ToString();
|
||||
AclAddrTextBox.Text = Global.Settings.ACL;
|
||||
LanguageComboBox.Items.AddRange(i18N.GetTranslateList().ToArray());
|
||||
LanguageComboBox.SelectedItem = Global.Settings.Language;
|
||||
InitSTUN();
|
||||
}
|
||||
|
||||
private void InitText()
|
||||
@@ -141,32 +143,42 @@ namespace Netch.Forms
|
||||
StartWhenOpenedCheckBox.Text = i18N.Translate(StartWhenOpenedCheckBox.Text);
|
||||
MinimizeWhenStartedCheckBox.Text = i18N.Translate(MinimizeWhenStartedCheckBox.Text);
|
||||
RunAtStartupCheckBox.Text = i18N.Translate(RunAtStartupCheckBox.Text);
|
||||
UpdateSubscribeatWhenOpenedCheckBox.Text = i18N.Translate(UpdateSubscribeatWhenOpenedCheckBox.Text);
|
||||
CheckUpdateWhenOpenedCheckBox.Text = i18N.Translate(CheckUpdateWhenOpenedCheckBox.Text);
|
||||
ProfileCountLabel.Text = i18N.Translate(ProfileCountLabel.Text);
|
||||
TcpingAtStartedCheckBox.Text = i18N.Translate(TcpingAtStartedCheckBox.Text);
|
||||
DetectionIntervalLabel.Text = i18N.Translate(DetectionIntervalLabel.Text);
|
||||
STUNServerLabel.Text = i18N.Translate(STUNServerLabel.Text);
|
||||
StunTextBoxSplitLabel.Text = i18N.Translate(StunTextBoxSplitLabel.Text);
|
||||
AclLabel.Text = i18N.Translate(AclLabel.Text);
|
||||
LanguageLabel.Text = i18N.Translate(LanguageLabel.Text);
|
||||
}
|
||||
|
||||
private void InitSTUN()
|
||||
{
|
||||
try
|
||||
{
|
||||
var stuns = File.ReadLines("bin\\stun.txt");
|
||||
STUN_ServerComboBox.Items.AddRange(stuns.ToArray());
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
STUN_ServerComboBox.Text = $"{Global.Settings.STUN_Server}:{Global.Settings.STUN_Server_Port}";
|
||||
}
|
||||
|
||||
private void SettingForm_Load(object sender, EventArgs e)
|
||||
{
|
||||
InitText();
|
||||
|
||||
InitValue();
|
||||
}
|
||||
|
||||
private void SettingForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
Global.MainForm.Show();
|
||||
}
|
||||
|
||||
private void GlobalBypassIPsButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
new GlobalBypassIPForm().Show();
|
||||
Hide();
|
||||
new GlobalBypassIPForm().ShowDialog();
|
||||
Show();
|
||||
}
|
||||
|
||||
private void ControlButton_Click(object sender, EventArgs e)
|
||||
@@ -176,6 +188,7 @@ namespace Netch.Forms
|
||||
Global.Settings.StartWhenOpened = StartWhenOpenedCheckBox.Checked;
|
||||
Global.Settings.CheckUpdateWhenOpened = CheckUpdateWhenOpenedCheckBox.Checked;
|
||||
Global.Settings.CheckBetaUpdate = CheckBetaUpdateCheckBox.Checked;
|
||||
Global.Settings.UpdateSubscribeatWhenOpened = UpdateSubscribeatWhenOpenedCheckBox.Checked;
|
||||
Global.Settings.MinimizeWhenStarted = MinimizeWhenStartedCheckBox.Checked;
|
||||
Global.Settings.RunAtStartup = RunAtStartupCheckBox.Checked;
|
||||
Global.Settings.BootShadowsocksFromDLL = BootShadowsocksFromDLLCheckBox.Checked;
|
||||
@@ -185,8 +198,10 @@ namespace Netch.Forms
|
||||
var scheduler = new TaskSchedulerClass();
|
||||
scheduler.Connect();
|
||||
var folder = scheduler.GetFolder("\\");
|
||||
|
||||
var taskIsExists = false;
|
||||
try
|
||||
|
||||
{
|
||||
folder.GetTask("Netch Startup");
|
||||
taskIsExists = true;
|
||||
@@ -215,8 +230,10 @@ namespace Netch.Forms
|
||||
task.Settings.DisallowStartIfOnBatteries = false;
|
||||
task.Settings.RunOnlyIfIdle = false;
|
||||
|
||||
folder.RegisterTaskDefinition("Netch Startup", task, (int) _TASK_CREATION.TASK_CREATE, null, null, _TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN, "");
|
||||
folder.RegisterTaskDefinition("Netch Startup", task, (int) _TASK_CREATION.TASK_CREATE, null, null,
|
||||
_TASK_LOGON_TYPE.TASK_LOGON_INTERACTIVE_TOKEN, "");
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (taskIsExists)
|
||||
@@ -230,9 +247,7 @@ namespace Netch.Forms
|
||||
return;
|
||||
if (!CheckPortText("RedirectorTCP", ref RedirectorTextBox, ref Global.Settings.RedirectorTCPPort, PortType.TCP))
|
||||
return;
|
||||
|
||||
Global.Settings.LocalAddress = AllowDevicesCheckBox.Checked ? "0.0.0.0" : "127.0.0.1";
|
||||
|
||||
try
|
||||
{
|
||||
var Address = IPAddress.Parse(TUNTAPAddressTextBox.Text);
|
||||
@@ -290,10 +305,13 @@ namespace Netch.Forms
|
||||
|
||||
try
|
||||
{
|
||||
var STUN_Server = STUN_ServerTextBox.Text;
|
||||
var stun = STUN_ServerComboBox.Text.Split(':');
|
||||
var STUN_Server = stun[0];
|
||||
Global.Settings.STUN_Server = STUN_Server;
|
||||
|
||||
var STUN_ServerPort = int.Parse(STUN_ServerPortTextBox.Text);
|
||||
var STUN_ServerPort = 3478;
|
||||
if (stun.Length > 1)
|
||||
STUN_ServerPort = int.Parse(stun[1]);
|
||||
|
||||
if (STUN_ServerPort > 0)
|
||||
{
|
||||
@@ -336,11 +354,9 @@ namespace Netch.Forms
|
||||
}
|
||||
|
||||
Global.Settings.ACL = AclAddrTextBox.Text;
|
||||
|
||||
Global.Settings.TUNTAP.Address = TUNTAPAddressTextBox.Text;
|
||||
Global.Settings.TUNTAP.Netmask = TUNTAPNetmaskTextBox.Text;
|
||||
Global.Settings.TUNTAP.Gateway = TUNTAPGatewayTextBox.Text;
|
||||
|
||||
Global.Settings.TUNTAP.DNS.Clear();
|
||||
foreach (var ip in TUNTAPDNSTextBox.Text.Split(','))
|
||||
{
|
||||
@@ -350,9 +366,7 @@ namespace Netch.Forms
|
||||
Global.Settings.TUNTAP.UseCustomDNS = UseCustomDNSCheckBox.Checked;
|
||||
Global.Settings.TUNTAP.ProxyDNS = ProxyDNSCheckBox.Checked;
|
||||
Global.Settings.TUNTAP.UseFakeDNS = UseFakeDNSCheckBox.Checked;
|
||||
|
||||
Global.Settings.ModifySystemDNS = ModifySystemDNSCheckBox.Checked;
|
||||
|
||||
Configuration.Save();
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
Close();
|
||||
|
||||
108
Netch/Forms/SubscribeForm.Designer.cs
generated
@@ -32,37 +32,43 @@
|
||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SubscribeForm));
|
||||
this.AddSubscriptionBox = new System.Windows.Forms.GroupBox();
|
||||
this.UserAgentTextBox = new System.Windows.Forms.TextBox();
|
||||
this.ClearButton = new System.Windows.Forms.Button();
|
||||
this.AddButton = new System.Windows.Forms.Button();
|
||||
this.UserAgentLabel = new System.Windows.Forms.Label();
|
||||
this.LinkTextBox = new System.Windows.Forms.TextBox();
|
||||
this.LinkLabel = new System.Windows.Forms.Label();
|
||||
this.RemarkTextBox = new System.Windows.Forms.TextBox();
|
||||
this.RemarkLabel = new System.Windows.Forms.Label();
|
||||
this.ControlButton = new System.Windows.Forms.Button();
|
||||
this.SubscribeLinkListView = new System.Windows.Forms.ListView();
|
||||
this.RemarkColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.LinkColumnHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.UserAgentHeader = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader()));
|
||||
this.RemarkColumnHeader = new System.Windows.Forms.ColumnHeader();
|
||||
this.LinkColumnHeader = new System.Windows.Forms.ColumnHeader();
|
||||
this.UserAgentHeader = new System.Windows.Forms.ColumnHeader();
|
||||
this.pContextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
|
||||
this.DeleteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.CopyLinkToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
|
||||
this.UseSelectedServerCheckBox = new System.Windows.Forms.CheckBox();
|
||||
this.MainTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.ControlsPanel = new System.Windows.Forms.Panel();
|
||||
this.AddSubscriptionBox.SuspendLayout();
|
||||
this.pContextMenuStrip.SuspendLayout();
|
||||
this.MainTableLayoutPanel.SuspendLayout();
|
||||
this.ControlsPanel.SuspendLayout();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// AddSubscriptionBox
|
||||
//
|
||||
this.AddSubscriptionBox.Controls.Add(this.UserAgentTextBox);
|
||||
this.AddSubscriptionBox.Controls.Add(this.ClearButton);
|
||||
this.AddSubscriptionBox.Controls.Add(this.AddButton);
|
||||
this.AddSubscriptionBox.Controls.Add(this.UserAgentLabel);
|
||||
this.AddSubscriptionBox.Controls.Add(this.LinkTextBox);
|
||||
this.AddSubscriptionBox.Controls.Add(this.LinkLabel);
|
||||
this.AddSubscriptionBox.Controls.Add(this.RemarkTextBox);
|
||||
this.AddSubscriptionBox.Controls.Add(this.RemarkLabel);
|
||||
this.AddSubscriptionBox.Location = new System.Drawing.Point(12, 226);
|
||||
this.AddSubscriptionBox.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.AddSubscriptionBox.Location = new System.Drawing.Point(8, 214);
|
||||
this.AddSubscriptionBox.Name = "AddSubscriptionBox";
|
||||
this.AddSubscriptionBox.Size = new System.Drawing.Size(660, 135);
|
||||
this.AddSubscriptionBox.Size = new System.Drawing.Size(668, 141);
|
||||
this.AddSubscriptionBox.TabIndex = 1;
|
||||
this.AddSubscriptionBox.TabStop = false;
|
||||
//
|
||||
@@ -73,6 +79,16 @@
|
||||
this.UserAgentTextBox.Size = new System.Drawing.Size(545, 23);
|
||||
this.UserAgentTextBox.TabIndex = 6;
|
||||
//
|
||||
// ClearButton
|
||||
//
|
||||
this.ClearButton.Location = new System.Drawing.Point(477, 103);
|
||||
this.ClearButton.Name = "ClearButton";
|
||||
this.ClearButton.Size = new System.Drawing.Size(58, 26);
|
||||
this.ClearButton.TabIndex = 7;
|
||||
this.ClearButton.Text = "Clear";
|
||||
this.ClearButton.UseVisualStyleBackColor = true;
|
||||
this.ClearButton.Click += new System.EventHandler(this.ClearButton_Click);
|
||||
//
|
||||
// AddButton
|
||||
//
|
||||
this.AddButton.Location = new System.Drawing.Point(541, 103);
|
||||
@@ -98,6 +114,7 @@
|
||||
this.LinkTextBox.Name = "LinkTextBox";
|
||||
this.LinkTextBox.Size = new System.Drawing.Size(545, 23);
|
||||
this.LinkTextBox.TabIndex = 4;
|
||||
this.LinkTextBox.TextChanged += new System.EventHandler(this.ListTextBox_TextChanged);
|
||||
//
|
||||
// LinkLabel
|
||||
//
|
||||
@@ -124,33 +141,21 @@
|
||||
this.RemarkLabel.TabIndex = 1;
|
||||
this.RemarkLabel.Text = "Remark";
|
||||
//
|
||||
// ControlButton
|
||||
//
|
||||
this.ControlButton.Location = new System.Drawing.Point(597, 396);
|
||||
this.ControlButton.Name = "ControlButton";
|
||||
this.ControlButton.Size = new System.Drawing.Size(75, 23);
|
||||
this.ControlButton.TabIndex = 8;
|
||||
this.ControlButton.Text = "Save";
|
||||
this.ControlButton.UseVisualStyleBackColor = true;
|
||||
this.ControlButton.Click += new System.EventHandler(this.ControlButton_Click);
|
||||
//
|
||||
// SubscribeLinkListView
|
||||
//
|
||||
this.SubscribeLinkListView.AllowColumnReorder = true;
|
||||
this.SubscribeLinkListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
|
||||
this.RemarkColumnHeader,
|
||||
this.LinkColumnHeader,
|
||||
this.UserAgentHeader});
|
||||
this.SubscribeLinkListView.ContextMenuStrip = this.pContextMenuStrip;
|
||||
this.SubscribeLinkListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {this.RemarkColumnHeader, this.LinkColumnHeader, this.UserAgentHeader});
|
||||
this.SubscribeLinkListView.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.SubscribeLinkListView.FullRowSelect = true;
|
||||
this.SubscribeLinkListView.HideSelection = false;
|
||||
this.SubscribeLinkListView.Location = new System.Drawing.Point(12, 12);
|
||||
this.SubscribeLinkListView.Location = new System.Drawing.Point(8, 8);
|
||||
this.SubscribeLinkListView.Name = "SubscribeLinkListView";
|
||||
this.SubscribeLinkListView.Size = new System.Drawing.Size(660, 208);
|
||||
this.SubscribeLinkListView.Size = new System.Drawing.Size(668, 200);
|
||||
this.SubscribeLinkListView.TabIndex = 0;
|
||||
this.SubscribeLinkListView.UseCompatibleStateImageBehavior = false;
|
||||
this.SubscribeLinkListView.View = System.Windows.Forms.View.Details;
|
||||
this.SubscribeLinkListView.SelectedIndexChanged += new System.EventHandler(this.SubscribeLinkListView_SelectedIndexChanged);
|
||||
this.SubscribeLinkListView.MouseUp += new System.Windows.Forms.MouseEventHandler(this.SubscribeLinkListView_MouseUp);
|
||||
//
|
||||
// RemarkColumnHeader
|
||||
//
|
||||
@@ -169,9 +174,7 @@
|
||||
//
|
||||
// pContextMenuStrip
|
||||
//
|
||||
this.pContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
|
||||
this.DeleteToolStripMenuItem,
|
||||
this.CopyLinkToolStripMenuItem});
|
||||
this.pContextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {this.DeleteToolStripMenuItem, this.CopyLinkToolStripMenuItem});
|
||||
this.pContextMenuStrip.Name = "pContextMenuStrip";
|
||||
this.pContextMenuStrip.Size = new System.Drawing.Size(130, 48);
|
||||
//
|
||||
@@ -192,25 +195,50 @@
|
||||
// UseSelectedServerCheckBox
|
||||
//
|
||||
this.UseSelectedServerCheckBox.AutoSize = true;
|
||||
this.UseSelectedServerCheckBox.Location = new System.Drawing.Point(12, 396);
|
||||
this.UseSelectedServerCheckBox.Location = new System.Drawing.Point(3, 4);
|
||||
this.UseSelectedServerCheckBox.Name = "UseSelectedServerCheckBox";
|
||||
this.UseSelectedServerCheckBox.Size = new System.Drawing.Size(285, 21);
|
||||
this.UseSelectedServerCheckBox.TabIndex = 9;
|
||||
this.UseSelectedServerCheckBox.Text = "Use Selected Server To Update Subscription";
|
||||
this.UseSelectedServerCheckBox.UseVisualStyleBackColor = true;
|
||||
//
|
||||
// MainTableLayoutPanel
|
||||
//
|
||||
this.MainTableLayoutPanel.ColumnCount = 1;
|
||||
this.MainTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
|
||||
this.MainTableLayoutPanel.Controls.Add(this.SubscribeLinkListView, 0, 0);
|
||||
this.MainTableLayoutPanel.Controls.Add(this.AddSubscriptionBox, 0, 1);
|
||||
this.MainTableLayoutPanel.Controls.Add(this.ControlsPanel, 0, 2);
|
||||
this.MainTableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.MainTableLayoutPanel.Location = new System.Drawing.Point(0, 0);
|
||||
this.MainTableLayoutPanel.Name = "MainTableLayoutPanel";
|
||||
this.MainTableLayoutPanel.Padding = new System.Windows.Forms.Padding(5);
|
||||
this.MainTableLayoutPanel.RowCount = 3;
|
||||
this.MainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 58.35777F));
|
||||
this.MainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 41.64223F));
|
||||
this.MainTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27F));
|
||||
this.MainTableLayoutPanel.Size = new System.Drawing.Size(684, 391);
|
||||
this.MainTableLayoutPanel.TabIndex = 11;
|
||||
//
|
||||
// ControlsPanel
|
||||
//
|
||||
this.ControlsPanel.Controls.Add(this.UseSelectedServerCheckBox);
|
||||
this.ControlsPanel.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.ControlsPanel.Location = new System.Drawing.Point(5, 358);
|
||||
this.ControlsPanel.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.ControlsPanel.Name = "ControlsPanel";
|
||||
this.ControlsPanel.Size = new System.Drawing.Size(674, 28);
|
||||
this.ControlsPanel.TabIndex = 2;
|
||||
//
|
||||
// SubscribeForm
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
|
||||
this.ClientSize = new System.Drawing.Size(684, 431);
|
||||
this.Controls.Add(this.UseSelectedServerCheckBox);
|
||||
this.Controls.Add(this.SubscribeLinkListView);
|
||||
this.Controls.Add(this.ControlButton);
|
||||
this.Controls.Add(this.AddSubscriptionBox);
|
||||
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
|
||||
this.ClientSize = new System.Drawing.Size(684, 391);
|
||||
this.Controls.Add(this.MainTableLayoutPanel);
|
||||
this.Font = new System.Drawing.Font("微软雅黑", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte) (134)));
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
|
||||
this.Icon = ((System.Drawing.Icon) (resources.GetObject("$this.Icon")));
|
||||
this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);
|
||||
this.MaximizeBox = false;
|
||||
this.Name = "SubscribeForm";
|
||||
@@ -221,19 +249,21 @@
|
||||
this.AddSubscriptionBox.ResumeLayout(false);
|
||||
this.AddSubscriptionBox.PerformLayout();
|
||||
this.pContextMenuStrip.ResumeLayout(false);
|
||||
this.MainTableLayoutPanel.ResumeLayout(false);
|
||||
this.ControlsPanel.ResumeLayout(false);
|
||||
this.ControlsPanel.PerformLayout();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
private System.Windows.Forms.Panel ControlsPanel;
|
||||
private System.Windows.Forms.TableLayoutPanel MainTableLayoutPanel;
|
||||
private System.Windows.Forms.Button ClearButton;
|
||||
private System.Windows.Forms.GroupBox AddSubscriptionBox;
|
||||
private System.Windows.Forms.Label RemarkLabel;
|
||||
private System.Windows.Forms.TextBox LinkTextBox;
|
||||
private System.Windows.Forms.Label LinkLabel;
|
||||
private System.Windows.Forms.TextBox RemarkTextBox;
|
||||
private System.Windows.Forms.Button AddButton;
|
||||
private System.Windows.Forms.Button ControlButton;
|
||||
private System.Windows.Forms.ListView SubscribeLinkListView;
|
||||
private System.Windows.Forms.ColumnHeader RemarkColumnHeader;
|
||||
private System.Windows.Forms.ColumnHeader LinkColumnHeader;
|
||||
@@ -244,5 +274,7 @@
|
||||
private System.Windows.Forms.TextBox UserAgentTextBox;
|
||||
private System.Windows.Forms.ColumnHeader UserAgentHeader;
|
||||
private System.Windows.Forms.CheckBox UseSelectedServerCheckBox;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
using Netch.Models;
|
||||
using Netch.Utils;
|
||||
@@ -8,6 +8,8 @@ namespace Netch.Forms
|
||||
{
|
||||
public partial class SubscribeForm : Form
|
||||
{
|
||||
private int _editingIndex = -1;
|
||||
|
||||
public SubscribeForm()
|
||||
{
|
||||
InitializeComponent();
|
||||
@@ -19,20 +21,12 @@ namespace Netch.Forms
|
||||
|
||||
foreach (var item in Global.Settings.SubscribeLink)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(item.UserAgent))
|
||||
SubscribeLinkListView.Items.Add(new ListViewItem(new[]
|
||||
{
|
||||
SubscribeLinkListView.Items.Add(new ListViewItem(new[] {
|
||||
item.Remark,
|
||||
item.Link,
|
||||
item.UserAgent}));
|
||||
}
|
||||
else
|
||||
{
|
||||
SubscribeLinkListView.Items.Add(new ListViewItem(new[] {
|
||||
item.Remark,
|
||||
item.Link,
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36"}));
|
||||
}
|
||||
!string.IsNullOrEmpty(item.UserAgent) ? item.UserAgent : WebUtil.DefaultUserAgent
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,10 +40,10 @@ namespace Netch.Forms
|
||||
CopyLinkToolStripMenuItem.Text = i18N.Translate(CopyLinkToolStripMenuItem.Text);
|
||||
RemarkLabel.Text = i18N.Translate(RemarkLabel.Text);
|
||||
LinkLabel.Text = i18N.Translate(LinkLabel.Text);
|
||||
ClearButton.Text = i18N.Translate(ClearButton.Text);
|
||||
AddButton.Text = i18N.Translate(AddButton.Text);
|
||||
ControlButton.Text = i18N.Translate(ControlButton.Text);
|
||||
|
||||
UserAgentTextBox.Text = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36";
|
||||
ResetEditingGroup();
|
||||
|
||||
if (Global.Settings.Server.Count > 0)
|
||||
{
|
||||
@@ -67,8 +61,10 @@ namespace Netch.Forms
|
||||
|
||||
private void SubscribeForm_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
Global.MainForm.Show();
|
||||
Configuration.Save();
|
||||
Global.Settings.UseProxyToUpdateSubscription = UseSelectedServerCheckBox.Checked;
|
||||
}
|
||||
|
||||
private void CopyLinkToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (SubscribeLinkListView.SelectedItems.Count > 0)
|
||||
@@ -81,142 +77,208 @@ namespace Netch.Forms
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DeleteToolStripMenuItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (MessageBoxX.Show(i18N.Translate("Delete or not ? Will clean up the corresponding group of items in the server list"), confirm: true) == DialogResult.OK)
|
||||
{
|
||||
if (SubscribeLinkListView.SelectedItems.Count > 0)
|
||||
{
|
||||
DeleteSubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
public void DeleteSubscribe()
|
||||
{
|
||||
if (SubscribeLinkListView.SelectedItems.Count > 0)
|
||||
{
|
||||
for (var i = SubscribeLinkListView.SelectedItems.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var item = SubscribeLinkListView.SelectedItems[i];
|
||||
var link = Global.Settings.SubscribeLink[item.Index];
|
||||
|
||||
var list = new List<Models.Server>();
|
||||
foreach (var server in Global.Settings.Server)
|
||||
for (var i = SubscribeLinkListView.SelectedItems.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (server.Group != link.Remark)
|
||||
{
|
||||
list.Add(server);
|
||||
}
|
||||
var item = SubscribeLinkListView.SelectedItems[i];
|
||||
|
||||
DeleteServersInGroup(item.SubItems[0].Text);
|
||||
Global.Settings.SubscribeLink.RemoveAt(item.Index);
|
||||
SubscribeLinkListView.Items.Remove(item);
|
||||
ResetEditingGroup();
|
||||
}
|
||||
|
||||
Global.Settings.Server = list;
|
||||
Global.Settings.SubscribeLink.RemoveAt(item.Index);
|
||||
SubscribeLinkListView.Items.Remove(item);
|
||||
|
||||
Global.MainForm.InitServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(RemarkTextBox.Text))
|
||||
if (string.IsNullOrWhiteSpace(RemarkTextBox.Text))
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(LinkTextBox.Text))
|
||||
MessageBoxX.Show(i18N.Translate("Remark can not be empty"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(LinkTextBox.Text))
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Link can not be empty"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!LinkTextBox.Text.StartsWith("HTTP://", StringComparison.OrdinalIgnoreCase) && !LinkTextBox.Text.StartsWith("HTTPS://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Links must start with http:// or https://"));
|
||||
return;
|
||||
}
|
||||
|
||||
// 备注重复的订阅项
|
||||
var duplicateRemarkItems = Global.Settings.SubscribeLink.Where(link => link.Remark.Equals(RemarkLabel.Text));
|
||||
|
||||
// 链接重复的订阅项
|
||||
SubscribeLink duplicateLinkItem = null;
|
||||
try
|
||||
{
|
||||
duplicateLinkItem = Global.Settings.SubscribeLink.First(link => link.Link.Equals(LinkTextBox.Text));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
if (duplicateRemarkItems.Any())
|
||||
{
|
||||
MessageBoxX.Show("Remark Name Duplicate!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (duplicateLinkItem != null)
|
||||
{
|
||||
if (duplicateLinkItem.Remark != RemarkTextBox.Text)
|
||||
{
|
||||
if (LinkTextBox.Text.StartsWith("HTTP://", StringComparison.OrdinalIgnoreCase) || LinkTextBox.Text.StartsWith("HTTPS://", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
//是否为新增订阅
|
||||
var saveFlag = true;
|
||||
Global.Settings.SubscribeLink.ForEach(subitem =>
|
||||
{
|
||||
if (subitem.Link.Equals(LinkTextBox.Text))
|
||||
{
|
||||
if (!subitem.Remark.Equals(RemarkTextBox.Text))
|
||||
{
|
||||
//修改了订阅备注,修改旧订阅服务器
|
||||
Global.Settings.Server.ForEach(serverItem =>
|
||||
{
|
||||
try
|
||||
{
|
||||
//当前服务器组群组为订阅群组时批量修改备注
|
||||
if (serverItem.Group == subitem.Remark) {
|
||||
RenameServersGroup(duplicateLinkItem.Remark, RemarkTextBox.Text);
|
||||
}
|
||||
|
||||
//serverItem.Group OldGroupRemark
|
||||
//RemarkTextBox.Text NewGroupRemark
|
||||
serverItem.Group = RemarkTextBox.Text;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
});
|
||||
|
||||
subitem.Remark = RemarkTextBox.Text;
|
||||
Global.MainForm.InitServer();
|
||||
}
|
||||
|
||||
subitem.UserAgent = UserAgentTextBox.Text;
|
||||
saveFlag = false;
|
||||
|
||||
Configuration.Save();
|
||||
Global.Settings.UseProxyToUpdateSubscription = UseSelectedServerCheckBox.Checked;
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
}
|
||||
});
|
||||
if (saveFlag)
|
||||
{
|
||||
Global.Settings.SubscribeLink.Add(new SubscribeLink
|
||||
{
|
||||
Remark = RemarkTextBox.Text,
|
||||
Link = LinkTextBox.Text,
|
||||
UserAgent = UserAgentTextBox.Text
|
||||
});
|
||||
}
|
||||
|
||||
RemarkTextBox.Text = string.Empty;
|
||||
LinkTextBox.Text = string.Empty;
|
||||
UserAgentTextBox.Text = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36";
|
||||
|
||||
InitSubscribeLink();
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Links must start with http:// or https://"));
|
||||
}
|
||||
duplicateLinkItem.Remark = RemarkTextBox.Text;
|
||||
duplicateLinkItem.UserAgent = UserAgentTextBox.Text;
|
||||
}
|
||||
else if (_editingIndex != -1)
|
||||
{
|
||||
// 只修改备注/未修改被上面处理
|
||||
var target = Global.Settings.SubscribeLink[_editingIndex];
|
||||
if (MessageBox.Show(i18N.Translate("Delete the corresponding group of items in the server list?"), i18N.Translate("Confirm"), MessageBoxButtons.YesNo) == DialogResult.Yes)
|
||||
{
|
||||
DeleteServersInGroup(target.Remark);
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Link can not be empty"));
|
||||
RenameServersGroup(target.Remark, RemarkTextBox.Text);
|
||||
}
|
||||
|
||||
target.Link = LinkTextBox.Text;
|
||||
target.Remark = RemarkTextBox.Text;
|
||||
target.UserAgent = UserAgentTextBox.Text;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBoxX.Show(i18N.Translate("Remark can not be empty"));
|
||||
Global.Settings.SubscribeLink.Add(new SubscribeLink
|
||||
{
|
||||
Remark = RemarkTextBox.Text,
|
||||
Link = LinkTextBox.Text,
|
||||
UserAgent = UserAgentTextBox.Text
|
||||
});
|
||||
}
|
||||
|
||||
Configuration.Save();
|
||||
Global.Settings.UseProxyToUpdateSubscription = UseSelectedServerCheckBox.Checked;
|
||||
// MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
|
||||
ResetEditingGroup();
|
||||
|
||||
InitSubscribeLink();
|
||||
}
|
||||
|
||||
private static void DeleteServersInGroup(string group)
|
||||
{
|
||||
Global.Settings.Server.RemoveAll(server => server.Group == group);
|
||||
}
|
||||
|
||||
private static void RenameServersGroup(string oldGroup, string newGroup)
|
||||
{
|
||||
foreach (var server in Global.Settings.Server)
|
||||
{
|
||||
if (server.Group == oldGroup)
|
||||
{
|
||||
server.Group = newGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ControlButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
Configuration.Save();
|
||||
Global.Settings.UseProxyToUpdateSubscription = UseSelectedServerCheckBox.Checked;
|
||||
MessageBoxX.Show(i18N.Translate("Saved"));
|
||||
Close();
|
||||
}
|
||||
/// <summary>
|
||||
/// 订阅列表选中节点
|
||||
/// TODO 选中节点编辑
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void SubscribeLinkListView_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (SubscribeLinkListView.SelectedItems.Count > 0)
|
||||
var editingCanOverwrite = true;
|
||||
if (_editingIndex != -1)
|
||||
{
|
||||
RemarkTextBox.Text = SubscribeLinkListView.SelectedItems[0].SubItems[0].Text;
|
||||
LinkTextBox.Text = SubscribeLinkListView.SelectedItems[0].SubItems[1].Text;
|
||||
UserAgentTextBox.Text = SubscribeLinkListView.SelectedItems[0].SubItems[2].Text;
|
||||
var targetItem = SubscribeLinkListView.Items[_editingIndex].SubItems;
|
||||
editingCanOverwrite = RemarkTextBox.Text == targetItem[0].Text &&
|
||||
LinkTextBox.Text == targetItem[1].Text &&
|
||||
UserAgentTextBox.Text == targetItem[2].Text;
|
||||
}
|
||||
|
||||
if (SubscribeLinkListView.SelectedItems.Count == 1)
|
||||
{
|
||||
if (editingCanOverwrite)
|
||||
{
|
||||
SelectEditing(SubscribeLinkListView.SelectedItems[0].Index);
|
||||
}
|
||||
}
|
||||
else if (SubscribeLinkListView.SelectedItems.Count > 1)
|
||||
{
|
||||
}
|
||||
else if (editingCanOverwrite)
|
||||
{
|
||||
// 不选
|
||||
// 重置
|
||||
ResetEditingGroup();
|
||||
}
|
||||
}
|
||||
|
||||
private void SubscribeLinkListView_MouseUp(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.Button == MouseButtons.Right)
|
||||
{
|
||||
if (SubscribeLinkListView.SelectedItems.Count > 0)
|
||||
{
|
||||
pContextMenuStrip.Show(SubscribeLinkListView, e.Location);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectEditing(int index)
|
||||
{
|
||||
_editingIndex = index;
|
||||
ListViewItem target;
|
||||
target = SubscribeLinkListView.Items[index];
|
||||
AddSubscriptionBox.Text = target.SubItems[0].Text;
|
||||
RemarkTextBox.Text = target.SubItems[0].Text;
|
||||
LinkTextBox.Text = target.SubItems[1].Text;
|
||||
UserAgentTextBox.Text = target.SubItems[2].Text;
|
||||
}
|
||||
|
||||
private void ResetEditingGroup()
|
||||
{
|
||||
_editingIndex = -1;
|
||||
AddSubscriptionBox.Text = string.Empty;
|
||||
RemarkTextBox.Text = string.Empty;
|
||||
LinkTextBox.Text = string.Empty;
|
||||
UserAgentTextBox.Text = WebUtil.DefaultUserAgent;
|
||||
}
|
||||
|
||||
private void ClearButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
ResetEditingGroup();
|
||||
}
|
||||
|
||||
private void ListTextBox_TextChanged(object sender, EventArgs e)
|
||||
{
|
||||
for (var i = 0; i < SubscribeLinkListView.Items.Count; i++)
|
||||
{
|
||||
if (((TextBox) sender).Text == SubscribeLinkListView.Items[i].SubItems[1].Text)
|
||||
{
|
||||
_editingIndex = i;
|
||||
AddSubscriptionBox.Text = SubscribeLinkListView.Items[i].SubItems[0].Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,11 +21,6 @@ namespace Netch
|
||||
/// </summary>
|
||||
public static Forms.MainForm MainForm;
|
||||
|
||||
/// <summary>
|
||||
/// 设置窗体
|
||||
/// </summary>
|
||||
public static Forms.SettingForm SettingForm;
|
||||
|
||||
/// <summary>
|
||||
/// SS/SSR 加密方式
|
||||
/// </summary>
|
||||
@@ -206,8 +201,8 @@ namespace Netch
|
||||
public static Models.Setting Settings = new Models.Setting();
|
||||
|
||||
/// <summary>
|
||||
/// 用于存储模式文件内容
|
||||
/// 用于存储模式
|
||||
/// </summary>
|
||||
public static List<Models.Mode> ModeFiles = new List<Models.Mode>();
|
||||
public static readonly List<Models.Mode> Modes = new List<Models.Mode>();
|
||||
}
|
||||
}
|
||||
|
||||
137
Netch/Models/GitHubRelease/SuffixVersion.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Netch.Models.GitHubRelease
|
||||
{
|
||||
[Serializable]
|
||||
public struct SuffixVersion : ICloneable, IComparable, IComparable<SuffixVersion>, IEquatable<SuffixVersion>
|
||||
{
|
||||
public int Major { get; }
|
||||
public int Minor { get; }
|
||||
public int Patch { get; }
|
||||
public string PreRelease { get; }
|
||||
public int Build { get; }
|
||||
|
||||
public SuffixVersion(int major, int minor, int patch, string preRelease, int build)
|
||||
{
|
||||
Major = major;
|
||||
Minor = minor;
|
||||
Patch = patch;
|
||||
PreRelease = preRelease;
|
||||
Build = build;
|
||||
}
|
||||
|
||||
public SuffixVersion(Version version, string preRelease, int build)
|
||||
{
|
||||
Major = version.Major;
|
||||
Minor = version.Minor;
|
||||
Patch = version.Build;
|
||||
PreRelease = preRelease;
|
||||
Build = build;
|
||||
}
|
||||
|
||||
public static SuffixVersion Parse(string input)
|
||||
{
|
||||
var splitStr = input.Split('-');
|
||||
var dotNetVersion = Version.Parse(splitStr[0]);
|
||||
var preRelease = new StringBuilder();
|
||||
var build = 0;
|
||||
|
||||
if (splitStr.Length > 1)
|
||||
foreach (var c in splitStr[1])
|
||||
{
|
||||
if (int.TryParse(c.ToString(), out var n))
|
||||
{
|
||||
build = build * 10 + n;
|
||||
}
|
||||
else
|
||||
{
|
||||
preRelease.Append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return new SuffixVersion(dotNetVersion, preRelease.ToString(), build);
|
||||
}
|
||||
|
||||
public static bool TryParse(string input, out SuffixVersion result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = Parse(input);
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
result = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public object Clone() => new SuffixVersion(Major, Major, Patch, PreRelease, Build);
|
||||
|
||||
public int CompareTo(object obj)
|
||||
{
|
||||
if (obj is SuffixVersion version)
|
||||
return CompareTo(version);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="other"></param>
|
||||
/// <returns>
|
||||
/// greater than 0 newer
|
||||
/// </returns>
|
||||
public int CompareTo(SuffixVersion other)
|
||||
{
|
||||
var majorComparison = Major.CompareTo(other.Major);
|
||||
if (majorComparison != 0)
|
||||
return majorComparison;
|
||||
var minorComparison = Minor.CompareTo(other.Minor);
|
||||
if (minorComparison != 0)
|
||||
return minorComparison;
|
||||
var patchComparison = Patch.CompareTo(other.Patch);
|
||||
if (patchComparison != 0)
|
||||
return patchComparison;
|
||||
if (PreRelease == string.Empty)
|
||||
return other.PreRelease == string.Empty ? 0 : 1;
|
||||
if (other.PreRelease == string.Empty)
|
||||
return -1;
|
||||
var suffixComparison = string.Compare(PreRelease, other.PreRelease, StringComparison.Ordinal);
|
||||
if (suffixComparison != 0)
|
||||
return suffixComparison;
|
||||
return Build.CompareTo(other.Build);
|
||||
}
|
||||
|
||||
public bool Equals(SuffixVersion other)
|
||||
{
|
||||
return Major == other.Major && Minor == other.Minor && Patch == other.Patch && PreRelease == other.PreRelease && Build == other.Build;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is SuffixVersion other && Equals(other);
|
||||
}
|
||||
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
var hashCode = Major;
|
||||
hashCode = (hashCode * 397) ^ Minor;
|
||||
hashCode = (hashCode * 397) ^ Patch;
|
||||
hashCode = (hashCode * 397) ^ (PreRelease != null ? PreRelease.GetHashCode() : 0);
|
||||
hashCode = (hashCode * 397) ^ Build;
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Major}.{Minor}.{Patch}{(string.IsNullOrEmpty(PreRelease) ? "" : "-")}{PreRelease}{(Build == 0 ? "" : Build.ToString())}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Netch.Models.GitHubRelease
|
||||
@@ -20,9 +19,7 @@ namespace Netch.Models.GitHubRelease
|
||||
|
||||
private static bool IsVersionString(string str)
|
||||
{
|
||||
if (Global.Settings.CheckBetaUpdate)
|
||||
str = str.Split('-')[0];
|
||||
return Version.TryParse(str, out _);
|
||||
return SuffixVersion.TryParse(str, out _);
|
||||
}
|
||||
|
||||
/// <returns> =0:versions are equal</returns>
|
||||
@@ -30,28 +27,9 @@ namespace Netch.Models.GitHubRelease
|
||||
/// <returns> <0:version2 is greater</returns>
|
||||
public static int CompareVersion(string v1, string v2)
|
||||
{
|
||||
var version1 = ToVersion(v1);
|
||||
var version2 = ToVersion(v2);
|
||||
var res = version1.CompareTo(version2);
|
||||
return res;
|
||||
}
|
||||
|
||||
private static Version ToVersion(string versionStr)
|
||||
{
|
||||
var v = versionStr.Split('-');
|
||||
var version = Version.Parse(v[0]);
|
||||
if (v.Length == 1)
|
||||
return version;
|
||||
var beta = v[1];
|
||||
|
||||
var result = string.Empty;
|
||||
foreach (var c in beta)
|
||||
{
|
||||
if (int.TryParse(c.ToString(), out var n))
|
||||
result += n;
|
||||
}
|
||||
|
||||
return new Version(version.Major, version.Minor, version.Build, int.Parse(result));
|
||||
var version1 = SuffixVersion.Parse(v1);
|
||||
var version2 = SuffixVersion.Parse(v2);
|
||||
return version1.CompareTo(version2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Netch.Utils;
|
||||
|
||||
namespace Netch.Models
|
||||
{
|
||||
@@ -10,6 +13,11 @@ namespace Netch.Models
|
||||
/// </summary>
|
||||
public string Remark;
|
||||
|
||||
/// <summary>
|
||||
/// 文件相对路径(必须是存在的文件)
|
||||
/// </summary>
|
||||
public string RelativePath;
|
||||
|
||||
/// <summary>
|
||||
/// 无后缀文件名
|
||||
/// </summary>
|
||||
@@ -20,9 +28,9 @@ namespace Netch.Models
|
||||
/// 0. Socks5 + 进程加速<para />
|
||||
/// 1. Socks5 + TUN/TAP 规则内 IP CIDR 加速<para />
|
||||
/// 2. Socks5 + TUN/TAP 全局,绕过规则内 IP CIDR<para />
|
||||
/// 3. Socks5 + HTTP 代理(自动设置到系统代理)<para />
|
||||
/// 4. Socks5 代理(不自动设置到系统代理)<para />
|
||||
/// 5. Socks5 + HTTP 代理(不自动设置到系统代理)<para />
|
||||
/// 3. Socks5 + HTTP 代理(设置到系统代理)<para />
|
||||
/// 4. Socks5 代理(不设置到系统代理)<para />
|
||||
/// 5. Socks5 + HTTP 代理(不设置到系统代理)<para />
|
||||
/// </summary>
|
||||
public int Type = 0;
|
||||
|
||||
@@ -34,7 +42,7 @@ namespace Netch.Models
|
||||
/// <summary>
|
||||
/// 规则
|
||||
/// </summary>
|
||||
public List<string> Rule = new List<string>();
|
||||
public readonly List<string> Rule = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 获取备注
|
||||
@@ -42,7 +50,7 @@ namespace Netch.Models
|
||||
/// <returns>备注</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("[{0}] {1}", Type + 1, Remark);
|
||||
return $"[{Type + 1}] {Remark}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -51,85 +59,44 @@ namespace Netch.Models
|
||||
/// <returns>模式文件字符串</returns>
|
||||
public string ToFileString()
|
||||
{
|
||||
string FileString;
|
||||
string fileString;
|
||||
|
||||
// 进程模式
|
||||
if (Type == 0)
|
||||
switch (Type)
|
||||
{
|
||||
FileString = $"# {Remark}\r\n";
|
||||
case 0:
|
||||
// 进程模式
|
||||
fileString = $"# {Remark}";
|
||||
break;
|
||||
case 1:
|
||||
// TUN/TAP 规则内 IP CIDR,无 Bypass China 设置
|
||||
fileString = $"# {Remark}, {Type}, 0";
|
||||
break;
|
||||
default:
|
||||
fileString = $"# {Remark}, {Type}, {(BypassChina ? 1 : 0)}";
|
||||
break;
|
||||
}
|
||||
|
||||
// TUN/TAP 规则内 IP CIDR,无 Bypass China 设置
|
||||
else if (Type == 1)
|
||||
{
|
||||
FileString = $"# {Remark}, {Type}, 0\r\n";
|
||||
}
|
||||
fileString += Global.EOF;
|
||||
|
||||
// TUN/TAP 全局,绕过规则内 IP CIDR
|
||||
// HTTP 代理(自动设置到系统代理)
|
||||
// Socks5 代理(不自动设置到系统代理)
|
||||
// Socks5 + HTTP 代理(不自动设置到系统代理)
|
||||
else
|
||||
{
|
||||
FileString = $"# {Remark}, {Type}, {(BypassChina ? 1 : 0)}\r\n";
|
||||
}
|
||||
fileString = Rule.Aggregate(fileString, (current, item) => $"{current}{item}{Global.EOF}");
|
||||
// 去除最后的行尾符
|
||||
fileString = fileString.Substring(0, fileString.Length - 2);
|
||||
|
||||
foreach (var item in Rule)
|
||||
{
|
||||
FileString = $"{FileString}{item}\r\n";
|
||||
}
|
||||
|
||||
// 去除最后两个多余回车符和换行符
|
||||
FileString = FileString.Substring(0, FileString.Length - 2);
|
||||
|
||||
return FileString;
|
||||
return fileString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 写入模式文件
|
||||
/// </summary>
|
||||
public void ToFile(string Dir)
|
||||
public string TypeToString()
|
||||
{
|
||||
if (!System.IO.Directory.Exists(Dir))
|
||||
return Type switch
|
||||
{
|
||||
System.IO.Directory.CreateDirectory(Dir);
|
||||
}
|
||||
|
||||
var NewPath = System.IO.Path.Combine(Dir, FileName);
|
||||
if (System.IO.File.Exists(NewPath + ".txt"))
|
||||
{
|
||||
// 重命名该模式文件名
|
||||
NewPath += "_";
|
||||
|
||||
while (System.IO.File.Exists(NewPath + ".txt"))
|
||||
{
|
||||
// 循环重命名该模式文件名,直至不重名
|
||||
NewPath += "_";
|
||||
}
|
||||
}
|
||||
|
||||
FileName = System.IO.Path.GetFileName(NewPath);
|
||||
|
||||
// 加上文件名后缀
|
||||
NewPath += ".txt";
|
||||
|
||||
// 写入到模式文件里
|
||||
System.IO.File.WriteAllText(NewPath, ToFileString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除模式文件
|
||||
/// </summary>
|
||||
public void DeleteFile(string Dir)
|
||||
{
|
||||
if (System.IO.Directory.Exists(Dir))
|
||||
{
|
||||
var NewPath = System.IO.Path.Combine(Dir, FileName);
|
||||
if (System.IO.File.Exists(NewPath + ".txt"))
|
||||
{
|
||||
System.IO.File.Delete(NewPath + ".txt");
|
||||
}
|
||||
}
|
||||
0 => "Process",
|
||||
1 => "TUNTAP",
|
||||
2 => "TUNTAP",
|
||||
3 => "SYSTEM",
|
||||
4 => "S5",
|
||||
5 => "S5+HTTP",
|
||||
_ => "ERROR",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ namespace Netch.Models
|
||||
/// <summary>
|
||||
/// 协议(SSR)
|
||||
/// </summary>
|
||||
public string Protocol;
|
||||
public string Protocol = Global.Protocols[0];
|
||||
|
||||
/// <summary>
|
||||
/// 协议参数(SSR)
|
||||
@@ -86,7 +86,7 @@ namespace Netch.Models
|
||||
/// <summary>
|
||||
/// 混淆(SSR)
|
||||
/// </summary>
|
||||
public string OBFS;
|
||||
public string OBFS = Global.OBFSs[0];
|
||||
|
||||
/// <summary>
|
||||
/// 混淆参数(SSR)
|
||||
@@ -96,12 +96,12 @@ namespace Netch.Models
|
||||
/// <summary>
|
||||
/// 传输协议(VMess)
|
||||
/// </summary>
|
||||
public string TransferProtocol = "tcp";
|
||||
public string TransferProtocol = Global.TransferProtocols[0];
|
||||
|
||||
/// <summary>
|
||||
/// 伪装类型(VMess)
|
||||
/// </summary>
|
||||
public string FakeType = string.Empty;
|
||||
public string FakeType = Global.FakeTypes[0];
|
||||
|
||||
/// <summary>
|
||||
/// 伪装域名(VMess:HTTP、WebSocket、HTTP/2)
|
||||
@@ -116,7 +116,7 @@ namespace Netch.Models
|
||||
/// <summary>
|
||||
/// QUIC 加密方式(VMess)
|
||||
/// </summary>
|
||||
public string QUICSecure = "none";
|
||||
public string QUICSecure = Global.EncryptMethods.VMessQUIC[0];
|
||||
|
||||
/// <summary>
|
||||
/// QUIC 加密密钥(VMess)
|
||||
@@ -131,7 +131,7 @@ namespace Netch.Models
|
||||
/// <summary>
|
||||
/// Mux 多路复用(VMess)
|
||||
/// </summary>
|
||||
public bool UseMux = false;
|
||||
public bool UseMux = true;
|
||||
|
||||
/// <summary>
|
||||
/// 延迟
|
||||
@@ -144,10 +144,10 @@ namespace Netch.Models
|
||||
public string Country;
|
||||
|
||||
/// <summary>
|
||||
/// 获取备注
|
||||
/// </summary>
|
||||
/// <returns>备注</returns>
|
||||
public override string ToString()
|
||||
/// 获取备注
|
||||
/// </summary>
|
||||
/// <returns>备注</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(Remark))
|
||||
{
|
||||
@@ -220,4 +220,4 @@ namespace Netch.Models
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace Netch.Models
|
||||
{
|
||||
@@ -87,12 +88,17 @@ namespace Netch.Models
|
||||
/// 是否打开软件时检查更新
|
||||
/// </summary>
|
||||
public bool CheckUpdateWhenOpened = true;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 是否检查 Beta 更新
|
||||
/// </summary>
|
||||
public bool CheckBetaUpdate = false;
|
||||
|
||||
/// <summary>
|
||||
/// 是否打开软件时更新订阅
|
||||
/// </summary>
|
||||
public bool UpdateSubscribeatWhenOpened = false;
|
||||
|
||||
/// <summary>
|
||||
/// 修改系统 DNS
|
||||
/// </summary>
|
||||
@@ -103,12 +109,6 @@ namespace Netch.Models
|
||||
/// </summary>
|
||||
public int RequestTimeout = 10000;
|
||||
|
||||
/// <summary>
|
||||
/// 使用何种模式文件名
|
||||
/// 0 为自定义文件名,1 为使用和备注一致的文件名,2 为使用时间数据作为文件名
|
||||
/// </summary>
|
||||
public int ModeFileNameType = 1;
|
||||
|
||||
/// <summary>
|
||||
/// HTTP 本地端口
|
||||
/// </summary>
|
||||
@@ -152,7 +152,7 @@ namespace Netch.Models
|
||||
/// <summary>
|
||||
/// 服务器列表
|
||||
/// </summary>
|
||||
public List<Server> Server = new List<Server>();
|
||||
public readonly List<Server> Server = new List<Server>();
|
||||
|
||||
/// <summary>
|
||||
/// 全局绕过 IP 列表
|
||||
@@ -172,7 +172,7 @@ namespace Netch.Models
|
||||
/// <summary>
|
||||
/// STUN测试服务器
|
||||
/// </summary>
|
||||
public string STUN_Server = "stun.stunprotocol.org";
|
||||
public string STUN_Server = "stun.syncthing.net";
|
||||
|
||||
/// <summary>
|
||||
/// STUN测试服务器
|
||||
@@ -204,4 +204,4 @@ namespace Netch.Models
|
||||
/// </summary>
|
||||
public string Language = "System";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,32 @@ namespace Netch
|
||||
// 设置当前目录
|
||||
Directory.SetCurrentDirectory(Global.NetchDir);
|
||||
|
||||
// 预创建目录
|
||||
var directories = new[] {"mode", "data", "i18n", "logging"};
|
||||
foreach (var item in directories)
|
||||
{
|
||||
if (!Directory.Exists(item))
|
||||
{
|
||||
Directory.CreateDirectory(item);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载配置
|
||||
Configuration.Load();
|
||||
|
||||
// 加载语言
|
||||
i18N.Load(Global.Settings.Language);
|
||||
|
||||
// 检查是否已经运行
|
||||
if (!mutex.WaitOne(0, false))
|
||||
{
|
||||
OnlyInstance.Send(OnlyInstance.Commands.Show);
|
||||
Logging.Info("唤起单实例");
|
||||
|
||||
// 退出进程
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
// 清理上一次的日志文件,防止淤积占用磁盘空间
|
||||
if (Directory.Exists("logging"))
|
||||
{
|
||||
@@ -39,39 +65,9 @@ namespace Netch
|
||||
}
|
||||
}
|
||||
|
||||
// 预创建目录
|
||||
var directories = new[] {"mode", "data", "i18n", "logging"};
|
||||
foreach (var item in directories)
|
||||
{
|
||||
// 检查是否已经存在
|
||||
if (!Directory.Exists(item))
|
||||
{
|
||||
// 创建目录
|
||||
Directory.CreateDirectory(item);
|
||||
}
|
||||
}
|
||||
|
||||
// 加载配置
|
||||
Configuration.Load();
|
||||
|
||||
// 加载语言
|
||||
i18N.Load(Global.Settings.Language);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
Logging.Info($"版本: {UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
|
||||
Logging.Info($"主程序 SHA256: {Utils.Utils.SHA256CheckSum(Application.ExecutablePath)}");
|
||||
});
|
||||
|
||||
// 检查是否已经运行
|
||||
if (!mutex.WaitOne(0, false))
|
||||
{
|
||||
OnlyInstance.Send(OnlyInstance.Commands.Show);
|
||||
|
||||
// 退出进程
|
||||
Environment.Exit(1);
|
||||
}
|
||||
|
||||
Logging.Info($"版本: {UpdateChecker.Owner}/{UpdateChecker.Repo}@{UpdateChecker.Version}");
|
||||
Task.Run(() => { Logging.Info($"主程序 SHA256: {Utils.Utils.SHA256CheckSum(Application.ExecutablePath)}"); });
|
||||
Logging.Info("启动单实例");
|
||||
Task.Run(OnlyInstance.Server);
|
||||
|
||||
// 绑定错误捕获
|
||||
|
||||
@@ -57,20 +57,23 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ini-parser" Version="2.5.2" />
|
||||
<PackageReference Include="ILMerge" Version="3.0.41" />
|
||||
<PackageReference Include="IPNetwork2" Version="2.5.211" />
|
||||
<PackageReference Include="MaxMind.GeoIP2" Version="3.2.0" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.56" />
|
||||
<PackageReference Include="Microsoft.Diagnostics.Tracing.TraceEvent" Version="2.0.58" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="System.Buffers" Version="4.5.1" />
|
||||
<PackageReference Include="System.Collections.Immutable" Version="5.0.0-preview.8.20407.11" />
|
||||
<PackageReference Include="System.Reflection.Metadata" Version="5.0.0-preview.8.20407.11" />
|
||||
<PackageReference Include="WindowsAPICodePack-Shell" Version="1.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\NetchLib\NetchLib.csproj" />
|
||||
<ProjectReference Include="..\NetchUpdater\NetchUpdater.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Management" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.ServiceProcess" />
|
||||
@@ -108,6 +111,6 @@
|
||||
</ItemGroup>
|
||||
<ProjectExtensions><VisualStudio><UserProperties /></VisualStudio></ProjectExtensions>
|
||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||
<Exec Command="@ECHO OFF
RD /S /Q $(TargetDir)bin > NUL 2>&1
RD /S /Q $(TargetDir)i18n > NUL 2>&1
RD /S /Q $(TargetDir)mode > NUL 2>&1

MKDIR $(TargetDir)bin > NUL 2>&1
MKDIR $(TargetDir)i18n > NUL 2>&1
MKDIR $(TargetDir)mode > NUL 2>&1

COPY /Y $(SolutionDir)binaries\* $(TargetDir)bin > NUL 2>&1
COPY /Y $(SolutionDir)translations\i18n\* $(TargetDir)i18n > NUL 2>&1
COPY /Y $(SolutionDir)modes\mode\* $(TargetDir)mode > NUL 2>&1
MKDIR $(TargetDir)bin\tap-driver > NUL 2>&1
COPY /Y $(SolutionDir)binaries\tap-driver\* $(TargetDir)bin\tap-driver > NUL 2>&1

DEL / f $(TargetDir)\*.config
DEL / f $(TargetDir)\*.pdb
RD /s /Q $(TargetDir)\x86" />
|
||||
<Exec Command="set Configuration=$(Configuration)
set ILMergeConsolePath=$(ILMergeConsolePath)
set TargetDir=$(TargetDir)
set SolutionDir=$(SolutionDir)
$(ProjectDir)PostBuild.bat" />
|
||||
</Target>
|
||||
</Project>
|
||||
|
||||
40
Netch/PostBuild.bat
Normal file
@@ -0,0 +1,40 @@
|
||||
if %Configuration%==Release (
|
||||
:: Merge dlls
|
||||
%ILMergeConsolePath% %TargetDir%Netch.exe ^
|
||||
/out:%TargetDir%NetchMerged.exe ^
|
||||
%TargetDir%Dia2Lib.dll ^
|
||||
%TargetDir%Interop.NetFwTypeLib.dll ^
|
||||
%TargetDir%Interop.TaskScheduler.dll ^
|
||||
%TargetDir%MaxMind.Db.dll ^
|
||||
%TargetDir%MaxMind.GeoIP2.dll ^
|
||||
%TargetDir%Microsoft.Diagnostics.FastSerialization.dll ^
|
||||
%TargetDir%Microsoft.Diagnostics.Tracing.TraceEvent.dll ^
|
||||
%TargetDir%Microsoft.WindowsAPICodePack.dll ^
|
||||
%TargetDir%Microsoft.WindowsAPICodePack.Shell.dll ^
|
||||
%TargetDir%NetchLib.dll ^
|
||||
%TargetDir%Newtonsoft.Json.dll ^
|
||||
%TargetDir%OSExtensions.dll ^
|
||||
%TargetDir%System.Buffers.dll ^
|
||||
%TargetDir%System.Collections.Immutable.dll ^
|
||||
%TargetDir%System.Memory.dll ^
|
||||
%TargetDir%System.Net.IPNetwork.dll ^
|
||||
%TargetDir%System.Numerics.Vectors.dll ^
|
||||
%TargetDir%System.Reflection.Metadata.dll ^
|
||||
%TargetDir%System.Runtime.CompilerServices.Unsafe.dll ^
|
||||
%TargetDir%TraceReloggerLib.dll
|
||||
|
||||
DEL /f %TargetDir%*.dll >NUL 2>&1
|
||||
MOVE /Y %TargetDir%NetchMerged.exe %TargetDir%Netch.exe >NUL
|
||||
)
|
||||
|
||||
RD /S /Q %TargetDir%bin >NUL 2>&1
|
||||
RD /S /Q %TargetDir%i18n >NUL 2>&1
|
||||
RD /S /Q %TargetDir%mode >NUL 2>&1
|
||||
|
||||
XCOPY /s /Y %SolutionDir%binaries %TargetDir%bin\ >NUL
|
||||
XCOPY /s /Y %SolutionDir%translations\i18n %TargetDir%i18n\ >NUL
|
||||
XCOPY /s /Y %SolutionDir%modes\mode %TargetDir%mode\ >NUL
|
||||
|
||||
DEL /f %TargetDir%*.config >NUL 2>&1
|
||||
DEL /f %TargetDir%*.pdb >NUL 2>&1
|
||||
RD /s /Q %TargetDir%x86 >NUL 2>&1
|
||||
52
Netch/Properties/Resources.Designer.cs
generated
@@ -1,10 +1,10 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 此代码由工具生成。
|
||||
// 运行时版本:4.0.30319.42000
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果
|
||||
// 重新生成代码,这些更改将会丢失。
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -13,13 +13,13 @@ namespace Netch.Properties {
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 一个强类型的资源类,用于查找本地化的字符串等。
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// 此类是由 StronglyTypedResourceBuilder
|
||||
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
|
||||
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
|
||||
// (以 /str 作为命令选项),或重新生成 VS 项目。
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
@@ -33,7 +33,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回此类使用的缓存的 ResourceManager 实例。
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
@@ -47,8 +47,8 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写当前线程的 CurrentUICulture 属性
|
||||
/// 重写当前线程的 CurrentUICulture 属性。
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
@@ -61,17 +61,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Byte[] 类型的本地化资源。
|
||||
/// </summary>
|
||||
internal static byte[] CNIP {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("CNIP", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap CopyLink {
|
||||
get {
|
||||
@@ -81,7 +71,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Byte[] 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] defaultTUNTAP {
|
||||
get {
|
||||
@@ -91,7 +81,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap delete {
|
||||
get {
|
||||
@@ -101,7 +91,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap edit {
|
||||
get {
|
||||
@@ -111,7 +101,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap Netch {
|
||||
get {
|
||||
@@ -121,7 +111,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap speed {
|
||||
get {
|
||||
@@ -131,7 +121,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
internal static System.Drawing.Bitmap Sponsor {
|
||||
get {
|
||||
@@ -141,7 +131,7 @@ namespace Netch.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Byte[] 类型的本地化资源。
|
||||
/// Looks up a localized resource of type System.Byte[].
|
||||
/// </summary>
|
||||
internal static byte[] zh_CN {
|
||||
get {
|
||||
|
||||
@@ -133,9 +133,6 @@
|
||||
<data name="edit" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\edit.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
<data name="CNIP" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\CNIP;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
<data name="Netch" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\Netch.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
|
||||
</data>
|
||||
|
||||
5695
Netch/Resources/CNIP
@@ -40,7 +40,14 @@
|
||||
"Add [Trojan] Server": "添加 [Trojan] 服务器",
|
||||
"Netch is now minimized to the notification bar, double click this icon to restore.": "Netch 已最小化至通知栏,双击此图标恢复窗口。",
|
||||
"New version available": "发现新版本",
|
||||
"Already latest version": "已经是最新版本",
|
||||
"New version found failed": "寻找新版本失败",
|
||||
"Mode": "模式",
|
||||
"Help": "帮助",
|
||||
"Check for updates": "检查更新",
|
||||
"Download and install now?": "立即下载并安装?",
|
||||
"Start downloading new version": "开始下载新版本",
|
||||
"Download update failed": "下载更新错误",
|
||||
"Create Process Mode": "创建进程模式",
|
||||
"Edit Process Mode": "修改进程模式",
|
||||
|
||||
@@ -158,6 +165,7 @@
|
||||
"Port value illegal. Try again.": "端口值非法。请重试。",
|
||||
"Check update when opened": "打开软件时检查更新",
|
||||
"Check Beta update": "检查 Beta 更新",
|
||||
"Update subscribeat when opened": "自动更新订阅",
|
||||
"SS DLL(No ACL support)": "SS DLL(不支持 ACL)",
|
||||
"Modify System DNS": "修改系统 DNS",
|
||||
"ProfileCount": "快捷配置数量",
|
||||
|
||||
@@ -6,116 +6,131 @@ using System.Threading.Tasks;
|
||||
using Microsoft.Diagnostics.Tracing.Parsers;
|
||||
using Microsoft.Diagnostics.Tracing.Session;
|
||||
using Netch.Controllers;
|
||||
using Netch.Forms;
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
public static class Bandwidth
|
||||
{
|
||||
public static int received;
|
||||
public static ulong received;
|
||||
public static TraceEventSession tSession;
|
||||
|
||||
/// <summary>
|
||||
/// 计算流量
|
||||
/// </summary>
|
||||
/// <param name="bandwidth">流量</param>
|
||||
/// <returns>带单位的流量字符串</returns>
|
||||
public static string Compute(long bandwidth)
|
||||
public static string Compute(ulong size)
|
||||
{
|
||||
string[] units = {"KB", "MB", "GB", "TB", "PB"};
|
||||
double result = bandwidth;
|
||||
var i = -1;
|
||||
|
||||
do
|
||||
var mStrSize = @"0";
|
||||
const double step = 1024.00;
|
||||
var factSize = size;
|
||||
if (factSize < step)
|
||||
{
|
||||
i++;
|
||||
} while ((result /= 1024) > 1024);
|
||||
|
||||
if (result < 0)
|
||||
mStrSize = $@"{factSize:0.##} B";
|
||||
}
|
||||
else if (factSize >= step && factSize < 1048576)
|
||||
{
|
||||
result = 0;
|
||||
mStrSize = $@"{factSize / step:0.##} KB";
|
||||
}
|
||||
else if (factSize >= 1048576 && factSize < 1073741824)
|
||||
{
|
||||
mStrSize = $@"{factSize / step / step:0.##} MB";
|
||||
}
|
||||
else if (factSize >= 1073741824 && factSize < 1099511627776)
|
||||
{
|
||||
mStrSize = $@"{factSize / step / step / step:0.##} GB";
|
||||
}
|
||||
else if (factSize >= 1099511627776)
|
||||
{
|
||||
mStrSize = $@"{factSize / step / step / step / step:0.##} TB";
|
||||
}
|
||||
|
||||
return string.Format("{0} {1}", Math.Round(result, 2), units[i]);
|
||||
return mStrSize;
|
||||
}
|
||||
|
||||
public static bool NetTrafficAvailable => /*Global.Settings.EnableNetTraffic && */Environment.OSVersion.Version.Major >= 10;
|
||||
|
||||
/// <summary>
|
||||
/// 根据程序名统计流量
|
||||
/// </summary>
|
||||
/// <param name="ProcessName"></param>
|
||||
public static void NetTraffic(Server server, Mode mode, ref MainController mainController)
|
||||
public static void NetTraffic(Server server, Mode mode)
|
||||
{
|
||||
if (!NetTrafficAvailable)
|
||||
return;
|
||||
|
||||
var counterLock = new object();
|
||||
//int sent = 0;
|
||||
|
||||
//var processList = Process.GetProcessesByName(ProcessName).Select(p => p.Id).ToHashSet();
|
||||
var instances = new List<Process>();
|
||||
if (server.Type.Equals("Socks5") && mainController.pModeController.Name == "HTTP")
|
||||
if (server.Type.Equals("Socks5") && MainController.ModeController.Name == "HTTP")
|
||||
{
|
||||
instances.Add(((HTTPController) mainController.pModeController).pPrivoxyController.Instance);
|
||||
instances.Add(((HTTPController) MainController.ModeController).pPrivoxyController.Instance);
|
||||
}
|
||||
else if (server.Type.Equals("SS") && Global.Settings.BootShadowsocksFromDLL)
|
||||
else if (server.Type.Equals("SS") && Global.Settings.BootShadowsocksFromDLL &&
|
||||
(mode.Type == 0 || mode.Type == 1 || mode.Type == 2))
|
||||
{
|
||||
instances.Add(Process.GetCurrentProcess());
|
||||
}
|
||||
else if (mainController.pEncryptedProxyController != null)
|
||||
else if (MainController.EncryptedProxyController != null)
|
||||
{
|
||||
instances.Add(mainController.pEncryptedProxyController.Instance);
|
||||
instances.Add(MainController.EncryptedProxyController.Instance);
|
||||
}
|
||||
else if (mainController.pModeController != null)
|
||||
else if (MainController.ModeController != null)
|
||||
{
|
||||
instances.Add(mainController.pModeController.Instance);
|
||||
instances.Add(MainController.ModeController.Instance);
|
||||
}
|
||||
|
||||
var processList = instances.Select(instance => instance.Id).ToList();
|
||||
|
||||
Logging.Info("流量统计进程:" + string.Join(",", instances.Select(instance => $"({instance.Id})"+instance.ProcessName).ToArray()));
|
||||
Logging.Info("流量统计进程:" + string.Join(",",
|
||||
instances.Select(instance => $"({instance.Id})" + instance.ProcessName).ToArray()));
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
using (var session = new TraceEventSession("KernelAndClrEventsSession"))
|
||||
tSession = new TraceEventSession("KernelAndClrEventsSession");
|
||||
tSession.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP);
|
||||
|
||||
//这玩意儿上传和下载得到的data是一样的:)
|
||||
//所以暂时没办法区分上传下载流量
|
||||
tSession.Source.Kernel.TcpIpRecv += data =>
|
||||
{
|
||||
session.EnableKernelProvider(KernelTraceEventParser.Keywords.NetworkTCPIP);
|
||||
|
||||
//这玩意儿上传和下载得到的data是一样的:)
|
||||
//所以暂时没办法区分上传下载流量
|
||||
session.Source.Kernel.TcpIpRecv += data =>
|
||||
if (processList.Contains(data.ProcessID))
|
||||
{
|
||||
if (processList.Contains(data.ProcessID))
|
||||
{
|
||||
lock (counterLock)
|
||||
received += data.size;
|
||||
//Logging.Info($"TcpIpRecv: {Compute(data.size)}");
|
||||
}
|
||||
};
|
||||
session.Source.Kernel.UdpIpRecv += data =>
|
||||
{
|
||||
if (processList.Contains(data.ProcessID))
|
||||
{
|
||||
lock (counterLock)
|
||||
received += data.size;
|
||||
//Logging.Info($"UdpIpRecv: {Compute(data.size)}");
|
||||
}
|
||||
};
|
||||
lock (counterLock)
|
||||
received += (ulong) data.size;
|
||||
|
||||
session.Source.Process();
|
||||
}
|
||||
// Debug.WriteLine($"TcpIpRecv: {ToByteSize(data.size)}");
|
||||
}
|
||||
};
|
||||
tSession.Source.Kernel.UdpIpRecv += data =>
|
||||
{
|
||||
if (processList.Contains(data.ProcessID))
|
||||
{
|
||||
lock (counterLock)
|
||||
received += (ulong) data.size;
|
||||
|
||||
// Debug.WriteLine($"UdpIpRecv: {ToByteSize(data.size)}");
|
||||
}
|
||||
};
|
||||
tSession.Source.Process();
|
||||
});
|
||||
|
||||
if ((Convert.ToInt32(Global.MainForm.LastDownloadBandwidth) - Convert.ToInt32(received)) == 0)
|
||||
{
|
||||
Global.MainForm.OnBandwidthUpdated(0);
|
||||
received = 0;
|
||||
}
|
||||
|
||||
while (Global.MainForm.State != State.Stopped)
|
||||
{
|
||||
Task.Delay(1000).Wait();
|
||||
lock (counterLock)
|
||||
{
|
||||
Global.MainForm.OnBandwidthUpdated(Convert.ToInt64(received));
|
||||
Global.MainForm.OnBandwidthUpdated(received);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
tSession?.Dispose();
|
||||
received = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ namespace Netch.Utils
|
||||
/// <summary>
|
||||
/// 数据目录
|
||||
/// </summary>
|
||||
public static readonly string DATA_DIR = "data";
|
||||
public const string DATA_DIR = "data";
|
||||
|
||||
/// <summary>
|
||||
/// 设置
|
||||
|
||||
138
Netch/Utils/Modes.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Netch.Models;
|
||||
|
||||
namespace Netch.Utils
|
||||
{
|
||||
public static class Modes
|
||||
{
|
||||
private const string MODE_DIR = "mode";
|
||||
|
||||
public static readonly string ModeDirectory = Path.Combine(Global.NetchDir, $"{MODE_DIR}\\");
|
||||
|
||||
public static string GetRelativePath(string fullName) => fullName.Substring(ModeDirectory.Length);
|
||||
public static string GetFullPath(string relativeName) => Path.Combine(ModeDirectory, relativeName);
|
||||
public static string GetFullPath(Mode mode) => Path.Combine(ModeDirectory, mode.RelativePath);
|
||||
|
||||
/// <summary>
|
||||
/// 从模式文件夹读取模式并为 <see cref="Forms.MainForm.ModeComboBox"/> 绑定数据
|
||||
/// </summary>
|
||||
public static void Load()
|
||||
{
|
||||
Global.Modes.Clear();
|
||||
|
||||
if (!Directory.Exists(MODE_DIR)) return;
|
||||
|
||||
var stack = new Stack<string>();
|
||||
stack.Push(MODE_DIR);
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var dirInfo = new DirectoryInfo(stack.Pop());
|
||||
try
|
||||
{
|
||||
foreach (var childDirInfo in dirInfo.GetDirectories())
|
||||
stack.Push(childDirInfo.FullName);
|
||||
|
||||
foreach (var childFileInfo in dirInfo.GetFiles().Where(info => info.Name.EndsWith(".txt")))
|
||||
LoadModeFile(childFileInfo.FullName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
Sort();
|
||||
}
|
||||
|
||||
private static void LoadModeFile(string fullName)
|
||||
{
|
||||
var mode = new Mode
|
||||
{
|
||||
FileName = Path.GetFileNameWithoutExtension(fullName),
|
||||
RelativePath = GetRelativePath(fullName)
|
||||
};
|
||||
|
||||
var content = File.ReadAllLines(fullName);
|
||||
if (content.Length == 0) return;
|
||||
|
||||
for (var i = 0; i < content.Length; i++)
|
||||
{
|
||||
var text = content[i];
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
var splited = text.Substring(text.IndexOf('#') + 1).Split(',').Select(s => s.Trim()).ToArray();
|
||||
try
|
||||
{
|
||||
string tmp;
|
||||
if ((tmp = splited.ElementAtOrDefault(0)) != null)
|
||||
mode.Remark = i18N.Translate(tmp);
|
||||
|
||||
tmp = splited.ElementAtOrDefault(1);
|
||||
mode.Type = tmp != null ? int.Parse(tmp) : 0;
|
||||
|
||||
if ((tmp = splited.ElementAtOrDefault(2)) != null)
|
||||
mode.BypassChina = int.Parse(tmp) == 1;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!text.StartsWith("#") && !string.IsNullOrWhiteSpace(text))
|
||||
mode.Rule.Add(text.Trim());
|
||||
}
|
||||
}
|
||||
|
||||
Global.Modes.Add(mode);
|
||||
}
|
||||
|
||||
public static void WriteFile(Mode mode)
|
||||
{
|
||||
if (!Directory.Exists(ModeDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(ModeDirectory);
|
||||
}
|
||||
|
||||
var fullName = GetFullPath(mode.RelativePath ?? mode.FileName + ".txt");
|
||||
|
||||
if (mode.RelativePath == null && File.Exists(fullName))
|
||||
{
|
||||
throw new Exception("新建模式的文件名已存在,请贡献者检查代码");
|
||||
}
|
||||
|
||||
// 写入到模式文件里
|
||||
File.WriteAllText(fullName, mode.ToFileString());
|
||||
mode.RelativePath = GetRelativePath(fullName);
|
||||
}
|
||||
|
||||
private static void Sort()
|
||||
{
|
||||
Global.Modes.Sort((a, b) => string.Compare(a.Remark, b.Remark, StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
public static void Add(Mode mode)
|
||||
{
|
||||
Global.Modes.Add(mode);
|
||||
Sort();
|
||||
Global.MainForm.InitMode();
|
||||
}
|
||||
|
||||
public static void Delete(Mode mode)
|
||||
{
|
||||
var fullName = GetFullPath(mode);
|
||||
if (File.Exists(fullName))
|
||||
{
|
||||
File.Delete(fullName);
|
||||
}
|
||||
|
||||
Global.Modes.Remove(mode);
|
||||
Global.MainForm.InitMode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
@@ -103,5 +104,19 @@ namespace Netch.Utils
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsZipValid(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var zipFile = ZipFile.OpenRead(path);
|
||||
_ = zipFile.Entries;
|
||||
return true;
|
||||
}
|
||||
catch (InvalidDataException)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
@@ -8,7 +9,8 @@ namespace Netch.Utils
|
||||
{
|
||||
public class WebUtil
|
||||
{
|
||||
private const string DefaultUserAgent = @"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36";
|
||||
public const string DefaultUserAgent =
|
||||
@"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36";
|
||||
|
||||
private static int DefaultGetTimeout => Global.Settings.RequestTimeout;
|
||||
|
||||
@@ -24,53 +26,51 @@ namespace Netch.Utils
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 异步下载
|
||||
/// </summary>
|
||||
/// <param name="req"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="WebException"></exception>
|
||||
public static async Task<string> DownloadStringAsync(HttpWebRequest req)
|
||||
public static async Task<byte[]> DownloadBytesAsync(HttpWebRequest req)
|
||||
{
|
||||
string content;
|
||||
var response = (HttpWebResponse) await req.GetResponseAsync();
|
||||
using (var responseStream = response.GetResponseStream())
|
||||
{
|
||||
using (var sr = new StreamReader(responseStream, Encoding.GetEncoding("utf-8")))
|
||||
{
|
||||
content = await sr.ReadToEndAsync();
|
||||
}
|
||||
}
|
||||
using var webResponse = (HttpWebResponse) await req.GetResponseAsync();
|
||||
using var memoryStream = new MemoryStream();
|
||||
using var input = webResponse.GetResponseStream();
|
||||
|
||||
response.Close();
|
||||
return content;
|
||||
await input.CopyToAsync(memoryStream);
|
||||
return memoryStream.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步下载并编码为字符串
|
||||
/// </summary>
|
||||
/// <param name="req"></param>
|
||||
/// <param name="encoding">编码,默认UTF-8</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="WebException"></exception>
|
||||
public static string DownloadString(HttpWebRequest req)
|
||||
public static async Task<string> DownloadStringAsync(HttpWebRequest req, string encoding = "UTF-8")
|
||||
{
|
||||
string content;
|
||||
var response = (HttpWebResponse) req.GetResponse();
|
||||
using (var responseStream = response.GetResponseStream())
|
||||
{
|
||||
using (var sr = new StreamReader(responseStream, Encoding.GetEncoding("utf-8")))
|
||||
{
|
||||
content = sr.ReadToEnd();
|
||||
}
|
||||
}
|
||||
using var webResponse = await req.GetResponseAsync();
|
||||
using var responseStream = webResponse.GetResponseStream();
|
||||
using var streamReader = new StreamReader(responseStream, Encoding.GetEncoding(encoding));
|
||||
|
||||
response.Close();
|
||||
return content;
|
||||
return await streamReader.ReadToEndAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步下载到文件
|
||||
/// </summary>
|
||||
/// <param name="req"></param>
|
||||
/// <param name="fileFullPath"></param>
|
||||
/// <exception cref="WebException"></exception>
|
||||
/// <returns></returns>
|
||||
public static async Task DownloadFileAsync(HttpWebRequest req, string fileFullPath)
|
||||
{
|
||||
var dir = Path.GetDirectoryName(fileFullPath) ?? throw new ArgumentException();
|
||||
if (!Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
File.WriteAllText(fileFullPath, await DownloadStringAsync(req));
|
||||
using var webResponse = (HttpWebResponse) await req.GetResponseAsync();
|
||||
using var input = webResponse.GetResponseStream();
|
||||
using var fileStream = File.OpenWrite(fileFullPath);
|
||||
|
||||
await input.CopyToAsync(fileStream);
|
||||
fileStream.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ namespace Netch.Utils
|
||||
{
|
||||
public static class i18N
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 数据
|
||||
/// </summary>
|
||||
@@ -50,6 +49,12 @@ namespace Netch.Utils
|
||||
// 从外置文件中加载语言
|
||||
text = File.ReadAllText($"i18n\\{langCode}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logging.Error($"无法找到语言 {langCode}, 使用系统语言");
|
||||
// 加载系统语言
|
||||
LangCode = CultureInfo.CurrentCulture.Name;
|
||||
}
|
||||
|
||||
var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(text);
|
||||
|
||||
|
||||
2
NetchUpdater/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/bin
|
||||
/obj
|
||||
59
NetchUpdater/NetchUpdater.csproj
Normal file
@@ -0,0 +1,59 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<StartupObject>NetchUpdater.Program</StartupObject>
|
||||
<Platforms>x64</Platforms>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<PackageProjectUrl>https://github.com/NetchX/Netch</PackageProjectUrl>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<OutputPath>bin\x64\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<NoWarn />
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<NoWarn />
|
||||
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
|
||||
<WarningsAsErrors />
|
||||
<OutputPath>bin\x64\Release\</OutputPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove=".gitignore" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.IO.Compression.FileSystem" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
289
NetchUpdater/Program.cs
Normal file
@@ -0,0 +1,289 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using NetchUpdater.Properties;
|
||||
|
||||
namespace NetchUpdater
|
||||
{
|
||||
internal class Program
|
||||
{
|
||||
static string UpdaterFullName;
|
||||
static string UpdaterDirectory;
|
||||
static string UpdaterFriendlyName;
|
||||
static Process CurrentProcess;
|
||||
|
||||
static Program()
|
||||
{
|
||||
CurrentProcess = Process.GetCurrentProcess();
|
||||
UpdaterFullName = CurrentProcess.MainModule.FileName;
|
||||
UpdaterDirectory = Path.GetDirectoryName(UpdaterFullName);
|
||||
UpdaterFriendlyName = Path.GetFileName(UpdaterFullName);
|
||||
}
|
||||
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var result = false;
|
||||
|
||||
try
|
||||
{
|
||||
#region Check Arguments
|
||||
|
||||
if (CurrentProcess.MainModule == null)
|
||||
{
|
||||
Console.WriteLine("Current Process MainModule is null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Length != 3)
|
||||
{
|
||||
Console.WriteLine("The program is not user-oriented\n此程序不是面向用户的");
|
||||
return;
|
||||
}
|
||||
|
||||
// arg0 port
|
||||
if (!int.TryParse(args[0], out var port))
|
||||
{
|
||||
Console.WriteLine("arg0 Port Parse failed");
|
||||
return;
|
||||
}
|
||||
|
||||
var updateExtracted = true;
|
||||
// arg1 update File/Directory
|
||||
var updatePath = Path.GetFullPath(args[1]);
|
||||
if (File.Exists(updatePath))
|
||||
{
|
||||
updateExtracted = false;
|
||||
}
|
||||
else if (!Directory.Exists(updatePath))
|
||||
{
|
||||
Console.WriteLine("arg1 update file/directory Not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// arg2 target Directory
|
||||
string targetPath;
|
||||
if (!File.Exists(Path.Combine(targetPath = Path.GetFullPath(args[2]), "Netch.exe")))
|
||||
{
|
||||
Console.Write("arg2 Netch Directory doesn't seems right");
|
||||
return;
|
||||
}
|
||||
|
||||
#region if under target Directory,then rerun in temp directory
|
||||
|
||||
if (UpdaterDirectory.StartsWith(targetPath))
|
||||
{
|
||||
// Updater 在目标目录下
|
||||
// 将程序复制到临时目录,传递参数
|
||||
var tempPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
|
||||
var newUpdaterPath = Path.Combine(tempPath, UpdaterFriendlyName);
|
||||
Directory.CreateDirectory(tempPath);
|
||||
File.Copy(UpdaterFullName, newUpdaterPath);
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = newUpdaterPath,
|
||||
Arguments = $"{port} {updatePath} {targetPath}",
|
||||
WorkingDirectory = tempPath,
|
||||
UseShellExecute = false
|
||||
});
|
||||
result = true;
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
/*while (!Debugger.IsAttached)
|
||||
{
|
||||
Console.WriteLine("Waiting Attach");
|
||||
Thread.Sleep(1000);
|
||||
}*/
|
||||
|
||||
#region Send Netch Exit command
|
||||
|
||||
Process[] _;
|
||||
if ((_ = Process.GetProcessesByName("Netch")).Any())
|
||||
{
|
||||
Console.WriteLine("Found Netch process, Send exit command");
|
||||
try
|
||||
{
|
||||
var udpClient = new UdpClient("127.0.0.1", port);
|
||||
var sendBytes = Encoding.ASCII.GetBytes("Exit");
|
||||
udpClient.Send(sendBytes, sendBytes.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Console.WriteLine("Send command failed");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var proc in _)
|
||||
{
|
||||
try
|
||||
{
|
||||
proc.WaitForExit();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
var counter = 0;
|
||||
while (!TestFileFree(Path.Combine(targetPath, "Netch.exe")))
|
||||
{
|
||||
// wait 5 sec
|
||||
if (counter > 25)
|
||||
{
|
||||
Console.WriteLine("Waiting Netch exit timeout");
|
||||
return;
|
||||
}
|
||||
|
||||
Thread.Sleep(200);
|
||||
counter++;
|
||||
}
|
||||
|
||||
#region Update
|
||||
|
||||
if (!updateExtracted)
|
||||
Extract(updatePath, targetPath, true);
|
||||
else
|
||||
MoveDirectory(updatePath, targetPath, true);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Finished Update,Start Netch
|
||||
|
||||
Console.WriteLine("Start Netch");
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = Path.Combine(targetPath, "Netch.exe"),
|
||||
UseShellExecute = true,
|
||||
});
|
||||
|
||||
#endregion
|
||||
|
||||
result = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InvalidDataException)
|
||||
Console.WriteLine("Archive file Broken");
|
||||
Console.WriteLine(e.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!result)
|
||||
{
|
||||
Console.WriteLine("Press any key to exit...");
|
||||
Console.Read();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Extract(string archiveFileName, string destDirName, bool overwrite)
|
||||
{
|
||||
archiveFileName = Path.GetFullPath(archiveFileName);
|
||||
destDirName = Path.GetFullPath(destDirName);
|
||||
|
||||
if (!File.Exists("7za.exe"))
|
||||
File.WriteAllBytes("7za.exe", Resources._7za);
|
||||
|
||||
var argument = new StringBuilder();
|
||||
argument.Append($" x {archiveFileName} -o{destDirName} ");
|
||||
if (overwrite)
|
||||
argument.Append(" -y ");
|
||||
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
UseShellExecute = false,
|
||||
WindowStyle = ProcessWindowStyle.Hidden,
|
||||
FileName = Path.GetFullPath("7za.exe"),
|
||||
Arguments = argument.ToString()
|
||||
})?.WaitForExit();
|
||||
}
|
||||
|
||||
private static void MoveDirectory(string sourceDirName, string destDirName, bool overwrite)
|
||||
{
|
||||
sourceDirName = Path.GetFullPath(sourceDirName);
|
||||
destDirName = Path.GetFullPath(destDirName);
|
||||
if (!overwrite)
|
||||
{
|
||||
Directory.Move(sourceDirName, destDirName);
|
||||
}
|
||||
else
|
||||
{
|
||||
var DirStack = new Stack<string>();
|
||||
DirStack.Push(sourceDirName);
|
||||
|
||||
while (DirStack.Count > 0)
|
||||
{
|
||||
var DirInfo = new DirectoryInfo(DirStack.Pop());
|
||||
try
|
||||
{
|
||||
foreach (var DirChildInfo in DirInfo.GetDirectories())
|
||||
{
|
||||
try
|
||||
{
|
||||
var destPath = DirChildInfo.ToString().Replace(sourceDirName, destDirName);
|
||||
if (!Directory.Exists(destPath))
|
||||
{
|
||||
Directory.CreateDirectory(destPath);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"Create {DirChildInfo} Folder Error: {e.Message}");
|
||||
}
|
||||
|
||||
DirStack.Push(DirChildInfo.FullName);
|
||||
}
|
||||
|
||||
foreach (var FileChildInfo in DirInfo.GetFiles())
|
||||
{
|
||||
try
|
||||
{
|
||||
var destPath = FileChildInfo.ToString().Replace(sourceDirName, destDirName);
|
||||
if (File.Exists(destPath))
|
||||
{
|
||||
File.Delete(destPath);
|
||||
}
|
||||
|
||||
FileChildInfo.MoveTo(destDirName);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"Move {FileChildInfo} Error: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"ERROR:{e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TestFileFree(string FileName)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Move(FileName, FileName);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
35
NetchUpdater/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("NetchUpdater")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("NetchUpdater")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2020")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("828318A8-9B90-4A5F-BD6B-E632CC9D8933")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
73
NetchUpdater/Properties/Resources.Designer.cs
generated
Normal file
@@ -0,0 +1,73 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 此代码由工具生成。
|
||||
// 运行时版本:4.0.30319.42000
|
||||
//
|
||||
// 对此文件的更改可能会导致不正确的行为,并且如果
|
||||
// 重新生成代码,这些更改将会丢失。
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace NetchUpdater.Properties {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 一个强类型的资源类,用于查找本地化的字符串等。
|
||||
/// </summary>
|
||||
// 此类是由 StronglyTypedResourceBuilder
|
||||
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
|
||||
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
|
||||
// (以 /str 作为命令选项),或重新生成 VS 项目。
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回此类使用的缓存的 ResourceManager 实例。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NetchUpdater.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重写当前线程的 CurrentUICulture 属性
|
||||
/// 重写当前线程的 CurrentUICulture 属性。
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找 System.Byte[] 类型的本地化资源。
|
||||
/// </summary>
|
||||
internal static byte[] _7za {
|
||||
get {
|
||||
object obj = ResourceManager.GetObject("7za", resourceCulture);
|
||||
return ((byte[])(obj));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
124
NetchUpdater/Properties/Resources.resx
Normal file
@@ -0,0 +1,124 @@
|
||||
<?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>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="7za" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>..\Resources\7za.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</data>
|
||||
</root>
|
||||
BIN
NetchUpdater/Resources/7za.exe
Normal file
@@ -17,6 +17,7 @@ Game accelerator
|
||||
- [Netch](#netch)
|
||||
- [TOC](#toc)
|
||||
- [Description](#description)
|
||||
- [Sponsor](#sponsor)
|
||||
- [Donate](#donate)
|
||||
- [Screenshots](#screenshots)
|
||||
- [Requirements](#requirements)
|
||||
@@ -27,6 +28,13 @@ Netch is an open source game accelerator. Unlike SSTap, which needs to add rules
|
||||
|
||||
As well, Netch avoid the restricted NAT problem caused by SSTap. You can use an NATTypeTester to test out what your NAT type is. When using SSTap to speed up some P2P gaming connections or the game is required for that kind of open NAT type, you may experience some bad situations such as unable to join the game.
|
||||
|
||||
## Sponsor
|
||||
- [RabbitHosts](https://rabbithosts.com/cart.php)
|
||||
- [ManSora](https://www.mansora.co/cart.php)
|
||||
- [ExCloud](https://excloud.net/cart.php)
|
||||
- [NyanCat](https://nyancat.info/register)
|
||||
- [YoYu](https://home.yoyu.ltd/cart.php)
|
||||
|
||||
## Donate
|
||||
- XMR *48ju3ELNZEa6wwPBMexCJ9G218BGY2XwhH6B6bmkFuJ3QgM4hPw2Pra35jPtuBZSc7SLNWeBpiWJZWjQeMAiLnTx2tH2Efx*
|
||||
|
||||
|
||||
2
binaries
@@ -1,9 +1,5 @@
|
||||
# Netch 模式
|
||||
|
||||
用于存储 Netch 模式文件的仓库
|
||||
|
||||
|
||||
|
||||
## 目录
|
||||
|
||||
1. [模式介绍](#模式介绍)
|
||||
@@ -41,9 +37,7 @@
|
||||
- 底层依赖于 [NetFilter SDK](https://netfiltersdk.com) 和 Redirector.exe(未开源)等
|
||||
- 对于第一次使用 Netch 的用户而言,不需要做多余的事情
|
||||
- 若 [NetFilter SDK](https://netfiltersdk.com) 的驱动不存在,会自动安装
|
||||
- 自动安装驱动时不会判断旧驱动是否需要更新
|
||||
- 对于老用户而言,版本更新日志里如果提到要更新驱动,或者你发现无法使用本模式时,可以通过运行 `DriverUpdater.exe` 的方式强制覆盖旧驱动
|
||||
- 相关代码 [NFController.cs](..\Netch\Controllers\NFController.cs)
|
||||
- 若驱动版本过低,会自动更新
|
||||
|
||||
范例文件
|
||||
|
||||
@@ -159,7 +153,7 @@ V2Ray
|
||||
|
||||
当前版本已添加配置编辑功能,根据自己的情况,使用订阅或者别的方法添加代理配置,我这里使用的是剪贴板导入
|
||||
|
||||

|
||||

|
||||
|
||||
如果你发现你的程序没我截图的看起来清晰,可以右键 `Netch.exe - 属性 - 兼容性 - 更改高 DPI 设置 - 替代高 DPI 缩放执行 - 系统(增强)`
|
||||
|
||||
@@ -173,24 +167,24 @@ ping 的值未必准确,因为这只是你本地到代理服务器而非游戏
|
||||
|
||||
接着点击菜单栏上的`模式 - 创建进程模式`
|
||||
|
||||

|
||||

|
||||
|
||||
### 扫描
|
||||
|
||||
在弹出的窗口中点击`扫描`
|
||||
|
||||

|
||||

|
||||
|
||||
选择你要加速的游戏的安装路径,根据游戏不同,可能需要选择多个不同的目录进行扫描,参见[萌鹰的 Netch 教程](https://www.eaglemoe.com/archives/142)(包括 GTAOL 和 R6S 的配置方法)
|
||||
|
||||
>4. 选定 GTA5 游戏目录,点击确定,软件会自动扫描目录下的 exe 程式并填写进去
|
||||
>5. 再次点击扫描,选择 SocialClub 的安装地址(一般为 C:\Program Files\Rockstar Games\Social Club),点击确定,点击保存
|
||||
>
|
||||
>注意:加入游戏时请不要忘记加入社交组件,比如说 GTA 不要忘记 SocialClub ,彩虹六号不要忘记 Uplay
|
||||
>注意:加入游戏时请不要忘记加入社交组件,比如说 GTA 不要忘记 SocialClub ,彩虹六号不要忘记 Uplay,如果游戏进程名与其他进程名重复,则可手动修改已创建好的模式文件,在进程名前加上绝对路径即可。csgo.exe -> C:\steam\game\Counter-Strike Global Offensive\csgo.exe
|
||||
|
||||
这里以战争雷霆为例,只需添加战争雷霆游戏根目录即可
|
||||
这里以CSGO为例,只需添加CSGO游戏根目录即可
|
||||
|
||||

|
||||

|
||||
|
||||
扫描时可能需要稍等片刻,扫描后记得填写备注
|
||||
|
||||
@@ -198,13 +192,13 @@ ping 的值未必准确,因为这只是你本地到代理服务器而非游戏
|
||||
|
||||
之后点保存进行`保存`
|
||||
|
||||

|
||||

|
||||
|
||||
### 启动
|
||||
|
||||
最后确认服务器一栏和模式一栏均为之前自己添加并需要使用的,没问题后点击`启动`即可
|
||||
|
||||

|
||||

|
||||
|
||||
启动后,你再去游戏根目录或者别的启动器如 Steam,Uplay 启动游戏即可。此时游戏就已经被代理了
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 新手入门
|
||||
**Version : 1.4.10**
|
||||
**Version : 1.5.1**
|
||||
|
||||
[下载地址](https://github.com/NetchX/Netch/releases)
|
||||
|
||||
@@ -7,36 +7,42 @@
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 设置界面
|
||||
|
||||

|
||||
|
||||
## 添加服务器
|
||||
|
||||
> Netch 目前仅支持以下代理协议:Shadowsocks,VMess,Socks5,ShadowsockR,Trojan。
|
||||
|
||||
首先,点击`服务器`增加所需服务器
|
||||
|
||||
<img width="50%" height="50%" src="screenshots/addServer.zh-CN.png">
|
||||
<img width="80%" height="80%" src="screenshots/addServer.zh-CN.png">
|
||||
|
||||
可手动添加单个服务器,或者通过剪切板链接添加单个服务器。也可通过订阅链接批量添加。
|
||||
|
||||
点击 `订阅` ` 管理订阅链接` 进入以下界面。
|
||||
|
||||
<img width="50%" height="50%" src="screenshots/addLink.zh-CN.png">
|
||||
<img width="80%" height="80%" src="screenshots/addLink.zh-CN.png">
|
||||
|
||||
填写备注与链接,点击添加,然后保存。保存后点击 `订阅` ` 从订阅链接更新服务器`。完成服务器添加。添加完服务器后可对服务器进行修改,删除和测速。
|
||||
填写备注与链接,然后保存即可。保存后在主界面点击 `订阅` ` 从订阅链接更新服务器`。完成服务器添加。添加完服务器后可对服务器进行修改,删除和测速。
|
||||
|
||||
## 选择模式
|
||||
|
||||
> 此处需要会一点英语,比如你应该知道 `吃鸡` 的英文名称是 `PlayerUnknown's Battlegrounds`
|
||||
|
||||
1.3.7 上线了模式搜索功能,即在模式框里输入字符即可搜索,使用英文名称进行搜索,搜索到所需的模式后单击选择,启用模式。相对应的游戏即可被加速
|
||||
模式选择框有搜索功能,在模式框里输入字符即可搜索,使用英文名称进行搜索,搜索到所需的模式后单击选择,启用模式。相对应的游戏即可被加速
|
||||
|
||||
若没有所需的模式,请选择 `[3] Bypass LAN and China (TUN/TAP)` 的模式。此模式需要安装 [TAP-Windows](https://github.com/OpenVPN/tap-windows) 适配器,如果 Netch 提示没有该适配器,可以直接安装 [TAP-Windows](https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe) 来获得该适配器
|
||||
|
||||
关于更多的模式说明,详见 [进阶用法](https://github.com/NormanBB/NetchMode/blob/master/docs/README.zh-CN.md)。
|
||||
关于更多的模式说明,详见 [进阶用法](Advanced_Usage.zh-CN.md)。
|
||||
|
||||
选择完模式后,点击启用,游戏已被代理。这一步需在开启游戏前完成。
|
||||
|
||||
## 配置说明
|
||||
|
||||
目前,Netch 支持自定义四个配置,填入配置名,选择相应的服务器和游戏模式,按下 `Ctrl` 与鼠标左键,即可保存当前配置。下次使用时,点击配置名即可快速启用。
|
||||
在设置界面填写完快捷配置数量后即可在主界面进行配置,填入配置名,选择相应的服务器和游戏模式,按下 `Ctrl` 与鼠标左键,即可保存当前配置。下次使用时,点击配置名即可快速启用。
|
||||
|
||||
~~ 如果你还觉得不会用,可以去用 SSTap (逃~~
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
- [Netch](#Netch)
|
||||
- [TOC](#TOC)
|
||||
- [简介](#简介)
|
||||
- [赞助商](#赞助商)
|
||||
- [捐赠](#捐赠)
|
||||
- [新手入门](Quickstart.zh-CN.md)
|
||||
- [进阶用法](Advanced_Usage.zh-CN.md)
|
||||
@@ -23,6 +24,13 @@ Netch 是一款 Windows 平台的开源游戏加速工具,Netch 可以实现
|
||||
|
||||
需要更多特性请移步魔改仓库 [Netch-ForOwnUse](https://github.com/AmazingDM/Netch-ForOwnUse),
|
||||
|
||||
## 赞助商
|
||||
- [RabbitHosts](https://rabbithosts.com/cart.php)
|
||||
- [ManSora](https://www.mansora.co/cart.php)
|
||||
- [ExCloud](https://excloud.net/cart.php)
|
||||
- [NyanCat](https://nyancat.info/register)
|
||||
- [YoYu](https://home.yoyu.ltd/cart.php)
|
||||
|
||||
## 捐赠
|
||||
- XMR *48ju3ELNZEa6wwPBMexCJ9G218BGY2XwhH6B6bmkFuJ3QgM4hPw2Pra35jPtuBZSc7SLNWeBpiWJZWjQeMAiLnTx2tH2Efx*
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 23 KiB |
BIN
docs/screenshots/advanced/createMode.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
docs/screenshots/advanced/importServer.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/screenshots/advanced/modeForm.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
docs/screenshots/advanced/saveMode.png
Normal file
|
After Width: | Height: | Size: 9.3 KiB |
BIN
docs/screenshots/advanced/scan.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
docs/screenshots/advanced/started.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 21 KiB |
BIN
docs/screenshots/main.zh-CN2.png
Normal file
|
After Width: | Height: | Size: 51 KiB |
BIN
docs/screenshots/set.png
Normal file
|
After Width: | Height: | Size: 64 KiB |