/trunk/Winify/Gotify/GotifyConnection.cs |
@@ -4,7 +4,6 @@ |
using System.Net; |
using System.Net.Http; |
using System.Net.Http.Headers; |
using System.Net.Security; |
using System.Security.Authentication; |
using System.Security.Cryptography.X509Certificates; |
using System.Text; |
@@ -15,8 +14,6 @@ |
using Servers; |
using WebSocketSharp; |
using WebSocketSharp.Net; |
using static System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel; |
using Configuration = Configuration.Configuration; |
using ErrorEventArgs = WebSocketSharp.ErrorEventArgs; |
using NetworkCredential = System.Net.NetworkCredential; |
|
@@ -46,7 +43,8 @@ |
|
private readonly Uri _httpUri; |
private WebSocket _webSocketSharp; |
private readonly global::Configuration.Configuration _configuration; |
private readonly Configuration.Configuration _configuration; |
private Task _initTask; |
|
#endregion |
|
@@ -54,16 +52,15 @@ |
|
private GotifyConnection() |
{ |
|
} |
|
public GotifyConnection(Server server, global::Configuration.Configuration configuration) : this() |
public GotifyConnection(Server server, Configuration.Configuration configuration) : this() |
{ |
_server = server; |
_configuration = configuration; |
|
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; |
var httpClientHandler = new HttpClientHandler() |
var httpClientHandler = new HttpClientHandler |
{ |
// mono does not implement this |
//SslProtocols = SslProtocols.Tls12 |
@@ -71,23 +68,17 @@ |
|
_httpClient = new HttpClient(httpClientHandler); |
if (_configuration.IgnoreSelfSignedCertificates) |
{ |
httpClientHandler.ServerCertificateCustomValidationCallback = |
(httpRequestMessage, cert, cetChain, policyErrors) => true; |
} |
|
if (_configuration.Proxy.Enable) |
{ |
httpClientHandler.Proxy = new WebProxy(_configuration.Proxy.Url, false, new string[] { }, |
new NetworkCredential(_configuration.Proxy.Username, _configuration.Proxy.Password)); |
} |
|
_httpClient = new HttpClient(httpClientHandler); |
if (!string.IsNullOrEmpty(_server.Username) && !string.IsNullOrEmpty(_server.Password)) |
{ |
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", |
Convert.ToBase64String(Encoding.Default.GetBytes($"{_server.Username}:{_server.Password}"))); |
} |
|
if (!Uri.TryCreate(_server.Url, UriKind.Absolute, out _httpUri)) |
{ |
@@ -113,7 +104,8 @@ |
} |
catch (ArgumentException exception) |
{ |
Log.Error($"No WebSockets URL could be built from the provided URL {_server.Url} due to {exception.Message}"); |
Log.Error( |
$"No WebSockets URL could be built from the provided URL {_server.Url} due to {exception.Message}"); |
} |
|
_webSocketsUri = webSocketsUriBuilder.Uri; |
@@ -157,6 +149,11 @@ |
|
Connect(); |
|
if (_configuration.RetrievePastNotificationHours != 0) |
{ |
_initTask = RetrievePastMessages(_cancellationToken); |
} |
|
_runTask = Run(_cancellationToken); |
} |
|
@@ -166,20 +163,15 @@ |
_webSocketSharp.SslConfiguration = new ClientSslConfiguration(_webSocketsUri.Host, |
new X509CertificateCollection(new X509Certificate[] { }), SslProtocols.Tls12, false); |
if (_configuration.Proxy.Enable) |
{ |
_webSocketSharp.SetProxy(_configuration.Proxy.Url, _configuration.Proxy.Username, _configuration.Proxy.Password); |
} |
_webSocketSharp.SetProxy(_configuration.Proxy.Url, _configuration.Proxy.Username, |
_configuration.Proxy.Password); |
|
if (!string.IsNullOrEmpty(_server.Username) && !string.IsNullOrEmpty(_server.Password)) |
{ |
_webSocketSharp.SetCredentials(_server.Username, _server.Password, true); |
} |
|
if (_configuration.IgnoreSelfSignedCertificates) |
{ |
_webSocketSharp.SslConfiguration.ServerCertificateValidationCallback += |
(sender, certificate, chain, errors) => true; |
} |
|
_webSocketSharp.Log.Output = (logData, s) => |
{ |
@@ -196,7 +188,8 @@ |
|
private void WebSocketSharp_OnClose(object sender, CloseEventArgs e) |
{ |
Log.Information($"WebSockets connection to server {_webSocketsUri.AbsoluteUri} closed with reason {e.Reason}"); |
Log.Information( |
$"WebSockets connection to server {_webSocketsUri.AbsoluteUri} closed with reason {e.Reason}"); |
} |
|
private void WebSocketSharp_OnOpen(object sender, EventArgs e) |
@@ -206,7 +199,9 @@ |
|
private async void WebSocketSharp_OnError(object sender, ErrorEventArgs e) |
{ |
Log.Error($"Connection to WebSockets server {_webSocketsUri.AbsoluteUri} terminated unexpectedly with message {e.Message}", e.Exception); |
Log.Error( |
$"Connection to WebSockets server {_webSocketsUri.AbsoluteUri} terminated unexpectedly with message {e.Message}", |
e.Exception); |
|
if (_cancellationToken.IsCancellationRequested) |
{ |
@@ -224,17 +219,17 @@ |
{ |
if (e.RawData.Length == 0) |
{ |
Log.Warning($"Empty message received from server"); |
Log.Warning("Empty message received from server"); |
return; |
} |
|
var message = Encoding.UTF8.GetString(e.RawData, 0, e.RawData.Length); |
|
GotifyNotification gotifyNotification; |
GotifyMessage gotifyNotification; |
|
try |
{ |
gotifyNotification = JsonConvert.DeserializeObject<GotifyNotification>(message); |
gotifyNotification = JsonConvert.DeserializeObject<GotifyMessage>(message); |
} |
catch (JsonSerializationException exception) |
{ |
@@ -251,15 +246,21 @@ |
|
gotifyNotification.Server = _server; |
|
if (!Uri.TryCreate(Path.Combine($"{_httpUri}", "application"), UriKind.Absolute, |
out var applicationUri)) |
var applicationUriBuilder = new UriBuilder(_httpUri); |
try |
{ |
Log.Warning($"Could not build an URI to an application"); |
applicationUriBuilder.Path = Path.Combine(applicationUriBuilder.Path, "application"); |
} |
catch (ArgumentException exception) |
{ |
Log.Warning("Could not build an URI to an application"); |
|
return; |
} |
|
using (var imageStream = |
await RetrieveGotifyApplicationImage(gotifyNotification.AppId, applicationUri, _cancellationToken)) |
await RetrieveGotifyApplicationImage(gotifyNotification.AppId, applicationUriBuilder.Uri, |
_cancellationToken)) |
{ |
if (imageStream == null) |
{ |
@@ -278,7 +279,9 @@ |
|
public void Stop() |
{ |
if (_cancellationTokenSource != null) _cancellationTokenSource.Cancel(); |
if (_cancellationTokenSource == null) return; |
|
_cancellationTokenSource.Cancel(); |
} |
|
#endregion |
@@ -285,6 +288,79 @@ |
|
#region Private Methods |
|
private async Task RetrievePastMessages(CancellationToken cancellationToken) |
{ |
var messageUriBuilder = new UriBuilder(_httpUri); |
foreach (var application in await RetrieveGotifyApplications(cancellationToken)) |
{ |
try |
{ |
messageUriBuilder.Path = Path.Combine(messageUriBuilder.Path, "application", $"{application.Id}", |
"message"); |
} |
catch (ArgumentException exception) |
{ |
Log.Error($"No application URL could be built for {_server.Url} due to {exception.Message}"); |
|
continue; |
} |
|
var messagesResponse = await _httpClient.GetAsync(messageUriBuilder.Uri, cancellationToken); |
|
|
var messages = await messagesResponse.Content.ReadAsStringAsync(); |
|
GotifyMessageQuery gotifyMessageQuery; |
try |
{ |
gotifyMessageQuery = |
JsonConvert.DeserializeObject<GotifyMessageQuery>(messages); |
} |
catch (JsonSerializationException exception) |
{ |
Log.Warning($"Could not deserialize the message response: {exception.Message}"); |
|
continue; |
} |
|
var applicationUriBuilder = new UriBuilder(_httpUri); |
try |
{ |
applicationUriBuilder.Path = Path.Combine(applicationUriBuilder.Path, "application"); |
} |
catch (ArgumentException exception) |
{ |
Log.Warning($"Could not build an URI to an application: {exception}"); |
|
return; |
} |
|
foreach (var message in gotifyMessageQuery.Messages) |
{ |
if (message.Date < DateTime.Now - TimeSpan.FromHours(_configuration.RetrievePastNotificationHours)) |
continue; |
|
message.Server = _server; |
|
using (var imageStream = |
await RetrieveGotifyApplicationImage(message.AppId, applicationUriBuilder.Uri, |
_cancellationToken)) |
{ |
if (imageStream == null) |
{ |
Log.Warning("Could not find any application image for notification"); |
return; |
} |
|
var image = Image.FromStream(imageStream); |
|
GotifyNotification?.Invoke(this, |
new GotifyNotificationEventArgs(message, image)); |
} |
} |
} |
} |
|
private async Task Run(CancellationToken cancellationToken) |
{ |
try |
@@ -294,7 +370,8 @@ |
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); |
} while (!cancellationToken.IsCancellationRequested); |
} |
catch (Exception exception) when (exception is OperationCanceledException || exception is ObjectDisposedException) |
catch (Exception exception) when (exception is OperationCanceledException || |
exception is ObjectDisposedException) |
{ |
} |
catch (Exception exception) |
@@ -303,6 +380,38 @@ |
} |
} |
|
private async Task<GotifyApplication[]> RetrieveGotifyApplications(CancellationToken cancellationToken) |
{ |
var applicationsUriBuilder = new UriBuilder(_httpUri); |
try |
{ |
applicationsUriBuilder.Path = Path.Combine(applicationsUriBuilder.Path, "application"); |
} |
catch (ArgumentException exception) |
{ |
Log.Error($"No application URL could be built for {_server.Url} due to {exception}"); |
} |
|
var applicationsResponse = await _httpClient.GetAsync(applicationsUriBuilder.Uri, cancellationToken); |
|
var applications = await applicationsResponse.Content.ReadAsStringAsync(); |
|
GotifyApplication[] gotifyApplications; |
try |
{ |
gotifyApplications = |
JsonConvert.DeserializeObject<GotifyApplication[]>(applications); |
} |
catch (JsonSerializationException exception) |
{ |
Log.Warning($"Could not deserialize the list of applications from the server: {exception}"); |
|
return null; |
} |
|
return gotifyApplications; |
} |
|
private async Task<Stream> RetrieveGotifyApplicationImage(int appId, Uri applicationUri, |
CancellationToken cancellationToken) |
{ |