@@ -7,6 +7,8 @@ using Shadowsocks.Properties; | |||
using SimpleJson; | |||
using Shadowsocks.Util; | |||
using Shadowsocks.Model; | |||
using System.Linq; | |||
using System.Text.RegularExpressions; | |||
namespace Shadowsocks.Controller | |||
{ | |||
@@ -32,34 +34,20 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) | |||
private void savePacFile(string pacContent) | |||
{ | |||
try | |||
{ | |||
List<string> lines = ParseResult(e.Result); | |||
if (File.Exists(USER_RULE_FILE)) | |||
{ | |||
string local = File.ReadAllText(USER_RULE_FILE, Encoding.UTF8); | |||
string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); | |||
foreach(string rule in rules) | |||
{ | |||
if (rule.StartsWith("!") || rule.StartsWith("[")) | |||
continue; | |||
lines.Add(rule); | |||
} | |||
} | |||
string abpContent = Utils.UnGzip(Resources.abp_js); | |||
abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines)); | |||
if (File.Exists(PAC_FILE)) | |||
{ | |||
string original = File.ReadAllText(PAC_FILE, Encoding.UTF8); | |||
if (original == abpContent) | |||
if (original == pacContent) | |||
{ | |||
UpdateCompleted(this, new ResultEventArgs(false)); | |||
return; | |||
} | |||
} | |||
File.WriteAllText(PAC_FILE, abpContent, Encoding.UTF8); | |||
File.WriteAllText(PAC_FILE, pacContent, Encoding.UTF8); | |||
if (UpdateCompleted != null) | |||
{ | |||
UpdateCompleted(this, new ResultEventArgs(true)); | |||
@@ -74,12 +62,152 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
private string getHostname(string domain) | |||
{ | |||
string host = null; | |||
if (!(domain.StartsWith("http:") || domain.StartsWith("https:"))) | |||
domain = "http://" + domain; | |||
Uri hostUri; | |||
if (Uri.TryCreate(domain, UriKind.Absolute, out hostUri)) | |||
host = hostUri.Host; | |||
return host; | |||
} | |||
private void addDomainToList(List<string> list, string domain) | |||
{ | |||
string hostname = getHostname(domain); | |||
if (hostname != null) | |||
list.Add(hostname); | |||
} | |||
private List<string> parseGfwlist(List<string> lines) | |||
{ | |||
List<string> domains = new List<string>(); | |||
lines.ForEach(delegate(string line) | |||
{ | |||
if (line.StartsWith("@@") || line.StartsWith("/") || Regex.IsMatch(line, "^(\\d+\\.){3}\\d+$") || !line.Contains(".")) | |||
return; | |||
if (line.Contains("//*")) | |||
line = line.Replace("//*", "//"); | |||
else if (line.Contains("*/") || line.Contains("*.")) | |||
line = line.Replace("*", ""); | |||
else if (line.Contains("*")) | |||
line = line.Replace("*", "/"); | |||
if (line.StartsWith("|")) | |||
line = line.TrimStart('|'); | |||
else if (line.StartsWith(".")) | |||
line = line.TrimStart('.'); | |||
addDomainToList(domains, line); | |||
}); | |||
return domains; | |||
} | |||
private List<string> reduceDomains(List<string> domains) | |||
{ | |||
HashSet<string> uniDomains = new HashSet<string>(); | |||
foreach (var domain in domains) | |||
{ | |||
var domainParts = domain.Split('.'); | |||
bool isContainRootDomain = false; | |||
for (var i = 0; i <= domainParts.Length - 2; i++) | |||
{ | |||
var domainPartsArray = new ArraySegment<string>(domainParts, domainParts.Length - i - 2, i + 2); | |||
var rootDomain = string.Join(".", domainPartsArray); | |||
if (domains.Contains(rootDomain)) | |||
{ | |||
uniDomains.Add(rootDomain); | |||
isContainRootDomain = true; | |||
break; | |||
} | |||
} | |||
if (!isContainRootDomain) | |||
uniDomains.Add(domain); | |||
} | |||
return uniDomains.ToList(); | |||
} | |||
private void generatePacFast(List<string> lines) | |||
{ | |||
List<string> domains = parseGfwlist(lines); | |||
domains = reduceDomains(domains).Distinct().ToList(); | |||
var fastContent = Utils.UnGzip(Resources.proxy_pac); | |||
var domainsJsonStr = "{\n " + | |||
string.Join(",\n ", domains.Select(d=>string.Format("\"{0}\": 1", d))) + | |||
"\n}"; | |||
fastContent = fastContent.Replace("__DOMAINS__", domainsJsonStr); | |||
savePacFile(fastContent); | |||
} | |||
private void generatePacPrecise(List<string> lines) | |||
{ | |||
string abpContent = Utils.UnGzip(Resources.abp_js); | |||
abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines)); | |||
savePacFile(abpContent); | |||
} | |||
private void parsePacStr(string pacStr, List<string> lines) | |||
{ | |||
string[] rules = pacStr.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); | |||
foreach (string rule in rules) | |||
{ | |||
if (rule.StartsWith("!") || rule.StartsWith("[")) | |||
continue; | |||
lines.Add(rule); | |||
} | |||
} | |||
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) | |||
{ | |||
try | |||
{ | |||
List<string> lines = ParseResult(e.Result); | |||
string local = ""; | |||
if (File.Exists(USER_RULE_FILE)) | |||
{ | |||
local = File.ReadAllText(USER_RULE_FILE, Encoding.UTF8); | |||
} | |||
bool usePreciseMode = (bool) e.UserState; | |||
if (!usePreciseMode) | |||
{ | |||
local += Utils.UnGzip(Resources.builtin_txt); | |||
} | |||
parsePacStr(local, lines); | |||
if (usePreciseMode) | |||
{ | |||
generatePacPrecise(lines); | |||
} | |||
else | |||
{ | |||
generatePacFast(lines); | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
if (Error != null) | |||
{ | |||
Error(this, new ErrorEventArgs(ex)); | |||
} | |||
} | |||
} | |||
public void UpdatePACFromGFWList(Configuration config) | |||
{ | |||
WebClient http = new WebClient(); | |||
http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), config.localPort); | |||
http.DownloadStringCompleted += http_DownloadStringCompleted; | |||
http.DownloadStringAsync(new Uri(GFWLIST_URL)); | |||
http.DownloadStringAsync(new Uri(GFWLIST_URL), config.usePreciseMode); | |||
} | |||
public List<string> ParseResult(string response) | |||
@@ -214,6 +214,16 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
public void UsePreciseMode(bool usePreciseMode) | |||
{ | |||
_config.usePreciseMode = usePreciseMode; | |||
SaveConfig(_config); | |||
if (ConfigChanged != null) | |||
{ | |||
ConfigChanged(this, new EventArgs()); | |||
} | |||
} | |||
protected void Reload() | |||
{ | |||
// some logic in configuration updated the config when saving, we need to read it again | |||
@@ -14,6 +14,8 @@ Start on Boot=开机启动 | |||
Allow Clients from LAN=允许来自局域网的连接 | |||
Local PAC=使用本地 PAC | |||
Online PAC=使用在线 PAC | |||
Fast Mode=快速模式 | |||
Precise Mode=精确模式 | |||
Edit Local PAC File...=编辑本地 PAC 文件... | |||
Update Local PAC from GFWList=从 GFWList 更新本地 PAC | |||
Edit User Rule for GFWList...=编辑 GFWList 的用户规则... | |||
@@ -19,6 +19,7 @@ namespace Shadowsocks.Model | |||
public int localPort; | |||
public string pacUrl; | |||
public bool useOnlinePac; | |||
public bool usePreciseMode; | |||
private static string CONFIG_FILE = "gui-config.json"; | |||
@@ -1,7 +1,7 @@ | |||
//------------------------------------------------------------------------------ | |||
// <auto-generated> | |||
// This code was generated by a tool. | |||
// Runtime Version:4.0.30319.34209 | |||
// Runtime Version:4.0.30319.34014 | |||
// | |||
// Changes to this file may cause incorrect behavior and will be lost if | |||
// the code is regenerated. | |||
@@ -71,33 +71,39 @@ namespace Shadowsocks.Properties { | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to Shadowsocks=Shadowsocks | |||
///Enable System Proxy=启用系统代理 | |||
///Mode=系统代理模式 | |||
///PAC=PAC 模式 | |||
///Global=全局模式 | |||
///Servers=服务器 | |||
///Edit Servers...=编辑服务器... | |||
///Start on Boot=开机启动 | |||
///Allow Clients from LAN=允许来自局域网的连接 | |||
///Edit PAC File...=编辑 PAC 文件... | |||
///Edit User Rule for GFWList...=编辑 GFWList 的用户规则... | |||
///Show QRCode...=显示二维码... | |||
///Scan QRCode from Screen...=扫描屏幕上的二维码... | |||
///Show Logs...=显示日志... | |||
///About...=关于... | |||
///Quit=退出 | |||
///Edit Servers=编辑服务器 | |||
///&Add=添加(&A) | |||
///&Delete=删除(&D) | |||
///Server=服务器 | |||
///Server IP=服务器 IP | |||
///Server Port=服务器端口 | |||
///Password=密码 | |||
///Encryption=加密 | |||
///Proxy Port=代理端口 | |||
///Remarks=备注 | |||
///OK= [rest of string was truncated]";. | |||
/// Looks up a localized resource of type System.Byte[]. | |||
/// </summary> | |||
internal static byte[] builtin_txt { | |||
get { | |||
object obj = ResourceManager.GetObject("builtin_txt", resourceCulture); | |||
return ((byte[])(obj)); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized string similar to # translation for Simplified Chinese | |||
/// | |||
///Shadowsocks=Shadowsocks | |||
/// | |||
///# Menu items | |||
/// | |||
///Enable System Proxy=启用系统代理 | |||
///Mode=系统代理模式 | |||
///PAC=PAC 模式 | |||
///Global=全局模式 | |||
///Servers=服务器 | |||
///Edit Servers...=编辑服务器... | |||
///Start on Boot=开机启动 | |||
///Allow Clients from LAN=允许来自局域网的连接 | |||
///Local PAC=使用本地 PAC | |||
///Online PAC=使用在线 PAC | |||
///Fast Mode=快速模式 | |||
///Precise Mode=精确模式 | |||
///Edit Local PAC File...=编辑本地 PAC 文件... | |||
///Update Local PAC from GFWList=从 GFWList 更新本地 PAC | |||
///Edit User Rule for GFWList...=编辑 GFWList 的用户规则... | |||
///Show QRCode...=显示二维码... | |||
///Scan QRCode from Screen...=扫 [rest of string was truncated]";. | |||
/// </summary> | |||
internal static string cn { | |||
get { | |||
@@ -146,6 +152,16 @@ namespace Shadowsocks.Properties { | |||
/// <summary> | |||
/// Looks up a localized resource of type System.Byte[]. | |||
/// </summary> | |||
internal static byte[] proxy_pac { | |||
get { | |||
object obj = ResourceManager.GetObject("proxy_pac", resourceCulture); | |||
return ((byte[])(obj)); | |||
} | |||
} | |||
/// <summary> | |||
/// Looks up a localized resource of type System.Byte[]. | |||
/// </summary> | |||
internal static byte[] proxy_pac_txt { | |||
get { | |||
object obj = ResourceManager.GetObject("proxy_pac_txt", resourceCulture); | |||
@@ -112,15 +112,18 @@ | |||
<value>2.0</value> | |||
</resheader> | |||
<resheader name="reader"> | |||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
<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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
<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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> | |||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> | |||
<data name="abp_js" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||
<value>..\Data\abp.js.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</data> | |||
<data name="builtin_txt" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||
<value>..\Data\builtin.txt.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</data> | |||
<data name="cn" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||
<value>..\data\cn.txt;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value> | |||
</data> | |||
@@ -133,6 +136,9 @@ | |||
<data name="polipo_exe" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||
<value>..\Data\polipo.exe.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</data> | |||
<data name="proxy_pac" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||
<value>..\Data\proxy.pac.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</data> | |||
<data name="proxy_pac_txt" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||
<value>..\Data\proxy.pac.txt.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | |||
</data> | |||
@@ -37,6 +37,8 @@ namespace Shadowsocks.View | |||
private MenuItem PACModeItem; | |||
private MenuItem localPACItem; | |||
private MenuItem onlinePACItem; | |||
private MenuItem fastModeItem; | |||
private MenuItem preciseModeItem; | |||
private MenuItem editLocalPACItem; | |||
private MenuItem updateFromGFWListItem; | |||
private MenuItem editGFWUserRuleItem; | |||
@@ -161,6 +163,9 @@ namespace Shadowsocks.View | |||
this.localPACItem = CreateMenuItem("Local PAC", new EventHandler(this.LocalPACItem_Click)), | |||
this.onlinePACItem = CreateMenuItem("Online PAC", new EventHandler(this.OnlinePACItem_Click)), | |||
new MenuItem("-"), | |||
this.fastModeItem = CreateMenuItem("Fast Mode", new EventHandler(this.fastModeItem_Click)), | |||
this.preciseModeItem = CreateMenuItem("Precise Mode", new EventHandler(this.preciseModeItem_Click)), | |||
new MenuItem("-"), | |||
this.editLocalPACItem = CreateMenuItem("Edit Local PAC File...", new EventHandler(this.EditPACFileItem_Click)), | |||
this.updateFromGFWListItem = CreateMenuItem("Update Local PAC from GFWList", new EventHandler(this.UpdatePACFromGFWListItem_Click)), | |||
this.editGFWUserRuleItem = CreateMenuItem("Edit User Rule for GFWList...", new EventHandler(this.EditUserRuleFileForGFWListItem_Click)), | |||
@@ -253,6 +258,8 @@ namespace Shadowsocks.View | |||
AutoStartupItem.Checked = AutoStartup.Check(); | |||
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | |||
localPACItem.Checked = !onlinePACItem.Checked; | |||
fastModeItem.Checked = !config.usePreciseMode; | |||
preciseModeItem.Checked = config.usePreciseMode; | |||
UpdatePACItemsEnabledStatus(); | |||
} | |||
@@ -525,6 +532,26 @@ namespace Shadowsocks.View | |||
} | |||
} | |||
private void fastModeItem_Click(object sender, EventArgs e) | |||
{ | |||
if (!fastModeItem.Checked) | |||
{ | |||
fastModeItem.Checked = true; | |||
preciseModeItem.Checked = false; | |||
controller.UsePreciseMode(false); | |||
} | |||
} | |||
private void preciseModeItem_Click(object sender, EventArgs e) | |||
{ | |||
if (!preciseModeItem.Checked) | |||
{ | |||
fastModeItem.Checked = false; | |||
preciseModeItem.Checked = true; | |||
controller.UsePreciseMode(true); | |||
} | |||
} | |||
private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e) | |||
{ | |||
string origPacUrl = controller.GetConfiguration().pacUrl; | |||
@@ -544,6 +571,8 @@ namespace Shadowsocks.View | |||
{ | |||
this.editLocalPACItem.Enabled = true; | |||
this.updateFromGFWListItem.Enabled = true; | |||
this.fastModeItem.Enabled = true; | |||
this.preciseModeItem.Enabled = true; | |||
this.editGFWUserRuleItem.Enabled = true; | |||
this.editOnlinePACItem.Enabled = false; | |||
} | |||
@@ -551,6 +580,8 @@ namespace Shadowsocks.View | |||
{ | |||
this.editLocalPACItem.Enabled = false; | |||
this.updateFromGFWListItem.Enabled = false; | |||
this.fastModeItem.Enabled = false; | |||
this.preciseModeItem.Enabled = false; | |||
this.editGFWUserRuleItem.Enabled = false; | |||
this.editOnlinePACItem.Enabled = true; | |||
} | |||