Winify – Blame information for rev 56

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