@@ -18,10 +18,9 @@ namespace Shadowsocks.Controller | |||
{ | |||
using (var sr = new StringReader(Resources.cn)) | |||
{ | |||
string line; | |||
while ((line = sr.ReadLine()) != null) | |||
foreach (var line in sr.NonWhiteSpaceLines()) | |||
{ | |||
if (line == "" || line[0] == '#') | |||
if (line[0] == '#') | |||
continue; | |||
var pos = line.IndexOf('='); | |||
@@ -247,7 +247,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
var currentHour = DateTime.Now.Hour; | |||
filteredData = filteredData.Where(data => | |||
data.Timestamp != UnknownDateTime && data.Timestamp.Hour.Equals(currentHour) | |||
data.Timestamp != UnknownDateTime && data.Timestamp.Hour == currentHour | |||
); | |||
if (filteredData.LongCount() == 0) return; | |||
} | |||
@@ -30,6 +30,7 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
private static readonly IEnumerable<char> IgnoredLineBegins = new[] { '!', '[' }; | |||
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) | |||
{ | |||
try | |||
@@ -41,10 +42,9 @@ namespace Shadowsocks.Controller | |||
string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8); | |||
using (var sr = new StringReader(local)) | |||
{ | |||
string rule; | |||
while ((rule = sr.ReadLine()) != null) | |||
foreach (var rule in sr.NonWhiteSpaceLines()) | |||
{ | |||
if (rule == "" || rule[0] == '!' || rule[0] == '[') | |||
if (rule.BeginWithAny(IgnoredLineBegins)) | |||
continue; | |||
lines.Add(rule); | |||
} | |||
@@ -99,10 +99,9 @@ namespace Shadowsocks.Controller | |||
List<string> valid_lines = new List<string>(); | |||
using (var sr = new StringReader(content)) | |||
{ | |||
string line; | |||
while ((line = sr.ReadLine()) != null) | |||
foreach (var line in sr.NonWhiteSpaceLines()) | |||
{ | |||
if (line == "" || line[0] == '!' || line[0] == '[') | |||
if (line.BeginWithAny(IgnoredLineBegins)) | |||
continue; | |||
valid_lines.Add(line); | |||
} | |||
@@ -442,6 +442,7 @@ namespace Shadowsocks.Controller | |||
UpdatePACFromGFWListError(this, e); | |||
} | |||
private static readonly IEnumerable<char> IgnoredLineBegins = new[] { '!', '[' }; | |||
private void pacServer_UserRuleFileChanged(object sender, EventArgs e) | |||
{ | |||
// TODO: this is a dirty hack. (from code GListUpdater.http_DownloadStringCompleted()) | |||
@@ -456,10 +457,9 @@ namespace Shadowsocks.Controller | |||
string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8); | |||
using (var sr = new StringReader(local)) | |||
{ | |||
string rule; | |||
while ((rule = sr.ReadLine()) != null) | |||
foreach (var rule in sr.NonWhiteSpaceLines()) | |||
{ | |||
if (rule == "" || rule[0] == '!' || rule[0] == '[') | |||
if (rule.BeginWithAny(IgnoredLineBegins)) | |||
continue; | |||
lines.Add(rule); | |||
} | |||
@@ -60,7 +60,7 @@ namespace Shadowsocks.Controller | |||
else | |||
{ | |||
string pacUrl; | |||
if (config.useOnlinePac && !string.IsNullOrEmpty(config.pacUrl)) | |||
if (config.useOnlinePac && !config.pacUrl.IsNullOrEmpty()) | |||
pacUrl = config.pacUrl; | |||
else | |||
pacUrl = $"http://127.0.0.1:{config.localPort}/pac?t={GetTimestamp(DateTime.Now)}"; | |||
@@ -25,7 +25,7 @@ namespace Shadowsocks.Encryption | |||
public static IEncryptor GetEncryptor(string method, string password, bool onetimeauth, bool isudp) | |||
{ | |||
if (string.IsNullOrEmpty(method)) | |||
if (method.IsNullOrEmpty()) | |||
{ | |||
method = "aes-256-cfb"; | |||
} | |||
@@ -1,5 +1,5 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<Weavers> | |||
<Costura /> | |||
<Caseless StringComparison="Ordinal" /> | |||
<Caseless StringComparison="Ordinal"/> | |||
<Costura/> | |||
</Weavers> |
@@ -124,13 +124,13 @@ namespace Shadowsocks.Model | |||
private static void CheckPassword(string password) | |||
{ | |||
if (string.IsNullOrEmpty(password)) | |||
if (password.IsNullOrEmpty()) | |||
throw new ArgumentException(I18N.GetString("Password can not be blank")); | |||
} | |||
private static void CheckServer(string server) | |||
{ | |||
if (string.IsNullOrEmpty(server)) | |||
if (server.IsNullOrEmpty()) | |||
throw new ArgumentException(I18N.GetString("Server IP can not be blank")); | |||
} | |||
} | |||
@@ -29,11 +29,11 @@ namespace Shadowsocks.Model | |||
public string FriendlyName() | |||
{ | |||
if (string.IsNullOrEmpty(server)) | |||
if (server.IsNullOrEmpty()) | |||
{ | |||
return I18N.GetString("New server"); | |||
} | |||
if (string.IsNullOrEmpty(remarks)) | |||
if (remarks.IsNullOrEmpty()) | |||
{ | |||
return server + ":" + server_port; | |||
} | |||
@@ -0,0 +1,237 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Globalization; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Text; | |||
static partial class StringEx | |||
{ | |||
#pragma warning disable 1591 | |||
public static StringComparison GlobalDefaultComparison { get; set; } = StringComparison.Ordinal; | |||
[ThreadStatic] | |||
private static StringComparison? _DefaultComparison; | |||
public static StringComparison DefaultComparison | |||
{ | |||
get { return _DefaultComparison ?? GlobalDefaultComparison; } | |||
set { _DefaultComparison = value; } | |||
} | |||
#region basic String methods | |||
public static bool IsNullOrEmpty(this string value) | |||
=> string.IsNullOrEmpty(value); | |||
public static bool IsNullOrWhiteSpace(this string value) | |||
=> string.IsNullOrWhiteSpace(value); | |||
public static bool IsWhiteSpace(this string value) | |||
{ | |||
foreach(var c in value) | |||
{ | |||
if (char.IsWhiteSpace(c)) continue; | |||
return false; | |||
} | |||
return true; | |||
} | |||
#if !PCL | |||
public static string IsInterned(this string value) | |||
{ | |||
if (value == null) | |||
throw new ArgumentNullException(nameof(value)); | |||
return string.IsInterned(value); | |||
} | |||
public static string Intern(this string value) | |||
{ | |||
if (value == null) | |||
throw new ArgumentNullException(nameof(value)); | |||
return string.Intern(value); | |||
} | |||
#endif | |||
#endregion | |||
#region comparing | |||
#region Is | |||
public static bool Is(this string a, string b) | |||
=> string.Equals(a, b, DefaultComparison); | |||
public static bool Is(this string a, string b, StringComparison comparisonType) | |||
=> string.Equals(a, b, comparisonType); | |||
#endregion | |||
#region BeginWith | |||
public static bool BeginWith(this string s, char c) | |||
{ | |||
if (s.IsNullOrEmpty()) return false; | |||
return s[0] == c; | |||
} | |||
public static bool BeginWithAny(this string s, IEnumerable<char> chars) | |||
{ | |||
if (s.IsNullOrEmpty()) return false; | |||
return chars.Contains(s[0]); | |||
} | |||
public static bool BeginWithAny(this string s, params char[] chars) | |||
=> s.BeginWithAny(chars.AsEnumerable()); | |||
public static bool BeginWith(this string a, string b) | |||
{ | |||
if (a == null || b == null) return false; | |||
return a.StartsWith(b, DefaultComparison); | |||
} | |||
public static bool BeginWith(this string a, string b, StringComparison comparisonType) | |||
{ | |||
if (a == null || b == null) return false; | |||
return a.StartsWith(b, comparisonType); | |||
} | |||
#if !PCL | |||
public static bool BeginWith(this string a, string b, bool ignoreCase, CultureInfo culture) | |||
{ | |||
if (a == null || b == null) return false; | |||
return a.StartsWith(b, ignoreCase, culture); | |||
} | |||
#endif | |||
#endregion | |||
#region FinishWith | |||
public static bool FinishWith(this string s, char c) | |||
{ | |||
if (s.IsNullOrEmpty()) return false; | |||
return s.Last() == c; | |||
} | |||
public static bool FinishWithAny(this string s, IEnumerable<char> chars) | |||
{ | |||
if (s.IsNullOrEmpty()) return false; | |||
return chars.Contains(s.Last()); | |||
} | |||
public static bool FinishWithAny(this string s, params char[] chars) | |||
=> s.FinishWithAny(chars.AsEnumerable()); | |||
public static bool FinishWith(this string a, string b) | |||
{ | |||
if (a == null || b == null) return false; | |||
return a.EndsWith(b, DefaultComparison); | |||
} | |||
public static bool FinishWith(this string a, string b, StringComparison comparisonType) | |||
{ | |||
if (a == null || b == null) return false; | |||
return a.EndsWith(b, comparisonType); | |||
} | |||
#if !PCL | |||
public static bool FinishWith(this string a, string b, bool ignoreCase, CultureInfo culture) | |||
{ | |||
if (a == null || b == null) return false; | |||
return a.EndsWith(b, ignoreCase, culture); | |||
} | |||
#endif | |||
#endregion | |||
#endregion | |||
#region ToLines | |||
public static IEnumerable<string> ToLines(this TextReader reader) | |||
{ | |||
string line; | |||
while ((line = reader.ReadLine()) != null) | |||
yield return line; | |||
} | |||
public static IEnumerable<string> NonEmptyLines(this TextReader reader) | |||
{ | |||
string line; | |||
while ((line = reader.ReadLine()) != null) | |||
{ | |||
if (line == "") continue; | |||
yield return line; | |||
} | |||
} | |||
public static IEnumerable<string> NonWhiteSpaceLines(this TextReader reader) | |||
{ | |||
string line; | |||
while ((line = reader.ReadLine()) != null) | |||
{ | |||
if (line.IsWhiteSpace()) continue; | |||
yield return line; | |||
} | |||
} | |||
#endregion | |||
#region others | |||
private static readonly char[][] Quotes = new[] | |||
{ | |||
"\"\"", | |||
"''", | |||
"“”", | |||
"‘’", | |||
"『』", | |||
"「」", | |||
"〖〗", | |||
"【】", | |||
}.Select(s => s.ToCharArray()).ToArray(); | |||
public static string Enquote(this string value) | |||
{ | |||
if (value == null) | |||
return "(null)"; | |||
foreach (var pair in Quotes) | |||
{ | |||
if (value.IndexOfAny(pair) < 0) | |||
return pair[0] + value + pair[1]; | |||
} | |||
return '"' + value.Replace("\\", @"\\").Replace("\"", @"\""") + '"'; | |||
} | |||
public static string Replace(this string value, string find, string rep, StringComparison comparsionType) | |||
{ | |||
if (find.IsNullOrEmpty()) | |||
throw new ArgumentException(null, nameof(find)); | |||
if (rep == null) | |||
rep = ""; | |||
if (value.IsNullOrEmpty()) | |||
return value; | |||
var sb = new StringBuilder(value.Length); | |||
var last = 0; | |||
var len = find.Length; | |||
var idx = value.IndexOf(find, DefaultComparison); | |||
while (idx != -1) | |||
{ | |||
sb.Append(value.Substring(last, idx - last)); | |||
sb.Append(rep); | |||
idx += len; | |||
last = idx; | |||
idx = value.IndexOf(find, idx, comparsionType); | |||
} | |||
sb.Append(value.Substring(last)); | |||
return sb.ToString(); | |||
} | |||
public static string ReplaceEx(this string value, string find, string rep) | |||
=> value.Replace(find, rep, DefaultComparison); | |||
#endregion | |||
} |
@@ -630,11 +630,11 @@ namespace Shadowsocks.View | |||
{ | |||
if (!onlinePACItem.Checked) | |||
{ | |||
if (String.IsNullOrEmpty(controller.GetConfigurationCopy().pacUrl)) | |||
if (controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty()) | |||
{ | |||
UpdateOnlinePACURLItem_Click(sender, e); | |||
} | |||
if (!String.IsNullOrEmpty(controller.GetConfigurationCopy().pacUrl)) | |||
if (!controller.GetConfigurationCopy().pacUrl.IsNullOrEmpty()) | |||
{ | |||
localPACItem.Checked = false; | |||
onlinePACItem.Checked = true; | |||
@@ -651,7 +651,7 @@ namespace Shadowsocks.View | |||
I18N.GetString("Please input PAC Url"), | |||
I18N.GetString("Edit Online PAC URL"), | |||
origPacUrl, -1, -1); | |||
if (!string.IsNullOrEmpty(pacUrl) && pacUrl != origPacUrl) | |||
if (!pacUrl.IsNullOrEmpty() && pacUrl != origPacUrl) | |||
{ | |||
controller.SavePACUrl(pacUrl); | |||
} | |||
@@ -1,12 +1,13 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<packages> | |||
<package id="Caseless.Fody" version="1.3.7" targetFramework="net40-client" developmentDependency="true" /> | |||
<package id="Costura.Fody" version="1.3.3.0" targetFramework="net4-client" developmentDependency="true" /> | |||
<package id="Caseless.Fody" version="1.4.1" targetFramework="net40-client" developmentDependency="true" /> | |||
<package id="Costura.Fody" version="1.3.3.0" targetFramework="net40-client" developmentDependency="true" /> | |||
<package id="Fody" version="1.29.4" targetFramework="net40-client" developmentDependency="true" /> | |||
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net40-client" /> | |||
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net40-client" /> | |||
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net40-client" /> | |||
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net4-client" /> | |||
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net40-client" /> | |||
<package id="StringEx.CS" version="0.2" targetFramework="net40-client" /> | |||
<package id="System.Net.Http" version="2.0.20710.0" targetFramework="net40-client" /> | |||
</packages> |
@@ -204,6 +204,7 @@ | |||
<Compile Include="Controller\Strategy\BalancingStrategy.cs" /> | |||
<Compile Include="Controller\Strategy\StrategyManager.cs" /> | |||
<Compile Include="Controller\Strategy\IStrategy.cs" /> | |||
<Compile Include="StringEx.cs" /> | |||
<Compile Include="Util\Util.cs" /> | |||
<Compile Include="View\ConfigForm.cs"> | |||
<SubType>Form</SubType> | |||
@@ -333,6 +334,43 @@ | |||
</Target> | |||
<Import Project="3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" /> | |||
<Import Project="3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets" Condition="Exists('3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets')" /> | |||
<UsingTask TaskName="CosturaCleanup" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" TaskFactory="CodeTaskFactory"> | |||
<ParameterGroup> | |||
<Config Output="false" Required="true" ParameterType="Microsoft.Build.Framework.ITaskItem" /> | |||
<Files Output="false" Required="true" ParameterType="Microsoft.Build.Framework.ITaskItem[]" /> | |||
</ParameterGroup> | |||
<Task Evaluate="true"> | |||
<Reference xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Include="System.Xml" /> | |||
<Reference xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Include="System.Xml.Linq" /> | |||
<Using xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Namespace="System" /> | |||
<Using xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Namespace="System.IO" /> | |||
<Using xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Namespace="System.Xml.Linq" /> | |||
<Code xmlns="http://schemas.microsoft.com/developer/msbuild/2003" Type="Fragment" Language="cs"> | |||
<![CDATA[ | |||
var config = XElement.Load(Config.ItemSpec).Elements("Costura").FirstOrDefault(); | |||
if (config == null) return true; | |||
var excludedAssemblies = new List<string>(); | |||
var attribute = config.Attribute("ExcludeAssemblies"); | |||
if (attribute != null) | |||
foreach (var item in attribute.Value.Split('|').Select(x => x.Trim()).Where(x => x != string.Empty)) | |||
excludedAssemblies.Add(item); | |||
var element = config.Element("ExcludeAssemblies"); | |||
if (element != null) | |||
foreach (var item in element.Value.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()).Where(x => x != string.Empty)) | |||
excludedAssemblies.Add(item); | |||
var filesToCleanup = Files.Select(f => f.ItemSpec).Where(f => !excludedAssemblies.Contains(Path.GetFileNameWithoutExtension(f), StringComparer.InvariantCultureIgnoreCase)); | |||
foreach (var item in filesToCleanup) | |||
File.Delete(item); | |||
]]> | |||
</Code></Task> | |||
</UsingTask> | |||
<Target Name="CleanReferenceCopyLocalPaths" AfterTargets="AfterBuild;NonWinFodyTarget"> | |||
<CosturaCleanup Config="FodyWeavers.xml" Files="@(ReferenceCopyLocalPaths->'$(OutDir)%(DestinationSubDirectory)%(Filename)%(Extension)')" /> | |||
</Target> | |||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
Other similar extension points exist, see Microsoft.Common.targets. | |||
<Target Name="BeforeBuild"> | |||
@@ -1,30 +1,32 @@ | |||
| |||
Microsoft Visual Studio Solution File, Format Version 12.00 | |||
# Visual Studio Express 2012 for Windows Desktop | |||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "shadowsocks-csharp", "shadowsocks-csharp\shadowsocks-csharp.csproj", "{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}" | |||
EndProject | |||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test", "test\test.csproj", "{45913187-0685-4903-B250-DCEF0479CD86}" | |||
ProjectSection(ProjectDependencies) = postProject | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062} = {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062} | |||
EndProjectSection | |||
EndProject | |||
Global | |||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
Debug|x86 = Debug|x86 | |||
Release|x86 = Release|x86 | |||
EndGlobalSection | |||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Debug|x86.ActiveCfg = Debug|x86 | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Debug|x86.Build.0 = Debug|x86 | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Debug|x86.Deploy.0 = Debug|x86 | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Release|x86.ActiveCfg = Release|x86 | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Release|x86.Build.0 = Release|x86 | |||
{45913187-0685-4903-B250-DCEF0479CD86}.Debug|x86.ActiveCfg = Debug|x86 | |||
{45913187-0685-4903-B250-DCEF0479CD86}.Debug|x86.Build.0 = Debug|x86 | |||
{45913187-0685-4903-B250-DCEF0479CD86}.Release|x86.ActiveCfg = Release|x86 | |||
{45913187-0685-4903-B250-DCEF0479CD86}.Release|x86.Build.0 = Release|x86 | |||
EndGlobalSection | |||
GlobalSection(SolutionProperties) = preSolution | |||
HideSolutionNode = FALSE | |||
EndGlobalSection | |||
EndGlobal | |||
| |||
Microsoft Visual Studio Solution File, Format Version 12.00 | |||
# Visual Studio 14 | |||
VisualStudioVersion = 14.0.24720.0 | |||
MinimumVisualStudioVersion = 10.0.40219.1 | |||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "shadowsocks-csharp", "shadowsocks-csharp\shadowsocks-csharp.csproj", "{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}" | |||
EndProject | |||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "test", "test\test.csproj", "{45913187-0685-4903-B250-DCEF0479CD86}" | |||
ProjectSection(ProjectDependencies) = postProject | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062} = {8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062} | |||
EndProjectSection | |||
EndProject | |||
Global | |||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | |||
Debug|x86 = Debug|x86 | |||
Release|x86 = Release|x86 | |||
EndGlobalSection | |||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Debug|x86.ActiveCfg = Debug|x86 | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Debug|x86.Build.0 = Debug|x86 | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Debug|x86.Deploy.0 = Debug|x86 | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Release|x86.ActiveCfg = Release|x86 | |||
{8C02D2F7-7CDB-4D55-9F25-CD03EF4AA062}.Release|x86.Build.0 = Release|x86 | |||
{45913187-0685-4903-B250-DCEF0479CD86}.Debug|x86.ActiveCfg = Debug|x86 | |||
{45913187-0685-4903-B250-DCEF0479CD86}.Debug|x86.Build.0 = Debug|x86 | |||
{45913187-0685-4903-B250-DCEF0479CD86}.Release|x86.ActiveCfg = Release|x86 | |||
{45913187-0685-4903-B250-DCEF0479CD86}.Release|x86.Build.0 = Release|x86 | |||
EndGlobalSection | |||
GlobalSection(SolutionProperties) = preSolution | |||
HideSolutionNode = FALSE | |||
EndGlobalSection | |||
EndGlobal |