Winify – Blame information for rev 72

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 using System;
25 office 2 using System.Collections.Concurrent;
1 office 3 using System.ComponentModel;
30 office 4 using System.Drawing;
4 office 5 using System.IO;
48 office 6 using System.Net;
30 office 7 using System.Reflection;
15 office 8 using System.Text;
19 office 9 using System.Threading;
4 office 10 using System.Threading.Tasks;
1 office 11 using System.Windows.Forms;
30 office 12 using NetSparkleUpdater;
13 using NetSparkleUpdater.Enums;
14 using NetSparkleUpdater.SignatureVerifiers;
15 using NetSparkleUpdater.UI.WinForms;
18 office 16 using Serilog;
15 office 17 using Servers;
29 office 18 using Toasts;
1 office 19 using Winify.Gotify;
25 office 20 using Winify.Settings;
15 office 21 using Winify.Utilities;
30 office 22 using Winify.Utilities.Serialization;
56 office 23 using ScheduledContinuation = Toasts.ScheduledContinuation;
1 office 24  
25 namespace Winify
26 {
30 office 27 public partial class MainForm : Form
1 office 28 {
30 office 29 #region Public Enums, Properties and Fields
30  
31 public Configuration.Configuration Configuration { get; set; }
32  
33 public ScheduledContinuation ChangedConfigurationContinuation { get; set; }
34  
43 office 35 public bool MemorySinkEnabled { get; set; }
36  
30 office 37 #endregion
38  
1 office 39 #region Private Delegates, Events, Enums, Properties, Indexers and Fields
40  
41 private AboutForm _aboutForm;
42  
25 office 43 private ConcurrentBag<GotifyConnection> _gotifyConnections;
1 office 44  
45 private SettingsForm _settingsForm;
46  
30 office 47 private readonly SparkleUpdater _sparkle;
48  
49 private readonly CancellationTokenSource _cancellationTokenSource;
50  
51 private readonly CancellationToken _cancellationToken;
52  
43 office 53 private LogViewForm _logViewForm;
54  
55 private readonly LogMemorySink _memorySink;
56  
67 office 57 private readonly Toasts.Toasts _toasts;
56 office 58  
1 office 59 #endregion
60  
61 #region Constructors, Destructors and Finalizers
62  
30 office 63 public MainForm()
1 office 64 {
67 office 65 InitializeComponent();
66  
30 office 67 _cancellationTokenSource = new CancellationTokenSource();
68 _cancellationToken = _cancellationTokenSource.Token;
69  
70 ChangedConfigurationContinuation = new ScheduledContinuation();
56 office 71  
67 office 72 _toasts = new Toasts.Toasts(_cancellationToken);
30 office 73 }
74  
75 public MainForm(Mutex mutex) : this()
76 {
43 office 77 _memorySink = new LogMemorySink();
18 office 78 Log.Logger = new LoggerConfiguration()
79 .MinimumLevel.Debug()
43 office 80 .WriteTo.Conditional(condition => MemorySinkEnabled, configureSink => configureSink.Sink(_memorySink))
18 office 81 .WriteTo.File(Path.Combine(Constants.UserApplicationDirectory, "Logs", $"{Constants.AssemblyName}.log"),
82 rollingInterval: RollingInterval.Day)
83 .CreateLogger();
84  
30 office 85 // Start application update.
86 var manifestModuleName = Assembly.GetEntryAssembly().ManifestModule.FullyQualifiedName;
87 var icon = Icon.ExtractAssociatedIcon(manifestModuleName);
19 office 88  
30 office 89 _sparkle = new SparkleUpdater("https://winify.grimore.org/update/appcast.xml",
90 new Ed25519Checker(SecurityMode.Strict, "LonrgxVjSF0GnY4hzwlRJnLkaxnDn2ikdmOifILzLJY="))
4 office 91 {
30 office 92 UIFactory = new UIFactory(icon),
48 office 93 RelaunchAfterUpdate = true,
94 SecurityProtocolType = SecurityProtocolType.Tls12
30 office 95 };
96 _sparkle.StartLoop(true, true);
1 office 97 }
98  
99 /// <summary>
100 /// Clean up any resources being used.
101 /// </summary>
102 /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
103 protected override void Dispose(bool disposing)
104 {
30 office 105 if (disposing && components != null) components.Dispose();
7 office 106  
1 office 107 base.Dispose(disposing);
108 }
109  
110 #endregion
111  
112 #region Event Handlers
113  
30 office 114 private async void MainForm_Load(object sender, EventArgs e)
21 office 115 {
30 office 116 Configuration = await LoadConfiguration();
21 office 117  
30 office 118 var servers = await LoadServers();
119 _gotifyConnections = new ConcurrentBag<GotifyConnection>();
120 foreach (var server in servers.Server)
121 {
44 office 122 var gotifyConnection = new GotifyConnection(server, Configuration);
67 office 123 gotifyConnection.GotifyMessage += GotifyConnectionGotifyMessage;
30 office 124 gotifyConnection.Start();
125 _gotifyConnections.Add(gotifyConnection);
126 }
21 office 127 }
128  
56 office 129 private void LogViewToolStripMenuItem_Click(object sender, EventArgs e)
130 {
67 office 131 if (_logViewForm != null)
132 {
133 return;
134 }
56 office 135  
136 _logViewForm = new LogViewForm(this, _memorySink, _cancellationToken);
137 _logViewForm.Closing += LogViewForm_Closing;
138 _logViewForm.Show();
139 }
140  
141 private void LogViewForm_Closing(object sender, CancelEventArgs e)
142 {
67 office 143 if (_logViewForm == null)
144 {
145 return;
146 }
56 office 147  
148 _logViewForm.Closing -= LogViewForm_Closing;
149 _logViewForm.Close();
150 _logViewForm = null;
151 }
152  
25 office 153 private async void SettingsToolStripMenuItem_Click(object sender, EventArgs e)
11 office 154 {
30 office 155 if (_settingsForm == null)
156 {
157 var servers = await LoadServers();
158 var announcements = await LoadAnnouncements();
25 office 159  
30 office 160 _settingsForm = new SettingsForm(this, servers, announcements, _cancellationToken);
161 _settingsForm.Save += SettingsForm_Save;
162 _settingsForm.Closing += SettingsForm_Closing;
163 _settingsForm.Show();
164 }
25 office 165 }
166  
167 private async void SettingsForm_Save(object sender, SettingsSavedEventArgs e)
168 {
55 office 169 // Save the configuration.
170 Miscellaneous.LaunchOnBootSet(Configuration.LaunchOnBoot);
171 ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
172 async () => { await SaveConfiguration(); }, _cancellationToken);
173  
25 office 174 // Save the servers.
175 await Task.WhenAll(SaveServers(e.Servers), SaveAnnouncements(e.Announcements));
176  
177 // Update connections to gotify servers.
178 while (_gotifyConnections.TryTake(out var gotifyConnection))
179 {
67 office 180 gotifyConnection.GotifyMessage -= GotifyConnectionGotifyMessage;
181 await gotifyConnection.Stop();
25 office 182 gotifyConnection.Dispose();
183 }
184  
185 foreach (var server in e.Servers.Server)
186 {
44 office 187 var gotifyConnection = new GotifyConnection(server, Configuration);
67 office 188 gotifyConnection.GotifyMessage += GotifyConnectionGotifyMessage;
25 office 189 gotifyConnection.Start();
190 _gotifyConnections.Add(gotifyConnection);
191 }
192 }
193  
67 office 194 private async void GotifyConnectionGotifyMessage(object sender, GotifyMessageEventArgs e)
25 office 195 {
30 office 196 var announcements = await LoadAnnouncements();
25 office 197  
30 office 198 foreach (var announcement in announcements.Announcement)
67 office 199 {
200 if (announcement.AppId != e.Message.AppId)
30 office 201 {
67 office 202 continue;
30 office 203 }
24 office 204  
71 office 205 if (announcement.Ignore)
206 {
207 return;
208 }
209  
210 if (announcement.LingerTime <= 0)
211 {
212 return;
213 }
214  
67 office 215 var configuredNotification = new ToastForm(
216 $"{e.Message.Title} ({e.Message.Server.Name}/{e.Message.AppId})",
71 office 217 e.Message.Message, (int)announcement.LingerTime, e.Image);
67 office 218  
219 await _toasts.Queue(configuredNotification);
220  
221 return;
222 }
223  
55 office 224 if (Configuration.InfiniteToastDuration)
225 {
226 var infiniteToastForm = new ToastForm(
67 office 227 $"{e.Message.Title} ({e.Message.Server.Name}/{e.Message.AppId})",
228 e.Message.Message, e.Image);
55 office 229  
56 office 230 await _toasts.Queue(infiniteToastForm);
55 office 231  
232 return;
233 }
234  
235 var toastForm = new ToastForm(
67 office 236 $"{e.Message.Title} ({e.Message.Server.Name}/{e.Message.AppId})",
237 e.Message.Message, Configuration.ToastDuration, e.Image);
238  
56 office 239 await _toasts.Queue(toastForm);
11 office 240 }
241  
1 office 242 private void SettingsForm_Closing(object sender, CancelEventArgs e)
243 {
67 office 244 if (_settingsForm == null)
245 {
246 return;
247 }
6 office 248  
25 office 249 _settingsForm.Save -= SettingsForm_Save;
1 office 250 _settingsForm.Closing -= SettingsForm_Closing;
251 _settingsForm.Dispose();
252 _settingsForm = null;
253 }
254  
255 private void AboutToolStripMenuItem_Click(object sender, EventArgs e)
256 {
67 office 257 if (_aboutForm != null)
258 {
259 return;
260 }
1 office 261  
262 _aboutForm = new AboutForm();
263 _aboutForm.Closing += AboutForm_Closing;
264 _aboutForm.Show();
265 }
266  
267 private void AboutForm_Closing(object sender, CancelEventArgs e)
268 {
67 office 269 if (_aboutForm == null)
270 {
271 return;
272 }
1 office 273  
274 _aboutForm.Closing -= AboutForm_Closing;
275 _aboutForm.Dispose();
276 _aboutForm = null;
277 }
278  
279 private void QuitToolStripMenuItem_Click(object sender, EventArgs e)
280 {
17 office 281 Close();
30 office 282 }
17 office 283  
30 office 284 private async void UpdateToolStripMenuItem_Click(object sender, EventArgs e)
285 {
286 // Manually check for updates, this will not show a ui
287 var result = await _sparkle.CheckForUpdatesQuietly();
48 office 288 var updates = result.Updates;
30 office 289 if (result.Status == UpdateStatus.UpdateAvailable)
290 {
291 // if update(s) are found, then we have to trigger the UI to show it gracefully
292 _sparkle.ShowUpdateNeededUI();
293 return;
294 }
295  
49 office 296 MessageBox.Show("No updates available at this time.", "Winify", MessageBoxButtons.OK,
30 office 297 MessageBoxIcon.Asterisk,
298 MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly, false);
1 office 299 }
300  
30 office 301 #endregion
302  
303 #region Public Methods
304  
305 public async Task SaveConfiguration()
9 office 306 {
30 office 307 if (!Directory.Exists(Constants.UserApplicationDirectory))
308 Directory.CreateDirectory(Constants.UserApplicationDirectory);
309  
310 switch (await Serialization.Serialize(Configuration, Constants.ConfigurationFile, "Configuration",
311 "<!ATTLIST Configuration xmlns:xsi CDATA #IMPLIED xsi:noNamespaceSchemaLocation CDATA #IMPLIED>",
312 CancellationToken.None))
313 {
314 case SerializationSuccess<Configuration.Configuration> _:
315 Log.Information("Serialized configuration.");
316 break;
317 case SerializationFailure serializationFailure:
318 Log.Warning(serializationFailure.Exception.Message, "Failed to serialize configuration.");
319 break;
320 }
9 office 321 }
322  
30 office 323 public static async Task<Configuration.Configuration> LoadConfiguration()
324 {
325 if (!Directory.Exists(Constants.UserApplicationDirectory))
326 Directory.CreateDirectory(Constants.UserApplicationDirectory);
327  
328 var deserializationResult =
329 await Serialization.Deserialize<Configuration.Configuration>(Constants.ConfigurationFile,
330 Constants.ConfigurationNamespace, Constants.ConfigurationXsd, CancellationToken.None);
331  
332 switch (deserializationResult)
333 {
334 case SerializationSuccess<Configuration.Configuration> serializationSuccess:
335 return serializationSuccess.Result;
336 case SerializationFailure serializationFailure:
337 Log.Warning(serializationFailure.Exception, "Failed to load configuration.");
338 return new Configuration.Configuration();
339 default:
340 return new Configuration.Configuration();
341 }
342 }
343  
1 office 344 #endregion
4 office 345  
346 #region Private Methods
347  
25 office 348 private static async Task SaveAnnouncements(Announcements.Announcements announcements)
21 office 349 {
30 office 350 switch (await Serialization.Serialize(announcements, Constants.AnnouncementsFile, "Announcements",
351 "<!ATTLIST Announcements xmlns:xsi CDATA #IMPLIED xsi:noNamespaceSchemaLocation CDATA #IMPLIED>",
352 CancellationToken.None))
21 office 353 {
354 case SerializationFailure serializationFailure:
355 Log.Warning(serializationFailure.Exception, "Unable to serialize announcements.");
356 break;
357 }
358 }
359  
30 office 360 private static async Task SaveServers(Servers.Servers servers)
21 office 361 {
362 // Encrypt password for all servers.
363 var deviceId = Miscellaneous.GetMachineGuid();
30 office 364 var @protected = new Servers.Servers
21 office 365 {
366 Server = new BindingListWithCollectionChanged<Server>()
367 };
43 office 368  
25 office 369 foreach (var server in servers.Server)
21 office 370 {
41 office 371 var password = Encoding.UTF8.GetBytes(server.Password);
372 var encrypted = await AES.Encrypt(password, deviceId);
21 office 373 var armored = Convert.ToBase64String(encrypted);
374  
375 @protected.Server.Add(new Server(server.Name, server.Url, server.Username, armored));
376 }
377  
30 office 378 switch (await Serialization.Serialize(@protected, Constants.ServersFile, "Servers",
379 "<!ATTLIST Servers xmlns:xsi CDATA #IMPLIED xsi:noNamespaceSchemaLocation CDATA #IMPLIED>",
380 CancellationToken.None))
21 office 381 {
382 case SerializationFailure serializationFailure:
383 Log.Warning(serializationFailure.Exception, "Unable to serialize servers.");
384 break;
385 }
386 }
387  
15 office 388 private static async Task<Announcements.Announcements> LoadAnnouncements()
389 {
390 if (!Directory.Exists(Constants.UserApplicationDirectory))
391 Directory.CreateDirectory(Constants.UserApplicationDirectory);
392  
393 var deserializationResult =
30 office 394 await Serialization.Deserialize<Announcements.Announcements>(Constants.AnnouncementsFile,
395 "urn:winify-announcements-schema", "Announcements.xsd", CancellationToken.None);
15 office 396  
397 switch (deserializationResult)
398 {
399 case SerializationSuccess<Announcements.Announcements> serializationSuccess:
400 return serializationSuccess.Result;
21 office 401 case SerializationFailure serializationFailure:
402 Log.Warning(serializationFailure.Exception, "Unable to load announcements.");
403 return new Announcements.Announcements();
15 office 404 default:
405 return new Announcements.Announcements();
406 }
407 }
408  
30 office 409 private static async Task<Servers.Servers> LoadServers()
4 office 410 {
411 if (!Directory.Exists(Constants.UserApplicationDirectory))
412 Directory.CreateDirectory(Constants.UserApplicationDirectory);
413  
15 office 414 var deserializationResult =
30 office 415 await Serialization.Deserialize<Servers.Servers>(Constants.ServersFile,
416 "urn:winify-servers-schema", "Servers.xsd", CancellationToken.None);
4 office 417  
418 switch (deserializationResult)
419 {
30 office 420 case SerializationSuccess<Servers.Servers> serializationSuccess:
15 office 421 // Decrypt password.
422 var deviceId = Miscellaneous.GetMachineGuid();
30 office 423 var @protected = new Servers.Servers
15 office 424 {
425 Server = new BindingListWithCollectionChanged<Server>()
426 };
427 foreach (var server in serializationSuccess.Result.Server)
428 {
429 var unarmored = Convert.FromBase64String(server.Password);
72 office 430 byte[] decrypted;
431 try
432 {
433 decrypted = await AES.Decrypt(unarmored, deviceId);
434 }
435 catch(Exception exception)
436 {
437 Log.Warning(exception, $"Could not decrypt password for server {server.Name} in configuration file.");
438 continue;
439 }
4 office 440  
72 office 441 var password = Encoding.UTF8.GetString(decrypted);
442  
443 @protected.Server.Add(new Server(server.Name, server.Url, server.Username, password));
15 office 444 }
445  
446 return @protected;
447  
21 office 448 case SerializationFailure serializationFailure:
449 Log.Warning(serializationFailure.Exception, "Unable to load servers.");
30 office 450 return new Servers.Servers();
21 office 451  
4 office 452 default:
30 office 453 return new Servers.Servers();
4 office 454 }
455 }
456  
457 #endregion
1 office 458 }
459 }