From 6ed32ff7094e9776262bf113bf4c51f60ebeb8dc Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Thu, 20 Aug 2015 17:17:21 +0800 Subject: [PATCH 01/54] improved `log viewer`: 1. add menu items for `clear logs`, `change fonts`, `wrap text` and `top most` functions. 2. hide toolbar default. but you can trigger it out by menu. --- shadowsocks-csharp/Data/cn.txt | 6 +- shadowsocks-csharp/View/LogForm.Designer.cs | 159 ++++++++++++++------ shadowsocks-csharp/View/LogForm.cs | 109 +++++++++++++- shadowsocks-csharp/View/LogForm.resx | 5 +- 4 files changed, 217 insertions(+), 62 deletions(-) diff --git a/shadowsocks-csharp/Data/cn.txt b/shadowsocks-csharp/Data/cn.txt index 81022f99..79f8d6f4 100644 --- a/shadowsocks-csharp/Data/cn.txt +++ b/shadowsocks-csharp/Data/cn.txt @@ -50,10 +50,12 @@ Move D&own=下移(&O) &File=文件(&F) &Open Location=在资源管理器中打开(&O) E&xit=退出(&X) -&Clean logs=清空(&C) -&Font=字体(&F) +&View=视图(&V) +&Clean logs=清空日志(&C) +Change &font=设置字体(&F) &Wrap text=自动换行(&W) &Top most=置顶(&T) +&Show toolbar=显示工具栏(&S) Log Viewer=日志查看器 # QRCode Form diff --git a/shadowsocks-csharp/View/LogForm.Designer.cs b/shadowsocks-csharp/View/LogForm.Designer.cs index d0913d16..187ab948 100644 --- a/shadowsocks-csharp/View/LogForm.Designer.cs +++ b/shadowsocks-csharp/View/LogForm.Designer.cs @@ -30,19 +30,25 @@ { this.components = new System.ComponentModel.Container(); this.LogMessageTextBox = new System.Windows.Forms.TextBox(); - this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); - this.mainMenu1 = new System.Windows.Forms.MainMenu(this.components); + this.MainMenu = new System.Windows.Forms.MainMenu(this.components); this.FileMenuItem = new System.Windows.Forms.MenuItem(); this.OpenLocationMenuItem = new System.Windows.Forms.MenuItem(); this.ExitMenuItem = new System.Windows.Forms.MenuItem(); - this.panel1 = new System.Windows.Forms.Panel(); + this.ViewMenuItem = new System.Windows.Forms.MenuItem(); + this.CleanLogsMenuItem = new System.Windows.Forms.MenuItem(); + this.ChangeFontMenuItem = new System.Windows.Forms.MenuItem(); + this.WrapTextMenuItem = new System.Windows.Forms.MenuItem(); + this.TopMostMenuItem = new System.Windows.Forms.MenuItem(); + this.MenuItemSeparater = new System.Windows.Forms.MenuItem(); + this.ShowToolbarMenuItem = new System.Windows.Forms.MenuItem(); + this.TopMostCheckBox = new System.Windows.Forms.CheckBox(); this.ChangeFontButton = new System.Windows.Forms.Button(); this.CleanLogsButton = new System.Windows.Forms.Button(); this.WrapTextCheckBox = new System.Windows.Forms.CheckBox(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); - this.TopMostCheckBox = new System.Windows.Forms.CheckBox(); - this.panel1.SuspendLayout(); + this.ToolbarFlowLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); this.tableLayoutPanel1.SuspendLayout(); + this.ToolbarFlowLayoutPanel.SuspendLayout(); this.SuspendLayout(); // // LogMessageTextBox @@ -51,25 +57,20 @@ this.LogMessageTextBox.Dock = System.Windows.Forms.DockStyle.Fill; this.LogMessageTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.LogMessageTextBox.ForeColor = System.Drawing.Color.White; - this.LogMessageTextBox.Location = new System.Drawing.Point(3, 43); + this.LogMessageTextBox.Location = new System.Drawing.Point(3, 38); this.LogMessageTextBox.MaxLength = 2147483647; this.LogMessageTextBox.Multiline = true; this.LogMessageTextBox.Name = "LogMessageTextBox"; this.LogMessageTextBox.ReadOnly = true; this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; - this.LogMessageTextBox.Size = new System.Drawing.Size(541, 307); + this.LogMessageTextBox.Size = new System.Drawing.Size(584, 377); this.LogMessageTextBox.TabIndex = 0; - this.LogMessageTextBox.WordWrap = false; - // - // contextMenuStrip1 // - this.contextMenuStrip1.Name = "contextMenuStrip1"; - this.contextMenuStrip1.Size = new System.Drawing.Size(61, 4); + // MainMenu // - // mainMenu1 - // - this.mainMenu1.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { - this.FileMenuItem}); + this.MainMenu.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.FileMenuItem, + this.ViewMenuItem}); // // FileMenuItem // @@ -91,21 +92,70 @@ this.ExitMenuItem.Text = "E&xit"; this.ExitMenuItem.Click += new System.EventHandler(this.ExitMenuItem_Click); // - // panel1 + // ViewMenuItem + // + this.ViewMenuItem.Index = 1; + this.ViewMenuItem.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] { + this.CleanLogsMenuItem, + this.ChangeFontMenuItem, + this.WrapTextMenuItem, + this.TopMostMenuItem, + this.MenuItemSeparater, + this.ShowToolbarMenuItem}); + this.ViewMenuItem.Text = "&View"; + // + // CleanLogsMenuItem + // + this.CleanLogsMenuItem.Index = 0; + this.CleanLogsMenuItem.Text = "&Clean logs"; + this.CleanLogsMenuItem.Click += new System.EventHandler(this.CleanLogsMenuItem_Click); + // + // ChangeFontMenuItem + // + this.ChangeFontMenuItem.Index = 1; + this.ChangeFontMenuItem.Text = "Change &font"; + this.ChangeFontMenuItem.Click += new System.EventHandler(this.ChangeFontMenuItem_Click); // - this.panel1.Controls.Add(this.TopMostCheckBox); - this.panel1.Controls.Add(this.ChangeFontButton); - this.panel1.Controls.Add(this.CleanLogsButton); - this.panel1.Controls.Add(this.WrapTextCheckBox); - this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; - this.panel1.Location = new System.Drawing.Point(3, 3); - this.panel1.Name = "panel1"; - this.panel1.Size = new System.Drawing.Size(541, 34); - this.panel1.TabIndex = 1; + // WrapTextMenuItem + // + this.WrapTextMenuItem.Index = 2; + this.WrapTextMenuItem.Text = "&Wrap text"; + this.WrapTextMenuItem.Click += new System.EventHandler(this.WrapTextMenuItem_Click); + // + // TopMostMenuItem + // + this.TopMostMenuItem.Index = 3; + this.TopMostMenuItem.Text = "&Top most"; + this.TopMostMenuItem.Click += new System.EventHandler(this.TopMostMenuItem_Click); + // + // MenuItemSeparater + // + this.MenuItemSeparater.Index = 4; + this.MenuItemSeparater.Text = "-"; + // + // ShowToolbarMenuItem + // + this.ShowToolbarMenuItem.Index = 5; + this.ShowToolbarMenuItem.Text = "&Show toolbar"; + this.ShowToolbarMenuItem.Click += new System.EventHandler(this.ShowToolbarMenuItem_Click); + // + // TopMostCheckBox + // + this.TopMostCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.TopMostCheckBox.AutoSize = true; + this.TopMostCheckBox.Location = new System.Drawing.Point(249, 3); + this.TopMostCheckBox.Name = "TopMostCheckBox"; + this.TopMostCheckBox.Size = new System.Drawing.Size(72, 23); + this.TopMostCheckBox.TabIndex = 3; + this.TopMostCheckBox.Text = "&Top most"; + this.TopMostCheckBox.UseVisualStyleBackColor = true; + this.TopMostCheckBox.CheckedChanged += new System.EventHandler(this.TopMostCheckBox_CheckedChanged); // // ChangeFontButton // - this.ChangeFontButton.Location = new System.Drawing.Point(107, 4); + this.ChangeFontButton.AutoSize = true; + this.ChangeFontButton.Location = new System.Drawing.Point(84, 3); this.ChangeFontButton.Name = "ChangeFontButton"; this.ChangeFontButton.Size = new System.Drawing.Size(75, 23); this.ChangeFontButton.TabIndex = 2; @@ -115,7 +165,8 @@ // // CleanLogsButton // - this.CleanLogsButton.Location = new System.Drawing.Point(9, 4); + this.CleanLogsButton.AutoSize = true; + this.CleanLogsButton.Location = new System.Drawing.Point(3, 3); this.CleanLogsButton.Name = "CleanLogsButton"; this.CleanLogsButton.Size = new System.Drawing.Size(75, 23); this.CleanLogsButton.TabIndex = 1; @@ -125,10 +176,12 @@ // // WrapTextCheckBox // + this.WrapTextCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); this.WrapTextCheckBox.AutoSize = true; - this.WrapTextCheckBox.Location = new System.Drawing.Point(209, 9); + this.WrapTextCheckBox.Location = new System.Drawing.Point(165, 3); this.WrapTextCheckBox.Name = "WrapTextCheckBox"; - this.WrapTextCheckBox.Size = new System.Drawing.Size(78, 16); + this.WrapTextCheckBox.Size = new System.Drawing.Size(78, 23); this.WrapTextCheckBox.TabIndex = 0; this.WrapTextCheckBox.Text = "&Wrap text"; this.WrapTextCheckBox.UseVisualStyleBackColor = true; @@ -138,45 +191,47 @@ // this.tableLayoutPanel1.ColumnCount = 1; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); - this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 0); this.tableLayoutPanel1.Controls.Add(this.LogMessageTextBox, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.ToolbarFlowLayoutPanel, 0, 0); this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.RowCount = 2; - this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 40F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tableLayoutPanel1.Size = new System.Drawing.Size(547, 353); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.Size = new System.Drawing.Size(590, 418); this.tableLayoutPanel1.TabIndex = 2; // - // TopMostCheckBox + // ToolbarFlowLayoutPanel // - this.TopMostCheckBox.AutoSize = true; - this.TopMostCheckBox.Location = new System.Drawing.Point(311, 9); - this.TopMostCheckBox.Name = "TopMostCheckBox"; - this.TopMostCheckBox.Size = new System.Drawing.Size(72, 16); - this.TopMostCheckBox.TabIndex = 3; - this.TopMostCheckBox.Text = "&Top most"; - this.TopMostCheckBox.UseVisualStyleBackColor = true; - this.TopMostCheckBox.CheckedChanged += new System.EventHandler(this.TopMostCheckBox_CheckedChanged); + this.ToolbarFlowLayoutPanel.AutoSize = true; + this.ToolbarFlowLayoutPanel.Controls.Add(this.CleanLogsButton); + this.ToolbarFlowLayoutPanel.Controls.Add(this.ChangeFontButton); + this.ToolbarFlowLayoutPanel.Controls.Add(this.WrapTextCheckBox); + this.ToolbarFlowLayoutPanel.Controls.Add(this.TopMostCheckBox); + this.ToolbarFlowLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; + this.ToolbarFlowLayoutPanel.Location = new System.Drawing.Point(3, 3); + this.ToolbarFlowLayoutPanel.Name = "ToolbarFlowLayoutPanel"; + this.ToolbarFlowLayoutPanel.Size = new System.Drawing.Size(584, 29); + this.ToolbarFlowLayoutPanel.TabIndex = 2; // // LogForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(547, 353); + this.ClientSize = new System.Drawing.Size(590, 418); this.Controls.Add(this.tableLayoutPanel1); - this.Menu = this.mainMenu1; + this.Menu = this.MainMenu; this.Name = "LogForm"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Log Viewer"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.LogForm_FormClosing); this.Load += new System.EventHandler(this.LogForm_Load); this.Shown += new System.EventHandler(this.LogForm_Shown); - this.panel1.ResumeLayout(false); - this.panel1.PerformLayout(); this.tableLayoutPanel1.ResumeLayout(false); this.tableLayoutPanel1.PerformLayout(); + this.ToolbarFlowLayoutPanel.ResumeLayout(false); + this.ToolbarFlowLayoutPanel.PerformLayout(); this.ResumeLayout(false); } @@ -184,16 +239,22 @@ #endregion private System.Windows.Forms.TextBox LogMessageTextBox; - private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; - private System.Windows.Forms.MainMenu mainMenu1; + private System.Windows.Forms.MainMenu MainMenu; private System.Windows.Forms.MenuItem FileMenuItem; private System.Windows.Forms.MenuItem OpenLocationMenuItem; private System.Windows.Forms.MenuItem ExitMenuItem; - private System.Windows.Forms.Panel panel1; private System.Windows.Forms.CheckBox WrapTextCheckBox; private System.Windows.Forms.Button CleanLogsButton; private System.Windows.Forms.Button ChangeFontButton; private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private System.Windows.Forms.CheckBox TopMostCheckBox; + private System.Windows.Forms.MenuItem ViewMenuItem; + private System.Windows.Forms.MenuItem CleanLogsMenuItem; + private System.Windows.Forms.MenuItem ChangeFontMenuItem; + private System.Windows.Forms.MenuItem WrapTextMenuItem; + private System.Windows.Forms.MenuItem TopMostMenuItem; + private System.Windows.Forms.FlowLayoutPanel ToolbarFlowLayoutPanel; + private System.Windows.Forms.MenuItem MenuItemSeparater; + private System.Windows.Forms.MenuItem ShowToolbarMenuItem; } } \ No newline at end of file diff --git a/shadowsocks-csharp/View/LogForm.cs b/shadowsocks-csharp/View/LogForm.cs index c4fcaf51..dcbbf1e2 100644 --- a/shadowsocks-csharp/View/LogForm.cs +++ b/shadowsocks-csharp/View/LogForm.cs @@ -34,9 +34,15 @@ namespace Shadowsocks.View OpenLocationMenuItem.Text = I18N.GetString("&Open Location"); ExitMenuItem.Text = I18N.GetString("E&xit"); CleanLogsButton.Text = I18N.GetString("&Clean logs"); - ChangeFontButton.Text = I18N.GetString("&Font"); + ChangeFontButton.Text = I18N.GetString("Change &font"); WrapTextCheckBox.Text = I18N.GetString("&Wrap text"); TopMostCheckBox.Text = I18N.GetString("&Top most"); + ViewMenuItem.Text = I18N.GetString("&View"); + CleanLogsMenuItem.Text = I18N.GetString("&Clean logs"); + ChangeFontMenuItem.Text = I18N.GetString("Change &font"); + WrapTextMenuItem.Text = I18N.GetString("&Wrap text"); + TopMostMenuItem.Text = I18N.GetString("&Top most"); + ShowToolbarMenuItem.Text = I18N.GetString("&Show toolbar"); this.Text = I18N.GetString("Log Viewer"); } @@ -97,6 +103,9 @@ namespace Shadowsocks.View timer.Interval = 300; timer.Tick += Timer_Tick; timer.Start(); + this.TopMost = TopMostMenuItem.Checked = TopMostCheckBox.Checked = TopMostTrigger; + LogMessageTextBox.WordWrap = WrapTextCheckBox.Checked = WrapTextMenuItem.Checked = WrapTextTrigger; + ToolbarFlowLayoutPanel.Visible = ShowToolbarTrigger; } private void LogForm_FormClosing(object sender, FormClosingEventArgs e) @@ -121,18 +130,25 @@ namespace Shadowsocks.View LogMessageTextBox.ScrollToCaret(); } - private void WrapTextCheckBox_CheckedChanged(object sender, EventArgs e) + #region Clean up the content in LogMessageTextBox. + private void DoCleanLogs() { - LogMessageTextBox.WordWrap = WrapTextCheckBox.Checked; - LogMessageTextBox.ScrollToCaret(); + LogMessageTextBox.Clear(); + } + + private void CleanLogsMenuItem_Click(object sender, EventArgs e) + { + DoCleanLogs(); } private void CleanLogsButton_Click(object sender, EventArgs e) { - LogMessageTextBox.Clear(); + DoCleanLogs(); } + #endregion - private void ChangeFontButton_Click(object sender, EventArgs e) + #region Change the font settings applied in LogMessageTextBox. + private void DoChangeFont() { FontDialog fd = new FontDialog(); fd.Font = LogMessageTextBox.Font; @@ -142,9 +158,88 @@ namespace Shadowsocks.View } } + private void ChangeFontMenuItem_Click(object sender, EventArgs e) + { + DoChangeFont(); + } + + private void ChangeFontButton_Click(object sender, EventArgs e) + { + DoChangeFont(); + } + #endregion + + #region Trigger the log messages wrapable, or not. + bool WrapTextTrigger = false; + bool WrapTextTriggerLock = false; + + private void TriggerWrapText() + { + WrapTextTriggerLock = true; + + WrapTextTrigger = !WrapTextTrigger; + LogMessageTextBox.WordWrap = WrapTextTrigger; + LogMessageTextBox.ScrollToCaret(); + WrapTextMenuItem.Checked = WrapTextCheckBox.Checked = WrapTextTrigger; + + WrapTextTriggerLock = false; + } + + private void WrapTextMenuItem_Click(object sender, EventArgs e) + { + if (!WrapTextTriggerLock) + { + TriggerWrapText(); + } + } + + private void WrapTextCheckBox_CheckedChanged(object sender, EventArgs e) + { + if (!WrapTextTriggerLock) + { + TriggerWrapText(); + } + } + #endregion + + #region Trigger this window top most, or not. + bool TopMostTrigger = false; + bool TopMostTriggerLock = false; + + private void TriggerTopMost() + { + TopMostTriggerLock = true; + + TopMostTrigger = !TopMostTrigger; + this.TopMost = TopMostTrigger; + TopMostMenuItem.Checked = TopMostCheckBox.Checked = TopMostTrigger; + + TopMostTriggerLock = false; + } + private void TopMostCheckBox_CheckedChanged(object sender, EventArgs e) { - this.TopMost = TopMostCheckBox.Checked; + if (!TopMostTriggerLock) + { + TriggerTopMost(); + } + } + + private void TopMostMenuItem_Click(object sender, EventArgs e) + { + if (!TopMostTriggerLock) + { + TriggerTopMost(); + } + } + #endregion + + private bool ShowToolbarTrigger = false; + private void ShowToolbarMenuItem_Click(object sender, EventArgs e) + { + ShowToolbarTrigger = !ShowToolbarTrigger; + ToolbarFlowLayoutPanel.Visible = ShowToolbarTrigger; + ShowToolbarMenuItem.Checked = ShowToolbarTrigger; } } } diff --git a/shadowsocks-csharp/View/LogForm.resx b/shadowsocks-csharp/View/LogForm.resx index 34b690ce..e8bf04bf 100644 --- a/shadowsocks-csharp/View/LogForm.resx +++ b/shadowsocks-csharp/View/LogForm.resx @@ -117,10 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + 17, 17 - - 172, 17 - \ No newline at end of file From 9953ae2e6821148bb41d698ff97d26d856b229b2 Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Thu, 20 Aug 2015 21:01:40 +0800 Subject: [PATCH 02/54] fix variables' name: (first letter uppercase -> lowercase) 1. WrapTextTrigger -> wrapTextTrigger 2. WrapTextTriggerLock -> wrapTextTriggerLock 3. TopMostTrigger -> topMostTrigger 4. TopMostTriggerLock -> topMostTriggerLock 5. ShowToolbarTrigger -> toolbarTrigger --- shadowsocks-csharp/View/LogForm.cs | 50 +++++++++++++++--------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/shadowsocks-csharp/View/LogForm.cs b/shadowsocks-csharp/View/LogForm.cs index dcbbf1e2..214dbba3 100644 --- a/shadowsocks-csharp/View/LogForm.cs +++ b/shadowsocks-csharp/View/LogForm.cs @@ -103,9 +103,9 @@ namespace Shadowsocks.View timer.Interval = 300; timer.Tick += Timer_Tick; timer.Start(); - this.TopMost = TopMostMenuItem.Checked = TopMostCheckBox.Checked = TopMostTrigger; - LogMessageTextBox.WordWrap = WrapTextCheckBox.Checked = WrapTextMenuItem.Checked = WrapTextTrigger; - ToolbarFlowLayoutPanel.Visible = ShowToolbarTrigger; + this.TopMost = TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger; + LogMessageTextBox.WordWrap = WrapTextCheckBox.Checked = WrapTextMenuItem.Checked = wrapTextTrigger; + ToolbarFlowLayoutPanel.Visible = toolbarTrigger; } private void LogForm_FormClosing(object sender, FormClosingEventArgs e) @@ -170,24 +170,24 @@ namespace Shadowsocks.View #endregion #region Trigger the log messages wrapable, or not. - bool WrapTextTrigger = false; - bool WrapTextTriggerLock = false; + bool wrapTextTrigger = false; + bool wrapTextTriggerLock = false; private void TriggerWrapText() { - WrapTextTriggerLock = true; + wrapTextTriggerLock = true; - WrapTextTrigger = !WrapTextTrigger; - LogMessageTextBox.WordWrap = WrapTextTrigger; + wrapTextTrigger = !wrapTextTrigger; + LogMessageTextBox.WordWrap = wrapTextTrigger; LogMessageTextBox.ScrollToCaret(); - WrapTextMenuItem.Checked = WrapTextCheckBox.Checked = WrapTextTrigger; + WrapTextMenuItem.Checked = WrapTextCheckBox.Checked = wrapTextTrigger; - WrapTextTriggerLock = false; + wrapTextTriggerLock = false; } private void WrapTextMenuItem_Click(object sender, EventArgs e) { - if (!WrapTextTriggerLock) + if (!wrapTextTriggerLock) { TriggerWrapText(); } @@ -195,7 +195,7 @@ namespace Shadowsocks.View private void WrapTextCheckBox_CheckedChanged(object sender, EventArgs e) { - if (!WrapTextTriggerLock) + if (!wrapTextTriggerLock) { TriggerWrapText(); } @@ -203,23 +203,23 @@ namespace Shadowsocks.View #endregion #region Trigger this window top most, or not. - bool TopMostTrigger = false; - bool TopMostTriggerLock = false; + bool topMostTrigger = false; + bool topMostTriggerLock = false; private void TriggerTopMost() { - TopMostTriggerLock = true; + topMostTriggerLock = true; - TopMostTrigger = !TopMostTrigger; - this.TopMost = TopMostTrigger; - TopMostMenuItem.Checked = TopMostCheckBox.Checked = TopMostTrigger; + topMostTrigger = !topMostTrigger; + this.TopMost = topMostTrigger; + TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger; - TopMostTriggerLock = false; + topMostTriggerLock = false; } private void TopMostCheckBox_CheckedChanged(object sender, EventArgs e) { - if (!TopMostTriggerLock) + if (!topMostTriggerLock) { TriggerTopMost(); } @@ -227,19 +227,19 @@ namespace Shadowsocks.View private void TopMostMenuItem_Click(object sender, EventArgs e) { - if (!TopMostTriggerLock) + if (!topMostTriggerLock) { TriggerTopMost(); } } #endregion - private bool ShowToolbarTrigger = false; + private bool toolbarTrigger = false; private void ShowToolbarMenuItem_Click(object sender, EventArgs e) { - ShowToolbarTrigger = !ShowToolbarTrigger; - ToolbarFlowLayoutPanel.Visible = ShowToolbarTrigger; - ShowToolbarMenuItem.Checked = ShowToolbarTrigger; + toolbarTrigger = !toolbarTrigger; + ToolbarFlowLayoutPanel.Visible = toolbarTrigger; + ShowToolbarMenuItem.Checked = toolbarTrigger; } } } From 79e9a23d94feaa5324cf769acf1d71b70c535040 Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Thu, 20 Aug 2015 21:03:38 +0800 Subject: [PATCH 03/54] add feature: save LogForm's config into json file. --- shadowsocks-csharp/Model/Configuration.cs | 1 + shadowsocks-csharp/Model/LogViewerConfig.cs | 63 ++++++++++++++++++++ shadowsocks-csharp/View/LogForm.cs | 36 ++++++++++- shadowsocks-csharp/shadowsocks-csharp.csproj | 1 + 4 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 shadowsocks-csharp/Model/LogViewerConfig.cs diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs index 1ccba56c..d9de24f5 100755 --- a/shadowsocks-csharp/Model/Configuration.cs +++ b/shadowsocks-csharp/Model/Configuration.cs @@ -23,6 +23,7 @@ namespace Shadowsocks.Model public string pacUrl; public bool useOnlinePac; public bool availabilityStatistics; + public LogViewerConfig logViewer; private static string CONFIG_FILE = "gui-config.json"; diff --git a/shadowsocks-csharp/Model/LogViewerConfig.cs b/shadowsocks-csharp/Model/LogViewerConfig.cs new file mode 100644 index 00000000..28471084 --- /dev/null +++ b/shadowsocks-csharp/Model/LogViewerConfig.cs @@ -0,0 +1,63 @@ +using System; +using System.Drawing; + +namespace Shadowsocks.Model +{ + [Serializable] + public class LogViewerConfig + { + public string fontName; + public float fontSize; + public string bgColor; + public string textColor; + public bool topMost; + public bool wrapText; + public bool toolbarShown; + + public LogViewerConfig() + { + this.fontName = "Console"; + this.fontSize = 8; + this.bgColor = "black"; + this.textColor = "white"; + this.topMost = false; + this.wrapText = false; + this.toolbarShown = false; + } + + public Color GetBackgroundColor() + { + try + { + return ColorTranslator.FromHtml(bgColor); + } + catch (Exception) + { + return ColorTranslator.FromHtml("black"); + } + } + + public void SetBackgroundColor(Color color) + { + bgColor = ColorTranslator.ToHtml(color); + } + + public Color GetTextColor() + { + try + { + return ColorTranslator.FromHtml(textColor); + } + catch (Exception) + { + return ColorTranslator.FromHtml("white"); + throw; + } + } + + public void SetTextColor(Color color) + { + textColor = ColorTranslator.ToHtml(color); + } + } +} diff --git a/shadowsocks-csharp/View/LogForm.cs b/shadowsocks-csharp/View/LogForm.cs index 214dbba3..91eb69bb 100644 --- a/shadowsocks-csharp/View/LogForm.cs +++ b/shadowsocks-csharp/View/LogForm.cs @@ -18,6 +18,7 @@ namespace Shadowsocks.View string filename; Timer timer; const int BACK_OFFSET = 65536; + Model.Configuration config; public LogForm(string filename) { @@ -25,6 +26,21 @@ namespace Shadowsocks.View InitializeComponent(); this.Icon = Icon.FromHandle(Resources.ssw128.GetHicon()); + config = Model.Configuration.Load(); + if (config.logViewer == null) + { + config.logViewer = new Model.LogViewerConfig(); + } + else + { + topMostTrigger = config.logViewer.topMost; + wrapTextTrigger = config.logViewer.wrapText; + toolbarTrigger = config.logViewer.toolbarShown; + LogMessageTextBox.Font = new Font(config.logViewer.fontName, config.logViewer.fontSize); + LogMessageTextBox.BackColor = config.logViewer.GetBackgroundColor(); + LogMessageTextBox.ForeColor = config.logViewer.GetTextColor(); + } + UpdateTexts(); } @@ -103,14 +119,29 @@ namespace Shadowsocks.View timer.Interval = 300; timer.Tick += Timer_Tick; timer.Start(); + + topMostTriggerLock = true; this.TopMost = TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger; - LogMessageTextBox.WordWrap = WrapTextCheckBox.Checked = WrapTextMenuItem.Checked = wrapTextTrigger; - ToolbarFlowLayoutPanel.Visible = toolbarTrigger; + topMostTriggerLock = false; + + wrapTextTriggerLock = true; + LogMessageTextBox.WordWrap = WrapTextMenuItem.Checked = WrapTextCheckBox.Checked = wrapTextTrigger; + wrapTextTriggerLock = false; + + ToolbarFlowLayoutPanel.Visible = ShowToolbarMenuItem.Checked = toolbarTrigger; } private void LogForm_FormClosing(object sender, FormClosingEventArgs e) { timer.Stop(); + config.logViewer.topMost = topMostTrigger; + config.logViewer.wrapText = wrapTextTrigger; + config.logViewer.toolbarShown = toolbarTrigger; + config.logViewer.fontName = LogMessageTextBox.Font.Name; + config.logViewer.fontSize = LogMessageTextBox.Font.Size; + config.logViewer.SetBackgroundColor(LogMessageTextBox.BackColor); + config.logViewer.SetTextColor(LogMessageTextBox.ForeColor); + Model.Configuration.Save(config); } private void OpenLocationMenuItem_Click(object sender, EventArgs e) @@ -235,6 +266,7 @@ namespace Shadowsocks.View #endregion private bool toolbarTrigger = false; + private void ShowToolbarMenuItem_Click(object sender, EventArgs e) { toolbarTrigger = !toolbarTrigger; diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 8200aa07..dd957ffb 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -145,6 +145,7 @@ + From b1f17e02a11a07627e5d0b4da920ee434db8aba1 Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Fri, 21 Aug 2015 16:47:56 +0800 Subject: [PATCH 04/54] 1. limit the windows' minimized size 2. store window's size & position in config file. 3. fix: scroll bar sometimes disappear while sizing the window. --- shadowsocks-csharp/Model/LogViewerConfig.cs | 9 +++++++++ shadowsocks-csharp/View/LogForm.Designer.cs | 9 +++++---- shadowsocks-csharp/View/LogForm.cs | 9 +++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/shadowsocks-csharp/Model/LogViewerConfig.cs b/shadowsocks-csharp/Model/LogViewerConfig.cs index 28471084..80bb220d 100644 --- a/shadowsocks-csharp/Model/LogViewerConfig.cs +++ b/shadowsocks-csharp/Model/LogViewerConfig.cs @@ -1,5 +1,6 @@ using System; using System.Drawing; +using System.Windows.Forms; namespace Shadowsocks.Model { @@ -13,6 +14,10 @@ namespace Shadowsocks.Model public bool topMost; public bool wrapText; public bool toolbarShown; + public int width; + public int height; + public int top; + public int left; public LogViewerConfig() { @@ -23,6 +28,10 @@ namespace Shadowsocks.Model this.topMost = false; this.wrapText = false; this.toolbarShown = false; + this.width = 600; + this.height = 400; + this.top = (Screen.PrimaryScreen.WorkingArea.Height - height) / 2; + this.left = (Screen.PrimaryScreen.WorkingArea.Width - width) / 2; } public Color GetBackgroundColor() diff --git a/shadowsocks-csharp/View/LogForm.Designer.cs b/shadowsocks-csharp/View/LogForm.Designer.cs index 187ab948..ba090850 100644 --- a/shadowsocks-csharp/View/LogForm.Designer.cs +++ b/shadowsocks-csharp/View/LogForm.Designer.cs @@ -63,7 +63,7 @@ this.LogMessageTextBox.Name = "LogMessageTextBox"; this.LogMessageTextBox.ReadOnly = true; this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; - this.LogMessageTextBox.Size = new System.Drawing.Size(584, 377); + this.LogMessageTextBox.Size = new System.Drawing.Size(378, 99); this.LogMessageTextBox.TabIndex = 0; // // MainMenu @@ -199,7 +199,7 @@ this.tableLayoutPanel1.RowCount = 2; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tableLayoutPanel1.Size = new System.Drawing.Size(590, 418); + this.tableLayoutPanel1.Size = new System.Drawing.Size(384, 140); this.tableLayoutPanel1.TabIndex = 2; // // ToolbarFlowLayoutPanel @@ -212,16 +212,17 @@ this.ToolbarFlowLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; this.ToolbarFlowLayoutPanel.Location = new System.Drawing.Point(3, 3); this.ToolbarFlowLayoutPanel.Name = "ToolbarFlowLayoutPanel"; - this.ToolbarFlowLayoutPanel.Size = new System.Drawing.Size(584, 29); + this.ToolbarFlowLayoutPanel.Size = new System.Drawing.Size(378, 29); this.ToolbarFlowLayoutPanel.TabIndex = 2; // // LogForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(590, 418); + this.ClientSize = new System.Drawing.Size(384, 140); this.Controls.Add(this.tableLayoutPanel1); this.Menu = this.MainMenu; + this.MinimumSize = new System.Drawing.Size(400, 200); this.Name = "LogForm"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Log Viewer"; diff --git a/shadowsocks-csharp/View/LogForm.cs b/shadowsocks-csharp/View/LogForm.cs index 91eb69bb..a80965fd 100644 --- a/shadowsocks-csharp/View/LogForm.cs +++ b/shadowsocks-csharp/View/LogForm.cs @@ -120,6 +120,11 @@ namespace Shadowsocks.View timer.Tick += Timer_Tick; timer.Start(); + this.Top = config.logViewer.top; + this.Left = config.logViewer.left; + this.Height = config.logViewer.height; + this.Width = config.logViewer.width; + topMostTriggerLock = true; this.TopMost = TopMostMenuItem.Checked = TopMostCheckBox.Checked = topMostTrigger; topMostTriggerLock = false; @@ -141,6 +146,10 @@ namespace Shadowsocks.View config.logViewer.fontSize = LogMessageTextBox.Font.Size; config.logViewer.SetBackgroundColor(LogMessageTextBox.BackColor); config.logViewer.SetTextColor(LogMessageTextBox.ForeColor); + config.logViewer.top = this.Top; + config.logViewer.left = this.Left; + config.logViewer.height = this.Height; + config.logViewer.width = this.Width; Model.Configuration.Save(config); } From 17c75de9c064f20342e4b832e420c31476a7b59e Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Fri, 21 Aug 2015 17:12:11 +0800 Subject: [PATCH 05/54] convert "\r\n" -> Environment.NewLine --- shadowsocks-csharp/View/LogForm.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowsocks-csharp/View/LogForm.cs b/shadowsocks-csharp/View/LogForm.cs index a80965fd..ee85698c 100644 --- a/shadowsocks-csharp/View/LogForm.cs +++ b/shadowsocks-csharp/View/LogForm.cs @@ -80,7 +80,7 @@ namespace Shadowsocks.View string line = ""; while ((line = reader.ReadLine()) != null) - LogMessageTextBox.AppendText(line + "\r\n"); + LogMessageTextBox.AppendText(line + Environment.NewLine); LogMessageTextBox.ScrollToCaret(); @@ -100,7 +100,7 @@ namespace Shadowsocks.View while ((line = reader.ReadLine()) != null) { changed = true; - LogMessageTextBox.AppendText(line + "\r\n"); + LogMessageTextBox.AppendText(line + Environment.NewLine); } if (changed) From b2c2d05c940cf7249c3597274cd5a3281b93749b Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Fri, 21 Aug 2015 22:12:14 +0800 Subject: [PATCH 06/54] support auto check `user-rule.txt`'s change, and update the PAC file. Note: This is a dirty hack. --- .../Controller/Service/GfwListUpdater.cs | 3 +- .../Controller/Service/PACServer.cs | 57 ++++++++++++++----- .../Controller/ShadowsocksController.cs | 43 ++++++++++++++ 3 files changed, 88 insertions(+), 15 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs b/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs index 70720fb9..dc8d692c 100644 --- a/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs +++ b/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs @@ -36,6 +36,7 @@ namespace Shadowsocks.Controller { try { + File.WriteAllText(Utils.GetTempPath() + "\\gfwlist.txt", e.Result, Encoding.UTF8); List lines = ParseResult(e.Result); if (File.Exists(USER_RULE_FILE)) { @@ -82,7 +83,7 @@ namespace Shadowsocks.Controller http.DownloadStringAsync(new Uri(GFWLIST_URL)); } - public List ParseResult(string response) + public static List ParseResult(string response) { byte[] bytes = Convert.FromBase64String(response); string content = Encoding.ASCII.GetString(bytes); diff --git a/shadowsocks-csharp/Controller/Service/PACServer.cs b/shadowsocks-csharp/Controller/Service/PACServer.cs index f8fc80e8..52ea241d 100644 --- a/shadowsocks-csharp/Controller/Service/PACServer.cs +++ b/shadowsocks-csharp/Controller/Service/PACServer.cs @@ -18,14 +18,17 @@ namespace Shadowsocks.Controller public static string USER_RULE_FILE = "user-rule.txt"; - FileSystemWatcher watcher; + FileSystemWatcher PACFileWatcher; + FileSystemWatcher UserRuleFileWatcher; private Configuration _config; public event EventHandler PACFileChanged; + public event EventHandler UserRuleFileChanged; public PACServer() { this.WatchPacFile(); + this.WatchUserRuleFile(); } public void UpdateConfiguration(Configuration config) @@ -167,28 +170,54 @@ Connection: Close private void WatchPacFile() { - if (watcher != null) - { - watcher.Dispose(); - } - watcher = new FileSystemWatcher(Directory.GetCurrentDirectory()); - watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; - watcher.Filter = PAC_FILE; - watcher.Changed += Watcher_Changed; - watcher.Created += Watcher_Changed; - watcher.Deleted += Watcher_Changed; - watcher.Renamed += Watcher_Changed; - watcher.EnableRaisingEvents = true; + if (PACFileWatcher != null) + { + PACFileWatcher.Dispose(); + } + PACFileWatcher = new FileSystemWatcher(Directory.GetCurrentDirectory()); + PACFileWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; + PACFileWatcher.Filter = PAC_FILE; + PACFileWatcher.Changed += PACFileWatcher_Changed; + PACFileWatcher.Created += PACFileWatcher_Changed; + PACFileWatcher.Deleted += PACFileWatcher_Changed; + PACFileWatcher.Renamed += PACFileWatcher_Changed; + PACFileWatcher.EnableRaisingEvents = true; + } + + private void WatchUserRuleFile() + { + if (UserRuleFileWatcher != null) + { + UserRuleFileWatcher.Dispose(); + } + UserRuleFileWatcher = new FileSystemWatcher(Directory.GetCurrentDirectory()); + UserRuleFileWatcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; + UserRuleFileWatcher.Filter = USER_RULE_FILE; + UserRuleFileWatcher.Changed += UserRuleFileWatcher_Changed; + UserRuleFileWatcher.Created += UserRuleFileWatcher_Changed; + UserRuleFileWatcher.Deleted += UserRuleFileWatcher_Changed; + UserRuleFileWatcher.Renamed += UserRuleFileWatcher_Changed; + UserRuleFileWatcher.EnableRaisingEvents = true; } - private void Watcher_Changed(object sender, FileSystemEventArgs e) + private void PACFileWatcher_Changed(object sender, FileSystemEventArgs e) { if (PACFileChanged != null) { + Console.WriteLine("Detected: PAC file '{0}' was {1}.", e.Name, e.ChangeType.ToString().ToLower()); PACFileChanged(this, new EventArgs()); } } + private void UserRuleFileWatcher_Changed(object sender, FileSystemEventArgs e) + { + if (UserRuleFileChanged != null) + { + Console.WriteLine("Detected: User Rule file '{0}' was {1}.", e.Name, e.ChangeType.ToString().ToLower()); + UserRuleFileChanged(this, new EventArgs()); + } + } + private string GetPACAddress(byte[] requestBuf, int length, IPEndPoint localEndPoint, bool useSocks) { //try diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index 2e2f5528..87496199 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -7,6 +7,8 @@ using System.Threading; using System.Net.Sockets; using Shadowsocks.Controller.Strategy; using System.Net; +using Shadowsocks.Util; +using Shadowsocks.Properties; namespace Shadowsocks.Controller { @@ -293,6 +295,7 @@ namespace Shadowsocks.Controller { _pacServer = new PACServer(); _pacServer.PACFileChanged += pacServer_PACFileChanged; + _pacServer.UserRuleFileChanged += pacServer_UserRuleFileChanged; } _pacServer.UpdateConfiguration(_config); if (gfwListUpdater == null) @@ -404,6 +407,46 @@ namespace Shadowsocks.Controller UpdatePACFromGFWListError(this, e); } + private void pacServer_UserRuleFileChanged(object sender, EventArgs e) + { + // TODO: this is a dirty hack. (from code GListUpdater.http_DownloadStringCompleted()) + + //UpdatePACFromGFWList(); // TODO: code like this temporary + //try + //{ + List lines = GFWListUpdater.ParseResult(File.ReadAllText(Utils.GetTempPath() + "\\gfwlist.txt")); + if (File.Exists(PACServer.USER_RULE_FILE)) + { + string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8); + string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); + foreach (string rule in rules) + { + if (rule.StartsWith("!") || rule.StartsWith("[")) + continue; + lines.Add(rule); + } + } + string abpContent = Utils.UnGzip(Resources.abp_js); + abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines)); + if (File.Exists(PACServer.PAC_FILE)) + { + string original = File.ReadAllText(PACServer.PAC_FILE, Encoding.UTF8); + if (original == abpContent) + { + return; + } + } + File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8); + //} + //catch (Exception ex) + //{ + // if (Error != null) + // { + // Error(this, new ErrorEventArgs(ex)); + // } + //} + } + private void StartReleasingMemory() { _ramThread = new Thread(new ThreadStart(ReleaseMemory)); From 3283c93857c791ee41f1e187157dea86ecf77968 Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Sat, 22 Aug 2015 12:26:26 +0800 Subject: [PATCH 07/54] fix: auto detected `user-rule.txt`'s change while no `gfwlist.txt` file, the application crashs. --- shadowsocks-csharp/Controller/ShadowsocksController.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index 87496199..263852a0 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -414,7 +414,12 @@ namespace Shadowsocks.Controller //UpdatePACFromGFWList(); // TODO: code like this temporary //try //{ - List lines = GFWListUpdater.ParseResult(File.ReadAllText(Utils.GetTempPath() + "\\gfwlist.txt")); + if (!File.Exists(Utils.GetTempPath() + "\\gfwlist.txt")) + { + UpdatePACFromGFWList(); + return; + } + List lines = GFWListUpdater.ParseResult(File.ReadAllText(Utils.GetTempPath() + "\\gfwlist.txt")); if (File.Exists(PACServer.USER_RULE_FILE)) { string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8); From bdd29e8384eebd12b3d08d13021b630c12b95497 Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Thu, 27 Aug 2015 04:38:52 +0800 Subject: [PATCH 08/54] fixed: Log Form will hidden when restart Windows --- shadowsocks-csharp/View/LogForm.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowsocks-csharp/View/LogForm.cs b/shadowsocks-csharp/View/LogForm.cs index ee85698c..babf11f7 100644 --- a/shadowsocks-csharp/View/LogForm.cs +++ b/shadowsocks-csharp/View/LogForm.cs @@ -120,8 +120,8 @@ namespace Shadowsocks.View timer.Tick += Timer_Tick; timer.Start(); - this.Top = config.logViewer.top; - this.Left = config.logViewer.left; + this.Top = (config.logViewer.top >=0) ? config.logViewer.top : 0; + this.Left = (config.logViewer.left >=0) ? config.logViewer.left : 0; this.Height = config.logViewer.height; this.Width = config.logViewer.width; From 1280f43878d4874d93f8e988f5973a05090366f9 Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Thu, 27 Aug 2015 06:04:04 +0800 Subject: [PATCH 09/54] fixed: file changes event triggers twice --- .../Controller/Service/PACServer.cs | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/PACServer.cs b/shadowsocks-csharp/Controller/Service/PACServer.cs index 52ea241d..a4aa33b6 100644 --- a/shadowsocks-csharp/Controller/Service/PACServer.cs +++ b/shadowsocks-csharp/Controller/Service/PACServer.cs @@ -2,6 +2,7 @@ using Shadowsocks.Properties; using Shadowsocks.Util; using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -200,24 +201,51 @@ Connection: Close UserRuleFileWatcher.EnableRaisingEvents = true; } + #region FileSystemWatcher.OnChanged() + + // FileSystemWatcher Changed event is raised twice + // http://stackoverflow.com/questions/1764809/filesystemwatcher-changed-event-is-raised-twice + private static Hashtable fileChangedTime = new Hashtable(); + private void PACFileWatcher_Changed(object sender, FileSystemEventArgs e) { - if (PACFileChanged != null) + string path = e.FullPath.ToString(); + string currentLastWriteTime = File.GetLastWriteTime(e.FullPath).ToString(); + + // if there is no path info stored yet or stored path has different time of write then the one now is inspected + if (!fileChangedTime.ContainsKey(path) || fileChangedTime[path].ToString() != currentLastWriteTime) { - Console.WriteLine("Detected: PAC file '{0}' was {1}.", e.Name, e.ChangeType.ToString().ToLower()); - PACFileChanged(this, new EventArgs()); + if (PACFileChanged != null) + { + Console.WriteLine("Detected: PAC file '{0}' was {1}.", e.Name, e.ChangeType.ToString().ToLower()); + PACFileChanged(this, new EventArgs()); + } + + //lastly we update the last write time in the hashtable + fileChangedTime[path] = currentLastWriteTime; } } private void UserRuleFileWatcher_Changed(object sender, FileSystemEventArgs e) { - if (UserRuleFileChanged != null) + string path = e.FullPath.ToString(); + string currentLastWriteTime = File.GetLastWriteTime(e.FullPath).ToString(); + + // if there is no path info stored yet or stored path has different time of write then the one now is inspected + if (!fileChangedTime.ContainsKey(path) || fileChangedTime[path].ToString() != currentLastWriteTime) { - Console.WriteLine("Detected: User Rule file '{0}' was {1}.", e.Name, e.ChangeType.ToString().ToLower()); - UserRuleFileChanged(this, new EventArgs()); + if (UserRuleFileChanged != null) + { + Console.WriteLine("Detected: User Rule file '{0}' was {1}.", e.Name, e.ChangeType.ToString().ToLower()); + UserRuleFileChanged(this, new EventArgs()); + } + //lastly we update the last write time in the hashtable + fileChangedTime[path] = currentLastWriteTime; } } + #endregion + private string GetPACAddress(byte[] requestBuf, int length, IPEndPoint localEndPoint, bool useSocks) { //try From 0bf8132d564e84287ee3aa0be221b7c97c5b17fe Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Sun, 30 Aug 2015 03:46:20 +0800 Subject: [PATCH 10/54] update urls --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 83bff1ef..52a39e5b 100644 --- a/README.md +++ b/README.md @@ -75,9 +75,9 @@ Visual Studio 2015 is required. GPLv3 -[Appveyor]: https://ci.appveyor.com/project/clowwindy/shadowsocks-csharp -[Build Status]: https://ci.appveyor.com/api/projects/status/gknc8l1lxy423ehv/branch/master -[latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases +[Appveyor]: https://ci.appveyor.com/project/kimw/shadowsocks-windows +[Build Status]: https://ci.appveyor.com/api/projects/status/q0h3wsyisbic5fpt/branch/master +[latest release]: https://github.com/kimw/shadowsocks-windows/releases [GFWList]: https://github.com/gfwlist/gfwlist -[Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side -[中文说明]: https://github.com/shadowsocks/shadowsocks-windows/wiki/Shadowsocks-Windows-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E +[Servers]: https://github.com/kimw/shadowsocks/wiki/Ports-and-Clients#linux--server-side +[中文说明]: https://github.com/kimw/shadowsocks-windows/wiki/Shadowsocks-Windows-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E From 0759bc894fea2373c1c2b89e17a3796ead2fe8d0 Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Fri, 11 Sep 2015 23:29:17 +0800 Subject: [PATCH 11/54] Move: gfwlist from googelcode -> github --- shadowsocks-csharp/Controller/Service/GfwListUpdater.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs b/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs index dc8d692c..800379f2 100644 --- a/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs +++ b/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs @@ -12,7 +12,7 @@ namespace Shadowsocks.Controller { public class GFWListUpdater { - private const string GFWLIST_URL = "https://autoproxy-gfwlist.googlecode.com/svn/trunk/gfwlist.txt"; + private const string GFWLIST_URL = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt"; private static string PAC_FILE = PACServer.PAC_FILE; From 7a5451976cbc70df0b357327f8def35db5815946 Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Mon, 14 Sep 2015 12:58:15 +0800 Subject: [PATCH 12/54] fixed: remove useless exception throw out. --- shadowsocks-csharp/Model/LogViewerConfig.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/shadowsocks-csharp/Model/LogViewerConfig.cs b/shadowsocks-csharp/Model/LogViewerConfig.cs index 80bb220d..6dc8929d 100644 --- a/shadowsocks-csharp/Model/LogViewerConfig.cs +++ b/shadowsocks-csharp/Model/LogViewerConfig.cs @@ -60,7 +60,6 @@ namespace Shadowsocks.Model catch (Exception) { return ColorTranslator.FromHtml("white"); - throw; } } From b4f02853077c545064e98773932334acb3c72709 Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Mon, 14 Sep 2015 12:58:44 +0800 Subject: [PATCH 13/54] fixed: typo --- shadowsocks-csharp/View/LogForm.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowsocks-csharp/View/LogForm.cs b/shadowsocks-csharp/View/LogForm.cs index babf11f7..907783bb 100644 --- a/shadowsocks-csharp/View/LogForm.cs +++ b/shadowsocks-csharp/View/LogForm.cs @@ -209,7 +209,7 @@ namespace Shadowsocks.View } #endregion - #region Trigger the log messages wrapable, or not. + #region Trigger the log messages to wrapable, or not. bool wrapTextTrigger = false; bool wrapTextTriggerLock = false; @@ -242,7 +242,7 @@ namespace Shadowsocks.View } #endregion - #region Trigger this window top most, or not. + #region Trigger the window to top most, or not. bool topMostTrigger = false; bool topMostTriggerLock = false; From a66441ea714c74e6751f6e1dff5f60c4da58944c Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Mon, 14 Sep 2015 13:01:44 +0800 Subject: [PATCH 14/54] feature: double click on tray icon shows up LogForm instead of ConfigForm. --- shadowsocks-csharp/View/MenuViewController.cs | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index c8a4c0eb..bae81e72 100755 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -43,6 +43,8 @@ namespace Shadowsocks.View private MenuItem editGFWUserRuleItem; private MenuItem editOnlinePACItem; private ConfigForm configForm; + private List logForms = new List(); + private bool logFormsVisible = false; private string _urlToOpen; public MenuViewController(ShadowsocksController controller) @@ -251,7 +253,6 @@ namespace Shadowsocks.View _notifyIcon.BalloonTipClicked -= notifyIcon1_BalloonTipClicked; } - private void LoadCurrentConfiguration() { Configuration config = controller.GetConfigurationCopy(); @@ -319,6 +320,32 @@ namespace Shadowsocks.View } } + private void ShowLogForms() + { + if (logForms.Count == 0) + { + LogForm f = new LogForm(Logging.LogFile); + f.Show(); + f.FormClosed += logForm_FormClosed; + + logForms.Add(f); + logFormsVisible = true; + } + else + { + logFormsVisible = !logFormsVisible; + foreach (LogForm f in logForms) + { + f.Visible = logFormsVisible; + } + } + } + + void logForm_FormClosed(object sender, FormClosedEventArgs e) + { + logForms.Remove((LogForm)sender); + } + void configForm_FormClosed(object sender, FormClosedEventArgs e) { configForm = null; @@ -359,7 +386,7 @@ namespace Shadowsocks.View { if (e.Button == MouseButtons.Left) { - ShowConfigForm(); + ShowLogForms(); } } @@ -413,9 +440,11 @@ namespace Shadowsocks.View private void ShowLogItem_Click(object sender, EventArgs e) { - string argument = Logging.LogFile; + LogForm f = new LogForm(Logging.LogFile); + f.Show(); + f.FormClosed += logForm_FormClosed; - new LogForm(argument).Show(); + logForms.Add(f); } private void QRCodeItem_Click(object sender, EventArgs e) From 2a71bd7ed7597a617a8a318697d40faae627d4d6 Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Mon, 14 Sep 2015 14:44:14 +0800 Subject: [PATCH 15/54] fixed: rename LogFile -> LogFilePath, for more understandable --- shadowsocks-csharp/Controller/Logging.cs | 6 +++--- shadowsocks-csharp/View/MenuViewController.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/shadowsocks-csharp/Controller/Logging.cs b/shadowsocks-csharp/Controller/Logging.cs index d77a3fbe..2ef247a6 100755 --- a/shadowsocks-csharp/Controller/Logging.cs +++ b/shadowsocks-csharp/Controller/Logging.cs @@ -9,15 +9,15 @@ namespace Shadowsocks.Controller { public class Logging { - public static string LogFile; + public static string LogFilePath; public static bool OpenLogFile() { try { string temppath = Utils.GetTempPath(); - LogFile = Path.Combine(temppath, "shadowsocks.log"); - FileStream fs = new FileStream(LogFile, FileMode.Append); + LogFilePath = Path.Combine(temppath, "shadowsocks.log"); + FileStream fs = new FileStream(LogFilePath, FileMode.Append); StreamWriterWithTimestamp sw = new StreamWriterWithTimestamp(fs); sw.AutoFlush = true; Console.SetOut(sw); diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index bae81e72..62f0a88d 100755 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -324,7 +324,7 @@ namespace Shadowsocks.View { if (logForms.Count == 0) { - LogForm f = new LogForm(Logging.LogFile); + LogForm f = new LogForm(Logging.LogFilePath); f.Show(); f.FormClosed += logForm_FormClosed; @@ -440,7 +440,7 @@ namespace Shadowsocks.View private void ShowLogItem_Click(object sender, EventArgs e) { - LogForm f = new LogForm(Logging.LogFile); + LogForm f = new LogForm(Logging.LogFilePath); f.Show(); f.FormClosed += logForm_FormClosed; From 0a733c61b8de38378dcc6d15605d9146ee15a381 Mon Sep 17 00:00:00 2001 From: kimw <1@kimwong.me> Date: Mon, 14 Sep 2015 14:58:46 +0800 Subject: [PATCH 16/54] fixed: standardize Logging usage 1. Add Logging.Info() 2. Console.WriteLine() -> Logging.Info() 3. Console.WriteLine() in exceptions -> Logging.LogUsefulException() --- shadowsocks-csharp/Controller/FileManager.cs | 5 ++--- shadowsocks-csharp/Controller/Logging.cs | 8 ++++++-- .../Service/AvailabilityStatistics.cs | 2 +- .../Controller/Service/Listener.cs | 6 +++--- .../Controller/Service/PACServer.cs | 8 ++++---- .../Controller/Service/PolipoRunner.cs | 4 ++-- .../Controller/Service/PortForwarder.cs | 2 +- .../Controller/Service/TCPRelay.cs | 19 +++++++++---------- .../Strategy/HighAvailabilityStrategy.cs | 10 +++++----- .../SimplyChooseByStatisticsStrategy.cs | 7 +++---- shadowsocks-csharp/Encryption/PolarSSL.cs | 2 +- shadowsocks-csharp/Encryption/Sodium.cs | 2 +- shadowsocks-csharp/Model/Configuration.cs | 2 +- shadowsocks-csharp/Util/Util.cs | 2 +- shadowsocks-csharp/View/LogForm.cs | 2 +- shadowsocks-csharp/View/MenuViewController.cs | 2 +- 16 files changed, 42 insertions(+), 41 deletions(-) diff --git a/shadowsocks-csharp/Controller/FileManager.cs b/shadowsocks-csharp/Controller/FileManager.cs index cc8f3bf9..0dac0224 100755 --- a/shadowsocks-csharp/Controller/FileManager.cs +++ b/shadowsocks-csharp/Controller/FileManager.cs @@ -19,10 +19,9 @@ namespace Shadowsocks.Controller _FileStream.Close(); return true; } - catch (Exception _Exception) + catch (Exception e) { - Console.WriteLine("Exception caught in process: {0}", - _Exception.ToString()); + Logging.LogUsefulException(e); } return false; } diff --git a/shadowsocks-csharp/Controller/Logging.cs b/shadowsocks-csharp/Controller/Logging.cs index 2ef247a6..49fdde34 100755 --- a/shadowsocks-csharp/Controller/Logging.cs +++ b/shadowsocks-csharp/Controller/Logging.cs @@ -32,11 +32,15 @@ namespace Shadowsocks.Controller } } - public static void Debug(object o) + public static void Info(object o) { + Console.WriteLine(o); + } + public static void Debug(object o) + { #if DEBUG - Console.WriteLine(o); + Console.WriteLine("[D] " + o); #endif } diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index 08125e92..494e1afc 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -81,7 +81,7 @@ namespace Shadowsocks.Controller } catch (Exception e) { - Console.WriteLine($"An exception occured when eveluating {server.FriendlyName()}"); + Logging.Info($"An exception occured while eveluating {server.FriendlyName()}"); Logging.LogUsefulException(e); } } diff --git a/shadowsocks-csharp/Controller/Service/Listener.cs b/shadowsocks-csharp/Controller/Service/Listener.cs index fe59fe98..06994787 100644 --- a/shadowsocks-csharp/Controller/Service/Listener.cs +++ b/shadowsocks-csharp/Controller/Service/Listener.cs @@ -78,7 +78,7 @@ namespace Shadowsocks.Controller _tcpSocket.Listen(1024); // Start an asynchronous socket to listen for connections. - Console.WriteLine("Shadowsocks started"); + Logging.Info("Shadowsocks started"); _tcpSocket.BeginAccept( new AsyncCallback(AcceptCallback), _tcpSocket); @@ -163,7 +163,7 @@ namespace Shadowsocks.Controller } catch (Exception e) { - Console.WriteLine(e); + Logging.LogUsefulException(e); } finally { @@ -208,7 +208,7 @@ namespace Shadowsocks.Controller } catch (Exception e) { - Console.WriteLine(e); + Logging.LogUsefulException(e); conn.Close(); } } diff --git a/shadowsocks-csharp/Controller/Service/PACServer.cs b/shadowsocks-csharp/Controller/Service/PACServer.cs index a4aa33b6..6f8fa414 100644 --- a/shadowsocks-csharp/Controller/Service/PACServer.cs +++ b/shadowsocks-csharp/Controller/Service/PACServer.cs @@ -153,7 +153,7 @@ Connection: Close } catch (Exception e) { - Console.WriteLine(e); + Logging.LogUsefulException(e); socket.Close(); } } @@ -217,7 +217,7 @@ Connection: Close { if (PACFileChanged != null) { - Console.WriteLine("Detected: PAC file '{0}' was {1}.", e.Name, e.ChangeType.ToString().ToLower()); + Logging.Info($"Detected: PAC file '{e.Name}' was {e.ChangeType.ToString().ToLower()}."); PACFileChanged(this, new EventArgs()); } @@ -236,7 +236,7 @@ Connection: Close { if (UserRuleFileChanged != null) { - Console.WriteLine("Detected: User Rule file '{0}' was {1}.", e.Name, e.ChangeType.ToString().ToLower()); + Logging.Info($"Detected: User Rule file '{e.Name}' was {e.ChangeType.ToString().ToLower()}."); UserRuleFileChanged(this, new EventArgs()); } //lastly we update the last write time in the hashtable @@ -259,7 +259,7 @@ Connection: Close //} //catch (Exception e) //{ - // Console.WriteLine(e); + // Logging.LogUsefulException(e); //} return (useSocks ? "SOCKS5 " : "PROXY ") + localEndPoint.Address + ":" + this._config.localPort + ";"; } diff --git a/shadowsocks-csharp/Controller/Service/PolipoRunner.cs b/shadowsocks-csharp/Controller/Service/PolipoRunner.cs index 5b784bcd..ad582d1e 100644 --- a/shadowsocks-csharp/Controller/Service/PolipoRunner.cs +++ b/shadowsocks-csharp/Controller/Service/PolipoRunner.cs @@ -56,7 +56,7 @@ namespace Shadowsocks.Controller } catch (Exception e) { - Console.WriteLine(e.ToString()); + Logging.LogUsefulException(e); } } string polipoConfig = Resources.privoxy_conf; @@ -94,7 +94,7 @@ namespace Shadowsocks.Controller } catch (Exception e) { - Console.WriteLine(e.ToString()); + Logging.LogUsefulException(e); } _process = null; } diff --git a/shadowsocks-csharp/Controller/Service/PortForwarder.cs b/shadowsocks-csharp/Controller/Service/PortForwarder.cs index 63848079..46f2cf10 100644 --- a/shadowsocks-csharp/Controller/Service/PortForwarder.cs +++ b/shadowsocks-csharp/Controller/Service/PortForwarder.cs @@ -140,7 +140,7 @@ namespace Shadowsocks.Controller } else { - //Console.WriteLine("bytesRead: " + bytesRead.ToString()); + Logging.Debug($"bytes read: {bytesRead}"); _local.Shutdown(SocketShutdown.Send); _localShutdown = true; CheckClose(); diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index bf0497d4..2bb0eb98 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -49,7 +49,7 @@ namespace Shadowsocks.Controller lock (this.Handlers) { this.Handlers.Add(handler); - Logging.Debug($"connections: {Handlers.Count}"); + Logging.Debug($"TCP connections: {Handlers.Count}"); DateTime now = DateTime.Now; if (now - _lastSweepTime > TimeSpan.FromSeconds(1)) { @@ -65,10 +65,10 @@ namespace Shadowsocks.Controller } foreach (Handler handler1 in handlersToClose) { - Logging.Debug("Closing timed out connection"); + Logging.Debug("Closing timed out TCP connection."); handler1.Close(); } - return true; + return true; } } @@ -148,7 +148,7 @@ namespace Shadowsocks.Controller { lock (relay.Handlers) { - Logging.Debug($"connections: {relay.Handlers.Count}"); + Logging.Debug($"TCP connections: {relay.Handlers.Count}"); relay.Handlers.Remove(this); } lock (this) @@ -212,7 +212,7 @@ namespace Shadowsocks.Controller { // reject socks 4 response = new byte[] { 0, 91 }; - Console.WriteLine("socks 5 protocol error"); + Logging.Info("socks 5 protocol error"); } connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null); } @@ -280,7 +280,7 @@ namespace Shadowsocks.Controller } else { - Console.WriteLine("failed to recv data in handshakeReceive2Callback"); + Logging.Info("failed to recv data in handshakeReceive2Callback"); this.Close(); } } @@ -423,7 +423,7 @@ namespace Shadowsocks.Controller { strategy.SetFailure(server); } - Console.WriteLine(String.Format("{0} timed out", server.FriendlyName())); + Logging.Info($"{server.FriendlyName()} timed out"); remote.Close(); RetryConnect(); } @@ -462,8 +462,7 @@ namespace Shadowsocks.Controller connected = true; - //Console.WriteLine("Socket connected to {0}", - // remote.RemoteEndPoint.ToString()); + Logging.Debug($"Socket connected to {remote.RemoteEndPoint}"); var latency = DateTime.Now - _startConnectTime; IStrategy strategy = controller.GetCurrentStrategy(); @@ -545,7 +544,7 @@ namespace Shadowsocks.Controller } else { - //Console.WriteLine("bytesRead: " + bytesRead.ToString()); + Logging.Debug($"bytes read: {bytesRead}"); connection.Shutdown(SocketShutdown.Send); connectionShutdown = true; CheckClose(); diff --git a/shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs b/shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs index fa05a0c1..ba25ee18 100644 --- a/shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs +++ b/shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs @@ -132,14 +132,14 @@ namespace Shadowsocks.Controller.Strategy if (_currentServer == null || max.score - _currentServer.score > 200) { _currentServer = max; - Console.WriteLine("HA switching to server: {0}", _currentServer.server.FriendlyName()); + Logging.Info($"HA switching to server: {_currentServer.server.FriendlyName()}"); } } } public void UpdateLatency(Model.Server server, TimeSpan latency) { - Logging.Debug(String.Format("latency: {0} {1}", server.FriendlyName(), latency)); + Logging.Debug($"latency: {server.FriendlyName()} {latency}"); ServerStatus status; if (_serverStatus.TryGetValue(server, out status)) @@ -151,7 +151,7 @@ namespace Shadowsocks.Controller.Strategy public void UpdateLastRead(Model.Server server) { - Logging.Debug(String.Format("last read: {0}", server.FriendlyName())); + Logging.Debug($"last read: {server.FriendlyName()}"); ServerStatus status; if (_serverStatus.TryGetValue(server, out status)) @@ -162,7 +162,7 @@ namespace Shadowsocks.Controller.Strategy public void UpdateLastWrite(Model.Server server) { - Logging.Debug(String.Format("last write: {0}", server.FriendlyName())); + Logging.Debug($"last write: {server.FriendlyName()}"); ServerStatus status; if (_serverStatus.TryGetValue(server, out status)) @@ -173,7 +173,7 @@ namespace Shadowsocks.Controller.Strategy public void SetFailure(Model.Server server) { - Logging.Debug(String.Format("failure: {0}", server.FriendlyName())); + Logging.Debug($"failure: {server.FriendlyName()}"); ServerStatus status; if (_serverStatus.TryGetValue(server, out status)) diff --git a/shadowsocks-csharp/Controller/Strategy/SimplyChooseByStatisticsStrategy.cs b/shadowsocks-csharp/Controller/Strategy/SimplyChooseByStatisticsStrategy.cs index 0cdfcfc5..ab684256 100644 --- a/shadowsocks-csharp/Controller/Strategy/SimplyChooseByStatisticsStrategy.cs +++ b/shadowsocks-csharp/Controller/Strategy/SimplyChooseByStatisticsStrategy.cs @@ -47,7 +47,7 @@ namespace Shadowsocks.Controller.Strategy try { var path = AvailabilityStatistics.AvailabilityStatisticsFile; - Logging.Debug(string.Format("loading statistics from{0}", path)); + Logging.Debug($"loading statistics from {path}"); statistics = (from l in File.ReadAllLines(path) .Skip(1) let strings = l.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) @@ -113,7 +113,7 @@ namespace Shadowsocks.Controller.Strategy if (_controller.GetCurrentStrategy().ID == ID && _currentServer != bestResult.server) //output when enabled { - Console.WriteLine("Switch to server: {0} by package loss:{1}", bestResult.server.FriendlyName(), 1 - bestResult.score); + Logging.Info($"Switch to server: {bestResult.server.FriendlyName()} by package loss:{1 - bestResult.score}"); } _currentServer = bestResult.server; } @@ -154,7 +154,7 @@ namespace Shadowsocks.Controller.Strategy public void SetFailure(Server server) { - Logging.Debug(String.Format("failure: {0}", server.FriendlyName())); + Logging.Debug($"failure: {server.FriendlyName()}"); } public void UpdateLastRead(Server server) @@ -171,6 +171,5 @@ namespace Shadowsocks.Controller.Strategy { //TODO: combine this part of data with ICMP statics } - } } diff --git a/shadowsocks-csharp/Encryption/PolarSSL.cs b/shadowsocks-csharp/Encryption/PolarSSL.cs index 42ce5bf7..3c1d8831 100755 --- a/shadowsocks-csharp/Encryption/PolarSSL.cs +++ b/shadowsocks-csharp/Encryption/PolarSSL.cs @@ -30,7 +30,7 @@ namespace Shadowsocks.Encryption } catch (Exception e) { - Console.WriteLine(e.ToString()); + Logging.LogUsefulException(e); } LoadLibrary(dllPath); } diff --git a/shadowsocks-csharp/Encryption/Sodium.cs b/shadowsocks-csharp/Encryption/Sodium.cs index 564aaeda..9580dea5 100755 --- a/shadowsocks-csharp/Encryption/Sodium.cs +++ b/shadowsocks-csharp/Encryption/Sodium.cs @@ -27,7 +27,7 @@ namespace Shadowsocks.Encryption } catch (Exception e) { - Console.WriteLine(e.ToString()); + Logging.LogUsefulException(e); } LoadLibrary(dllPath); } diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs index d9de24f5..74d40f0b 100755 --- a/shadowsocks-csharp/Model/Configuration.cs +++ b/shadowsocks-csharp/Model/Configuration.cs @@ -70,7 +70,7 @@ namespace Shadowsocks.Model { if (!(e is FileNotFoundException)) { - Console.WriteLine(e); + Logging.LogUsefulException(e); } return new Configuration { diff --git a/shadowsocks-csharp/Util/Util.cs b/shadowsocks-csharp/Util/Util.cs index 1858ee90..219978b2 100755 --- a/shadowsocks-csharp/Util/Util.cs +++ b/shadowsocks-csharp/Util/Util.cs @@ -21,7 +21,7 @@ namespace Shadowsocks.Util Directory.CreateDirectory(Application.StartupPath + "\\temp"); } catch (Exception e) { - Console.WriteLine(e); + Logging.LogUsefulException(e); } // don't use "/", it will fail when we call explorer /select xxx/temp\xxx.log return Application.StartupPath + "\\temp"; diff --git a/shadowsocks-csharp/View/LogForm.cs b/shadowsocks-csharp/View/LogForm.cs index 907783bb..fe12c5a1 100644 --- a/shadowsocks-csharp/View/LogForm.cs +++ b/shadowsocks-csharp/View/LogForm.cs @@ -156,7 +156,7 @@ namespace Shadowsocks.View private void OpenLocationMenuItem_Click(object sender, EventArgs e) { string argument = "/select, \"" + filename + "\""; - Console.WriteLine(argument); + Logging.Debug(argument); System.Diagnostics.Process.Start("explorer.exe", argument); } diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index 62f0a88d..0c581c13 100755 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -231,7 +231,7 @@ namespace Shadowsocks.View 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()); + Logging.LogUsefulException(e); } void controller_UpdatePACFromGFWListCompleted(object sender, GFWListUpdater.ResultEventArgs e) From 29d70acfb583731c58886059fc6ecb8f346d74d6 Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Wed, 7 Oct 2015 23:43:41 -0400 Subject: [PATCH 17/54] replace 'one_time_auth' to 'auth' --- shadowsocks-csharp/Controller/Service/TCPRelay.cs | 2 +- shadowsocks-csharp/Controller/Service/UDPRelay.cs | 4 ++-- shadowsocks-csharp/Model/Server.cs | 4 ++-- shadowsocks-csharp/View/ConfigForm.cs | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index eef5948c..bfff60c7 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -125,7 +125,7 @@ namespace Shadowsocks.Controller { throw new ArgumentException("No server configured"); } - this.encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.one_time_auth, false); + this.encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false); this.server = server; } diff --git a/shadowsocks-csharp/Controller/Service/UDPRelay.cs b/shadowsocks-csharp/Controller/Service/UDPRelay.cs index b449c412..58ec5e3c 100644 --- a/shadowsocks-csharp/Controller/Service/UDPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/UDPRelay.cs @@ -74,7 +74,7 @@ namespace Shadowsocks.Controller } public void Send(byte[] data, int length) { - IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.one_time_auth, true); + IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.auth, true); byte[] dataIn = new byte[length - 3 + IVEncryptor.ONETIMEAUTH_BYTES]; Array.Copy(data, 3, dataIn, 0, length - 3); byte[] dataOut = new byte[length - 3 + 16 + IVEncryptor.ONETIMEAUTH_BYTES]; @@ -97,7 +97,7 @@ namespace Shadowsocks.Controller byte[] dataOut = new byte[bytesRead]; int outlen; - IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.one_time_auth, true); + IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.auth, true); encryptor.Decrypt(_buffer, bytesRead, dataOut, out outlen); byte[] sendBuf = new byte[outlen + 3]; diff --git a/shadowsocks-csharp/Model/Server.cs b/shadowsocks-csharp/Model/Server.cs index 55134335..24f4f2d6 100755 --- a/shadowsocks-csharp/Model/Server.cs +++ b/shadowsocks-csharp/Model/Server.cs @@ -17,7 +17,7 @@ namespace Shadowsocks.Model public string password; public string method; public string remarks; - public bool one_time_auth; + public bool auth; public override int GetHashCode() { @@ -53,7 +53,7 @@ namespace Shadowsocks.Model this.method = "aes-256-cfb"; this.password = ""; this.remarks = ""; - this.one_time_auth = false; + this.auth = false; } public Server(string ssURL) : this() diff --git a/shadowsocks-csharp/View/ConfigForm.cs b/shadowsocks-csharp/View/ConfigForm.cs index 9ae26583..fd24071c 100755 --- a/shadowsocks-csharp/View/ConfigForm.cs +++ b/shadowsocks-csharp/View/ConfigForm.cs @@ -84,7 +84,7 @@ namespace Shadowsocks.View password = PasswordTextBox.Text, method = EncryptionSelect.Text, remarks = RemarksTextBox.Text, - one_time_auth = OneTimeAuth.Checked + auth = OneTimeAuth.Checked }; int localPort = int.Parse(ProxyPortTextBox.Text); Configuration.CheckServer(server); @@ -117,7 +117,7 @@ namespace Shadowsocks.View ProxyPortTextBox.Text = _modifiedConfiguration.localPort.ToString(); EncryptionSelect.Text = server.method ?? "aes-256-cfb"; RemarksTextBox.Text = server.remarks; - OneTimeAuth.Checked = server.one_time_auth; + OneTimeAuth.Checked = server.auth; } } From 8bc9a7911b930e7f5d355297329a4de0f3a7f07c Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Thu, 8 Oct 2015 05:57:14 -0400 Subject: [PATCH 18/54] remove reference of Newtonsoft.Json --- shadowsocks-csharp/3rd/SimpleJson.cs | 49 +++++++++++++------ .../Controller/Strategy/StatisticsStrategy.cs | 3 +- .../Model/StatisticsStrategyConfiguration.cs | 7 ++- shadowsocks-csharp/packages.config | 1 - shadowsocks-csharp/shadowsocks-csharp.csproj | 4 -- 5 files changed, 38 insertions(+), 26 deletions(-) diff --git a/shadowsocks-csharp/3rd/SimpleJson.cs b/shadowsocks-csharp/3rd/SimpleJson.cs index 6581e84b..df811228 100644 --- a/shadowsocks-csharp/3rd/SimpleJson.cs +++ b/shadowsocks-csharp/3rd/SimpleJson.cs @@ -580,7 +580,7 @@ namespace SimpleJson public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy) { StringBuilder builder = new StringBuilder(BUILDER_CAPACITY); - bool success = SerializeValue(jsonSerializerStrategy, json, builder); + bool success = SerializeValue(jsonSerializerStrategy, json, 0, builder); return (success ? builder.ToString() : null); } @@ -966,7 +966,7 @@ namespace SimpleJson return TOKEN_NONE; } - protected static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, StringBuilder builder) + protected static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, int level, StringBuilder builder) { bool success = true; @@ -975,15 +975,15 @@ namespace SimpleJson else if (value is IDictionary) { IDictionary dict = (IDictionary)value; - success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder); + success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, level, builder); } else if (value is IDictionary) { IDictionary dict = (IDictionary)value; - success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, builder); + success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, level, builder); } else if (value is IEnumerable) - success = SerializeArray(jsonSerializerStrategy, (IEnumerable)value, builder); + success = SerializeArray(jsonSerializerStrategy, (IEnumerable)value, level, builder); else if (IsNumeric(value)) success = SerializeNumber(value, builder); else if (value is Boolean) @@ -995,16 +995,18 @@ namespace SimpleJson object serializedObject; success = jsonSerializerStrategy.SerializeNonPrimitiveObject(value, out serializedObject); if (success) - SerializeValue(jsonSerializerStrategy, serializedObject, builder); + SerializeValue(jsonSerializerStrategy, serializedObject, level, builder); } return success; } - protected static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, StringBuilder builder) + protected static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, int level, StringBuilder builder) { builder.Append("{\r\n"); + level++; + IEnumerator ke = keys.GetEnumerator(); IEnumerator ve = values.GetEnumerator(); @@ -1017,39 +1019,50 @@ namespace SimpleJson if (!first) builder.Append(",\r\n"); + FeedIndent(level, builder); + if (key is string) SerializeString((string)key, builder); else - if (!SerializeValue(jsonSerializerStrategy, value, builder)) return false; + if (!SerializeValue(jsonSerializerStrategy, value, level, builder)) return false; builder.Append(" : "); - if (!SerializeValue(jsonSerializerStrategy, value, builder)) + if (!SerializeValue(jsonSerializerStrategy, value, level, builder)) return false; first = false; } - builder.Append("}\r\n"); + builder.Append("\r\n"); + FeedIndent(level - 1, builder); + builder.Append("}"); return true; } - protected static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, StringBuilder builder) + protected static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, int level, StringBuilder builder) { - builder.Append("[\r\n "); + + builder.Append("[\r\n"); + + level++; bool first = true; foreach (object value in anArray) { if (!first) - builder.Append(",\r\n "); + builder.Append(",\r\n"); - if (!SerializeValue(jsonSerializerStrategy, value, builder)) + FeedIndent(level, builder); + + if (!SerializeValue(jsonSerializerStrategy, value, level, builder)) return false; first = false; } - builder.Append("\r\n]"); + builder.Append("\r\n"); + FeedIndent(level - 1, builder); + builder.Append("]"); return true; } @@ -1117,6 +1130,12 @@ namespace SimpleJson return true; } + protected static void FeedIndent(int level, StringBuilder builder) + { + for (int i = 0; i < level * 2; i++) + builder.Append(" "); + } + /// /// Determines if a given object is numeric in any way /// (can be integer, double, null, etc). diff --git a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs index d3b5e26b..3a5e1c59 100644 --- a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs +++ b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Threading; -using Newtonsoft.Json; using Shadowsocks.Model; namespace Shadowsocks.Controller.Strategy @@ -66,7 +65,7 @@ namespace Shadowsocks.Controller.Strategy score += statisticsData.MinResponse*factor; if (!config.Calculations.TryGetValue("MaxResponse", out factor)) factor = 0; score += statisticsData.MaxResponse*factor; - Logging.Debug($"{serverName} {JsonConvert.SerializeObject(statisticsData)}"); + Logging.Debug($"{serverName} {SimpleJson.SimpleJson.SerializeObject(statisticsData)}"); return score; } diff --git a/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs b/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs index 62a48c2e..1b9ba90b 100644 --- a/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs +++ b/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs @@ -6,7 +6,6 @@ using System.Reflection; using Shadowsocks.Controller; using Shadowsocks.Controller.Strategy; using SimpleJson; -using Newtonsoft.Json; namespace Shadowsocks.Model { @@ -29,10 +28,10 @@ namespace Shadowsocks.Model try { var content = File.ReadAllText(ConfigFile); - var configuration = JsonConvert.DeserializeObject(content); + var configuration = SimpleJson.SimpleJson.DeserializeObject(content); return configuration; } - catch (FileNotFoundException e) + catch (FileNotFoundException) { var configuration = new StatisticsStrategyConfiguration(); Save(configuration); @@ -49,7 +48,7 @@ namespace Shadowsocks.Model { try { - var content = JsonConvert.SerializeObject(configuration, Formatting.Indented); + var content = SimpleJson.SimpleJson.SerializeObject(configuration); File.WriteAllText(ConfigFile, content); } catch (Exception e) diff --git a/shadowsocks-csharp/packages.config b/shadowsocks-csharp/packages.config index b309fb97..e79d8643 100644 --- a/shadowsocks-csharp/packages.config +++ b/shadowsocks-csharp/packages.config @@ -5,5 +5,4 @@ - \ No newline at end of file diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 423268d8..0ec088a7 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -81,10 +81,6 @@ False - - 3rd\Newtonsoft.Json.7.0.1\lib\net40\Newtonsoft.Json.dll - True - From 092d1168ff1c1690c1e627650b4b029430ff5feb Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Thu, 8 Oct 2015 06:33:02 -0400 Subject: [PATCH 19/54] start all instances on boot under portable model --- .../Controller/System/AutoStartup.cs | 44 ++++++++++++++++--- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/shadowsocks-csharp/Controller/System/AutoStartup.cs b/shadowsocks-csharp/Controller/System/AutoStartup.cs index 36ad775f..a2ad21c1 100644 --- a/shadowsocks-csharp/Controller/System/AutoStartup.cs +++ b/shadowsocks-csharp/Controller/System/AutoStartup.cs @@ -6,21 +6,23 @@ namespace Shadowsocks.Controller { class AutoStartup { + static string Key = "Shadowsocks_" + Application.StartupPath.GetHashCode(); + public static bool Set(bool enabled) { + RegistryKey runKey = null; try { string path = Application.ExecutablePath; - RegistryKey runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true); + runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true); if (enabled) { - runKey.SetValue("Shadowsocks", path); + runKey.SetValue(Key, path); } else { - runKey.DeleteValue("Shadowsocks"); + runKey.DeleteValue(Key); } - runKey.Close(); return true; } catch (Exception e) @@ -28,20 +30,39 @@ namespace Shadowsocks.Controller Logging.LogUsefulException(e); return false; } + finally + { + if (runKey != null) + { + try { runKey.Close(); } + catch (Exception e) + { Logging.LogUsefulException(e); } + } + } } public static bool Check() { + RegistryKey runKey = null; try { string path = Application.ExecutablePath; - RegistryKey runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run"); + runKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true); string[] runList = runKey.GetValueNames(); - runKey.Close(); foreach (string item in runList) { - if (item.Equals("Shadowsocks")) + if (item.Equals(Key)) return true; + else if (item.Equals("Shadowsocks")) // Compatibility with older versions + { + string value = Convert.ToString(runKey.GetValue(item)); + if (path.Equals(value, StringComparison.InvariantCultureIgnoreCase)) + { + runKey.DeleteValue(item); + runKey.SetValue(Key, path); + return true; + } + } } return false; } @@ -50,6 +71,15 @@ namespace Shadowsocks.Controller Logging.LogUsefulException(e); return false; } + finally + { + if (runKey != null) + { + try { runKey.Close(); } + catch(Exception e) + { Logging.LogUsefulException(e); } + } + } } } } From c532cce99d912fb0a1971c8c419f0403dadbfa2a Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Thu, 8 Oct 2015 06:40:33 -0400 Subject: [PATCH 20/54] small fix --- shadowsocks-csharp/3rd/SimpleJson.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowsocks-csharp/3rd/SimpleJson.cs b/shadowsocks-csharp/3rd/SimpleJson.cs index df811228..e3301f4e 100644 --- a/shadowsocks-csharp/3rd/SimpleJson.cs +++ b/shadowsocks-csharp/3rd/SimpleJson.cs @@ -1132,8 +1132,8 @@ namespace SimpleJson protected static void FeedIndent(int level, StringBuilder builder) { - for (int i = 0; i < level * 2; i++) - builder.Append(" "); + for (int i = 0; i < level; i++) + builder.Append(" "); } /// From 8a6249c4a34cf8caf9d0cf6ec3a146f274e1f44a Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Sat, 10 Oct 2015 03:34:27 -0400 Subject: [PATCH 21/54] revert Newtonsoft.Json --- .../Controller/Strategy/StatisticsStrategy.cs | 3 ++- .../Model/StatisticsStrategyConfiguration.cs | 7 ++++--- shadowsocks-csharp/packages.config | 1 + shadowsocks-csharp/shadowsocks-csharp.csproj | 4 ++++ 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs index 3a5e1c59..d3b5e26b 100644 --- a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs +++ b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Threading; +using Newtonsoft.Json; using Shadowsocks.Model; namespace Shadowsocks.Controller.Strategy @@ -65,7 +66,7 @@ namespace Shadowsocks.Controller.Strategy score += statisticsData.MinResponse*factor; if (!config.Calculations.TryGetValue("MaxResponse", out factor)) factor = 0; score += statisticsData.MaxResponse*factor; - Logging.Debug($"{serverName} {SimpleJson.SimpleJson.SerializeObject(statisticsData)}"); + Logging.Debug($"{serverName} {JsonConvert.SerializeObject(statisticsData)}"); return score; } diff --git a/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs b/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs index 1b9ba90b..62a48c2e 100644 --- a/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs +++ b/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs @@ -6,6 +6,7 @@ using System.Reflection; using Shadowsocks.Controller; using Shadowsocks.Controller.Strategy; using SimpleJson; +using Newtonsoft.Json; namespace Shadowsocks.Model { @@ -28,10 +29,10 @@ namespace Shadowsocks.Model try { var content = File.ReadAllText(ConfigFile); - var configuration = SimpleJson.SimpleJson.DeserializeObject(content); + var configuration = JsonConvert.DeserializeObject(content); return configuration; } - catch (FileNotFoundException) + catch (FileNotFoundException e) { var configuration = new StatisticsStrategyConfiguration(); Save(configuration); @@ -48,7 +49,7 @@ namespace Shadowsocks.Model { try { - var content = SimpleJson.SimpleJson.SerializeObject(configuration); + var content = JsonConvert.SerializeObject(configuration, Formatting.Indented); File.WriteAllText(ConfigFile, content); } catch (Exception e) diff --git a/shadowsocks-csharp/packages.config b/shadowsocks-csharp/packages.config index e79d8643..b309fb97 100644 --- a/shadowsocks-csharp/packages.config +++ b/shadowsocks-csharp/packages.config @@ -5,4 +5,5 @@ + \ No newline at end of file diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 0ec088a7..423268d8 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -81,6 +81,10 @@ False + + 3rd\Newtonsoft.Json.7.0.1\lib\net40\Newtonsoft.Json.dll + True + From 1e9f1f37645fd56b6c8f72efba6558d0264a6e31 Mon Sep 17 00:00:00 2001 From: everyx Date: Sun, 11 Oct 2015 00:21:25 +0800 Subject: [PATCH 22/54] fix ArgumentOutOfRangeException --- shadowsocks-csharp/View/ConfigForm.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shadowsocks-csharp/View/ConfigForm.cs b/shadowsocks-csharp/View/ConfigForm.cs index 9ae26583..667e886d 100755 --- a/shadowsocks-csharp/View/ConfigForm.cs +++ b/shadowsocks-csharp/View/ConfigForm.cs @@ -188,7 +188,10 @@ namespace Shadowsocks.View ServersListBox.SelectedIndex = _lastSelectedIndex; return; } - ServersListBox.Items[_lastSelectedIndex] = _modifiedConfiguration.configs[_lastSelectedIndex].FriendlyName(); + if (_lastSelectedIndex >= 0) + { + ServersListBox.Items[_lastSelectedIndex] = _modifiedConfiguration.configs[_lastSelectedIndex].FriendlyName(); + } UpdateMoveUpAndDownButton(); LoadSelectedServer(); _lastSelectedIndex = ServersListBox.SelectedIndex; From d24548a04f4701dfe9f3e07d6305b5ba3b7a56f8 Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Fri, 16 Oct 2015 04:36:31 -0400 Subject: [PATCH 23/54] rm rc4 and table --- .../Encryption/EncryptorFactory.cs | 6 +- .../Encryption/PolarSSLEncryptor.cs | 1 - .../Encryption/TableEncryptor.cs | 106 ------------------ .../View/ConfigForm.Designer.cs | 4 +- shadowsocks-csharp/shadowsocks-csharp.csproj | 1 - 5 files changed, 2 insertions(+), 116 deletions(-) delete mode 100644 shadowsocks-csharp/Encryption/TableEncryptor.cs diff --git a/shadowsocks-csharp/Encryption/EncryptorFactory.cs b/shadowsocks-csharp/Encryption/EncryptorFactory.cs index f0e2d284..b9cda1a7 100644 --- a/shadowsocks-csharp/Encryption/EncryptorFactory.cs +++ b/shadowsocks-csharp/Encryption/EncryptorFactory.cs @@ -13,10 +13,6 @@ namespace Shadowsocks.Encryption static EncryptorFactory() { _registeredEncryptors = new Dictionary(); - foreach (string method in TableEncryptor.SupportedCiphers()) - { - _registeredEncryptors.Add(method, typeof(TableEncryptor)); - } foreach (string method in PolarSSLEncryptor.SupportedCiphers()) { _registeredEncryptors.Add(method, typeof(PolarSSLEncryptor)); @@ -31,7 +27,7 @@ namespace Shadowsocks.Encryption { if (string.IsNullOrEmpty(method)) { - method = "table"; + method = "aes-256-cfb"; } method = method.ToLowerInvariant(); Type t = _registeredEncryptors[method]; diff --git a/shadowsocks-csharp/Encryption/PolarSSLEncryptor.cs b/shadowsocks-csharp/Encryption/PolarSSLEncryptor.cs index 3b3331f9..a40a12a1 100755 --- a/shadowsocks-csharp/Encryption/PolarSSLEncryptor.cs +++ b/shadowsocks-csharp/Encryption/PolarSSLEncryptor.cs @@ -26,7 +26,6 @@ namespace Shadowsocks.Encryption {"aes-128-cfb", new int[]{16, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, {"aes-192-cfb", new int[]{24, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, {"aes-256-cfb", new int[]{32, 16, CIPHER_AES, PolarSSL.AES_CTX_SIZE}}, - {"rc4", new int[]{16, 0, CIPHER_RC4, PolarSSL.ARC4_CTX_SIZE}}, {"rc4-md5", new int[]{16, 16, CIPHER_RC4, PolarSSL.ARC4_CTX_SIZE}}, }; diff --git a/shadowsocks-csharp/Encryption/TableEncryptor.cs b/shadowsocks-csharp/Encryption/TableEncryptor.cs deleted file mode 100644 index 4b6c8fe3..00000000 --- a/shadowsocks-csharp/Encryption/TableEncryptor.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Shadowsocks.Encryption -{ - public class TableEncryptor - : EncryptorBase - { - public TableEncryptor(string method, string password, bool onetimeauth, bool isudp) - : base(method, password, onetimeauth, isudp) - { - byte[] hash = GetPasswordHash(); - // TODO endian - ulong a = BitConverter.ToUInt64(hash, 0); - for (int i = 0; i < 256; i++) - { - _encryptTable[i] = (byte)i; - } - for (int i = 1; i < 1024; i++) - { - _encryptTable = MergeSort(_encryptTable, a, i); - } - for (int i = 0; i < 256; i++) - { - _decryptTable[_encryptTable[i]] = (byte)i; - } - } - - public static List SupportedCiphers() - { - return new List(new string[]{"table"}); - } - - public override void Encrypt(byte[] buf, int length, byte[] outbuf, out int outlength) - { - byte[] result = new byte[length]; - for (int i = 0; i < length; i++) - { - outbuf[i] = _encryptTable[buf[i]]; - } - outlength = length; - } - - public override void Decrypt(byte[] buf, int length, byte[] outbuf, out int outlength) - { - byte[] result = new byte[length]; - for (int i = 0; i < length; i++) - { - outbuf[i] = _decryptTable[buf[i]]; - } - outlength = length; - } - - private readonly byte[] _encryptTable = new byte[256]; - private readonly byte[] _decryptTable = new byte[256]; - - private static long Compare(byte x, byte y, ulong a, int i) - { - return (long)(a % (ulong)(x + i)) - (long)(a % (ulong)(y + i)); - } - - private byte[] MergeSort(byte[] array, ulong a, int j) - { - if (array.Length == 1) - { - return array; - } - int middle = array.Length / 2; - byte[] left = new byte[middle]; - for (int i = 0; i < middle; i++) - { - left[i] = array[i]; - } - byte[] right = new byte[array.Length - middle]; - for (int i = 0; i < array.Length - middle; i++) - { - right[i] = array[i + middle]; - } - left = MergeSort(left, a, j); - right = MergeSort(right, a, j); - - int leftptr = 0; - int rightptr = 0; - - byte[] sorted = new byte[array.Length]; - for (int k = 0; k < array.Length; k++) - { - if (rightptr == right.Length || ((leftptr < left.Length) && (Compare(left[leftptr], right[rightptr], a, j) <= 0))) - { - sorted[k] = left[leftptr]; - leftptr++; - } - else if (leftptr == left.Length || ((rightptr < right.Length) && (Compare(right[rightptr], left[leftptr], a, j)) <= 0)) - { - sorted[k] = right[rightptr]; - rightptr++; - } - } - return sorted; - } - - public override void Dispose() - { - } - } -} diff --git a/shadowsocks-csharp/View/ConfigForm.Designer.cs b/shadowsocks-csharp/View/ConfigForm.Designer.cs index 8c5334d0..306a92ec 100755 --- a/shadowsocks-csharp/View/ConfigForm.Designer.cs +++ b/shadowsocks-csharp/View/ConfigForm.Designer.cs @@ -198,14 +198,12 @@ this.EncryptionSelect.ImeMode = System.Windows.Forms.ImeMode.NoControl; this.EncryptionSelect.ItemHeight = 12; this.EncryptionSelect.Items.AddRange(new object[] { - "table", "rc4-md5", "salsa20", "chacha20", "aes-256-cfb", "aes-192-cfb", - "aes-128-cfb", - "rc4"}); + "aes-128-cfb"}); this.EncryptionSelect.Location = new System.Drawing.Point(83, 87); this.EncryptionSelect.Name = "EncryptionSelect"; this.EncryptionSelect.Size = new System.Drawing.Size(160, 20); diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 423268d8..bfe37140 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -188,7 +188,6 @@ - From ad75e4fd9c0e944d4ac21d5114926fd440941f33 Mon Sep 17 00:00:00 2001 From: ayanamist Date: Thu, 12 Nov 2015 13:46:10 +0800 Subject: [PATCH 24/54] Update privoxy_conf.txt use __POLIPO_BIND_PORT__ to avoid potential port conflict --- shadowsocks-csharp/Data/privoxy_conf.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shadowsocks-csharp/Data/privoxy_conf.txt b/shadowsocks-csharp/Data/privoxy_conf.txt index 104b9626..98267fa6 100644 --- a/shadowsocks-csharp/Data/privoxy_conf.txt +++ b/shadowsocks-csharp/Data/privoxy_conf.txt @@ -1,5 +1,5 @@ -listen-address __POLIPO_BIND_IP__:8123 +listen-address __POLIPO_BIND_IP__:__POLIPO_BIND_PORT__ show-on-task-bar 0 activity-animation 0 -forward-socks5 / 127.0.0.1:__SOCKS_PORT__ . -hide-console \ No newline at end of file +forward-socks5 / 127.0.0.1:__SOCKS_PORT__ . +hide-console From ca44b5259385dbb1533838c08cff1e19143adf87 Mon Sep 17 00:00:00 2001 From: 10people Date: Mon, 16 Nov 2015 14:48:26 +0800 Subject: [PATCH 25/54] Fix error in open log file in debug mode. --- shadowsocks-csharp/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowsocks-csharp/Program.cs b/shadowsocks-csharp/Program.cs index cb40cb23..efa241e9 100755 --- a/shadowsocks-csharp/Program.cs +++ b/shadowsocks-csharp/Program.cs @@ -37,9 +37,9 @@ namespace Shadowsocks return; } Directory.SetCurrentDirectory(Application.StartupPath); -#if !DEBUG + Logging.OpenLogFile(); -#endif + ShadowsocksController controller = new ShadowsocksController(); MenuViewController viewController = new MenuViewController(controller); From d0e16dc658de094e286e6f80460f46433917010a Mon Sep 17 00:00:00 2001 From: 10people Date: Mon, 16 Nov 2015 14:59:10 +0800 Subject: [PATCH 26/54] Add debug log in network transfer. --- .gitignore | 1 + nuget.config | 1 + shadowsocks-csharp/Controller/Service/TCPRelay.cs | 13 +++++++++++++ shadowsocks-csharp/Controller/Service/UDPRelay.cs | 3 +++ 4 files changed, 18 insertions(+) diff --git a/.gitignore b/.gitignore index 4a64d6be..2e8b8bf7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/.vs/ Backup/ bin/ obj/ diff --git a/nuget.config b/nuget.config index 57b36f04..f5cdc0be 100644 --- a/nuget.config +++ b/nuget.config @@ -1,3 +1,4 @@ + diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index bfff60c7..c0989085 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -215,6 +215,7 @@ namespace Shadowsocks.Controller response = new byte[] { 0, 91 }; Console.WriteLine("socks 5 protocol error"); } + Logging.Debug($"======Send Local Port, size:" + response.Length); connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null); } else @@ -246,6 +247,7 @@ namespace Shadowsocks.Controller // +----+-----+-------+------+----------+----------+ // Skip first 3 bytes // TODO validate + Logging.Debug($"======Receive Local Port, size:" + 3); connection.BeginReceive(connetionRecvBuffer, 0, 3, 0, new AsyncCallback(handshakeReceive2Callback), null); } @@ -272,6 +274,7 @@ namespace Shadowsocks.Controller if (command == 1) { byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; + Logging.Debug($"======Send Local Port, size:" + response.Length); connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ResponseCallback), null); } else if (command == 3) @@ -310,6 +313,7 @@ namespace Shadowsocks.Controller address.CopyTo(response, 4); response[response.Length - 1] = (byte)(port & 0xFF); response[response.Length - 2] = (byte)((port >> 8) & 0xFF); + Logging.Debug($"======Send Local Port, size:" + response.Length); connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ReadAll), true); } @@ -324,6 +328,7 @@ namespace Shadowsocks.Controller if (ar.AsyncState != null) { connection.EndSend(ar); + Logging.Debug($"======Receive Local Port, size:" + RecvSize); connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null); } @@ -332,6 +337,7 @@ namespace Shadowsocks.Controller int bytesRead = connection.EndReceive(ar); if (bytesRead > 0) { + Logging.Debug($"======Receive Local Port, size:" + RecvSize); connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null); } @@ -402,6 +408,7 @@ namespace Shadowsocks.Controller connected = false; // Connect to the remote endpoint. + Logging.Debug($"++++++Connect Server Port"); remote.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), connectTimer); } @@ -501,8 +508,10 @@ namespace Shadowsocks.Controller } try { + Logging.Debug($"++++++Receive Server Port, size:" + RecvSize); remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null); + Logging.Debug($"======Receive Local Port, size:"+ RecvSize); connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null); } @@ -536,6 +545,7 @@ namespace Shadowsocks.Controller } encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend); } + Logging.Debug($"======Send Local Port, size:" + bytesToSend); connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null); IStrategy strategy = controller.GetCurrentStrategy(); @@ -588,6 +598,7 @@ namespace Shadowsocks.Controller } encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); } + Logging.Debug($"++++++Send Server Port, size:" + bytesToSend); remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); IStrategy strategy = controller.GetCurrentStrategy(); @@ -619,6 +630,7 @@ namespace Shadowsocks.Controller try { remote.EndSend(ar); + Logging.Debug($"======Receive Local Port, size:" + RecvSize); connection.BeginReceive(this.connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null); } @@ -638,6 +650,7 @@ namespace Shadowsocks.Controller try { connection.EndSend(ar); + Logging.Debug($"++++++Receive Server Port, size:" + RecvSize); remote.BeginReceive(this.remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null); } diff --git a/shadowsocks-csharp/Controller/Service/UDPRelay.cs b/shadowsocks-csharp/Controller/Service/UDPRelay.cs index 58ec5e3c..5f8d2433 100644 --- a/shadowsocks-csharp/Controller/Service/UDPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/UDPRelay.cs @@ -80,11 +80,13 @@ namespace Shadowsocks.Controller byte[] dataOut = new byte[length - 3 + 16 + IVEncryptor.ONETIMEAUTH_BYTES]; int outlen; encryptor.Encrypt(dataIn, length - 3, dataOut, out outlen); + Logging.Debug($"++++++Send Server Port, size:" + outlen); _remote.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint); } public void Receive() { EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + Logging.Debug($"++++++Receive Server Port, size:" + _buffer.Length); _remote.BeginReceiveFrom(_buffer, 0, _buffer.Length, 0, ref remoteEndPoint, new AsyncCallback(RecvFromCallback), null); } public void RecvFromCallback(IAsyncResult ar) @@ -103,6 +105,7 @@ namespace Shadowsocks.Controller byte[] sendBuf = new byte[outlen + 3]; Array.Copy(dataOut, 0, sendBuf, 3, outlen); + Logging.Debug($"======Send Local Port, size:" + (outlen + 3)); _local.SendTo(sendBuf, outlen + 3, 0, _localEndPoint); Receive(); } From 1097d4fc5baf6f3deaf6725a98f03d7e059028fe Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Fri, 20 Nov 2015 22:49:42 +0800 Subject: [PATCH 27/54] remove space by trim() --- shadowsocks-csharp/View/ConfigForm.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks-csharp/View/ConfigForm.cs b/shadowsocks-csharp/View/ConfigForm.cs index 7f8cec71..0135f48d 100755 --- a/shadowsocks-csharp/View/ConfigForm.cs +++ b/shadowsocks-csharp/View/ConfigForm.cs @@ -79,7 +79,7 @@ namespace Shadowsocks.View } Server server = new Server { - server = IPTextBox.Text, + server = IPTextBox.Text.Trim(), server_port = int.Parse(ServerPortTextBox.Text), password = PasswordTextBox.Text, method = EncryptionSelect.Text, From dd0d0474023d652edb4d03e5c3bf04e7cebcdca4 Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Mon, 23 Nov 2015 23:52:54 +0800 Subject: [PATCH 28/54] override MD5, so compatible with FIPS --- shadowsocks-csharp/Data/libsscrypto.dll.gz | Bin 47058 -> 48781 bytes .../Encryption/EncryptorBase.cs | 2 +- shadowsocks-csharp/Encryption/IVEncryptor.cs | 5 +- shadowsocks-csharp/Encryption/MbedTLS.cs | 68 ++++++++++++++++++ .../Encryption/PolarSSLEncryptor.cs | 2 +- shadowsocks-csharp/shadowsocks-csharp.csproj | 1 + test/UnitTest.cs | 15 ++++ 7 files changed, 88 insertions(+), 5 deletions(-) create mode 100644 shadowsocks-csharp/Encryption/MbedTLS.cs diff --git a/shadowsocks-csharp/Data/libsscrypto.dll.gz b/shadowsocks-csharp/Data/libsscrypto.dll.gz index b042986074202b38d3189a9142a2d9ea8f17b772..544c80bfeaa76315f82892588d3fbace38a902d9 100755 GIT binary patch literal 48781 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 Z?QC$(9UAxG{M~Op{{xFpGU5Pa0RSxSPlNyf literal 47058 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 + diff --git a/test/UnitTest.cs b/test/UnitTest.cs index 1970e85f..7cbcee1c 100755 --- a/test/UnitTest.cs +++ b/test/UnitTest.cs @@ -22,6 +22,21 @@ namespace test Assert.IsTrue(UpdateChecker.Asset.CompareVersion("1.3.2", "1.3.1") > 0); } + [TestMethod] + public void TestMD5() + { + for (int len = 1; len < 64; len++) + { + System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create(); + byte[] bytes = new byte[len]; + var random = new Random(); + random.NextBytes(bytes); + string md5str = Convert.ToBase64String(md5.ComputeHash(bytes)); + string md5str2 = Convert.ToBase64String(MbedTLS.MD5(bytes)); + Assert.IsTrue(md5str == md5str2); + } + } + private void RunEncryptionRound(IEncryptor encryptor, IEncryptor decryptor) { byte[] plain = new byte[16384]; From 961ca041b2d0e17f72e154803f88ea2d1ad5d268 Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Wed, 25 Nov 2015 21:26:45 -0500 Subject: [PATCH 29/54] Fix bug --- shadowsocks-csharp/Encryption/IVEncryptor.cs | 24 ++++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/shadowsocks-csharp/Encryption/IVEncryptor.cs b/shadowsocks-csharp/Encryption/IVEncryptor.cs index 29b98abe..f1922ac0 100755 --- a/shadowsocks-csharp/Encryption/IVEncryptor.cs +++ b/shadowsocks-csharp/Encryption/IVEncryptor.cs @@ -62,18 +62,22 @@ namespace Shadowsocks.Encryption } keyLen = ciphers[_method][0]; ivLen = ciphers[_method][1]; - if (CachedKeys.ContainsKey(k)) + if (!CachedKeys.ContainsKey(k)) { - _key = CachedKeys[k]; - } - else - { - byte[] passbuf = Encoding.UTF8.GetBytes(password); - _key = new byte[32]; - byte[] iv = new byte[16]; - bytesToKey(passbuf, _key); - CachedKeys[k] = _key; + lock (CachedKeys) + { + if (!CachedKeys.ContainsKey(k)) + { + byte[] passbuf = Encoding.UTF8.GetBytes(password); + _key = new byte[32]; + byte[] iv = new byte[16]; + bytesToKey(passbuf, _key); + CachedKeys[k] = _key; + } + } } + if (_key == null) + _key = CachedKeys[k]; } protected void bytesToKey(byte[] password, byte[] key) From 1196696ed7c1c87c9dbbe296975254cc30fb6ec5 Mon Sep 17 00:00:00 2001 From: icylogic Date: Tue, 8 Dec 2015 22:53:29 +0800 Subject: [PATCH 30/54] fix #389: crash on empty statistic data. --- shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs index 88e32309..028f1099 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs @@ -85,6 +85,9 @@ namespace Shadowsocks.View { string serverName = _servers[serverSelector.SelectedIndex]; _dataTable.Rows.Clear(); + + //return directly when no data is usable + if (_controller.availabilityStatistics?.FilteredStatistics == null) return; List statistics; if (!_controller.availabilityStatistics.FilteredStatistics.TryGetValue(serverName, out statistics)) return; IEnumerable> dataGroups; From 87c5b23e2151f3113f6832de01dd1bc01f6c3b77 Mon Sep 17 00:00:00 2001 From: Kaguya Date: Tue, 15 Dec 2015 14:27:27 +0800 Subject: [PATCH 31/54] https://github.com/shadowsocks/shadowsocks-windows/issues/399 --- .../Controller/Service/AvailabilityStatistics.cs | 2 +- shadowsocks-csharp/packages.config | 2 ++ shadowsocks-csharp/shadowsocks-csharp.csproj | 11 +++++++++-- test/packages.config | 5 +++++ test/test.csproj | 5 +++++ 5 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 test/packages.config diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index 49820f63..aca7e022 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -123,7 +123,7 @@ namespace Shadowsocks.Controller { Logging.Debug("Ping " + server.FriendlyName()); if (server.server == "") return null; - var IP = Dns.GetHostAddresses(server.server).First(ip => ip.AddressFamily == AddressFamily.InterNetwork); + var IP = Dns.GetHostAddresses(server.server).First(ip => (ip.AddressFamily == AddressFamily.InterNetwork || ip.AddressFamily == AddressFamily.InterNetworkV6)); var ping = new Ping(); var ret = new List(); foreach ( diff --git a/shadowsocks-csharp/packages.config b/shadowsocks-csharp/packages.config index b309fb97..cd42cb7e 100644 --- a/shadowsocks-csharp/packages.config +++ b/shadowsocks-csharp/packages.config @@ -5,5 +5,7 @@ + + \ No newline at end of file diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index d58ca508..cd85c6a3 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -96,7 +96,14 @@ False - + + 3rd\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll + True + + + 3rd\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll + True + 3rd\Microsoft.Bcl.1.1.8\lib\net40\System.Runtime.dll True @@ -184,7 +191,7 @@ - + diff --git a/test/packages.config b/test/packages.config new file mode 100644 index 00000000..8f91c0e4 --- /dev/null +++ b/test/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/test/test.csproj b/test/test.csproj index d7420145..55798adc 100755 --- a/test/test.csproj +++ b/test/test.csproj @@ -31,6 +31,8 @@ + + @@ -54,6 +56,9 @@ shadowsocks-csharp + + + From 3d49bc312883264a819251051d380190b386ffc0 Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Wed, 16 Dec 2015 23:19:07 +0800 Subject: [PATCH 32/54] delay check for updates --- .../Controller/Service/UpdateChecker.cs | 38 ++++++++++++++++++- shadowsocks-csharp/View/MenuViewController.cs | 25 +++++++++--- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/UpdateChecker.cs b/shadowsocks-csharp/Controller/Service/UpdateChecker.cs index 643c7014..45962353 100644 --- a/shadowsocks-csharp/Controller/Service/UpdateChecker.cs +++ b/shadowsocks-csharp/Controller/Service/UpdateChecker.cs @@ -28,12 +28,41 @@ namespace Shadowsocks.Controller public const string Version = "2.5.8"; + private class CheckUpdateTimer : System.Timers.Timer + { + public Configuration config; + + public CheckUpdateTimer(int p) : base(p) + { + } + } + + public void CheckUpdate(Configuration config, int delay) + { + CheckUpdateTimer timer = new CheckUpdateTimer(delay); + timer.AutoReset = false; + timer.Elapsed += Timer_Elapsed; + timer.config = config; + timer.Enabled = true; + } + + private void Timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + { + CheckUpdateTimer timer = (CheckUpdateTimer)sender; + Configuration config = timer.config; + timer.Elapsed -= Timer_Elapsed; + timer.Enabled = false; + timer.Dispose(); + CheckUpdate(config); + } + public void CheckUpdate(Configuration config) { this.config = config; try { + Logging.Debug("Checking updates..."); WebClient http = CreateWebClient(); http.DownloadStringCompleted += http_DownloadStringCompleted; http.DownloadStringAsync(new Uri(UpdateURL)); @@ -81,9 +110,13 @@ namespace Shadowsocks.Controller startDownload(); } - else if (CheckUpdateCompleted != null) + else { - CheckUpdateCompleted(this, new EventArgs()); + Logging.Debug("No update is available"); + if (CheckUpdateCompleted != null) + { + CheckUpdateCompleted(this, new EventArgs()); + } } } catch (Exception ex) @@ -117,6 +150,7 @@ namespace Shadowsocks.Controller Logging.LogUsefulException(e.Error); return; } + Logging.Debug($"New version {LatestVersionNumber} found: {LatestVersionLocalName}"); if (CheckUpdateCompleted != null) { CheckUpdateCompleted(this, new EventArgs()); diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index b19a244f..03bdcb36 100755 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -66,7 +66,9 @@ namespace Shadowsocks.View UpdateTrayIcon(); _notifyIcon.Visible = true; _notifyIcon.ContextMenu = contextMenu1; + _notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked; _notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick; + _notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed; this.updateChecker = new UpdateChecker(); updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted; @@ -78,7 +80,7 @@ namespace Shadowsocks.View if (config.autoCheckUpdate) { _isStartupChecking = true; - updateChecker.CheckUpdate(config); + updateChecker.CheckUpdate(config, 3000); } if (config.isDefault) @@ -256,7 +258,6 @@ namespace Shadowsocks.View if (updateChecker.NewVersionFound) { ShowBalloonTip(String.Format(I18N.GetString("Shadowsocks {0} Update Found"), updateChecker.LatestVersionNumber), I18N.GetString("Click here to update"), ToolTipIcon.Info, 5000); - _notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked; _isFirstRun = false; } else if (!_isStartupChecking) @@ -269,9 +270,23 @@ namespace Shadowsocks.View void notifyIcon1_BalloonTipClicked(object sender, EventArgs e) { - _notifyIcon.BalloonTipClicked -= notifyIcon1_BalloonTipClicked; - string argument = "/select, \"" + updateChecker.LatestVersionLocalName + "\""; - System.Diagnostics.Process.Start("explorer.exe", argument); + if (updateChecker.NewVersionFound) + { + updateChecker.NewVersionFound = false; /* Reset the flag */ + if (System.IO.File.Exists(updateChecker.LatestVersionLocalName)) + { + string argument = "/select, \"" + updateChecker.LatestVersionLocalName + "\""; + System.Diagnostics.Process.Start("explorer.exe", argument); + } + } + } + + private void _notifyIcon_BalloonTipClosed(object sender, EventArgs e) + { + if (updateChecker.NewVersionFound) + { + updateChecker.NewVersionFound = false; /* Reset the flag */ + } } From c539e027d7b49e11150ccf1636722b71a3b195d2 Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Wed, 16 Dec 2015 23:35:53 +0800 Subject: [PATCH 33/54] support custom abp file --- .../Controller/Service/GfwListUpdater.cs | 12 +++++++++++- shadowsocks-csharp/Controller/Service/PACServer.cs | 2 ++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs b/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs index 493defa4..0185cc28 100644 --- a/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs +++ b/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs @@ -18,6 +18,8 @@ namespace Shadowsocks.Controller private static string USER_RULE_FILE = PACServer.USER_RULE_FILE; + private static string USER_ABP_FILE = PACServer.USER_ABP_FILE; + public event EventHandler UpdateCompleted; public event ErrorEventHandler Error; @@ -48,7 +50,15 @@ namespace Shadowsocks.Controller lines.Add(rule); } } - string abpContent = Utils.UnGzip(Resources.abp_js); + string abpContent; + if (File.Exists(USER_ABP_FILE)) + { + abpContent = File.ReadAllText(USER_ABP_FILE, Encoding.UTF8); + } + else + { + abpContent = Utils.UnGzip(Resources.abp_js); + } abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines)); if (File.Exists(PAC_FILE)) { diff --git a/shadowsocks-csharp/Controller/Service/PACServer.cs b/shadowsocks-csharp/Controller/Service/PACServer.cs index f8fc80e8..e2d23658 100644 --- a/shadowsocks-csharp/Controller/Service/PACServer.cs +++ b/shadowsocks-csharp/Controller/Service/PACServer.cs @@ -18,6 +18,8 @@ namespace Shadowsocks.Controller public static string USER_RULE_FILE = "user-rule.txt"; + public static string USER_ABP_FILE = "abp.txt"; + FileSystemWatcher watcher; private Configuration _config; From 131c4940c6fe580cfabfdb4692b3c4cf3a2aaa04 Mon Sep 17 00:00:00 2001 From: Syrone Wong Date: Thu, 17 Dec 2015 18:25:13 +0800 Subject: [PATCH 34/54] add chacha20-ietf support with updated libsscrypto ChaCha20 with an extended (96 bit) nonce and a 32-bit counter has been implemented as crypto_stream_chacha20_ietf() from LibSodium 1.0.4 Please refer to https://tools.ietf.org/html/rfc7539 for details. Signed-off-by: Syrone Wong --- shadowsocks-csharp/Data/libsscrypto.dll.gz | Bin 48781 -> 59550 bytes shadowsocks-csharp/Encryption/Sodium.cs | 3 +++ .../Encryption/SodiumEncryptor.cs | 5 +++++ .../View/ConfigForm.Designer.cs | 1 + 4 files changed, 9 insertions(+) diff --git a/shadowsocks-csharp/Data/libsscrypto.dll.gz b/shadowsocks-csharp/Data/libsscrypto.dll.gz index 544c80bfeaa76315f82892588d3fbace38a902d9..6c814b36c1602abf466094461d6ddf7359e64526 100755 GIT binary patch literal 59550 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ⅈ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 literal 48781 zcmV)9K*hfwiwFqiEK^nj18iwxb8};Id2n=ZE@W(M0POt>TvJ!pI1HbV00DxD3W|z~ z1zTFwQjfM;wBRL*)@Ts9)dJq2h^Q12FRds{pw01^*3PsucE(Qa=*-xuozfYqXlO9O(DvMsrFpFfq%r_H~QzgGv#%ElRY!Jh=f#V?co4y!+G-#7C#D;bshVc{ANRVli zQKpjM1Q|)Bp-CvDpfN+nv_t6e|vSt`35Ur|oMMu>=IM;;Iwm)#X_HuHb}{#Sn~dGBnb<348wPtPG~B+4Z+*TsE>4) zonaXNQpZq!i9&rurccWENfQD9C&bC-YqK)#fZSya;k8PaQR6bIT!rDT!Z2Z$4x@xr zShp+HN36k!gG5c`jY`+1aMz|VVI@Y%4FeO-WMoiGyjI1YOZ`|clW8if1`XfHcUl(8 zcKXaNw;FH7H1Rs6Wwy+;Qo)#3su=DOZkE!WE;WsHeY^}3@|Pg#Fq+gSm-jK%_~gdb z8GnjtYPyXG6e-vYxvdIrt18HhN2Tt)Rnq)pR9QYMsYG(`&nc8K>=<;(0KlS00 zvmCxhv1@QjUKhe8!fzOtsN&K!+?EiBpDBU?k5s-e78nSOnBp=>#cP}+mE8pA|BMaeHz^0QTuI#r}r6I2g{n62R#hD2(?OA|L+WAu$% z7-CRNwr1KZWrSu^hi2c`OQFmuD*hz$wyZrCoMjDA!XVy<({}N*6`a<|&sLghRGhAp z*M)Q1UVfe;3AyMphH)}*0DVw<>Lb9`5C#}hH%mo$lmvc0+^jH*wo_j>D;#^ISb1MO ziS-+v#eb7&KTQZpS0A1&<8)o>!wZ$k>cb;#*Oa!el|jwm?USlLT%;f_+qX*akV_%H zL7iOYL9qBiKu{-G{3T03P(5eFIDvpEs2(gqk|mha9fE9Bl6#P3QA?J=obCj~&`9nf zl4Yo53FUME7KTXfVUlIIWEsKfPD6|^$vslCj3SmaPS*&*;otzj(IjMy6lJVr8OP}^ zK&%MKeV=5Blq};pT@}QLlHB)8mIoxugPg7!Vnj>sha^jkWSPk6jzf$Il6#V5d04VM z!s)CKBUW-xmMpAfnab%ZA;uKR{itMlOtMVpbOOYfCb=Ic7CsZIe0H4VIg``%LZlgz z`w7VsPb{3y$?LlKSt0BkP2?OEJ12zSs^pWx__KWGL7UCD%>Me;Upys0DYwa+d1$@R zJUaMO5WwqI{5*~Aqz_vYS;zOW^EG@)v-}pnIE3G%lsn68n9g>_2fS+7TK;UL9TLfP zhu9h*P;M)$!Ek=LM$7<^ci8HE_*0y=gRPCMBau(AwQ`&7tdG1ALiv-#ub!=ow3pRl zB39c0!KZxWoqQc@=g&mekQk@gI`BT}!|OWA>M$OqMPAR_Io%Z?x0Xa}WbN{LB=9U> z!>n@07)no3b_&Pqb6(d|Br2wjQRkQ#Q0rEcy5mK(OcEo@*00Fvc zN+U!#=_5bG+j(OvTZ0%dcgR|IoDwI}07(f|R*{&%oo4I>bOZ>J+oby5Mz!+|KH%Sg1J|G4w4{NTM)mF3G6Rftm$;zMPv~84NY(v1OHr5`g?PTjj zB5l0R$?A^FJ6Wh{-60;4L%V=XMD_yBP_<{cl!I(aCnX#(MP8KaTIDwIk~^Ukyw=K} zWwobCwjB`OWR>IS#TpOFo8;#??G-+=lI>wL8~KxLW<8tP#%l#udx0duifFRSPa)&4 zb0sHuU1yo?El0}+F7ptd*(|>f#W;hNcn5F-UALi$>kiT=P+IvdR$GO+Hz)zBk zmC|!ijBoX#--+}??bJhgMfwFAzfz=Mq(&@7FP7p2DQ^dFti<@eydCM0@b`+Pa3b+m zQrY(-Y=xy_{K zwsG3ZCL1jmR07Obd_DF^sN>@-FxRUD0+!$c$)uG}L5g%x$(_73)^(Dh)Xn=wsc}FJ zT;YueK{{x%LR9PoFt#?cwNQB}m7bMwn!N})NX_UtUvh}O1eu6E7kgq8b_{5L=UH7N z)IQ`1Lr&(wNIMQbjr`f5Q(VfS$TPMY-=KQv0WdCFesSgasF!h6ghAy5t2@m%lG3+f ziqrh5Al;$Jvz+!IB;jEUI#gDN@iFyj`5BrRQW_5d-!LT%M;BOKD<+0+U&(6^@>qZ% z%rf#+WNi@SBCwbdM7|=|);HPYbxv}OiD^Lt0A?9Z##B$oI$g{j1v(p~JlvXk286Xl;0yAMUhm-;#mW;{*eN>TAX=*&20dMeycQaVyr#*R3Wj42s@F%wWOG0PP%HCnMw|c z4Vlmj+_@rV)<(#TJOeC3nvq;AW}Ez!yO^1kB8|YB<6=FwVx<7_r%0t#VJiR@W6IWk z)>etEJ%NckNuJf1_zIyGNj(S?pSx17NUSB)ifyNv_B({Dy+JTJ#5Wv&~5*bX9^*)y0VtT+UZ{d-*jYKx6roHk!Ur(s* zgee|rh3Opo<{3=)H=3?feqHRH7rlEYri_GaQ9KAD!VP%S`KI1-3=_kMfs<`3L_X7G zjfBP2C77^a{Sx^FX>?Se0TeiUoS=ll=p=8!VF^+I&q0DYkJA-OoH%mO398ISqgtVN z;^?M5EpNh6=_HJN#zR~Q%oA2JSmH>DltE1GLCgCWZL3boaRCLR8h=Kbw5r@Q77;E{ z3WE_#CH-2=>$p)FkV*E>8e(W3jh3aMFUDsJK8RMPH0n zRzu>#$f`YvvVtH@R|NSXFIho*NM3^_s3%m2vlI^S)z}474!X%MtEEUQ%j$@w7Ryo3 z8>EtXxI1I7DYDA|+Iln1pg1=(Sfw`l0DpBVAU) z7(ic1r%p2UcZlfQM7ppLd`gGh-G!=XAJ4%tQSAA}VHaP>OjOEcEg6#4Or8!Qse-YFh%}{@2ze;%%5OE*@MpP{dWo&T9bE^M`#2DkBCshZP;9-(rd;8H z515FpBc&5aY%)Y1Y^sryrH8hPD}njAmo2eAlG4lGVM{=W>19)zZMD82rKcRirP&vx zemZTsPTB&S>g07WcxkI`c3*ijXM~}(sm5l79*I&gMwM$jXn)~#Rq{?-gAWh#Qx!yE zPr>kq%LXUqDV0Znq0j>>`ICG}7f1x!PHguu*uyGAT#?OJO7Wd^420>TO7^TqmJuRL zQpvW4WoL1-l*=XkNK!ItXZV&h{&q#Ty8K0kVR-p5RO0gW@>crz^6Z8kw;*8YT0 zd2||to3-6BXZRX(${mPgCIT&uE=coV-GtUX;=Z8QjOljshgV@i&GsHYc4Pcac zjt0O;^PIu_{4j!G2)Lo1&2xq>7v32UtPuGK{448dg%b6%5Nm%a-&9u-;ldplhOt=C zR#y=vy6Y;U!6u8;K2ujQ0c_kmpZ!=@5ev4yE64WKRZIcfre%{gbrsXV)_LumUHIeA zP{Ah@6cc|@&;p-;ZuYY*VYCM|Vts~r)+bbEiV`Z-bUR^Fm<48wQ+i>Zi=eC1DnpRI zOd&tPGXLrPNA<$qDGURSBYVL=|LIg=7r1$y(sUvW8tKT~-Ri?~%W_%lJga4sOnrEc zD|Vg>Ri=m_?ddvy(n`+O%_ln!b@P>^kn>ffg!46|c=JO@+2)6lf}QRq1p#8%&!Bk< zOQJkWBl5TFF8-$RB^uzd#iD|e2>$TnGOGBCqI)wSwWJ85YXtBP$V)j`SD|v(h>FyR z#$6*J?ivYm*GRa#Mk3rb5+&8hP{<=cq(2vVFS1laUHm0sKP^}`R?%~j?m`h2dN{Au zxQt)To?)2y!bodanZuEGgfs>dcU{H_Yj)KcMu zU)wdG7#kl3a^x+4i((Y&0&Q$F2x_;*g+v)AiLDS_9z(wRsO_ zXEwD~$TSHwx0$2_FMo&?#&&aWbb%kX$6sSizJ?T{rv_MKpC zpH+#rm)*8EC0lEmOf!#Gv3l2cbZE%VVn16hFyp+t6Sk+kFo>7W;Qqv4W68YW9nNc& z6;{LGqZn;>Ep(Oy8FulXr1lHri)m&vFr6`~g{Pr&)%l_eTM4hz@V4ZM*s=@{nS=zx zNc;(`ZiVJerXD_1StlO^!Hy7ORiQP2SOdYDnr3VFwaifhPsptJ1P{Hs#Z>E)s~%fU z_P5lBQ)QM-F!(R-wSA=wY68n0$#R=mxRC1kad(O?0#ZK6)E=lV4+7Mtb{}=Q%a>Xz zt^@R~QDnokc?A3iE(Gs*T(H=0}Ep6KZY7OD?7c=;rTuv;|j zS#C=#=HcWrySVge?B`tK6!qa#T>1}Rt!l!81CN43N(=UQlOSlw3{)(8#Vqmm{0~nvE^K^>^@bYo8m)MhB zN(WM(xQx^5`IIj7??m4&PS?pLJ`IUDeR_W;*#k1k8sN_KbhDuX7T`RWB5?W@>_z?t zr(eSwd-*T8!aO!p!=)EMl8ya!uwg(QtoL@G@^rE#9UT1MQplD#xh)1*PU(Q=E-rBk z$diQlZ9u#j9Yz@rIgq$R^mv50w%c+vN-v)hZy#VAs#k(Lpd8qA%-D(8lYA5M%#4j6 zN_CRkZARNEPA72YU8FylU&fA?DY%=l`6V1Uyjrf=a+lXBPbX{a;EV!Sxf_x9@O@n6 zt7t0$k&LU}gH|kP2WQ?(vh*rzFQ9*uc#B0rkKbc{IlW3R&o}P!#P$c}4(=p{?&Zw; zNM26DfCFv@baG}qt^w7%`w%VjdjyDW-uzcY?A6T8@80Eg($mRi3fPVIR3h3h_?z6G zDq8e2TxB((p@Wq7xKv)T-IzZ?yk1Sk{PEph9}VzgVUaVl3kwM?sM<<+t3lqLka!D4 ztS99qHG7(t@+<*jhcKT(q+Wf%{Mi66utP_Mg%}ifK-20XJZ6rbJ+k+=j*vw9D4<0jt;cHI^E#;2@va|Zj z`NQ4i{QfSlQ{Y_Q-~CfG-0n_93;ln07qN74yPd?+!R^Ln6Kf?CP+|y_j@K*rLM6XN z)jw_yA23c18{iK08aH7`4r52R(lIAfU~h2gVSKuV?Sq2q!-2zv5o9))fTP-$D3}2T z4i67|4F;3$8XhLzdw6IQhlj*i+BF3BBFqCg+)X1Lw{Qk~hyRk>lE7wmklA7GfWDeI z-?L99&KuZE6Sep5qJr2(6BB_BLct;r3iL?9GSYtv)3Jed&=I|#zu5!$nKi(j>D_vI z+0i%>@^@fzgTBJ0ui(FgPRpO-wyc3plAecIYcT5q7!~z}r1=V5qJd;8P3W5kOyHXa zxQ*U}U?)43j6^UR7H(mCxWa9GA7N)T8H_VEko}(W{$UAQo7qfSF^tT+`UgR&mdV)M zUx~Cz$@uJ9*K~BgySh8r;4W!4AS3e`{yOrzhcn~wjKee;n+b(`Y41V`_lW~Z2OUER zN2Jd6=es-CpYHbh;{Y${Uu-Dp&DhZniX9F6+95hI-QZF>fv(+^B3)H(y2xmn*}?6h zL+Po0Qa-sm-;eM1`siMy^dc#{tx|_4qi9Mex0?*17rEUyezKRi-DJ2spPj|+CZnB| z%Ak4`3VwwOev7ideX0kv$3X+!!CviC>ijo3lr5m&Ls1JuU{Ko zfP3^^rE3!zt&gCBOcd=`(RAP@V@f~W4-cSo(g62F?|!9WM+;)V(y+I=^jQ89w`B_C z^Er+nC`F(|+24cW2lSXH2DoQ>_aF^Bl?X6K4Of^z2c)^|ZOkK)J&(NvrAdkOB?CxZ zJix8Hi*(XEk>h$YX{WnK95QW+BhLR$fJDZ3NsM2?p673)1c~*!B@d+q6d+Ia^JU`z zK5Q7^UVj%~u=^mfh0?Hj2}Ua%ySA{WKzt)Z>NZbVBXP$78Fl*rcgcWt1I#`%v1hQ- zfOTFPt+1=&C_;y*1}a7N_r#a)?shNT?PVF*6GItTE3=od^LIc`*u7iq{8V-nu=nqw z<0iLf@4(*w=G__m{%)@~?&|#=Y^dTz?EU-1zJ5UL>z)!KEPk!a`_HRIB&!cUg$L2_ zaGFoh7w{{?V`*Qc@wZ4D3k-txw$LAjZGOH21a5Hn1?Zf%947=W^bH_B3UmhGp(ZDr zD8>qsrqzXk0lHo~b1n=BAU;ZT2GAKaQOSnUcxsXYkIoGWARyS5(*@DG5a3BSSQ^11 zHnW3QvwD@us%g5lfUJ>PD;m@<;aNSvXKUA>`UEI!SE4u*@){~Icw-kEVnL+_f5z4x zhIXRNIEA*~p)ad}lrU72#JygLrIe@oQ}S5EhW^BQ1*G6mxNO2`WSQqsV*20E^gK!9 ziJ-S10jpP%K*+xhFaTM6IL$en=8Ob|p#xK%!Gz&7XI&ecu9Au@{Aa2=J*Ym#>S>uf zFtx}hT^mIrrPg(NCKtSuhmb-$N#>oj)ZRtrQed$x@F#gA7Qpr$#5l)J5s|ftc_Pj! zA|p8?Gze@C1fdrpZC>@pXLd-0=vrBQ_yUtP!rc~L?)sT1dhXp8+!(v=)e=}5@V{&d z&=5#+C9O%REnszGvJ@v)9P;^OKR?2y2I0XEsd_sA6^nY91{9@3JX?d5dzW{e(ALnv zrL>B(3CXHo_(IKwSjXP#j!xoi==V8EF|p||uXKo`6KxM3*!r8Dw_2m^E}u@)UMOu{ zJhG>sJi-Z23R~|*p)kM)CQTNVMAdqb7m}dp^HRcSZ%@LN3-bpIkcVSuP zj3D!@LFOd&U7Jl*f0$${Q8BzW1h<-E`4WvWyTYX|$KjmUhVv;AF*ST<7?+~rw7t2z zmsE5cLx6hze6kRG8yb9y%5*}(YhhqfX?7~cOtzVQW`onL8Z*!Its0V5xQr(Odo~x# zTIqgQh0CDjbrF0@I3m`mpu>8iZMlX0h@egBMU+0X&9Yf$1w}}r1J2L9aeC@gm2Q1% z)~J7@PgVX4eJX_45#6RmudGWTJ14AF(s!B_ngpu({)y=QQC}QqgodOVKFS)|$!kHD zRrt-5+vIlj;pUx+p=QPK`DWG7G8aj7Av8$7D%7R3laOc<^RGbcAXWO1KoAIagAw{VpEG`nO8O9opiIltZv z5ysN;j=`{}SodLe7;UIMoE}@Jl8yV&`Z&qrKG=RghvUma_o4OilEr?>@{fS?Fqmnm8*5E1RAI^MalWXhc zoq)<_JENB0h^%G7r$g>UDcs(o?3T#F|d>HsUGBzS#? z&Wk;W$4R`=`pRCL_cI>U8GyTaPw84bIx_(Gdp&b`x%R{Wntk72?sDOgq5-%+zVtLF z*Pb4LTW+_!e&68T>*UP@x7}aU`z5*g8T>!rv3D zSo}s?O@RCo5L5>Rs`E{HD(ikE5wX_}lHZ_}Csx~E>JGOf2W$;0z;6R`YJD>`4_`#? zI(eHX`s9$gF|xY#hyyg?*6xbJiYNXF2BF|6?y?^nJ)vT3U^E%;*$r<8;-LHF%Q8 zURJYn(L)yGcqimk>npzw)Y)0m_MEo50C^u&L~W#vKMO>)_-uRzn@Ka~@g*!pZ9r5V z9y@CU)F+{`*pp?p+0$>kwfk{qyR7mAx6N4-uJD1V?FX6@M$PM z>=JXo`}lybYT7qHlOiJG07Q&ujX=7u!SjXHHk(3z((;?P&wlYFmZl2%*vOYu*=mC1 zP1w-NY738l@-YijkMD{nKkOE%)&OTP7wBNM+kY2LTdUo6CP03ZJ%b0!FW@2iUR#}_ z%y#>Mx4x72LGfy!ZP|k4XL%br%nTJ;5&Po>!fnLZV)?c2XtRPnWjQjv@j?qvYNENH z@EZ0cp5MF9>#BeuWwvi#zx*w>0c0CNe1?Zc%aTUx-1djD@{%jqXf;^pz@wbhLnp8@ z8hKk!BVxQ11oe$)R(pX}_dSSxq7Fi8{p2@-YN57&<9cWg7Pp?S0dE_0iXiN?e~x_h z9b~AJuLbWqwft5P^j_cT`+n6+#|fM5WKd5dMel3RSe}Cf4#54()2f%T-~(`%bp1$r z{s7!fndKF9Y$K!xiMW?+Ke!PZgXOnx4OT85m_zHg6Z){P49Fp5j%+`2)jJ1lw#@|j zsUZ26L3Tj?i|xrh*cbpUAcgYW1hjT&WR_Q^-gx{Yd86g!56@kCh%Y(L+aX;|p!{M` zjcl_L>!ZHsqP*T>dgc5J4x3FaKPg+H3A)bSkQ-0PYi;#Ga+_?vN|qMlJ{g7b5lBKB zJh_GXh-5xfNzZP{`Fhi}czLaOc1y#_#nW496%B^ixEn@y51!!C#27W=2`+~m;^Hx`@!~NqjU?}p<6LPy{l~d9UdOq@$Z@V`DDv#(S$9cT z_d3s2qU1AG6RnOiyzS!n0l3 z<~iytaxiMSfWjg2CoUMdal`0y!+xWEg`5#DVwwcTz?uY^Ap(B}o2tKS4@| zuW(j@Dl4mFEV=Ef6!30&hC^d2(J<=QKn0!s%v{ac!ums>zK#24O z|8lF*pS8B*_LmT?z|(PG|6OLz{HH0Wmod%keNLuf4F384!YO}{VSUk!M<|4a5VhQD z$Pq3o878lB8KV*2_XGdc7)=qSO9qd9=ut>L!euf|o(g`1{Xk(xMupY*pzyt%Ac=sr z34|&z^g$9zGr}96lP?1;&>s8n^wM0sx_?<+u?QVg{K?JWCm6$+R3Vxc#W2)mROR~s zANWW4Gs12qUTk&wHgAS@W=#+-gWJ9{^Peu`*MbRC*z;9+>By1i@$3>ttFZ1+uvW*C z#Qo?JhRKm5ZT-yfAki=zd z6a1viw_xUdP!h}mdz(vU2sF8T48G8#9gjerrhz3{_#9hFCueNK9*_GE7?LbBqjE*^ z#!VAQZ}d+VeuN6npixfyiYYND1{RcKz+ay%RQVDlsma2xe#UcYXfucy6ws9wu$l}? z7G?zkCRm#Knp|>YB1O5XKUc$4>h(CG&k@DzI(S)g6cNk${yzXook`ruFyX6JJR zwi^IOe@KN(vv4jd&anbB8BR}lKeDDU^t zlW8;xR6*!2Muz>t73u^_UVwtJxPsV2t z?r+^9({RC8@Hey>e@9=y->HrGJM%RB=I#o05)j-$!037cd`}QC-b%p4;{f))?Ar-| z*E0Lt2vqDB09v%l{axs;xYCP9lVZ1EzmhR;Vc`A_XnJ+Nt?-L<*ZH<>roFDtw^Ov` zE7I3lnYz-h&9s|PH&4b8ZjrAO+_qM5Gom|hE_SYKc?l~0@@uU8+L6=F#W zlQ)9JZ(V1!{6^eG!w6odid(2E3gl-i<+D|B^+hUEO?2Ev<9DnzE7Q)`IBp4Z{gG!z z`DCko&09F?q<+ji4<*to9E!L`19Uk4WT*Q5K@g{NvNg+IDa*18YLdv{FXZ@=(W_x3 zzQbUL!3a+glv^`k%(4q9foLbFh3c`y?k_wEo{sM!vQPN{RC6wM`Xm*o9bQX z>sen?-`Xxt>x5bKkAM8*CzzgkKoan)l!0$HnSt|t1wUvjr}LG0dvMr!QBH@QYTZ)| zlRMoPpW-mCcxwiLGTaZ<;OivDB}ao~7;Fy1;91aaj$&AhO`jxG!9ajRx#6Ln1bkYh z)HqJ;*_ATKSUeZcunlF?0JBy7DvW~I|HTNCt%6QYyS{K+L3pArMJ@-h)}z>tUZe~x zoryeW41T30Dh4Z8DI6WmkRev9#QGE&O}NA(Ml zpr>&^_Af^+3KFi+2nPGNlSS_Lx(cD_Jy|{A@B0zoIS)>2a zC4k$ZsW@X)@wVW>^YKWfCa9hx{kf-!BmJ1xB8-kO$mK;qXc?aWc^=xQbW7VnNG3JgiA5z!8y8=*`!26}w63!BjnwDj)&SQl~u_$}kCa z+Cw-ba@s0aXf!Y+)spV&xeSr96A(NjFGG#=+3BQU(K_{XR)8@Bq%wO?^C;^$Uo0dQ zuTA68&e)SvKaPfe@*)1j-7F4EN(+ZL?X@Agw*iZFbj^HxleJQaQVF|UJ zYxZNh8+K?{YdFl1$;hTlg`^v#Esvjq1u}I^NM&ShbG2k% z;N)Dff_qZQB?INnZ{v$Uc%ew^Gz6H=`cQP)S#RSLKZf;`0g5_bEIiJuZ{vegh7<%E z!(z5UEo&wJUTAZNI*` z+}zE5*;%4Ev&hHu2tJI}77OmbfoRPzNqXV;^!&bQW*9{A-Skvi9=@`a&wPUJG(mQZ z1cMkIZ>hL|>C&O8>V==G@#s7v(pxIp5C|iWq8E<;`g}z`pvarG6j9thiYTJ9$>K(c zzUh`X{v&NZ|DLv-qXTKnBeZ4QP22YBfwYwn+OoW8+jWD|##;4yeI8~ax}VGJqNJQkSqBN9z)x_eU}^!n#z|ouJ57S5%QfuFaxt<4Oenc@;OO z*W~I`ms`n^cBf^&k3}(`4==MQ%3!pIMh+|fT>hf)28`VA%gj1gg>_5*_B23QHf?qB z`c~r<(@f2NoLpn_5DJeL;}EZaiC`o@6@B4Xv=1=6X6Ex7v9 zs-|yI8L}Nu?$o*XJEl8*>WX5BS>9lH*a4D27f~XFnVwhm8OG$1vht^nk%iJyNS+Eq zneYc9xGQ;Wm#Ewj<6YwGg^4r|jK`&`gw5p2KnKRY$S+U|S70JIE&Ktb0AV?FDq*6s zUurz>FG`KelqfX{!}?Gru?usE{E1x+L^TGdOi_kZLIfgz3=vL(w-Bx*OXT}~NfZ$A zs{AE+lXS}&r2iQPr}D9S;l)tAqLKkJ-SsS@>EIVd!LomD6yAH=7Nw|YP?yhuDqd@q z!Jp|?g!&~suUd8s&%da5@%VdeJdQ-N;((ifff+!--?oDn4ihr9>2DArSG~cSt1h%M z^0Vr}{-!#O>7Si3XDN#9i!2Ao({>`*ovYsA4`#jweR;cU;VX+CKLpq9Zov7_f71jf7886Bm zM`<)i3x#}5egsem^qoJ;-hMHTwUQl9pbs^5Y(pArwaj>91`r73srh>7sX>-mGL%Fj zZH^Q^{EaOzx~1kZq!lK+LZ24%G(JFwev>oJ7@V5w4srOf-4Gf8%YqmES*v3(U;Cmz zF0@izq02BvEk5r>rkA`4KCmE;#vimnBmWdTmiWfnaPl%(|H4|=gI(Vt&!3#0C8kfn zq%-gbZTT{x!Xy1iSZQJUkbV?_ccVzh)XVS(ZTUXJY!4KJ@sivbOxf6dSIPoRl!rfP z%a@amE2WHZ86z+yF891hc=2vTX2em7KWNKW2%r4ik4TLgq%(q^B!d)LnXd73fsG{P zUQF~V{-6z3RTbpgBh-S6tn`8)0gSLD>;mNyGUinE%ndp(y ziixW62W|QO!r=ats{X`px)U$*NPGd)oyH%u-^nIxH5@-5Nc|YwHR|Qy$7N8u9;HptbUy=qs;mzY7+bTv zG{UI=T^5A{M+gj^VJS=XNlAJ8R=8ziDmR?qU_@QRjTfclSUz0|u_v%2_(GMbug?$% zIHs6x_Zbv%H#SYOOxa|4`tKS(h)YzNo>wr&(;zW0IQ0sDfT8Fpxd{diRO2}+*SzdY zE-jx3Lm?f|Dtcxq@L5fAuh78qIG86Y*?J-QJC`ek&kJ)Es$7K{S7C_ogm^8I#DQXP zKGitZ*;8DGD5VhHO>VIQN+tiG>)CMEvth1hqlEx5QH_F0Nli=UzhkZJW%eA~&AMDo z%Tc1^zvHj-Ht1hBeekRWgf>99%k?b|BdKZm%+kE2EAfrQ@O5~WkaX{YDCN>5$TmcH zThfRr8TV<0K{E@1`38Tz@Q*=6C9J@(w^rcb=yVw#psEVvAiba?oq>d8YLhPaG!)@lbil?+L)sp!W$Nz>#zGq6cQ&>AxOIA-TZLrF|(R1;=k*pO73 zKnNF=2u3M3>vFgg_OacmLNpS~*JWqf^EN<%g&EL%_Y=}^fsgZj9+pclH1&$8`~|E{ z$2TxxyGsY9nu)hxg9+(4@57pY56%(YAR_Jq%F=|-$n{tiuqxE$TZzrMHq2BPX6g$o zx?Cx9P_&a_Ft?}jQ!yL|oprTgX_o3zZ~@hY$cbQ*^v5vM4>F@88x>z(b5?)f*v}7auk09CNid5`ataS}f++|x8NCS*LNh*G+YI>5==u3)b zPfq%<5U0{0(SouRuHWV;xOJ+OBz<~DX2x=8xvoY#@4}6+B+pfaiacu&Q){xy(^!{H z@R@E@jk?@(KC;;m&ijFU4RssD*SkV-74AxL!jLpS?vAm|eHYd6EnOE-+ym}&JBE{m z{UH9Vh~N@5wm>2(sElo_Rk}+3uqTpg6t<^pl?DZC&9tKoK{kC*6?ssbX9x4%|kd7puWtaoKE7at(l+vZUvthKB}iPfTh6{;j(l~;`l(TmRj zk8o2J76G$mWNRt_d*XAre+ELzc@03+n3m4$MTLsG;&pNgF5zewQCQY#dyf~Wgt z@S}+&c;56C>GdZ`uebBTPKX;y6)W5z zGlT+_vI3_hMTbH@sv+v|p}-5uD`7+k-KAUeRxGAY$g>H`gEAxCUGdmbLHtV?vhf4~ z2JktJ8!@psj@RQezc5gXx@MX2CO>P2+*US?=$OA1b=KHhC!SDVX7vtJmW&` zu0Db~SybZXbp)dL>I&UL*J6>Wd5Rz;!|5TT2NOw7Wbef+Q;CzoN_C22F;NASedW{Q_~e`&%K3s3jQ*>il)PJc((jF|@6 zZ2pWTL$*^ki@Za8YM%W1PCu}=S!T=f6?$PL)@L&`hs%cbTxPFhDSy_IuNPj2c66E)NZb+`%loDAeGV88=RpF?4h8Rz z{OL>;9;FEGH40x(vuszu`bMWrl?HKT29CfzMu#-~R<@D~g-JrHQl5$PMOLHivlZ8D z{k4rjN--#l2IZ~IXEdaKgy+F0uN<8UbQa(n9#cuBO(s@MDLjD5GVJX?;Ts;RyjC1c z9t1Jbl+(uGJJhkl9+lXOQFjvPBW}q`Jv5j+aUslrBsD7Nhi&-sMy&9TO57!8(g0y| zyC=f=o(NxO?v2pV?v3y%f{+y4)S*HZoX$nGrd>oSZdBn$hQPZ#y2HPTH^ejEzh0D! zXgpqf;cc8&Nah(ntm3j!34?TdJJK6U_gVNj{szBXVfuC~dz*vx_Zjx{^JmYW!qe9u zli3U`AE1t)q>Ib!#Qe^Xp|!^3P{e&%v|_s4@DtDSx(@cn`7`_(PA+wRUMq;J^BaI1 zQ8VyQ$tNiAVwRJw;m@4cInUSgI%k}&%NPWxRivc$VM)u*Py}Y6s!r{V9NqUoVFK)7Z64g6woe@ zLBC1n{DrYU0828Rd=MYN$t?3>91BLDpj2?rQV0xj;Hnp#O4cf#EFuhqL7B{*LY9|k zWzi~;Vr{P;rZzd5GTlkU)s8Qe&k@c0#m!S1$NmdBo$~`}f}5wLyWez7Ca9ejt%_ve z#0*aMN*Ux;8fBWJua*KqzX>LaLrR?qAM{Z~k(qrnjvA-eNxIKSR@rPfHl;J{ zRN^IAVF8pX93O?ht4HH+%~@)piQA-KSRQFp2X71nPZgq%$cuGCJoO=%oOoFAKu zLFTA61nUL7IG@2yMPJv{JxJe46so4~#S0kS9Q8Uvyb4P3&;tete|(+={?!YoA!F5~7a_*n|au(uACl6n~OR^BFPO!F0Q6%eR&%uN-(h9T9G zrr`XSS~e^B#6?^npO~oU1__tO;BLWVLNAI`Y8(K{^bj}eppP-wRPVzP7i1;_nF(k8 zd}ZDWY(RhfitM1k2ovVpKV1SLDF5;@@W16lj+==s2)`O49R7+q@i$TX6e2n7v;p#-{}%&0EAgGt%07Xwn?ZG5Y^jmRN(_@O-Sr?F-mykcd?Q)?9pr0fD(lM#4<{7NKIq zV~+3@N?dr(V8F<(5Qe#^y46h}jxgjj${>cGMls#NvEMCoB4NC;>=x2v2uu>z+#%JG z;VPUVq*J@A5b9L2P7!8H)@j0Hk~LbGAXz5}<0NZ>Fif(}6@o--C7u}-e!eY|FO_F% zpO=S1FpyQ>6t7v(w0#j{h<0sSB>aSf)y0_Q6mU0;xY#4g>g`}49B!I5#>j6dj2fU@l75Qn><)AJt|*a|m}Py_ASa^7#B*w+5j9a|l4h_@s|@o+EU=i*W2oGo z%1_1Q{W?w*Vca@Sg4t8Yc^94n#x4It3#FTK)sTvp|KGPEcjfE<$%dRnTbgA?MIKV7 z_TGt0OXkBXP-6MNb}Mdq_B~d%dDAo?8O3U(9Vxj`(>HKO$}Ri&xkBek`im^V+T^v~ z^}?Ef2np)Fbf2rB&uh;~<&9O}k2U|_U`<4X1oa`h2R7{AVomJF8uD+j>JcG9ZKeBT z*1LAO1|VM4kNCqzidcCM#0JD!ppkaKLWE~LHv2+7cEH@Yy|%!_95TclRQJeX8zLv! zpbbPbUymFfa2ca9hbWgZ;_lr^cb)^o=t*)sS__T5tDlahvy_ggd(iPJlCTSZ(3T%2 zlz8m*4Uu+M-B}X-rUDN|inxtuB`$hy`eCp2-t>#excN?{Ec+fR_~2>V$)l8ktkrZ& zrhe7xSeBZXjuesVMO(g7`1Xzmdqj<1KBMoiU6b=U;~=)-7-F0CVjzrKs(hdHqv(85 zHd$On3$RoYc#hIi5&JXsYQ2ib zlUm76Qy5p#jIY3wK#rVM}!1!W6SpLx^}1Rk9MH7NW7hKqZQfx;RmPJM={eC5+P zuXY<^26UGG20|1RA=qYZr)d^=^nk3G$pHj*D&|Cv0&C8nkp>Xs$HHJ-CrV1mtwgX3 zvBTQwtWI1z`NG;MHDC7ea0O#cg=M`T-AYaM(spN|q}_^=cKk(2(*7Gz?^ens;(>in zAv^P;hmd_1qy9%>_jR|ho629$SMh#Cdgl`q`{zR0+)m~GL=R_aIJCF@3H?4Mt4a@R z>P5)-bEv=dJBmAYNn&7_J^*z(ZFoc_gVCbcSjBbs+Ri#0J z;jKyv4pDGWyw`U1hX4ahOL`etr|LRQZ{fn>XLpOm^E#<%WQUV=S&GBbQiZXfLqB{TDCVBW z!+G3?$Zx_?%k!#t-g)N}WJk=Mor=fIe)Gti02Uq@c?=o5Q!(8vn`QbAeETJkvW^IU z@3)gdD7RDy&_>equ1yM8=w93xzx)Yez$3f(h#+q_m-}M#N{Fi~>e^ZH1H`si@VB-C zy(1iB)rXU0csDFKuC6$^md1`qh^yK3qsiv8Q&w&@UQGt{mWp0TrBAbvbozW*vM^y7 z!-zpYBN0+qn(Hu@Hd)9Sff|Jb@P>f^WA?|%Lq((%ZzhQs#h+3iDc4~(2W5sqrHclQ z(CjP^M%)g~K_5dX`b3})U^ABwLUuX)&3lPuR@~VlnQ07Xtea^;E6{SlaXPlAl-OBs$$wV!H`or^J(l_#v%E#{GWyI`*3Aj z>x+IeysV?jv*NfnA0#<)n@e~I&%iqQV9ciCWz5wuc>3l=AgpapgdsOiNCXlg%U=RN zGdN=}ctf%{qJ=czhbcwHiJwtj)<*}ERAF)lL?(oB^AmXz7ZP>4{G>sZ9P!x48{4oj zH_32XD>y+$lPWO6vq$0FVN?lKD0ehxR9rR=N)<+;zLvGNzYDxeV_m?#I!@O%ea~)4 zrY`>u@XsS^Mt1P_2z+UD)#*Q*)dZw@`skGz=Ncn?d+|EQJI}eFK9`>2$_;hS#>uH(8Z{ z!sJpERVPEy>*guRHPV0H@*tUq%oeNi{Vcm*#p^D)ypLt6PcA33EbuY?C^P<;DxA4Z zDh!WslO9`l!lTFH(=gE5uuHBd=bem0g~9+y&l=>!`-YBWe3Kt~XUKG;sz{v*rL`_3 zx=~2=D3Q6L{1@Cyci~}zz@dWZ#Y2U0=(-Ql9q%FNyyjNfCuzjuVAr~;Z8el<0z}I-m3#+%?KOIO0eci41%YWY){?HUC|KZ1HC`8p>)htUccu!NAuyfh zmBOMWu5&y1q*1=&Fsd}&av2g#xB3imJBN1H$P81uYm~;IBv)u9W!yM?B*N4hZ@cBU zt}zxrlM`aeLt%>HW{2&Rn`cLZAGBx19LP1U&-ku%MhjI+Fo^W0?u?aBAxTZ!!(0Xx z{Pr3AN*70$I-ck2YUf3l;pJ{3{`lv20dV-`|K#w=?@otOAHwF)Y`Wtz1Oq1nk|}w> zXViO)8IP~-b6H^~r;iaaCO956IpxMEuqqv6(4%f%7!Hze9VGg85N#CI_~sCJ%{RH^ z>WUPw;ejmNT~3F={JB#wLi-DyIG3<^=bFpxO%_6jQss+U$m_#zR6rtfIb0fogMf^k zb^&HQ7v8G|1U=tVxfa$^{0mriJa(uTelr9**`LtOWRO)D$_PbQQElW3t%8#6HgA0y( zAvF&~DxN*q*x){UpwqY>5x#KzHw&I$xV(?zBx3KFh--zRCLsV941OL9hHA_lKsg1w zC@3v;FBsMZw+6~IkefrVvu{~LZ-`WhZrjhu?gpY z#5)MeqnMFUD>^W9;i%}qyoEzQVx0=3@T7)SY0saKmw;^CJ`2Xp%UtG#aha#_BF@(! z-RCQ_<9c?48Kzq1xv)Ih$;9(n{zY)X_Fg(3QpT>%4uXk z&hSOeQ&zYPu^{X{p zO1(MdtMYT|3LKbm>VJTb3-DuDG(lGk#De*n`V_`UevX3EHkwu|n7AHeoKTC( zPKMK-CXaK}G58291l+gIw+*XME?*Zc5KBF$Jppw&-n2r=Ks`D-|HYm?uWRPqXW$+| zS*fI+F|Grgj|yAyN^GGrjn`d~4iR0+aKu2R;5V@{FYMHQ#cAtHOTK1EA@0LM*i*e? zdld#`@I~0ybq!g?R(*OnY z(=b(`mkOZ@rT{TPC03D10qip%{dNeFLzA>PtFNWQESldnkSreZ|lphv9D-m&n6 zO_m*GqmN5Bp%#&BQd#Cj=!K<%D5CI}Y`6WmDb;)($WH6(V|m4EMCO{S48u*Y0K9Xl zKoaZb5h2D@J8gbh9~RvB9-gT}i9?tJQ;(272tT|;kh-S$3GIVWYlV^^ukbTjBlL9p z3Se(8X>tozA zoj#RR=0o`ka`KqWo2g`jP~ha)KKG+t8lG^UPnZd5?GV4J%{@~l31{)hLE<7#*K5)B zCh3LKL{ZxdlN94QPjBDfg~fOtI;(&6LT<*sU?u~LaBF*Dg7~4;q{LU*QRa4*nZEDD`cjA zVHZuN#bgQm3wTO?A});e!i~$osTc5-Z*t2qrcN(>Nxb%muBOX~4G{>nmq~YY9VX@b z4Q@H+`=wiczmHtG?X_%=F55wwG@8e~yb#PztruQG6s+*#WkQU$*R%r|H;kW;Ycv(c z@z)Fa7>HqZ1>V{eWPs3EnJO$rfA`vfj@3{x!^&eIG2;0!L89RR}59L{-seA4{qt98%MOqcbQ2rt;HZU56Xd z6H{TSzX!EMAnN~^CS1f}dxwh2Fn(3$B1a=WPZNH^a7PnvDQ9}jaWLL}__cIS)d`DW zpeY5vs}@tk*EikDpOww&I{7J|B}_tDMVbFp%$eotBZ^0I6wLYfxy)r{!F7VYiH?Gl81M?0{WPGL*g4IXP#WWhmR-D_av}Cw| z0EeGts|;v~yQ6+-ql-)d4u4+p+1yE}3|Q`cvSl=R`4(%Y!n#E>6TgX58~|cL*l0TL z!MxuL@;rIoiazY(y8Dxk_vfqNKoP@UBg+JRi|qs(r}K4DHf(i zi{ew%3F(vQ>`LzHsLSzevn8CdJgJOp8D*`o85WAxNE=vYGB7u89zos02akcE(ghW{ zqR#L1X}m^41N6e!X6a6vw$AVBRalJHDuJ;*;4vjREme53hRj%a_MPrF;l=lGX_`0S zcM$cdICqC0M{&WKN^VtTl$MaTtrblB#QENma|O9cHYXqaoA+V{5DhU2%Cc!l01gdH zF7=;8_uNCpAvFvdn$YXBg3!J5lKxk}pe>*r+adk?S!|aq}%LnF>V0yLeuo+k^Unhm3byo+$=+f-r5k7P74T*jNLQYI&8_i7f4 znUs{AmLHp+AS@^Rhc$BvJ$9ww?>G`IFCykRr{RfI%jO8)nJmhYt$HC41+z|!2SPIJ~5adpPi(D<^Fg-hp=|8?2R2b={rj8C@w9y3Yf z`{HgI28m~#Om(qwcZ@eug*P!>Hfk^PL-MRRTg`##vkW*I!7#X@GY%K}K>p4TKo#-p z*T@TZxe&!p_DS;nh25WvJ0N{TJ;{~e#lUT2r%(9^#a-hgLKn<1oZ=&@IfLG#%JP`a zLUG6A5J_pWChSKz-|-<&FmM0dEj8lEP%r8rt+8?i(LvrN$=)-H$B`{6*Hv_Hc_wZu z=2V=`*YbUs#^txJ86hrrCN`3xM4!J(R+Z9@m-GX8;ErO)5njI$CuVd>8z&80u+~AGC%?XclS~{iaRv)xo zGYu4e<&N)|Q_-;Hfw*8o=UComx)yJ9`mJjKDk~_JN}$pYmlMz^6UFq&{rO=Jri&tv zoPvc%`g52DIYfy$gx{Tm`Y>c+Y6VG6=jl#ngVRsg`G9?G`1U;e!e+$CJF@ zL93ygpQT9Sr^h%snQ5hxsc>zNP17d{GoT@5!0L-0-G*i#l&?we4DiE=dob^36MPOr zk@9~|?;PjnA$y3o8Uj*xGk07G|`-)NT-WhMI~^30iPfidYHH>trWJ9o1{=|ITTxp+rK(>$D4GP?15r`kE&$mIo^=U z7(n>YCIn#FfQ-_M?t+nSYuT>i{b{aIXz>HfDBL5wat0z+e*#E4$N95Xyw>8!%~K9& zK6LXFT1uSn+|BfC$9_!6&(p;DCW=0EJGINxQW&vNP$ z5$BsO`YfP6QE|Rci$0H0pJ{Qvg`&@R>N6wG*C6@~r9KI9zFR~e8TFYP=etex`SCO2 zlNje)D*9ZdK8xaf%SE44pV9S)=QNOk`ju&7gul=via6gHqR$@cqm1)S5Pe>tKCyAW zd7_Vj`b>%QEf9THd1fSCREI?ZbRE7v=8VZEx3&Ak)soBVEg*3|D#SvH`Ib$J448d7 z`^Q14itw{>`TnWGJ9uP$UU)v_v5#1%<%1mX8nNb~bthWMvNzvHFKokmi-&P#E1W%z zR+O8B6U2)8HR0%Kj9x{s>jfQ2+$g%@$#JQt>Mf_q45~P-%HW?+Is=53u1xz|)u=~=rD8O8 z!`e#m00P~gU~Y+XE4k({ji{6c=#%I?FfL7a2jfC-Youzt(ZUo5ijnk0FXtyrb4X*j z>5S{{&5d7HdT(wV{I5=P&GovvK(89%EOY8R_bl@y%(st;^Xm2%}rbe3s>LoMoEVGYQh{pA1uZDo^Ks+#2Uf8adqser{M8r)}o7 zA@Vv-d&L!c0q1L@lGk0~wDmA-i1*>*ZmCT(@l?~ptTmU@3NSRA*MGx@kSTHJqLCB3 zZ3@GPopVP{D!q2cOpc!9`88Y8jpD%+5^I;`vte3XUPvcVJ z6>?SCqH9gkljk^%&92bXR0e$(I&_S-5z0GH!D~-X*R~ermX>@IXq=EAoSx6*l9$(7 z3FbDhgwsqVt+JxWUGtPK7$kuFU;F?~#ql7Ya$%=GuOhW(o;zZaSb?RB!X_#prb8(; zHvHUFEj@wMv){9YkP(8l;;VBK!+EU+Yos^MSsbrEGB#Nl3gSUVZk*j1ZJMX>G2IC? zhUNz^PZsV3ivAkN3urUd`NWm<7M<$P)_u;Xzd5D~KX(&R0iU&Xq=OV}`j&`$-TmTT zY;>t%tS}AF-6kxOH1b2l;L>fe4>Bb2NNk&;ugee{<_c}5Yrxm6+5N4uS_6}{Fz7+A zmjQJu-UhN2NLV0VCG(jYQN*Hp#zo37XqKk?8M)n*t6)sIN`|v^I>;yN;n$I4`KT9Q zTyyc5o$@&|0*(Gq`da_s`DTBM_Rjn`Cr{!PePOA@Yl#;>+!N*9SnD;Z#`bq)_@RqhQhY3Y zV=R~1CKe4AA6$yC^J&e4rFVP`zNN6bh{n%z9F)P_o-R_kx*zAyAoI5;k!ye#y60-5 zBMj<%AEMV0o>Y*l&8b2g9yTkt8m5Qor+xPZ+ zr|JdXNp=$)--3kBt4M?AwLWbfbCAQyzLCn_geybPU z@Atb;K&Fnj__?w$wjl|>o{ZmR?}BvR0b&c(c36xZmXhW_{_&5n7=nP+ zytWw{a#X%=R$Q$yM9_WZuDgf9%EdZn=mnpzP^-i8C5>1Ho#3&KPA~+N#*ZW$(4zzm zNgR%eZ(Ng>><)R1wspDm7)RtAwAp*UhiaM`h7XUb%kg<2Q-@Oh*QcWt_`>7((j5#l zvve%XDe9IPQMtS#D%aGyBiH2IVObNUG^Vt_4%M?;Ob^S*|3O;cb}d$Ihw5rbl&}=$ zO(M&SAfC+mnC@jOqu2wegL9WqVhq%Wnm&)4yn96EB|z?mfKQeB}}XkSuG zuLU^1z;1CKAFfF0jjV~QGt{B|42ns&h?t}zl62{sN1}hu_I#bf<1xwt*MPl$)YpUx zpMK#Uow7fp`!h+f0#5^C=|X;bs!#>Qr}9o=XeU5nWGiZKvVsX0E38||!$Lz%+rt?{ zuxnGe(1}}Vx#9St&@$->J)*?O!|BJopG-fTv6Z#LH1nF392AJ}r=sOFr6P`Kj8v&W zf>H=VL=J)M&|PSiHn!N-{W6z_VM}l@j9^C!B+ctt1$=f#H%O;wc5lY}nSI7U;U(ab=yVzVt6eVaaJUrcO#+c#r%Zy{ zZlhEDSGlMi#fdsl_;3Wn?8m;vTI~bhb5@$#IZfaS)frs?Z$Q831N#XGhg9jcW!GRZVSq(tw|<7?>K8vIPyO~mpOT-v zp?>H(5Z7(H7AUtJ)i8KD1xFbtqn_J@bXHP&nvqs3v1dT9%eV}fv4e&-K7lgz&Mv>^ATy}%?QE1%ks?9GXItT(Id7Jf@-b3uPhiQy~y8CwG>8!=@KrZo6p z_1y!3_G9h%5a<#}N+RD-X91N%f_H=c7}l<13OY+^!EMJ-b8X$GUF`_=`2!}!}igJ&DXi-u?2EiylhJgZVOeeyzfXk3SF8kI znLz=L>)ka8MxU0S1HU);w6wPW4ezhagLpw!L&)UsO->(!Pxn6n*)_*|?H@p3`;(CC z)n=TPI)Mi?10Q?K_YkM;cUM1J0nXpi3}zY6wOqH0F{EkC9@t<^wzqURd@u<({t9v0 zmt(g0=@-Ndo`YI`!0iJ+N|l6zW{CmcLqK~=tK*xi&F#THn2ZpYK9B^NQ7uvGg-X$W zNp^QJ#3#=1K$O|N*Gn>@9(Z|QVL@OaexoG znyb9(XF6fPhZlU?8v#N8h*bLAAYbF)T>g!x@%GMHpVEK!{XO2H$o(*WXfF2#zG~L; zF8+SF9qIUndkyCxkzUFI=65?SSd_+Vjkg+YJ8e zY+4#}uHgF3I?t#-AHM-T5hGytCDVipI9aw!jm2(;}P!rnNL&ZMF;eilw6) zE4AeZI|b#m(uqT`U;bzn~lrAUz2FqkX88{)C<8}1c3K#b(@7cF1d z?uj(wze_HI_UKj$9aT{8|d4{Oll^Dj9Vs_8g21idR8f~FM43(mzD;hA%mz_wZj{mk zhm3gGMNwMbG9l_II}t%=f^!TAq7jBr=nurAswS6YTNspQNlfelAyGUT{3%vP$lZR1 znKZ9#H{yp7pK+XoIE)}R-5zc57oI2Bh0Oj;uKFIww%ULk!+w8d^dWF#C#}XuT8rzh z1fK>E$0t{Ps|G|oAVtJSWH$Z&O7IDc_-jc%IMK>!TX}65J{zYE=d@jp0JrzmOed$q zNBMYN2&WZb8D~MAajQOQsj!?ry?9mY%q=TX627fhHb_h9D35#RGp)ELNKG?nTvJ2# z<9@n84yQPLKNm8)z)Q?f7P0fpkWuefHQzPWjoROhEq8n7*39G=!`lKY7QgY+! zjgJY}pi?JiWWY4Pc~EMyaFoW+$Z)I%aMhOq0W%rovS!=Wr=xHp%2(wp9r40RUx=6D zyoz5XQC_|FWo!3|xz7zrz1os|wOKF_Bu+pwrrRE2Y)1qzK)VrWgn#&A(+;O<=X_P3 z8P_4qZ#*xV#C<7kE|XoHSpFjJ;?&+_7bhrH_zJhF`A)|m!QAw3_iMiTwf94q&M*F( z{hFulxnC11D4VE0X{b9>NAmw--=>7_+l&&b8!0|}qgxY``N@F&8}a~bnsD{gA^ozv1KvwdzGBeHJ!f4*&D;glnEM0SB-Sy#IyEG`qA|GpO+Fx4%fQ zSQl4IdM|&a{zTjB(F16FK71tuG^+G;BZcTpcnoQ9Njhz#)%Sra5pQU3PCQ+>^;TxZAkU?X{J5JnTvt8CP4dJH=cH)+fp|5Dpy;qNLJp^?5r+!3e(j zKIx7CybGqd+RR$rIb9{6=Ih+iat-G5PS>pFRlFLxgwlO8a{%#8cF-5XW?#$~Xzn zo2e^M&)4$9Rjr01nx!ts4+S;15Uh~?`Bf;WIi8-bRT(qL5w&E9OnHRECrO);(cHw> zlk~=+{RJ`Ik{KglO%N`kdrpl$gD|)gf{{x&(uZ9QqepR)`723Duy!|{P^9qd6kI`+ z0-sg?YkQl&^6N${*qg%pdIV z%U3vj@|D?1mU&_NGy!jy)3=o8^G_;Gw=@PId6*%{bZfN1&y-{Ba}43N)~oY0rq-`b z_42Ekm7La!NiFk~`lK{V1!k|eXiN3NY&m&JpC=UM;eekc{ERQjJON|3Aw>A)DVM9< zibLZEr|3qW()Fki;YZ*1A@jzoFOorx|9ZJc`)uh2i3A#%=V{f05e7xRuVeT@CAoN* zX+Nj{4<82}CGpLdr)S#LhpnboANhaJRN!cYWlmUHs@_$s6yFd!oicIqz_iO4CAlP34>Kr7|b*pXeT(|%+Wx@0X%6! zGMRFs9ARm~2w(CgVlqt%b$47YNSEQ}%eEK1`GRM-@$C=(rs>}G6v>BF>BdLbRW)hX^$T*vE6%1l6KU^6Boy%8%_6sfg`9+RIw=cRU&>+WTPv7Up zh`wXIe3i79I7HaedUb^9+rIV-NSWP7U4Dv^s8N^WwtE`u>b7ePviX4Hs;uP)9Cl#P zQS+or%W&qRy>hRDJWBx;=*!$D&tzOp>1_F7<{2npi9hT5qG7%xp@&kUY*-5`*tWcXn$#p;=Y9jI#MnLbtX{24b^8iPLu}*qYXNp|bDu ztJ=ohn$;5qD1@QVlm>56Wfj{zs3Pw?Xg zrbLAg&N0N}73!hD8H9MeL_L%^?GTUM)B~>q&>LR1Rz*)EJ4ok|<;eYEl7mD&d}x>D{KhqTB{t$p}Bs|*G>>ijv# z?&!oQ5xs~-POwDG#A|0H`F$kpYsW}2{y$Ci3P+IWaG2_qoD4;0U5M?PFRLuO*6NZm zk0=RQ5vGy|#xMa9tcbVb*#;~@2s+P86GWZZ^gV~VoJM)uznFv8@2(eeq7RdnoSI`k#!5V+sFb38)cYVSfZi?h!H;d z3doq;I0Z@KNJH5*dI-#F97D2$n(!lck<>jcqZ&-tc3a9yNz)^BDo#t!sc2xUWfovX zd)YN5oiNRa`cV!Vi5G zs^a>J0{BFw{7l@fqQKcEYjoWCB0+pQbl~?r^ZW7+!g7{=$@eW(i0Mro%Kp#yI@wS4 z?r-&SuT2k|y5i_~!tcT>RP7zh}(EGmS(3^pa$R6U>fG@CwW_>=s3z6JBi zs?jk2^Y1Hj+P44riRCu-vm3$^Xxca0cfBBe=aqbF3yL08k6)`Y2B3nn=NizE+;dH2 z0~AS4ZP(=ImwS_UIxRJYb+O-5E|!GO*W!1(dYXGaH#IXHcyjdPc;OHjG9SXi`oqE@ zEKFp57$R`9UgKtcn8^At!un8*%jKlQtf=fAV#7c)w)jXf{eSu1a9x`3h2hrx2D#o7Yw{c>BB=F0-oUXjJP zNj&3=c9;Xkw1h|Ts;Dyjx>jkK4X@Ig8X4rR=^W#jAcj)^X6pYr;|M`ZPYwDSt6QFq z3PaQ2*{87NikN!x5NSPsVmyr2uDJReMM1nR<5iyF6j3+q6B!M|R4|TzlJ>(JqVP*; zA^bMzQZ~LxUK`i9Ng3x<|Ke4AE&5i3`WNRE(kEw&-QRds|KcU^ZU0*4^#$4k*oCvQ z?R*_u`?eYncn<1M&?%h`CE1< zz)M~{_@;XMAXB^2vPqe%zNohBQ1;aN<5%r+)W^P+t4&p8`+Vc#F3YAa-UYTSjj25r z;JgU<*$`{l6l=PL?(@0o$JH&WT=kBz;A%xzjY|CP?Z(g0*@4cD5tbdXB}XiqRAll; z)K1GLC)D$Q#I5wgt@OsN^u(=nq$1*8%ckD{f@XymUWGSag(qHx>6T{O(3|Rwz%h+w z6MoSbIHqw3>SNdL=I*t7ahF$Es?IYSS1YY3!_b|6X8#5BYz6f)e$O|NHu6{ehD^K9 z%|DvHlDnc_BgvvHCjjimpVMHP!E}HT+sJN2=4szKPiCGk6MgVJhud%GJRkFXA1SV7 z$|IH;(@CyLwE2k*46p2ql!ow^fRLDzJLkE~^IeOV;JcYONfP`v4&MZ%Lb&M%h3QAd z&o}?r3nZ_&0JIju&lWIdFncd}&^k|Uo-g;Hjkvw(^7Qj6OHUnk1%rCQ;w4xn>NH-b zU8*Rr><98O=_|Vh#MHM!-*2Ed1mqz|RIf{A^kQKidn%FD5U6Xgks%+Kz09 zw&NLywqqki+p!sza{0+I{ z07=@nAGUUEv#Hp?vtq?hPA|a*nued8&c@cBn}DrfpNmD##Ukfo`LKQCXQ(&hXQ+1+ zS`K6R=*OrR8utTj*-*9L`GO!mj(!jN?t)r2K?jLgX#B=>ZLA@dw?RuVsQF=d0$PEO zdyo>r)FG#>ect>9#-9H1U1|YRd5+-`n3%?xlSzCHO%J6XYP#0ci)Wa_0UhGjdeQq)GhTV}hK+xgkOkJ}B8mRk+q zJ^r=ff$}!PS2jTdUZLQ13B3Hk>#XCItZWk9GP@5F1NfPOUIe1N^MB^6|1)3xoB3+Z z=l`quiX@A&7y#_XpKV~wVD^6g|JeCTa~WujfS+hE)4(jc{D0=F|1)3x-#cI7`CD<$ zQvc#-@+gOL^Jewo6ct}%`)a`4Mdq!@voI;aymgB`2L)4m&s=2I3Io^>S+D-cCa!!0-vBid}53A!&s^>xs zL&SWs-8z7z8o&o5&g%J{36@3bPW5b?@q0i}4XYzpWF63C1$r2f-eY(q`21Ipxij*% zF+>ba(3q?m^s8_gI@HH6$!i=R;ipxtrUuuIznW~ao)&0lJGH8x!Br=l=1(r_`|x$W z*&Zu2vOdg9<}*W5(~^ZO+zKhtq;c8{!cwqXmf*{VNy0p0S903ZNy6jAuHv-yNx~$w zKZ&1HP2#jCgt3_1s7mEfKWZhPO0q1*FEvBxaY06$s3`?!wQvK!k+vA{bb_;relqPz z2<-r8rEmeAOEjET1I|Ojr^JaTjKFzNI82;joVE>|2ZTQpXE>*A1?N6t4?zW(uYhwe zd8N#BB7wuNVC|{XHj^7bDHr(6MjoHhsONQU@{{@gNy2V9`B)@dpC5p)lqm-5g%aY` z$=2iRxi(JMnIEhd@`z^}dUm2`TmBHekPMz|Bf0U0*Nf+pI2{-V?}F(%@&ok3!^E={ zJv-2|6|s%Pgw4FR6%qoTAaFjS4FQ6Aty8YmAhI6v70WA_PJ`*LAhNH(lkMcSSLC&b z<~)hxWa~MdGk=Ih@rYiq`XDO2u_M>25YKy<1V5WCuR{#`NRlonZ!liGQB2nh7UCsf zPF zOcpDVDDuRKM2wP%z~X{u7Z%3PN302vj9w(;x~!iw9U^D0%G~s7D7}EPUgoYe!Ch&8 zm$@sXlU4|z{Jl)%&=rwG7MZ(hRkUgW-xis>+S=UJwp!+{lU74hQ03A{T2ZjD~> zA?^^l)(Y+ry>NpZ;esY|9Nb}ga)b!krL6{cxL!C%+?_y+Tw4WR5qjYh`s%b?TM6zc zXb;4#k!ufuJ6bQiN!-v{4uX4vUU-?l%^=qv0C%ij*g@Q_a_v5FPtgk-i5qyg7u?hI zLN;-$^4U(#OTshiA*xhZp+b>cR#VnP>?l&{HWZzVM<`owvHv z6SrAuYgd{RW#rd~{K_E)xuSCKuM-IggOHqv%mMcw$~ty?ADGaEkjt$0ARG6_78l-A zF)Fza@I)(TF}U!ny?)S1)AAvYD2sNNUYL(xOVEblevK~Ntm^_f5cm+GK4e2dP}^Y> zlxE#e=p?pox2?x*bGmJ}+_u|jGwbf)E}@{TXfOtw?j#iB6_fU7W#sGZ7TqDf){=QV z#>Q#Qd|iw!=1WdnYMCF;Y0J4c$k7Vt0+|Mh#9QJPsAwS&Kdh&7m`-R|YuxqCftJPb zmXt$rCyP~a#`4Xv;EX$4^dO&U=8dJfyCF#4n6Ohdd2*mxHPYlvDEgi!tl$ZH$4ver ze>Ub)u6iuKtp~!SMrK(Mq1G(InfJ$f_25N;t!mAzPV{LNef%`!BNqup=hTOFUFyTy zpJoBmpbF*n;OGWcN%kIKmSlGVyCnN9V3=gT4J;#ez5*)?*aj{}cgJM4@}#;6|0w&31;&bKLs(PC;(_wn~vdgzN8)ScJ3pL04gAKWSeInUy(;Fd-;kr#8S;ytRR+ye8tnmvYW3+Czf4&#WG?s^A(GT zrIN2mB$g_^VlJ^9; z#wzwYud9~p%o&^){0y^=cueREjQw{U+0$BhuOoc99Tdh7EfZ+$^q(`x0R7mX)QT?RU z0Kw_&VOOAipPY0*nu;_VNEig6f!+xNJnY1OubgyOF}uBxN)17Syi)~x*i~RB9a|1u zRIxA%?o%J0KU95q$q-SZSTb0$Xhbvz^2uLtoiW4eXLd3|s7ANoQxzS@)+n@5P7T=XS8Gq7bBbHWV9iX?}Oe4kt3xz<0MOj70`>=@Q&`x@A^4oD9aS z4JLaTr+t+Ut475<&REW;RP!gP42e(!Qlwd9`!39^m-DZYX_tojilKgHy@CY8z_3>g z3lPHs&3YvX3xW|r3=0;+2ATCL5~hZJFNO`KVR{H2Lc)gfFqqJ=P%$h70}_Xs^}~4> zGHBQc{-jwS2Ein3q**_TPce&OqcJR84AYP0Q}&n>$Dm&X2_7esa~}zgBfrz z_w%QS`$2@@e#oqkB5@ypQA~_GQH(pute-%_9_C-$*3YU(MABo;iIYh%3uCz$HdPFp zLP&qqtbYuKQZZ~gk~vK(*5hV<9E`nU*h~zYfdT2!Hb)r25zmtW-aR40X}{u}IX#9a za)3GU2_w$7o9;K+tyDBHopYJ2M8YGH>FmjY?7R|$v}~B4@1yhcD43Ur^RMAF8&k7; zr%Gd4Dl;#1nH)0X4ZdOmIh<0;CB|}vQ>2eIja=>)b;x=~Q*q9yU>h7`U?~H`>uev} zHDwUUrn6{CkYt;%WZ@3%7Vi*1+{t z3OSdlKD<~CQPhX$`C$Bc5@w2tTd08u{xpIgi6CK~Ct_Y00x@7%pBE?v1V|R87$Xc~ zzz{xfkQ5LsSyW<-aEJlp^}Hccz+lOu5o1I^jN#xOCIy5_mJl&U6vP+>?vYZ!2+0yA z#)yU(!0&luq=3B4y%p90IoN?gxrf8uR6L1Wf-^EJeM3n92~TeMZM z05Ye*nkEcdx*k?K=9D{vo_z7VOAqTUhKkU$TT9GV|&d{cemH-u!S;>cY8>(XnXPN2#+x0IcTMa82?t|W|`)wjJ(RO zo@?8W&H?LZ@_kU?1HNANV)S+w`NV{f)`G{{)#byt7sSu$n_nESyz3T6?Q zHDGpu*#qWJV5-1;4CXYLFTiwy=>?<8mN8LaW`J1+W)+w%U|s@q08AyA5Xj>+z%Rhy z#~Hiu1C1+W%t$a(z{~@a3T6$MO<>GmehcPLV5-2J0MiJj4NMmpKPcNsFwtP9fk_0D z3g$TAbv{kY9hSyuJo!=c8%DG96-M)?zxDe!{gTl{S{cpKUwX%t{#v2NW4FjOVTCex zxNhaz4G{}AY}&XkXU!zZy)?&QEZRUk%rco~FPLU9AwDwAN@fkSiCM`MGHV%@nM`m{ z*Fc27kpNMt%!sdFpt15HUBwnF|!qXRx^3-l#{{4l6=<@ zoEzY`z)cJOX*cC;Ff7e^W^J({r^wSUImfVIL+-|+^(zf)H*VY@AIvf^>0q7)vjWT-FnM4Kz!ZWpfY}0O8<x^ zuYx%aMgXG-0RDhk1ZE4E17NHHl!uJ~+rTh^z)vt!z+`8yT3MX4CK~HKJA3t-oYe&Y z3s)9Htx>;X!y2#`5W697Z83FZXXmagHc0kOP`?|C#At}8pvavjXXUE32n!8Gk4^zt zlv8XdTD#hSe#Q;ZOrC)r$w=VJD9nAeC>>&o#Px$QA3^b7b3B(I<0>J188D=}c zEHHQm`F=>W4J;2qcnp|{U>*iD9@9ah8Gzt^ux|sC1!l#jT*Jy$1v&UJgcX}sdDxK= z4=^hXTMKhG=Asv~0{Yp?f{0=Rbl24p&lp!0t)c$Nf(_45`zEXx-~jj;k+~0`A7L|P z3q%1I^(zapd^vd$|A7S0IQJyOBmCcy!K_%lv2g3Z zVAKlZ8bd@8nJY+jtk5PhE0!)>wxGZ6+_mlPzBfW_&)8PQ_Ka3$#m4m;)<$64C|-*- zzM`KQG?phl1H+bQdR8m5V*SbuE1$_(^PiYEFy6ncN6&f~z$Gtyck}vR#$K^z>xPx< zp}DVhPYy*9Fl9wx`J}LwhMX;H4W4v~F+AJmzb0tKe@o0gdG>Gcdi7UBUQy1CIU~45MqGv^+Z1OmL~tRJh=v97{KNY=M|= z^8X-R0oYbUtO5vmj?l0cd^UJVwg2DY*$6UrG5i*g(&gMkUjA?RQwR`q$bs-;z>`P( zQR?@W-6#K7vbzCn==+RU+Svb6?k^_3x!^aDOaD1N^(g`h2Il;q({3S@uXJ;9@_(co z^XX5I?b8V1xH^&Itbx$Y|1~X`b~WHY?iqj+58B69{pN(E{-{P#c5si$p7?BQ{8D zEE4?|uH3-P%UK2M{32#nVG+Qs%tFX>p|OCOWqgK7&M9P;z@lUE#!bxJoYfc~Ch)D8 zrifUGjsRv`fRKJ|0XfmQA&1~L7LahjV=RVs3*a*3tY1Y|)qry&+5oTQh4|g}SvrQ% zgVE_8UbE)mtpv{+_?r-+gTDy0Y=u82G4b*B>mR4HUoz}b z1AHL;2;*Z5lG5Q!u$AgSNcw&^#Z&C|q4+j<`BB_O9zIe&{cf5c#_IQ52(}F*ZVvPf z%zGt+446emUh3XT;?c4pEFj@V=uL<-2Vw$0N&jb{Ep22rAuq{@A>lU?tXtjr6nXea zd9EfmNol@oNgg!M9Fb1Um&T`g8%ZBz+%(X!i0XADdN#V69+(0!gap zfSC(s5tygJ6oAgJ}fQ45k%~0Hza+6AUvH@&Ka&69y&%Of;BjU>1Q% zA4=n|0ayrT8<<^S_JOGca{^2=m{yDjrWZ_92&508x<#aYm^j`_`Ys=F>1%)rfwB#j#l$>&J=yQ$_bI(LG0W>%}xnMgL?G zriu77#Jmec|Mj@80E2xHA2BZyY4sE1mx?r(iSQRU;ImimhMGEFaJqtBmQ4s|D|QWFqK!l@S>U9$?vkf^z!ake)+3cU;FiM_PqYv-|cks=6{P9nJKKS-u-Z}K{U*D^I|8IY<`rz=9>Z8X#JpR$gpIB`*_7k7h*3~zhJaziB zGiMu{&Yi#TkBgU@TRy*h$Y#c{io3WoulLXAAan-e&c4>Pu)GvTet7@ z{@mwcWIl3*ub;m%ATUT3JV>n>JY;A{=&<2oBSwx2A3bJl#JKw+qsHI=K=gwT#Y~tu z>EYN%CbLtfKKj_S>5s?FnE6C}!pc>v*W~0rlead1UBUVd8w;N;DmEB5ZQio=x#dr1 zW@WGV*Wu4^E7`u||JUXJ|9k$=nmuQ(cHaEN1q+{Cq+7g1pR_c2SxRbJdd5?f|NlDt z{}KGhd3IWqOoMYn0zjN+69M8ps|Sel>;@X zas$NkvU-5He~}Il_cT@jM4ewAK%8?60pkA07J#_#Q3??EHp~EVFJw1B+ymJI@KG#3 zz{jxs0HeHM_$t6ifcpSO0Xzh7Jkka5et;(c zK7jNAd=U8o@F9Q#z!-pC04HDviiY+8Fbv?s0HXlnr$u4`P60Rr;8cK#03QXo4B%q` zR{)#_umIq6fLj3KZLV^FariMBfKLG23ostwL4XMWs{qadXazV2;Awz!0k#3e57Bf2 z#2aG00Otc#J_zjxUVHT!i%pa52ES0GD8U0I0|I01$7T5S7(kis=9>6$&r2wA*xC`JMfO`P016T=A1_PB9 zpaS4&fc^lR0R{kU0~m-8R{>N3?8STlDq{dIzzBfr08RjitCkr66#x?f`U6}BFaY2R zfPnxD0IC3P0XPz1IpP7h8`Hypw-3_;JcQ{1R%3dAConz0MobUz3Z};|pJ93&C?`OA z7#KAG0|15t3xqG0LB4K$M`T%=3#t*28<7|6ypKhh4BC$z<4lVR$)AV zR*VPmG=>9g#&8%g1q=t+h2c1WPK0oPDuA+Rpa)<8z-WMh0H>fo444V%4{#Cs158K% zSm>YV53msZ0d7P8DbPRBAKizIYYUR)m`fG!ShLLp1JGA6g{lzeaSg6Zw!MrjyD=^D7YX6o~Fzk={*WIrBta z%u3)KO-JckFXCMZDQNjtis?6s>DG$zipB7CBHSv{v00?=Igx&Y$Onq&88IJ&=r)M- zuM_KIy_+v9MfV0VU4fh5>qz)k5)*aPn+UzAVNNFcfK_CFVm32hY#$RDEol|2iEerh z^OV>wCNguGC&jjbcCFYxCPK=kVw;%A%wraa?E>vf#kMgKd>4uBVo1V4(&_CHZqaX6aPXIe>TBiM0CkZNt_~re<{JAM`%bSaj@NBd7l*9 z&O|8rBC-9TzgBERsE1Az+Y#EAifsw=eNt>sNatL!O`&dWme{V4o(!>VA z?$HAoQ#!H0ub>#iYEnUg#c%j+A{J zq2Wo=X4VnZ%gUO5&4_$?pL!3A8DVV@oAr-{u@YIsm~Zl z+og3zDb^3*JM9Cs-+H#QIi!pQVw@azKcV$Kn{dLjEmnP-vur)8$Rr)8c)c;{J$yXm84kn+|OYNfmvk(#1;Clfko-iyTiHn{VkdCwR7 zW|5o6c_QyA9qVCElejG9F_+XS%|k~*q&yb5^Pu&fBaW?0-FYk$+kLT^$G|$G`6YXm zNhi`v+q>3{S5N$N#W-`^e7iT^m3Nh68L8VGk1>Seol9bQ;_|Koip!hEC1m`fxE6}` zVlu{Q#Wcn4nv>FGh-IO5GTVb+OT;uA?n*;@B26>fgU7TL&^QZ8dKzb$*yf5!n_KG9 zPG)E2hdc&x*PeBKLs18RrUs_)&v$fbXL& z0N_4=djaA#{euAU>tj^_|AA|NfcOHy>8a$CpG-5rqX63g;#a0R0bT*vJC*7Jl#j|Z zr*TgQAU+ci0kB@wJ)mx40=V%(n7IJgi8>9`bvzC3)uN6Ebv}8IQk{YUU_2>?Gsegiy&RSE;+{Xi4^g)S5cg!( z0KA6$19%zF!~*;R=>dozBzhHKt*DDa9o4>RR9CZG)Il8rcY&yrLS0ogxbs9E9O~pw zfP0asbHelajo>a8b#thzy8>=}PD%jyM}S=b|B4NIx=e#(jS3)s7CH>zaZz`KI;$vf zn?)TI>b7F1Q{B-%QTI1vI@Li{i@LBxa2rHj9qRCwfg7K-STUXIpbm<7uST58TT|-7M;C4dBL=X(>Sb&ggD{cnxVE zz)EZ%ar7f|R)F}uiAI2RqHY*<#;xFfnxSiae8Q^(-1yBd#SEF|Bkacj|AgTHUl(=B zsAFCPZv4bXI>1a(N6TVA2KRHKE){jSh2Y*P>Sj^rTMF({QAdlq-(BD?6Lr(5v)%)4 zJQIHa;094wjXLZ~aA%9UU)+N}4(@HD&Kh;u_26D5>a*gNcOF6 z`+ARjHWWs|X!A;@QTP9`;pk`!gQ)wIpw9&nI<|Bc93Y z$5TjXrFN>Rp>`uFKea#Sk=Jvi?x~EnQlyjeaiiP5n$(VD_w?_NzsAF#o^z7y#cumE zB0Xr&Beg^A>zOTX`v$^OYA+P&r1l&Sdy$8ItJ_XBZPae`@ZTh+H4u7o+&T$L8`W*B zB{->mh-wb0j)7{)a>YKD*A=!mg*a5ol!g0=xz}E%tp7)gZ76)u^nu1>+z_@ zm+B{0kup*{)c{Lt_*J4FRdUnT-|sFI^`TS;NVUdP2S_!?#UA+xub z$KYadPAI%*eCl6tm%c~h!zPciZx+u;_v^QcMLo5_gO3KWKW%X9OsV$W;4%Ig#JQ=+ zt=FUVL3O4^5{K$l3kk1jZ5r>IvpoGh%Ru#{n~00*H)+0f4UB!U*khjq-3BlB2Dd$r z$g@~3t~ z@@KsqFF}nH{a>CfK}?IEM{vU}Z|aB6Js+R`_aS2+`El$_Z@m3~|BC*5Qyn|U8+MB6 zJ9o$iz*}d2QyaofO_*8s;F^G6DXl5xQ}at3##_%#{q+@}{NDJU*Tc5I`OdI}sqb&T za^txhpD1Td`lR5wrv~O*N=ig+t}B1ObhIM(+4qAJq_PAj^gKF8wP!^4Yd?!ATjS+9 zTMFIjWg*PU4;W2;HKW-G=9R+(ZiZW3uBkP7L7AXbD3xd!g^C(C zzjpXYsh&un9Q{$GSjuRRRfGu=ucXw_hTQE4i7wo@nORx9mNf8{t2dGcT>Na2!Lxo) z1BSY~6lNI{<-sRZhRCEg6nn3_YYRLm-xfcrCu!m|U%buw#o;&7_;}ygJADD`rp;tZ zfBnT@3tx>NeeD(1mfq=jd-bJBpZ_*~WZ{P2H;tM)ld)#>G{3tq9%Si&q=!a9`0Co8 z+xz3=e=57a>QNiND0ZaK5oV?cBJTOoDwPJNe!C zv2$CTm!E_5`z{s^j;@T~q<=4(zX|RY*E`1UeLwy}O7fWaR-n(C^Yf_CzsJA!%q~Ug zSC2vXr=<`4sw)2GA6I>L=)}W7Z+lj;@8S42OWuk+Yab5y7Fss1I}-oghJ8Z>c{rqR zTO4$;I{p`n7N7oxf$}6o-B-mPi=UeI%#_0Gk3#xCFFX9&hw*ibH$`O)dT=Jw+U|4z zcgN#TotpHE2b&x-n0*Dds@WgMx9co7)!&SV{PW-UwR{r)=gmj{@W+=Pn#n}W`1E({ zt?}C1*M6{+O-6nmdLq~s|JP2_DecZNGnqz1XneIT{(|dQ*GB}&z`ew`FR3Qpx;Z)_ z;85QTrnM_Pwx=e3*6NYLbw9<8kKYp3f5dTf9>p7o@D4^%u{ITr~@exrs z3I?&zJ`&{ofek0)cPx)NddR^-`lX*9VNS&t|7FdhwOx>3;jL}onoq?S?b~u+qzm|! zFzMLWey8KNeU$Q9=qco1!G$+oIUQeg=-XH49Ue53**$dSoxz{Q2mSi5uUwuoawens z`JIe6KZ`FexO6=7d7y8__N6n2or!Pz)ubD1-*^P*_qj6Rw`byCyk55ZCkKS@esxp& z*t7A>?&04#-t&k2FOMyK{cL>RuW$5}6yFEs3D7PNY>eMAJ~?vvI^ef(=dsen#`xMH zN00t0AKK%=ajB2I*ckuUo1c_?z7puG&L913ZDagoTdZYaJCyg}xzAc$jq#;#T<;z< z725ljnvWk&Xo}~L-8Va82GFy6?TFtz*A)Ny(-X&Sy#eh%{ZQ<4A2h{pFL$ka;wK|W^~Ya)_c*kN5C8mk)%p0z&4)+4{!8f3_Zt5_ z<_`jsykV#Cxfg6L^@i55-f-W?EncuR(;MpL-Z0|rW-r*9;tiu+m%QML_q^erY;V}8 z_J*bQi(c^HbKbD=5pP)7@eePk`Lj2S&hdr`k>0Se?SdCv@h5MXx6T_nCwRjxKb`l2 zrN_J>WAcX1#olnsaBo=n^*Jxt`ZsUbyxkinF7}4iqrKriXrG?YZ1;wRzxIadYrSE0 zyf@r8#2Yg0jb5<1#v4w1-5Y9(y_lC^F-mtXilow>I10YCC zhLs!0!I_m{ks#`WQ4B#51ie@1kZW#CZox+KfJW}hRYW#fzi|yY^ir^fid@;NX+U<~ z*20Y-wd6tslCDS$-x>?%5ilE=Y!G}<_maIKXNzHFuKS^y5JDBz9k#_&UFU^(s=I8C zr#ke;1yuLjynyZvr6*Edx)4uw@SX8gm%bvA>dx~L>0XaBp6VD26RB?5kVtiqTN0_x ze_JBmQz%cQy5e1lRJZwRBGuvUO{BW?1BrAG;7}sf5mzNr-SF{5s@px0NcTGG6RGa} zbRyNMHzv|Oj%I+sU&43bah`{Kx5Hb8WJ>Y9FnlQtp9#Y^!UnpFw-y(H$Hw&o7ZCr; zzvQ(Wo+-$QSXHob^}2}l#n1f51pndu`TsONo>%vb9v9y->Bz))BiJbmoWhJd3+w-+ zeQe*~-;V$6>(AbOGyEI=e&g6@_Mdt`;r-HG*Izu)^`-Sm->KU&!v}qLqITn(JvYAn ztUh?-)$6<7nNpJx(D2(?hG)*~*ikn8(Q&^X8B;iX^vvfbjoCT+AHOrdU;nN8`!Tmq zG+49W-21j^$-dctefe1WlL4+P+si-seCx0$w|`dt!k)}&E2g`?4~dI@zkc%I8LJ=p zCgJ%nHAAC9zj^YPZ#~g?@c!q%x@Fv7VHkPzj5FeqRWnoGZ+kXod+4y}zn=TkJ3XQ1 zfU`?x3}nYk#g#pu4_dRdUUM_1bobV8Ryn^rw{*g~=cVu!GaZ`Hlh1w}Q~cM?ijvJ` zW2a8=k6Wp{G;xn+e?Z8WUAIp@)>>JgT>NF>hT@)v_g={C-g0&LvBk#2!}Dt2{c+p# ziHd7wFMs=>am0RMSK1?rq3diPe>ExL&0n|A+&;MYSHmCr>%^UDW8{bS{{G0uL)!OV zN{X!ypSS<%w!z=rv5goRxb(;FH>0#)F8ST?cVlKf|82ozn!n~Pbq>)ybNb1iKW<9d zeDn9)qx`lEzukIi&c!ohCnvwMdebW(%d_+m2M=vnS@GbleOKnr%CBkqX+}CLe`Wi+ zc1LUC*JFM^{+X~FzkKkKy(6wKKj6FT?9U&bnl^Fl#?K-`sjl*!%Jb3Z^In?yd`s>>9&fgNSaI&% z+rsnjEBa<6=REPS_JfU|ng4d0d%3eMAalu)KmUE}#FfVV_YWOh{hyk9lHx7DVLOXj zwg%7o;ib>dn;nzJ^v?S{t-kl$*e_q1^>OsT__ni&2j6`7=a#ecmbU+Rx#*HM;Xuq6 z>$g1mkKaE1-T#NZCk>CH*tRFj4V&C>Sqwas5Q9Mpp||OMNk{?-CJ>UaqhXTiBm*;< zFg=rnCyR&*vI>fV7Y*B!MKG*_AhOGHkyXHm7z9BPkVOTQ<(=x8EE7!hUH#q<-|H`x zt~#fdQ>RXyI$d3TX6230j83aVy4B9VBPBR`YsFMOvE99_z%5UVz44Rh$!|_P--5du z*Z*(Cs#cRrul#FP*_Eg7<7+Q=%yBF-J(id^Y59TgADCu7cwM~b{g=kgsoXW&{p$7$ zp-(j`JsmbFZXx@$aO{yOpH0VB2YoxP@f(+Jbsy7gn`h(JYwMQ~JLY_4D}LNE!<6=h zbfi!D@!9I=&S%EDZjZ2ye)j!YQ_}}`BbHsW9NKZ`=n2crbJ*x#lI{EQ&WMNI%ilOs z{MnC-E$ySVmgMbAN8ky2xa-xE6mRMb1QUcx^BtS!mYME zO?Uq}6xQ=;)W2C_{onf3-&D;9MZZ#EH4guCVt;&nt9^YPozcI_R|0zHd@kLD-V6KT zn)z%JDZ_+GqCXDV8poh;8sO6E;F{NIYv>=K6FC75Pxrpx#7rjt9W39b&((ooR}TZ_ z=@Z(-%mFyc_+s;*iCuzU-V_{M^TjA~aOQP+h`b&4w^`!TGqDIiPlyp%Ko6VOw*zMK zD;ooKm)P77l&tod=vOqL3zlljaXEEot~&-Yr@5R(HY*r=EiqF&J+05dN#lF2sXX<~ z-k_onOz9a38PgYO?|g9LaGxcy&D4sYO75evi5G_U(>0eqG;dVC$2qjv=^CLIT5`0Z zc3ZZt=eSA>-Oi!unq9N#ez2HAYjzdo8+-v>==@F3_{h4(6b z=}1RGHG|rFmy}-i}ueq5M{*uVMcwlA1u?TkMoBa`C(d?ge0%(deD|vKvnk#U>ZnLT{z|<4@MYf87`GeZBZ|_0<1bo);08m6;gME&jbD8xd0 z71mEDy1%YBj9=(#7$5W-<@@Lj)Bn_jhRfd>({TA?lN!d)n(WV?wRQVHt>N+)CpS!A zyIBpFFPiO-|5qJ+>O6n^&N_HkSClmU3z3!Yu;gTEC0^stR6XYT z7PMxYylaE+df8&CG35T=@3)`d@A1L?3i2%lR=Z|REXmOdk$Y$`FOB`e_}~o9T`#(E zKrP>9K9`Y};7rZsuob|#ReOl)P8qq$1cG?(EuyC6r4 zcX|p??M)u6J=uyiuLaFC{C@%vJC2|krMg>lWZE2BoQ~{c6vLhhtnq=Jkz&)`Nl*mK z1Bw!~Y)@XE=BlD(i%|y>K2IU?;XMG$Ly%Ob)nnI^3=Rx{ClLfovFPqZmkVvx=G5e= zvdJM=CbSBDKwEs8wTIoK=NoNfNrClx@SgTG}$w-j5p3%O96hp0kKi?do?#u&U4nBufpefY!@pm9Y|y4&8bpx9Pm#hRjV zuGOnfrbI-49TG8YL12nzDbgN*2b&TMcer&CeNDC9iRRYjr$NWzz*LXjZR=j@)-s&~ z;L#;M-{Qg!1Xo2foz-}Z3WVV+G&+oRGvZz*%?_;PK@aM5!T17)Y}Rg`@cJ_;iiav>{D82?kS7^vZHP==S@ zG^Y)T0CGn-7K^t#b>Em_mEJadr3PU4ke5BgW^Vy(I~EVq(o4huEH)_JV$-#1)dBWc zVtVhy6pAz!F05UkciQ&w6c{3dQw$cD&_6CMxhlv1*0rkbM+v>p$8M@Rvr~Ju*pO}t zt*_kj8JC`k*t$Al@fUYT4v%=Ubrrns?|JAjmlVvIw4$hK(;B z-_eKvuP?p#`HQ{x=iaWoV?6(j8E4#=K>lqDD;C!0vFW*c-g~U?zV3JCkMrHpFZCW= zaBe|6?CS;JEErOM{bS}EvA)3&DxRqb$FeGhS3LOm^xV;trz>w`7%`@*%Dv& zulM>(Ijs7vP0RZCAphc?y#BAt$6rG*ChA^Y92QpNztZ&JcI^XKVUfQM|D)-uiz}{y zztPSV8rI$vn$)WP^`hKk3PpX0$7=jhKJaeo-;}t3@cR4zTjc_NXI}KYjlL6%@uj1o z|K;O;JB;;RIOJbDuKrMWU#l?>$X8p$Z`4Lb80Da(uH|^Z@+D9>E=)@8zimba7tw^(n+tGhH%A$MUg}9=^J+`?|Z07d=-8XKTTXFysu->H@htTCOyg= z!c;KZ*b252m&y(1EZko1Aa{d%jPJ^`d>o&|pWx5%-|;{4`9hJvhzVk*Xcb3_^Tg%i zCh>syv)D)qlR8VB^o(Seo|UFaJEX(X8R?pITk0rxlU2F5JX)S6m&vQ;ZSq~YxzbU= z6`L|znW?;^tXHlm_mzLB9o0eVaCNjgLw!>{rhcv7QjsvGfxJD0KZOs)o%ncsD!v&% zfLG#IagvB7(uiS%gZL-0g7}CyOjHuB$;U~K>_rYDXOd-PIk}bGPliyfsZNxcilxR= zv#I6OR_Y+tmVT0!=`?x>y@*~%@1Z}Z&(owi)|_S@W*%;yY@TCYYTjtxX})N_Wp2cT zF^@6H%m5~jDQ3nqi{0eCdxOQe7*6HV zIgN903%E7hZtgSg9CwQk5Sj~Nf?1G+B%z<+5uOz$3A2Uwg!RG>;h^xHa9y}3G!?@| zT$IILV!r4UUlv~#-x4>9yTzkokkl0T93jO@J*5n3m^4P3D9x1?Nh_tD!0*%24e7r0 zu>6D^E2qkuTrAI&=gS-A{c?qTLB21yP_S6c_>v(+7$vk8M~O?J-b3OU@w#|Z43%0# z%}!~GG#hGMFYT8a%U{W@l^n&c6f0wtx0FT7GNl|wYqPRL*{2*;Neq zFTW&zC~ucPmN6w*X`%>X+XP3j)?w0c#&gLpFx!_YyLPB?)l5v9a5VhORGI7A#Lz9T}&wqzv9lksF<(oT*h zCy|TEUF1=+f~+L3lR;Etsy+1_HI)jcJJ6l!KJ@eSGI}Q+YEEDVGp{h)m_y8Y<_^=6 zZO1;&@~p!4V*9Ye*nR9_kk3mj&Gq1Va|2`Sbi$zM1f_&|c^$s6w)!35CFwQNo+Ta^Yj)h;TwUCH!5a#6IF+ zF$*-|cyX~u8JBmuNxA!x&$(rS6LyjQ*hdQVY?D7nf8yQVJK@oI54<0q1-vT4U&K%2Z3u#hrwXZ8 zK$p#@)`2!VL50xmL7!1{KFo|y>2E-ruQY#Z4u`R4*rDtgHkfP1wE>yexYxLH?mI4m zr}^RhRQ>{glW#7x6xs@p3eiHEFabvVFzBC9F2xLXhF55SV{#MSwU^14p5&_x2afqIPIdRgOn_%KcKgmzc&+1KJx-Ihgk$Osg$i` zU*j6{kMj&anV%y3Q&=Ia6Lty51xf5Fri%kb3vl-n@rHO`Y$`n}$x^;F4y56*^aaSn z9Vr}E_YlyACt)>DQeRM~tIJeuD`xn#X^FQ5X^zCr_+)$@{vO@|wEyQsGMP`#A+J!K z&GF_zW+#mAA?_%Djvo%Qrbs$2eGc4cDtC}O$s?6%3U&xHJkw<3WAF*Y>%=-@2QiXb zLcK?=r8ZETVJ5VJ^?Vp@g?V77k1#aX6C_^eItl}YEWs+|3wD^ryJ2+=6}->s`S>5Cp-2AP9mW2!bF8f?yyBf*=TjAUF^N!GRzM4g^7P;6M<(-*e#N zUH2boYTBRA^Lbjck~x`|1<52ySNiCXGGE>H3-FY-fP)<5!s84CsM;-=X9(k_kdnMFfi1S&)321n)uwBdR zdVO!;4ZV>!_9p0)$eUqqF1)3;f(JL=*4x1xd+*>K(HAH0>|MMVHrgKU;gYS`nr+yh z-ot=~G@>z0Xi5>yXpT->(u&r!p)Kv`4U_IbM|!6dD&RsNbcOfs^hr;Oskln0q) zl~x&*RXLSc1;rF8SNW*CGF%d(^=hiF8YsS|YN<9VqN{qUuLf$UMry1kYN{eNQ*)HX zQe}c%PzWgSgK`iCwV)9+gLcpj`oS<52h(5{EP@rd-39yL7@UGja1HLk7n~nANs}@e zlQRWF#z!lJre+$Z2@7>i-we&zOw9~+wL(Aa%-$S9{l#3(9aHrQ{*yLkGd5=nmaK2f zHncU&*`{sVuI<~Q9owm$*@a!%4QB1$9%0`LX35=teOE^IS8Rm)9%o6GVi{0aV8nb@ zhCgbk>LzAOm-R7;$85@GY=Q4*gUa5sBRjz?S9WJ#?8)LhiL;-<*)MS7K2CqgYrMgm zyv@5{V93XOilSTa75u*Adw%36e&JVs=U@EE<02_iA_LnO1PNc1MJQ^bA)2V0uIP)Q z7>lWx!LBQ@5j(LLM@)eWrobI!JuwNAASwfz3Lwb`MIi`kfSxwU>4Tavh?#+w6-e2E zk|PMYfQ~!Jc*;1KNP&kOSRg8yim74}yqcEjn4TG+-zFw9bF)O_Zp|C!^E+JjVQ%Kr zJWw|Y`(o2J3mY@*!p9X`wRQVyTj-sh9oP}}mdMV38gFagFk9a3*?!m?TJK?h9;Ax3 ze%dF&URauCS)MWGvJ$JXDyy?s)?yvjV*@r~6Be;K%)Ul5zoC!b*%>>}4O`EH#drc1 zN@GUlIpZ$2pbGY&IyRve@4!X_KH?J|@i|&@&A0rGA5fHMY)3c#iFq3n3GpJ*A}jKO zVOuJRil~aZc!iBSq9+DoBqk!l{3ja2mF?R+tTn_9!_)NvkF{R zhsRoQSP%Xh!Ceu&wS=>_@YMmXI>S>pIO+jECE%tsyp)HNT==L057gm+7WnUh{Smm2 z!2A-tZ^8NjoS(t?4SYYqcLHpu!F3)?yWqJ3mh0*tYwW-FKN~Ck`uTs&yZhxn-MCKb zl+Ng!E}(jR^j@fIx}lr8t-HFfhkC51dZrh8r8jz~_xkw##2A*&?^*Nwxm(p@2NQi# zTou=->T~gey&~<9<2rSx=S1j`v-5D$u8W@Txskhd&uGlJ&gv5Ca-ipU-8cRHoYwbq zSQ&iVOSM)X>QlvvFU4%p#nw_OhM2|eqU-oh2{l`BLZ|A~aD|4`a@tPE>Ec>_Y%xP; Z?QC$(9UAxG{M~Op{{xFpGU5Pa0RSxSPlNyf diff --git a/shadowsocks-csharp/Encryption/Sodium.cs b/shadowsocks-csharp/Encryption/Sodium.cs index 8d690dd2..dc564ace 100755 --- a/shadowsocks-csharp/Encryption/Sodium.cs +++ b/shadowsocks-csharp/Encryption/Sodium.cs @@ -40,6 +40,9 @@ namespace Shadowsocks.Encryption [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public extern static int crypto_stream_chacha20_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, ulong ic, byte[] k); + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] + public extern static int crypto_stream_chacha20_ietf_xor_ic(byte[] c, byte[] m, ulong mlen, byte[] n, uint ic, byte[] k); + [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public extern static void ss_sha1_hmac_ex(byte[] key, uint keylen, byte[] input, int ioff, uint ilen, diff --git a/shadowsocks-csharp/Encryption/SodiumEncryptor.cs b/shadowsocks-csharp/Encryption/SodiumEncryptor.cs index a18d2a69..b3877f86 100755 --- a/shadowsocks-csharp/Encryption/SodiumEncryptor.cs +++ b/shadowsocks-csharp/Encryption/SodiumEncryptor.cs @@ -10,6 +10,7 @@ namespace Shadowsocks.Encryption { const int CIPHER_SALSA20 = 1; const int CIPHER_CHACHA20 = 2; + const int CIPHER_CHACHA20_IETF = 3; const int SODIUM_BLOCK_SIZE = 64; @@ -29,6 +30,7 @@ namespace Shadowsocks.Encryption private static Dictionary _ciphers = new Dictionary { {"salsa20", new int[]{32, 8, CIPHER_SALSA20, PolarSSL.AES_CTX_SIZE}}, {"chacha20", new int[]{32, 8, CIPHER_CHACHA20, PolarSSL.AES_CTX_SIZE}}, + {"chacha20-ietf", new int[]{32, 12, CIPHER_CHACHA20_IETF, PolarSSL.AES_CTX_SIZE}}, }; protected override Dictionary getCiphers() @@ -75,6 +77,9 @@ namespace Shadowsocks.Encryption case CIPHER_CHACHA20: Sodium.crypto_stream_chacha20_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, ic, _key); break; + case CIPHER_CHACHA20_IETF: + Sodium.crypto_stream_chacha20_ietf_xor_ic(sodiumBuf, sodiumBuf, (ulong)(padding + length), iv, (uint)ic, _key); + break; } Buffer.BlockCopy(sodiumBuf, padding, outbuf, 0, length); padding += length; diff --git a/shadowsocks-csharp/View/ConfigForm.Designer.cs b/shadowsocks-csharp/View/ConfigForm.Designer.cs index 306a92ec..fcd11838 100755 --- a/shadowsocks-csharp/View/ConfigForm.Designer.cs +++ b/shadowsocks-csharp/View/ConfigForm.Designer.cs @@ -201,6 +201,7 @@ "rc4-md5", "salsa20", "chacha20", + "chacha20-ietf", "aes-256-cfb", "aes-192-cfb", "aes-128-cfb"}); From 2cce64670019740e8b9c1903b5a12abb377adf7b Mon Sep 17 00:00:00 2001 From: kimw Date: Mon, 4 Jan 2016 23:21:53 +0800 Subject: [PATCH 35/54] featured show bandwidth in log form's title --- .../Controller/Service/Listener.cs | 10 +- .../Controller/Service/PortForwarder.cs | 2 - .../Controller/Service/TCPRelay.cs | 174 +++++++++--------- .../Controller/Service/UDPRelay.cs | 22 ++- .../Controller/ShadowsocksController.cs | 27 ++- shadowsocks-csharp/Util/Util.cs | 29 ++- shadowsocks-csharp/View/LogForm.Designer.cs | 28 +-- shadowsocks-csharp/View/LogForm.cs | 9 +- shadowsocks-csharp/View/LogForm.resx | 24 +++ 9 files changed, 194 insertions(+), 131 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/Listener.cs b/shadowsocks-csharp/Controller/Service/Listener.cs index 06994787..e9744988 100644 --- a/shadowsocks-csharp/Controller/Service/Listener.cs +++ b/shadowsocks-csharp/Controller/Service/Listener.cs @@ -1,10 +1,10 @@ -using Shadowsocks.Model; -using System; +using System; using System.Collections.Generic; using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; -using System.Text; + +using Shadowsocks.Model; namespace Shadowsocks.Controller { @@ -79,9 +79,7 @@ namespace Shadowsocks.Controller // Start an asynchronous socket to listen for connections. Logging.Info("Shadowsocks started"); - _tcpSocket.BeginAccept( - new AsyncCallback(AcceptCallback), - _tcpSocket); + _tcpSocket.BeginAccept(new AsyncCallback(AcceptCallback), _tcpSocket); UDPState udpState = new UDPState(); _udpSocket.BeginReceiveFrom(udpState.buffer, 0, udpState.buffer.Length, 0, ref udpState.remoteEndPoint, new AsyncCallback(RecvFromCallback), udpState); } diff --git a/shadowsocks-csharp/Controller/Service/PortForwarder.cs b/shadowsocks-csharp/Controller/Service/PortForwarder.cs index e577b535..4f78a24c 100644 --- a/shadowsocks-csharp/Controller/Service/PortForwarder.cs +++ b/shadowsocks-csharp/Controller/Service/PortForwarder.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.Net; using System.Net.Sockets; -using System.Text; namespace Shadowsocks.Controller { diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index 061345dc..4944c44c 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -1,31 +1,30 @@ using System; using System.Collections.Generic; -using System.Text; -using System.Net.Sockets; using System.Net; +using System.Net.Sockets; +using System.Timers; + +using Shadowsocks.Controller.Strategy; using Shadowsocks.Encryption; using Shadowsocks.Model; -using Shadowsocks.Controller.Strategy; -using System.Timers; namespace Shadowsocks.Controller { - class TCPRelay : Listener.Service { private ShadowsocksController _controller; private DateTime _lastSweepTime; - public ISet Handlers + public ISet Handlers { get; set; } public TCPRelay(ShadowsocksController controller) { - this._controller = controller; - this.Handlers = new HashSet(); - this._lastSweepTime = DateTime.Now; + _controller = controller; + Handlers = new HashSet(); + _lastSweepTime = DateTime.Now; } public bool Handle(byte[] firstPacket, int length, Socket socket, object state) @@ -39,22 +38,21 @@ namespace Shadowsocks.Controller return false; } socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); - Handler handler = new Handler(); + TCPHandler handler = new TCPHandler(this); handler.connection = socket; handler.controller = _controller; handler.relay = this; handler.Start(firstPacket, length); - IList handlersToClose = new List(); - lock (this.Handlers) + IList handlersToClose = new List(); + lock (Handlers) { - this.Handlers.Add(handler); - Logging.Debug($"TCP connections: {Handlers.Count}"); + Handlers.Add(handler); DateTime now = DateTime.Now; if (now - _lastSweepTime > TimeSpan.FromSeconds(1)) { _lastSweepTime = now; - foreach (Handler handler1 in this.Handlers) + foreach (TCPHandler handler1 in Handlers) { if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) { @@ -63,18 +61,28 @@ namespace Shadowsocks.Controller } } } - foreach (Handler handler1 in handlersToClose) + foreach (TCPHandler handler1 in handlersToClose) { Logging.Debug("Closing timed out TCP connection."); handler1.Close(); } return true; } + + public void UpdateInboundCounter(long n) + { + _controller.UpdateInboundCounter(n); + } + + public void UpdateOutboundCounter(long n) + { + _controller.UpdateOutboundCounter(n); + } } - class Handler + class TCPHandler { - //public Encryptor encryptor; + // public Encryptor encryptor; public IEncryptor encryptor; public Server server; // Client socket. @@ -85,6 +93,7 @@ namespace Shadowsocks.Controller public DateTime lastActivity; + private const int maxRetry = 4; private int retryCount = 0; private bool connected; @@ -117,6 +126,12 @@ namespace Shadowsocks.Controller private object decryptionLock = new object(); private DateTime _startConnectTime; + private TCPRelay tcprelay; // TODO: tcprelay ?= relay + + public TCPHandler(TCPRelay tcprelay) + { + this.tcprelay = tcprelay; + } public void CreateRemote() { @@ -125,23 +140,23 @@ namespace Shadowsocks.Controller { throw new ArgumentException("No server configured"); } - this.encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false); + encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false); this.server = server; } public void Start(byte[] firstPacket, int length) { - this._firstPacket = firstPacket; - this._firstPacketLength = length; - this.HandshakeReceive(); - this.lastActivity = DateTime.Now; + _firstPacket = firstPacket; + _firstPacketLength = length; + HandshakeReceive(); + lastActivity = DateTime.Now; } private void CheckClose() { if (connectionShutdown && remoteShutdown) { - this.Close(); + Close(); } } @@ -149,7 +164,6 @@ namespace Shadowsocks.Controller { lock (relay.Handlers) { - Logging.Debug($"TCP connections: {relay.Handlers.Count}"); relay.Handlers.Remove(this); } lock (this) @@ -215,18 +229,17 @@ namespace Shadowsocks.Controller response = new byte[] { 0, 91 }; Logging.Error("socks 5 protocol error"); } - Logging.Debug($"======Send Local Port, size:" + response.Length); connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null); } else { - this.Close(); + Close(); } } catch (Exception e) { Logging.LogUsefulException(e); - this.Close(); + Close(); } } @@ -240,21 +253,19 @@ namespace Shadowsocks.Controller { connection.EndSend(ar); - // +----+-----+-------+------+----------+----------+ - // |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | - // +----+-----+-------+------+----------+----------+ - // | 1 | 1 | X'00' | 1 | Variable | 2 | - // +----+-----+-------+------+----------+----------+ + // +-----+-----+-------+------+----------+----------+ + // | VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | + // +-----+-----+-------+------+----------+----------+ + // | 1 | 1 | X'00' | 1 | Variable | 2 | + // +-----+-----+-------+------+----------+----------+ // Skip first 3 bytes // TODO validate - Logging.Debug($"======Receive Local Port, size:" + 3); - connection.BeginReceive(connetionRecvBuffer, 0, 3, 0, - new AsyncCallback(handshakeReceive2Callback), null); + connection.BeginReceive(connetionRecvBuffer, 0, 3, 0, new AsyncCallback(handshakeReceive2Callback), null); } catch (Exception e) { Logging.LogUsefulException(e); - this.Close(); + Close(); } } @@ -274,7 +285,6 @@ namespace Shadowsocks.Controller if (command == 1) { byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; - Logging.Debug($"======Send Local Port, size:" + response.Length); connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ResponseCallback), null); } else if (command == 3) @@ -284,14 +294,14 @@ namespace Shadowsocks.Controller } else { - Logging.Error("failed to recv data in handshakeReceive2Callback"); - this.Close(); + Logging.Debug("failed to recv data in Shadowsocks.Controller.TCPHandler.handshakeReceive2Callback()"); + Close(); } } catch (Exception e) { Logging.LogUsefulException(e); - this.Close(); + Close(); } } @@ -313,7 +323,6 @@ namespace Shadowsocks.Controller address.CopyTo(response, 4); response[response.Length - 1] = (byte)(port & 0xFF); response[response.Length - 2] = (byte)((port >> 8) & 0xFF); - Logging.Debug($"======Send Local Port, size:" + response.Length); connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ReadAll), true); } @@ -328,29 +337,27 @@ namespace Shadowsocks.Controller if (ar.AsyncState != null) { connection.EndSend(ar); - Logging.Debug($"======Receive Local Port, size:" + RecvSize); - connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, - new AsyncCallback(ReadAll), null); + Logging.Debug(remote, RecvSize, "TCP Relay"); + connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null); } else { int bytesRead = connection.EndReceive(ar); if (bytesRead > 0) { - Logging.Debug($"======Receive Local Port, size:" + RecvSize); - connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, - new AsyncCallback(ReadAll), null); + Logging.Debug(remote, RecvSize, "TCP Relay"); + connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null); } else { - this.Close(); + Close(); } } } catch (Exception e) { Logging.LogUsefulException(e); - this.Close(); + Close(); } } @@ -366,15 +373,16 @@ namespace Shadowsocks.Controller catch (Exception e) { Logging.LogUsefulException(e); - this.Close(); + Close(); } } + // inner class private class ServerTimer : Timer { public Server Server; - public ServerTimer(int p) :base(p) + public ServerTimer(int p) : base(p) { } } @@ -408,14 +416,12 @@ namespace Shadowsocks.Controller connected = false; // Connect to the remote endpoint. - Logging.Debug($"++++++Connect Server Port"); - remote.BeginConnect(remoteEP, - new AsyncCallback(ConnectCallback), connectTimer); + remote.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), connectTimer); } catch (Exception e) { Logging.LogUsefulException(e); - this.Close(); + Close(); } } @@ -438,15 +444,15 @@ namespace Shadowsocks.Controller private void RetryConnect() { - if (retryCount < 4) + if (retryCount < maxRetry) { - Logging.Debug("Connection failed, retrying"); + Logging.Debug($"Connection failed, retry ({retryCount})"); StartConnect(); retryCount++; } else { - this.Close(); + Close(); } } @@ -507,17 +513,13 @@ namespace Shadowsocks.Controller } try { - Logging.Debug($"++++++Receive Server Port, size:" + RecvSize); - remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, - new AsyncCallback(PipeRemoteReceiveCallback), null); - Logging.Debug($"======Receive Local Port, size:"+ RecvSize); - connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, - new AsyncCallback(PipeConnectionReceiveCallback), null); + remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null); + connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null); } catch (Exception e) { Logging.LogUsefulException(e); - this.Close(); + Close(); } } @@ -531,10 +533,11 @@ namespace Shadowsocks.Controller { int bytesRead = remote.EndReceive(ar); totalRead += bytesRead; + tcprelay.UpdateInboundCounter(bytesRead); if (bytesRead > 0) { - this.lastActivity = DateTime.Now; + lastActivity = DateTime.Now; int bytesToSend; lock (decryptionLock) { @@ -544,13 +547,13 @@ namespace Shadowsocks.Controller } encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend); } - Logging.Debug($"======Send Local Port, size:" + bytesToSend); + Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeRemoteReceiveCallback() (download)"); connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null); IStrategy strategy = controller.GetCurrentStrategy(); if (strategy != null) { - strategy.UpdateLastRead(this.server); + strategy.UpdateLastRead(server); } } else @@ -559,18 +562,18 @@ namespace Shadowsocks.Controller connectionShutdown = true; CheckClose(); - if (totalRead == 0) - { - // closed before anything received, reports as failure - // disable this feature - // controller.GetCurrentStrategy().SetFailure(this.server); - } + //if (totalRead == 0) + //{ + // // closed before anything received, reports as failure + // // disable this feature + // controller.GetCurrentStrategy().SetFailure(this.server); + //} } } catch (Exception e) { Logging.LogUsefulException(e); - this.Close(); + Close(); } } @@ -584,6 +587,7 @@ namespace Shadowsocks.Controller { int bytesRead = connection.EndReceive(ar); totalWrite += bytesRead; + tcprelay.UpdateOutboundCounter(bytesRead); if (bytesRead > 0) { @@ -596,13 +600,13 @@ namespace Shadowsocks.Controller } encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); } - Logging.Debug($"++++++Send Server Port, size:" + bytesToSend); + Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeConnectionReceiveCallback() (upload)"); remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); IStrategy strategy = controller.GetCurrentStrategy(); if (strategy != null) { - strategy.UpdateLastWrite(this.server); + strategy.UpdateLastWrite(server); } } else @@ -615,7 +619,7 @@ namespace Shadowsocks.Controller catch (Exception e) { Logging.LogUsefulException(e); - this.Close(); + Close(); } } @@ -628,14 +632,12 @@ namespace Shadowsocks.Controller try { remote.EndSend(ar); - Logging.Debug($"======Receive Local Port, size:" + RecvSize); - connection.BeginReceive(this.connetionRecvBuffer, 0, RecvSize, 0, - new AsyncCallback(PipeConnectionReceiveCallback), null); + connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null); } catch (Exception e) { Logging.LogUsefulException(e); - this.Close(); + Close(); } } @@ -648,14 +650,12 @@ namespace Shadowsocks.Controller try { connection.EndSend(ar); - Logging.Debug($"++++++Receive Server Port, size:" + RecvSize); - remote.BeginReceive(this.remoteRecvBuffer, 0, RecvSize, 0, - new AsyncCallback(PipeRemoteReceiveCallback), null); + remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null); } catch (Exception e) { Logging.LogUsefulException(e); - this.Close(); + Close(); } } } diff --git a/shadowsocks-csharp/Controller/Service/UDPRelay.cs b/shadowsocks-csharp/Controller/Service/UDPRelay.cs index 5f8d2433..e1325a3d 100644 --- a/shadowsocks-csharp/Controller/Service/UDPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/UDPRelay.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; -using System.Text; -using Shadowsocks.Encryption; -using Shadowsocks.Model; -using System.Net.Sockets; using System.Net; +using System.Net.Sockets; using System.Runtime.CompilerServices; + using Shadowsocks.Controller.Strategy; +using Shadowsocks.Encryption; +using Shadowsocks.Model; namespace Shadowsocks.Controller { @@ -14,6 +14,10 @@ namespace Shadowsocks.Controller { private ShadowsocksController _controller; private LRUCache _cache; + + public long outbound = 0; + public long inbound = 0; + public UDPRelay(ShadowsocksController controller) { this._controller = controller; @@ -70,8 +74,8 @@ namespace Shadowsocks.Controller } _remoteEndPoint = new IPEndPoint(ipAddress, server.server_port); _remote = new Socket(_remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); - } + public void Send(byte[] data, int length) { IEncryptor encryptor = EncryptorFactory.GetEncryptor(_server.method, _server.password, _server.auth, true); @@ -80,15 +84,17 @@ namespace Shadowsocks.Controller byte[] dataOut = new byte[length - 3 + 16 + IVEncryptor.ONETIMEAUTH_BYTES]; int outlen; encryptor.Encrypt(dataIn, length - 3, dataOut, out outlen); - Logging.Debug($"++++++Send Server Port, size:" + outlen); + Logging.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay"); _remote.SendTo(dataOut, outlen, SocketFlags.None, _remoteEndPoint); } + public void Receive() { EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); Logging.Debug($"++++++Receive Server Port, size:" + _buffer.Length); _remote.BeginReceiveFrom(_buffer, 0, _buffer.Length, 0, ref remoteEndPoint, new AsyncCallback(RecvFromCallback), null); } + public void RecvFromCallback(IAsyncResult ar) { try @@ -105,7 +111,7 @@ namespace Shadowsocks.Controller byte[] sendBuf = new byte[outlen + 3]; Array.Copy(dataOut, 0, sendBuf, 3, outlen); - Logging.Debug($"======Send Local Port, size:" + (outlen + 3)); + Logging.Debug(_localEndPoint, _remoteEndPoint, outlen, "UDP Relay"); _local.SendTo(sendBuf, outlen + 3, 0, _localEndPoint); Receive(); } @@ -121,6 +127,7 @@ namespace Shadowsocks.Controller { } } + public void Close() { try @@ -142,7 +149,6 @@ namespace Shadowsocks.Controller } } - // cc by-sa 3.0 http://stackoverflow.com/a/3719378/1124054 class LRUCache where V : UDPRelay.UDPHandler { diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index 9e1693c4..e826f8e7 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -1,14 +1,15 @@ -using System.IO; -using Shadowsocks.Model; -using System; +using System; using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Sockets; using System.Text; using System.Threading; -using System.Net.Sockets; + using Shadowsocks.Controller.Strategy; -using System.Net; -using Shadowsocks.Util; +using Shadowsocks.Model; using Shadowsocks.Properties; +using Shadowsocks.Util; namespace Shadowsocks.Controller { @@ -30,6 +31,9 @@ namespace Shadowsocks.Controller public AvailabilityStatistics availabilityStatistics { get; private set; } public StatisticsStrategyConfiguration StatisticsConfiguration { get; private set; } + public long inboundCounter = 0; + public long outboundCounter = 0; + private bool stopped = false; private bool _systemProxyIsDirty = false; @@ -62,7 +66,6 @@ namespace Shadowsocks.Controller StartReleasingMemory(); } - public void Start() { Reload(); @@ -302,6 +305,16 @@ namespace Shadowsocks.Controller Configuration.Save(_config); } + public void UpdateInboundCounter(long n) + { + inboundCounter += n; + } + + public void UpdateOutboundCounter(long n) + { + outboundCounter += n; + } + protected void Reload() { // some logic in configuration updated the config when saving, we need to read it again diff --git a/shadowsocks-csharp/Util/Util.cs b/shadowsocks-csharp/Util/Util.cs index eaa79501..450faa56 100755 --- a/shadowsocks-csharp/Util/Util.cs +++ b/shadowsocks-csharp/Util/Util.cs @@ -1,10 +1,8 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Runtime.InteropServices; -using System.Text; using System.Windows.Forms; using Shadowsocks.Controller; @@ -85,6 +83,33 @@ namespace Shadowsocks.Util } } + public static string FormatBandwide(long n) + { + float f = n; + string unit = "B"; + if (f > 1024) + { + f = f / 1024; + unit = "KiB"; + } + if (f > 1024) + { + f = f / 1024; + unit = "MiB"; + } + if (f > 1024) + { + f = f / 1024; + unit = "GiB"; + } + if (f > 1024) + { + f = f / 1024; + unit = "TiB"; + } + return $"{f:.##}{unit}"; + } + [DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool SetProcessWorkingSetSize(IntPtr process, diff --git a/shadowsocks-csharp/View/LogForm.Designer.cs b/shadowsocks-csharp/View/LogForm.Designer.cs index 8f5e35c7..3ac114ba 100644 --- a/shadowsocks-csharp/View/LogForm.Designer.cs +++ b/shadowsocks-csharp/View/LogForm.Designer.cs @@ -57,13 +57,13 @@ this.LogMessageTextBox.Dock = System.Windows.Forms.DockStyle.Fill; this.LogMessageTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); this.LogMessageTextBox.ForeColor = System.Drawing.Color.White; - this.LogMessageTextBox.Location = new System.Drawing.Point(3, 38); + this.LogMessageTextBox.Location = new System.Drawing.Point(3, 40); this.LogMessageTextBox.MaxLength = 2147483647; this.LogMessageTextBox.Multiline = true; this.LogMessageTextBox.Name = "LogMessageTextBox"; this.LogMessageTextBox.ReadOnly = true; this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; - this.LogMessageTextBox.Size = new System.Drawing.Size(378, 99); + this.LogMessageTextBox.Size = new System.Drawing.Size(378, 131); this.LogMessageTextBox.TabIndex = 0; // // MainMenu @@ -141,12 +141,12 @@ // // TopMostCheckBox // - this.TopMostCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + this.TopMostCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); this.TopMostCheckBox.AutoSize = true; - this.TopMostCheckBox.Location = new System.Drawing.Point(249, 3); + this.TopMostCheckBox.Location = new System.Drawing.Point(247, 3); this.TopMostCheckBox.Name = "TopMostCheckBox"; - this.TopMostCheckBox.Size = new System.Drawing.Size(72, 23); + this.TopMostCheckBox.Size = new System.Drawing.Size(71, 25); this.TopMostCheckBox.TabIndex = 3; this.TopMostCheckBox.Text = "&Top Most"; this.TopMostCheckBox.UseVisualStyleBackColor = true; @@ -157,7 +157,7 @@ this.ChangeFontButton.AutoSize = true; this.ChangeFontButton.Location = new System.Drawing.Point(84, 3); this.ChangeFontButton.Name = "ChangeFontButton"; - this.ChangeFontButton.Size = new System.Drawing.Size(75, 23); + this.ChangeFontButton.Size = new System.Drawing.Size(75, 25); this.ChangeFontButton.TabIndex = 2; this.ChangeFontButton.Text = "&Font"; this.ChangeFontButton.UseVisualStyleBackColor = true; @@ -168,7 +168,7 @@ this.CleanLogsButton.AutoSize = true; this.CleanLogsButton.Location = new System.Drawing.Point(3, 3); this.CleanLogsButton.Name = "CleanLogsButton"; - this.CleanLogsButton.Size = new System.Drawing.Size(75, 23); + this.CleanLogsButton.Size = new System.Drawing.Size(75, 25); this.CleanLogsButton.TabIndex = 1; this.CleanLogsButton.Text = "&Clean Logs"; this.CleanLogsButton.UseVisualStyleBackColor = true; @@ -176,12 +176,12 @@ // // WrapTextCheckBox // - this.WrapTextCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + this.WrapTextCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); this.WrapTextCheckBox.AutoSize = true; this.WrapTextCheckBox.Location = new System.Drawing.Point(165, 3); this.WrapTextCheckBox.Name = "WrapTextCheckBox"; - this.WrapTextCheckBox.Size = new System.Drawing.Size(78, 23); + this.WrapTextCheckBox.Size = new System.Drawing.Size(76, 25); this.WrapTextCheckBox.TabIndex = 0; this.WrapTextCheckBox.Text = "&Wrap Text"; this.WrapTextCheckBox.UseVisualStyleBackColor = true; @@ -199,7 +199,7 @@ this.tableLayoutPanel1.RowCount = 2; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); - this.tableLayoutPanel1.Size = new System.Drawing.Size(384, 140); + this.tableLayoutPanel1.Size = new System.Drawing.Size(384, 174); this.tableLayoutPanel1.TabIndex = 2; // // ToolbarFlowLayoutPanel @@ -212,17 +212,17 @@ this.ToolbarFlowLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; this.ToolbarFlowLayoutPanel.Location = new System.Drawing.Point(3, 3); this.ToolbarFlowLayoutPanel.Name = "ToolbarFlowLayoutPanel"; - this.ToolbarFlowLayoutPanel.Size = new System.Drawing.Size(378, 29); + this.ToolbarFlowLayoutPanel.Size = new System.Drawing.Size(378, 31); this.ToolbarFlowLayoutPanel.TabIndex = 2; // // LogForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(384, 140); + this.ClientSize = new System.Drawing.Size(384, 174); this.Controls.Add(this.tableLayoutPanel1); this.Menu = this.MainMenu; - this.MinimumSize = new System.Drawing.Size(400, 200); + this.MinimumSize = new System.Drawing.Size(400, 213); this.Name = "LogForm"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Log Viewer"; diff --git a/shadowsocks-csharp/View/LogForm.cs b/shadowsocks-csharp/View/LogForm.cs index a8fe2e4c..cf3f1732 100644 --- a/shadowsocks-csharp/View/LogForm.cs +++ b/shadowsocks-csharp/View/LogForm.cs @@ -1,16 +1,12 @@ using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; using System.Drawing; using System.IO; -using System.Linq; -using System.Text; using System.Windows.Forms; using Shadowsocks.Controller; using Shadowsocks.Properties; using Shadowsocks.Model; +using Shadowsocks.Util; namespace Shadowsocks.View { @@ -112,11 +108,14 @@ namespace Shadowsocks.View lastOffset = reader.BaseStream.Position; } + + this.Text = $"Log Viewer [in: {Utils.FormatBandwide(controller.inboundCounter)}, out: {Utils.FormatBandwide(controller.outboundCounter)}]"; } private void LogForm_Load(object sender, EventArgs e) { InitContent(); + timer = new Timer(); timer.Interval = 300; timer.Tick += Timer_Tick; diff --git a/shadowsocks-csharp/View/LogForm.resx b/shadowsocks-csharp/View/LogForm.resx index e8bf04bf..c921ecfb 100644 --- a/shadowsocks-csharp/View/LogForm.resx +++ b/shadowsocks-csharp/View/LogForm.resx @@ -117,7 +117,31 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + True + 17, 17 + + True + + + True + + + True + + + True + + + True + + + True + + + True + \ No newline at end of file From 64390d45ac6eb10d5f1b6928a9dde61cbe146cda Mon Sep 17 00:00:00 2001 From: kimw Date: Mon, 4 Jan 2016 23:56:43 +0800 Subject: [PATCH 36/54] GetTempPath() now supports: return a full path with filename combined which pointed to the temporary directory --- shadowsocks-csharp/Controller/Logging.cs | 32 ++++++++--- .../Service/AvailabilityStatistics.cs | 56 +++++++++---------- .../Controller/Service/GfwListUpdater.cs | 12 ++-- .../Controller/Service/PACServer.cs | 4 +- .../Controller/Service/PolipoRunner.cs | 30 ++++------ .../Controller/Service/UpdateChecker.cs | 11 +--- .../Controller/ShadowsocksController.cs | 4 +- shadowsocks-csharp/Encryption/MbedTLS.cs | 17 +++--- shadowsocks-csharp/Encryption/PolarSSL.cs | 15 ++--- shadowsocks-csharp/Encryption/Sodium.cs | 15 ++--- shadowsocks-csharp/Program.cs | 24 ++++---- shadowsocks-csharp/Util/Util.cs | 39 +++++++++---- 12 files changed, 137 insertions(+), 122 deletions(-) diff --git a/shadowsocks-csharp/Controller/Logging.cs b/shadowsocks-csharp/Controller/Logging.cs index fb07dcae..3db34658 100755 --- a/shadowsocks-csharp/Controller/Logging.cs +++ b/shadowsocks-csharp/Controller/Logging.cs @@ -1,9 +1,9 @@ -using Shadowsocks.Util; -using System; -using System.Collections.Generic; +using System; using System.IO; using System.Net.Sockets; -using System.Text; +using System.Net; + +using Shadowsocks.Util; namespace Shadowsocks.Controller { @@ -15,8 +15,7 @@ namespace Shadowsocks.Controller { try { - string temppath = Utils.GetTempPath(); - LogFile = Path.Combine(temppath, "shadowsocks.log"); + LogFile = Utils.GetTempPath("shadowsocks.log"); FileStream fs = new FileStream(LogFile, FileMode.Append); StreamWriterWithTimestamp sw = new StreamWriterWithTimestamp(fs); sw.AutoFlush = true; @@ -34,7 +33,7 @@ namespace Shadowsocks.Controller public static void Error(object o) { - Console.WriteLine("[E] "+ o); + Console.WriteLine("[E] " + o); } public static void Info(object o) @@ -49,6 +48,25 @@ namespace Shadowsocks.Controller #endif } +#if DEBUG + public static void Debug(EndPoint local, EndPoint remote, int len, string header = null, string tailer = null) + { + if (header == null && tailer == null) + Debug($"{local} => {remote} (size={len})"); + else if (header == null && tailer != null) + Debug($"{local} => {remote} (size={len}), {tailer}"); + else if (header != null && tailer == null) + Debug($"{header}: {local} => {remote} (size={len})"); + else + Debug($"{header}: {local} => {remote} (size={len}), {tailer}"); + } + + public static void Debug(Socket sock, int len, string header = null, string tailer = null) + { + Debug(sock.LocalEndPoint, sock.RemoteEndPoint, len, header, tailer); + } +#endif + public static void LogUsefulException(Exception e) { // just log useful exceptions, not all of them diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index 4d3bc119..bbb7a9c2 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -9,6 +9,7 @@ using System.Net.NetworkInformation; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; + using Shadowsocks.Model; using Shadowsocks.Util; @@ -30,8 +31,8 @@ namespace Shadowsocks.Controller public Statistics FilteredStatistics { get; private set; } public static readonly DateTime UnknownDateTime = new DateTime(1970, 1, 1); private int Repeat => _config.RepeatTimesNum; - private const int RetryInterval = 2*60*1000; //retry 2 minutes after failed - private int Interval => (int) TimeSpan.FromMinutes(_config.DataCollectionMinutes).TotalMilliseconds; + private const int RetryInterval = 2 * 60 * 1000; //retry 2 minutes after failed + private int Interval => (int)TimeSpan.FromMinutes(_config.DataCollectionMinutes).TotalMilliseconds; private Timer _timer; private State _state; private List _servers; @@ -42,8 +43,7 @@ namespace Shadowsocks.Controller //static constructor to initialize every public static fields before refereced static AvailabilityStatistics() { - var temppath = Utils.GetTempPath(); - AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName); + AvailabilityStatisticsFile = Utils.GetTempPath(StatisticsFilesName); } public AvailabilityStatistics(Configuration config, StatisticsStrategyConfiguration statisticsConfig) @@ -181,11 +181,11 @@ namespace Shadowsocks.Controller if (!File.Exists(AvailabilityStatisticsFile)) { var headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray()); - lines = new[] {headerLine, dataLine}; + lines = new[] { headerLine, dataLine }; } else { - lines = new[] {dataLine}; + lines = new[] { dataLine }; } try { @@ -248,30 +248,29 @@ namespace Shadowsocks.Controller Logging.Debug($"loading statistics from {path}"); if (!File.Exists(path)) { - Console.WriteLine($"statistics file does not exist, try to reload {RetryInterval/60/1000} minutes later"); + Console.WriteLine($"statistics file does not exist, try to reload {RetryInterval / 60 / 1000} minutes later"); _timer.Change(RetryInterval, Interval); return; } - RawStatistics = (from l in File.ReadAllLines(path) - .Skip(1) - let strings = l.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries) - let rawData = new RawStatisticsData - { - Timestamp = ParseExactOrUnknown(strings[0]), - ServerName = strings[1], - ICMPStatus = strings[2], - RoundtripTime = int.Parse(strings[3]), - Geolocation = 5 > strings.Length ? - null - : strings[4], - ISP = 6 > strings.Length ? null : strings[5] - } - group rawData by rawData.ServerName into server - select new - { - ServerName = server.Key, - data = server.ToList() - }).ToDictionary(server => server.ServerName, server=> server.data); + RawStatistics = (from l in File.ReadAllLines(path).Skip(1) + let strings = l.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) + let rawData = new RawStatisticsData + { + Timestamp = ParseExactOrUnknown(strings[0]), + ServerName = strings[1], + ICMPStatus = strings[2], + RoundtripTime = int.Parse(strings[3]), + Geolocation = 5 > strings.Length ? + null + : strings[4], + ISP = 6 > strings.Length ? null : strings[5] + } + group rawData by rawData.ServerName into server + select new + { + ServerName = server.Key, + data = server.ToList() + }).ToDictionary(server => server.ServerName, server => server.data); } catch (Exception e) { @@ -300,7 +299,7 @@ namespace Shadowsocks.Controller public string ICMPStatus; public int RoundtripTime; public string Geolocation; - public string ISP ; + public string ISP; } public class StatisticsData @@ -310,6 +309,5 @@ namespace Shadowsocks.Controller public int MinResponse; public int MaxResponse; } - } } diff --git a/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs b/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs index 96d86ba0..e0fd12f4 100644 --- a/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs +++ b/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; -using System.Text; -using System.Net; using System.IO; +using System.Net; +using System.Text; + +using Shadowsocks.Model; using Shadowsocks.Properties; -using SimpleJson; using Shadowsocks.Util; -using Shadowsocks.Model; namespace Shadowsocks.Controller { @@ -38,13 +38,13 @@ namespace Shadowsocks.Controller { try { - File.WriteAllText(Utils.GetTempPath() + "\\gfwlist.txt", e.Result, Encoding.UTF8); + File.WriteAllText(Utils.GetTempPath("gfwlist.txt"), e.Result, Encoding.UTF8); List lines = ParseResult(e.Result); if (File.Exists(USER_RULE_FILE)) { string local = File.ReadAllText(USER_RULE_FILE, Encoding.UTF8); string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); - foreach(string rule in rules) + foreach (string rule in rules) { if (rule.StartsWith("!") || rule.StartsWith("[")) continue; diff --git a/shadowsocks-csharp/Controller/Service/PACServer.cs b/shadowsocks-csharp/Controller/Service/PACServer.cs index c428fe0c..ac8b762a 100644 --- a/shadowsocks-csharp/Controller/Service/PACServer.cs +++ b/shadowsocks-csharp/Controller/Service/PACServer.cs @@ -146,8 +146,8 @@ Content-Type: application/x-ns-proxy-autoconfig Content-Length: {0} Connection: Close -", System.Text.Encoding.UTF8.GetBytes(pac).Length) + pac; - byte[] response = System.Text.Encoding.UTF8.GetBytes(text); +", Encoding.UTF8.GetBytes(pac).Length) + pac; + byte[] response = Encoding.UTF8.GetBytes(text); socket.BeginSend(response, 0, response.Length, 0, new AsyncCallback(SendCallback), socket); Util.Utils.ReleaseMemory(true); } diff --git a/shadowsocks-csharp/Controller/Service/PolipoRunner.cs b/shadowsocks-csharp/Controller/Service/PolipoRunner.cs index ad582d1e..f824c2e5 100644 --- a/shadowsocks-csharp/Controller/Service/PolipoRunner.cs +++ b/shadowsocks-csharp/Controller/Service/PolipoRunner.cs @@ -1,14 +1,14 @@ -using Shadowsocks.Model; -using Shadowsocks.Properties; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.IO.Compression; -using System.Text; -using System.Net.NetworkInformation; using System.Net; +using System.Net.NetworkInformation; using System.Runtime.InteropServices; +using System.Text; + +using Shadowsocks.Model; +using Shadowsocks.Properties; using Shadowsocks.Util; namespace Shadowsocks.Controller @@ -16,16 +16,14 @@ namespace Shadowsocks.Controller class PolipoRunner { private Process _process; - private static string temppath; private int _runningPort; static PolipoRunner() { - temppath = Utils.GetTempPath(); try { - FileManager.UncompressFile(temppath + "/ss_privoxy.exe", Resources.privoxy_exe); - FileManager.UncompressFile(temppath + "/mgwz.dll", Resources.mgwz_dll); + FileManager.UncompressFile(Utils.GetTempPath("ss_privoxy.exe"), Resources.privoxy_exe); + FileManager.UncompressFile(Utils.GetTempPath("mgwz.dll"), Resources.mgwz_dll); } catch (IOException e) { @@ -64,20 +62,16 @@ namespace Shadowsocks.Controller polipoConfig = polipoConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString()); polipoConfig = polipoConfig.Replace("__POLIPO_BIND_PORT__", _runningPort.ToString()); polipoConfig = polipoConfig.Replace("__POLIPO_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1"); - FileManager.ByteArrayToFile(temppath + "/privoxy.conf", System.Text.Encoding.UTF8.GetBytes(polipoConfig)); + FileManager.ByteArrayToFile(Utils.GetTempPath("privoxy.conf"), Encoding.UTF8.GetBytes(polipoConfig)); - if (!(temppath.EndsWith("\\") || temppath.EndsWith("/"))) { - temppath = temppath + "\\"; - } _process = new Process(); // Configure the process using the StartInfo properties. - _process.StartInfo.FileName = temppath + "ss_privoxy.exe"; - _process.StartInfo.Arguments = " \"" + temppath + "privoxy.conf\""; + _process.StartInfo.FileName = "ss_privoxy.exe"; + _process.StartInfo.Arguments = "privoxy.conf"; + _process.StartInfo.WorkingDirectory = Utils.GetTempPath(); _process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; _process.StartInfo.UseShellExecute = true; _process.StartInfo.CreateNoWindow = true; - //_process.StartInfo.RedirectStandardOutput = true; - //_process.StartInfo.RedirectStandardError = true; _process.Start(); } RefreshTrayArea(); diff --git a/shadowsocks-csharp/Controller/Service/UpdateChecker.cs b/shadowsocks-csharp/Controller/Service/UpdateChecker.cs index 45962353..59ff3cf6 100644 --- a/shadowsocks-csharp/Controller/Service/UpdateChecker.cs +++ b/shadowsocks-csharp/Controller/Service/UpdateChecker.cs @@ -1,11 +1,8 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Net; -using System.Reflection; -using System.Text; using System.Text.RegularExpressions; -using System.IO; + using SimpleJson; using Shadowsocks.Model; @@ -129,8 +126,7 @@ namespace Shadowsocks.Controller { try { - string temppath = Utils.GetTempPath(); - LatestVersionLocalName = Path.Combine(temppath, LatestVersionName); + LatestVersionLocalName = Utils.GetTempPath(LatestVersionName); WebClient http = CreateWebClient(); http.DownloadFileCompleted += Http_DownloadFileCompleted; http.DownloadFileAsync(new Uri(LatestVersionURL), LatestVersionLocalName); @@ -145,7 +141,7 @@ namespace Shadowsocks.Controller { try { - if(e.Error != null) + if (e.Error != null) { Logging.LogUsefulException(e.Error); return; @@ -241,6 +237,5 @@ namespace Shadowsocks.Controller return Asset.CompareVersion(x.version, y.version); } } - } } diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index e826f8e7..1dfd81d2 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -443,12 +443,12 @@ namespace Shadowsocks.Controller private void pacServer_UserRuleFileChanged(object sender, EventArgs e) { // TODO: this is a dirty hack. (from code GListUpdater.http_DownloadStringCompleted()) - if (!File.Exists(Utils.GetTempPath() + "\\gfwlist.txt")) + if (!File.Exists(Utils.GetTempPath("gfwlist.txt"))) { UpdatePACFromGFWList(); return; } - List lines = GFWListUpdater.ParseResult(File.ReadAllText(Utils.GetTempPath() + "\\gfwlist.txt")); + List lines = GFWListUpdater.ParseResult(File.ReadAllText(Utils.GetTempPath("gfwlist.txt"))); if (File.Exists(PACServer.USER_RULE_FILE)) { string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8); diff --git a/shadowsocks-csharp/Encryption/MbedTLS.cs b/shadowsocks-csharp/Encryption/MbedTLS.cs index a9266a2c..8ad9cd09 100644 --- a/shadowsocks-csharp/Encryption/MbedTLS.cs +++ b/shadowsocks-csharp/Encryption/MbedTLS.cs @@ -1,11 +1,10 @@ -using Shadowsocks.Controller; -using Shadowsocks.Properties; -using Shadowsocks.Util; -using System; -using System.Collections.Generic; +using System; using System.IO; using System.Runtime.InteropServices; -using System.Text; + +using Shadowsocks.Controller; +using Shadowsocks.Properties; +using Shadowsocks.Util; namespace Shadowsocks.Encryption { @@ -15,8 +14,7 @@ namespace Shadowsocks.Encryption static MbedTLS() { - string tempPath = Utils.GetTempPath(); - string dllPath = tempPath + "/libsscrypto.dll"; + string dllPath = Utils.GetTempPath("libsscrypto.dll"); try { FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll); @@ -59,10 +57,9 @@ namespace Shadowsocks.Encryption public extern static void md5_starts(IntPtr ctx); [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] - public extern static void md5_update(IntPtr ctx, byte[] input, uint ilen ); + public extern static void md5_update(IntPtr ctx, byte[] input, uint ilen); [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public extern static void md5_finish(IntPtr ctx, byte[] output); - } } diff --git a/shadowsocks-csharp/Encryption/PolarSSL.cs b/shadowsocks-csharp/Encryption/PolarSSL.cs index 3c1d8831..ddf59564 100755 --- a/shadowsocks-csharp/Encryption/PolarSSL.cs +++ b/shadowsocks-csharp/Encryption/PolarSSL.cs @@ -1,11 +1,10 @@ -using Shadowsocks.Controller; -using Shadowsocks.Properties; -using Shadowsocks.Util; -using System; -using System.Collections.Generic; +using System; using System.IO; using System.Runtime.InteropServices; -using System.Text; + +using Shadowsocks.Controller; +using Shadowsocks.Properties; +using Shadowsocks.Util; namespace Shadowsocks.Encryption { @@ -19,8 +18,7 @@ namespace Shadowsocks.Encryption static PolarSSL() { - string tempPath = Utils.GetTempPath(); - string dllPath = tempPath + "/libsscrypto.dll"; + string dllPath = Utils.GetTempPath("libsscrypto.dll"); try { FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll); @@ -63,6 +61,5 @@ namespace Shadowsocks.Encryption [DllImport(DLLNAME, CallingConvention = CallingConvention.Cdecl)] public extern static int arc4_crypt(IntPtr ctx, int length, byte[] input, byte[] output); - } } diff --git a/shadowsocks-csharp/Encryption/Sodium.cs b/shadowsocks-csharp/Encryption/Sodium.cs index 7a9d0c3c..3d20bdea 100755 --- a/shadowsocks-csharp/Encryption/Sodium.cs +++ b/shadowsocks-csharp/Encryption/Sodium.cs @@ -1,11 +1,10 @@ -using Shadowsocks.Controller; -using Shadowsocks.Properties; -using Shadowsocks.Util; -using System; -using System.Collections.Generic; +using System; using System.IO; using System.Runtime.InteropServices; -using System.Text; + +using Shadowsocks.Controller; +using Shadowsocks.Properties; +using Shadowsocks.Util; namespace Shadowsocks.Encryption { @@ -15,8 +14,7 @@ namespace Shadowsocks.Encryption static Sodium() { - string tempPath = Utils.GetTempPath(); - string dllPath = tempPath + "/libsscrypto.dll"; + string dllPath = Utils.GetTempPath("libsscrypto.dll"); try { FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll); @@ -47,7 +45,6 @@ namespace Shadowsocks.Encryption public extern static void ss_sha1_hmac_ex(byte[] key, uint keylen, byte[] input, int ioff, uint ilen, byte[] output); - } } diff --git a/shadowsocks-csharp/Program.cs b/shadowsocks-csharp/Program.cs index efa241e9..977ebffd 100755 --- a/shadowsocks-csharp/Program.cs +++ b/shadowsocks-csharp/Program.cs @@ -1,13 +1,13 @@ -using Shadowsocks.Controller; -using Shadowsocks.Properties; -using Shadowsocks.View; -using System; -using System.Collections.Generic; +using System; using System.Diagnostics; using System.IO; using System.Threading; using System.Windows.Forms; +using Shadowsocks.Controller; +using Shadowsocks.Util; +using Shadowsocks.View; + namespace Shadowsocks { static class Program @@ -18,7 +18,7 @@ namespace Shadowsocks [STAThread] static void Main() { - Util.Utils.ReleaseMemory(true); + Utils.ReleaseMemory(true); using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode())) { Application.EnableVisualStyles(); @@ -37,15 +37,19 @@ namespace Shadowsocks return; } Directory.SetCurrentDirectory(Application.StartupPath); - +#if DEBUG Logging.OpenLogFile(); + // truncate privoxy log file while debugging + string privoxyLogFilename = Utils.GetTempPath("privoxy.log"); + if (File.Exists(privoxyLogFilename)) + using (new FileStream(privoxyLogFilename, FileMode.Truncate)) { } +#else + Logging.OpenLogFile(); +#endif ShadowsocksController controller = new ShadowsocksController(); - MenuViewController viewController = new MenuViewController(controller); - controller.Start(); - Application.Run(); } } diff --git a/shadowsocks-csharp/Util/Util.cs b/shadowsocks-csharp/Util/Util.cs index 450faa56..0fe193c7 100755 --- a/shadowsocks-csharp/Util/Util.cs +++ b/shadowsocks-csharp/Util/Util.cs @@ -11,23 +11,38 @@ namespace Shadowsocks.Util { public class Utils { + private static string TempPath = null; + // return path to store temporary files public static string GetTempPath() { - if (File.Exists(Application.StartupPath + "\\shadowsocks_portable_mode.txt")) + if (TempPath == null) { - try - { - Directory.CreateDirectory(Application.StartupPath + "\\temp"); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - } - // don't use "/", it will fail when we call explorer /select xxx/temp\xxx.log - return Application.StartupPath + "\\temp"; + if (File.Exists(Path.Combine(Application.StartupPath, "shadowsocks_portable_mode.txt"))) + try + { + Directory.CreateDirectory(Path.Combine(Application.StartupPath, "temp")); + } + catch (Exception e) + { + TempPath = Path.GetTempPath(); + Logging.LogUsefulException(e); + } + finally + { + // don't use "/", it will fail when we call explorer /select xxx/temp\xxx.log + TempPath = Path.Combine(Application.StartupPath, "temp"); + } + else + TempPath = Path.GetTempPath(); } - return Path.GetTempPath(); + return TempPath; + } + + // return a full path with filename combined which pointed to the temporary directory + public static string GetTempPath(string filename) + { + return Path.Combine(GetTempPath(), filename); } public static void ReleaseMemory(bool removePages) From 9194b0325fe27f46eb43a0dc0a51b7d5c0785f02 Mon Sep 17 00:00:00 2001 From: kimw Date: Mon, 4 Jan 2016 23:57:30 +0800 Subject: [PATCH 37/54] avoid warning message while compile --- shadowsocks-csharp/Encryption/MbedTLS.cs | 2 +- .../Model/StatisticsStrategyConfiguration.cs | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/shadowsocks-csharp/Encryption/MbedTLS.cs b/shadowsocks-csharp/Encryption/MbedTLS.cs index 8ad9cd09..a7162f66 100644 --- a/shadowsocks-csharp/Encryption/MbedTLS.cs +++ b/shadowsocks-csharp/Encryption/MbedTLS.cs @@ -19,7 +19,7 @@ namespace Shadowsocks.Encryption { FileManager.UncompressFile(dllPath, Resources.libsscrypto_dll); } - catch (IOException ex) + catch (IOException) { } catch (Exception e) diff --git a/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs b/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs index 62a48c2e..70433117 100644 --- a/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs +++ b/shadowsocks-csharp/Model/StatisticsStrategyConfiguration.cs @@ -3,17 +3,17 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -using Shadowsocks.Controller; -using Shadowsocks.Controller.Strategy; -using SimpleJson; + using Newtonsoft.Json; +using Shadowsocks.Controller; + namespace Shadowsocks.Model { [Serializable] public class StatisticsStrategyConfiguration { - public static readonly string ID = "com.shadowsocks.strategy.statistics"; + public static readonly string ID = "com.shadowsocks.strategy.statistics"; private bool _statisticsEnabled = true; private bool _byIsp = false; private bool _byHourOfDay = false; @@ -21,7 +21,6 @@ namespace Shadowsocks.Model private int _dataCollectionMinutes = 10; private int _repeatTimesNum = 4; - private const string ConfigFile = "statistics-config.json"; public static StatisticsStrategyConfiguration Load() @@ -32,7 +31,7 @@ namespace Shadowsocks.Model var configuration = JsonConvert.DeserializeObject(content); return configuration; } - catch (FileNotFoundException e) + catch (FileNotFoundException) { var configuration = new StatisticsStrategyConfiguration(); Save(configuration); @@ -62,10 +61,10 @@ namespace Shadowsocks.Model public StatisticsStrategyConfiguration() { - var availabilityStatisticsType = typeof (AvailabilityStatistics); + var availabilityStatisticsType = typeof(AvailabilityStatistics); var statisticsData = availabilityStatisticsType.GetNestedType("StatisticsData"); var properties = statisticsData.GetFields(BindingFlags.Instance | BindingFlags.Public); - Calculations = properties.ToDictionary(p => p.Name, _ => (float) 0); + Calculations = properties.ToDictionary(p => p.Name, _ => (float)0); } public bool StatisticsEnabled From b813d9eebc0f322ccc6354c4dd30e66be655b973 Mon Sep 17 00:00:00 2001 From: kimw Date: Mon, 4 Jan 2016 23:58:24 +0800 Subject: [PATCH 38/54] featured middle click on tray pops up log viewer --- shadowsocks-csharp/View/MenuViewController.cs | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index b47fe038..25a3b3e8 100755 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -1,16 +1,17 @@ -using Shadowsocks.Controller; -using Shadowsocks.Model; -using Shadowsocks.Properties; -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; -using System.Text; using System.Windows.Forms; + using ZXing; using ZXing.Common; using ZXing.QrCode; +using Shadowsocks.Controller; +using Shadowsocks.Model; +using Shadowsocks.Properties; + namespace Shadowsocks.View { public class MenuViewController @@ -69,6 +70,7 @@ namespace Shadowsocks.View _notifyIcon.Visible = true; _notifyIcon.ContextMenu = contextMenu1; _notifyIcon.BalloonTipClicked += notifyIcon1_BalloonTipClicked; + _notifyIcon.MouseClick += notifyIcon1_Click; _notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick; _notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed; @@ -204,7 +206,6 @@ namespace Shadowsocks.View }); } - private void controller_ConfigChanged(object sender, EventArgs e) { LoadCurrentConfiguration(); @@ -420,6 +421,18 @@ namespace Shadowsocks.View Process.Start("https://github.com/shadowsocks/shadowsocks-windows"); } + private void notifyIcon1_Click(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtons.Left) + { + // TODO: show something interesting + } + else if (e.Button == MouseButtons.Middle) + { + ShowLogForms(); + } + } + private void notifyIcon1_DoubleClick(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) From 84f14d71c9a516649e55a1c09a2a823018fd83cf Mon Sep 17 00:00:00 2001 From: kimw Date: Tue, 5 Jan 2016 00:01:06 +0800 Subject: [PATCH 39/54] clean up avoid log message on timeout and network cut off. it's useless but confuse users. --- shadowsocks-csharp/Controller/Logging.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/shadowsocks-csharp/Controller/Logging.cs b/shadowsocks-csharp/Controller/Logging.cs index 3db34658..edf84144 100755 --- a/shadowsocks-csharp/Controller/Logging.cs +++ b/shadowsocks-csharp/Controller/Logging.cs @@ -84,11 +84,19 @@ namespace Shadowsocks.Controller } else if (se.SocketErrorCode == SocketError.NotConnected) { - // close when not connected + // The application tried to send or receive data, and the System.Net.Sockets.Socket is not connected. + } + else if (se.SocketErrorCode == SocketError.HostUnreachable) + { + // There is no network route to the specified host. + } + else if (se.SocketErrorCode == SocketError.TimedOut) + { + // The connection attempt timed out, or the connected host has failed to respond. } else { - Console.WriteLine(e); + Info(e); } } else if (e is ObjectDisposedException) @@ -96,7 +104,7 @@ namespace Shadowsocks.Controller } else { - Console.WriteLine(e); + Info(e); } } } From 04486094f786e2ae3fc5802715709b5aa2fdf0bc Mon Sep 17 00:00:00 2001 From: kimw Date: Tue, 5 Jan 2016 03:26:55 +0800 Subject: [PATCH 40/54] fixed compile errors in Release mode --- shadowsocks-csharp/Controller/Logging.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/shadowsocks-csharp/Controller/Logging.cs b/shadowsocks-csharp/Controller/Logging.cs index edf84144..38201660 100755 --- a/shadowsocks-csharp/Controller/Logging.cs +++ b/shadowsocks-csharp/Controller/Logging.cs @@ -48,9 +48,9 @@ namespace Shadowsocks.Controller #endif } -#if DEBUG public static void Debug(EndPoint local, EndPoint remote, int len, string header = null, string tailer = null) { +#if DEBUG if (header == null && tailer == null) Debug($"{local} => {remote} (size={len})"); else if (header == null && tailer != null) @@ -59,13 +59,15 @@ namespace Shadowsocks.Controller Debug($"{header}: {local} => {remote} (size={len})"); else Debug($"{header}: {local} => {remote} (size={len}), {tailer}"); +#endif } public static void Debug(Socket sock, int len, string header = null, string tailer = null) { +#if DEBUG Debug(sock.LocalEndPoint, sock.RemoteEndPoint, len, header, tailer); - } #endif + } public static void LogUsefulException(Exception e) { From c2095b5ad7ef7120f8cee15fdf1dbf28957a1654 Mon Sep 17 00:00:00 2001 From: kimw Date: Tue, 5 Jan 2016 03:38:30 +0800 Subject: [PATCH 41/54] migrated SimpleJson => Newtonesoft.Json, and of cause removed SimpleJson source files. --- shadowsocks-csharp/3rd/SimpleJson.cs | 1902 ----------------- .../Service/AvailabilityStatistics.cs | 58 +- .../Controller/Service/GfwListUpdater.cs | 6 +- .../Controller/Service/UpdateChecker.cs | 32 +- .../Controller/ShadowsocksController.cs | 2 + .../Controller/Strategy/StatisticsStrategy.cs | 3 +- shadowsocks-csharp/Model/Configuration.cs | 27 +- shadowsocks-csharp/Model/Server.cs | 7 +- .../StatisticsStrategyConfigurationForm.cs | 14 +- shadowsocks-csharp/shadowsocks-csharp.csproj | 2 +- 10 files changed, 74 insertions(+), 1979 deletions(-) delete mode 100644 shadowsocks-csharp/3rd/SimpleJson.cs diff --git a/shadowsocks-csharp/3rd/SimpleJson.cs b/shadowsocks-csharp/3rd/SimpleJson.cs deleted file mode 100644 index e3301f4e..00000000 --- a/shadowsocks-csharp/3rd/SimpleJson.cs +++ /dev/null @@ -1,1902 +0,0 @@ -//----------------------------------------------------------------------- -// -// Copyright (c) 2011, The Outercurve Foundation. -// -// Licensed under the MIT License (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.opensource.org/licenses/mit-license.php -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Nathan Totten (ntotten.com), Jim Zimmerman (jimzimmerman.com) and Prabir Shrestha (prabir.me) -// https://github.com/facebook-csharp-sdk/simple-json -//----------------------------------------------------------------------- - -// VERSION: - -// NOTE: uncomment the following line to make SimpleJson class internal. -//#define SIMPLE_JSON_INTERNAL - -// NOTE: uncomment the following line to make JsonArray and JsonObject class internal. -//#define SIMPLE_JSON_OBJARRAYINTERNAL - -// NOTE: uncomment the following line to enable dynamic support. -//#define SIMPLE_JSON_DYNAMIC - -// NOTE: uncomment the following line to enable DataContract support. -//#define SIMPLE_JSON_DATACONTRACT - -// NOTE: uncomment the following line to use Reflection.Emit (better performance) instead of method.invoke(). -// don't enable ReflectionEmit for WinRT, Silverlight and WP7. -//#define SIMPLE_JSON_REFLECTIONEMIT - -// NOTE: uncomment the following line if you are compiling under Window Metro style application/library. -// usually already defined in properties -//#define NETFX_CORE; - -// original json parsing code from http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html - -using System; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -#if SIMPLE_JSON_DYNAMIC -using System.Dynamic; -#endif -using System.Globalization; -using System.Reflection; -#if SIMPLE_JSON_REFLECTIONEMIT -using System.Reflection.Emit; -#endif -#if SIMPLE_JSON_DATACONTRACT -using System.Runtime.Serialization; -#endif -using System.Text; -using SimpleJson.Reflection; - -namespace SimpleJson -{ - #region JsonArray - - /// - /// Represents the json array. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] -#if SIMPLE_JSON_OBJARRAYINTERNAL - internal -#else - public -#endif - class JsonArray : List - { - /// - /// Initializes a new instance of the class. - /// - public JsonArray() { } - - /// - /// Initializes a new instance of the class. - /// - /// The capacity of the json array. - public JsonArray(int capacity) : base(capacity) { } - - /// - /// The json representation of the array. - /// - /// The json representation of the array. - public override string ToString() - { - return SimpleJson.SerializeObject(this) ?? string.Empty; - } - } - - #endregion - - #region JsonObject - - /// - /// Represents the json object. - /// - [EditorBrowsable(EditorBrowsableState.Never)] - [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] -#if SIMPLE_JSON_OBJARRAYINTERNAL - internal -#else - public -#endif - class JsonObject : -#if SIMPLE_JSON_DYNAMIC - DynamicObject, -#endif - IDictionary - { - /// - /// The internal member dictionary. - /// - private readonly Dictionary _members = new Dictionary(); - - /// - /// Gets the at the specified index. - /// - /// - public object this[int index] - { - get { return GetAtIndex(_members, index); } - } - - internal static object GetAtIndex(IDictionary obj, int index) - { - if (obj == null) - throw new ArgumentNullException("obj"); - - if (index >= obj.Count) - throw new ArgumentOutOfRangeException("index"); - - int i = 0; - foreach (KeyValuePair o in obj) - if (i++ == index) return o.Value; - - return null; - } - - /// - /// Adds the specified key. - /// - /// The key. - /// The value. - public void Add(string key, object value) - { - _members.Add(key, value); - } - - /// - /// Determines whether the specified key contains key. - /// - /// The key. - /// - /// true if the specified key contains key; otherwise, false. - /// - public bool ContainsKey(string key) - { - return _members.ContainsKey(key); - } - - /// - /// Gets the keys. - /// - /// The keys. - public ICollection Keys - { - get { return _members.Keys; } - } - - /// - /// Removes the specified key. - /// - /// The key. - /// - public bool Remove(string key) - { - return _members.Remove(key); - } - - /// - /// Tries the get value. - /// - /// The key. - /// The value. - /// - public bool TryGetValue(string key, out object value) - { - return _members.TryGetValue(key, out value); - } - - /// - /// Gets the values. - /// - /// The values. - public ICollection Values - { - get { return _members.Values; } - } - - /// - /// Gets or sets the with the specified key. - /// - /// - public object this[string key] - { - get { return _members[key]; } - set { _members[key] = value; } - } - - /// - /// Adds the specified item. - /// - /// The item. - public void Add(KeyValuePair item) - { - _members.Add(item.Key, item.Value); - } - - /// - /// Clears this instance. - /// - public void Clear() - { - _members.Clear(); - } - - /// - /// Determines whether [contains] [the specified item]. - /// - /// The item. - /// - /// true if [contains] [the specified item]; otherwise, false. - /// - public bool Contains(KeyValuePair item) - { - return _members.ContainsKey(item.Key) && _members[item.Key] == item.Value; - } - - /// - /// Copies to. - /// - /// The array. - /// Index of the array. - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - int num = Count; - foreach (KeyValuePair kvp in this) - { - array[arrayIndex++] = kvp; - - if (--num <= 0) - return; - } - } - - /// - /// Gets the count. - /// - /// The count. - public int Count - { - get { return _members.Count; } - } - - /// - /// Gets a value indicating whether this instance is read only. - /// - /// - /// true if this instance is read only; otherwise, false. - /// - public bool IsReadOnly - { - get { return false; } - } - - /// - /// Removes the specified item. - /// - /// The item. - /// - public bool Remove(KeyValuePair item) - { - return _members.Remove(item.Key); - } - - /// - /// Gets the enumerator. - /// - /// - public IEnumerator> GetEnumerator() - { - return _members.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through a collection. - /// - /// - /// An object that can be used to iterate through the collection. - /// - IEnumerator IEnumerable.GetEnumerator() - { - return _members.GetEnumerator(); - } - - /// - /// Returns a json that represents the current . - /// - /// - /// A json that represents the current . - /// - public override string ToString() - { - return SimpleJson.SerializeObject(this); - } - -#if SIMPLE_JSON_DYNAMIC - /// - /// Provides implementation for type conversion operations. Classes derived from the class can override this method to specify dynamic behavior for operations that convert an object from one type to another. - /// - /// Provides information about the conversion operation. The binder.Type property provides the type to which the object must be converted. For example, for the statement (String)sampleObject in C# (CType(sampleObject, Type) in Visual Basic), where sampleObject is an instance of the class derived from the class, binder.Type returns the type. The binder.Explicit property provides information about the kind of conversion that occurs. It returns true for explicit conversion and false for implicit conversion. - /// The result of the type conversion operation. - /// - /// Alwasy returns true. - /// - public override bool TryConvert(ConvertBinder binder, out object result) - { - // - if (binder == (ConvertBinder)null) - throw new ArgumentNullException("binder"); - // - Type targetType = binder.Type; - - if ((targetType == typeof(IEnumerable)) || - (targetType == typeof(IEnumerable>)) || - (targetType == typeof(IDictionary)) || -#if NETFX_CORE - (targetType == typeof(IDictionary<,>)) -#else - (targetType == typeof(IDictionary)) -#endif -) - { - result = this; - return true; - } - - return base.TryConvert(binder, out result); - } - - /// - /// Provides the implementation for operations that delete an object member. This method is not intended for use in C# or Visual Basic. - /// - /// Provides information about the deletion. - /// - /// Alwasy returns true. - /// - public override bool TryDeleteMember(DeleteMemberBinder binder) - { - // - if (binder == (DeleteMemberBinder)null) - throw new ArgumentNullException("binder"); - // - return _members.Remove(binder.Name); - } - - /// - /// Provides the implementation for operations that get a value by index. Classes derived from the class can override this method to specify dynamic behavior for indexing operations. - /// - /// Provides information about the operation. - /// The indexes that are used in the operation. For example, for the sampleObject[3] operation in C# (sampleObject(3) in Visual Basic), where sampleObject is derived from the DynamicObject class, is equal to 3. - /// The result of the index operation. - /// - /// Alwasy returns true. - /// - public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) - { - if (indexes.Length == 1) - { - result = ((IDictionary)this)[(string)indexes[0]]; - return true; - } - result = (object)null; - return true; - } - - /// - /// Provides the implementation for operations that get member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as getting a value for a property. - /// - /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. - /// The result of the get operation. For example, if the method is called for a property, you can assign the property value to . - /// - /// Alwasy returns true. - /// - public override bool TryGetMember(GetMemberBinder binder, out object result) - { - object value; - if (_members.TryGetValue(binder.Name, out value)) - { - result = value; - return true; - } - result = (object)null; - return true; - } - - /// - /// Provides the implementation for operations that set a value by index. Classes derived from the class can override this method to specify dynamic behavior for operations that access objects by a specified index. - /// - /// Provides information about the operation. - /// The indexes that are used in the operation. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the class, is equal to 3. - /// The value to set to the object that has the specified index. For example, for the sampleObject[3] = 10 operation in C# (sampleObject(3) = 10 in Visual Basic), where sampleObject is derived from the class, is equal to 10. - /// - /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown. - /// - public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) - { - if (indexes.Length == 1) - { - ((IDictionary)this)[(string)indexes[0]] = value; - return true; - } - - return base.TrySetIndex(binder, indexes, value); - } - - /// - /// Provides the implementation for operations that set member values. Classes derived from the class can override this method to specify dynamic behavior for operations such as setting a value for a property. - /// - /// Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member to which the value is being assigned. For example, for the statement sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive. - /// The value to set to the member. For example, for sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class derived from the class, the is "Test". - /// - /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a language-specific run-time exception is thrown.) - /// - public override bool TrySetMember(SetMemberBinder binder, object value) - { - // - if (binder == (SetMemberBinder)null) - throw new ArgumentNullException("binder"); - // - _members[binder.Name] = value; - return true; - } - - /// - /// Returns the enumeration of all dynamic member names. - /// - /// - /// A sequence that contains dynamic member names. - /// - public override IEnumerable GetDynamicMemberNames() - { - foreach (var key in Keys) - yield return key; - } -#endif - } - - #endregion -} - -namespace SimpleJson -{ - #region JsonParser - - /// - /// This class encodes and decodes JSON strings. - /// Spec. details, see http://www.json.org/ - /// - /// JSON uses Arrays and Objects. These correspond here to the datatypes JsonArray(IList<object>) and JsonObject(IDictionary<string,object>). - /// All numbers are parsed to doubles. - /// -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - class SimpleJson - { - private const int TOKEN_NONE = 0; - private const int TOKEN_CURLY_OPEN = 1; - private const int TOKEN_CURLY_CLOSE = 2; - private const int TOKEN_SQUARED_OPEN = 3; - private const int TOKEN_SQUARED_CLOSE = 4; - private const int TOKEN_COLON = 5; - private const int TOKEN_COMMA = 6; - private const int TOKEN_STRING = 7; - private const int TOKEN_NUMBER = 8; - private const int TOKEN_TRUE = 9; - private const int TOKEN_FALSE = 10; - private const int TOKEN_NULL = 11; - - private const int BUILDER_CAPACITY = 2000; - - /// - /// Parses the string json into a value - /// - /// A JSON string. - /// An IList<object>, a IDictionary<string,object>, a double, a string, null, true, or false - public static object DeserializeObject(string json) - { - object @object; - if (TryDeserializeObject(json, out @object)) - return @object; - throw new System.Runtime.Serialization.SerializationException("Invalid JSON string"); - } - - /// - /// Try parsing the json string into a value. - /// - /// - /// A JSON string. - /// - /// - /// The object. - /// - /// - /// Returns true if successfull otherwise false. - /// - public static bool TryDeserializeObject(string json, out object @object) - { - bool success = true; - if (json != null) - { - char[] charArray = json.ToCharArray(); - int index = 0; - @object = ParseValue(charArray, ref index, ref success); - } - else - @object = null; - - return success; - } - - public static object DeserializeObject(string json, Type type, IJsonSerializerStrategy jsonSerializerStrategy) - { - object jsonObject = DeserializeObject(json); - - return type == null || jsonObject != null && -#if NETFX_CORE - jsonObject.GetType().GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()) -#else - jsonObject.GetType().IsAssignableFrom(type) -#endif - ? jsonObject - : (jsonSerializerStrategy ?? CurrentJsonSerializerStrategy).DeserializeObject(jsonObject, type); - } - - public static object DeserializeObject(string json, Type type) - { - return DeserializeObject(json, type, null); - } - - public static T DeserializeObject(string json, IJsonSerializerStrategy jsonSerializerStrategy) - { - return (T)DeserializeObject(json, typeof(T), jsonSerializerStrategy); - } - - public static T DeserializeObject(string json) - { - return (T)DeserializeObject(json, typeof(T), null); - } - - /// - /// Converts a IDictionary<string,object> / IList<object> object into a JSON string - /// - /// A IDictionary<string,object> / IList<object> - /// Serializer strategy to use - /// A JSON encoded string, or null if object 'json' is not serializable - public static string SerializeObject(object json, IJsonSerializerStrategy jsonSerializerStrategy) - { - StringBuilder builder = new StringBuilder(BUILDER_CAPACITY); - bool success = SerializeValue(jsonSerializerStrategy, json, 0, builder); - return (success ? builder.ToString() : null); - } - - public static string SerializeObject(object json) - { - return SerializeObject(json, CurrentJsonSerializerStrategy); - } - - protected static IDictionary ParseObject(char[] json, ref int index, ref bool success) - { - IDictionary table = new JsonObject(); - int token; - - // { - NextToken(json, ref index); - - bool done = false; - while (!done) - { - token = LookAhead(json, index); - if (token == TOKEN_NONE) - { - success = false; - return null; - } - else if (token == TOKEN_COMMA) - NextToken(json, ref index); - else if (token == TOKEN_CURLY_CLOSE) - { - NextToken(json, ref index); - return table; - } - else - { - // name - string name = ParseString(json, ref index, ref success); - if (!success) - { - success = false; - return null; - } - - // : - token = NextToken(json, ref index); - if (token != TOKEN_COLON) - { - success = false; - return null; - } - - // value - object value = ParseValue(json, ref index, ref success); - if (!success) - { - success = false; - return null; - } - - table[name] = value; - } - } - - return table; - } - - protected static JsonArray ParseArray(char[] json, ref int index, ref bool success) - { - JsonArray array = new JsonArray(); - - // [ - NextToken(json, ref index); - - bool done = false; - while (!done) - { - int token = LookAhead(json, index); - if (token == TOKEN_NONE) - { - success = false; - return null; - } - else if (token == TOKEN_COMMA) - NextToken(json, ref index); - else if (token == TOKEN_SQUARED_CLOSE) - { - NextToken(json, ref index); - break; - } - else - { - object value = ParseValue(json, ref index, ref success); - if (!success) - return null; - array.Add(value); - } - } - - return array; - } - - protected static object ParseValue(char[] json, ref int index, ref bool success) - { - switch (LookAhead(json, index)) - { - case TOKEN_STRING: - return ParseString(json, ref index, ref success); - case TOKEN_NUMBER: - return ParseNumber(json, ref index, ref success); - case TOKEN_CURLY_OPEN: - return ParseObject(json, ref index, ref success); - case TOKEN_SQUARED_OPEN: - return ParseArray(json, ref index, ref success); - case TOKEN_TRUE: - NextToken(json, ref index); - return true; - case TOKEN_FALSE: - NextToken(json, ref index); - return false; - case TOKEN_NULL: - NextToken(json, ref index); - return null; - case TOKEN_NONE: - break; - } - - success = false; - return null; - } - - protected static string ParseString(char[] json, ref int index, ref bool success) - { - StringBuilder s = new StringBuilder(BUILDER_CAPACITY); - char c; - - EatWhitespace(json, ref index); - - // " - c = json[index++]; - - bool complete = false; - while (!complete) - { - if (index == json.Length) - { - break; - } - - c = json[index++]; - if (c == '"') - { - complete = true; - break; - } - else if (c == '\\') - { - if (index == json.Length) - break; - c = json[index++]; - if (c == '"') - s.Append('"'); - else if (c == '\\') - s.Append('\\'); - else if (c == '/') - s.Append('/'); - else if (c == 'b') - s.Append('\b'); - else if (c == 'f') - s.Append('\f'); - else if (c == 'n') - s.Append('\n'); - else if (c == 'r') - s.Append('\r'); - else if (c == 't') - s.Append('\t'); - else if (c == 'u') - { - int remainingLength = json.Length - index; - if (remainingLength >= 4) - { - // parse the 32 bit hex into an integer codepoint - uint codePoint; - if ( - !(success = - UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, - CultureInfo.InvariantCulture, out codePoint))) - return ""; - - // convert the integer codepoint to a unicode char and add to string - - if (0xD800 <= codePoint && codePoint <= 0xDBFF) // if high surrogate - { - index += 4; // skip 4 chars - remainingLength = json.Length - index; - if (remainingLength >= 6) - { - uint lowCodePoint; - if (new string(json, index, 2) == "\\u" && - UInt32.TryParse(new string(json, index + 2, 4), NumberStyles.HexNumber, - CultureInfo.InvariantCulture, out lowCodePoint)) - { - if (0xDC00 <= lowCodePoint && lowCodePoint <= 0xDFFF) // if low surrogate - { - s.Append((char)codePoint); - s.Append((char)lowCodePoint); - index += 6; // skip 6 chars - continue; - } - } - } - success = false; // invalid surrogate pair - return ""; - } -#if SILVERLIGHT - s.Append(ConvertFromUtf32((int)codePoint)); -#else - s.Append(Char.ConvertFromUtf32((int)codePoint)); -#endif - // skip 4 chars - index += 4; - } - else - break; - } - } - else - s.Append(c); - } - - if (!complete) - { - success = false; - return null; - } - - return s.ToString(); - } - -#if SILVERLIGHT - private static string ConvertFromUtf32(int utf32) - { - // http://www.java2s.com/Open-Source/CSharp/2.6.4-mono-.net-core/System/System/Char.cs.htm - if (utf32 < 0 || utf32 > 0x10FFFF) - throw new ArgumentOutOfRangeException("utf32", "The argument must be from 0 to 0x10FFFF."); - if (0xD800 <= utf32 && utf32 <= 0xDFFF) - throw new ArgumentOutOfRangeException("utf32", "The argument must not be in surrogate pair range."); - if (utf32 < 0x10000) - return new string((char)utf32, 1); - utf32 -= 0x10000; - return new string(new char[] {(char) ((utf32 >> 10) + 0xD800),(char) (utf32 % 0x0400 + 0xDC00)}); - } -#endif - - protected static object ParseNumber(char[] json, ref int index, ref bool success) - { - EatWhitespace(json, ref index); - - int lastIndex = GetLastIndexOfNumber(json, index); - int charLength = (lastIndex - index) + 1; - - object returnNumber; - string str = new string(json, index, charLength); - if (str.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || str.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1) - { - double number; - success = double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); - returnNumber = number; - } - else - { - long number; - success = long.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number); - returnNumber = number; - } - - index = lastIndex + 1; - return returnNumber; - } - - protected static int GetLastIndexOfNumber(char[] json, int index) - { - int lastIndex; - - for (lastIndex = index; lastIndex < json.Length; lastIndex++) - if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) break; - return lastIndex - 1; - } - - protected static void EatWhitespace(char[] json, ref int index) - { - for (; index < json.Length; index++) - if (" \t\n\r\b\f".IndexOf(json[index]) == -1) break; - } - - protected static int LookAhead(char[] json, int index) - { - int saveIndex = index; - return NextToken(json, ref saveIndex); - } - - [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] - protected static int NextToken(char[] json, ref int index) - { - EatWhitespace(json, ref index); - - if (index == json.Length) - return TOKEN_NONE; - - char c = json[index]; - index++; - switch (c) - { - case '{': - return TOKEN_CURLY_OPEN; - case '}': - return TOKEN_CURLY_CLOSE; - case '[': - return TOKEN_SQUARED_OPEN; - case ']': - return TOKEN_SQUARED_CLOSE; - case ',': - return TOKEN_COMMA; - case '"': - return TOKEN_STRING; - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case '-': - return TOKEN_NUMBER; - case ':': - return TOKEN_COLON; - } - index--; - - int remainingLength = json.Length - index; - - // false - if (remainingLength >= 5) - { - if (json[index] == 'f' && - json[index + 1] == 'a' && - json[index + 2] == 'l' && - json[index + 3] == 's' && - json[index + 4] == 'e') - { - index += 5; - return TOKEN_FALSE; - } - } - - // true - if (remainingLength >= 4) - { - if (json[index] == 't' && - json[index + 1] == 'r' && - json[index + 2] == 'u' && - json[index + 3] == 'e') - { - index += 4; - return TOKEN_TRUE; - } - } - - // null - if (remainingLength >= 4) - { - if (json[index] == 'n' && - json[index + 1] == 'u' && - json[index + 2] == 'l' && - json[index + 3] == 'l') - { - index += 4; - return TOKEN_NULL; - } - } - - return TOKEN_NONE; - } - - protected static bool SerializeValue(IJsonSerializerStrategy jsonSerializerStrategy, object value, int level, StringBuilder builder) - { - bool success = true; - - if (value is string) - success = SerializeString((string)value, builder); - else if (value is IDictionary) - { - IDictionary dict = (IDictionary)value; - success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, level, builder); - } - else if (value is IDictionary) - { - IDictionary dict = (IDictionary)value; - success = SerializeObject(jsonSerializerStrategy, dict.Keys, dict.Values, level, builder); - } - else if (value is IEnumerable) - success = SerializeArray(jsonSerializerStrategy, (IEnumerable)value, level, builder); - else if (IsNumeric(value)) - success = SerializeNumber(value, builder); - else if (value is Boolean) - builder.Append((bool)value ? "true" : "false"); - else if (value == null) - builder.Append("null"); - else - { - object serializedObject; - success = jsonSerializerStrategy.SerializeNonPrimitiveObject(value, out serializedObject); - if (success) - SerializeValue(jsonSerializerStrategy, serializedObject, level, builder); - } - - return success; - } - - protected static bool SerializeObject(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable keys, IEnumerable values, int level, StringBuilder builder) - { - builder.Append("{\r\n"); - - level++; - - IEnumerator ke = keys.GetEnumerator(); - IEnumerator ve = values.GetEnumerator(); - - bool first = true; - while (ke.MoveNext() && ve.MoveNext()) - { - object key = ke.Current; - object value = ve.Current; - - if (!first) - builder.Append(",\r\n"); - - FeedIndent(level, builder); - - if (key is string) - SerializeString((string)key, builder); - else - if (!SerializeValue(jsonSerializerStrategy, value, level, builder)) return false; - - builder.Append(" : "); - if (!SerializeValue(jsonSerializerStrategy, value, level, builder)) - return false; - - first = false; - } - - builder.Append("\r\n"); - FeedIndent(level - 1, builder); - builder.Append("}"); - return true; - } - - protected static bool SerializeArray(IJsonSerializerStrategy jsonSerializerStrategy, IEnumerable anArray, int level, StringBuilder builder) - { - - builder.Append("[\r\n"); - - level++; - - bool first = true; - foreach (object value in anArray) - { - if (!first) - builder.Append(",\r\n"); - - FeedIndent(level, builder); - - if (!SerializeValue(jsonSerializerStrategy, value, level, builder)) - return false; - - first = false; - } - - builder.Append("\r\n"); - FeedIndent(level - 1, builder); - builder.Append("]"); - return true; - } - - protected static bool SerializeString(string aString, StringBuilder builder) - { - builder.Append("\""); - - 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("\""); - return true; - } - - protected static bool SerializeNumber(object number, StringBuilder builder) - { - if (number is long) - { - builder.Append(((long)number).ToString(CultureInfo.InvariantCulture)); - } - else if (number is ulong) - { - builder.Append(((ulong)number).ToString(CultureInfo.InvariantCulture)); - } - else if (number is int) - { - builder.Append(((int)number).ToString(CultureInfo.InvariantCulture)); - } - else if (number is uint) - { - builder.Append(((uint)number).ToString(CultureInfo.InvariantCulture)); - } - else if (number is decimal) - { - builder.Append(((decimal)number).ToString(CultureInfo.InvariantCulture)); - } - else if (number is float) - { - builder.Append(((float)number).ToString(CultureInfo.InvariantCulture)); - } - else - { - builder.Append(Convert.ToDouble(number, CultureInfo.InvariantCulture).ToString("r", CultureInfo.InvariantCulture)); - } - - return true; - } - - protected static void FeedIndent(int level, StringBuilder builder) - { - for (int i = 0; i < level; i++) - builder.Append(" "); - } - - /// - /// Determines if a given object is numeric in any way - /// (can be integer, double, null, etc). - /// - protected static bool IsNumeric(object value) - { - if (value is sbyte) return true; - if (value is byte) return true; - if (value is short) return true; - if (value is ushort) return true; - if (value is int) return true; - if (value is uint) return true; - if (value is long) return true; - if (value is ulong) return true; - if (value is float) return true; - if (value is double) return true; - if (value is decimal) return true; - return false; - } - - private static IJsonSerializerStrategy currentJsonSerializerStrategy; - public static IJsonSerializerStrategy CurrentJsonSerializerStrategy - { - get - { - // todo: implement locking mechanism. - return currentJsonSerializerStrategy ?? - (currentJsonSerializerStrategy = -#if SIMPLE_JSON_DATACONTRACT - DataContractJsonSerializerStrategy -#else - PocoJsonSerializerStrategy -#endif -); - } - - set - { - currentJsonSerializerStrategy = value; - } - } - - private static PocoJsonSerializerStrategy pocoJsonSerializerStrategy; - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Advanced)] - public static PocoJsonSerializerStrategy PocoJsonSerializerStrategy - { - get - { - // todo: implement locking mechanism. - return pocoJsonSerializerStrategy ?? (pocoJsonSerializerStrategy = new PocoJsonSerializerStrategy()); - } - } - -#if SIMPLE_JSON_DATACONTRACT - - private static DataContractJsonSerializerStrategy dataContractJsonSerializerStrategy; - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Advanced)] - public static DataContractJsonSerializerStrategy DataContractJsonSerializerStrategy - { - get - { - // todo: implement locking mechanism. - return dataContractJsonSerializerStrategy ?? (dataContractJsonSerializerStrategy = new DataContractJsonSerializerStrategy()); - } - } - -#endif - } - - #endregion - - #region Simple Json Serializer Strategies - -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - interface IJsonSerializerStrategy - { - bool SerializeNonPrimitiveObject(object input, out object output); - - object DeserializeObject(object value, Type type); - } - -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - class PocoJsonSerializerStrategy : IJsonSerializerStrategy - { - internal CacheResolver CacheResolver; - - private static readonly string[] Iso8601Format = new string[] - { - @"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z", - @"yyyy-MM-dd\THH:mm:ss\Z", - @"yyyy-MM-dd\THH:mm:ssK" - }; - - public PocoJsonSerializerStrategy() - { - CacheResolver = new CacheResolver(BuildMap); - } - - protected virtual void BuildMap(Type type, SafeDictionary memberMaps) - { -#if NETFX_CORE - foreach (PropertyInfo info in type.GetTypeInfo().DeclaredProperties) { - var getMethod = info.GetMethod; - if(getMethod==null || !getMethod.IsPublic || getMethod.IsStatic) continue; -#else - foreach (PropertyInfo info in type.GetProperties(BindingFlags.Instance | BindingFlags.Public)) - { -#endif - memberMaps.Add(info.Name, new CacheResolver.MemberMap(info)); - } -#if NETFX_CORE - foreach (FieldInfo info in type.GetTypeInfo().DeclaredFields) { - if(!info.IsPublic || info.IsStatic) continue; -#else - foreach (FieldInfo info in type.GetFields(BindingFlags.Public | BindingFlags.Instance)) - { -#endif - memberMaps.Add(info.Name, new CacheResolver.MemberMap(info)); - } - } - - public virtual bool SerializeNonPrimitiveObject(object input, out object output) - { - return TrySerializeKnownTypes(input, out output) || TrySerializeUnknownTypes(input, out output); - } - - [SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] - public virtual object DeserializeObject(object value, Type type) - { - object obj = null; - if (value is string) - { - string str = value as string; - - if (!string.IsNullOrEmpty(str)) - { - if (type == typeof(DateTime) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(DateTime))) - obj = DateTime.ParseExact(str, Iso8601Format, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); - else if (type == typeof(Guid) || (ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid))) - obj = new Guid(str); - else - obj = str; - } - else - { - if (type == typeof(Guid)) - obj= default(Guid); - else if(ReflectionUtils.IsNullableType(type) && Nullable.GetUnderlyingType(type) == typeof(Guid)) - obj = null; - else - obj = str; - } - } - else if (value is bool) - obj = value; - else if (value == null) - obj = null; - else if ((value is long && type == typeof(long)) || (value is double && type == typeof(double))) - obj = value; - else if ((value is double && type != typeof(double)) || (value is long && type != typeof(long))) - { - obj = -#if NETFX_CORE - type == typeof(int) || type == typeof(long) || type == typeof(double) ||type == typeof(float) || type == typeof(bool) || type == typeof(decimal) ||type == typeof(byte) || type == typeof(short) -#else - typeof(IConvertible).IsAssignableFrom(type) -#endif - ? Convert.ChangeType(value, type, CultureInfo.InvariantCulture) : value; - } - else - { - if (value is IDictionary) - { - IDictionary jsonObject = (IDictionary)value; - - if (ReflectionUtils.IsTypeDictionary(type)) - { - // if dictionary then -#if NETFX_CORE - Type keyType = type.GetTypeInfo().GenericTypeArguments[0]; - Type valueType = type.GetTypeInfo().GenericTypeArguments[1]; -#else - Type keyType = type.GetGenericArguments()[0]; - Type valueType = type.GetGenericArguments()[1]; -#endif - - Type genericType = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); - -#if NETFX_CORE - dynamic dict = CacheResolver.GetNewInstance(genericType); -#else - IDictionary dict = (IDictionary)CacheResolver.GetNewInstance(genericType); -#endif - foreach (KeyValuePair kvp in jsonObject) - { - dict.Add(kvp.Key, DeserializeObject(kvp.Value, valueType)); - } - - obj = dict; - } - else - { - obj = CacheResolver.GetNewInstance(type); - SafeDictionary maps = CacheResolver.LoadMaps(type); - - if (maps == null) - { - obj = value; - } - else - { - foreach (KeyValuePair keyValuePair in maps) - { - CacheResolver.MemberMap v = keyValuePair.Value; - if (v.Setter == null) - continue; - - string jsonKey = keyValuePair.Key; - if (jsonObject.ContainsKey(jsonKey)) - { - object jsonValue = DeserializeObject(jsonObject[jsonKey], v.Type); - v.Setter(obj, jsonValue); - } - } - } - } - } - else if (value is IList) - { - IList jsonObject = (IList)value; - IList list = null; - - if (type.IsArray) - { - list = (IList)Activator.CreateInstance(type, jsonObject.Count); - int i = 0; - foreach (object o in jsonObject) - list[i++] = DeserializeObject(o, type.GetElementType()); - } - else if (ReflectionUtils.IsTypeGenericeCollectionInterface(type) || -#if NETFX_CORE - typeof(IList).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo()) -#else - typeof(IList).IsAssignableFrom(type) -#endif -) - { -#if NETFX_CORE - Type innerType = type.GetTypeInfo().GenericTypeArguments[0]; -#else - Type innerType = type.GetGenericArguments()[0]; -#endif - Type genericType = typeof(List<>).MakeGenericType(innerType); - list = (IList)CacheResolver.GetNewInstance(genericType); - foreach (object o in jsonObject) - list.Add(DeserializeObject(o, innerType)); - } - - obj = list; - } - - return obj; - } - - if (ReflectionUtils.IsNullableType(type)) - return ReflectionUtils.ToNullableType(obj, type); - - if (obj == null) - { - if (type == typeof(Guid)) - return default(Guid); - } - - return obj; - } - - protected virtual object SerializeEnum(Enum p) - { - return Convert.ToDouble(p, CultureInfo.InvariantCulture); - } - - protected virtual bool TrySerializeKnownTypes(object input, out object output) - { - bool returnValue = true; - if (input is DateTime) - output = ((DateTime)input).ToUniversalTime().ToString(Iso8601Format[0], CultureInfo.InvariantCulture); - else if (input is Guid) - output = ((Guid)input).ToString("D"); - else if (input is Uri) - output = input.ToString(); - else if (input is Enum) - output = SerializeEnum((Enum)input); - else - { - returnValue = false; - output = null; - } - - return returnValue; - } - - protected virtual bool TrySerializeUnknownTypes(object input, out object output) - { - output = null; - - // todo: implement caching for types - Type type = input.GetType(); - - if (type.FullName == null) - return false; - - IDictionary obj = new JsonObject(); - - SafeDictionary maps = CacheResolver.LoadMaps(type); - - foreach (KeyValuePair keyValuePair in maps) - { - if (keyValuePair.Value.Getter != null) - obj.Add(keyValuePair.Key, keyValuePair.Value.Getter(input)); - } - - output = obj; - return true; - } - } - -#if SIMPLE_JSON_DATACONTRACT -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - class DataContractJsonSerializerStrategy : PocoJsonSerializerStrategy - { - public DataContractJsonSerializerStrategy() - { - CacheResolver = new CacheResolver(BuildMap); - } - - protected override void BuildMap(Type type, SafeDictionary map) - { - bool hasDataContract = ReflectionUtils.GetAttribute(type, typeof(DataContractAttribute)) != null; - if (!hasDataContract) - { - base.BuildMap(type, map); - return; - } - - string jsonKey; -#if NETFX_CORE - foreach (PropertyInfo info in type.GetTypeInfo().DeclaredProperties) -#else - foreach (PropertyInfo info in type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) -#endif - { - if (CanAdd(info, out jsonKey)) - map.Add(jsonKey, new CacheResolver.MemberMap(info)); - } - -#if NETFX_CORE - foreach (FieldInfo info in type.GetTypeInfo().DeclaredFields) -#else - foreach (FieldInfo info in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) -#endif - { - if (CanAdd(info, out jsonKey)) - map.Add(jsonKey, new CacheResolver.MemberMap(info)); - } - - // todo implement sorting for DATACONTRACT. - } - - private static bool CanAdd(MemberInfo info, out string jsonKey) - { - jsonKey = null; - - if (ReflectionUtils.GetAttribute(info, typeof(IgnoreDataMemberAttribute)) != null) - return false; - - DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)ReflectionUtils.GetAttribute(info, typeof(DataMemberAttribute)); - - if (dataMemberAttribute == null) - return false; - - jsonKey = string.IsNullOrEmpty(dataMemberAttribute.Name) ? info.Name : dataMemberAttribute.Name; - return true; - } - } -#endif - - #endregion - - #region Reflection helpers - - namespace Reflection - { -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - class ReflectionUtils - { - public static Attribute GetAttribute(MemberInfo info, Type type) - { -#if NETFX_CORE - if (info == null || type == null || !info.IsDefined(type)) - return null; - return info.GetCustomAttribute(type); -#else - if (info == null || type == null || !Attribute.IsDefined(info, type)) - return null; - return Attribute.GetCustomAttribute(info, type); -#endif - } - - public static Attribute GetAttribute(Type objectType, Type attributeType) - { - -#if NETFX_CORE - if (objectType == null || attributeType == null || !objectType.GetTypeInfo().IsDefined(attributeType)) - return null; - return objectType.GetTypeInfo().GetCustomAttribute(attributeType); -#else - if (objectType == null || attributeType == null || !Attribute.IsDefined(objectType, attributeType)) - return null; - return Attribute.GetCustomAttribute(objectType, attributeType); -#endif - } - - public static bool IsTypeGenericeCollectionInterface(Type type) - { -#if NETFX_CORE - if (!type.GetTypeInfo().IsGenericType) -#else - if (!type.IsGenericType) -#endif - return false; - - Type genericDefinition = type.GetGenericTypeDefinition(); - - return (genericDefinition == typeof(IList<>) || genericDefinition == typeof(ICollection<>) || genericDefinition == typeof(IEnumerable<>)); - } - - public static bool IsTypeDictionary(Type type) - { -#if NETFX_CORE - if (typeof(IDictionary<,>).GetTypeInfo().IsAssignableFrom(type.GetTypeInfo())) - return true; - - if (!type.GetTypeInfo().IsGenericType) - return false; -#else - if (typeof(IDictionary).IsAssignableFrom(type)) - return true; - - if (!type.IsGenericType) - return false; -#endif - Type genericDefinition = type.GetGenericTypeDefinition(); - return genericDefinition == typeof(IDictionary<,>); - } - - public static bool IsNullableType(Type type) - { - return -#if NETFX_CORE - type.GetTypeInfo().IsGenericType -#else - type.IsGenericType -#endif - && type.GetGenericTypeDefinition() == typeof(Nullable<>); - } - - public static object ToNullableType(object obj, Type nullableType) - { - return obj == null ? null : Convert.ChangeType(obj, Nullable.GetUnderlyingType(nullableType), CultureInfo.InvariantCulture); - } - } - -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - delegate object GetHandler(object source); - -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - delegate void SetHandler(object source, object value); - -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - delegate void MemberMapLoader(Type type, SafeDictionary memberMaps); - -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - class CacheResolver - { - private readonly MemberMapLoader _memberMapLoader; - private readonly SafeDictionary> _memberMapsCache = new SafeDictionary>(); - - delegate object CtorDelegate(); - readonly static SafeDictionary ConstructorCache = new SafeDictionary(); - - public CacheResolver(MemberMapLoader memberMapLoader) - { - _memberMapLoader = memberMapLoader; - } - - [SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")] - public static object GetNewInstance(Type type) - { - CtorDelegate c; - if (ConstructorCache.TryGetValue(type, out c)) - return c(); -#if SIMPLE_JSON_REFLECTIONEMIT - DynamicMethod dynamicMethod = new DynamicMethod("Create" + type.FullName, typeof(object), Type.EmptyTypes, type, true); - dynamicMethod.InitLocals = true; - ILGenerator generator = dynamicMethod.GetILGenerator(); - if (type.IsValueType) - { - generator.DeclareLocal(type); - generator.Emit(OpCodes.Ldloc_0); - generator.Emit(OpCodes.Box, type); - } - else - { - ConstructorInfo constructorInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null); - if (constructorInfo == null) - throw new Exception(string.Format(CultureInfo.InvariantCulture, "Could not get constructor for {0}.", type)); - generator.Emit(OpCodes.Newobj, constructorInfo); - } - generator.Emit(OpCodes.Ret); - c = (CtorDelegate)dynamicMethod.CreateDelegate(typeof(CtorDelegate)); - ConstructorCache.Add(type, c); - return c(); -#else -#if NETFX_CORE - IEnumerable constructorInfos = type.GetTypeInfo().DeclaredConstructors; - ConstructorInfo constructorInfo = null; - foreach (ConstructorInfo item in constructorInfos) // FirstOrDefault() - { - if (item.GetParameters().Length == 0) // Default ctor - make sure it doesn't contain any parameters - { - constructorInfo = item; - break; - } - } -#else - ConstructorInfo constructorInfo = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, Type.EmptyTypes, null); -#endif - c = delegate { return constructorInfo.Invoke(null); }; - ConstructorCache.Add(type, c); - return c(); -#endif - } - - public SafeDictionary LoadMaps(Type type) - { - if (type == null || type == typeof(object)) - return null; - SafeDictionary maps; - if (_memberMapsCache.TryGetValue(type, out maps)) - return maps; - maps = new SafeDictionary(); - _memberMapLoader(type, maps); - _memberMapsCache.Add(type, maps); - return maps; - } - -#if SIMPLE_JSON_REFLECTIONEMIT - static DynamicMethod CreateDynamicMethod(string name, Type returnType, Type[] parameterTypes, Type owner) - { - DynamicMethod dynamicMethod = !owner.IsInterface - ? new DynamicMethod(name, returnType, parameterTypes, owner, true) - : new DynamicMethod(name, returnType, parameterTypes, (Module)null, true); - - return dynamicMethod; - } -#endif - - static GetHandler CreateGetHandler(FieldInfo fieldInfo) - { -#if SIMPLE_JSON_REFLECTIONEMIT - Type type = fieldInfo.FieldType; - DynamicMethod dynamicGet = CreateDynamicMethod("Get" + fieldInfo.Name, fieldInfo.DeclaringType, new Type[] { typeof(object) }, fieldInfo.DeclaringType); - ILGenerator getGenerator = dynamicGet.GetILGenerator(); - - getGenerator.Emit(OpCodes.Ldarg_0); - getGenerator.Emit(OpCodes.Ldfld, fieldInfo); - if (type.IsValueType) - getGenerator.Emit(OpCodes.Box, type); - getGenerator.Emit(OpCodes.Ret); - - return (GetHandler)dynamicGet.CreateDelegate(typeof(GetHandler)); -#else - return delegate(object instance) { return fieldInfo.GetValue(instance); }; -#endif - } - - static SetHandler CreateSetHandler(FieldInfo fieldInfo) - { - if (fieldInfo.IsInitOnly || fieldInfo.IsLiteral) - return null; -#if SIMPLE_JSON_REFLECTIONEMIT - Type type = fieldInfo.FieldType; - DynamicMethod dynamicSet = CreateDynamicMethod("Set" + fieldInfo.Name, null, new Type[] { typeof(object), typeof(object) }, fieldInfo.DeclaringType); - ILGenerator setGenerator = dynamicSet.GetILGenerator(); - - setGenerator.Emit(OpCodes.Ldarg_0); - setGenerator.Emit(OpCodes.Ldarg_1); - if (type.IsValueType) - setGenerator.Emit(OpCodes.Unbox_Any, type); - setGenerator.Emit(OpCodes.Stfld, fieldInfo); - setGenerator.Emit(OpCodes.Ret); - - return (SetHandler)dynamicSet.CreateDelegate(typeof(SetHandler)); -#else - return delegate(object instance, object value) { fieldInfo.SetValue(instance, value); }; -#endif - } - - static GetHandler CreateGetHandler(PropertyInfo propertyInfo) - { -#if NETFX_CORE - MethodInfo getMethodInfo = propertyInfo.GetMethod; -#else - MethodInfo getMethodInfo = propertyInfo.GetGetMethod(true); -#endif - if (getMethodInfo == null) - return null; -#if SIMPLE_JSON_REFLECTIONEMIT - Type type = propertyInfo.PropertyType; - DynamicMethod dynamicGet = CreateDynamicMethod("Get" + propertyInfo.Name, propertyInfo.DeclaringType, new Type[] { typeof(object) }, propertyInfo.DeclaringType); - ILGenerator getGenerator = dynamicGet.GetILGenerator(); - - getGenerator.Emit(OpCodes.Ldarg_0); - getGenerator.Emit(OpCodes.Call, getMethodInfo); - if (type.IsValueType) - getGenerator.Emit(OpCodes.Box, type); - getGenerator.Emit(OpCodes.Ret); - - return (GetHandler)dynamicGet.CreateDelegate(typeof(GetHandler)); -#else -#if NETFX_CORE - return delegate(object instance) { return getMethodInfo.Invoke(instance, new Type[] { }); }; -#else - return delegate(object instance) { return getMethodInfo.Invoke(instance, Type.EmptyTypes); }; -#endif -#endif - } - - static SetHandler CreateSetHandler(PropertyInfo propertyInfo) - { -#if NETFX_CORE - MethodInfo setMethodInfo = propertyInfo.SetMethod; -#else - MethodInfo setMethodInfo = propertyInfo.GetSetMethod(true); -#endif - if (setMethodInfo == null) - return null; -#if SIMPLE_JSON_REFLECTIONEMIT - Type type = propertyInfo.PropertyType; - DynamicMethod dynamicSet = CreateDynamicMethod("Set" + propertyInfo.Name, null, new Type[] { typeof(object), typeof(object) }, propertyInfo.DeclaringType); - ILGenerator setGenerator = dynamicSet.GetILGenerator(); - - setGenerator.Emit(OpCodes.Ldarg_0); - setGenerator.Emit(OpCodes.Ldarg_1); - if (type.IsValueType) - setGenerator.Emit(OpCodes.Unbox_Any, type); - setGenerator.Emit(OpCodes.Call, setMethodInfo); - setGenerator.Emit(OpCodes.Ret); - return (SetHandler)dynamicSet.CreateDelegate(typeof(SetHandler)); -#else - return delegate(object instance, object value) { setMethodInfo.Invoke(instance, new[] { value }); }; -#endif - } - -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - sealed class MemberMap - { - public readonly MemberInfo MemberInfo; - public readonly Type Type; - public readonly GetHandler Getter; - public readonly SetHandler Setter; - - public MemberMap(PropertyInfo propertyInfo) - { - MemberInfo = propertyInfo; - Type = propertyInfo.PropertyType; - Getter = CreateGetHandler(propertyInfo); - Setter = CreateSetHandler(propertyInfo); - } - - public MemberMap(FieldInfo fieldInfo) - { - MemberInfo = fieldInfo; - Type = fieldInfo.FieldType; - Getter = CreateGetHandler(fieldInfo); - Setter = CreateSetHandler(fieldInfo); - } - } - } - -#if SIMPLE_JSON_INTERNAL - internal -#else - public -#endif - class SafeDictionary - { - private readonly object _padlock = new object(); - private readonly Dictionary _dictionary = new Dictionary(); - - public bool TryGetValue(TKey key, out TValue value) - { - return _dictionary.TryGetValue(key, out value); - } - - public TValue this[TKey key] - { - get { return _dictionary[key]; } - } - - public IEnumerator> GetEnumerator() - { - return ((ICollection>)_dictionary).GetEnumerator(); - } - - public void Add(TKey key, TValue value) - { - lock (_padlock) - { - if (_dictionary.ContainsKey(key) == false) - _dictionary.Add(key, value); - } - } - } - } - - #endregion -} \ No newline at end of file diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index bbb7a9c2..8f2e6eaa 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -10,6 +10,9 @@ using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + using Shadowsocks.Model; using Shadowsocks.Util; @@ -32,7 +35,7 @@ namespace Shadowsocks.Controller public static readonly DateTime UnknownDateTime = new DateTime(1970, 1, 1); private int Repeat => _config.RepeatTimesNum; private const int RetryInterval = 2 * 60 * 1000; //retry 2 minutes after failed - private int Interval => (int)TimeSpan.FromMinutes(_config.DataCollectionMinutes).TotalMilliseconds; + private int Interval => (int)TimeSpan.FromMinutes(_config.DataCollectionMinutes).TotalMilliseconds; private Timer _timer; private State _state; private List _servers; @@ -107,11 +110,18 @@ namespace Shadowsocks.Controller Logging.LogUsefulException(e); return null; } - dynamic obj; - if (!SimpleJson.SimpleJson.TryDeserializeObject(jsonString, out obj)) return null; - string country = obj["country"]; - string city = obj["city"]; - string isp = obj["isp"]; + JObject obj; + try + { + obj = JObject.Parse(jsonString); + } + catch (JsonReaderException) + { + return null; + } + string country = (string)obj["country"]; + string city = (string)obj["city"]; + string isp = (string)obj["isp"]; if (country == null || city == null || isp == null) return null; return new DataList { new DataUnit(State.Geolocation, $"\"{country} {city}\""), @@ -252,24 +262,24 @@ namespace Shadowsocks.Controller _timer.Change(RetryInterval, Interval); return; } - RawStatistics = (from l in File.ReadAllLines(path).Skip(1) - let strings = l.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) - let rawData = new RawStatisticsData - { - Timestamp = ParseExactOrUnknown(strings[0]), - ServerName = strings[1], - ICMPStatus = strings[2], - RoundtripTime = int.Parse(strings[3]), - Geolocation = 5 > strings.Length ? - null - : strings[4], - ISP = 6 > strings.Length ? null : strings[5] - } - group rawData by rawData.ServerName into server - select new - { - ServerName = server.Key, - data = server.ToList() + RawStatistics = (from l in File.ReadAllLines(path).Skip(1) + let strings = l.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) + let rawData = new RawStatisticsData + { + Timestamp = ParseExactOrUnknown(strings[0]), + ServerName = strings[1], + ICMPStatus = strings[2], + RoundtripTime = int.Parse(strings[3]), + Geolocation = 5 > strings.Length ? + null + : strings[4], + ISP = 6 > strings.Length ? null : strings[5] + } + group rawData by rawData.ServerName into server + select new + { + ServerName = server.Key, + data = server.ToList() }).ToDictionary(server => server.ServerName, server => server.data); } catch (Exception e) diff --git a/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs b/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs index e0fd12f4..344726aa 100644 --- a/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs +++ b/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs @@ -4,6 +4,8 @@ using System.IO; using System.Net; using System.Text; +using Newtonsoft.Json; + using Shadowsocks.Model; using Shadowsocks.Properties; using Shadowsocks.Util; @@ -60,8 +62,8 @@ namespace Shadowsocks.Controller { abpContent = Utils.UnGzip(Resources.abp_js); } - abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines)); - if (File.Exists(PAC_FILE)) + abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented)); + if (File.Exists(PACServer.PAC_FILE)) { string original = File.ReadAllText(PAC_FILE, Encoding.UTF8); if (original == abpContent) diff --git a/shadowsocks-csharp/Controller/Service/UpdateChecker.cs b/shadowsocks-csharp/Controller/Service/UpdateChecker.cs index 59ff3cf6..da8a5f83 100644 --- a/shadowsocks-csharp/Controller/Service/UpdateChecker.cs +++ b/shadowsocks-csharp/Controller/Service/UpdateChecker.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Net; using System.Text.RegularExpressions; -using SimpleJson; +using Newtonsoft.Json.Linq; using Shadowsocks.Model; using Shadowsocks.Util; @@ -23,7 +23,7 @@ namespace Shadowsocks.Controller public string LatestVersionLocalName; public event EventHandler CheckUpdateCompleted; - public const string Version = "2.5.8"; + public const string Version = "2.5.8.1"; private class CheckUpdateTimer : System.Timers.Timer { @@ -76,26 +76,28 @@ namespace Shadowsocks.Controller { string response = e.Result; - JsonArray result = (JsonArray)SimpleJson.SimpleJson.DeserializeObject(e.Result); + JArray result = JArray.Parse(response); List asserts = new List(); - foreach (JsonObject release in result) + if (result != null) { - if ((bool)release["prerelease"]) + foreach (JObject release in result) { - continue; - } - foreach (JsonObject asset in (JsonArray)release["assets"]) - { - Asset ass = new Asset(); - ass.Parse(asset); - if (ass.IsNewVersion(Version)) + if ((bool)release["prerelease"]) { - asserts.Add(ass); + continue; + } + foreach (JObject asset in (JArray)release["assets"]) + { + Asset ass = new Asset(); + ass.Parse(asset); + if (ass.IsNewVersion(Version)) + { + asserts.Add(ass); + } } } } - if (asserts.Count != 0) { SortByVersions(asserts); @@ -191,7 +193,7 @@ namespace Shadowsocks.Controller return CompareVersion(version, currentVersion) > 0; } - public void Parse(JsonObject asset) + public void Parse(JObject asset) { name = (string)asset["name"]; browser_download_url = (string)asset["browser_download_url"]; diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index 1dfd81d2..d4282fdd 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -6,6 +6,8 @@ using System.Net.Sockets; using System.Text; using System.Threading; +using Newtonsoft.Json; + using Shadowsocks.Controller.Strategy; using Shadowsocks.Model; using Shadowsocks.Properties; diff --git a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs index d3b5e26b..df34c994 100644 --- a/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs +++ b/shadowsocks-csharp/Controller/Strategy/StatisticsStrategy.cs @@ -4,7 +4,9 @@ using System.Linq; using System.Net; using System.Net.NetworkInformation; using System.Threading; + using Newtonsoft.Json; + using Shadowsocks.Model; namespace Shadowsocks.Controller.Strategy @@ -147,6 +149,5 @@ namespace Shadowsocks.Controller.Strategy { //TODO: combine this part of data with ICMP statics } - } } diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs index c62ac090..66fab2f4 100755 --- a/shadowsocks-csharp/Model/Configuration.cs +++ b/shadowsocks-csharp/Model/Configuration.cs @@ -1,10 +1,9 @@ -using Shadowsocks.Controller; -using System; +using System; using System.Collections.Generic; using System.IO; -using System.Text; -using System.Windows.Forms; -using SimpleJson; + +using Shadowsocks.Controller; +using Newtonsoft.Json; namespace Shadowsocks.Model { @@ -53,7 +52,7 @@ namespace Shadowsocks.Model try { string configContent = File.ReadAllText(CONFIG_FILE); - Configuration config = SimpleJson.SimpleJson.DeserializeObject(configContent, new JsonSerializerStrategy()); + Configuration config = JsonConvert.DeserializeObject(configContent); config.isDefault = false; if (config.localPort == 0) { @@ -110,7 +109,7 @@ namespace Shadowsocks.Model { using (StreamWriter sw = new StreamWriter(File.Open(CONFIG_FILE, FileMode.Create))) { - string jsonString = SimpleJson.SimpleJson.SerializeObject(config); + string jsonString = JsonConvert.SerializeObject(config, Formatting.Indented); sw.Write(jsonString); sw.Flush(); } @@ -166,19 +165,5 @@ namespace Shadowsocks.Model throw new ArgumentException(I18N.GetString("Server IP can not be blank")); } } - - // internal class - private class JsonSerializerStrategy : SimpleJson.PocoJsonSerializerStrategy - { - // convert string to int - public override object DeserializeObject(object value, Type type) - { - if (type == typeof(Int32) && value.GetType() == typeof(string)) - { - return Int32.Parse(value.ToString()); - } - return base.DeserializeObject(value, type); - } - } } } diff --git a/shadowsocks-csharp/Model/Server.cs b/shadowsocks-csharp/Model/Server.cs index 24f4f2d6..e21f3576 100755 --- a/shadowsocks-csharp/Model/Server.cs +++ b/shadowsocks-csharp/Model/Server.cs @@ -1,12 +1,9 @@ using System; -using System.Collections.Generic; using System.Text; -using System.IO; -using System.Diagnostics; -using SimpleJson; -using Shadowsocks.Controller; using System.Text.RegularExpressions; +using Shadowsocks.Controller; + namespace Shadowsocks.Model { [Serializable] diff --git a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs index 028f1099..aae33546 100644 --- a/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs +++ b/shadowsocks-csharp/View/StatisticsStrategyConfigurationForm.cs @@ -2,16 +2,15 @@ using System.Collections.Generic; using System.Data; using System.Linq; +using System.Net.NetworkInformation; using System.Windows.Forms; -using System.Windows.Forms.DataVisualization.Charting; + using Shadowsocks.Controller; using Shadowsocks.Model; -using SimpleJson; -using System.Net.NetworkInformation; namespace Shadowsocks.View { - public partial class StatisticsStrategyConfigurationForm: Form + public partial class StatisticsStrategyConfigurationForm : Form { private readonly ShadowsocksController _controller; private StatisticsStrategyConfiguration _configuration; @@ -51,9 +50,9 @@ namespace Shadowsocks.View serverSelector.DataSource = _servers; - _dataTable.Columns.Add("Timestamp", typeof (DateTime)); - _dataTable.Columns.Add("Package Loss", typeof (int)); - _dataTable.Columns.Add("Ping", typeof (int)); + _dataTable.Columns.Add("Timestamp", typeof(DateTime)); + _dataTable.Columns.Add("Package Loss", typeof(int)); + _dataTable.Columns.Add("Ping", typeof(int)); StatisticsChart.Series["Package Loss"].XValueMember = "Timestamp"; StatisticsChart.Series["Package Loss"].YValueMembers = "Package Loss"; @@ -64,7 +63,6 @@ namespace Shadowsocks.View StatisticsChart.DataBind(); } - private void CancelButton_Click(object sender, EventArgs e) { Close(); diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index cd85c6a3..8ce00215 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -48,6 +48,7 @@ prompt ManagedMinimumRules.ruleset false + false bin\x86\Release\ @@ -169,7 +170,6 @@ - From d2bb2c4a7db40f183f84ffd99c534fc62c7a005d Mon Sep 17 00:00:00 2001 From: kimw Date: Tue, 5 Jan 2016 03:48:55 +0800 Subject: [PATCH 42/54] fixed don't update abp rules while event user-rule file changed. in fact, the PAC will overwrite with gfwlist & use-rule but abp. --- .../Controller/ShadowsocksController.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index d4282fdd..b67081d5 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -462,8 +462,16 @@ namespace Shadowsocks.Controller lines.Add(rule); } } - string abpContent = Utils.UnGzip(Resources.abp_js); - abpContent = abpContent.Replace("__RULES__", SimpleJson.SimpleJson.SerializeObject(lines)); + string abpContent; + if (File.Exists(PACServer.USER_ABP_FILE)) + { + abpContent = File.ReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8); + } + else + { + abpContent = Utils.UnGzip(Resources.abp_js); + } + abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented)); if (File.Exists(PACServer.PAC_FILE)) { string original = File.ReadAllText(PACServer.PAC_FILE, Encoding.UTF8); From 22accf4a90257fd0a7f63fd91be745cef278ecaa Mon Sep 17 00:00:00 2001 From: kimw Date: Tue, 5 Jan 2016 03:49:57 +0800 Subject: [PATCH 43/54] reformated source code --- .../Controller/Service/GfwListUpdater.cs | 18 +++----- .../Controller/Service/PACServer.cs | 42 +++++++++---------- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs b/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs index 344726aa..2497090d 100644 --- a/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs +++ b/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs @@ -16,12 +16,6 @@ namespace Shadowsocks.Controller { private const string GFWLIST_URL = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt"; - private static string PAC_FILE = PACServer.PAC_FILE; - - private static string USER_RULE_FILE = PACServer.USER_RULE_FILE; - - private static string USER_ABP_FILE = PACServer.USER_ABP_FILE; - public event EventHandler UpdateCompleted; public event ErrorEventHandler Error; @@ -42,9 +36,9 @@ namespace Shadowsocks.Controller { File.WriteAllText(Utils.GetTempPath("gfwlist.txt"), e.Result, Encoding.UTF8); List lines = ParseResult(e.Result); - if (File.Exists(USER_RULE_FILE)) + if (File.Exists(PACServer.USER_RULE_FILE)) { - string local = File.ReadAllText(USER_RULE_FILE, Encoding.UTF8); + string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8); string[] rules = local.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); foreach (string rule in rules) { @@ -54,9 +48,9 @@ namespace Shadowsocks.Controller } } string abpContent; - if (File.Exists(USER_ABP_FILE)) + if (File.Exists(PACServer.USER_ABP_FILE)) { - abpContent = File.ReadAllText(USER_ABP_FILE, Encoding.UTF8); + abpContent = File.ReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8); } else { @@ -65,14 +59,14 @@ namespace Shadowsocks.Controller abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented)); if (File.Exists(PACServer.PAC_FILE)) { - string original = File.ReadAllText(PAC_FILE, Encoding.UTF8); + string original = File.ReadAllText(PACServer.PAC_FILE, Encoding.UTF8); if (original == abpContent) { UpdateCompleted(this, new ResultEventArgs(false)); return; } } - File.WriteAllText(PAC_FILE, abpContent, Encoding.UTF8); + File.WriteAllText(PACServer.PAC_FILE, abpContent, Encoding.UTF8); if (UpdateCompleted != null) { UpdateCompleted(this, new ResultEventArgs(true)); diff --git a/shadowsocks-csharp/Controller/Service/PACServer.cs b/shadowsocks-csharp/Controller/Service/PACServer.cs index ac8b762a..5db8b64a 100644 --- a/shadowsocks-csharp/Controller/Service/PACServer.cs +++ b/shadowsocks-csharp/Controller/Service/PACServer.cs @@ -1,23 +1,21 @@ -using Shadowsocks.Model; -using Shadowsocks.Properties; -using Shadowsocks.Util; -using System; +using System; using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; using System.IO; -using System.IO.Compression; using System.Net; using System.Net.Sockets; using System.Text; +using Shadowsocks.Model; +using Shadowsocks.Properties; +using Shadowsocks.Util; + namespace Shadowsocks.Controller { class PACServer : Listener.Service { - public static string PAC_FILE = "pac.txt"; - public static string USER_RULE_FILE = "user-rule.txt"; - public static string USER_ABP_FILE = "abp.txt"; + public static readonly string PAC_FILE = "pac.txt"; + public static readonly string USER_RULE_FILE = "user-rule.txt"; + public static readonly string USER_ABP_FILE = "abp.txt"; FileSystemWatcher PACFileWatcher; FileSystemWatcher UserRuleFileWatcher; @@ -50,7 +48,7 @@ namespace Shadowsocks.Controller bool hostMatch = false, pathMatch = false, useSocks = false; foreach (string line in lines) { - string[] kv = line.Split(new char[]{':'}, 2); + string[] kv = line.Split(new char[] { ':' }, 2); if (kv.Length == 2) { if (kv[0] == "Host") @@ -60,14 +58,14 @@ namespace Shadowsocks.Controller hostMatch = true; } } - else if (kv[0] == "User-Agent") - { - // we need to drop connections when changing servers - /* if (kv[1].IndexOf("Chrome") >= 0) - { - useSocks = true; - } */ - } + //else if (kv[0] == "User-Agent") + //{ + // // we need to drop connections when changing servers + // if (kv[1].IndexOf("Chrome") >= 0) + // { + // useSocks = true; + // } + //} } else if (kv.Length == 1) { @@ -202,7 +200,6 @@ Connection: Close } #region FileSystemWatcher.OnChanged() - // FileSystemWatcher Changed event is raised twice // http://stackoverflow.com/questions/1764809/filesystemwatcher-changed-event-is-raised-twice private static Hashtable fileChangedTime = new Hashtable(); @@ -221,7 +218,7 @@ Connection: Close PACFileChanged(this, new EventArgs()); } - //lastly we update the last write time in the hashtable + // lastly we update the last write time in the hashtable fileChangedTime[path] = currentLastWriteTime; } } @@ -239,11 +236,10 @@ Connection: Close Logging.Info($"Detected: User Rule file '{e.Name}' was {e.ChangeType.ToString().ToLower()}."); UserRuleFileChanged(this, new EventArgs()); } - //lastly we update the last write time in the hashtable + // lastly we update the last write time in the hashtable fileChangedTime[path] = currentLastWriteTime; } } - #endregion private string GetPACAddress(byte[] requestBuf, int length, IPEndPoint localEndPoint, bool useSocks) From 6f38bbb1d39579320f89c795d889a3171618a6b4 Mon Sep 17 00:00:00 2001 From: kimw Date: Tue, 5 Jan 2016 04:22:15 +0800 Subject: [PATCH 44/54] fixed 0 bytes will not show up, and, corrected function name --- shadowsocks-csharp/Util/Util.cs | 4 ++-- shadowsocks-csharp/View/LogForm.cs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/shadowsocks-csharp/Util/Util.cs b/shadowsocks-csharp/Util/Util.cs index 0fe193c7..178c994b 100755 --- a/shadowsocks-csharp/Util/Util.cs +++ b/shadowsocks-csharp/Util/Util.cs @@ -98,7 +98,7 @@ namespace Shadowsocks.Util } } - public static string FormatBandwide(long n) + public static string FormatBandwidth(long n) { float f = n; string unit = "B"; @@ -122,7 +122,7 @@ namespace Shadowsocks.Util f = f / 1024; unit = "TiB"; } - return $"{f:.##}{unit}"; + return $"{f:0.##}{unit}"; } [DllImport("kernel32.dll")] diff --git a/shadowsocks-csharp/View/LogForm.cs b/shadowsocks-csharp/View/LogForm.cs index cf3f1732..d49cd1af 100644 --- a/shadowsocks-csharp/View/LogForm.cs +++ b/shadowsocks-csharp/View/LogForm.cs @@ -109,7 +109,8 @@ namespace Shadowsocks.View lastOffset = reader.BaseStream.Position; } - this.Text = $"Log Viewer [in: {Utils.FormatBandwide(controller.inboundCounter)}, out: {Utils.FormatBandwide(controller.outboundCounter)}]"; + this.Text = I18N.GetString("Log Viewer") + + $" [in: {Utils.FormatBandwidth(controller.inboundCounter)}, out: {Utils.FormatBandwidth(controller.outboundCounter)}]"; } private void LogForm_Load(object sender, EventArgs e) From b918cb85aefd1b4fbce56ba9cd81696690bd5bc8 Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Tue, 5 Jan 2016 01:54:42 -0500 Subject: [PATCH 45/54] tiny fix --- shadowsocks-csharp/Controller/Service/TCPRelay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index 4944c44c..2fd3c149 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -587,7 +587,6 @@ namespace Shadowsocks.Controller { int bytesRead = connection.EndReceive(ar); totalWrite += bytesRead; - tcprelay.UpdateOutboundCounter(bytesRead); if (bytesRead > 0) { @@ -601,6 +600,7 @@ namespace Shadowsocks.Controller encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); } Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeConnectionReceiveCallback() (upload)"); + tcprelay.UpdateOutboundCounter(bytesToSend); remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); IStrategy strategy = controller.GetCurrentStrategy(); From 12ba488c7bac56753f575c86e6c7fd64d8f171bc Mon Sep 17 00:00:00 2001 From: Gang Zhuo Date: Tue, 5 Jan 2016 21:42:57 -0500 Subject: [PATCH 46/54] atomic operations to increase traffic statistics --- shadowsocks-csharp/Controller/ShadowsocksController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index b67081d5..982d6237 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -309,12 +309,12 @@ namespace Shadowsocks.Controller public void UpdateInboundCounter(long n) { - inboundCounter += n; + Interlocked.Add(ref inboundCounter, n); } public void UpdateOutboundCounter(long n) { - outboundCounter += n; + Interlocked.Add(ref outboundCounter, n); } protected void Reload() From af3fe3fc9220f7457687c6954a296ee156b07037 Mon Sep 17 00:00:00 2001 From: kimw Date: Sun, 10 Jan 2016 04:37:56 +0800 Subject: [PATCH 47/54] upgraded 3rd packages: Fody 1.29.3 => 1.29.4 Microsoft.Bcl 1.1.8 => 1.1.10 Microsoft.Bcl.Build 1.0.14 => 1.0.21 Newtonsoft.Json 7.0.1 => 8.0.2 --- shadowsocks-csharp/app.config | 4 +-- shadowsocks-csharp/packages.config | 20 ++++++------ shadowsocks-csharp/shadowsocks-csharp.csproj | 33 +++++++------------- 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/shadowsocks-csharp/app.config b/shadowsocks-csharp/app.config index a7ba1069..c96d7c75 100755 --- a/shadowsocks-csharp/app.config +++ b/shadowsocks-csharp/app.config @@ -8,11 +8,11 @@ - + - + diff --git a/shadowsocks-csharp/packages.config b/shadowsocks-csharp/packages.config index cd42cb7e..2230519e 100644 --- a/shadowsocks-csharp/packages.config +++ b/shadowsocks-csharp/packages.config @@ -1,11 +1,11 @@ - - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 8ce00215..69f7b546 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -69,21 +69,18 @@ 3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll True - False 3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll True - False 3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll True - False - - 3rd\Newtonsoft.Json.7.0.1\lib\net40\Newtonsoft.Json.dll + + 3rd\Newtonsoft.Json.8.0.2\lib\net40\Newtonsoft.Json.dll True @@ -91,10 +88,9 @@ - - 3rd\Microsoft.Bcl.1.1.8\lib\net40\System.IO.dll + + 3rd\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll True - False @@ -105,15 +101,13 @@ 3rd\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll True - - 3rd\Microsoft.Bcl.1.1.8\lib\net40\System.Runtime.dll + + 3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll True - False - - 3rd\Microsoft.Bcl.1.1.8\lib\net40\System.Threading.Tasks.dll + + 3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll True - False @@ -330,18 +324,15 @@ - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + + + +