From e0612854d65dd121a2b4b6e1ee6b3ac60d81c2d9 Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Fri, 20 Mar 2015 00:39:31 -0400 Subject: [PATCH] support online pac, see https://github.com/shadowsocks/shadowsocks-csharp/issues/163 --- shadowsocks-csharp/Controller/PACServer.cs | 29 ++++- .../Controller/ShadowsocksController.cs | 22 ++++ shadowsocks-csharp/Data/cn.txt | 8 +- shadowsocks-csharp/Model/Configuration.cs | 2 + shadowsocks-csharp/View/MenuViewController.cs | 65 +++++++++- .../View/PACUrlForm.Designer.cs | 105 +++++++++++++++ shadowsocks-csharp/View/PACUrlForm.cs | 69 ++++++++++ shadowsocks-csharp/View/PACUrlForm.resx | 120 ++++++++++++++++++ shadowsocks-csharp/shadowsocks-csharp.csproj | 9 ++ 9 files changed, 423 insertions(+), 6 deletions(-) create mode 100644 shadowsocks-csharp/View/PACUrlForm.Designer.cs create mode 100644 shadowsocks-csharp/View/PACUrlForm.cs create mode 100644 shadowsocks-csharp/View/PACUrlForm.resx diff --git a/shadowsocks-csharp/Controller/PACServer.cs b/shadowsocks-csharp/Controller/PACServer.cs index e193d9b4..b987e84d 100755 --- a/shadowsocks-csharp/Controller/PACServer.cs +++ b/shadowsocks-csharp/Controller/PACServer.cs @@ -71,7 +71,10 @@ namespace Shadowsocks.Controller } if (hostMatch && pathMatch) { - SendResponse(firstPacket, length, socket, useSocks); + if (_config.useOnlinePac && !string.IsNullOrEmpty(_config.pacUrl)) + RedirectToOnlinePAC(firstPacket, length, socket, useSocks); + else + SendResponse(firstPacket, length, socket, useSocks); return true; } return false; @@ -121,6 +124,30 @@ namespace Shadowsocks.Controller } } + private void RedirectToOnlinePAC(byte[] firstPacket, int length, Socket socket, bool useSocks) + { + try + { + string friendlyMessage = "Redirect to online pac " + _config.pacUrl; + string text = String.Format(@"HTTP/1.1 302 Found +Server: Shadowsocks +Content-Type: text/html; charset=utf-8 +Location: {0} +Content-Length: {1} +Connection: Close + +", _config.pacUrl, System.Text.Encoding.UTF8.GetBytes(friendlyMessage).Length) + friendlyMessage; + byte[] response = System.Text.Encoding.UTF8.GetBytes(text); + socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket); + Util.Utils.ReleaseMemory(); + } + catch (Exception e) + { + Console.WriteLine(e); + socket.Close(); + } + } + public void SendResponse(byte[] firstPacket, int length, Socket socket, bool useSocks) { try diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index 8d271372..734f27d1 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -192,6 +192,28 @@ namespace Shadowsocks.Controller } } + public void SavePACUrl(string pacUrl) + { + _config.pacUrl = pacUrl; + UpdateSystemProxy(); + SaveConfig(_config); + if (ConfigChanged != null) + { + ConfigChanged(this, new EventArgs()); + } + } + + public void UseOnlinePAC(bool useOnlinePac) + { + _config.useOnlinePac = useOnlinePac; + UpdateSystemProxy(); + SaveConfig(_config); + if (ConfigChanged != null) + { + ConfigChanged(this, new EventArgs()); + } + } + protected void Reload() { // some logic in configuration updated the config when saving, we need to read it again diff --git a/shadowsocks-csharp/Data/cn.txt b/shadowsocks-csharp/Data/cn.txt index 486ba27d..c3fc26f1 100644 --- a/shadowsocks-csharp/Data/cn.txt +++ b/shadowsocks-csharp/Data/cn.txt @@ -40,6 +40,11 @@ New server=未配置的服务器 QRCode=二维码 +# PAC Url Form + +Update Online PAC URL=更新在线 PAC 网址 +PAC Url=PAC 网址 + # Messages Shadowsocks Error: {0}=Shadowsocks 错误: {0} @@ -63,4 +68,5 @@ No QRCode found. Try to zoom in or move it to the center of the screen.=未发 Failed to decode QRCode=无法解析二维码 Failed to update registry=无法修改注册表 System Proxy On: =系统代理已启用: -Running: Port {0}=正在运行:端口 {0} \ No newline at end of file +Running: Port {0}=正在运行:端口 {0} +PAC Url can not be blank=PAC 网址不能为空 diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs index 56c91759..b78358e8 100755 --- a/shadowsocks-csharp/Model/Configuration.cs +++ b/shadowsocks-csharp/Model/Configuration.cs @@ -17,6 +17,8 @@ namespace Shadowsocks.Model public bool shareOverLan; public bool isDefault; public int localPort; + public string pacUrl; + public bool useOnlinePac; private static string CONFIG_FILE = "gui-config.json"; diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index 0660f182..f60be124 100755 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -35,7 +35,10 @@ namespace Shadowsocks.View private MenuItem ServersItem; private MenuItem globalModeItem; private MenuItem PACModeItem; + private MenuItem localPACItem; + private MenuItem onlinePACItem; private ConfigForm configForm; + private PACUrlForm pacUrlForm; private string _urlToOpen; public MenuViewController(ShadowsocksController controller) @@ -151,10 +154,15 @@ namespace Shadowsocks.View CreateMenuItem("Show QRCode...", new EventHandler(this.QRCodeItem_Click)), CreateMenuItem("Scan QRCode from Screen...", new EventHandler(this.ScanQRCodeItem_Click)) }), - new MenuItem("-"), - CreateMenuItem("Edit PAC File...", new EventHandler(this.EditPACFileItem_Click)), - CreateMenuItem("Update PAC from GFWList", new EventHandler(this.UpdatePACFromGFWListItem_Click)), - CreateMenuItem("Edit User Rule for GFWList...", new EventHandler(this.EditUserRuleFileForGFWListItem_Click)), + CreateMenuGroup("PAC", new MenuItem[] { + this.localPACItem = CreateMenuItem("Local PAC", new EventHandler(this.LocalPACItem_Click)), + this.onlinePACItem = CreateMenuItem("Online PAC", new EventHandler(this.OnlinePACItem_Click)), + new MenuItem("-"), + CreateMenuItem("Edit PAC File...", new EventHandler(this.EditPACFileItem_Click)), + CreateMenuItem("Update PAC from GFWList", new EventHandler(this.UpdatePACFromGFWListItem_Click)), + CreateMenuItem("Edit User Rule for GFWList...", new EventHandler(this.EditUserRuleFileForGFWListItem_Click)), + CreateMenuItem("Update Online PAC URL", new EventHandler(this.UpdateOnlinePACURLItem_Click)), + }), new MenuItem("-"), this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)), this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)), @@ -240,6 +248,9 @@ namespace Shadowsocks.View PACModeItem.Checked = !config.global; ShareOverLANItem.Checked = config.shareOverLan; AutoStartupItem.Checked = AutoStartup.Check(); + onlinePACItem.Enabled = !string.IsNullOrEmpty(config.pacUrl); + onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; + localPACItem.Checked = !onlinePACItem.Checked; } private void UpdateServersMenu() @@ -481,5 +492,51 @@ namespace Shadowsocks.View MessageBox.Show(I18N.GetString("Failed to update registry")); } } + + private void LocalPACItem_Click(object sender, EventArgs e) + { + if (!localPACItem.Checked) + { + localPACItem.Checked = true; + onlinePACItem.Checked = false; + controller.UseOnlinePAC(false); + } + } + + private void OnlinePACItem_Click(object sender, EventArgs e) + { + if (!onlinePACItem.Checked) + { + localPACItem.Checked = false; + onlinePACItem.Checked = true; + controller.UseOnlinePAC(true); + } + } + + private void showPACUrlForm() + { + if (pacUrlForm != null) + { + pacUrlForm.Activate(); + } + else + { + pacUrlForm = new PACUrlForm(controller); + pacUrlForm.Show(); + pacUrlForm.FormClosed += pacUrlForm_FormClosed; + } + } + + private void pacUrlForm_FormClosed(object sender, FormClosedEventArgs e) + { + pacUrlForm = null; + Util.Utils.ReleaseMemory(); + ShowFirstTimeBalloon(); + } + + private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e) + { + showPACUrlForm(); + } } } diff --git a/shadowsocks-csharp/View/PACUrlForm.Designer.cs b/shadowsocks-csharp/View/PACUrlForm.Designer.cs new file mode 100644 index 00000000..feff4bb1 --- /dev/null +++ b/shadowsocks-csharp/View/PACUrlForm.Designer.cs @@ -0,0 +1,105 @@ +namespace Shadowsocks.View +{ + partial class PACUrlForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.PACUrlTextBox = new System.Windows.Forms.TextBox(); + this.PACUrlLabel = new System.Windows.Forms.Label(); + this.OkButton = new System.Windows.Forms.Button(); + this.CancelButton = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // PACUrlTextBox + // + this.PACUrlTextBox.Location = new System.Drawing.Point(61, 12); + this.PACUrlTextBox.MaxLength = 256; + this.PACUrlTextBox.Name = "PACUrlTextBox"; + this.PACUrlTextBox.Size = new System.Drawing.Size(245, 20); + this.PACUrlTextBox.TabIndex = 4; + this.PACUrlTextBox.WordWrap = false; + // + // PACUrlLabel + // + this.PACUrlLabel.AutoSize = true; + this.PACUrlLabel.Location = new System.Drawing.Point(6, 15); + this.PACUrlLabel.Name = "PACUrlLabel"; + this.PACUrlLabel.Size = new System.Drawing.Size(44, 13); + this.PACUrlLabel.TabIndex = 3; + this.PACUrlLabel.Text = "PAC Url"; + // + // OkButton + // + this.OkButton.Location = new System.Drawing.Point(150, 50); + this.OkButton.Name = "OkButton"; + this.OkButton.Size = new System.Drawing.Size(75, 23); + this.OkButton.TabIndex = 5; + this.OkButton.Text = "OK"; + this.OkButton.UseVisualStyleBackColor = true; + this.OkButton.Click += new System.EventHandler(this.OkButton_Click); + // + // CancelButton + // + this.CancelButton.Location = new System.Drawing.Point(231, 50); + this.CancelButton.Name = "CancelButton"; + this.CancelButton.Size = new System.Drawing.Size(75, 23); + this.CancelButton.TabIndex = 6; + this.CancelButton.Text = "Cancel"; + this.CancelButton.UseVisualStyleBackColor = true; + this.CancelButton.Click += new System.EventHandler(this.CancelButton_Click); + // + // PACUrlForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoSize = true; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.ClientSize = new System.Drawing.Size(327, 88); + this.Controls.Add(this.CancelButton); + this.Controls.Add(this.OkButton); + this.Controls.Add(this.PACUrlTextBox); + this.Controls.Add(this.PACUrlLabel); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "PACUrlForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Update Online PAC URL"; + this.Load += new System.EventHandler(this.PACUrlForm_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox PACUrlTextBox; + private System.Windows.Forms.Label PACUrlLabel; + private System.Windows.Forms.Button OkButton; + private System.Windows.Forms.Button CancelButton; + } +} \ No newline at end of file diff --git a/shadowsocks-csharp/View/PACUrlForm.cs b/shadowsocks-csharp/View/PACUrlForm.cs new file mode 100644 index 00000000..d2232700 --- /dev/null +++ b/shadowsocks-csharp/View/PACUrlForm.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using Shadowsocks.Controller; +using Shadowsocks.Model; +using Shadowsocks.Properties; + +namespace Shadowsocks.View +{ + public partial class PACUrlForm : Form + { + private ShadowsocksController controller; + private string orig_pacUrl; + + public PACUrlForm(ShadowsocksController controller) + { + this.Font = System.Drawing.SystemFonts.MessageBoxFont; + InitializeComponent(); + + UpdateTexts(); + this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); + + this.controller = controller; + controller.ConfigChanged += controller_ConfigChanged; + } + + private void UpdateTexts() + { + OkButton.Text = I18N.GetString("OK"); + CancelButton.Text = I18N.GetString("Cancel"); + PACUrlLabel.Text = I18N.GetString("PAC Url"); + this.Text = I18N.GetString("Update Online PAC URL"); + } + + private void controller_ConfigChanged(object sender, EventArgs e) + { + orig_pacUrl = PACUrlTextBox.Text = controller.GetConfiguration().pacUrl; + } + + private void PACUrlForm_Load(object sender, EventArgs e) + { + PACUrlTextBox.Text = controller.GetConfiguration().pacUrl; + } + + private void OkButton_Click(object sender, EventArgs e) + { + string pacUrl = PACUrlTextBox.Text.Trim(); + if (string.IsNullOrEmpty(pacUrl)) + { + MessageBox.Show(I18N.GetString("PAC Url can not be blank")); + return; + } + if (pacUrl != this.orig_pacUrl) + { + controller.SavePACUrl(pacUrl); + } + this.Close(); + } + + private void CancelButton_Click(object sender, EventArgs e) + { + this.Close(); + } + } +} diff --git a/shadowsocks-csharp/View/PACUrlForm.resx b/shadowsocks-csharp/View/PACUrlForm.resx new file mode 100644 index 00000000..7080a7d1 --- /dev/null +++ b/shadowsocks-csharp/View/PACUrlForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index fd849f85..273b9f52 100755 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -162,6 +162,12 @@ + + Form + + + PACUrlForm.cs + Form @@ -180,6 +186,9 @@ Designer Resources.Designer.cs + + PACUrlForm.cs + QRCodeForm.cs