OSDN Git Service

283564d87812a6f40435c152129fdf4b49326cb3
[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.Windows.Forms;\r
20 using Microsoft.Win32;\r
21 \r
22 namespace KancolleSniffer\r
23 {\r
24     public class ProxyManager\r
25     {\r
26         private readonly Config _config;\r
27         private readonly Control _parent;\r
28         private string _prevAutoConfigUrl;\r
29         private int _prevProxyPort;\r
30         private int _autoConfigRetryCount;\r
31         private readonly Timer _timer = new Timer {Interval = 1000};\r
32         private bool _initiated;\r
33 \r
34         public ProxyManager(Config config, Control parent)\r
35         {\r
36             _config = config;\r
37             _parent = parent;\r
38             SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;\r
39             _timer.Tick += CheckProxy;\r
40         }\r
41 \r
42         public bool ApplyConfig()\r
43         {\r
44             if (!_config.Proxy.Auto)\r
45                 RestoreSystemProxy();\r
46             if (_config.Proxy.UseUpstream)\r
47             {\r
48                 HttpProxy.UpstreamProxyHost = "127.0.0.1";\r
49                 HttpProxy.UpstreamProxyPort = _config.Proxy.UpstreamPort;\r
50             }\r
51             HttpProxy.IsEnableUpstreamProxy = _config.Proxy.UseUpstream;\r
52             var result = true;\r
53             if (!HttpProxy.IsInListening || _config.Proxy.Listen != _prevProxyPort)\r
54             {\r
55                 ShutdownProxy();\r
56                 result = StartProxy();\r
57             }\r
58             if (_config.Proxy.Auto && result)\r
59             {\r
60                 if (_prevAutoConfigUrl == null)\r
61                 {\r
62                     SystemProxy.AdjustLocalIntranetZoneFlags();\r
63                     _prevAutoConfigUrl = SystemProxy.AutoConfigUrl;\r
64                 }\r
65                 SetAndCheckAutoConfigUrl();\r
66             }\r
67             _prevProxyPort = _config.Proxy.Listen;\r
68             return result;\r
69         }\r
70 \r
71         private bool StartProxy()\r
72         {\r
73             try\r
74             {\r
75                 HttpProxy.Startup(_config.Proxy.Listen, false, false);\r
76             }\r
77             catch (SocketException e)\r
78             {\r
79                 if (e.SocketErrorCode != SocketError.AddressAlreadyInUse &&\r
80                     e.SocketErrorCode != SocketError.AccessDenied)\r
81                 {\r
82                     throw;\r
83                 }\r
84                 if (WarnConflictPortNumber(_config.Proxy.Listen, _config.Proxy.Auto) == DialogResult.No ||\r
85                     !_config.Proxy.Auto)\r
86                 {\r
87                     RestoreSystemProxy();\r
88                     return false;\r
89                 }\r
90                 HttpProxy.Startup(0, false, false);\r
91                 _config.Proxy.Listen = HttpProxy.LocalPort;\r
92             }\r
93             return true;\r
94         }\r
95 \r
96         private DialogResult WarnConflictPortNumber(int port, bool auto)\r
97         {\r
98             var msg = $"ポート番号{port}は他のアプリケーションが使用中です。";\r
99             var cap = "ポート番号の衝突";\r
100             return auto\r
101                 ? MessageBox.Show(_parent, msg + "自動的に別の番号を割り当てますか?", cap,\r
102                     MessageBoxButtons.YesNo, MessageBoxIcon.Question)\r
103                 : MessageBox.Show(_parent, msg + "設定ダイアログでポート番号を変更してください。", cap,\r
104                     MessageBoxButtons.OK, MessageBoxIcon.Exclamation);\r
105         }\r
106 \r
107         private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e)\r
108         {\r
109             if (e.Mode != PowerModes.Resume || !_config.Proxy.Auto)\r
110                 return;\r
111             SystemProxy.Refresh();\r
112         }\r
113 \r
114         public void Shutdown()\r
115         {\r
116             ShutdownProxy();\r
117             if (_config.Proxy.Auto)\r
118                 RestoreSystemProxy();\r
119             SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;\r
120         }\r
121 \r
122         private void ShutdownProxy()\r
123         {\r
124             HttpProxy.Shutdown();\r
125         }\r
126 \r
127         private void SetAndCheckAutoConfigUrl()\r
128         {\r
129             SetAutoConfigUrl();\r
130             _initiated = false;\r
131             _timer.Start();\r
132         }\r
133 \r
134         private void CheckProxy(object sender, EventArgs ev)\r
135         {\r
136             if (_initiated)\r
137             {\r
138                 // Windows 10でプロキシ設定がいつの間にか消えるのに対応するために、\r
139                 // 設定が消えていないか毎秒確認して、消えていたら再設定する。\r
140                 if (IsProxyWorking)\r
141                     return;\r
142                 File.AppendAllText("proxy.log", $"[{DateTime.Now:G}] proxy setting vanished.\r\n");\r
143                 SetAutoConfigUrl();\r
144                 return;\r
145             }\r
146             if (IsProxyWorking)\r
147             {\r
148                 _initiated = true;\r
149                 return;\r
150             }\r
151             if (_autoConfigRetryCount > 0 && _autoConfigRetryCount % 5 == 0)\r
152             {\r
153                 _timer.Stop();\r
154                 switch (MessageBox.Show(_parent, "プロキシの自動設定に失敗しました。", "エラー", MessageBoxButtons.AbortRetryIgnore,\r
155                     MessageBoxIcon.Error))\r
156                 {\r
157                     case DialogResult.Abort:\r
158                         Shutdown();\r
159                         Environment.Exit(1);\r
160                         break;\r
161                     case DialogResult.Ignore:\r
162                         return;\r
163                 }\r
164                 _timer.Start();\r
165             }\r
166             _autoConfigRetryCount++;\r
167             SetAutoConfigUrl();\r
168         }\r
169 \r
170         private bool IsProxyWorking =>\r
171             WebRequest.GetSystemWebProxy().GetProxy(new Uri("http://125.6.184.16/")).IsLoopback;\r
172 \r
173         private void SetAutoConfigUrl()\r
174         {\r
175             var count = _autoConfigRetryCount == 0 ? "" : _autoConfigRetryCount.ToString();\r
176             SystemProxy.AutoConfigUrl = $"http://localhost:{_config.Proxy.Listen}/proxy{count}.pac";\r
177         }\r
178 \r
179         private void RestoreSystemProxy()\r
180         {\r
181             _timer.Stop();\r
182             if (_prevAutoConfigUrl == null)\r
183                 return;\r
184             if (_prevAutoConfigUrl == "")\r
185             {\r
186                 SystemProxy.AutoConfigUrl = "";\r
187                 return;\r
188             }\r
189             HttpWebRequest request;\r
190             try\r
191             {\r
192                 request = WebRequest.CreateHttp(_prevAutoConfigUrl);\r
193                 if (!request.Address.IsLoopback)\r
194                     throw new Exception();\r
195             }\r
196             catch\r
197             {\r
198                 // httpではなくloopbackでもない\r
199                 SystemProxy.AutoConfigUrl = _prevAutoConfigUrl;\r
200                 return;\r
201             }\r
202             try\r
203             {\r
204                 request.GetResponse().Dispose();\r
205             }\r
206             catch\r
207             {\r
208                 // httpサーバーが応答しない\r
209                 SystemProxy.AutoConfigUrl = "";\r
210                 return;\r
211             }\r
212             SystemProxy.AutoConfigUrl = _prevAutoConfigUrl;\r
213         }\r
214 \r
215         public void UpdatePacFile()\r
216         {\r
217             var request = (HttpWebRequest)WebRequest.Create("https://kancollesniffer.osdn.jp/proxy.pac");\r
218             if (File.Exists("proxy.pac"))\r
219             {\r
220                 var date = File.GetLastWriteTime("proxy.pac");\r
221                 request.IfModifiedSince = date;\r
222             }\r
223             try\r
224             {\r
225                 var response = (HttpWebResponse)request.GetResponse();\r
226                 using (var stream = response.GetResponseStream())\r
227                 using (var file = new FileStream("proxy.pac", FileMode.OpenOrCreate))\r
228                     stream?.CopyTo(file);\r
229             }\r
230             // ReSharper disable once EmptyGeneralCatchClause\r
231             catch\r
232             {\r
233             }\r
234         }\r
235     }\r
236 }