mirror of
https://github.com/netchx/netch.git
synced 2026-04-03 19:35:10 +08:00
Refactor: ModeHelper, Mode.Type, Netch.Enums.ModeType
This commit is contained in:
@@ -4,138 +4,126 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Netch.Enums;
|
||||
|
||||
namespace Netch.Models
|
||||
{
|
||||
public class Mode
|
||||
{
|
||||
private readonly Lazy<List<string>> _lazyRule;
|
||||
|
||||
public string? FullName { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="fullName">Mode File FullPath</param>
|
||||
/// <exception cref="FormatException"></exception>
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
public Mode(string? fullName)
|
||||
{
|
||||
_lazyRule = new Lazy<List<string>>(ReadRules);
|
||||
if (fullName == null)
|
||||
return;
|
||||
|
||||
FullName = fullName;
|
||||
if (!File.Exists(FullName))
|
||||
if (FullName == null || !File.Exists(FullName))
|
||||
return;
|
||||
|
||||
var text = File.ReadLines(FullName).First();
|
||||
|
||||
// load head
|
||||
if (text.First() != '#')
|
||||
throw new Exception($"mode {FullName} head not found at Line 0");
|
||||
|
||||
var split = text.Substring(1).SplitTrimEntries(',');
|
||||
Remark = split[0];
|
||||
|
||||
var typeResult = int.TryParse(split.ElementAtOrDefault(1), out var type);
|
||||
Type = typeResult ? type : 0;
|
||||
if (!ModeHelper.ModeTypes.Contains(Type))
|
||||
throw new NotSupportedException($"not support mode \"[{Type}]{Remark}\".");
|
||||
(Remark, Type) = ReadHead(FullName);
|
||||
}
|
||||
|
||||
public string? FullName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 规则
|
||||
/// </summary>
|
||||
public List<string> Rule => _lazyRule.Value;
|
||||
public List<string> Content => _content ??= ReadContent();
|
||||
|
||||
private List<string>? _content;
|
||||
|
||||
/// <summary>
|
||||
/// 备注
|
||||
/// </summary>
|
||||
public string Remark { get; set; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// 类型
|
||||
/// <para />
|
||||
/// 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 />
|
||||
/// </summary>
|
||||
public int Type { get; set; } = 0;
|
||||
public ModeType Type { get; set; } = ModeType.Process;
|
||||
|
||||
/// <summary>
|
||||
/// 文件相对路径(必须是存在的文件)
|
||||
/// </summary>
|
||||
public string? RelativePath => FullName == null ? null : ModeHelper.GetRelativePath(FullName);
|
||||
|
||||
public List<string> FullRule
|
||||
public IEnumerable<string> GetRules()
|
||||
{
|
||||
get
|
||||
var result = new List<string>();
|
||||
foreach (var s in Content)
|
||||
{
|
||||
var result = new List<string>();
|
||||
foreach (var s in Rule)
|
||||
if (string.IsNullOrWhiteSpace(s))
|
||||
continue;
|
||||
|
||||
if (s.StartsWith("//"))
|
||||
continue;
|
||||
|
||||
const string include = "#include";
|
||||
if (s.StartsWith(include))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(s))
|
||||
continue;
|
||||
var relativePath = new StringBuilder(s[include.Length..].Trim());
|
||||
relativePath.Replace("<", "").Replace(">", "");
|
||||
relativePath.Replace(".h", ".txt");
|
||||
|
||||
if (s.StartsWith("//"))
|
||||
continue;
|
||||
var mode = Global.Modes.FirstOrDefault(m => m.RelativePath?.Equals(relativePath.ToString()) ?? false) ??
|
||||
throw new MessageException($"{relativePath} file included in {Remark} not found");
|
||||
|
||||
if (s.StartsWith("#include"))
|
||||
{
|
||||
var relativePath = new StringBuilder(s.Substring(8).Trim());
|
||||
relativePath.Replace("<", "");
|
||||
relativePath.Replace(">", "");
|
||||
relativePath.Replace(".h", ".txt");
|
||||
if (mode == this)
|
||||
throw new MessageException("Can't self-reference");
|
||||
|
||||
var mode = Global.Modes.FirstOrDefault(m => m.FullName != null && m.RelativePath!.Equals(relativePath.ToString()));
|
||||
if (mode.Type != Type)
|
||||
throw new MessageException($"{mode.Remark}'s mode is not as same as {Remark}'s mode");
|
||||
|
||||
if (mode == null)
|
||||
throw new MessageException($"{relativePath} file included in {Remark} not found");
|
||||
if (mode.Content.Any(rule => rule.StartsWith(include)))
|
||||
throw new Exception("Cannot reference mode that reference other mode");
|
||||
|
||||
if (mode == this)
|
||||
throw new MessageException("Can't self-reference");
|
||||
|
||||
if (mode.Type != Type)
|
||||
throw new MessageException($"{mode.Remark}'s mode is not as same as {Remark}'s mode");
|
||||
|
||||
if (mode.Rule.Any(rule => rule.StartsWith("#include")))
|
||||
throw new Exception("Cannot reference mode that reference other mode");
|
||||
|
||||
result.AddRange(mode.FullRule);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(s);
|
||||
}
|
||||
result.AddRange(mode.GetRules());
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(s);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<string> ReadRules()
|
||||
private static (string, ModeType) ReadHead(string fileName)
|
||||
{
|
||||
var text = File.ReadLines(fileName).First();
|
||||
if (text.First() != '#')
|
||||
throw new FormatException($"{fileName} head not found at Line 0");
|
||||
|
||||
var split = text[1..].SplitTrimEntries(',');
|
||||
|
||||
var typeNumber = int.TryParse(split.ElementAtOrDefault(1), out var type) ? type : 0;
|
||||
if (!Enum.GetValues(typeof(ModeType)).Cast<int>().Contains(typeNumber))
|
||||
throw new NotSupportedException($"Not support mode \"{typeNumber}\".");
|
||||
|
||||
return (split[0], (ModeType)typeNumber);
|
||||
}
|
||||
|
||||
private List<string> ReadContent()
|
||||
{
|
||||
if (FullName == null || !File.Exists(FullName))
|
||||
return new List<string>();
|
||||
|
||||
return File.ReadLines(FullName!).Skip(1).ToList();
|
||||
return File.ReadLines(FullName).Skip(1).ToList();
|
||||
}
|
||||
|
||||
public void WriteFile(string? fullName = null)
|
||||
public void ResetContent()
|
||||
{
|
||||
if (fullName != null)
|
||||
throw new NotImplementedException();
|
||||
_content = null;
|
||||
}
|
||||
|
||||
public void WriteFile()
|
||||
{
|
||||
var dir = Path.GetDirectoryName(FullName)!;
|
||||
if (!Directory.Exists(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
var content = $"# {Remark}, {(int)Type}{Constants.EOF}{string.Join(Constants.EOF, Content)}";
|
||||
// 写入到模式文件里
|
||||
File.WriteAllText(FullName!, ToFileString());
|
||||
File.WriteAllText(FullName!, content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -144,16 +132,7 @@ namespace Netch.Models
|
||||
/// <returns>备注</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"[{Type + 1}] {i18N.Translate(Remark)}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取模式文件字符串
|
||||
/// </summary>
|
||||
/// <returns>模式文件字符串</returns>
|
||||
public string ToFileString()
|
||||
{
|
||||
return $"# {Remark}, {Type}{Constants.EOF}{string.Join(Constants.EOF, Rule)}";
|
||||
return $"[{(int)Type + 1}] {i18N.Translate(Remark)}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +141,7 @@ namespace Netch.Models
|
||||
/// 是否会转发 UDP
|
||||
public static bool TestNatRequired(this Mode mode)
|
||||
{
|
||||
return mode.Type is 0 or 2;
|
||||
return mode.Type is ModeType.Process or ModeType.BypassRuleIPs;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user