OSDN Git Service

プロキシ設定が消えたときに自動的に再設定する
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / ProxyManager.cs
1 // Copyright (C) 2017 Kazuhiro Fujieda <fujieda@users.osdn.me>\r
2 //\r
3 // Licensed under the Apache License, Version 2.0 (the "License");\r
4 // you may not use this file except in compliance with the License.\r
5 // You may obtain a copy of the License at\r
6 //\r
7 //    http://www.apache.org/licenses/LICENSE-2.0\r
8 //\r
9 // Unless required by applicable law or agreed to in writing, software\r
10 // distributed under the License is distributed on an "AS IS" BASIS,\r
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
12 // See the License for the specific language governing permissions and\r
13 // limitations under the License.\r
14 \r
15 using System;\r
16 using System.IO;\r
17 using System.Net;\r
18 using System.Net.Sockets;\r
19 using System.Threading;\r
20 using System.Threading.Tasks;\r
21 using System.Windows.Forms;\r
22 using Microsoft.Win32;\r
23 \r
24 namespace KancolleSniffer\r
25 {\r
26     public class ProxyManager\r
27     {\r
28         private readonly Config _config;\r
29         private readonly Control _parent;\r
30         private readonly SystemProxy _systemProxy = new SystemProxy();\r
31         private int _prevProxyPort;\r
32 \r
33         public ProxyManager(Config config, Control parent)\r
34         {\r
35             _config = config;\r
36             _parent = parent;\r
37             SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;\r
38         }\r
39 \r
40         public bool ApplyConfig()\r
41         {\r
42             if (!_config.Proxy.Auto)\r
43                 RestoreSystemProxy();\r
44             if (_config.Proxy.UseUpstream)\r
45             {\r
46                 HttpProxy.UpstreamProxyHost = "127.0.0.1";\r
47                 HttpProxy.UpstreamProxyPort = _config.Proxy.UpstreamPort;\r
48             }\r
49             HttpProxy.IsEnableUpstreamProxy = _config.Proxy.UseUpstream;\r
50             var result = true;\r
51             if (!HttpProxy.IsInListening || _config.Proxy.Listen != _prevProxyPort)\r
52             {\r
53                 ShutdownProxy();\r
54                 result = StartProxy();\r
55             }\r
56             if (_config.Proxy.Auto && result)\r
57             {\r
58                 SetAutoProxyUrl();\r
59             }\r
60             _prevProxyPort = _config.Proxy.Listen;\r
61             return result;\r
62         }\r
63 \r
64         private bool StartProxy()\r
65         {\r
66             try\r
67             {\r
68                 HttpProxy.Startup(_config.Proxy.Listen, false, false);\r
69             }\r
70             catch (SocketException e)\r
71             {\r
72                 if (e.SocketErrorCode != SocketError.AddressAlreadyInUse &&\r
73                     e.SocketErrorCode != SocketError.AccessDenied)\r
74                 {\r
75                     throw;\r
76                 }\r
77                 if (WarnConflictPortNumber("プロキシサーバー", _config.Proxy.Listen, _config.Proxy.Auto) == DialogResult.No ||\r
78                     !_config.Proxy.Auto)\r
79                 {\r
80                     RestoreSystemProxy();\r
81                     return false;\r
82                 }\r
83                 HttpProxy.Startup(0, false, false);\r
84                 _config.Proxy.Listen = HttpProxy.LocalPort;\r
85             }\r
86             return true;\r
87         }\r
88 \r
89         private DialogResult WarnConflictPortNumber(string name, int port, bool auto)\r
90         {\r
91             var msg = $"{name}のポート番号{port}は他のアプリケーションが使用中です。";\r
92             var cap = "ポート番号の衝突";\r
93             return auto\r
94                 ? MessageBox.Show(_parent, msg + "自動的に別の番号を割り当てますか?", cap,\r
95                     MessageBoxButtons.YesNo, MessageBoxIcon.Question)\r
96                 : MessageBox.Show(_parent, msg + "設定ダイアログでポート番号を変更してください。", cap,\r
97                     MessageBoxButtons.OK, MessageBoxIcon.Exclamation);\r
98         }\r
99 \r
100         private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)\r
101         {\r
102             if (e.Mode != PowerModes.Resume || !_config.Proxy.Auto)\r
103                 return;\r
104             Task.Run(() =>\r
105             {\r
106                 for (var i = 0; i < 5; i++, Thread.Sleep(15000))\r
107                     SystemProxy.Refresh();\r
108             });\r
109         }\r
110 \r
111         public void Shutdown()\r
112         {\r
113             Task.Run(() => ShutdownProxy());\r
114             if (_config.Proxy.Auto)\r
115                 RestoreSystemProxy();\r
116             SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;\r
117         }\r
118 \r
119         private void ShutdownProxy()\r
120         {\r
121             HttpProxy.Shutdown();\r
122         }\r
123 \r
124         private readonly AutoResetEvent _stopEvent = new AutoResetEvent(false);\r
125         private Task _checkerTask;\r
126 \r
127         private void SetAutoProxyUrl()\r
128         {\r
129             var url = $"http://localhost:{_config.Proxy.Listen}/proxy.pac";\r
130             _systemProxy.SetAutoProxyUrl(url);\r
131             if (_checkerTask != null && !_checkerTask.IsCompleted)\r
132                 return;\r
133             _checkerTask = Task.Run(() =>\r
134             {\r
135                 // Windows 10でプロキシ設定がいつの間にか消えるのに対応するために、\r
136                 // 設定が消えていないか毎秒確認して、消えていたら再設定する。\r
137                 do\r
138                 {\r
139                     var proxy = WebRequest.GetSystemWebProxy().GetProxy(new Uri("http://125.6.184.16/"));\r
140                     if (!proxy.IsLoopback)\r
141                     {\r
142                         File.AppendAllText("proxy.log", $"[{DateTime.Now:g}] proxy setting vanished.\r\n");\r
143                         _systemProxy.SetAutoProxyUrl(url);\r
144                     }\r
145                 } while (!_stopEvent.WaitOne(1000));\r
146             });\r
147         }\r
148 \r
149         private void RestoreSystemProxy()\r
150         {\r
151             if (_checkerTask != null && !_checkerTask.IsCompleted)\r
152             {\r
153                 _stopEvent.Set();\r
154                 _checkerTask.Wait();\r
155             }\r
156             _systemProxy.RestoreSettings();\r
157         }\r
158     }\r
159 }