Conflicts: shadowsocks-csharp/Data/cn.txtpull/76/head
@@ -30,6 +30,10 @@ about the change automatically | |||
6. Please disable other proxy addons in your browser, or set them to use | |||
system proxy | |||
### Develop | |||
Visual Studio Express 2012 is recommended. | |||
#### License | |||
GPLv3 | |||
@@ -1035,13 +1035,13 @@ namespace SimpleJson | |||
protected static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder) | |||
{ | |||
builder.Append("["); | |||
builder.Append("[\r\n "); | |||
bool first = true; | |||
foreach (object value in anArray) | |||
{ | |||
if (!first) | |||
builder.Append(","); | |||
builder.Append(",\r\n "); | |||
if (!SerializeValue(jsonSerializerStrategy, value, builder)) | |||
return false; | |||
@@ -1049,7 +1049,7 @@ namespace SimpleJson | |||
first = false; | |||
} | |||
builder.Append("]"); | |||
builder.Append("\r\n]"); | |||
return true; | |||
} | |||
@@ -0,0 +1,68 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Text; | |||
using System.Net; | |||
using System.IO; | |||
using Shadowsocks.Properties; | |||
using SimpleJson; | |||
using Shadowsocks.Util; | |||
namespace Shadowsocks.Controller | |||
{ | |||
public class GFWListUpdater | |||
{ | |||
private const string GFWLIST_URL = "https://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt"; | |||
private static string PAC_FILE = PACServer.PAC_FILE; | |||
public event EventHandler UpdateCompleted; | |||
public event ErrorEventHandler Error; | |||
private void http_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) | |||
{ | |||
try | |||
{ | |||
List<string> lines = ParseResult(e.Result); | |||
string abpContent = Utils.UnGzip(Resources.abp_js); | |||
abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines)); | |||
File.WriteAllText(PAC_FILE, abpContent, Encoding.UTF8); | |||
if (UpdateCompleted != null) | |||
{ | |||
UpdateCompleted(this, new EventArgs()); | |||
} | |||
} | |||
catch (Exception ex) | |||
{ | |||
if (Error != null) | |||
{ | |||
Error(this, new ErrorEventArgs(ex)); | |||
} | |||
} | |||
} | |||
public void UpdatePACFromGFWList() | |||
{ | |||
WebClient http = new WebClient(); | |||
http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), 8123); | |||
http.DownloadStringCompleted += http_DownloadStringCompleted; | |||
http.DownloadStringAsync(new Uri(GFWLIST_URL)); | |||
} | |||
public List<string> ParseResult(string response) | |||
{ | |||
byte[] bytes = Convert.FromBase64String(response); | |||
string content = Encoding.ASCII.GetString(bytes); | |||
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; | |||
} | |||
} | |||
} |
@@ -17,8 +17,7 @@ namespace Shadowsocks.Controller | |||
string temppath = Path.GetTempPath(); | |||
LogFile = Path.Combine(temppath, "shadowsocks.log"); | |||
FileStream fs = new FileStream(LogFile, FileMode.Append); | |||
TextWriter tmp = Console.Out; | |||
StreamWriter sw = new StreamWriter(fs); | |||
StreamWriterWithTimestamp sw = new StreamWriterWithTimestamp(fs); | |||
sw.AutoFlush = true; | |||
Console.SetOut(sw); | |||
Console.SetError(sw); | |||
@@ -61,5 +60,30 @@ namespace Shadowsocks.Controller | |||
Console.WriteLine(e); | |||
} | |||
} | |||
} | |||
// Simply extended System.IO.StreamWriter for adding timestamp workaround | |||
public class StreamWriterWithTimestamp : StreamWriter | |||
{ | |||
public StreamWriterWithTimestamp(Stream stream) : base(stream) | |||
{ | |||
} | |||
private string GetTimestamp() | |||
{ | |||
return "[" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "] "; | |||
} | |||
public override void WriteLine(string value) | |||
{ | |||
base.WriteLine(GetTimestamp() + value); | |||
} | |||
public override void Write(string value) | |||
{ | |||
base.Write(GetTimestamp() + value); | |||
} | |||
} | |||
} |
@@ -1,5 +1,6 @@ | |||
using Shadowsocks.Model; | |||
using Shadowsocks.Properties; | |||
using Shadowsocks.Util; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
@@ -14,7 +15,7 @@ namespace Shadowsocks.Controller | |||
class PACServer | |||
{ | |||
private static int PORT = 8093; | |||
private static string PAC_FILE = "pac.txt"; | |||
public static string PAC_FILE = "pac.txt"; | |||
private static Configuration config; | |||
Socket _listener; | |||
@@ -130,19 +131,7 @@ namespace Shadowsocks.Controller | |||
} | |||
else | |||
{ | |||
byte[] pacGZ = Resources.proxy_pac_txt; | |||
byte[] buffer = new byte[1024]; // builtin pac gzip size: maximum 100K | |||
MemoryStream sb = new MemoryStream(); | |||
int n; | |||
using (GZipStream input = new GZipStream(new MemoryStream(pacGZ), | |||
CompressionMode.Decompress, false)) | |||
{ | |||
while((n = input.Read(buffer, 0, buffer.Length)) > 0) | |||
{ | |||
sb.Write(buffer, 0, n); | |||
} | |||
return System.Text.Encoding.UTF8.GetString(sb.ToArray()); | |||
} | |||
return Utils.UnGzip(Resources.proxy_pac_txt); | |||
} | |||
} | |||
@@ -175,7 +164,7 @@ Connection: Close | |||
", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac; | |||
byte[] response = System.Text.Encoding.UTF8.GetBytes(text); | |||
conn.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), conn); | |||
Util.Util.ReleaseMemory(); | |||
Util.Utils.ReleaseMemory(); | |||
} | |||
else | |||
{ | |||
@@ -21,6 +21,7 @@ namespace Shadowsocks.Controller | |||
private PACServer pacServer; | |||
private Configuration _config; | |||
private PolipoRunner polipoRunner; | |||
private GFWListUpdater gfwListUpdater; | |||
private bool stopped = false; | |||
private bool _systemProxyIsDirty = false; | |||
@@ -38,6 +39,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() | |||
@@ -151,6 +156,14 @@ namespace Shadowsocks.Controller | |||
return "ss://" + base64; | |||
} | |||
public void UpdatePACFromGFWList() | |||
{ | |||
if (gfwListUpdater != null) | |||
{ | |||
gfwListUpdater.UpdatePACFromGFWList(); | |||
} | |||
} | |||
protected void Reload() | |||
{ | |||
// some logic in configuration updated the config when saving, we need to read it again | |||
@@ -165,6 +178,12 @@ namespace Shadowsocks.Controller | |||
pacServer = new PACServer(); | |||
pacServer.PACFileChanged += pacServer_PACFileChanged; | |||
} | |||
if (gfwListUpdater == null) | |||
{ | |||
gfwListUpdater = new GFWListUpdater(); | |||
gfwListUpdater.UpdateCompleted += pacServer_PACUpdateCompleted; | |||
gfwListUpdater.Error += pacServer_PACUpdateError; | |||
} | |||
pacServer.Stop(); | |||
@@ -208,7 +227,7 @@ namespace Shadowsocks.Controller | |||
} | |||
UpdateSystemProxy(); | |||
Util.Util.ReleaseMemory(); | |||
Util.Utils.ReleaseMemory(); | |||
} | |||
@@ -242,6 +261,18 @@ namespace Shadowsocks.Controller | |||
UpdateSystemProxy(); | |||
} | |||
private void pacServer_PACUpdateCompleted(object sender, EventArgs e) | |||
{ | |||
if (UpdatePACFromGFWListCompleted != null) | |||
UpdatePACFromGFWListCompleted(this, e); | |||
} | |||
private void pacServer_PACUpdateError(object sender, ErrorEventArgs e) | |||
{ | |||
if (UpdatePACFromGFWListError != null) | |||
UpdatePACFromGFWListError(this, e); | |||
} | |||
private void StartReleasingMemory() | |||
{ | |||
_ramThread = new Thread(new ThreadStart(ReleaseMemory)); | |||
@@ -253,7 +284,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
while (true) | |||
{ | |||
Util.Util.ReleaseMemory(); | |||
Util.Utils.ReleaseMemory(); | |||
Thread.Sleep(30 * 1000); | |||
} | |||
} | |||
@@ -23,6 +23,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
// TODO test failures | |||
WebClient http = new WebClient(); | |||
http.Proxy = new WebProxy(IPAddress.Loopback.ToString(), 8123); | |||
http.DownloadStringCompleted += http_DownloadStringCompleted; | |||
http.DownloadStringAsync(new Uri(UpdateURL)); | |||
} | |||
@@ -145,7 +146,7 @@ namespace Shadowsocks.Controller | |||
} | |||
catch (Exception ex) | |||
{ | |||
Console.Write(ex.ToString()); | |||
Console.WriteLine(ex.ToString()); | |||
return; | |||
} | |||
} | |||
@@ -4,8 +4,8 @@ Mode=代理模式 | |||
PAC=PAC 模式 | |||
Global=全局模式 | |||
Servers=服务器选择 | |||
Edit Servers...=编辑Shadowsocks服务器... | |||
Start on Boot=开机启动 | |||
Edit Servers...=编辑Shadowsocks服务器... | |||
Start on Boot=开机启动 | |||
Share over LAN=在局域网共享代理 | |||
Edit PAC File...=编辑 PAC 文件... | |||
Show QRCode...=显示二维码... | |||
@@ -39,6 +39,9 @@ Shadowsocks is here=Shadowsocks 在这里 | |||
You can turn on/off Shadowsocks in the context menu=可以在右键菜单中开关 Shadowsocks | |||
Enabled=已启用代理 | |||
Disabled=已禁用代理 | |||
Update PAC from GFWList=从 GFWList 更新 PAC | |||
Failed to update PAC file =更新 PAC 文件失败 | |||
PAC updated=更新 PAC 成功 | |||
&Parse=解析(&P) | |||
Shadowsocks URI=Shadowsocks URI | |||
Shadowsocks URI Parse=Shadowsocks URI解析 | |||
@@ -18,7 +18,7 @@ namespace Shadowsocks | |||
[STAThread] | |||
static void Main() | |||
{ | |||
Util.Util.ReleaseMemory(); | |||
Util.Utils.ReleaseMemory(); | |||
using (Mutex mutex = new Mutex(false, "Global\\" + "71981632-A427-497F-AB91-241CD227EC1F")) | |||
{ | |||
Application.EnableVisualStyles(); | |||
@@ -61,6 +61,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=启用代理 | |||
///Mode=代理模式 | |||
@@ -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> | |||
@@ -1,12 +1,14 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Diagnostics; | |||
using System.IO; | |||
using System.IO.Compression; | |||
using System.Runtime.InteropServices; | |||
using System.Text; | |||
namespace Shadowsocks.Util | |||
{ | |||
public class Util | |||
public class Utils | |||
{ | |||
public static void ReleaseMemory() | |||
{ | |||
@@ -22,6 +24,24 @@ namespace Shadowsocks.Util | |||
(UIntPtr)0xFFFFFFFF, (UIntPtr)0xFFFFFFFF); | |||
} | |||
public static string UnGzip(byte[] buf) | |||
{ | |||
byte[] buffer = new byte[1024]; | |||
int n; | |||
using (MemoryStream sb = new MemoryStream()) | |||
{ | |||
using (GZipStream input = new GZipStream(new MemoryStream(buf), | |||
CompressionMode.Decompress, false)) | |||
{ | |||
while ((n = input.Read(buffer, 0, buffer.Length)) > 0) | |||
{ | |||
sb.Write(buffer, 0, n); | |||
} | |||
} | |||
return System.Text.Encoding.UTF8.GetString(sb.ToArray()); | |||
} | |||
} | |||
[DllImport("kernel32.dll")] | |||
[return: MarshalAs(UnmanagedType.Bool)] | |||
private static extern bool SetProcessWorkingSetSize(IntPtr process, | |||
@@ -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 from 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,19 +179,36 @@ namespace Shadowsocks.View | |||
System.Diagnostics.Process.Start("explorer.exe", argument); | |||
} | |||
void ShowBalloonTip(string title, string content, ToolTipIcon icon, int timeout) | |||
{ | |||
_notifyIcon.BalloonTipTitle = title; | |||
_notifyIcon.BalloonTipText = content; | |||
_notifyIcon.BalloonTipIcon = icon; | |||
_notifyIcon.ShowBalloonTip(timeout); | |||
} | |||
void controller_UpdatePACFromGFWListError(object sender, System.IO.ErrorEventArgs e) | |||
{ | |||
ShowBalloonTip(I18N.GetString("Failed to update PAC file"), e.GetException().Message, ToolTipIcon.Error, 5000); | |||
Logging.LogUsefulException(e.GetException()); | |||
} | |||
void controller_UpdatePACFromGFWListCompleted(object sender, EventArgs e) | |||
{ | |||
ShowBalloonTip(I18N.GetString("Shadowsocks"), I18N.GetString("PAC updated"), ToolTipIcon.Info, 1000); | |||
} | |||
void updateChecker_NewVersionFound(object sender, EventArgs e) | |||
{ | |||
_notifyIcon.BalloonTipTitle = String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber); | |||
_notifyIcon.BalloonTipText = I18N.GetString("Click here to download"); | |||
_notifyIcon.BalloonTipIcon = ToolTipIcon.Info; | |||
ShowBalloonTip(String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber), I18N.GetString("Click here to download"), ToolTipIcon.Info, 5000); | |||
_notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked; | |||
_notifyIcon.ShowBalloonTip(5000); | |||
_isFirstRun = false; | |||
} | |||
void notifyIcon1_BalloonTipClicked(object sender, EventArgs e) | |||
{ | |||
System.Diagnostics.Process.Start(updateChecker.LatestVersionURL); | |||
_notifyIcon.BalloonTipClicked -= notifyIcon1_BalloonTipClicked; | |||
} | |||
@@ -245,7 +265,7 @@ namespace Shadowsocks.View | |||
void configForm_FormClosed(object sender, FormClosedEventArgs e) | |||
{ | |||
configForm = null; | |||
Util.Util.ReleaseMemory(); | |||
Util.Utils.ReleaseMemory(); | |||
ShowFirstTimeBalloon(); | |||
} | |||
@@ -312,6 +332,11 @@ namespace Shadowsocks.View | |||
controller.TouchPACFile(); | |||
} | |||
private void UpdatePACFromGFWListItem_Click(object sender, EventArgs e) | |||
{ | |||
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> | |||
@@ -133,14 +139,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> | |||
@@ -151,6 +152,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" /> | |||