Winify – Blame information for rev 49

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