Winify – Blame information for rev 46

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