Winify – Blame information for rev 44

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