Winify – Blame information for rev 67

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  
67 office 205 var configuredNotification = new ToastForm(
206 $"{e.Message.Title} ({e.Message.Server.Name}/{e.Message.AppId})",
207 e.Message.Message, announcement.LingerTime, e.Image);
208  
209 await _toasts.Queue(configuredNotification);
210  
211 return;
212 }
213  
55 office 214 if (Configuration.InfiniteToastDuration)
215 {
216 var infiniteToastForm = new ToastForm(
67 office 217 $"{e.Message.Title} ({e.Message.Server.Name}/{e.Message.AppId})",
218 e.Message.Message, e.Image);
55 office 219  
56 office 220 await _toasts.Queue(infiniteToastForm);
55 office 221  
222 return;
223 }
224  
225 var toastForm = new ToastForm(
67 office 226 $"{e.Message.Title} ({e.Message.Server.Name}/{e.Message.AppId})",
227 e.Message.Message, Configuration.ToastDuration, e.Image);
228  
56 office 229 await _toasts.Queue(toastForm);
11 office 230 }
231  
1 office 232 private void SettingsForm_Closing(object sender, CancelEventArgs e)
233 {
67 office 234 if (_settingsForm == null)
235 {
236 return;
237 }
6 office 238  
25 office 239 _settingsForm.Save -= SettingsForm_Save;
1 office 240 _settingsForm.Closing -= SettingsForm_Closing;
241 _settingsForm.Dispose();
242 _settingsForm = null;
243 }
244  
245 private void AboutToolStripMenuItem_Click(object sender, EventArgs e)
246 {
67 office 247 if (_aboutForm != null)
248 {
249 return;
250 }
1 office 251  
252 _aboutForm = new AboutForm();
253 _aboutForm.Closing += AboutForm_Closing;
254 _aboutForm.Show();
255 }
256  
257 private void AboutForm_Closing(object sender, CancelEventArgs e)
258 {
67 office 259 if (_aboutForm == null)
260 {
261 return;
262 }
1 office 263  
264 _aboutForm.Closing -= AboutForm_Closing;
265 _aboutForm.Dispose();
266 _aboutForm = null;
267 }
268  
269 private void QuitToolStripMenuItem_Click(object sender, EventArgs e)
270 {
17 office 271 Close();
30 office 272 }
17 office 273  
30 office 274 private async void UpdateToolStripMenuItem_Click(object sender, EventArgs e)
275 {
276 // Manually check for updates, this will not show a ui
277 var result = await _sparkle.CheckForUpdatesQuietly();
48 office 278 var updates = result.Updates;
30 office 279 if (result.Status == UpdateStatus.UpdateAvailable)
280 {
281 // if update(s) are found, then we have to trigger the UI to show it gracefully
282 _sparkle.ShowUpdateNeededUI();
283 return;
284 }
285  
49 office 286 MessageBox.Show("No updates available at this time.", "Winify", MessageBoxButtons.OK,
30 office 287 MessageBoxIcon.Asterisk,
288 MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly, false);
1 office 289 }
290  
30 office 291 #endregion
292  
293 #region Public Methods
294  
295 public async Task SaveConfiguration()
9 office 296 {
30 office 297 if (!Directory.Exists(Constants.UserApplicationDirectory))
298 Directory.CreateDirectory(Constants.UserApplicationDirectory);
299  
300 switch (await Serialization.Serialize(Configuration, Constants.ConfigurationFile, "Configuration",
301 "<!ATTLIST Configuration xmlns:xsi CDATA #IMPLIED xsi:noNamespaceSchemaLocation CDATA #IMPLIED>",
302 CancellationToken.None))
303 {
304 case SerializationSuccess<Configuration.Configuration> _:
305 Log.Information("Serialized configuration.");
306 break;
307 case SerializationFailure serializationFailure:
308 Log.Warning(serializationFailure.Exception.Message, "Failed to serialize configuration.");
309 break;
310 }
9 office 311 }
312  
30 office 313 public static async Task<Configuration.Configuration> LoadConfiguration()
314 {
315 if (!Directory.Exists(Constants.UserApplicationDirectory))
316 Directory.CreateDirectory(Constants.UserApplicationDirectory);
317  
318 var deserializationResult =
319 await Serialization.Deserialize<Configuration.Configuration>(Constants.ConfigurationFile,
320 Constants.ConfigurationNamespace, Constants.ConfigurationXsd, CancellationToken.None);
321  
322 switch (deserializationResult)
323 {
324 case SerializationSuccess<Configuration.Configuration> serializationSuccess:
325 return serializationSuccess.Result;
326 case SerializationFailure serializationFailure:
327 Log.Warning(serializationFailure.Exception, "Failed to load configuration.");
328 return new Configuration.Configuration();
329 default:
330 return new Configuration.Configuration();
331 }
332 }
333  
1 office 334 #endregion
4 office 335  
336 #region Private Methods
337  
25 office 338 private static async Task SaveAnnouncements(Announcements.Announcements announcements)
21 office 339 {
30 office 340 switch (await Serialization.Serialize(announcements, Constants.AnnouncementsFile, "Announcements",
341 "<!ATTLIST Announcements xmlns:xsi CDATA #IMPLIED xsi:noNamespaceSchemaLocation CDATA #IMPLIED>",
342 CancellationToken.None))
21 office 343 {
344 case SerializationFailure serializationFailure:
345 Log.Warning(serializationFailure.Exception, "Unable to serialize announcements.");
346 break;
347 }
348 }
349  
30 office 350 private static async Task SaveServers(Servers.Servers servers)
21 office 351 {
352 // Encrypt password for all servers.
353 var deviceId = Miscellaneous.GetMachineGuid();
30 office 354 var @protected = new Servers.Servers
21 office 355 {
356 Server = new BindingListWithCollectionChanged<Server>()
357 };
43 office 358  
25 office 359 foreach (var server in servers.Server)
21 office 360 {
41 office 361 var password = Encoding.UTF8.GetBytes(server.Password);
362 var encrypted = await AES.Encrypt(password, deviceId);
21 office 363 var armored = Convert.ToBase64String(encrypted);
364  
365 @protected.Server.Add(new Server(server.Name, server.Url, server.Username, armored));
366 }
367  
30 office 368 switch (await Serialization.Serialize(@protected, Constants.ServersFile, "Servers",
369 "<!ATTLIST Servers xmlns:xsi CDATA #IMPLIED xsi:noNamespaceSchemaLocation CDATA #IMPLIED>",
370 CancellationToken.None))
21 office 371 {
372 case SerializationFailure serializationFailure:
373 Log.Warning(serializationFailure.Exception, "Unable to serialize servers.");
374 break;
375 }
376 }
377  
15 office 378 private static async Task<Announcements.Announcements> LoadAnnouncements()
379 {
380 if (!Directory.Exists(Constants.UserApplicationDirectory))
381 Directory.CreateDirectory(Constants.UserApplicationDirectory);
382  
383 var deserializationResult =
30 office 384 await Serialization.Deserialize<Announcements.Announcements>(Constants.AnnouncementsFile,
385 "urn:winify-announcements-schema", "Announcements.xsd", CancellationToken.None);
15 office 386  
387 switch (deserializationResult)
388 {
389 case SerializationSuccess<Announcements.Announcements> serializationSuccess:
390 return serializationSuccess.Result;
21 office 391 case SerializationFailure serializationFailure:
392 Log.Warning(serializationFailure.Exception, "Unable to load announcements.");
393 return new Announcements.Announcements();
15 office 394 default:
395 return new Announcements.Announcements();
396 }
397 }
398  
30 office 399 private static async Task<Servers.Servers> LoadServers()
4 office 400 {
401 if (!Directory.Exists(Constants.UserApplicationDirectory))
402 Directory.CreateDirectory(Constants.UserApplicationDirectory);
403  
15 office 404 var deserializationResult =
30 office 405 await Serialization.Deserialize<Servers.Servers>(Constants.ServersFile,
406 "urn:winify-servers-schema", "Servers.xsd", CancellationToken.None);
4 office 407  
408 switch (deserializationResult)
409 {
30 office 410 case SerializationSuccess<Servers.Servers> serializationSuccess:
15 office 411 // Decrypt password.
412 var deviceId = Miscellaneous.GetMachineGuid();
30 office 413 var @protected = new Servers.Servers
15 office 414 {
415 Server = new BindingListWithCollectionChanged<Server>()
416 };
417 foreach (var server in serializationSuccess.Result.Server)
418 {
419 var unarmored = Convert.FromBase64String(server.Password);
41 office 420 var result = await AES.Decrypt(unarmored, deviceId);
421 var decrypted = Encoding.UTF8.GetString(result);
4 office 422  
15 office 423 @protected.Server.Add(new Server(server.Name, server.Url, server.Username, decrypted));
424 }
425  
426 return @protected;
427  
21 office 428 case SerializationFailure serializationFailure:
429 Log.Warning(serializationFailure.Exception, "Unable to load servers.");
30 office 430 return new Servers.Servers();
21 office 431  
4 office 432 default:
30 office 433 return new Servers.Servers();
4 office 434 }
435 }
436  
437 #endregion
1 office 438 }
439 }