Winify – Diff between revs 54 and 59

Subversion Repositories:
Rev:
Show entire fileIgnore whitespace
Rev 54 Rev 59
Line 2... Line 2...
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; -  
8 using System.Security.Authentication; 7 using System.Security.Authentication;
9 using System.Security.Cryptography.X509Certificates; 8 using System.Security.Cryptography.X509Certificates;
10 using System.Text; 9 using System.Text;
11 using System.Threading; 10 using System.Threading;
12 using System.Threading.Tasks; 11 using System.Threading.Tasks;
13 using Newtonsoft.Json; 12 using Newtonsoft.Json;
14 using Serilog; 13 using Serilog;
15 using Servers; 14 using Servers;
16 using WebSocketSharp; 15 using WebSocketSharp;
17 using WebSocketSharp.Net; 16 using WebSocketSharp.Net;
18 using static System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel; -  
19 using Configuration = Configuration.Configuration; -  
20 using ErrorEventArgs = WebSocketSharp.ErrorEventArgs; 17 using ErrorEventArgs = WebSocketSharp.ErrorEventArgs;
21 using NetworkCredential = System.Net.NetworkCredential; 18 using NetworkCredential = System.Net.NetworkCredential;
Line 22... Line 19...
22   19  
23 namespace Winify.Gotify 20 namespace Winify.Gotify
Line 44... Line 41...
44   41  
Line 45... Line 42...
45 private readonly Uri _webSocketsUri; 42 private readonly Uri _webSocketsUri;
46   43  
47 private readonly Uri _httpUri; 44 private readonly Uri _httpUri;
-   45 private WebSocket _webSocketSharp;
Line 48... Line 46...
48 private WebSocket _webSocketSharp; 46 private readonly Configuration.Configuration _configuration;
Line 49... Line 47...
49 private readonly global::Configuration.Configuration _configuration; 47 private Task _initTask;
Line 50... Line 48...
50   48  
51 #endregion 49 #endregion
52   -  
53 #region Constructors, Destructors and Finalizers 50  
Line 54... Line 51...
54   51 #region Constructors, Destructors and Finalizers
55 private GotifyConnection() 52  
56 { 53 private GotifyConnection()
57   54 {
Line 58... Line 55...
58 } 55 }
59   56  
60 public GotifyConnection(Server server, global::Configuration.Configuration configuration) : this() 57 public GotifyConnection(Server server, Configuration.Configuration configuration) : this()
61 { 58 {
62 _server = server; 59 _server = server;
63 _configuration = configuration; 60 _configuration = configuration;
Line 64... Line 61...
64   61  
65 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; 62 ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
66 var httpClientHandler = new HttpClientHandler() -  
67 { 63 var httpClientHandler = new HttpClientHandler
68 // mono does not implement this 64 {
69 //SslProtocols = SslProtocols.Tls12 -  
Line 70... Line 65...
70 }; 65 // mono does not implement this
71   -  
72 _httpClient = new HttpClient(httpClientHandler); 66 //SslProtocols = SslProtocols.Tls12
73 if (_configuration.IgnoreSelfSignedCertificates) 67 };
74 { -  
Line 75... Line 68...
75 httpClientHandler.ServerCertificateCustomValidationCallback = 68  
76 (httpRequestMessage, cert, cetChain, policyErrors) => true; 69 _httpClient = new HttpClient(httpClientHandler);
77 } -  
78   70 if (_configuration.IgnoreSelfSignedCertificates)
79 if (_configuration.Proxy.Enable) 71 httpClientHandler.ServerCertificateCustomValidationCallback =
80 { -  
Line 81... Line 72...
81 httpClientHandler.Proxy = new WebProxy(_configuration.Proxy.Url, false, new string[] { }, 72 (httpRequestMessage, cert, cetChain, policyErrors) => true;
82 new NetworkCredential(_configuration.Proxy.Username, _configuration.Proxy.Password)); 73  
83 } 74 if (_configuration.Proxy.Enable)
84   75 httpClientHandler.Proxy = new WebProxy(_configuration.Proxy.Url, false, new string[] { },
Line 111... Line 102...
111 { 102 {
112 webSocketsUriBuilder.Path = Path.Combine(webSocketsUriBuilder.Path, "stream"); 103 webSocketsUriBuilder.Path = Path.Combine(webSocketsUriBuilder.Path, "stream");
113 } 104 }
114 catch (ArgumentException exception) 105 catch (ArgumentException exception)
115 { 106 {
-   107 Log.Error(
116 Log.Error($"No WebSockets URL could be built from the provided URL {_server.Url} due to {exception.Message}"); 108 $"No WebSockets URL could be built from the provided URL {_server.Url} due to {exception.Message}");
117 } 109 }
Line 118... Line 110...
118   110  
119 _webSocketsUri = webSocketsUriBuilder.Uri; 111 _webSocketsUri = webSocketsUriBuilder.Uri;
Line 155... Line 147...
155 _cancellationTokenSource = new CancellationTokenSource(); 147 _cancellationTokenSource = new CancellationTokenSource();
156 _cancellationToken = _cancellationTokenSource.Token; 148 _cancellationToken = _cancellationTokenSource.Token;
Line 157... Line 149...
157   149  
Line -... Line 150...
-   150 Connect();
-   151  
-   152 if (_configuration.RetrievePastNotificationHours != 0)
-   153 {
-   154 _initTask = RetrievePastMessages(_cancellationToken);
158 Connect(); 155 }
159   156  
Line 160... Line 157...
160 _runTask = Run(_cancellationToken); 157 _runTask = Run(_cancellationToken);
161 } 158 }
162   159  
163 private void Connect() 160 private void Connect()
164 { 161 {
165 _webSocketSharp = new WebSocket(_webSocketsUri.AbsoluteUri); 162 _webSocketSharp = new WebSocket(_webSocketsUri.AbsoluteUri);
166 _webSocketSharp.SslConfiguration = new ClientSslConfiguration(_webSocketsUri.Host, -  
167 new X509CertificateCollection(new X509Certificate[] { }), SslProtocols.Tls12, false); 163 _webSocketSharp.SslConfiguration = new ClientSslConfiguration(_webSocketsUri.Host,
168 if (_configuration.Proxy.Enable) 164 new X509CertificateCollection(new X509Certificate[] { }), SslProtocols.Tls12, false);
Line 169... Line 165...
169 { 165 if (_configuration.Proxy.Enable)
170 _webSocketSharp.SetProxy(_configuration.Proxy.Url, _configuration.Proxy.Username, _configuration.Proxy.Password); -  
171 } 166 _webSocketSharp.SetProxy(_configuration.Proxy.Url, _configuration.Proxy.Username,
172   -  
Line 173... Line 167...
173 if (!string.IsNullOrEmpty(_server.Username) && !string.IsNullOrEmpty(_server.Password)) 167 _configuration.Proxy.Password);
174 { -  
175 _webSocketSharp.SetCredentials(_server.Username, _server.Password, true); 168  
176 } 169 if (!string.IsNullOrEmpty(_server.Username) && !string.IsNullOrEmpty(_server.Password))
177   -  
Line 178... Line 170...
178 if (_configuration.IgnoreSelfSignedCertificates) 170 _webSocketSharp.SetCredentials(_server.Username, _server.Password, true);
179 { 171  
180 _webSocketSharp.SslConfiguration.ServerCertificateValidationCallback += 172 if (_configuration.IgnoreSelfSignedCertificates)
181 (sender, certificate, chain, errors) => true; 173 _webSocketSharp.SslConfiguration.ServerCertificateValidationCallback +=
Line 182... Line 174...
182 } 174 (sender, certificate, chain, errors) => true;
183   175  
184 _webSocketSharp.Log.Output = (logData, s) => 176 _webSocketSharp.Log.Output = (logData, s) =>
185 { 177 {
186 Log.Information($"WebSockets low level logging reported: {logData.Message}"); 178 Log.Information($"WebSockets low level logging reported: {logData.Message}");
187 }; 179 };
188   180  
Line 189... Line 181...
189 _webSocketSharp.OnMessage += WebSocketSharp_OnMessage; 181 _webSocketSharp.OnMessage += WebSocketSharp_OnMessage;
190 _webSocketSharp.OnError += WebSocketSharp_OnError; 182 _webSocketSharp.OnError += WebSocketSharp_OnError;
-   183 _webSocketSharp.OnOpen += WebSocketSharp_OnOpen;
191 _webSocketSharp.OnOpen += WebSocketSharp_OnOpen; 184 _webSocketSharp.OnClose += WebSocketSharp_OnClose;
192 _webSocketSharp.OnClose += WebSocketSharp_OnClose; 185  
Line 193... Line 186...
193 186 _webSocketSharp.ConnectAsync();
194 _webSocketSharp.ConnectAsync(); 187 }
195 } 188  
196   189 private void WebSocketSharp_OnClose(object sender, CloseEventArgs e)
Line 197... Line 190...
197 private void WebSocketSharp_OnClose(object sender, CloseEventArgs e) 190 {
198 { 191 Log.Information(
-   192 $"WebSockets connection to server {_webSocketsUri.AbsoluteUri} closed with reason {e.Reason}");
199 Log.Information($"WebSockets connection to server {_webSocketsUri.AbsoluteUri} closed with reason {e.Reason}"); 193 }
-   194  
Line 200... Line 195...
200 } 195 private void WebSocketSharp_OnOpen(object sender, EventArgs e)
201   196 {
202 private void WebSocketSharp_OnOpen(object sender, EventArgs e) 197 Log.Information($"WebSockets connection to server {_webSocketsUri.AbsoluteUri} is now open");
203 { 198 }
Line 222... Line 217...
222   217  
223 private async void WebSocketSharp_OnMessage(object sender, MessageEventArgs e) 218 private async void WebSocketSharp_OnMessage(object sender, MessageEventArgs e)
224 { 219 {
225 if (e.RawData.Length == 0) 220 if (e.RawData.Length == 0)
226 { 221 {
227 Log.Warning($"Empty message received from server"); 222 Log.Warning("Empty message received from server");
228 return; 223 return;
Line 229... Line 224...
229 } 224 }
Line 230... Line 225...
230   225  
Line 231... Line 226...
231 var message = Encoding.UTF8.GetString(e.RawData, 0, e.RawData.Length); 226 var message = Encoding.UTF8.GetString(e.RawData, 0, e.RawData.Length);
232   227  
233 GotifyNotification gotifyNotification; 228 GotifyMessage gotifyNotification;
234   229  
235 try 230 try
236 { 231 {
237 gotifyNotification = JsonConvert.DeserializeObject<GotifyNotification>(message); 232 gotifyNotification = JsonConvert.DeserializeObject<GotifyMessage>(message);
238 } 233 }
Line 249... Line 244...
249 return; 244 return;
250 } 245 }
Line 251... Line 246...
251   246  
Line 252... Line 247...
252 gotifyNotification.Server = _server; 247 gotifyNotification.Server = _server;
253   248  
254 if (!Uri.TryCreate(Path.Combine($"{_httpUri}", "application"), UriKind.Absolute, 249 var applicationUriBuilder = new UriBuilder(_httpUri);
-   250 try
-   251 {
-   252 applicationUriBuilder.Path = Path.Combine(applicationUriBuilder.Path, "application");
-   253 }
255 out var applicationUri)) 254 catch (ArgumentException exception)
-   255 {
256 { 256 Log.Warning("Could not build an URI to an application");
257 Log.Warning($"Could not build an URI to an application"); 257  
Line 258... Line 258...
258 return; 258 return;
259 } 259 }
-   260  
260   261 using (var imageStream =
261 using (var imageStream = 262 await RetrieveGotifyApplicationImage(gotifyNotification.AppId, applicationUriBuilder.Uri,
262 await RetrieveGotifyApplicationImage(gotifyNotification.AppId, applicationUri, _cancellationToken)) 263 _cancellationToken))
263 { 264 {
264 if (imageStream == null) 265 if (imageStream == null)
Line 276... Line 277...
276 Log.Debug($"Notification message received: {gotifyNotification.Message}"); 277 Log.Debug($"Notification message received: {gotifyNotification.Message}");
277 } 278 }
Line 278... Line 279...
278   279  
279 public void Stop() 280 public void Stop()
280 { 281 {
-   282 if (_cancellationTokenSource == null) return;
-   283  
281 if (_cancellationTokenSource != null) _cancellationTokenSource.Cancel(); 284 _cancellationTokenSource.Cancel();
Line 282... Line 285...
282 } 285 }
Line 283... Line 286...
283   286  
Line -... Line 287...
-   287 #endregion
-   288  
-   289 #region Private Methods
-   290  
-   291 private async Task RetrievePastMessages(CancellationToken cancellationToken)
-   292 {
-   293 var messageUriBuilder = new UriBuilder(_httpUri);
-   294 foreach (var application in await RetrieveGotifyApplications(cancellationToken))
-   295 {
-   296 try
-   297 {
-   298 messageUriBuilder.Path = Path.Combine(messageUriBuilder.Path, "application", $"{application.Id}",
-   299 "message");
-   300 }
-   301 catch (ArgumentException exception)
-   302 {
-   303 Log.Error($"No application URL could be built for {_server.Url} due to {exception.Message}");
-   304  
-   305 continue;
-   306 }
-   307  
-   308 var messagesResponse = await _httpClient.GetAsync(messageUriBuilder.Uri, cancellationToken);
-   309  
-   310  
-   311 var messages = await messagesResponse.Content.ReadAsStringAsync();
-   312  
-   313 GotifyMessageQuery gotifyMessageQuery;
-   314 try
-   315 {
-   316 gotifyMessageQuery =
-   317 JsonConvert.DeserializeObject<GotifyMessageQuery>(messages);
-   318 }
-   319 catch (JsonSerializationException exception)
-   320 {
-   321 Log.Warning($"Could not deserialize the message response: {exception.Message}");
-   322  
-   323 continue;
-   324 }
-   325  
-   326 var applicationUriBuilder = new UriBuilder(_httpUri);
-   327 try
-   328 {
-   329 applicationUriBuilder.Path = Path.Combine(applicationUriBuilder.Path, "application");
-   330 }
-   331 catch (ArgumentException exception)
-   332 {
-   333 Log.Warning($"Could not build an URI to an application: {exception}");
-   334  
-   335 return;
-   336 }
-   337  
-   338 foreach (var message in gotifyMessageQuery.Messages)
-   339 {
-   340 if (message.Date < DateTime.Now - TimeSpan.FromHours(_configuration.RetrievePastNotificationHours))
-   341 continue;
-   342  
-   343 message.Server = _server;
-   344  
-   345 using (var imageStream =
-   346 await RetrieveGotifyApplicationImage(message.AppId, applicationUriBuilder.Uri,
-   347 _cancellationToken))
-   348 {
-   349 if (imageStream == null)
-   350 {
-   351 Log.Warning("Could not find any application image for notification");
-   352 return;
-   353 }
-   354  
-   355 var image = Image.FromStream(imageStream);
-   356  
-   357 GotifyNotification?.Invoke(this,
-   358 new GotifyNotificationEventArgs(message, image));
-   359 }
284 #endregion 360 }
285   361 }
286 #region Private Methods 362 }
287   363  
288 private async Task Run(CancellationToken cancellationToken) 364 private async Task Run(CancellationToken cancellationToken)
289 { 365 {
290 try 366 try
291 { 367 {
292 do 368 do
293 { 369 {
-   370 await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
294 await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); 371 } while (!cancellationToken.IsCancellationRequested);
295 } while (!cancellationToken.IsCancellationRequested); 372 }
296 } 373 catch (Exception exception) when (exception is OperationCanceledException ||
297 catch (Exception exception) when (exception is OperationCanceledException || exception is ObjectDisposedException) 374 exception is ObjectDisposedException)
298 { 375 {
299 } 376 }
300 catch (Exception exception) 377 catch (Exception exception)
Line -... Line 378...
-   378 {
-   379 Log.Warning(exception, "Failure running connection loop");
-   380 }
-   381 }
-   382  
-   383 private async Task<GotifyApplication[]> RetrieveGotifyApplications(CancellationToken cancellationToken)
-   384 {
-   385 var applicationsUriBuilder = new UriBuilder(_httpUri);
-   386 try
-   387 {
-   388 applicationsUriBuilder.Path = Path.Combine(applicationsUriBuilder.Path, "application");
-   389 }
-   390 catch (ArgumentException exception)
-   391 {
-   392 Log.Error($"No application URL could be built for {_server.Url} due to {exception}");
-   393 }
-   394  
-   395 var applicationsResponse = await _httpClient.GetAsync(applicationsUriBuilder.Uri, cancellationToken);
-   396  
-   397 var applications = await applicationsResponse.Content.ReadAsStringAsync();
-   398  
-   399 GotifyApplication[] gotifyApplications;
-   400 try
-   401 {
-   402 gotifyApplications =
-   403 JsonConvert.DeserializeObject<GotifyApplication[]>(applications);
-   404 }
-   405 catch (JsonSerializationException exception)
-   406 {
-   407 Log.Warning($"Could not deserialize the list of applications from the server: {exception}");
-   408  
-   409 return null;
301 { 410 }
302 Log.Warning(exception, "Failure running connection loop"); 411  
303 } 412 return gotifyApplications;
304 } 413 }