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