Compare commits

..
20 Commits
Author SHA1 Message Date
Syrone Wong e9ba34373b Update CHANGES and bump version
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2017-01-11 14:27:24 +08:00
Syrone Wong 4c49f1b736 Make the previous portable mode as default
- change dir name to "ss_win_temp" to make things clear.
- throw any exception caught when creating temp folder

To workaround silly cleaning softwares and careless users.

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2017-01-11 14:05:11 +08:00
破娃酱 b266bf42f5 create "help" menu group (#989) 2017-01-08 08:44:49 +08:00
Noisyfox 2e7518d802 Send first chunk with Socks5 header (#984)
* Send socks5 handshake respond before read the target address
So that the next package will (hopefully) contain the first data chunk which means we could get a 'random' first package size.

* Try read available data before send first packet

* Add random size first packet
But commented out ^u^

* Oops forget to delete some test code
2017-01-04 07:59:41 +08:00
破娃酱 18e956f34c not set alpha of the icon (#981) 2017-01-02 15:19:23 +08:00
Syrone Wong 333ad2516f Fix language detection
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-31 19:35:37 +08:00
Syrone Wong 9ffcf77ab1 Update LICENSE
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-31 16:25:41 +08:00
Syrone Wong d0d6a3cdfa Happy New Year
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-31 11:11:45 +08:00
Syrone Wong 8c76ac3401 Update CHANGES and bump version
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-30 19:24:12 +08:00
Syrone Wong 8f3994c7d8 Set LAN even if the RAS query fails
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-30 19:12:55 +08:00
Syrone Wong db1eafbfd8 Tweak OTA mask
sync with shadowsocks-libev

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-30 17:28:21 +08:00
LNDDYL 32225ed655 Update translation for Traditional Chinese (#973)
Credit to @LNDDYL
2016-12-30 17:13:46 +08:00
Syrone Wong 9e529361c4 Misc
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-23 21:48:30 +08:00
Syrone Wong d21162318e Workaround NotifyIcon's 63 chars limit(#965)
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-23 21:41:11 +08:00
Syrone Wong f9ed9174cc Fix crash when reading pac user rules
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-23 12:16:26 +08:00
Syrone Wong 675a5801d8 Fix appveyor link
Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-21 10:39:20 +08:00
Syrone Wong 4407095d01 Fix auto startup
other minor cleanup

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-21 10:36:21 +08:00
Syrone Wong 1a283aa4ea Drop obsolete tray area refreshing code
- naming private vars
- object initializer

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-21 10:36:21 +08:00
Syrone Wong 97d08e4d40 Tweak privoxy config
only redirect
enable critical logging

Signed-off-by: Syrone Wong <wong.syrone@gmail.com>
2016-12-21 10:36:21 +08:00
Syrone Wong 673d6b3c44 update sysproxy
This will reduce false positives on virus detection.
2016-12-21 10:34:27 +08:00
21 changed files with 331 additions and 316 deletions
+13
View File
@@ -1,3 +1,16 @@
3.4.3 2017-1-11
- Make the previous portable mode as default
- Refine networking by Noisyfox
- Bug fixes and improvements
3.4.2.1 2016-12-30
- Refine Traditional Chinese translation by LNDDYL
- sysproxy: reduce false positives on virus detection
- sysproxy: set LAN proxy settings even if RAS query fails
- privoxy: drop obsolete tray area refreshing code
- Fix auto startup
- Bug fixes and improvements
3.4.2 2016-12-16 3.4.2 2016-12-16
- Fix null ref in TCPRelay. (#940) - Fix null ref in TCPRelay. (#940)
- Bring Privoxy back. (#948) - Bring Privoxy back. (#948)
+19 -19
View File
@@ -994,31 +994,31 @@ https://tls.mbed.org
License: https://raw.githubusercontent.com/ARMmbed/mbedtls/master/LICENSE License: https://raw.githubusercontent.com/ARMmbed/mbedtls/master/LICENSE
SimpleJson Newtonsoft.Json
---------- ----------
https://github.com/facebook-csharp-sdk/simple-json https://raw.githubusercontent.com/JamesNK/Newtonsoft.Json/master/LICENSE.md
Copyright (c) 2011, The Outercurve Foundation The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining Copyright (c) 2007 James Newton-King
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be Permission is hereby granted, free of charge, to any person obtaining a copy of
included in all copies or substantial portions of the Software. this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, The above copyright notice and this permission notice shall be included in all
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF copies or substantial portions of the Software.
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
ZXing ZXing
+2 -7
View File
@@ -89,11 +89,6 @@ if you want to deactivate all, please clear all textboxes.
Please visit [Servers] for more information. Please visit [Servers] for more information.
#### Portable Mode
If you want to put all temporary files into shadowsocks/temp folder instead of
system temp folder, create a `shadowsocks_portable_mode.txt` into shadowsocks folder.
#### Develop #### Develop
[Visual Studio 2015] & [.NET Framework 4.6.2 Developer Pack] are required. [Visual Studio 2015] & [.NET Framework 4.6.2 Developer Pack] are required.
@@ -103,8 +98,8 @@ system temp folder, create a `shadowsocks_portable_mode.txt` into shadowsocks fo
GPLv3 GPLv3
[Appveyor]: https://ci.appveyor.com/project/Noisyfox/shadowsocks-windows [Appveyor]: https://ci.appveyor.com/project/wongsyrone/shadowsocks-windows-pljx3
[Build Status]: https://ci.appveyor.com/api/projects/status/04idbevayrghcrh7/branch/master?svg=true [Build Status]: https://ci.appveyor.com/api/projects/status/otie4km27ws1wak2/branch/master?svg=true
[latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases [latest release]: https://github.com/shadowsocks/shadowsocks-csharp/releases
[GFWList]: https://github.com/gfwlist/gfwlist [GFWList]: https://github.com/gfwlist/gfwlist
[Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side [Servers]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#linux--server-side
+25 -3
View File
@@ -1,10 +1,11 @@
using System; using System;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Text;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
public class FileManager public static class FileManager
{ {
public static bool ByteArrayToFile(string fileName, byte[] content) public static bool ByteArrayToFile(string fileName, byte[] content)
{ {
@@ -16,8 +17,7 @@ namespace Shadowsocks.Controller
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("Exception caught in process: {0}", Logging.Error(ex);
ex.ToString());
} }
return false; return false;
} }
@@ -39,5 +39,27 @@ namespace Shadowsocks.Controller
} }
} }
} }
public static string NonExclusiveReadAllText(string path)
{
return NonExclusiveReadAllText(path, Encoding.Default);
}
public static string NonExclusiveReadAllText(string path, Encoding encoding)
{
try
{
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var sr = new StreamReader(fs, encoding))
{
return sr.ReadToEnd();
}
}
catch (Exception ex)
{
Logging.Error(ex);
throw ex;
}
}
} }
} }
+13 -23
View File
@@ -7,11 +7,11 @@ namespace Shadowsocks.Controller
{ {
using Shadowsocks.Properties; using Shadowsocks.Properties;
public class I18N public static class I18N
{ {
protected static Dictionary<string, string> Strings; private static Dictionary<string, string> _strings = new Dictionary<string, string>();
static void Init(string res) private static void Init(string res)
{ {
using (var sr = new StringReader(res)) using (var sr = new StringReader(res))
{ {
@@ -23,38 +23,28 @@ namespace Shadowsocks.Controller
var pos = line.IndexOf('='); var pos = line.IndexOf('=');
if (pos < 1) if (pos < 1)
continue; continue;
Strings[line.Substring(0, pos)] = line.Substring(pos + 1); _strings[line.Substring(0, pos)] = line.Substring(pos + 1);
} }
} }
} }
static I18N() static I18N()
{ {
Strings = new Dictionary<string, string>(); string name = CultureInfo.CurrentCulture.EnglishName;
string name = CultureInfo.CurrentCulture.Name; if (name.StartsWith("Chinese", StringComparison.OrdinalIgnoreCase))
if (name.StartsWith("zh"))
{ {
if (name == "zh" || name == "zh-CN") // choose Traditional Chinese only if we get explicit indication
{ Init(name.Contains("Traditional")
Init(Resources.cn); ? Resources.zh_tw
} : Resources.cn);
else
{
Init(Resources.zh_tw);
}
} }
} }
public static string GetString(string key) public static string GetString(string key)
{ {
if (Strings.ContainsKey(key)) return _strings.ContainsKey(key)
{ ? _strings[key]
return Strings[key]; : key;
}
else
{
return key;
}
} }
} }
} }
@@ -39,7 +39,7 @@ namespace Shadowsocks.Controller
List<string> lines = ParseResult(e.Result); List<string> lines = ParseResult(e.Result);
if (File.Exists(PACServer.USER_RULE_FILE)) if (File.Exists(PACServer.USER_RULE_FILE))
{ {
string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8); string local = FileManager.NonExclusiveReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8);
using (var sr = new StringReader(local)) using (var sr = new StringReader(local))
{ {
foreach (var rule in sr.NonWhiteSpaceLines()) foreach (var rule in sr.NonWhiteSpaceLines())
@@ -53,7 +53,7 @@ namespace Shadowsocks.Controller
string abpContent; string abpContent;
if (File.Exists(PACServer.USER_ABP_FILE)) if (File.Exists(PACServer.USER_ABP_FILE))
{ {
abpContent = File.ReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8); abpContent = FileManager.NonExclusiveReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8);
} }
else else
{ {
@@ -62,7 +62,7 @@ namespace Shadowsocks.Controller
abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented)); abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented));
if (File.Exists(PACServer.PAC_FILE)) if (File.Exists(PACServer.PAC_FILE))
{ {
string original = File.ReadAllText(PACServer.PAC_FILE, Encoding.UTF8); string original = FileManager.NonExclusiveReadAllText(PACServer.PAC_FILE, Encoding.UTF8);
if (original == abpContent) if (original == abpContent)
{ {
UpdateCompleted(this, new ResultEventArgs(false)); UpdateCompleted(this, new ResultEventArgs(false));
@@ -1,13 +1,9 @@
using System; using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets; using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using Shadowsocks.Model; using Shadowsocks.Model;
@@ -19,9 +15,9 @@ namespace Shadowsocks.Controller
{ {
class PrivoxyRunner class PrivoxyRunner
{ {
private static int Uid; private static int _uid;
private static string UniqueConfigFile; private static string _uniqueConfigFile;
private static Job PrivoxyJob; private static Job _privoxyJob;
private Process _process; private Process _process;
private int _runningPort; private int _runningPort;
@@ -29,9 +25,9 @@ namespace Shadowsocks.Controller
{ {
try try
{ {
Uid = Application.StartupPath.GetHashCode(); // Currently we use ss's StartupPath to identify different Privoxy instance. _uid = Application.StartupPath.GetHashCode(); // Currently we use ss's StartupPath to identify different Privoxy instance.
UniqueConfigFile = $"privoxy_{Uid}.conf"; _uniqueConfigFile = $"privoxy_{_uid}.conf";
PrivoxyJob = new Job(); _privoxyJob = new Job();
FileManager.UncompressFile(Utils.GetTempPath("ss_privoxy.exe"), Resources.privoxy_exe); FileManager.UncompressFile(Utils.GetTempPath("ss_privoxy.exe"), Resources.privoxy_exe);
FileManager.UncompressFile(Utils.GetTempPath("mgwz.dll"), Resources.mgwz_dll); FileManager.UncompressFile(Utils.GetTempPath("mgwz.dll"), Resources.mgwz_dll);
@@ -54,29 +50,33 @@ namespace Shadowsocks.Controller
KillProcess(p); KillProcess(p);
} }
string privoxyConfig = Resources.privoxy_conf; string privoxyConfig = Resources.privoxy_conf;
_runningPort = this.GetFreePort(); _runningPort = GetFreePort();
privoxyConfig = privoxyConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString()); privoxyConfig = privoxyConfig.Replace("__SOCKS_PORT__", configuration.localPort.ToString());
privoxyConfig = privoxyConfig.Replace("__PRIVOXY_BIND_PORT__", _runningPort.ToString()); privoxyConfig = privoxyConfig.Replace("__PRIVOXY_BIND_PORT__", _runningPort.ToString());
privoxyConfig = privoxyConfig.Replace("__PRIVOXY_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1"); privoxyConfig = privoxyConfig.Replace("__PRIVOXY_BIND_IP__", configuration.shareOverLan ? "0.0.0.0" : "127.0.0.1");
FileManager.ByteArrayToFile(Utils.GetTempPath(UniqueConfigFile), Encoding.UTF8.GetBytes(privoxyConfig)); FileManager.ByteArrayToFile(Utils.GetTempPath(_uniqueConfigFile), Encoding.UTF8.GetBytes(privoxyConfig));
_process = new Process(); _process = new Process
// Configure the process using the StartInfo properties. {
_process.StartInfo.FileName = "ss_privoxy.exe"; // Configure the process using the StartInfo properties.
_process.StartInfo.Arguments = UniqueConfigFile; StartInfo =
_process.StartInfo.WorkingDirectory = Utils.GetTempPath(); {
_process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; FileName = "ss_privoxy.exe",
_process.StartInfo.UseShellExecute = true; Arguments = _uniqueConfigFile,
_process.StartInfo.CreateNoWindow = true; WorkingDirectory = Utils.GetTempPath(),
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = true,
CreateNoWindow = true
}
};
_process.Start(); _process.Start();
/* /*
* Add this process to job obj associated with this ss process, so that * Add this process to job obj associated with this ss process, so that
* when ss exit unexpectedly, this process will be forced killed by system. * when ss exit unexpectedly, this process will be forced killed by system.
*/ */
PrivoxyJob.AddProcess(_process.Handle); _privoxyJob.AddProcess(_process.Handle);
} }
RefreshTrayArea();
} }
public void Stop() public void Stop()
@@ -87,7 +87,6 @@ namespace Shadowsocks.Controller
_process.Dispose(); _process.Dispose();
_process = null; _process = null;
} }
RefreshTrayArea();
} }
private static void KillProcess(Process p) private static void KillProcess(Process p)
@@ -122,21 +121,13 @@ namespace Shadowsocks.Controller
{ {
try try
{ {
if (Utils.IsPortableMode()) /*
{ * Under PortableMode, we could identify it by the path of ss_privoxy.exe.
/* */
* Under PortableMode, we could identify it by the path of ss_privoxy.exe. var path = process.MainModule.FileName;
*/
var path = process.MainModule.FileName;
return Utils.GetTempPath("ss_privoxy.exe").Equals(path); return Utils.GetTempPath("ss_privoxy.exe").Equals(path);
}
else
{
var cmd = process.GetCommandLine();
return cmd.Contains(UniqueConfigFile);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -169,48 +160,5 @@ namespace Shadowsocks.Controller
return defaultPort; return defaultPort;
} }
} }
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll")]
public static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam);
public void RefreshTrayArea()
{
IntPtr systemTrayContainerHandle = FindWindow("Shell_TrayWnd", null);
IntPtr systemTrayHandle = FindWindowEx(systemTrayContainerHandle, IntPtr.Zero, "TrayNotifyWnd", null);
IntPtr sysPagerHandle = FindWindowEx(systemTrayHandle, IntPtr.Zero, "SysPager", null);
IntPtr notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "Notification Area");
if (notificationAreaHandle == IntPtr.Zero)
{
notificationAreaHandle = FindWindowEx(sysPagerHandle, IntPtr.Zero, "ToolbarWindow32", "User Promoted Notification Area");
IntPtr notifyIconOverflowWindowHandle = FindWindow("NotifyIconOverflowWindow", null);
IntPtr overflowNotificationAreaHandle = FindWindowEx(notifyIconOverflowWindowHandle, IntPtr.Zero, "ToolbarWindow32", "Overflow Notification Area");
RefreshTrayArea(overflowNotificationAreaHandle);
}
RefreshTrayArea(notificationAreaHandle);
}
private static void RefreshTrayArea(IntPtr windowHandle)
{
const uint wmMousemove = 0x0200;
RECT rect;
GetClientRect(windowHandle, out rect);
for (var x = 0; x < rect.right; x += 5)
for (var y = 0; y < rect.bottom; y += 5)
SendMessage(windowHandle, wmMousemove, 0, (y << 16) + x);
}
} }
} }
@@ -322,24 +322,15 @@ namespace Shadowsocks.Controller
} }
else else
{ {
int atyp = _connetionRecvBuffer[3]; if (_command == 1)
switch (atyp)
{ {
case 1: // IPv4 address, 4 bytes byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
ReadAddress(4 + 2 - 1); _connection.BeginSend(response, 0, response.Length, SocketFlags.None,
break; new AsyncCallback(ResponseCallback), null);
case 3: // domain name, length + str }
int len = _connetionRecvBuffer[4]; else if (_command == 3)
ReadAddress(len + 2); {
break; ReadAddress(HandleUDPAssociate);
case 4: // IPv6 address, 16 bytes
ReadAddress(16 + 2 - 1);
break;
default:
Logging.Debug("Unsupported ATYP=" + atyp);
Close();
break;
} }
} }
} }
@@ -356,12 +347,50 @@ namespace Shadowsocks.Controller
} }
} }
private void ReadAddress(int bytesRemain) private void ResponseCallback(IAsyncResult ar)
{
try
{
_connection.EndSend(ar);
ReadAddress(StartConnect);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
private void ReadAddress(Action onSuccess)
{
int atyp = _connetionRecvBuffer[3];
switch (atyp)
{
case 1: // IPv4 address, 4 bytes
ReadAddress(4 + 2 - 1, onSuccess);
break;
case 3: // domain name, length + str
int len = _connetionRecvBuffer[4];
ReadAddress(len + 2, onSuccess);
break;
case 4: // IPv6 address, 16 bytes
ReadAddress(16 + 2 - 1, onSuccess);
break;
default:
Logging.Debug("Unsupported ATYP=" + atyp);
Close();
break;
}
}
private void ReadAddress(int bytesRemain, Action onSuccess)
{ {
Array.Copy(_connetionRecvBuffer, 3, _connetionRecvBuffer, 0, 2); Array.Copy(_connetionRecvBuffer, 3, _connetionRecvBuffer, 0, 2);
// Read the remain address bytes // Read the remain address bytes
_connection.BeginReceive(_connetionRecvBuffer, 2, RecvSize - 2, SocketFlags.None, OnAddressFullyRead, bytesRemain); _connection.BeginReceive(_connetionRecvBuffer, 2, RecvSize - 2, SocketFlags.None, OnAddressFullyRead, new object[] {bytesRemain, onSuccess});
} }
private void OnAddressFullyRead(IAsyncResult ar) private void OnAddressFullyRead(IAsyncResult ar)
@@ -370,7 +399,12 @@ namespace Shadowsocks.Controller
try try
{ {
int bytesRead = _connection.EndReceive(ar); int bytesRead = _connection.EndReceive(ar);
int bytesRemain = (int) ar.AsyncState;
var states = (object[]) ar.AsyncState;
int bytesRemain = (int)states[0];
var onSuccess = (Action) states[1];
if (bytesRead >= bytesRemain) if (bytesRead >= bytesRemain)
{ {
_firstPacketLength = bytesRead + 2; _firstPacketLength = bytesRead + 2;
@@ -405,16 +439,7 @@ namespace Shadowsocks.Controller
_destEndPoint = SocketUtil.GetEndPoint(dst_addr, dst_port); _destEndPoint = SocketUtil.GetEndPoint(dst_addr, dst_port);
if (_command == 1) onSuccess.Invoke();
{
byte[] response = {5, 0, 0, 1, 0, 0, 0, 0, 0, 0};
_connection.BeginSend(response, 0, response.Length, SocketFlags.None,
new AsyncCallback(ResponseCallback), null);
}
else if (_command == 3)
{
HandleUDPAssociate();
}
} }
else else
{ {
@@ -479,20 +504,6 @@ namespace Shadowsocks.Controller
} }
} }
private void ResponseCallback(IAsyncResult ar)
{
try
{
_connection.EndSend(ar);
StartConnect();
}
catch (Exception e)
{
Logging.LogUsefulException(e);
Close();
}
}
// inner class // inner class
private class ProxyTimer : Timer private class ProxyTimer : Timer
{ {
@@ -716,6 +727,26 @@ namespace Shadowsocks.Controller
} }
} }
// private static readonly Random Rnd = new Random();
private void TryReadAvailableData()
{
int available = Math.Min(_connection.Available, RecvSize - _firstPacketLength);
if (available > 0)
{
// Pick a random chunk size, or is it truly necessary? Random packet size is some sort of 'characteristic' itself.
//lock (Rnd)
//{
// available = Rnd.Next(available + 1);
//}
var size = _connection.Receive(_connetionRecvBuffer, _firstPacketLength, available,
SocketFlags.None);
_firstPacketLength += size;
}
}
private void StartPipe(AsyncSession session) private void StartPipe(AsyncSession session)
{ {
if (_closed) return; if (_closed) return;
@@ -723,6 +754,9 @@ namespace Shadowsocks.Controller
{ {
_startReceivingTime = DateTime.Now; _startReceivingTime = DateTime.Now;
session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session); session.Remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), session);
TryReadAvailableData();
Logging.Debug($"_firstPacketLength = {_firstPacketLength}");
SendToServer(_firstPacketLength, session); SendToServer(_firstPacketLength, session);
} }
catch (Exception e) catch (Exception e)
@@ -24,7 +24,7 @@ namespace Shadowsocks.Controller
public string LatestVersionLocalName; public string LatestVersionLocalName;
public event EventHandler CheckUpdateCompleted; public event EventHandler CheckUpdateCompleted;
public const string Version = "3.4.2"; public const string Version = "3.4.3";
private class CheckUpdateTimer : System.Timers.Timer private class CheckUpdateTimer : System.Timers.Timer
{ {
@@ -557,10 +557,10 @@ namespace Shadowsocks.Controller
UpdatePACFromGFWList(); UpdatePACFromGFWList();
return; return;
} }
List<string> lines = GFWListUpdater.ParseResult(File.ReadAllText(Utils.GetTempPath("gfwlist.txt"))); List<string> lines = GFWListUpdater.ParseResult(FileManager.NonExclusiveReadAllText(Utils.GetTempPath("gfwlist.txt")));
if (File.Exists(PACServer.USER_RULE_FILE)) if (File.Exists(PACServer.USER_RULE_FILE))
{ {
string local = File.ReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8); string local = FileManager.NonExclusiveReadAllText(PACServer.USER_RULE_FILE, Encoding.UTF8);
using (var sr = new StringReader(local)) using (var sr = new StringReader(local))
{ {
foreach (var rule in sr.NonWhiteSpaceLines()) foreach (var rule in sr.NonWhiteSpaceLines())
@@ -574,7 +574,7 @@ namespace Shadowsocks.Controller
string abpContent; string abpContent;
if (File.Exists(PACServer.USER_ABP_FILE)) if (File.Exists(PACServer.USER_ABP_FILE))
{ {
abpContent = File.ReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8); abpContent = FileManager.NonExclusiveReadAllText(PACServer.USER_ABP_FILE, Encoding.UTF8);
} }
else else
{ {
@@ -583,7 +583,7 @@ namespace Shadowsocks.Controller
abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented)); abpContent = abpContent.Replace("__RULES__", JsonConvert.SerializeObject(lines, Formatting.Indented));
if (File.Exists(PACServer.PAC_FILE)) if (File.Exists(PACServer.PAC_FILE))
{ {
string original = File.ReadAllText(PACServer.PAC_FILE, Encoding.UTF8); string original = FileManager.NonExclusiveReadAllText(PACServer.PAC_FILE, Encoding.UTF8);
if (original == abpContent) if (original == abpContent)
{ {
return; return;
@@ -1,20 +1,24 @@
using System; using System;
using System.Reflection;
using System.Windows.Forms; using System.Windows.Forms;
using Microsoft.Win32; using Microsoft.Win32;
using Shadowsocks.Util; using Shadowsocks.Util;
namespace Shadowsocks.Controller namespace Shadowsocks.Controller
{ {
class AutoStartup static class AutoStartup
{ {
static string Key = "Shadowsocks_" + Application.StartupPath.GetHashCode(); // Don't use Application.ExecutablePath
// see https://stackoverflow.com/questions/12945805/odd-c-sharp-path-issue
private static readonly string ExecutablePath = Assembly.GetEntryAssembly().Location;
private static string Key = "Shadowsocks_" + Application.StartupPath.GetHashCode();
public static bool Set(bool enabled) public static bool Set(bool enabled)
{ {
RegistryKey runKey = null; RegistryKey runKey = null;
try try
{ {
string path = Application.ExecutablePath;
runKey = Utils.OpenRegKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true); runKey = Utils.OpenRegKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
if ( runKey == null ) { if ( runKey == null ) {
Logging.Error( @"Cannot find HKCU\Software\Microsoft\Windows\CurrentVersion\Run" ); Logging.Error( @"Cannot find HKCU\Software\Microsoft\Windows\CurrentVersion\Run" );
@@ -22,7 +26,7 @@ namespace Shadowsocks.Controller
} }
if (enabled) if (enabled)
{ {
runKey.SetValue(Key, path); runKey.SetValue(Key, ExecutablePath);
} }
else else
{ {
@@ -53,7 +57,6 @@ namespace Shadowsocks.Controller
RegistryKey runKey = null; RegistryKey runKey = null;
try try
{ {
string path = Application.ExecutablePath;
runKey = Utils.OpenRegKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true); runKey = Utils.OpenRegKey(@"Software\Microsoft\Windows\CurrentVersion\Run", true);
if (runKey == null) { if (runKey == null) {
Logging.Error(@"Cannot find HKCU\Software\Microsoft\Windows\CurrentVersion\Run"); Logging.Error(@"Cannot find HKCU\Software\Microsoft\Windows\CurrentVersion\Run");
@@ -67,10 +70,10 @@ namespace Shadowsocks.Controller
else if (item.Equals("Shadowsocks", StringComparison.OrdinalIgnoreCase)) // Compatibility with older versions else if (item.Equals("Shadowsocks", StringComparison.OrdinalIgnoreCase)) // Compatibility with older versions
{ {
string value = Convert.ToString(runKey.GetValue(item)); string value = Convert.ToString(runKey.GetValue(item));
if (path.Equals(value, StringComparison.OrdinalIgnoreCase)) if (ExecutablePath.Equals(value, StringComparison.OrdinalIgnoreCase))
{ {
runKey.DeleteValue(item); runKey.DeleteValue(item);
runKey.SetValue(Key, path); runKey.SetValue(Key, ExecutablePath);
return true; return true;
} }
} }
+1
View File
@@ -33,6 +33,7 @@ Check for Updates at Startup=启动时检查更新
Check Pre-release Version=检查测试版更新 Check Pre-release Version=检查测试版更新
Edit Hotkeys...=编辑快捷键... Edit Hotkeys...=编辑快捷键...
About...=关于... About...=关于...
Help=帮助
Quit=退出 Quit=退出
Edit Servers=编辑服务器 Edit Servers=编辑服务器
Load Balance=负载均衡 Load Balance=负载均衡
+2
View File
@@ -1,4 +1,6 @@
listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__ listen-address __PRIVOXY_BIND_IP__:__PRIVOXY_BIND_PORT__
toggle 0
logfile ss_privoxy.log
show-on-task-bar 0 show-on-task-bar 0
activity-animation 0 activity-animation 0
forward-socks5 / 127.0.0.1:__SOCKS_PORT__ . forward-socks5 / 127.0.0.1:__SOCKS_PORT__ .
Binary file not shown.
Binary file not shown.
+84 -81
View File
@@ -10,105 +10,108 @@ PAC=PAC 模式
Global=全局模式 Global=全局模式
Servers=伺服器 Servers=伺服器
Edit Servers...=編輯伺服器... Edit Servers...=編輯伺服器...
Statistics Config...=統計配置... Statistics Config...=統計設定檔...
Start on Boot=開機啟動 Start on Boot=開機啟動
Forward Proxy...=正向代理設置... Forward Proxy...=正向 Proxy 設定...
Allow Clients from LAN=允許來自區域網路的連接 Allow Clients from LAN=允許來自區域網路的連線
Local PAC=使用本地 PAC Local PAC=使用本機 PAC
Online PAC=使用在線 PAC Online PAC=使用線上 PAC
Edit Local PAC File...=編輯本地 PAC 文件... Edit Local PAC File...=編輯本機 PAC 檔案...
Update Local PAC from GFWList=從 GFWList 更新本地 PAC Update Local PAC from GFWList=從 GFWList 更新本機 PAC
Edit User Rule for GFWList...=編輯 GFWList 的用戶規則... Edit User Rule for GFWList...=編輯 GFWList 的使用者規則...
Secure Local PAC=保護本地 PAC Secure Local PAC=安全本機 PAC
Copy Local PAC URL=複製本地 PAC 網址 Copy Local PAC URL=複製本機 PAC 網址
Share Server Config...=分享伺服器配置... Share Server Config...=分享伺服器設定檔...
Scan QRCode from Screen...=掃描螢幕上的 QR 碼... Scan QRCode from Screen...=掃描螢幕上的 QR 碼...
Import URL from Clipboard...=從剪貼板導入URL... Import URL from Clipboard...=從剪貼簿匯入 URL...
Availability Statistics=統計可用性 Availability Statistics=統計可用性
Show Logs...=顯示日誌... Show Logs...=顯示記錄檔...
Verbose Logging=詳細記錄日誌 Verbose Logging=詳細資訊記錄
Updates...=更新... Updates...=更新...
Check for Updates...=檢查更新 Check for Updates...=檢查更新
Check for Updates at Startup=啟動時檢查更新 Check for Updates at Startup=啟動時檢查更新
Check Pre-release Version=檢查測試版更新 Check Pre-release Version=檢查發行前版本更新
Edit Hotkeys...=編輯捷徑鍵... Edit Hotkeys...=編輯快速鍵...
About...=關於... About...=關於...
Quit=退出 Help=幫助
Quit=結束
Edit Servers=編輯伺服器 Edit Servers=編輯伺服器
Load Balance=負載均衡 Load Balance=負載平衡
High Availability=高可用 High Availability=高可用性
Choose by statistics=根據統計 Choose by statistics=根據統計
# Config Form # Config Form
&Add=新增(&A) &Add=新增 (&A)
&Delete=移除(&D) &Delete=移除 (&D)
Dupli&cate=複製(&C) Dupli&cate=複製 (&C)
Server=伺服器 Server=伺服器
Server Addr=伺服器位址 Server Addr=伺服器位址
Server Port=伺服器連接埠 Server Port=伺服器連接埠
Password=密碼 Password=密碼
Encryption=加密 Encryption=加密
Proxy Port=代理連接埠 Proxy Port=Proxy 連接埠
Remarks=備註 Remarks=註解
Timeout(Sec)=超時(秒) Timeout(Sec)=逾時 (秒)
Onetime Authentication=單次驗證 Onetime Authentication=單次驗證
OK=確定 OK=確定
Cancel=取消 Cancel=取消
New server=未配置的伺服器 New server=新伺服器
Move &Up=上移(&U) Move &Up=上移 (&U)
Move D&own=下移(&O) Move D&own=下移 (&O)
# Proxy Form # Proxy Form
Edit Proxy=代理設置 Edit Proxy=編輯 Proxy
Use Proxy=使用代理 Use Proxy=使用 Proxy
Proxy Type=代理類別 Proxy Type=Proxy 類型
Proxy Addr=代理位址 Proxy Addr=Proxy 位址
Proxy Port=代理連接埠 Proxy Port=Proxy 連接埠
# Log Form # Log Form
&File=文件(&F) &File=檔案 (&F)
&Open Location=在資料總管中打開(&O) &Open Location=在檔案總管中開啟 (&O)
E&xit=退出(&X) E&xit=結束 (&X)
&View=視圖(&V) &View=檢視
&Clean Logs=清空日誌(&C)
Change &Font=設置字體(&F) (&V)
&Wrap Text=自動換行(&W) &Clean Logs=清除記錄檔 (&C)
&Top Most=置頂(&T) Change &Font=變更字型 (&F)
&Show Toolbar=顯示工具欄(&S) &Wrap Text=自動換行 (&W)
Log Viewer=日誌查看器 &Top Most=置頂 (&T)
Inbound=入站 &Show Toolbar=顯示工具列 (&S)
Outbound=出站 Log Viewer=記錄檔檢視器
Inbound=輸入
Outbound=輸出
# QRCode Form # QRCode Form
QRCode and URL=QR 碼与 URL QRCode and URL=QR 碼與 URL
# PAC Url Form # PAC Url Form
Edit Online PAC URL=編輯在線 PAC 網址 Edit Online PAC URL=編輯線上 PAC 網址
Edit Online PAC URL...=編輯在線 PAC 網址... Edit Online PAC URL...=編輯線上 PAC 網址...
Please input PAC Url=請輸入 PAC 網址 Please input PAC Url=請輸入 PAC 網址
# HotkeySettings Form # HotkeySettings Form
Switch system proxy=切換系統代理狀態 Switch system proxy=切換系統 Proxy 狀態
Switch system proxy mode=切換系統代理模式 Switch system proxy mode=切換系統 Proxy 模式
Switch share over LAN=切換區域網路共享 Switch share over LAN=切換區域網路共用
Show Logs=顯示日誌 Show Logs=顯示記錄檔
Switch to prev server=切換上個伺服器 Switch to prev server=切換上一個伺服器
Switch to next server=切換下個伺服器 Switch to next server=切換下一個伺服器
Reg All=註冊全部捷徑鍵 Reg All=註冊所有快速鍵
# Messages # Messages
Shadowsocks Error: {0}=Shadowsocks 錯誤: {0} Shadowsocks Error: {0}=Shadowsocks 錯誤: {0}
Port already in use=連接埠號碼已被占用 Port already in use=連接埠號碼已被使用
Invalid server address=非法伺服器位址 Invalid server address=無效伺服器位址
Illegal port number format=非法連接埠號碼格式 Illegal port number format=無效連接埠號碼格式
Illegal timeout format=非法超時格式 Illegal timeout format=無效逾時格式
Please add at least one server=請新增至少一個伺服器 Please add at least one server=請新增至少一個伺服器
Server IP can not be blank=伺服器 IP 不能為空 Server IP can not be blank=伺服器 IP 不能為空
Password can not be blank=密碼不能為空 Password can not be blank=密碼不能為空
@@ -116,27 +119,27 @@ Port out of range=連接埠號碼超出範圍
Port can't be 8123=連接埠號碼不能為 8123 Port can't be 8123=連接埠號碼不能為 8123
Shadowsocks {0} Update Found=Shadowsocks {0} 更新 Shadowsocks {0} Update Found=Shadowsocks {0} 更新
No update is available=沒有可用的更新 No update is available=沒有可用的更新
Click here to update=點擊這裏升級 Click here to update=點按此處升級
Shadowsocks is here=Shadowsocks 在這裏 Shadowsocks is here=Shadowsocks 在這裡
You can turn on/off Shadowsocks in the context menu=可以在右鍵菜單中開關 Shadowsocks You can turn on/off Shadowsocks in the context menu=可以在右鍵選項單中開關 Shadowsocks
System Proxy Enabled=系統代理已啟用 System Proxy Enabled=系統 Proxy 已啟用
System Proxy Disabled=系統代理未啟用 System Proxy Disabled=系統 Proxy 未啟用
Failed to update PAC file =更新 PAC 文件失敗 Failed to update PAC file =更新 PAC 檔案失敗
PAC updated=更新 PAC 成功 PAC updated=更新 PAC 成功
No updates found. Please report to GFWList if you have problems with it.=未發現更新。如有問題請提交給 GFWList。 No updates found. Please report to GFWList if you have problems with it.=未發現更新。如有問題請報告至 GFWList。
No QRCode found. Try to zoom in or move it to the center of the screen.=未發現 QR 碼,嘗試把它放大或移動到靠近屏幕中間的位置 No QRCode found. Try to zoom in or move it to the center of the screen.=未發現 QR 碼,嘗試把它放大或移動到靠近熒幕中間的位置
Shadowsocks is already running.=Shadowsocks 已經在運行。 Shadowsocks is already running.=Shadowsocks 已經在執行。
Find Shadowsocks icon in your notify tray.=請在工作列裏尋找 Shadowsocks 圖標。 Find Shadowsocks icon in your notify tray.=請在工作列裡尋找 Shadowsocks 圖示。
If you want to start multiple Shadowsocks, make a copy in another directory.=如果想同時啟動多個,可以另外復制一份到別的目錄。 If you want to start multiple Shadowsocks, make a copy in another directory.=如果想同時啟動多個,可以另外複製一份至別的目錄。
Failed to decode QRCode=無法解析 QR 碼 Failed to decode QRCode=QR 碼解碼失敗
Failed to update registry=無法修改登錄檔 Failed to update registry=無法修改登錄檔
System Proxy On: =系統代理已啟用: System Proxy On: =系統 Proxy 已啟用:
Running: Port {0}=正在運行:連接埠號碼 {0} Running: Port {0}=正在執行:連接埠號碼 {0}
Unexpected error, shadowsocks will exit. Please report to=非預期錯誤,Shadowsocks將退出。請提交此錯誤到 Unexpected error, shadowsocks will exit. Please report to=非預期錯誤,Shadowsocks 將結束。請報告此錯誤至
Unsupported operating system, use Windows Vista at least.=不支持的作業系統版本,最低需求為Windows Vista。 Unsupported operating system, use Windows Vista at least.=不支援的作業系統版本,最低需求為 Windows Vista。
Unsupported .NET Framework, please update to 4.6.2 or later.=當前 .NET Framework 版本過低,最低需求為4.6.2。 Unsupported .NET Framework, please update to 4.6.2 or later.=目前 .NET Framework 版本過低,最低需求為4.6.2。
Proxy request failed=代理請求失敗 Proxy request failed=Proxy 要求失敗
Proxy handshake failed=代理握手失敗 Proxy handshake failed=Proxy 交握失敗
Register hotkey failed=註冊捷徑鍵失敗 Register hotkey failed=註冊快速鍵失敗
Cannot parse hotkey: {0}=解析捷徑鍵失敗: {0} Cannot parse hotkey: {0}=剖析快速鍵失敗: {0}
Timeout is invalid, it should not exceed {0}=超時無效,不應超過 {0} Timeout is invalid, it should not exceed {0}=逾時無效,不應超過 {0}
+1 -1
View File
@@ -106,7 +106,7 @@ namespace Shadowsocks.Encryption
#region OneTimeAuth #region OneTimeAuth
public const int ONETIMEAUTH_FLAG = 0x10; public const int ONETIMEAUTH_FLAG = 0x10;
public const int ADDRTYPE_MASK = 0xF; public const int ADDRTYPE_MASK = 0xEF;
public const int ONETIMEAUTH_BYTES = 10; public const int ONETIMEAUTH_BYTES = 10;
@@ -11,7 +11,7 @@ using System.Runtime.InteropServices;
[assembly: AssemblyConfiguration("")] [assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")] [assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Shadowsocks")] [assembly: AssemblyProduct("Shadowsocks")]
[assembly: AssemblyCopyright("clowwindy and community 2016")] [assembly: AssemblyCopyright("clowwindy & community 2017")]
[assembly: AssemblyTrademark("")] [assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")] [assembly: AssemblyCulture("")]
+14 -31
View File
@@ -25,43 +25,26 @@ namespace Shadowsocks.Util
public static class Utils public static class Utils
{ {
private static bool? _portableMode; private static string _tempPath = null;
private static string TempPath = null;
public static bool IsPortableMode()
{
if (!_portableMode.HasValue)
{
_portableMode = File.Exists(Path.Combine(Application.StartupPath, "shadowsocks_portable_mode.txt"));
}
return _portableMode.Value;
}
// return path to store temporary files // return path to store temporary files
public static string GetTempPath() public static string GetTempPath()
{ {
if (TempPath == null) if (_tempPath == null)
{ {
if (IsPortableMode()) try
try {
{ Directory.CreateDirectory(Path.Combine(Application.StartupPath, "ss_win_temp"));
Directory.CreateDirectory(Path.Combine(Application.StartupPath, "temp")); // don't use "/", it will fail when we call explorer /select xxx/ss_win_temp\xxx.log
} _tempPath = Path.Combine(Application.StartupPath, "ss_win_temp");
catch (Exception e) }
{ catch (Exception e)
TempPath = Path.GetTempPath(); {
Logging.LogUsefulException(e); Logging.Error(e);
} throw;
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 TempPath; return _tempPath;
} }
// return a full path with filename combined which pointed to the temporary directory // return a full path with filename combined which pointed to the temporary directory
+14
View File
@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Windows.Forms; using System.Windows.Forms;
namespace Shadowsocks.Util namespace Shadowsocks.Util
@@ -17,5 +18,18 @@ namespace Shadowsocks.Util
var children = control.Controls.OfType<TControl>().ToList(); var children = control.Controls.OfType<TControl>().ToList();
return children.SelectMany(GetChildControls<TControl>).Concat(children); return children.SelectMany(GetChildControls<TControl>).Concat(children);
} }
// Workaround NotifyIcon's 63 chars limit
// https://stackoverflow.com/questions/579665/how-can-i-show-a-systray-tooltip-longer-than-63-chars
public static void SetNotifyIconText(NotifyIcon ni, string text)
{
if (text.Length >= 128)
throw new ArgumentOutOfRangeException("Text limited to 127 characters");
Type t = typeof(NotifyIcon);
BindingFlags hidden = BindingFlags.NonPublic | BindingFlags.Instance;
t.GetField("text", hidden).SetValue(ni, text);
if ((bool)t.GetField("added", hidden).GetValue(ni))
t.GetMethod("UpdateIcon", hidden).Invoke(ni, new object[] { true });
}
} }
} }
+19 -12
View File
@@ -180,13 +180,13 @@ namespace Shadowsocks.View
{ {
serverInfo = config.GetCurrentServer().FriendlyName(); serverInfo = config.GetCurrentServer().FriendlyName();
} }
// we want to show more details but notify icon title is limited to 63 characters // show more info by hacking the P/Invoke declaration for NOTIFYICONDATA inside Windows Forms
string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" + string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" +
(enabled ? (enabled ?
I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) : I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) :
String.Format(I18N.GetString("Running: Port {0}"), config.localPort)) // this feedback is very important because they need to know Shadowsocks is running String.Format(I18N.GetString("Running: Port {0}"), config.localPort)) // this feedback is very important because they need to know Shadowsocks is running
+ "\n" + serverInfo; + "\n" + serverInfo;
_notifyIcon.Text = text.Substring(0, Math.Min(63, text.Length)); ViewUtils.SetNotifyIconText(_notifyIcon, text);
} }
private Bitmap getTrayIconByState(Bitmap originIcon, bool enabled, bool global) private Bitmap getTrayIconByState(Bitmap originIcon, bool enabled, bool global)
@@ -197,11 +197,16 @@ namespace Shadowsocks.View
for (int y = 0; y < iconCopy.Height; y++) for (int y = 0; y < iconCopy.Height; y++)
{ {
Color color = originIcon.GetPixel(x, y); Color color = originIcon.GetPixel(x, y);
if (color.A != 0 && color.R > 30) if (color.A != 0)
{ {
if (!enabled) if (!enabled)
{ {
iconCopy.SetPixel(x, y, Color.FromArgb((byte)(color.A / 1.25), color.R, color.G, color.B)); Color flyBlue = Color.FromArgb(192, 192, 192);
// Multiply with flyBlue
int red = color.R * flyBlue.R / 255;
int green = color.G * flyBlue.G / 255;
int blue = color.B * flyBlue.B / 255;
iconCopy.SetPixel(x, y, Color.FromArgb(color.A, red, green, blue));
} }
else if (global) else if (global)
{ {
@@ -282,16 +287,18 @@ namespace Shadowsocks.View
this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)), this.AutoStartupItem = CreateMenuItem("Start on Boot", new EventHandler(this.AutoStartupItem_Click)),
this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)), this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)),
new MenuItem("-"), new MenuItem("-"),
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
this.VerboseLoggingToggleItem = CreateMenuItem( "Verbose Logging", new EventHandler(this.VerboseLoggingToggleItem_Click) ),
this.hotKeyItem = CreateMenuItem("Edit Hotkeys...", new EventHandler(this.hotKeyItem_Click)), this.hotKeyItem = CreateMenuItem("Edit Hotkeys...", new EventHandler(this.hotKeyItem_Click)),
CreateMenuGroup("Updates...", new MenuItem[] { CreateMenuGroup("Help", new MenuItem[] {
CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)), CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)),
new MenuItem("-"), this.VerboseLoggingToggleItem = CreateMenuItem( "Verbose Logging", new EventHandler(this.VerboseLoggingToggleItem_Click) ),
this.autoCheckUpdatesToggleItem = CreateMenuItem("Check for Updates at Startup", new EventHandler(this.autoCheckUpdatesToggleItem_Click)), CreateMenuGroup("Updates...", new MenuItem[] {
this.checkPreReleaseToggleItem = CreateMenuItem("Check Pre-release Version", new EventHandler(this.checkPreReleaseToggleItem_Click)), CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)),
new MenuItem("-"),
this.autoCheckUpdatesToggleItem = CreateMenuItem("Check for Updates at Startup", new EventHandler(this.autoCheckUpdatesToggleItem_Click)),
this.checkPreReleaseToggleItem = CreateMenuItem("Check Pre-release Version", new EventHandler(this.checkPreReleaseToggleItem_Click)),
}),
CreateMenuItem("About...", new EventHandler(this.AboutItem_Click)),
}), }),
CreateMenuItem("About...", new EventHandler(this.AboutItem_Click)),
new MenuItem("-"), new MenuItem("-"),
CreateMenuItem("Quit", new EventHandler(this.Quit_Click)) CreateMenuItem("Quit", new EventHandler(this.Quit_Click))
}); });