Winify – Diff between revs 50 and 51

Subversion Repositories:
Rev:
Only display areas with differencesIgnore whitespace
Rev 50 Rev 51
1 using System; 1 using System;
2 using System.Drawing; 2 using System.Drawing;
3 using System.IO; 3 using System.IO;
4 using System.Net; 4 using System.Net;
5 using System.Net.Http; 5 using System.Net.Http;
6 using System.Net.Http.Headers; 6 using System.Net.Http.Headers;
7 using System.Net.Security; 7 using System.Net.Security;
8 using System.Security.Authentication; 8 using System.Security.Authentication;
9 using System.Security.Cryptography.X509Certificates; 9 using System.Security.Cryptography.X509Certificates;
10 using System.Text; 10 using System.Text;
11 using System.Threading; 11 using System.Threading;
12 using System.Threading.Tasks; 12 using System.Threading.Tasks;
13 using Newtonsoft.Json; 13 using Newtonsoft.Json;
14 using Serilog; 14 using Serilog;
15 using Servers; 15 using Servers;
16 using WebSocketSharp; 16 using WebSocketSharp;
17 using WebSocketSharp.Net; 17 using WebSocketSharp.Net;
18 using static System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel; 18 using static System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel;
19 using Configuration = Configuration.Configuration; 19 using Configuration = Configuration.Configuration;
20 using ErrorEventArgs = WebSocketSharp.ErrorEventArgs; 20 using ErrorEventArgs = WebSocketSharp.ErrorEventArgs;
21 using NetworkCredential = System.Net.NetworkCredential; 21 using NetworkCredential = System.Net.NetworkCredential;
22   22  
23 namespace Winify.Gotify 23 namespace Winify.Gotify
24 { 24 {
25 public class GotifyConnection : IDisposable 25 public class GotifyConnection : IDisposable
26 { 26 {
27 #region Public Events & Delegates 27 #region Public Events & Delegates
28   28  
29 public event EventHandler<GotifyNotificationEventArgs> GotifyNotification; 29 public event EventHandler<GotifyNotificationEventArgs> GotifyNotification;
30   30  
31 #endregion 31 #endregion
32   32  
33 #region Private Delegates, Events, Enums, Properties, Indexers and Fields 33 #region Private Delegates, Events, Enums, Properties, Indexers and Fields
34   34  
35 private readonly Server _server; 35 private readonly Server _server;
36   36  
37 private CancellationToken _cancellationToken; 37 private CancellationToken _cancellationToken;
38   38  
39 private CancellationTokenSource _cancellationTokenSource; 39 private CancellationTokenSource _cancellationTokenSource;
40   40  
41 private Task _runTask; 41 private Task _runTask;
42   42  
43 private HttpClient _httpClient; 43 private HttpClient _httpClient;
44   44  
45 private readonly Uri _webSocketsUri; 45 private readonly Uri _webSocketsUri;
46   46  
47 private readonly Uri _httpUri; 47 private readonly Uri _httpUri;
48 private WebSocket _webSocketSharp; 48 private WebSocket _webSocketSharp;
49 private readonly global::Configuration.Configuration _configuration; 49 private readonly global::Configuration.Configuration _configuration;
50   50  
51 #endregion 51 #endregion
52   52  
53 #region Constructors, Destructors and Finalizers 53 #region Constructors, Destructors and Finalizers
54   54  
55 private GotifyConnection() 55 private GotifyConnection()
56 { 56 {
57   57  
58 } 58 }
59   59  
60 public GotifyConnection(Server server, global::Configuration.Configuration configuration) : this() 60 public GotifyConnection(Server server, global::Configuration.Configuration configuration) : this()
61 { 61 {
62 _server = server; 62 _server = server;
63 _configuration = configuration; 63 _configuration = configuration;
64   64  
65 var httpClientHandler = new HttpClientHandler() 65 var httpClientHandler = new HttpClientHandler()
66 { 66 {
67 SslProtocols = SslProtocols.Tls12 67 SslProtocols = SslProtocols.Tls12
68 }; 68 };
69   69  
70 _httpClient = new HttpClient(httpClientHandler); 70 _httpClient = new HttpClient(httpClientHandler);
71 if (_configuration.IgnoreSelfSignedCertificates) 71 if (_configuration.IgnoreSelfSignedCertificates)
72 { 72 {
73 httpClientHandler.ServerCertificateCustomValidationCallback = 73 httpClientHandler.ServerCertificateCustomValidationCallback =
74 (httpRequestMessage, cert, cetChain, policyErrors) => true; 74 (httpRequestMessage, cert, cetChain, policyErrors) => true;
75 } 75 }
76   76  
77 if (_configuration.Proxy.Enable) 77 if (_configuration.Proxy.Enable)
78 { 78 {
79 httpClientHandler.Proxy = new WebProxy(_configuration.Proxy.Url, false, new string[] { }, 79 httpClientHandler.Proxy = new WebProxy(_configuration.Proxy.Url, false, new string[] { },
80 new NetworkCredential(_configuration.Proxy.Username, _configuration.Proxy.Password)); 80 new NetworkCredential(_configuration.Proxy.Username, _configuration.Proxy.Password));
81 } 81 }
82   82  
83 _httpClient = new HttpClient(httpClientHandler); 83 _httpClient = new HttpClient(httpClientHandler);
84 if (!string.IsNullOrEmpty(_server.Username) && !string.IsNullOrEmpty(_server.Password)) 84 if (!string.IsNullOrEmpty(_server.Username) && !string.IsNullOrEmpty(_server.Password))
85 { 85 {
86 _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", 86 _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
87 Convert.ToBase64String(Encoding.Default.GetBytes($"{_server.Username}:{_server.Password}"))); 87 Convert.ToBase64String(Encoding.Default.GetBytes($"{_server.Username}:{_server.Password}")));
88 } 88 }
89   89  
90 if (!Uri.TryCreate(_server.Url, UriKind.Absolute, out _httpUri)) 90 if (!Uri.TryCreate(_server.Url, UriKind.Absolute, out _httpUri))
91 { 91 {
92 Log.Error($"No HTTP URL could be built out of the supplied server URI {_server.Url}."); 92 Log.Error($"No HTTP URL could be built out of the supplied server URI {_server.Url}");
93 return; 93 return;
94 } 94 }
95   95  
96 // Build the web sockets URI. 96 // Build the web sockets URI.
97 var webSocketsUriBuilder = new UriBuilder(_httpUri); 97 var webSocketsUriBuilder = new UriBuilder(_httpUri);
98 switch (webSocketsUriBuilder.Scheme.ToUpperInvariant()) 98 switch (webSocketsUriBuilder.Scheme.ToUpperInvariant())
99 { 99 {
100 case "HTTP": 100 case "HTTP":
101 webSocketsUriBuilder.Scheme = "ws"; 101 webSocketsUriBuilder.Scheme = "ws";
102 break; 102 break;
103 case "HTTPS": 103 case "HTTPS":
104 webSocketsUriBuilder.Scheme = "wss"; 104 webSocketsUriBuilder.Scheme = "wss";
105 break; 105 break;
106 } 106 }
107   107  
108 try 108 try
109 { 109 {
110 webSocketsUriBuilder.Path = Path.Combine(webSocketsUriBuilder.Path, "stream"); 110 webSocketsUriBuilder.Path = Path.Combine(webSocketsUriBuilder.Path, "stream");
111 } 111 }
112 catch (ArgumentException exception) 112 catch (ArgumentException exception)
113 { 113 {
114 Log.Error($"No WebSockets URL could be built from the provided URL {_server.Url} due to {exception.Message}."); 114 Log.Error($"No WebSockets URL could be built from the provided URL {_server.Url} due to {exception.Message}");
115 } 115 }
116   116  
117 _webSocketsUri = webSocketsUriBuilder.Uri; 117 _webSocketsUri = webSocketsUriBuilder.Uri;
118 } 118 }
119   119  
120 public void Dispose() 120 public void Dispose()
121 { 121 {
122 if (_cancellationTokenSource != null) 122 if (_cancellationTokenSource != null)
123 { 123 {
124 _cancellationTokenSource.Dispose(); 124 _cancellationTokenSource.Dispose();
125 _cancellationTokenSource = null; 125 _cancellationTokenSource = null;
126 } 126 }
127   127  
128 if (_webSocketSharp != null) 128 if (_webSocketSharp != null)
129 { 129 {
130 _webSocketSharp.Close(); 130 _webSocketSharp.Close();
131 _webSocketSharp = null; 131 _webSocketSharp = null;
132 } 132 }
133   133  
134 if (_httpClient != null) 134 if (_httpClient != null)
135 { 135 {
136 _httpClient.Dispose(); 136 _httpClient.Dispose();
137 _httpClient = null; 137 _httpClient = null;
138 } 138 }
139 } 139 }
140   140  
141 #endregion 141 #endregion
142   142  
143 #region Public Methods 143 #region Public Methods
144   144  
145 public void Start() 145 public void Start()
146 { 146 {
147 if (_webSocketsUri == null || _httpUri == null) 147 if (_webSocketsUri == null || _httpUri == null)
148 { 148 {
149 Log.Error("Could not start connection to server due to unreadable URLs."); 149 Log.Error("Could not start connection to server due to unreadable URLs");
150 return; 150 return;
151 } 151 }
152   152  
153 _cancellationTokenSource = new CancellationTokenSource(); 153 _cancellationTokenSource = new CancellationTokenSource();
154 _cancellationToken = _cancellationTokenSource.Token; 154 _cancellationToken = _cancellationTokenSource.Token;
155   155  
156 Connect(); 156 Connect();
157   157  
158 _runTask = Run(_cancellationToken); 158 _runTask = Run(_cancellationToken);
159 } 159 }
160   160  
161 private void Connect() 161 private void Connect()
162 { 162 {
163 _webSocketSharp = new WebSocket(_webSocketsUri.AbsoluteUri); 163 _webSocketSharp = new WebSocket(_webSocketsUri.AbsoluteUri);
164 _webSocketSharp.SslConfiguration = new ClientSslConfiguration(_webSocketsUri.Host, 164 _webSocketSharp.SslConfiguration = new ClientSslConfiguration(_webSocketsUri.Host,
165 new X509CertificateCollection(new X509Certificate[] { }), SslProtocols.Tls12, false); 165 new X509CertificateCollection(new X509Certificate[] { }), SslProtocols.Tls12, false);
166 if (_configuration.Proxy.Enable) 166 if (_configuration.Proxy.Enable)
167 { 167 {
168 _webSocketSharp.SetProxy(_configuration.Proxy.Url, _configuration.Proxy.Username, _configuration.Proxy.Password); 168 _webSocketSharp.SetProxy(_configuration.Proxy.Url, _configuration.Proxy.Username, _configuration.Proxy.Password);
169 } 169 }
170   170  
171 if (!string.IsNullOrEmpty(_server.Username) && !string.IsNullOrEmpty(_server.Password)) 171 if (!string.IsNullOrEmpty(_server.Username) && !string.IsNullOrEmpty(_server.Password))
172 { 172 {
173 _webSocketSharp.SetCredentials(_server.Username, _server.Password, true); 173 _webSocketSharp.SetCredentials(_server.Username, _server.Password, true);
174 } 174 }
175   175  
176 if (_configuration.IgnoreSelfSignedCertificates) 176 if (_configuration.IgnoreSelfSignedCertificates)
177 { 177 {
178 _webSocketSharp.SslConfiguration.ServerCertificateValidationCallback += 178 _webSocketSharp.SslConfiguration.ServerCertificateValidationCallback +=
179 (sender, certificate, chain, errors) => true; 179 (sender, certificate, chain, errors) => true;
180 } 180 }
-   181  
-   182 _webSocketSharp.Log.Output = (logData, s) =>
-   183 {
-   184 Log.Information($"WebSockets low level logging reported: {logData.Message}");
-   185 };
181   186  
182 _webSocketSharp.OnMessage += WebSocketSharp_OnMessage; 187 _webSocketSharp.OnMessage += WebSocketSharp_OnMessage;
183 _webSocketSharp.OnError += WebSocketSharp_OnError; 188 _webSocketSharp.OnError += WebSocketSharp_OnError;
184 _webSocketSharp.OnOpen += WebSocketSharp_OnOpen; 189 _webSocketSharp.OnOpen += WebSocketSharp_OnOpen;
185 _webSocketSharp.OnClose += WebSocketSharp_OnClose; 190 _webSocketSharp.OnClose += WebSocketSharp_OnClose;
186 _webSocketSharp.Log.Output = (data, s) => -  
187 { 191
188 var a = s; -  
189 }; -  
190 _webSocketSharp.ConnectAsync(); 192 _webSocketSharp.ConnectAsync();
191 } 193 }
192   194  
193 private void WebSocketSharp_OnClose(object sender, CloseEventArgs e) 195 private void WebSocketSharp_OnClose(object sender, CloseEventArgs e)
194 { 196 {
195 Log.Information($"WebSockets connection to server {_webSocketsUri.AbsoluteUri} closed with reason {e.Reason}."); 197 Log.Information($"WebSockets connection to server {_webSocketsUri.AbsoluteUri} closed with reason {e.Reason}");
196 } 198 }
197   199  
198 private void WebSocketSharp_OnOpen(object sender, EventArgs e) 200 private void WebSocketSharp_OnOpen(object sender, EventArgs e)
199 { 201 {
200 Log.Information($"WebSockets connection to server {_webSocketsUri.AbsoluteUri} is now open."); 202 Log.Information($"WebSockets connection to server {_webSocketsUri.AbsoluteUri} is now open");
201 } 203 }
202   204  
203 private async void WebSocketSharp_OnError(object sender, ErrorEventArgs e) 205 private async void WebSocketSharp_OnError(object sender, ErrorEventArgs e)
204 { 206 {
205 Log.Error($"Connection to WebSockets server {_webSocketsUri.AbsoluteUri} terminated unexpectedly with message {e.Message}.", e.Exception); 207 Log.Error($"Connection to WebSockets server {_webSocketsUri.AbsoluteUri} terminated unexpectedly with message {e.Message}", e.Exception);
206   208  
207 if (_cancellationToken.IsCancellationRequested) 209 if (_cancellationToken.IsCancellationRequested)
208 { 210 {
209 Stop(); 211 Stop();
210 return; 212 return;
211 } 213 }
212   214  
213 await Task.Delay(TimeSpan.FromSeconds(1), _cancellationToken); 215 await Task.Delay(TimeSpan.FromSeconds(1), _cancellationToken);
214 Log.Information($"Reconnecting to websocket server {_webSocketsUri.AbsoluteUri}."); 216 Log.Information($"Reconnecting to websocket server {_webSocketsUri.AbsoluteUri}");
215   217  
216 Connect(); 218 Connect();
217 } 219 }
218   220  
219 private async void WebSocketSharp_OnMessage(object sender, MessageEventArgs e) 221 private async void WebSocketSharp_OnMessage(object sender, MessageEventArgs e)
220 { 222 {
221 if (e.RawData.Length == 0) 223 if (e.RawData.Length == 0)
222 { 224 {
223 Log.Warning($"Empty message received from server."); 225 Log.Warning($"Empty message received from server");
224 return; 226 return;
225 } 227 }
226   228  
227 var message = Encoding.UTF8.GetString(e.RawData, 0, e.RawData.Length); 229 var message = Encoding.UTF8.GetString(e.RawData, 0, e.RawData.Length);
228   230  
229 GotifyNotification gotifyNotification; 231 GotifyNotification gotifyNotification;
230   232  
231 try 233 try
232 { 234 {
233 gotifyNotification = JsonConvert.DeserializeObject<GotifyNotification>(message); 235 gotifyNotification = JsonConvert.DeserializeObject<GotifyNotification>(message);
234 } 236 }
235 catch (JsonSerializationException exception) 237 catch (JsonSerializationException exception)
236 { 238 {
237 Log.Warning($"Could not deserialize notification: {exception.Message}"); 239 Log.Warning($"Could not deserialize notification: {exception.Message}");
238 return; 240 return;
239 } 241 }
240   242  
241 if (gotifyNotification == null) 243 if (gotifyNotification == null)
242 { 244 {
243 Log.Warning($"Could not deserialize gotify notification: {message}"); 245 Log.Warning($"Could not deserialize gotify notification: {message}");
244   246  
245 return; 247 return;
246 } 248 }
247   249  
248 gotifyNotification.Server = _server; 250 gotifyNotification.Server = _server;
249   251  
250 if (!Uri.TryCreate(Path.Combine($"{_httpUri}", "application"), UriKind.Absolute, 252 if (!Uri.TryCreate(Path.Combine($"{_httpUri}", "application"), UriKind.Absolute,
251 out var applicationUri)) 253 out var applicationUri))
252 { 254 {
253 Log.Warning($"Could not build an URI to an application."); 255 Log.Warning($"Could not build an URI to an application");
254 return; 256 return;
255 } 257 }
256   258  
257 using (var imageStream = 259 using (var imageStream =
258 await RetrieveGotifyApplicationImage(gotifyNotification.AppId, applicationUri, _cancellationToken)) 260 await RetrieveGotifyApplicationImage(gotifyNotification.AppId, applicationUri, _cancellationToken))
259 { 261 {
260 if (imageStream == null) 262 if (imageStream == null)
261 { 263 {
262 Log.Warning("Could not find any application image for notification."); 264 Log.Warning("Could not find any application image for notification");
263 return; 265 return;
264 } 266 }
265   267  
266 var image = Image.FromStream(imageStream); 268 var image = Image.FromStream(imageStream);
267   269  
268 GotifyNotification?.Invoke(this, 270 GotifyNotification?.Invoke(this,
269 new GotifyNotificationEventArgs(gotifyNotification, image)); 271 new GotifyNotificationEventArgs(gotifyNotification, image));
270 } 272 }
271   273  
272 Log.Debug($"Notification message received: {gotifyNotification.Message}"); 274 Log.Debug($"Notification message received: {gotifyNotification.Message}");
273 } 275 }
274   276  
275 public void Stop() 277 public void Stop()
276 { 278 {
277 if (_cancellationTokenSource != null) _cancellationTokenSource.Cancel(); 279 if (_cancellationTokenSource != null) _cancellationTokenSource.Cancel();
278 } 280 }
279   281  
280 #endregion 282 #endregion
281   283  
282 #region Private Methods 284 #region Private Methods
283   285  
284 private async Task Run(CancellationToken cancellationToken) 286 private async Task Run(CancellationToken cancellationToken)
285 { 287 {
286 try 288 try
287 { 289 {
288 do 290 do
289 { 291 {
290 await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); 292 await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
291 } while (!cancellationToken.IsCancellationRequested); 293 } while (!cancellationToken.IsCancellationRequested);
292 } 294 }
293 catch (Exception exception) when (exception is OperationCanceledException || exception is ObjectDisposedException) 295 catch (Exception exception) when (exception is OperationCanceledException || exception is ObjectDisposedException)
294 { 296 {
295 } 297 }
296 catch (Exception exception) 298 catch (Exception exception)
297 { 299 {
298 Log.Warning(exception, "Failure running connection loop."); 300 Log.Warning(exception, "Failure running connection loop");
299 } 301 }
300 } 302 }
301   303  
302 private async Task<Stream> RetrieveGotifyApplicationImage(int appId, Uri applicationUri, 304 private async Task<Stream> RetrieveGotifyApplicationImage(int appId, Uri applicationUri,
303 CancellationToken cancellationToken) 305 CancellationToken cancellationToken)
304 { 306 {
305 var applicationResponse = await _httpClient.GetAsync(applicationUri, cancellationToken); 307 var applicationResponse = await _httpClient.GetAsync(applicationUri, cancellationToken);
306   308  
307 var applications = await applicationResponse.Content.ReadAsStringAsync(); 309 var applications = await applicationResponse.Content.ReadAsStringAsync();
308   310  
309 GotifyApplication[] gotifyApplications; 311 GotifyApplication[] gotifyApplications;
310 try 312 try
311 { 313 {
312 gotifyApplications = 314 gotifyApplications =
313 JsonConvert.DeserializeObject<GotifyApplication[]>(applications); 315 JsonConvert.DeserializeObject<GotifyApplication[]>(applications);
314 } 316 }
315 catch (JsonSerializationException exception) 317 catch (JsonSerializationException exception)
316 { 318 {
317 Log.Warning($"Could not deserialize the list of applications from the server: {exception.Message}"); 319 Log.Warning($"Could not deserialize the list of applications from the server: {exception.Message}");
318   320  
319 return null; 321 return null;
320 } 322 }
321   323  
322 foreach (var application in gotifyApplications) 324 foreach (var application in gotifyApplications)
323 { 325 {
324 if (application.Id != appId) continue; 326 if (application.Id != appId) continue;
325   327  
326 if (!Uri.TryCreate(Path.Combine($"{_httpUri}", $"{application.Image}"), UriKind.Absolute, 328 if (!Uri.TryCreate(Path.Combine($"{_httpUri}", $"{application.Image}"), UriKind.Absolute,
327 out var applicationImageUri)) 329 out var applicationImageUri))
328 { 330 {
329 Log.Warning("Could not build URL path to application icon."); 331 Log.Warning("Could not build URL path to application icon");
330 continue; 332 continue;
331 } 333 }
332   334  
333 var imageResponse = await _httpClient.GetAsync(applicationImageUri, cancellationToken); 335 var imageResponse = await _httpClient.GetAsync(applicationImageUri, cancellationToken);
334   336  
335 var memoryStream = new MemoryStream(); 337 var memoryStream = new MemoryStream();
336   338  
337 await imageResponse.Content.CopyToAsync(memoryStream); 339 await imageResponse.Content.CopyToAsync(memoryStream);
338   340  
339 return memoryStream; 341 return memoryStream;
340 } 342 }
341   343  
342 return null; 344 return null;
343 } 345 }
344   346  
345 #endregion 347 #endregion
346 } 348 }
347 } 349 }
348   350  
349
Generated by GNU Enscript 1.6.5.90.
351
Generated by GNU Enscript 1.6.5.90.
350   352  
351   353  
352   354