diff --git a/shadowsocks-csharp/Controller/I18N.cs b/shadowsocks-csharp/Controller/I18N.cs
index eb948c39..5111a777 100755
--- a/shadowsocks-csharp/Controller/I18N.cs
+++ b/shadowsocks-csharp/Controller/I18N.cs
@@ -108,12 +108,12 @@ namespace Shadowsocks.Controller
if (item == null) continue;
item.Text = GetString(item.Text);
}
- TranslateMenu(c.Menu);
+ TranslateMenu(c.MainMenuStrip);
}
- public static void TranslateMenu(Menu m)
+ public static void TranslateMenu(MenuStrip m)
{
if (m == null) return;
- foreach (var item in ViewUtils.GetMenuItems(m))
+ foreach (var item in ViewUtils.GetToolStripMenuItems(m))
{
if (item == null) continue;
item.Text = GetString(item.Text);
diff --git a/shadowsocks-csharp/Controller/Service/PACServer.cs b/shadowsocks-csharp/Controller/Service/PACServer.cs
index 7fd9250e..ee5ed89e 100644
--- a/shadowsocks-csharp/Controller/Service/PACServer.cs
+++ b/shadowsocks-csharp/Controller/Service/PACServer.cs
@@ -24,7 +24,7 @@ namespace Shadowsocks.Controller
{
var rd = new byte[32];
RNG.GetBytes(rd);
- _cachedPacSecret = HttpServerUtility.UrlTokenEncode(rd);
+ _cachedPacSecret = HttpServerUtilityUrlToken.Encode(rd);
}
return _cachedPacSecret;
}
@@ -51,7 +51,7 @@ namespace Shadowsocks.Controller
private static string GetHash(string content)
{
- return HttpServerUtility.UrlTokenEncode(MbedTLS.MD5(Encoding.ASCII.GetBytes(content)));
+ return HttpServerUtilityUrlToken.Encode(MbedTLS.MD5(Encoding.ASCII.GetBytes(content)));
}
public override bool Handle(byte[] firstPacket, int length, Socket socket, object state)
diff --git a/shadowsocks-csharp/HttpServerUtilityUrlToken.cs b/shadowsocks-csharp/HttpServerUtilityUrlToken.cs
new file mode 100644
index 00000000..617e74e9
--- /dev/null
+++ b/shadowsocks-csharp/HttpServerUtilityUrlToken.cs
@@ -0,0 +1,144 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Shadowsocks
+{
+ ///
+ /// HttpServerUtility URL Token のエンコード及びデコードを行うクラス。
+ /// https://docs.microsoft.com/ja-jp/dotnet/api/system.web.httpserverutility.urltokenencode
+ /// https://docs.microsoft.com/ja-jp/dotnet/api/system.web.httpserverutility.urltokendecode
+ ///
+ ///
+ /// HttpServerUtility URL Token 形式は、パディング無し base64url にパディング数を文字として追記した文字列です。
+ /// 例えば、0x00 は AA2 になります。
+ ///
+ public static class HttpServerUtilityUrlToken
+ {
+#if NETSTANDARD2_0
+ private static readonly byte[] EmptyBytes = Array.Empty();
+#else
+ private static readonly byte[] EmptyBytes = new byte[0];
+#endif
+
+ ///
+ /// 配列を HttpServerUtility URL Token にエンコードします。
+ ///
+ /// エンコード対象の 配列。
+ /// HttpServerUtility URL Token エンコード文字列。 の長さが 0 の場合は空文字列を返します。
+ /// is null.
+ public static string Encode(byte[] bytes)
+ {
+ if (bytes == null) { throw new ArgumentNullException(nameof(bytes)); }
+
+ return Encode(bytes, 0, bytes.Length);
+ }
+
+ ///
+ /// 配列を HttpServerUtility URL Token にエンコードします。
+ ///
+ /// エンコード対象の 配列。
+ /// エンコードの開始位置を示すオフセット。
+ /// エンコード対象の要素の数。
+ /// HttpServerUtility URL Token エンコード文字列。 が 0 の場合は空文字列を返します。
+ /// is null.
+ ///
+ /// または が負の値です。
+ /// または と を加算した値が の長さを超えています。
+ ///
+ public static string Encode(byte[] bytes, int offset, int length)
+ {
+ if (bytes == null) { throw new ArgumentNullException(nameof(bytes)); }
+
+ var encoded = Encode(bytes, offset, length, padding: false);
+ if (encoded.Length == 0) { return ""; }
+
+ var paddingLen = unchecked(~encoded.Length + 1) & 0b11;
+ encoded += paddingLen;
+
+ return encoded;
+ }
+ ///
+ /// 配列を base64url にエンコードします。
+ ///
+ /// エンコード対象の 配列。
+ /// エンコードの開始位置を示すオフセット。
+ /// エンコード対象の要素の数。
+ /// パディングをする場合は true、それ以外は false。既定値は false。
+ /// base64url エンコード文字列。
+ /// is null.
+ ///
+ /// または が負の値です。
+ /// または と を加算した値が の長さを超えています。
+ ///
+ public static string Encode(byte[] bytes, int offset, int length, bool padding = false)
+ {
+ var encoded = Convert.ToBase64String(bytes, offset, length);
+
+ if (!padding)
+ {
+ encoded = encoded.TrimEnd('=');
+ }
+
+ return encoded
+ .Replace('+', '-')
+ .Replace('/', '_')
+ ;
+ }
+
+ ///
+ /// HttpServerUtility URL Token 文字列を 配列にデコードします。
+ ///
+ /// HttpServerUtility URL Token にエンコードされた文字列。
+ /// デコード後の 配列。 が空文字列の場合は の空配列を返します。
+ /// is null.
+ /// が HttpServerUtility URL Token 文字列ではありません。
+ public static byte[] Decode(string encoded)
+ {
+ if (encoded == null) { throw new ArgumentNullException(nameof(encoded)); }
+
+ if (!TryDecode(encoded, out var result)) { throw new FormatException("HttpServerUtility URL Token 文字列ではありません。"); }
+ return result;
+ }
+
+ ///
+ /// HttpServerUtility URL Token でエンコードされた文字列をデコードします。
+ ///
+ /// HttpServerUtility URL Token エンコードされた文字列。
+ /// デコード後の 配列。 が空文字列の場合は の空配列が設定されます。失敗した場合は null。
+ /// デコードに成功した場合は true、それ以外は false。
+ public static bool TryDecode(string encoded, out byte[] result)
+ {
+ if (encoded == null) { goto Failure; }
+ if (encoded.Length == 0)
+ {
+ result = EmptyBytes;
+ return true;
+ }
+
+ var paddingLen = encoded[encoded.Length - 1] - '0';
+ if (paddingLen < 0 || paddingLen > 3) { goto Failure; }
+
+ var base64Str = encoded
+ .Substring(0, encoded.Length - 1)
+ .Replace('-', '+')
+ .Replace('_', '/');
+
+ if (paddingLen > 0)
+ {
+ base64Str += new string('=', paddingLen);
+ }
+
+ try
+ {
+ result = Convert.FromBase64String(base64Str);
+ return true;
+ }
+ catch (FormatException) { goto Failure; }
+
+ Failure:
+ result = null;
+ return false;
+ }
+ }
+}
diff --git a/shadowsocks-csharp/Properties/AssemblyInfo.cs b/shadowsocks-csharp/Properties/AssemblyInfo.cs
deleted file mode 100755
index b140accf..00000000
--- a/shadowsocks-csharp/Properties/AssemblyInfo.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using Shadowsocks.Controller;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-
-// 有关程序集的常规信息通过下列属性集
-// 控制。更改这些属性值可修改
-// 与程序集关联的信息。
-[assembly: AssemblyTitle("Shadowsocks")]
-[assembly: AssemblyDescription("")]
-[assembly: AssemblyConfiguration("")]
-[assembly: AssemblyCompany("")]
-[assembly: AssemblyProduct("Shadowsocks")]
-[assembly: AssemblyCopyright("clowwindy & community 2020")]
-[assembly: AssemblyTrademark("")]
-[assembly: AssemblyCulture("")]
-
-// 将 ComVisible 设置为 false 使此程序集中的类型
-// 对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型,
-// 则将该类型上的 ComVisible 属性设置为 true。
-[assembly: ComVisible(false)]
-
-// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
-[assembly: Guid("f8334709-4309-436a-8bbd-6165dcf4a660")]
-
-// 程序集的版本信息由下面四个值组成:
-//
-// 主版本
-// 次版本
-// 内部版本号
-// 修订号
-//
-// 可以指定所有这些值,也可以使用“内部版本号”和“修订号”的默认值,
-// 方法是按如下所示使用“*”:
-// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion(UpdateChecker.Version)]
-// [assembly: AssemblyFileVersion("2.0.0")]
diff --git a/shadowsocks-csharp/Properties/PublishProfiles/FolderProfile.pubxml b/shadowsocks-csharp/Properties/PublishProfiles/FolderProfile.pubxml
new file mode 100644
index 00000000..0d3d74e9
--- /dev/null
+++ b/shadowsocks-csharp/Properties/PublishProfiles/FolderProfile.pubxml
@@ -0,0 +1,17 @@
+
+
+
+
+ FileSystem
+ Release
+ Any CPU
+ netcoreapp3.1
+ bin\Release\netcoreapp3.1\publish\
+ win-x86
+ false
+ False
+ False
+
+
\ No newline at end of file
diff --git a/shadowsocks-csharp/Properties/PublishProfiles/FolderProfile.pubxml.user b/shadowsocks-csharp/Properties/PublishProfiles/FolderProfile.pubxml.user
new file mode 100644
index 00000000..1a189e4f
--- /dev/null
+++ b/shadowsocks-csharp/Properties/PublishProfiles/FolderProfile.pubxml.user
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/shadowsocks-csharp/Properties/Settings.Designer.cs b/shadowsocks-csharp/Properties/Settings.Designer.cs
index efed8372..4cc88b29 100644
--- a/shadowsocks-csharp/Properties/Settings.Designer.cs
+++ b/shadowsocks-csharp/Properties/Settings.Designer.cs
@@ -1,10 +1,10 @@
//------------------------------------------------------------------------------
//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
+// 此代码由工具生成。
+// 运行时版本:4.0.30319.42000
//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
+// 对此文件的更改可能会导致不正确的行为,并且如果
+// 重新生成代码,这些更改将会丢失。
//
//------------------------------------------------------------------------------
@@ -12,7 +12,7 @@ namespace Shadowsocks.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.1.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
diff --git a/shadowsocks-csharp/Util/ViewUtils.cs b/shadowsocks-csharp/Util/ViewUtils.cs
index 43f54721..a29d790a 100644
--- a/shadowsocks-csharp/Util/ViewUtils.cs
+++ b/shadowsocks-csharp/Util/ViewUtils.cs
@@ -22,15 +22,25 @@ namespace Shadowsocks.Util
return children.SelectMany(GetChildControls).Concat(children);
}
- public static IEnumerable