Winify – Blame information for rev 42

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