OSDN Git Service

RepairListForMainをRepairListPanelに変える
[kancollesniffer/KancolleSniffer.git] / KancolleSniffer / Net / 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.Net\r
23 {\r
24     public class ProxyManager\r
25     {\r
26         private readonly Control _parent;\r
27         private readonly Config _config;\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();\r
32         private bool _initiated;\r
33         private DateTime _pacFileTime;\r
34 \r
35         public ProxyManager(Control parent, Config config)\r
36         {\r
37             _parent = parent;\r
38             _config = config;\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.Interval = 5000;\r
128             _timer.Start();\r
129         }\r
130 \r
131         private void CheckProxy(object sender, EventArgs ev)\r
132         {\r
133             if (_initiated)\r
134             {\r
135                 // Windows 10でプロキシ設定がいつの間にか消えるのに対応するために、\r
136                 // 設定が消えていないか毎秒確認して、消えていたら再設定する。\r
137                 if (IsProxyWorking)\r
138                     return;\r
139                 File.AppendAllText("proxy.log", $"[{DateTime.Now:G}] proxy setting vanished.\r\n");\r
140                 SetAutoConfigUrl();\r
141                 return;\r
142             }\r
143             if (IsProxyWorking)\r
144             {\r
145                 _initiated = true;\r
146                 _timer.Interval = 1000;\r
147                 return;\r
148             }\r
149             if (_autoConfigRetryCount > 0 && _autoConfigRetryCount % 6 == 0)\r
150             {\r
151                 _timer.Stop();\r
152                 switch (MessageBox.Show(_parent, "プロキシの自動設定に失敗しました。", "エラー", MessageBoxButtons.AbortRetryIgnore,\r
153                     MessageBoxIcon.Error))\r
154                 {\r
155                     case DialogResult.Abort:\r
156                         Shutdown();\r
157                         Environment.Exit(1);\r
158                         break;\r
159                     case DialogResult.Ignore:\r
160                         return;\r
161                 }\r
162                 _timer.Start();\r
163             }\r
164             _autoConfigRetryCount++;\r
165             SetAutoConfigUrl();\r
166         }\r
167 \r
168         private bool IsProxyWorking =>\r
169             WebRequest.GetSystemWebProxy().GetProxy(new Uri("http://125.6.184.16/")).IsLoopback;\r
170 \r
171         private void SetAutoConfigUrl()\r
172         {\r
173             var suffix = (DateTime.Now - _pacFileTime < TimeSpan.FromHours(6)\r
174                 ? (int)_pacFileTime.TimeOfDay.TotalSeconds\r
175                 : 0) + _autoConfigRetryCount;\r
176             _systemProxy.SetAutoConfigUrl(\r
177                 $"http://localhost:{_config.Proxy.Listen}/proxy{(suffix == 0 ? "" : suffix.ToString("x"))}.pac");\r
178         }\r
179 \r
180         private void RestoreSystemProxy()\r
181         {\r
182             _timer.Stop();\r
183             _systemProxy.RestoreSettings();\r
184         }\r
185 \r
186         public void UpdatePacFile()\r
187         {\r
188             var pacFile = "proxy.pac";\r
189             var request = (HttpWebRequest)WebRequest.Create($"https://kancollesniffer.osdn.jp/{pacFile}");\r
190             if (File.Exists(pacFile))\r
191             {\r
192                 _pacFileTime = File.GetLastWriteTime(pacFile);\r
193                 request.IfModifiedSince = _pacFileTime;\r
194             }\r
195             try\r
196             {\r
197                 var response = (HttpWebResponse)request.GetResponse();\r
198                 var mem = new MemoryStream();\r
199                 using (var stream = response.GetResponseStream())\r
200                     stream?.CopyTo(mem);\r
201                 mem.Position = 0;\r
202                 using (var file = new FileStream(pacFile, FileMode.Create))\r
203                     mem.CopyTo(file);\r
204                 _pacFileTime = File.GetLastWriteTime(pacFile);\r
205             }\r
206             // ReSharper disable once EmptyGeneralCatchClause\r
207             catch\r
208             {\r
209             }\r
210         }\r
211     }\r
212 }