313 lines
9.0 KiB
C#
Executable File
313 lines
9.0 KiB
C#
Executable File
using System.IO;
|
|
using Shadowsocks.Model;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Net.Sockets;
|
|
|
|
namespace Shadowsocks.Controller
|
|
{
|
|
public class ShadowsocksController
|
|
{
|
|
// controller:
|
|
// handle user actions
|
|
// manipulates UI
|
|
// interacts with low level logic
|
|
|
|
private Thread _ramThread;
|
|
|
|
private Local local;
|
|
private PACServer pacServer;
|
|
private Configuration _config;
|
|
private PolipoRunner polipoRunner;
|
|
private GFWListUpdater gfwListUpdater;
|
|
private bool stopped = false;
|
|
|
|
private bool _systemProxyIsDirty = false;
|
|
|
|
public class PathEventArgs : EventArgs
|
|
{
|
|
public string Path;
|
|
}
|
|
|
|
public event EventHandler ConfigChanged;
|
|
public event EventHandler EnableStatusChanged;
|
|
public event EventHandler EnableGlobalChanged;
|
|
public event EventHandler ShareOverLANStatusChanged;
|
|
|
|
// when user clicked Edit PAC, and PAC file has already created
|
|
public event EventHandler<PathEventArgs> PACFileReadyToOpen;
|
|
|
|
public event EventHandler UpdatePACFromGFWListCompleted;
|
|
|
|
public event ErrorEventHandler UpdatePACFromGFWListError;
|
|
|
|
public event ErrorEventHandler Errored;
|
|
|
|
public ShadowsocksController()
|
|
{
|
|
_config = Configuration.Load();
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
Reload();
|
|
}
|
|
|
|
protected void ReportError(Exception e)
|
|
{
|
|
if (Errored != null)
|
|
{
|
|
Errored(this, new ErrorEventArgs(e));
|
|
}
|
|
}
|
|
|
|
public Server GetCurrentServer()
|
|
{
|
|
return _config.GetCurrentServer();
|
|
}
|
|
|
|
// always return copy
|
|
public Configuration GetConfiguration()
|
|
{
|
|
return Configuration.Load();
|
|
}
|
|
|
|
public void SaveServers(List<Server> servers)
|
|
{
|
|
_config.configs = servers;
|
|
SaveConfig(_config);
|
|
}
|
|
|
|
public bool AddServerBySSURL(string ssURL)
|
|
{
|
|
try
|
|
{
|
|
var server = new Server(ssURL);
|
|
_config.configs.Add(server);
|
|
_config.index = _config.configs.Count - 1;
|
|
SaveConfig(_config);
|
|
return true;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logging.LogUsefulException(e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public void ToggleEnable(bool enabled)
|
|
{
|
|
_config.enabled = enabled;
|
|
UpdateSystemProxy();
|
|
SaveConfig(_config);
|
|
if (EnableStatusChanged != null)
|
|
{
|
|
EnableStatusChanged(this, new EventArgs());
|
|
}
|
|
}
|
|
|
|
public void ToggleGlobal(bool global)
|
|
{
|
|
_config.global = global;
|
|
UpdateSystemProxy();
|
|
SaveConfig(_config);
|
|
if (EnableGlobalChanged != null)
|
|
{
|
|
EnableGlobalChanged(this, new EventArgs());
|
|
}
|
|
}
|
|
|
|
public void ToggleShareOverLAN(bool enabled)
|
|
{
|
|
_config.shareOverLan = enabled;
|
|
SaveConfig(_config);
|
|
if (ShareOverLANStatusChanged != null)
|
|
{
|
|
ShareOverLANStatusChanged(this, new EventArgs());
|
|
}
|
|
}
|
|
|
|
public void SelectServerIndex(int index)
|
|
{
|
|
_config.index = index;
|
|
SaveConfig(_config);
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
Console.WriteLine("Stop");
|
|
if (stopped)
|
|
{
|
|
return;
|
|
}
|
|
stopped = true;
|
|
if (local != null)
|
|
{
|
|
local.Stop();
|
|
}
|
|
if (polipoRunner != null)
|
|
{
|
|
polipoRunner.Stop();
|
|
}
|
|
if (_config.enabled)
|
|
{
|
|
SystemProxy.Disable();
|
|
}
|
|
}
|
|
|
|
public void TouchPACFile()
|
|
{
|
|
string pacFilename = pacServer.TouchPACFile();
|
|
if (PACFileReadyToOpen != null)
|
|
{
|
|
PACFileReadyToOpen(this, new PathEventArgs() { Path = pacFilename });
|
|
}
|
|
}
|
|
|
|
public string GetQRCodeForCurrentServer()
|
|
{
|
|
Server server = GetCurrentServer();
|
|
string parts = server.method + ":" + server.password + "@" + server.server + ":" + server.server_port;
|
|
string base64 = System.Convert.ToBase64String(Encoding.UTF8.GetBytes(parts));
|
|
return "ss://" + base64;
|
|
}
|
|
|
|
public void UpdatePACFromGFWList()
|
|
{
|
|
if (gfwListUpdater != null)
|
|
{
|
|
gfwListUpdater.UpdatePACFromGFWList();
|
|
}
|
|
}
|
|
|
|
protected void Reload()
|
|
{
|
|
Console.WriteLine("Reload");
|
|
// some logic in configuration updated the config when saving, we need to read it again
|
|
_config = Configuration.Load();
|
|
|
|
if (polipoRunner == null)
|
|
{
|
|
polipoRunner = new PolipoRunner();
|
|
}
|
|
if (pacServer == null)
|
|
{
|
|
pacServer = new PACServer();
|
|
pacServer.PACFileChanged += pacServer_PACFileChanged;
|
|
}
|
|
if (gfwListUpdater == null)
|
|
{
|
|
gfwListUpdater = new GFWListUpdater();
|
|
gfwListUpdater.UpdateCompleted += pacServer_PACUpdateCompleted;
|
|
gfwListUpdater.Error += pacServer_PACUpdateError;
|
|
}
|
|
|
|
pacServer.Stop();
|
|
|
|
if (local != null)
|
|
{
|
|
local.Stop();
|
|
}
|
|
|
|
// don't put polipoRunner.Start() before pacServer.Stop()
|
|
// or bind will fail when switching bind address from 0.0.0.0 to 127.0.0.1
|
|
// though UseShellExecute is set to true now
|
|
// http://stackoverflow.com/questions/10235093/socket-doesnt-close-after-application-exits-if-a-launched-process-is-open
|
|
polipoRunner.Stop();
|
|
try
|
|
{
|
|
polipoRunner.Start(_config);
|
|
|
|
local = new Local(_config);
|
|
local.Start();
|
|
pacServer.Start(_config);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
// translate Microsoft language into human language
|
|
// i.e. An attempt was made to access a socket in a way forbidden by its access permissions => Port already in use
|
|
if (e is SocketException)
|
|
{
|
|
SocketException se = (SocketException)e;
|
|
if (se.SocketErrorCode == SocketError.AccessDenied)
|
|
{
|
|
e = new Exception(I18N.GetString("Port already in use"), e);
|
|
}
|
|
}
|
|
Logging.LogUsefulException(e);
|
|
ReportError(e);
|
|
}
|
|
|
|
if (ConfigChanged != null)
|
|
{
|
|
ConfigChanged(this, new EventArgs());
|
|
}
|
|
|
|
UpdateSystemProxy();
|
|
Util.Utils.ReleaseMemory();
|
|
}
|
|
|
|
|
|
protected void SaveConfig(Configuration newConfig)
|
|
{
|
|
Configuration.Save(newConfig);
|
|
Reload();
|
|
}
|
|
|
|
|
|
private void UpdateSystemProxy()
|
|
{
|
|
Console.WriteLine("UpdateSystemProxy");
|
|
if (_config.enabled)
|
|
{
|
|
SystemProxy.Enable(_config.global);
|
|
_systemProxyIsDirty = true;
|
|
}
|
|
else
|
|
{
|
|
// only switch it off if we have switched it on
|
|
if (_systemProxyIsDirty)
|
|
{
|
|
SystemProxy.Disable();
|
|
_systemProxyIsDirty = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void pacServer_PACFileChanged(object sender, EventArgs e)
|
|
{
|
|
UpdateSystemProxy();
|
|
}
|
|
|
|
private void pacServer_PACUpdateCompleted(object sender, EventArgs e)
|
|
{
|
|
if (UpdatePACFromGFWListCompleted != null)
|
|
UpdatePACFromGFWListCompleted(this, e);
|
|
}
|
|
|
|
private void pacServer_PACUpdateError(object sender, ErrorEventArgs e)
|
|
{
|
|
if (UpdatePACFromGFWListError != null)
|
|
UpdatePACFromGFWListError(this, e);
|
|
}
|
|
|
|
private void StartReleasingMemory()
|
|
{
|
|
_ramThread = new Thread(new ThreadStart(ReleaseMemory));
|
|
_ramThread.IsBackground = true;
|
|
_ramThread.Start();
|
|
}
|
|
|
|
private void ReleaseMemory()
|
|
{
|
|
while (true)
|
|
{
|
|
Util.Utils.ReleaseMemory();
|
|
Thread.Sleep(30 * 1000);
|
|
}
|
|
}
|
|
}
|
|
}
|