@@ -16,186 +16,84 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
private const string GFWLIST_URL = "https://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt"; | private const string GFWLIST_URL = "https://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt"; | ||||
private const int EXPIRE_HOURS = 6; | |||||
public IWebProxy proxy = null; | public IWebProxy proxy = null; | ||||
public bool useSystemProxy = true; | |||||
public class GfwListChangedArgs : EventArgs | |||||
public class GfwListDownloadCompletedArgs : EventArgs | |||||
{ | { | ||||
public string[] GfwList { get; set; } | |||||
public string Content; | |||||
} | } | ||||
public event EventHandler<GfwListChangedArgs> GfwListChanged; | |||||
private bool running = false; | |||||
private bool closed = false; | |||||
private int jobId = 0; | |||||
DateTime lastUpdateTimeUtc; | |||||
string lastUpdateMd5; | |||||
private object locker = new object(); | |||||
public event EventHandler<GfwListDownloadCompletedArgs> DownloadCompleted; | |||||
public GfwListUpdater() | |||||
{ | |||||
} | |||||
public event ErrorEventHandler Error; | |||||
~GfwListUpdater() | |||||
public void Download() | |||||
{ | { | ||||
Stop(); | |||||
WebClient http = new WebClient(); | |||||
http.Proxy = proxy; | |||||
http.DownloadStringCompleted += http_DownloadStringCompleted; | |||||
http.DownloadStringAsync(new Uri(GFWLIST_URL)); | |||||
} | } | ||||
public void Start() | |||||
protected void ReportError(Exception e) | |||||
{ | { | ||||
lock (locker) | |||||
if (Error != null) | |||||
{ | { | ||||
if (running) | |||||
return; | |||||
running = true; | |||||
closed = false; | |||||
jobId++; | |||||
new Thread(new ParameterizedThreadStart(UpdateJob)).Start(jobId); | |||||
Error(this, new ErrorEventArgs(e)); | |||||
} | } | ||||
} | } | ||||
public void Stop() | |||||
{ | |||||
lock(locker) | |||||
{ | |||||
closed = true; | |||||
running = false; | |||||
jobId++; | |||||
} | |||||
} | |||||
public void ScheduleUpdateTime(int delaySeconds) | |||||
{ | |||||
lock(locker) | |||||
{ | |||||
lastUpdateTimeUtc = DateTime.UtcNow.AddHours(-1 * EXPIRE_HOURS).AddSeconds(delaySeconds); | |||||
} | |||||
} | |||||
private string DownloadGfwListFile() | |||||
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) | |||||
{ | { | ||||
try | try | ||||
{ | { | ||||
WebClient http = new WebClient(); | |||||
http.Proxy = useSystemProxy ? WebRequest.GetSystemWebProxy() : proxy; | |||||
return http.DownloadString(new Uri(GFWLIST_URL)); | |||||
string response = e.Result; | |||||
if (DownloadCompleted != null) | |||||
{ | |||||
DownloadCompleted(this, new GfwListDownloadCompletedArgs | |||||
{ | |||||
Content = response | |||||
}); | |||||
} | |||||
} | } | ||||
catch (Exception ex) | catch (Exception ex) | ||||
{ | { | ||||
Console.WriteLine(ex.ToString()); | |||||
ReportError(ex); | |||||
} | } | ||||
return null; | |||||
} | } | ||||
private bool IsExpire() | |||||
public class Parser | |||||
{ | { | ||||
lock (locker) | |||||
{ | |||||
TimeSpan ts = DateTime.UtcNow - lastUpdateTimeUtc; | |||||
bool expire = ((int)ts.TotalHours) >= EXPIRE_HOURS; | |||||
if (expire) | |||||
lastUpdateTimeUtc = DateTime.UtcNow; | |||||
return expire; | |||||
} | |||||
} | |||||
private string _Content; | |||||
private bool IsJobStop(int currentJobId) | |||||
{ | |||||
lock (locker) | |||||
public string Content | |||||
{ | { | ||||
if (!running || closed || currentJobId != this.jobId) | |||||
return true; | |||||
get { return _Content; } | |||||
} | } | ||||
return false; | |||||
} | |||||
private bool IsGfwListChanged(string content) | |||||
{ | |||||
byte[] inputBytes = Encoding.UTF8.GetBytes(content); | |||||
byte[] md5Bytes = MD5.Create().ComputeHash(inputBytes); | |||||
string md5 = ""; | |||||
for (int i = 0; i < md5Bytes.Length; i++) | |||||
md5 += md5Bytes[i].ToString("x").PadLeft(2, '0'); | |||||
if (md5 == lastUpdateMd5) | |||||
return false; | |||||
lastUpdateMd5 = md5; | |||||
return true; | |||||
} | |||||
private void ParseGfwList(string response) | |||||
{ | |||||
if (!IsGfwListChanged(response)) | |||||
return; | |||||
if (GfwListChanged != null) | |||||
public Parser(string response) | |||||
{ | { | ||||
try | |||||
{ | |||||
Parser parser = new Parser(response); | |||||
GfwListChangedArgs args = new GfwListChangedArgs | |||||
{ | |||||
GfwList = parser.GetReducedDomains() | |||||
}; | |||||
GfwListChanged(this, args); | |||||
} | |||||
catch(Exception ex) | |||||
{ | |||||
Console.WriteLine(ex.ToString()); | |||||
} | |||||
byte[] bytes = Convert.FromBase64String(response); | |||||
this._Content = Encoding.ASCII.GetString(bytes); | |||||
} | } | ||||
} | |||||
private void UpdateJob(object state) | |||||
{ | |||||
int currentJobId = (int)state; | |||||
int retryTimes = 3; | |||||
while (!IsJobStop(currentJobId)) | |||||
public string[] GetValidLines() | |||||
{ | { | ||||
if (IsExpire()) | |||||
string[] lines = Content.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); | |||||
List<string> valid_lines = new List<string>(lines.Length); | |||||
foreach (string line in lines) | |||||
{ | { | ||||
string response = DownloadGfwListFile(); | |||||
if (response != null) | |||||
{ | |||||
ParseGfwList(response); | |||||
} | |||||
else if (retryTimes > 0) | |||||
{ | |||||
ScheduleUpdateTime(30); /*Delay 30 seconds to retry*/ | |||||
retryTimes--; | |||||
} | |||||
else | |||||
{ | |||||
retryTimes = 3; /* reset retry times, and wait next update time. */ | |||||
} | |||||
if (line.StartsWith("!") || line.StartsWith("[")) | |||||
continue; | |||||
valid_lines.Add(line); | |||||
} | } | ||||
Thread.Sleep(1000); | |||||
} | |||||
} | |||||
class Parser | |||||
{ | |||||
public string Content { get; private set; } | |||||
public Parser(string response) | |||||
{ | |||||
byte[] bytes = Convert.FromBase64String(response); | |||||
this.Content = Encoding.ASCII.GetString(bytes); | |||||
return valid_lines.ToArray(); | |||||
} | } | ||||
public string[] GetLines() | |||||
{ | |||||
return Content.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); | |||||
} | |||||
/* refer https://github.com/clowwindy/gfwlist2pac/blob/master/gfwlist2pac/main.py */ | /* refer https://github.com/clowwindy/gfwlist2pac/blob/master/gfwlist2pac/main.py */ | ||||
public string[] GetDomains() | public string[] GetDomains() | ||||
{ | { | ||||
List<string> lines = new List<string>(GetLines()); | |||||
List<string> lines = new List<string>(GetValidLines()); | |||||
lines.AddRange(GetBuildIn()); | lines.AddRange(GetBuildIn()); | ||||
List<string> domains = new List<string>(lines.Count); | List<string> domains = new List<string>(lines.Count); | ||||
for (int i = 0; i < lines.Count; i++) | for (int i = 0; i < lines.Count; i++) | ||||
@@ -222,7 +120,7 @@ namespace Shadowsocks.Controller | |||||
continue; | continue; | ||||
else if (line.StartsWith("@")) | else if (line.StartsWith("@")) | ||||
continue; /*ignore white list*/ | continue; /*ignore white list*/ | ||||
int pos = line.IndexOfAny(new char[] { '/'}); | |||||
int pos = line.IndexOfAny(new char[] { '/' }); | |||||
if (pos >= 0) | if (pos >= 0) | ||||
line = line.Substring(0, pos); | line = line.Substring(0, pos); | ||||
if (line.Length > 0) | if (line.Length > 0) | ||||
@@ -238,7 +136,7 @@ namespace Shadowsocks.Controller | |||||
List<string> new_domains = new List<string>(domains.Length); | List<string> new_domains = new List<string>(domains.Length); | ||||
TldIndex tldIndex = GetTldIndex(); | TldIndex tldIndex = GetTldIndex(); | ||||
foreach(string domain in domains) | |||||
foreach (string domain in domains) | |||||
{ | { | ||||
string last_root_domain = null; | string last_root_domain = null; | ||||
int pos; | int pos; | ||||
@@ -246,7 +144,7 @@ namespace Shadowsocks.Controller | |||||
last_root_domain = domain.Substring(pos + 1); | last_root_domain = domain.Substring(pos + 1); | ||||
if (!tldIndex.Contains(last_root_domain)) | if (!tldIndex.Contains(last_root_domain)) | ||||
continue; | continue; | ||||
while(pos > 0) | |||||
while (pos > 0) | |||||
{ | { | ||||
pos = domain.LastIndexOf('.', pos - 1); | pos = domain.LastIndexOf('.', pos - 1); | ||||
last_root_domain = domain.Substring(pos + 1); | last_root_domain = domain.Substring(pos + 1); | ||||
@@ -266,7 +164,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
List<string> list = new List<string>(src.Length); | List<string> list = new List<string>(src.Length); | ||||
Dictionary<string, string> dic = new Dictionary<string, string>(src.Length); | Dictionary<string, string> dic = new Dictionary<string, string>(src.Length); | ||||
foreach(string s in src) | |||||
foreach (string s in src) | |||||
{ | { | ||||
if (!dic.ContainsKey(s)) | if (!dic.ContainsKey(s)) | ||||
{ | { | ||||
@@ -281,9 +179,9 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
string[] tlds = null; | string[] tlds = null; | ||||
byte[] pacGZ = Resources.tld_txt; | byte[] pacGZ = Resources.tld_txt; | ||||
byte[] buffer = new byte[1024]; | |||||
byte[] buffer = new byte[1024]; | |||||
int n; | int n; | ||||
using(MemoryStream sb = new MemoryStream()) | |||||
using (MemoryStream sb = new MemoryStream()) | |||||
{ | { | ||||
using (GZipStream input = new GZipStream(new MemoryStream(pacGZ), | using (GZipStream input = new GZipStream(new MemoryStream(pacGZ), | ||||
CompressionMode.Decompress, false)) | CompressionMode.Decompress, false)) | ||||
@@ -332,7 +230,7 @@ namespace Shadowsocks.Controller | |||||
return buildin; | return buildin; | ||||
} | } | ||||
class TldIndex | |||||
public class TldIndex | |||||
{ | { | ||||
List<string> patterns = new List<string>(); | List<string> patterns = new List<string>(); | ||||
IDictionary<string, string> dic = new Dictionary<string, string>(); | IDictionary<string, string> dic = new Dictionary<string, string>(); | ||||
@@ -355,7 +253,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
if (dic.ContainsKey(tld)) | if (dic.ContainsKey(tld)) | ||||
return true; | return true; | ||||
foreach(string pattern in patterns) | |||||
foreach (string pattern in patterns) | |||||
{ | { | ||||
if (Regex.IsMatch(tld, pattern)) | if (Regex.IsMatch(tld, pattern)) | ||||
return true; | return true; | ||||
@@ -365,7 +263,7 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -1,6 +1,7 @@ | |||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using Shadowsocks.Properties; | using Shadowsocks.Properties; | ||||
using System; | using System; | ||||
using System.Collections; | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Diagnostics; | using System.Diagnostics; | ||||
using System.IO; | using System.IO; | ||||
@@ -21,10 +22,12 @@ namespace Shadowsocks.Controller | |||||
Socket _listener; | Socket _listener; | ||||
FileSystemWatcher watcher; | FileSystemWatcher watcher; | ||||
GfwListUpdater gfwlistUpdater; | |||||
public event EventHandler PACFileChanged; | public event EventHandler PACFileChanged; | ||||
public event EventHandler UpdatePACFromGFWListCompleted; | |||||
public event ErrorEventHandler UpdatePACFromGFWListError; | |||||
public void Start(Configuration configuration) | public void Start(Configuration configuration) | ||||
{ | { | ||||
try | try | ||||
@@ -51,7 +54,6 @@ namespace Shadowsocks.Controller | |||||
_listener); | _listener); | ||||
WatchPacFile(); | WatchPacFile(); | ||||
StartGfwListUpdater(); | |||||
} | } | ||||
catch (SocketException) | catch (SocketException) | ||||
{ | { | ||||
@@ -62,11 +64,6 @@ namespace Shadowsocks.Controller | |||||
public void Stop() | public void Stop() | ||||
{ | { | ||||
if (gfwlistUpdater != null) | |||||
{ | |||||
gfwlistUpdater.Stop(); | |||||
gfwlistUpdater = null; | |||||
} | |||||
if (_listener != null) | if (_listener != null) | ||||
{ | { | ||||
_listener.Close(); | _listener.Close(); | ||||
@@ -146,7 +143,7 @@ namespace Shadowsocks.Controller | |||||
using (GZipStream input = new GZipStream(new MemoryStream(pacGZ), | using (GZipStream input = new GZipStream(new MemoryStream(pacGZ), | ||||
CompressionMode.Decompress, false)) | CompressionMode.Decompress, false)) | ||||
{ | { | ||||
while((n = input.Read(buffer, 0, buffer.Length)) > 0) | |||||
while ((n = input.Read(buffer, 0, buffer.Length)) > 0) | |||||
{ | { | ||||
sb.Write(buffer, 0, n); | sb.Write(buffer, 0, n); | ||||
} | } | ||||
@@ -252,72 +249,103 @@ Connection: Close | |||||
return proxy; | return proxy; | ||||
} | } | ||||
private void StartGfwListUpdater() | |||||
public void UpdatePACFromGFWList() | |||||
{ | |||||
GfwListUpdater gfwlist = new GfwListUpdater(); | |||||
gfwlist.DownloadCompleted += gfwlist_DownloadCompleted; | |||||
gfwlist.Error += gfwlist_Error; | |||||
gfwlist.proxy = new WebProxy(IPAddress.Loopback.ToString(), 8123); /* use polipo proxy*/ | |||||
gfwlist.Download(); | |||||
} | |||||
private void gfwlist_DownloadCompleted(object sender, GfwListUpdater.GfwListDownloadCompletedArgs e) | |||||
{ | { | ||||
if (gfwlistUpdater != null) | |||||
GfwListUpdater.Parser parser = new GfwListUpdater.Parser(e.Content); | |||||
string[] lines = parser.GetValidLines(); | |||||
StringBuilder rules = new StringBuilder(lines.Length * 16); | |||||
SerializeRules(lines, rules); | |||||
string abpContent = GetAbpContent(); | |||||
abpContent = abpContent.Replace("__RULES__", rules.ToString()); | |||||
File.WriteAllText(PAC_FILE, abpContent); | |||||
if (UpdatePACFromGFWListCompleted != null) | |||||
{ | { | ||||
gfwlistUpdater.Stop(); | |||||
gfwlistUpdater = null; | |||||
UpdatePACFromGFWListCompleted(this, new EventArgs()); | |||||
} | } | ||||
} | |||||
gfwlistUpdater = new GfwListUpdater(); | |||||
gfwlistUpdater.GfwListChanged += gfwlistUpdater_GfwListChanged; | |||||
IPEndPoint localEndPoint = (IPEndPoint)_listener.LocalEndPoint; | |||||
gfwlistUpdater.proxy = new WebProxy(localEndPoint.Address.ToString(), 8123); | |||||
gfwlistUpdater.useSystemProxy = false; | |||||
/* Delay 30 seconds, wait proxy start up. */ | |||||
gfwlistUpdater.ScheduleUpdateTime(30); | |||||
gfwlistUpdater.Start(); | |||||
private void gfwlist_Error(object sender, ErrorEventArgs e) | |||||
{ | |||||
if (UpdatePACFromGFWListError != null) | |||||
{ | |||||
UpdatePACFromGFWListError(this, e); | |||||
} | |||||
} | } | ||||
private void gfwlistUpdater_GfwListChanged(object sender, GfwListUpdater.GfwListChangedArgs e) | |||||
private string GetAbpContent() | |||||
{ | { | ||||
if (e.GfwList == null || e.GfwList.Length == 0) return; | |||||
string pacfile = TouchPACFile(); | |||||
string pacContent = File.ReadAllText(pacfile); | |||||
string oldDomains; | |||||
if (ClearPacContent(ref pacContent, out oldDomains)) | |||||
byte[] abpGZ = Resources.abp_js; | |||||
byte[] buffer = new byte[1024]; // builtin pac gzip size: maximum 100K | |||||
int n; | |||||
using (MemoryStream sb = new MemoryStream()) | |||||
{ | { | ||||
StringBuilder sb = new StringBuilder(); | |||||
sb.AppendLine("{"); | |||||
for (int i = 0; i < e.GfwList.Length; i++) | |||||
{ | |||||
if (i == e.GfwList.Length - 1) | |||||
sb.AppendFormat("\t\"{0}\": {1}\r\n", e.GfwList[i], 1); | |||||
else | |||||
sb.AppendFormat("\t\"{0}\": {1},\r\n", e.GfwList[i], 1); | |||||
} | |||||
sb.Append("}"); | |||||
string newDomains = sb.ToString(); | |||||
if (!string.Equals(oldDomains, newDomains)) | |||||
using (GZipStream input = new GZipStream(new MemoryStream(abpGZ), | |||||
CompressionMode.Decompress, false)) | |||||
{ | { | ||||
pacContent = pacContent.Replace("__LAST_MODIFIED__", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); | |||||
pacContent = pacContent.Replace("__DOMAINS__", newDomains); | |||||
File.WriteAllText(pacfile, pacContent); | |||||
Console.WriteLine("gfwlist updated - " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); | |||||
while ((n = input.Read(buffer, 0, buffer.Length)) > 0) | |||||
{ | |||||
sb.Write(buffer, 0, n); | |||||
} | |||||
} | } | ||||
return System.Text.Encoding.UTF8.GetString(sb.ToArray()); | |||||
} | } | ||||
else | |||||
} | |||||
private static void SerializeRules(string[] rules, StringBuilder builder) | |||||
{ | |||||
builder.Append("[\n"); | |||||
bool first = true; | |||||
foreach (string rule in rules) | |||||
{ | { | ||||
Console.WriteLine("Broken pac file."); | |||||
if (!first) | |||||
builder.Append(",\n"); | |||||
SerializeString(rule, builder); | |||||
first = false; | |||||
} | } | ||||
builder.Append("\n]"); | |||||
} | } | ||||
private bool ClearPacContent(ref string pacContent, out string oldDomains) | |||||
private static void SerializeString(string aString, StringBuilder builder) | |||||
{ | { | ||||
Regex regex = new Regex("(/\\*.*?\\*/\\s*)?var\\s+domains\\s*=\\s*(\\{(\\s*\"[^\"]*\"\\s*:\\s*\\d+\\s*,)*\\s*(\\s*\"[^\"]*\"\\s*:\\s*\\d+\\s*)\\})", RegexOptions.Singleline); | |||||
Match m = regex.Match(pacContent); | |||||
if (m.Success) | |||||
builder.Append("\t\""); | |||||
char[] charArray = aString.ToCharArray(); | |||||
for (int i = 0; i < charArray.Length; i++) | |||||
{ | { | ||||
oldDomains = m.Result("$2"); | |||||
pacContent = regex.Replace(pacContent, "/* Last Modified: __LAST_MODIFIED__ */\r\nvar domains = __DOMAINS__"); | |||||
return true; | |||||
char c = charArray[i]; | |||||
if (c == '"') | |||||
builder.Append("\\\""); | |||||
else if (c == '\\') | |||||
builder.Append("\\\\"); | |||||
else if (c == '\b') | |||||
builder.Append("\\b"); | |||||
else if (c == '\f') | |||||
builder.Append("\\f"); | |||||
else if (c == '\n') | |||||
builder.Append("\\n"); | |||||
else if (c == '\r') | |||||
builder.Append("\\r"); | |||||
else if (c == '\t') | |||||
builder.Append("\\t"); | |||||
else | |||||
builder.Append(c); | |||||
} | } | ||||
oldDomains = null; | |||||
return false; | |||||
} | |||||
builder.Append("\""); | |||||
} | |||||
} | } | ||||
} | } |
@@ -38,6 +38,10 @@ namespace Shadowsocks.Controller | |||||
// when user clicked Edit PAC, and PAC file has already created | // when user clicked Edit PAC, and PAC file has already created | ||||
public event EventHandler<PathEventArgs> PACFileReadyToOpen; | public event EventHandler<PathEventArgs> PACFileReadyToOpen; | ||||
public event EventHandler UpdatePACFromGFWListCompleted; | |||||
public event ErrorEventHandler UpdatePACFromGFWListError; | |||||
public event ErrorEventHandler Errored; | public event ErrorEventHandler Errored; | ||||
public ShadowsocksController() | public ShadowsocksController() | ||||
@@ -156,6 +160,14 @@ namespace Shadowsocks.Controller | |||||
return "ss://" + base64; | return "ss://" + base64; | ||||
} | } | ||||
public void UpdatePACFromGFWList() | |||||
{ | |||||
if (pacServer != null) | |||||
{ | |||||
pacServer.UpdatePACFromGFWList(); | |||||
} | |||||
} | |||||
protected void Reload() | protected void Reload() | ||||
{ | { | ||||
// some logic in configuration updated the config when saving, we need to read it again | // some logic in configuration updated the config when saving, we need to read it again | ||||
@@ -169,6 +181,8 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
pacServer = new PACServer(); | pacServer = new PACServer(); | ||||
pacServer.PACFileChanged += pacServer_PACFileChanged; | pacServer.PACFileChanged += pacServer_PACFileChanged; | ||||
pacServer.UpdatePACFromGFWListCompleted += pacServer_UpdatePACFromGFWListCompleted; | |||||
pacServer.UpdatePACFromGFWListError += pacServer_UpdatePACFromGFWListError; | |||||
} | } | ||||
pacServer.Stop(); | pacServer.Stop(); | ||||
@@ -247,6 +261,18 @@ namespace Shadowsocks.Controller | |||||
UpdateSystemProxy(); | UpdateSystemProxy(); | ||||
} | } | ||||
private void pacServer_UpdatePACFromGFWListCompleted(object sender, EventArgs e) | |||||
{ | |||||
if (UpdatePACFromGFWListCompleted != null) | |||||
UpdatePACFromGFWListCompleted(this, e); | |||||
} | |||||
private void pacServer_UpdatePACFromGFWListError(object sender, ErrorEventArgs e) | |||||
{ | |||||
if (UpdatePACFromGFWListError != null) | |||||
UpdatePACFromGFWListError(this, e); | |||||
} | |||||
private void StartReleasingMemory() | private void StartReleasingMemory() | ||||
{ | { | ||||
_ramThread = new Thread(new ThreadStart(ReleaseMemory)); | _ramThread = new Thread(new ThreadStart(ReleaseMemory)); | ||||
@@ -39,3 +39,6 @@ Shadowsocks is here=Shadowsocks 在这里 | |||||
You can turn on/off Shadowsocks in the context menu=可以在右键菜单中开关 Shadowsocks | You can turn on/off Shadowsocks in the context menu=可以在右键菜单中开关 Shadowsocks | ||||
Enabled=已启用代理 | Enabled=已启用代理 | ||||
Disabled=已禁用代理 | Disabled=已禁用代理 | ||||
Update PAC File via gfwlist...=基于 gfwlist 更新 PAC 文件... | |||||
Update PAC file failed=更新 PAC 文件失败 | |||||
Update PAC file succeed=更新 PAC 文件成功 |
@@ -1,10 +1,10 @@ | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// <auto-generated> | // <auto-generated> | ||||
// 此代码由工具生成。 | |||||
// 运行时版本:4.0.30319.34014 | |||||
// This code was generated by a tool. | |||||
// Runtime Version:4.0.30319.18444 | |||||
// | // | ||||
// 对此文件的更改可能会导致不正确的行为,并且如果 | |||||
// 重新生成代码,这些更改将会丢失。 | |||||
// Changes to this file may cause incorrect behavior and will be lost if | |||||
// the code is regenerated. | |||||
// </auto-generated> | // </auto-generated> | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
@@ -13,12 +13,12 @@ namespace Shadowsocks.Properties { | |||||
/// <summary> | /// <summary> | ||||
/// 一个强类型的资源类,用于查找本地化的字符串等。 | |||||
/// A strongly-typed resource class, for looking up localized strings, etc. | |||||
/// </summary> | /// </summary> | ||||
// 此类是由 StronglyTypedResourceBuilder | |||||
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 | |||||
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen | |||||
// (以 /str 作为命令选项),或重新生成 VS 项目。 | |||||
// 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.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] | ||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()] | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] | ||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] | ||||
@@ -33,7 +33,7 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 返回此类使用的缓存的 ResourceManager 实例。 | |||||
/// Returns the cached ResourceManager instance used by this class. | |||||
/// </summary> | /// </summary> | ||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] | ||||
internal static global::System.Resources.ResourceManager ResourceManager { | internal static global::System.Resources.ResourceManager ResourceManager { | ||||
@@ -47,8 +47,8 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 使用此强类型资源类,为所有资源查找 | |||||
/// 重写当前线程的 CurrentUICulture 属性。 | |||||
/// Overrides the current thread's CurrentUICulture property for all | |||||
/// resource lookups using this strongly typed resource class. | |||||
/// </summary> | /// </summary> | ||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] | ||||
internal static global::System.Globalization.CultureInfo Culture { | internal static global::System.Globalization.CultureInfo Culture { | ||||
@@ -61,7 +61,17 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Byte[] 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Byte[]. | |||||
/// </summary> | |||||
internal static byte[] abp_js { | |||||
get { | |||||
object obj = ResourceManager.GetObject("abp_js", resourceCulture); | |||||
return ((byte[])(obj)); | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// Looks up a localized resource of type System.Byte[]. | |||||
/// </summary> | /// </summary> | ||||
internal static byte[] builtin_txt { | internal static byte[] builtin_txt { | ||||
get { | get { | ||||
@@ -71,7 +81,7 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找类似 Shadowsocks=Shadowsocks | |||||
/// Looks up a localized string similar to Shadowsocks=Shadowsocks | |||||
///Enable=启用代理 | ///Enable=启用代理 | ||||
///Mode=代理模式 | ///Mode=代理模式 | ||||
///PAC=PAC 模式 | ///PAC=PAC 模式 | ||||
@@ -101,7 +111,7 @@ namespace Shadowsocks.Properties { | |||||
///QRCode=二维码 | ///QRCode=二维码 | ||||
///Shadowsocks Error: {0}=Shadowsocks 错误: {0} | ///Shadowsocks Error: {0}=Shadowsocks 错误: {0} | ||||
///Port already in use=端口已被占用 | ///Port already in use=端口已被占用 | ||||
///Il [字符串的其余部分被截断]"; 的本地化字符串。 | |||||
///Il [rest of string was truncated]";. | |||||
/// </summary> | /// </summary> | ||||
internal static string cn { | internal static string cn { | ||||
get { | get { | ||||
@@ -110,7 +120,7 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Byte[] 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Byte[]. | |||||
/// </summary> | /// </summary> | ||||
internal static byte[] libsscrypto_dll { | internal static byte[] libsscrypto_dll { | ||||
get { | get { | ||||
@@ -120,7 +130,7 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找类似 proxyAddress = "__POLIPO_BIND_IP__" | |||||
/// Looks up a localized string similar to proxyAddress = "__POLIPO_BIND_IP__" | |||||
/// | /// | ||||
///socksParentProxy = "127.0.0.1:__SOCKS_PORT__" | ///socksParentProxy = "127.0.0.1:__SOCKS_PORT__" | ||||
///socksProxyType = socks5 | ///socksProxyType = socks5 | ||||
@@ -128,7 +138,7 @@ namespace Shadowsocks.Properties { | |||||
///localDocumentRoot = "" | ///localDocumentRoot = "" | ||||
/// | /// | ||||
///allowedPorts = 1-65535 | ///allowedPorts = 1-65535 | ||||
///tunnelAllowedPorts = 1-65535 的本地化字符串。 | |||||
///tunnelAllowedPorts = 1-65535. | |||||
/// </summary> | /// </summary> | ||||
internal static string polipo_config { | internal static string polipo_config { | ||||
get { | get { | ||||
@@ -137,7 +147,7 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Byte[] 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Byte[]. | |||||
/// </summary> | /// </summary> | ||||
internal static byte[] polipo_exe { | internal static byte[] polipo_exe { | ||||
get { | get { | ||||
@@ -147,7 +157,7 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Byte[] 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Byte[]. | |||||
/// </summary> | /// </summary> | ||||
internal static byte[] proxy_pac_txt { | internal static byte[] proxy_pac_txt { | ||||
get { | get { | ||||
@@ -157,7 +167,7 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||||
/// </summary> | /// </summary> | ||||
internal static System.Drawing.Bitmap ss16 { | internal static System.Drawing.Bitmap ss16 { | ||||
get { | get { | ||||
@@ -167,7 +177,7 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||||
/// </summary> | /// </summary> | ||||
internal static System.Drawing.Bitmap ss20 { | internal static System.Drawing.Bitmap ss20 { | ||||
get { | get { | ||||
@@ -177,7 +187,7 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||||
/// </summary> | /// </summary> | ||||
internal static System.Drawing.Bitmap ss24 { | internal static System.Drawing.Bitmap ss24 { | ||||
get { | get { | ||||
@@ -187,7 +197,7 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Drawing.Bitmap 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||||
/// </summary> | /// </summary> | ||||
internal static System.Drawing.Bitmap ssw128 { | internal static System.Drawing.Bitmap ssw128 { | ||||
get { | get { | ||||
@@ -197,7 +207,7 @@ namespace Shadowsocks.Properties { | |||||
} | } | ||||
/// <summary> | /// <summary> | ||||
/// 查找 System.Byte[] 类型的本地化资源。 | |||||
/// Looks up a localized resource of type System.Byte[]. | |||||
/// </summary> | /// </summary> | ||||
internal static byte[] tld_txt { | internal static byte[] tld_txt { | ||||
get { | get { | ||||
@@ -118,6 +118,9 @@ | |||||
<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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | ||||
</resheader> | </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=2.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"> | <data name="builtin_txt" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||
<value>..\Data\builtin.txt.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | <value>..\Data\builtin.txt.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> | ||||
</data> | </data> | ||||
@@ -45,6 +45,8 @@ namespace Shadowsocks.View | |||||
controller.ShareOverLANStatusChanged += controller_ShareOverLANStatusChanged; | controller.ShareOverLANStatusChanged += controller_ShareOverLANStatusChanged; | ||||
controller.EnableGlobalChanged += controller_EnableGlobalChanged; | controller.EnableGlobalChanged += controller_EnableGlobalChanged; | ||||
controller.Errored += controller_Errored; | controller.Errored += controller_Errored; | ||||
controller.UpdatePACFromGFWListCompleted += controller_UpdatePACFromGFWListCompleted; | |||||
controller.UpdatePACFromGFWListError += controller_UpdatePACFromGFWListError; | |||||
_notifyIcon = new NotifyIcon(); | _notifyIcon = new NotifyIcon(); | ||||
UpdateTrayIcon(); | UpdateTrayIcon(); | ||||
@@ -138,6 +140,7 @@ namespace Shadowsocks.View | |||||
this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)), | this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)), | ||||
this.ShareOverLANItem = CreateMenuItem("Share over LAN", new EventHandler(this.ShareOverLANItem_Click)), | this.ShareOverLANItem = CreateMenuItem("Share over LAN", new EventHandler(this.ShareOverLANItem_Click)), | ||||
CreateMenuItem("Edit PAC File...", new EventHandler(this.EditPACFileItem_Click)), | CreateMenuItem("Edit PAC File...", new EventHandler(this.EditPACFileItem_Click)), | ||||
CreateMenuItem("Update PAC File via gfwlist...", new EventHandler(this.UpdatePACFromGFWListItem_Click)), | |||||
new MenuItem("-"), | new MenuItem("-"), | ||||
CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)), | CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)), | ||||
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), | CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), | ||||
@@ -176,6 +179,23 @@ namespace Shadowsocks.View | |||||
System.Diagnostics.Process.Start("explorer.exe", argument); | System.Diagnostics.Process.Start("explorer.exe", argument); | ||||
} | } | ||||
void controller_UpdatePACFromGFWListError(object sender, System.IO.ErrorEventArgs e) | |||||
{ | |||||
_notifyIcon.BalloonTipTitle = I18N.GetString("Update PAC File via gfwlist..."); | |||||
_notifyIcon.BalloonTipText = I18N.GetString("Update PAC file failed"); | |||||
_notifyIcon.BalloonTipIcon = ToolTipIcon.Info; | |||||
_notifyIcon.ShowBalloonTip(5000); | |||||
Logging.LogUsefulException(e.GetException()); | |||||
} | |||||
void controller_UpdatePACFromGFWListCompleted(object sender, EventArgs e) | |||||
{ | |||||
_notifyIcon.BalloonTipTitle = I18N.GetString("Update PAC File via gfwlist..."); | |||||
_notifyIcon.BalloonTipText = I18N.GetString("Update PAC file succeed"); | |||||
_notifyIcon.BalloonTipIcon = ToolTipIcon.Info; | |||||
_notifyIcon.ShowBalloonTip(5000); | |||||
} | |||||
void updateChecker_NewVersionFound(object sender, EventArgs e) | void updateChecker_NewVersionFound(object sender, EventArgs e) | ||||
{ | { | ||||
_notifyIcon.BalloonTipTitle = String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber); | _notifyIcon.BalloonTipTitle = String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber); | ||||
@@ -311,6 +331,15 @@ namespace Shadowsocks.View | |||||
controller.TouchPACFile(); | controller.TouchPACFile(); | ||||
} | } | ||||
private void UpdatePACFromGFWListItem_Click(object sender, EventArgs e) | |||||
{ | |||||
_notifyIcon.BalloonTipTitle = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version; | |||||
_notifyIcon.BalloonTipText = I18N.GetString("Update PAC File via gfwlist..."); | |||||
_notifyIcon.BalloonTipIcon = ToolTipIcon.Info; | |||||
_notifyIcon.ShowBalloonTip(5000); | |||||
controller.UpdatePACFromGFWList(); | |||||
} | |||||
private void AServerItem_Click(object sender, EventArgs e) | private void AServerItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
MenuItem item = (MenuItem)sender; | MenuItem item = (MenuItem)sender; | ||||
@@ -143,6 +143,7 @@ | |||||
<None Include="app.manifest"> | <None Include="app.manifest"> | ||||
<SubType>Designer</SubType> | <SubType>Designer</SubType> | ||||
</None> | </None> | ||||
<None Include="Data\abp.js.gz" /> | |||||
<None Include="Data\builtin.txt.gz" /> | <None Include="Data\builtin.txt.gz" /> | ||||
<None Include="Data\libsscrypto.dll.gz" /> | <None Include="Data\libsscrypto.dll.gz" /> | ||||
<None Include="Data\polipo.exe.gz" /> | <None Include="Data\polipo.exe.gz" /> | ||||