Winify – Diff between revs 44 and 46

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