Merge branch 'gfwlist' of https://github.com/GangZhuo/shadowsocks-csharp into GangZhuo-gfwlist
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
|
||||
namespace Shadowsocks.Controller
|
||||
{
|
||||
public class GfwListUpdater
|
||||
{
|
||||
private const string GFWLIST_URL = "https://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt";
|
||||
|
||||
public IWebProxy proxy = null;
|
||||
|
||||
public class GfwListDownloadCompletedArgs : EventArgs
|
||||
{
|
||||
public string Content;
|
||||
}
|
||||
|
||||
public event EventHandler<GfwListDownloadCompletedArgs> DownloadCompleted;
|
||||
|
||||
public event ErrorEventHandler Error;
|
||||
|
||||
public void Download()
|
||||
{
|
||||
WebClient http = new WebClient();
|
||||
http.Proxy = proxy;
|
||||
http.DownloadStringCompleted += http_DownloadStringCompleted;
|
||||
http.DownloadStringAsync(new Uri(GFWLIST_URL));
|
||||
}
|
||||
|
||||
protected void ReportError(Exception e)
|
||||
{
|
||||
if (Error != null)
|
||||
{
|
||||
Error(this, new ErrorEventArgs(e));
|
||||
}
|
||||
}
|
||||
|
||||
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
string response = e.Result;
|
||||
if (DownloadCompleted != null)
|
||||
{
|
||||
DownloadCompleted(this, new GfwListDownloadCompletedArgs
|
||||
{
|
||||
Content = response
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ReportError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public class Parser
|
||||
{
|
||||
private string _Content;
|
||||
|
||||
public string Content
|
||||
{
|
||||
get { return _Content; }
|
||||
}
|
||||
|
||||
public Parser(string response)
|
||||
{
|
||||
byte[] bytes = Convert.FromBase64String(response);
|
||||
this._Content = Encoding.ASCII.GetString(bytes);
|
||||
}
|
||||
|
||||
public string[] GetValidLines()
|
||||
{
|
||||
string[] lines = Content.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
List<string> valid_lines = new List<string>(lines.Length);
|
||||
foreach (string line in lines)
|
||||
{
|
||||
if (line.StartsWith("!") || line.StartsWith("["))
|
||||
continue;
|
||||
valid_lines.Add(line);
|
||||
}
|
||||
return valid_lines.ToArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Shadowsocks.Controller
|
||||
{
|
||||
@@ -22,6 +23,10 @@ namespace Shadowsocks.Controller
|
||||
|
||||
public event EventHandler PACFileChanged;
|
||||
|
||||
public event EventHandler UpdatePACFromGFWListCompleted;
|
||||
|
||||
public event ErrorEventHandler UpdatePACFromGFWListError;
|
||||
|
||||
public void Start(Configuration configuration)
|
||||
{
|
||||
try
|
||||
@@ -137,7 +142,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);
|
||||
}
|
||||
@@ -242,5 +247,117 @@ Connection: Close
|
||||
//}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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, Encoding.UTF8);
|
||||
if (UpdatePACFromGFWListCompleted != null)
|
||||
{
|
||||
UpdatePACFromGFWListCompleted(this, new EventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
private void gfwlist_Error(object sender, ErrorEventArgs e)
|
||||
{
|
||||
if (UpdatePACFromGFWListError != null)
|
||||
{
|
||||
UpdatePACFromGFWListError(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetAbpContent()
|
||||
{
|
||||
string content;
|
||||
if (File.Exists(PAC_FILE))
|
||||
{
|
||||
content = File.ReadAllText(PAC_FILE, Encoding.UTF8);
|
||||
Regex regex = new Regex("var\\s+rules\\s*=\\s*(\\[(\\s*\"[^\"]*\"\\s*,)*(\\s*\"[^\"]*\")\\s*\\])", RegexOptions.Singleline);
|
||||
Match m = regex.Match(content);
|
||||
if (m.Success)
|
||||
{
|
||||
content = regex.Replace(content, "var rules = __RULES__");
|
||||
return content;
|
||||
}
|
||||
}
|
||||
byte[] abpGZ = Resources.abp_js;
|
||||
byte[] buffer = new byte[1024]; // builtin pac gzip size: maximum 100K
|
||||
int n;
|
||||
using (MemoryStream sb = new MemoryStream())
|
||||
{
|
||||
using (GZipStream input = new GZipStream(new MemoryStream(abpGZ),
|
||||
CompressionMode.Decompress, false))
|
||||
{
|
||||
while ((n = input.Read(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
sb.Write(buffer, 0, n);
|
||||
}
|
||||
}
|
||||
content = System.Text.Encoding.UTF8.GetString(sb.ToArray());
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
private static void SerializeRules(string[] rules, StringBuilder builder)
|
||||
{
|
||||
builder.Append("[\n");
|
||||
|
||||
bool first = true;
|
||||
foreach (string rule in rules)
|
||||
{
|
||||
if (!first)
|
||||
builder.Append(",\n");
|
||||
|
||||
SerializeString(rule, builder);
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
builder.Append("\n]");
|
||||
}
|
||||
|
||||
private static void SerializeString(string aString, StringBuilder builder)
|
||||
{
|
||||
builder.Append("\t\"");
|
||||
|
||||
char[] charArray = aString.ToCharArray();
|
||||
for (int i = 0; i < charArray.Length; i++)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
builder.Append("\"");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,10 @@ namespace Shadowsocks.Controller
|
||||
// when user clicked Edit PAC, and PAC file has already created
|
||||
public event EventHandler<PathEventArgs> PACFileReadyToOpen;
|
||||
|
||||
public event EventHandler UpdatePACFromGFWListCompleted;
|
||||
|
||||
public event ErrorEventHandler UpdatePACFromGFWListError;
|
||||
|
||||
public event ErrorEventHandler Errored;
|
||||
|
||||
public ShadowsocksController()
|
||||
@@ -128,6 +132,11 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
polipoRunner.Stop();
|
||||
}
|
||||
if (pacServer != null)
|
||||
{
|
||||
pacServer.Stop();
|
||||
pacServer = null;
|
||||
}
|
||||
if (_config.enabled)
|
||||
{
|
||||
SystemProxy.Disable();
|
||||
@@ -151,6 +160,18 @@ namespace Shadowsocks.Controller
|
||||
return "ss://" + base64;
|
||||
}
|
||||
|
||||
public void UpdatePACFromGFWList()
|
||||
{
|
||||
if (pacServer != null)
|
||||
{
|
||||
pacServer.UpdatePACFromGFWList();
|
||||
}
|
||||
else if (UpdatePACFromGFWListError != null)
|
||||
{
|
||||
UpdatePACFromGFWListError(this, new ErrorEventArgs(new Exception("The PACServer is not run.")));
|
||||
}
|
||||
}
|
||||
|
||||
protected void Reload()
|
||||
{
|
||||
// some logic in configuration updated the config when saving, we need to read it again
|
||||
@@ -164,6 +185,8 @@ namespace Shadowsocks.Controller
|
||||
{
|
||||
pacServer = new PACServer();
|
||||
pacServer.PACFileChanged += pacServer_PACFileChanged;
|
||||
pacServer.UpdatePACFromGFWListCompleted += pacServer_UpdatePACFromGFWListCompleted;
|
||||
pacServer.UpdatePACFromGFWListError += pacServer_UpdatePACFromGFWListError;
|
||||
}
|
||||
|
||||
pacServer.Stop();
|
||||
@@ -242,6 +265,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));
|
||||
|
||||
Binary file not shown.
@@ -39,3 +39,7 @@ Shadowsocks is here=Shadowsocks 在这里
|
||||
You can turn on/off Shadowsocks in the context menu=可以在右键菜单中开关 Shadowsocks
|
||||
Enabled=已启用代理
|
||||
Disabled=已禁用代理
|
||||
Update PAC File via gfwlist...=基于 gfwlist 更新 PAC 文件...
|
||||
Update PAC file failed=更新 PAC 文件失败
|
||||
Update PAC file succeed=更新 PAC 文件成功
|
||||
Job already running...=任务已经运行...
|
||||
|
||||
@@ -45,6 +45,8 @@ namespace Shadowsocks
|
||||
controller.Start();
|
||||
|
||||
Application.Run();
|
||||
|
||||
controller.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-1
@@ -60,6 +60,16 @@ namespace Shadowsocks.Properties {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 string similar to Shadowsocks=Shadowsocks
|
||||
///Enable=启用代理
|
||||
@@ -68,7 +78,7 @@ namespace Shadowsocks.Properties {
|
||||
///Global=全局模式
|
||||
///Servers=服务器选择
|
||||
///Edit Servers...=编辑服务器...
|
||||
///Start on Boot=自动启动
|
||||
///Start on Boot=开机启动
|
||||
///Share over LAN=在局域网共享代理
|
||||
///Edit PAC File...=编辑 PAC 文件...
|
||||
///Show QRCode...=显示二维码...
|
||||
|
||||
@@ -118,6 +118,9 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.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" />
|
||||
<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="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>
|
||||
|
||||
@@ -33,6 +33,8 @@ namespace Shadowsocks.View
|
||||
private MenuItem PACModeItem;
|
||||
private ConfigForm configForm;
|
||||
|
||||
private bool isUpdatePACFromGFWListRunning = false;
|
||||
|
||||
public MenuViewController(ShadowsocksController controller)
|
||||
{
|
||||
this.controller = controller;
|
||||
@@ -45,6 +47,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 +142,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 +181,25 @@ namespace Shadowsocks.View
|
||||
System.Diagnostics.Process.Start("explorer.exe", argument);
|
||||
}
|
||||
|
||||
void controller_UpdatePACFromGFWListError(object sender, System.IO.ErrorEventArgs e)
|
||||
{
|
||||
isUpdatePACFromGFWListRunning = false;
|
||||
_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)
|
||||
{
|
||||
isUpdatePACFromGFWListRunning = false;
|
||||
_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 +335,26 @@ namespace Shadowsocks.View
|
||||
controller.TouchPACFile();
|
||||
}
|
||||
|
||||
private void UpdatePACFromGFWListItem_Click(object sender, EventArgs e)
|
||||
{
|
||||
if (isUpdatePACFromGFWListRunning)
|
||||
{
|
||||
_notifyIcon.BalloonTipTitle = I18N.GetString("Update PAC File via gfwlist...");
|
||||
_notifyIcon.BalloonTipText = I18N.GetString("Job already running...");
|
||||
_notifyIcon.BalloonTipIcon = ToolTipIcon.Info;
|
||||
_notifyIcon.ShowBalloonTip(5000);
|
||||
}
|
||||
else
|
||||
{
|
||||
isUpdatePACFromGFWListRunning = true;
|
||||
_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;
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
<Compile Include="3rd\zxing\Version.cs" />
|
||||
<Compile Include="Controller\AutoStartup.cs" />
|
||||
<Compile Include="Controller\FileManager.cs" />
|
||||
<Compile Include="Controller\GfwListUpdater.cs" />
|
||||
<Compile Include="Controller\I18N.cs" />
|
||||
<Compile Include="Controller\Logging.cs" />
|
||||
<Compile Include="Controller\UpdateChecker.cs" />
|
||||
@@ -101,6 +102,11 @@
|
||||
<Compile Include="Controller\PACServer.cs" />
|
||||
<Compile Include="Model\Server.cs" />
|
||||
<Compile Include="Model\Configuration.cs" />
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Util\Util.cs" />
|
||||
<Compile Include="View\ConfigForm.cs">
|
||||
<SubType>Form</SubType>
|
||||
@@ -127,14 +133,9 @@
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
<DesignTime>True</DesignTime>
|
||||
</Compile>
|
||||
<EmbeddedResource Include="View\QRCodeForm.resx">
|
||||
<DependentUpon>QRCodeForm.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
@@ -142,6 +143,7 @@
|
||||
<None Include="app.manifest">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
<None Include="Data\abp.js.gz" />
|
||||
<None Include="Data\libsscrypto.dll.gz" />
|
||||
<None Include="Data\polipo.exe.gz" />
|
||||
<None Include="Data\proxy.pac.txt.gz" />
|
||||
|
||||
Reference in New Issue
Block a user