From c24c1d44b702285f487ef82aea2f045dc7ff1951 Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Wed, 7 Jan 2015 23:41:45 -0500 Subject: [PATCH] add new menu to update pac file via gfwlist --- shadowsocks-csharp/Controller/GfwListUpdater.cs | 196 +++++---------------- shadowsocks-csharp/Controller/PACServer.cs | 140 +++++++++------ .../Controller/ShadowsocksController.cs | 26 +++ shadowsocks-csharp/Data/abp.js.gz | Bin 0 -> 4594 bytes shadowsocks-csharp/Data/cn.txt | 3 + .../Properties/Resources.Designer.cs | 60 ++++--- shadowsocks-csharp/Properties/Resources.resx | 3 + shadowsocks-csharp/View/MenuViewController.cs | 29 +++ shadowsocks-csharp/shadowsocks-csharp.csproj | 1 + 9 files changed, 228 insertions(+), 230 deletions(-) create mode 100644 shadowsocks-csharp/Data/abp.js.gz diff --git a/shadowsocks-csharp/Controller/GfwListUpdater.cs b/shadowsocks-csharp/Controller/GfwListUpdater.cs index ef4fe4eb..c377efd2 100644 --- a/shadowsocks-csharp/Controller/GfwListUpdater.cs +++ b/shadowsocks-csharp/Controller/GfwListUpdater.cs @@ -16,186 +16,84 @@ namespace Shadowsocks.Controller { 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 bool useSystemProxy = true; - - public class GfwListChangedArgs : EventArgs + public class GfwListDownloadCompletedArgs : EventArgs { - public string[] GfwList { get; set; } + public string Content; } - public event EventHandler 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 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 { - 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) { - 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 valid_lines = new List(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 */ public string[] GetDomains() { - List lines = new List(GetLines()); + List lines = new List(GetValidLines()); lines.AddRange(GetBuildIn()); List domains = new List(lines.Count); for (int i = 0; i < lines.Count; i++) @@ -222,7 +120,7 @@ namespace Shadowsocks.Controller continue; else if (line.StartsWith("@")) continue; /*ignore white list*/ - int pos = line.IndexOfAny(new char[] { '/'}); + int pos = line.IndexOfAny(new char[] { '/' }); if (pos >= 0) line = line.Substring(0, pos); if (line.Length > 0) @@ -238,7 +136,7 @@ namespace Shadowsocks.Controller List new_domains = new List(domains.Length); TldIndex tldIndex = GetTldIndex(); - foreach(string domain in domains) + foreach (string domain in domains) { string last_root_domain = null; int pos; @@ -246,7 +144,7 @@ namespace Shadowsocks.Controller last_root_domain = domain.Substring(pos + 1); if (!tldIndex.Contains(last_root_domain)) continue; - while(pos > 0) + while (pos > 0) { pos = domain.LastIndexOf('.', pos - 1); last_root_domain = domain.Substring(pos + 1); @@ -266,7 +164,7 @@ namespace Shadowsocks.Controller { List list = new List(src.Length); Dictionary dic = new Dictionary(src.Length); - foreach(string s in src) + foreach (string s in src) { if (!dic.ContainsKey(s)) { @@ -281,9 +179,9 @@ namespace Shadowsocks.Controller { string[] tlds = null; byte[] pacGZ = Resources.tld_txt; - byte[] buffer = new byte[1024]; + byte[] buffer = new byte[1024]; int n; - using(MemoryStream sb = new MemoryStream()) + using (MemoryStream sb = new MemoryStream()) { using (GZipStream input = new GZipStream(new MemoryStream(pacGZ), CompressionMode.Decompress, false)) @@ -332,7 +230,7 @@ namespace Shadowsocks.Controller return buildin; } - class TldIndex + public class TldIndex { List patterns = new List(); IDictionary dic = new Dictionary(); @@ -355,7 +253,7 @@ namespace Shadowsocks.Controller { if (dic.ContainsKey(tld)) return true; - foreach(string pattern in patterns) + foreach (string pattern in patterns) { if (Regex.IsMatch(tld, pattern)) return true; @@ -365,7 +263,7 @@ namespace Shadowsocks.Controller } - + } } diff --git a/shadowsocks-csharp/Controller/PACServer.cs b/shadowsocks-csharp/Controller/PACServer.cs index 17351b19..52b838ba 100755 --- a/shadowsocks-csharp/Controller/PACServer.cs +++ b/shadowsocks-csharp/Controller/PACServer.cs @@ -1,6 +1,7 @@ using Shadowsocks.Model; using Shadowsocks.Properties; using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -21,10 +22,12 @@ namespace Shadowsocks.Controller Socket _listener; FileSystemWatcher watcher; - GfwListUpdater gfwlistUpdater; - public event EventHandler PACFileChanged; + public event EventHandler UpdatePACFromGFWListCompleted; + + public event ErrorEventHandler UpdatePACFromGFWListError; + public void Start(Configuration configuration) { try @@ -51,7 +54,6 @@ namespace Shadowsocks.Controller _listener); WatchPacFile(); - StartGfwListUpdater(); } catch (SocketException) { @@ -62,11 +64,6 @@ namespace Shadowsocks.Controller public void Stop() { - if (gfwlistUpdater != null) - { - gfwlistUpdater.Stop(); - gfwlistUpdater = null; - } if (_listener != null) { _listener.Close(); @@ -146,7 +143,7 @@ namespace Shadowsocks.Controller using (GZipStream input = new GZipStream(new MemoryStream(pacGZ), 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); } @@ -252,72 +249,103 @@ Connection: Close 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("\""); + } } } diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index 3d9d5119..427fe9bf 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -38,6 +38,10 @@ namespace Shadowsocks.Controller // when user clicked Edit PAC, and PAC file has already created public event EventHandler PACFileReadyToOpen; + public event EventHandler UpdatePACFromGFWListCompleted; + + public event ErrorEventHandler UpdatePACFromGFWListError; + public event ErrorEventHandler Errored; public ShadowsocksController() @@ -156,6 +160,14 @@ namespace Shadowsocks.Controller return "ss://" + base64; } + public void UpdatePACFromGFWList() + { + if (pacServer != null) + { + pacServer.UpdatePACFromGFWList(); + } + } + protected void Reload() { // 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.PACFileChanged += pacServer_PACFileChanged; + pacServer.UpdatePACFromGFWListCompleted += pacServer_UpdatePACFromGFWListCompleted; + pacServer.UpdatePACFromGFWListError += pacServer_UpdatePACFromGFWListError; } pacServer.Stop(); @@ -247,6 +261,18 @@ namespace Shadowsocks.Controller 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() { _ramThread = new Thread(new ThreadStart(ReleaseMemory)); diff --git a/shadowsocks-csharp/Data/abp.js.gz b/shadowsocks-csharp/Data/abp.js.gz new file mode 100644 index 0000000000000000000000000000000000000000..4742149658d7de5482c3dad933840410cd7358bb GIT binary patch literal 4594 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 -// 此代码由工具生成。 -// 运行时版本: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. // //------------------------------------------------------------------------------ @@ -13,12 +13,12 @@ namespace Shadowsocks.Properties { /// - /// 一个强类型的资源类,用于查找本地化的字符串等。 + /// A strongly-typed resource class, for looking up localized strings, etc. /// - // 此类是由 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.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] @@ -33,7 +33,7 @@ namespace Shadowsocks.Properties { } /// - /// 返回此类使用的缓存的 ResourceManager 实例。 + /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Resources.ResourceManager ResourceManager { @@ -47,8 +47,8 @@ namespace Shadowsocks.Properties { } /// - /// 使用此强类型资源类,为所有资源查找 - /// 重写当前线程的 CurrentUICulture 属性。 + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { @@ -61,7 +61,17 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Byte[] 类型的本地化资源。 + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] abp_js { + get { + object obj = ResourceManager.GetObject("abp_js", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. /// internal static byte[] builtin_txt { get { @@ -71,7 +81,7 @@ namespace Shadowsocks.Properties { } /// - /// 查找类似 Shadowsocks=Shadowsocks + /// Looks up a localized string similar to Shadowsocks=Shadowsocks ///Enable=启用代理 ///Mode=代理模式 ///PAC=PAC 模式 @@ -101,7 +111,7 @@ namespace Shadowsocks.Properties { ///QRCode=二维码 ///Shadowsocks Error: {0}=Shadowsocks 错误: {0} ///Port already in use=端口已被占用 - ///Il [字符串的其余部分被截断]"; 的本地化字符串。 + ///Il [rest of string was truncated]";. /// internal static string cn { get { @@ -110,7 +120,7 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Byte[] 类型的本地化资源。 + /// Looks up a localized resource of type System.Byte[]. /// internal static byte[] libsscrypto_dll { get { @@ -120,7 +130,7 @@ namespace Shadowsocks.Properties { } /// - /// 查找类似 proxyAddress = "__POLIPO_BIND_IP__" + /// Looks up a localized string similar to proxyAddress = "__POLIPO_BIND_IP__" /// ///socksParentProxy = "127.0.0.1:__SOCKS_PORT__" ///socksProxyType = socks5 @@ -128,7 +138,7 @@ namespace Shadowsocks.Properties { ///localDocumentRoot = "" /// ///allowedPorts = 1-65535 - ///tunnelAllowedPorts = 1-65535 的本地化字符串。 + ///tunnelAllowedPorts = 1-65535. /// internal static string polipo_config { get { @@ -137,7 +147,7 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Byte[] 类型的本地化资源。 + /// Looks up a localized resource of type System.Byte[]. /// internal static byte[] polipo_exe { get { @@ -147,7 +157,7 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Byte[] 类型的本地化资源。 + /// Looks up a localized resource of type System.Byte[]. /// internal static byte[] proxy_pac_txt { get { @@ -157,7 +167,7 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap ss16 { get { @@ -167,7 +177,7 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap ss20 { get { @@ -177,7 +187,7 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap ss24 { get { @@ -187,7 +197,7 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Drawing.Bitmap 类型的本地化资源。 + /// Looks up a localized resource of type System.Drawing.Bitmap. /// internal static System.Drawing.Bitmap ssw128 { get { @@ -197,7 +207,7 @@ namespace Shadowsocks.Properties { } /// - /// 查找 System.Byte[] 类型的本地化资源。 + /// Looks up a localized resource of type System.Byte[]. /// internal static byte[] tld_txt { get { diff --git a/shadowsocks-csharp/Properties/Resources.resx b/shadowsocks-csharp/Properties/Resources.resx index 2b53c8e1..24d1b168 100755 --- a/shadowsocks-csharp/Properties/Resources.resx +++ b/shadowsocks-csharp/Properties/Resources.resx @@ -118,6 +118,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\Data\abp.js.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Data\builtin.txt.gz;System.Byte[], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index a22a8b05..649ac44c 100755 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -45,6 +45,8 @@ namespace Shadowsocks.View controller.ShareOverLANStatusChanged += controller_ShareOverLANStatusChanged; controller.EnableGlobalChanged += controller_EnableGlobalChanged; controller.Errored += controller_Errored; + controller.UpdatePACFromGFWListCompleted += controller_UpdatePACFromGFWListCompleted; + controller.UpdatePACFromGFWListError += controller_UpdatePACFromGFWListError; _notifyIcon = new NotifyIcon(); UpdateTrayIcon(); @@ -138,6 +140,7 @@ namespace Shadowsocks.View this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)), this.ShareOverLANItem = CreateMenuItem("Share over LAN", new EventHandler(this.ShareOverLANItem_Click)), CreateMenuItem("Edit PAC File...", new EventHandler(this.EditPACFileItem_Click)), + CreateMenuItem("Update PAC File via gfwlist...", new EventHandler(this.UpdatePACFromGFWListItem_Click)), new MenuItem("-"), CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)), CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), @@ -176,6 +179,23 @@ namespace Shadowsocks.View 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) { _notifyIcon.BalloonTipTitle = String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber); @@ -311,6 +331,15 @@ namespace Shadowsocks.View 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) { MenuItem item = (MenuItem)sender; diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 6ed7cf4f..314ea6a3 100755 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -143,6 +143,7 @@ Designer +