Horizon – Blame information for rev 12

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 using System;
2 using System.Collections.Concurrent;
3 using System.Collections.Generic;
4 using System.ComponentModel;
5 using System.Data.SQLite;
6 using System.Diagnostics;
7 using System.Drawing;
8 using System.IO;
12 office 9 using System.IO.Ports;
1 office 10 using System.Linq;
12 office 11 using System.Net.Sockets;
1 office 12 using System.Security.Cryptography;
12 office 13 using System.Text;
1 office 14 using System.Threading;
15 using System.Threading.Tasks;
16 using System.Windows.Forms;
17 using Horizon.Database;
18 using Horizon.Utilities;
19 using Microsoft.WindowsAPICodePack.Dialogs;
12 office 20 using Mono.Zeroconf;
21 using Mono.Zeroconf.Providers.Bonjour;
22 using Newtonsoft.Json;
5 office 23 using Org.BouncyCastle.Crypto;
12 office 24 using Org.BouncyCastle.Utilities.Net;
1 office 25 using Serilog;
12 office 26 using WatsonTcp;
1 office 27  
28 namespace Horizon.Snapshots
29 {
30 public partial class SnapshotManagerForm : Form
31 {
32 #region Static Fields and Constants
33  
34 private static ScheduledContinuation _searchTextBoxChangedContinuation;
35  
36 #endregion
37  
38 #region Public Events & Delegates
39  
40 public event EventHandler<PreviewRetrievedEventArgs> PreviewRetrieved;
41  
42 #endregion
43  
44 #region Private Delegates, Events, Enums, Properties, Indexers and Fields
45  
46 private readonly MainForm _mainForm;
47  
48 private readonly SnapshotDatabase _snapshotDatabase;
49  
50 private HexViewForm _hexViewForm;
51  
52 private SnapshotNoteForm _snapshotNote;
53  
54 private SnapshotPreviewForm _snapshotPreviewForm;
55  
56 private readonly object _mouseMoveLock = new object();
57  
58 private readonly CancellationTokenSource _cancellationTokenSource;
59  
60 private readonly CancellationToken _cancellationToken;
61  
62 private readonly CancellationTokenSource _localCancellationTokenSource;
63  
64 private readonly CancellationToken _localCancellationToken;
65  
12 office 66 private readonly Mono.Zeroconf.ServiceBrowser _horizonServiceBrowser;
67  
68 private IResolvableService _resolvedService;
69  
70 private readonly ConcurrentDictionary<string, Service> _discoveredHorizonNetworkShares;
71  
1 office 72 #endregion
73  
74 #region Constructors, Destructors and Finalizers
75  
5 office 76 private SnapshotManagerForm()
1 office 77 {
78 InitializeComponent();
79  
80 dataGridView1.Columns["TimeColumn"].ValueType = typeof(DateTime);
81  
82 _searchTextBoxChangedContinuation = new ScheduledContinuation();
83  
84 _localCancellationTokenSource = new CancellationTokenSource();
85 _localCancellationToken = _localCancellationTokenSource.Token;
12 office 86  
87 _discoveredHorizonNetworkShares = new ConcurrentDictionary<string, Service>();
88 _horizonServiceBrowser = new Mono.Zeroconf.ServiceBrowser();
89 _horizonServiceBrowser.ServiceAdded += OnHorizonServiceBrowserOnServiceAdded;
1 office 90 }
91  
92 public SnapshotManagerForm(MainForm mainForm, SnapshotDatabase snapshotDatabase,
93 CancellationToken cancellationToken) : this()
94 {
95 _mainForm = mainForm;
96 _snapshotDatabase = snapshotDatabase;
97 _snapshotDatabase.SnapshotCreate += SnapshotManager_SnapshotCreate;
98  
99 _cancellationTokenSource =
100 CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, cancellationToken);
101 _cancellationToken = _cancellationTokenSource.Token;
102 }
103  
104 /// <summary>
105 /// Clean up any resources being used.
106 /// </summary>
107 /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
108 protected override void Dispose(bool disposing)
109 {
110 if (disposing && components != null)
111 {
112 components.Dispose();
113 }
114  
115 _snapshotDatabase.SnapshotCreate -= SnapshotManager_SnapshotCreate;
116  
12 office 117 _horizonServiceBrowser.ServiceAdded -= OnHorizonServiceBrowserOnServiceAdded;
118 _horizonServiceBrowser.Dispose();
1 office 119 _localCancellationTokenSource.Cancel();
120  
121 base.Dispose(disposing);
122 }
123  
124 #endregion
125  
126 #region Event Handlers
12 office 127 private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
128 {
129  
130 }
131  
132 private void contextMenuStrip1_Opened(object sender, EventArgs e)
133 {
134 _horizonServiceBrowser.Browse("_horizon._tcp", "local");
135 }
136  
1 office 137 private void DataGridView1_MouseDown(object sender, MouseEventArgs e)
138 {
139 var dataGridView = (DataGridView)sender;
140  
141 var index = dataGridView.HitTest(e.X, e.Y).RowIndex;
142  
143 if (index == -1)
144 {
145 base.OnMouseDown(e);
146 return;
147 }
148  
149 if (!dataGridView.SelectedRows.Contains(dataGridView.Rows[index]))
150 {
151 base.OnMouseDown(e);
152 }
153 }
154  
155 private async void DataGridView1_MouseMove(object sender, MouseEventArgs e)
156 {
157 var dataGridView = (DataGridView)sender;
158  
159 // Only accept dragging with left mouse button.
160 switch (e.Button)
161 {
162 case MouseButtons.Left:
163  
164 if (!Monitor.TryEnter(_mouseMoveLock))
165 {
166 break;
167 }
168  
169 try
170 {
171 var index = dataGridView.HitTest(e.X, e.Y).RowIndex;
172  
173 if (index == -1)
174 {
175 base.OnMouseMove(e);
176 return;
177 }
178  
179 var rows = GetSelectedDataGridViewRows(dataGridView);
180  
181 var count = rows.Count;
182  
183 if (count == 0)
184 {
185 base.OnMouseMove(e);
186 break;
187 }
188  
189 toolStripProgressBar1.Minimum = 0;
190 toolStripProgressBar1.Maximum = count;
191  
192 var virtualFileDataObject = new VirtualFileDataObject.VirtualFileDataObject();
193 var fileDescriptors =
194 new List<VirtualFileDataObject.VirtualFileDataObject.FileDescriptor>(count);
195  
196 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
197 {
198 if (_cancellationToken.IsCancellationRequested)
199 {
200 return;
201 }
202  
203 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
204 {
205 Log.Error(rowProgressFailure.Exception, "Unable to retrieve data for row.");
206  
207 toolStripStatusLabel1.Text =
208 $"Could not read file data {rowProgress.Row.Cells["NameColumn"].Value}...";
209 toolStripProgressBar1.Value = rowProgress.Index + 1;
210  
211 statusStrip1.Update();
212  
213 return;
214 }
215  
216 if (rowProgress is DataGridViewRowProgressSuccessRetrieveFileStream
217 rowProgressSuccessRetrieveFileStream)
218 {
219 toolStripStatusLabel1.Text =
220 $"Got {rowProgress.Row.Cells["NameColumn"].Value} file stream...";
221 toolStripProgressBar1.Value = rowProgress.Index + 1;
222  
223 statusStrip1.Update();
224  
225 var hash = (string)rowProgressSuccessRetrieveFileStream.Row.Cells["HashColumn"].Value;
226 var name = (string)rowProgressSuccessRetrieveFileStream.Row.Cells["NameColumn"].Value;
227  
228 var fileDescriptor = new VirtualFileDataObject.VirtualFileDataObject.FileDescriptor
229 {
230 Name = name,
231 StreamContents = stream =>
232 {
233 rowProgressSuccessRetrieveFileStream.MemoryStream.Seek(0, SeekOrigin.Begin);
234  
235 rowProgressSuccessRetrieveFileStream.MemoryStream.CopyTo(stream);
236 }
237 };
238  
239 fileDescriptors.Add(fileDescriptor);
240 }
241 });
242  
3 office 243 await Task.Run(() => RetrieveFileStream(rows, progress, _cancellationToken), _cancellationToken);
1 office 244  
3 office 245 if (_cancellationToken.IsCancellationRequested)
1 office 246 {
247 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
248 toolStripStatusLabel1.Text = "Done.";
249 }
250  
251 virtualFileDataObject.SetData(fileDescriptors);
252  
253 dataGridView1.DoDragDrop(virtualFileDataObject, DragDropEffects.Copy);
254 }
255 finally
256 {
257 Monitor.Exit(_mouseMoveLock);
258 }
259  
260 break;
261 }
262 }
263  
3 office 264 private async Task RetrieveFileStream(IReadOnlyList<DataGridViewRow> rows,
1 office 265 IProgress<DataGridViewRowProgress> progress,
266 CancellationToken cancellationToken)
267 {
268 var count = rows.Count;
269  
270 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
271 {
272 try
273 {
11 office 274 var fileStream = await _snapshotDatabase.RetrieveFileStreamAsync(
1 office 275 (string)rows[i].Cells["HashColumn"].Value,
276 cancellationToken);
277  
278 progress.Report(new DataGridViewRowProgressSuccessRetrieveFileStream(rows[i], i, fileStream));
279 }
280 catch (Exception exception)
281 {
282 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
283 }
284 }
285 }
286  
287 private void SnapshotManagerForm_Resize(object sender, EventArgs e)
288 {
289 if (_snapshotPreviewForm != null)
290 {
291 _snapshotPreviewForm.WindowState = WindowState;
292 }
293 }
294  
295 private void OpenInExplorerToolStripMenuItem_Click(object sender, EventArgs e)
296 {
297 var row = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault();
298 if (row == null)
299 {
300 return;
301 }
302  
303 Process.Start("explorer.exe", $"/select, \"{(string)row.Cells["PathColumn"].Value}\"");
304 }
305  
306 private async void DataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
307 {
308 var dataGridView = (DataGridView)sender;
309  
310 if (_snapshotPreviewForm == null)
311 {
312 _snapshotPreviewForm = new SnapshotPreviewForm(this, _snapshotDatabase);
313 _snapshotPreviewForm.Owner = this;
314 _snapshotPreviewForm.Closing += SnapshotPreviewForm_Closing;
315 _snapshotPreviewForm.Show();
316 }
317  
318 var row = GetSelectedDataGridViewRows(dataGridView).FirstOrDefault();
319 if (row == null)
320 {
321 return;
322 }
323  
324 var hash = (string)row.Cells["HashColumn"].Value;
325  
326 var snapshotPreview =
11 office 327 await Task.Run(async () => await _snapshotDatabase.RetrievePreviewAsync(hash, _cancellationToken),
1 office 328 _cancellationToken);
329  
330 if (snapshotPreview == null)
331 {
332 return;
333 }
334  
335 PreviewRetrieved?.Invoke(this, new PreviewRetrievedEventArgs(snapshotPreview));
336 }
337  
338 private void SnapshotPreviewForm_Closing(object sender, CancelEventArgs e)
339 {
340 if (_snapshotPreviewForm == null)
341 {
342 return;
343 }
344  
345 _snapshotPreviewForm.Dispose();
346 _snapshotPreviewForm = null;
347 }
348  
349 private async void NoneToolStripMenuItem_Click(object sender, EventArgs e)
350 {
351 var rows = GetSelectedDataGridViewRows(dataGridView1);
352  
353 var count = rows.Count;
354  
355 toolStripProgressBar1.Minimum = 0;
356 toolStripProgressBar1.Maximum = count;
357  
358 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
359 {
360 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
361 {
362 Log.Error(rowProgressFailure.Exception, "Failed to remove color from row.");
363  
364 toolStripStatusLabel1.Text =
365 $"Could not remove color from {rowProgress.Row.Cells["NameColumn"].Value}...";
366 toolStripProgressBar1.Value = rowProgress.Index + 1;
367  
368 statusStrip1.Update();
369  
370 return;
371 }
372  
373 rowProgress.Row.DefaultCellStyle.BackColor = Color.Empty;
374  
375 toolStripStatusLabel1.Text =
376 $"Removed color from {rowProgress.Row.Cells["NameColumn"].Value}...";
377 toolStripProgressBar1.Value = rowProgress.Index + 1;
378  
379 statusStrip1.Update();
380 });
381  
382 await Task.Run(() => RemoveColorFiles(rows, progress, _cancellationToken), _cancellationToken);
383  
3 office 384 if (_cancellationToken.IsCancellationRequested)
1 office 385 {
386 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
387 toolStripStatusLabel1.Text = "Done.";
388 }
389 }
390  
391 private async void ColorToolStripMenuItem_Click(object sender, EventArgs e)
392 {
393 var toolStripMenuItem = (ToolStripMenuItem)sender;
394 var color = toolStripMenuItem.BackColor;
395  
396 var rows = GetSelectedDataGridViewRows(dataGridView1);
397  
398 var count = rows.Count;
399  
400 toolStripProgressBar1.Minimum = 0;
401 toolStripProgressBar1.Maximum = count;
402  
403 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
404 {
405 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
406 {
407 Log.Error(rowProgressFailure.Exception, "Unable to color row.");
408  
409 toolStripStatusLabel1.Text =
410 $"Could not color {rowProgress.Row.Cells["NameColumn"].Value}...";
411 toolStripProgressBar1.Value = rowProgress.Index + 1;
412  
413 statusStrip1.Update();
414  
415 return;
416 }
417  
418 rowProgress.Row.DefaultCellStyle.BackColor = color;
419  
420 toolStripStatusLabel1.Text =
421 $"Colored {rowProgress.Row.Cells["NameColumn"].Value}...";
422 toolStripProgressBar1.Value = rowProgress.Index + 1;
423  
424 statusStrip1.Update();
425 });
426  
427 await Task.Run(() => ColorFiles(rows, color, progress, _cancellationToken), _cancellationToken);
428  
3 office 429 if (_cancellationToken.IsCancellationRequested)
1 office 430 {
431 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
432 toolStripStatusLabel1.Text = "Done.";
433 }
434 }
435  
436 private async void DeleteToolStripMenuItem_Click(object sender, EventArgs e)
437 {
438 var rows = GetSelectedDataGridViewRows(dataGridView1);
439  
440 var count = rows.Count;
441  
442 toolStripProgressBar1.Minimum = 0;
443 toolStripProgressBar1.Maximum = count;
444  
445 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
446 {
447 if (_cancellationToken.IsCancellationRequested)
448 {
449 return;
450 }
451  
452 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
453 {
454 Log.Error(rowProgressFailure.Exception, "Unable to delete row.");
455  
456 toolStripStatusLabel1.Text =
457 $"Could not remove {rowProgress.Row.Cells["NameColumn"].Value}...";
458 toolStripProgressBar1.Value = rowProgress.Index + 1;
459  
460 statusStrip1.Update();
461  
462 return;
463 }
464  
465 toolStripStatusLabel1.Text =
466 $"Removed {rowProgress.Row.Cells["NameColumn"].Value}...";
467 toolStripProgressBar1.Value = rowProgress.Index + 1;
468  
469 statusStrip1.Update();
470  
471 dataGridView1.Rows.Remove(rowProgress.Row);
472 });
473  
474 await Task.Run(() => DeleteFiles(rows, progress, _cancellationToken), _cancellationToken);
475  
3 office 476 if (_cancellationToken.IsCancellationRequested)
1 office 477 {
478 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
479 toolStripStatusLabel1.Text = "Done.";
480 }
481 }
482  
483 private async void DeleteFastToolStripMenuItem_Click(object sender, EventArgs e)
484 {
485 var rows = GetSelectedDataGridViewRows(dataGridView1);
486  
487 try
488 {
489 await DeleteFilesFast(rows, _cancellationToken);
490  
491 foreach (var row in rows)
492 {
493 dataGridView1.Rows.Remove(row);
494 }
495 }
496 catch (Exception exception)
497 {
498 Log.Error(exception, "Unable to remove rows.");
499 }
500 }
501  
502 private void SnapshotManager_SnapshotCreate(object sender, SnapshotCreateEventArgs e)
503 {
504 switch (e)
505 {
506 case SnapshotCreateSuccessEventArgs snapshotCreateSuccessEventArgs:
507 dataGridView1.InvokeIfRequired(dataGridView =>
508 {
509 var index = dataGridView.Rows.Add();
510  
511 dataGridView.Rows[index].Cells["TimeColumn"].Value =
512 DateTime.Parse(snapshotCreateSuccessEventArgs.Time);
513 dataGridView.Rows[index].Cells["NameColumn"].Value = snapshotCreateSuccessEventArgs.Name;
514 dataGridView.Rows[index].Cells["PathColumn"].Value = snapshotCreateSuccessEventArgs.Path;
515 dataGridView.Rows[index].Cells["HashColumn"].Value = snapshotCreateSuccessEventArgs.Hash;
516 dataGridView.Rows[index].DefaultCellStyle.BackColor = snapshotCreateSuccessEventArgs.Color;
517  
518 dataGridView.Sort(dataGridView.Columns["TimeColumn"], ListSortDirection.Descending);
519 });
520 break;
521 case SnapshotCreateFailureEventArgs snapshotCreateFailure:
522 Log.Warning(snapshotCreateFailure.Exception, "Could not create snapshot.");
523 break;
524 }
525 }
526  
527 private void RevertToThisToolStripMenuItem_Click(object sender, EventArgs e)
528 {
529 _mainForm.InvokeIfRequired(async form =>
530 {
531 var fileSystemWatchers = new List<FileSystemWatcherState>();
532 var watchPaths = new HashSet<string>();
533 // Temporary disable all filesystem watchers that are watching the selected file directory.
534 foreach (var row in GetSelectedDataGridViewRows(dataGridView1))
535 {
536 var path = (string)row.Cells["PathColumn"].Value;
537  
538 foreach (var fileSystemWatcher in form.FileSystemWatchers)
539 {
540 if (!path.IsPathEqual(fileSystemWatcher.Path) &&
541 !path.IsSubPathOf(fileSystemWatcher.Path))
542 {
543 continue;
544 }
545  
546 if (watchPaths.Contains(fileSystemWatcher.Path))
547 {
548 continue;
549 }
550  
551 fileSystemWatchers.Add(new FileSystemWatcherState(fileSystemWatcher));
552  
553 fileSystemWatcher.EnableRaisingEvents = false;
554  
555 watchPaths.Add(fileSystemWatcher.Path);
556 }
557 }
558  
559 try
560 {
561 var rows = GetSelectedDataGridViewRows(dataGridView1);
562  
563 var count = rows.Count;
564  
565 toolStripProgressBar1.Minimum = 0;
566 toolStripProgressBar1.Maximum = count;
567  
568 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
569 {
570 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
571 {
572 Log.Error(rowProgressFailure.Exception, "Could not revert to snapshot.");
573  
574 toolStripStatusLabel1.Text =
575 $"Could not revert {rowProgress.Row.Cells["NameColumn"].Value}...";
576 toolStripProgressBar1.Value = rowProgress.Index + 1;
577  
578 statusStrip1.Update();
579  
580 return;
581 }
582  
583 toolStripStatusLabel1.Text =
584 $"Reverted {rowProgress.Row.Cells["NameColumn"].Value}...";
585 toolStripProgressBar1.Value = rowProgress.Index + 1;
586  
587 statusStrip1.Update();
588 });
589  
590 await Task.Run(() => RevertFile(rows, progress, _cancellationToken), _cancellationToken);
591  
3 office 592 if (_cancellationToken.IsCancellationRequested)
1 office 593 {
594 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
595 toolStripStatusLabel1.Text = "Done.";
596 }
597 }
598 catch (Exception exception)
599 {
600 Log.Error(exception, "Could not update data grid view.");
601 }
602 finally
603 {
604 // Restore initial state.
605 foreach (var fileSystemWatcherState in fileSystemWatchers)
606 {
607 foreach (var fileSystemWatcher in form.FileSystemWatchers)
608 {
609 if (fileSystemWatcherState.FileSystemWatcher == fileSystemWatcher)
610 {
611 fileSystemWatcher.EnableRaisingEvents = fileSystemWatcherState.State;
612 }
613 }
614 }
615 }
616 });
617 }
618  
619 private async void SnapshotManagerForm_Load(object sender, EventArgs e)
620 {
12 office 621 // Start browsing for network services.
622 _horizonServiceBrowser.Browse("_horizon._tcp", "local");
623  
624 // Load snapshots.
1 office 625 toolStripProgressBar1.Minimum = 0;
11 office 626 toolStripProgressBar1.Maximum = (int)await _snapshotDatabase.CountSnapshotsAsync(_cancellationToken);
1 office 627  
628 var snapshotQueue = new ConcurrentQueue<Snapshot>();
629  
5 office 630 void IdleHandler(object idleHandlerSender, EventArgs idleHandlerArgs)
1 office 631 {
632 try
633 {
5 office 634 if (snapshotQueue.IsEmpty)
1 office 635 {
636 Application.Idle -= IdleHandler;
637  
638 dataGridView1.Sort(dataGridView1.Columns["TimeColumn"], ListSortDirection.Descending);
639 toolStripStatusLabel1.Text = "Done.";
5 office 640 }
1 office 641  
5 office 642 if (!snapshotQueue.TryDequeue(out var snapshot))
643 {
1 office 644 return;
645 }
646  
647 var index = dataGridView1.Rows.Add();
648  
649 dataGridView1.Rows[index].Cells["TimeColumn"].Value = DateTime.Parse(snapshot.Time);
650 dataGridView1.Rows[index].Cells["NameColumn"].Value = snapshot.Name;
651 dataGridView1.Rows[index].Cells["PathColumn"].Value = snapshot.Path;
652 dataGridView1.Rows[index].Cells["HashColumn"].Value = snapshot.Hash;
653 dataGridView1.Rows[index].DefaultCellStyle.BackColor = snapshot.Color;
654  
655 toolStripStatusLabel1.Text = $"Loaded {snapshot.Name}...";
656  
657 toolStripProgressBar1.Increment(1);
658  
659 statusStrip1.Update();
660 }
661 catch (Exception exception)
662 {
663 Log.Error(exception, "Could not update data grid view.");
664 }
665 }
5 office 666  
1 office 667 try
668 {
11 office 669 await foreach (var snapshot in _snapshotDatabase.LoadSnapshotsAsync(_cancellationToken).WithCancellation(_cancellationToken))
1 office 670 {
671 snapshotQueue.Enqueue(snapshot);
672 }
673  
5 office 674 Application.Idle += IdleHandler;
1 office 675 }
676 catch (Exception exception)
677 {
678 Application.Idle -= IdleHandler;
679  
680 Log.Error(exception, "Unable to load snapshots.");
681 }
682 }
683  
12 office 684 private void OnHorizonServiceBrowserOnServiceAdded(object o, Mono.Zeroconf.ServiceBrowseEventArgs args)
685 {
686 _resolvedService = args.Service;
687  
688 Log.Information("Found Service: {0}", _resolvedService.Name);
689  
690 _resolvedService.Resolved += OnServiceOnResolved;
691  
692 _resolvedService.Resolve();
693  
694 _resolvedService.Resolved -= OnServiceOnResolved;
695 }
696  
697 private void OnServiceOnResolved(object o, ServiceResolvedEventArgs args)
698 {
699 var service = (Service) args.Service;
700  
701 Log.Information("Resolved Service: {0} - {1}:{2} ({3} TXT record entries)", service.FullName, service.HostEntry.AddressList[0], service.UPort, service.TxtRecord.Count);
702  
703 _discoveredHorizonNetworkShares.TryAdd(service.Name, service);
704  
705 var discoveredServiceMenuItem = new ToolStripMenuItem(service.Name, null, OnShareWithNodeClicked);
706 discoveredServiceMenuItem.Tag = service;
707  
708 shareToToolStripMenuItem.DropDownItems.Add(discoveredServiceMenuItem);
709 }
710  
711 private async void OnShareWithNodeClicked(object sender, EventArgs e)
712 {
713 var toolStripMenuItem = (ToolStripMenuItem)sender;
714  
715 var service = (Service)toolStripMenuItem.Tag;
716  
717 var rows = GetSelectedDataGridViewRows(dataGridView1);
718  
719 var count = rows.Count;
720  
721 toolStripProgressBar1.Minimum = 0;
722 toolStripProgressBar1.Maximum = count;
723  
724 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
725 {
726 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
727 {
728 Log.Error(rowProgressFailure.Exception, "Unable to transfer data.");
729  
730 toolStripStatusLabel1.Text =
731 $"Could not transfer {rowProgress.Row.Cells["NameColumn"].Value}...";
732 toolStripProgressBar1.Value = rowProgress.Index + 1;
733  
734 statusStrip1.Update();
735  
736 return;
737 }
738  
739 toolStripStatusLabel1.Text =
740 $"Transferred {rowProgress.Row.Cells["NameColumn"].Value}...";
741 toolStripProgressBar1.Value = rowProgress.Index + 1;
742  
743 statusStrip1.Update();
744 });
745  
746 await Task.Run(() => TransferFiles(rows, service, progress, _cancellationToken), _cancellationToken);
747  
748 if (_cancellationToken.IsCancellationRequested)
749 {
750 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
751 toolStripStatusLabel1.Text = "Done.";
752 }
753 }
754  
1 office 755 private void SnapshotManagerForm_Closing(object sender, FormClosingEventArgs e)
756 {
757 _cancellationTokenSource.Cancel();
758  
759 if (_snapshotPreviewForm != null)
760 {
761 _snapshotPreviewForm.Close();
762 _snapshotPreviewForm = null;
763 }
764  
765 if (_hexViewForm != null)
766 {
767 _hexViewForm.Close();
768 _hexViewForm = null;
769 }
770 }
771  
772 private void DataGridView1_DragEnter(object sender, DragEventArgs e)
773 {
774 if (e.Data.GetDataPresent(DataFormats.FileDrop))
775 {
776 e.Effect = DragDropEffects.Copy;
777 return;
778 }
779  
780 e.Effect = DragDropEffects.None;
781 }
782  
7 office 783 private void CreateSnapshots(IReadOnlyList<string> files, Bitmap screenCapture, TrackedFolders.TrackedFolders trackedFolders, IProgress<CreateSnapshotProgress> progress, CancellationToken cancellationToken)
5 office 784 {
785 Parallel.ForEach(files, new ParallelOptions { MaxDegreeOfParallelism = 512 }, async file =>
786 {
787 var color = Color.Empty;
788 if (_mainForm.TrackedFolders.TryGet(file, out var folder))
789 {
790 color = folder.Color;
791 }
792  
793 var fileInfo = File.GetAttributes(file);
794 if (fileInfo.HasFlag(FileAttributes.Directory))
795 {
796 foreach (var directoryFile in Directory.GetFiles(file, "*.*", SearchOption.AllDirectories))
797 {
798 var name = Path.GetFileName(directoryFile);
799 var path = Path.Combine(Path.GetDirectoryName(directoryFile), name);
800  
801 try
802 {
11 office 803 await _snapshotDatabase.CreateSnapshotAsync(name, path, screenCapture, color,
5 office 804 _cancellationToken);
805  
806 progress.Report(new CreateSnapshotProgressSuccess(file));
807 }
808 catch (Exception exception)
809 {
810 progress.Report(new CreateSnapshotProgressFailure(file, exception));
811 }
812 }
813  
814 return;
815 }
816  
817 var fileName = Path.GetFileName(file);
818 var pathName = Path.Combine(Path.GetDirectoryName(file), fileName);
819  
820 try
821 {
11 office 822 await _snapshotDatabase.CreateSnapshotAsync(fileName, pathName, screenCapture, color,
5 office 823 _cancellationToken);
824  
825 progress.Report(new CreateSnapshotProgressSuccess(file));
826 }
827 catch (Exception exception)
828 {
829 progress.Report(new CreateSnapshotProgressFailure(file, exception));
830 }
831 });
832 }
1 office 833 private async void DataGridView1_DragDrop(object sender, DragEventArgs e)
834 {
835 if (!e.Data.GetDataPresent(DataFormats.FileDrop))
836 {
837 return;
838 }
839  
840 var files = (string[])e.Data.GetData(DataFormats.FileDrop);
841  
842 toolStripProgressBar1.Minimum = 0;
843 toolStripProgressBar1.Maximum = files.Length;
844 toolStripStatusLabel1.Text = "Snapshotting files...";
845  
846 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode);
5 office 847  
848 var progress = new Progress<CreateSnapshotProgress>(createSnapshotProgress =>
1 office 849 {
5 office 850 switch (createSnapshotProgress)
851 {
852 case CreateSnapshotProgressSuccess createSnapshotProgressSuccess:
853 toolStripStatusLabel1.Text = $"Snapshot taken of {createSnapshotProgressSuccess.File}.";
854 break;
855 case CreateSnapshotProgressFailure createSnapshotProgressFailure:
856 if (createSnapshotProgressFailure.Exception is SQLiteException { ResultCode: SQLiteErrorCode.Constraint })
857 {
858 toolStripStatusLabel1.Text = $"Snapshot of file {createSnapshotProgressFailure.File} already exists.";
859 break;
860 }
861  
862 toolStripStatusLabel1.Text = $"Could not snapshot file {createSnapshotProgressFailure.File}";
863 Log.Warning(createSnapshotProgressFailure.Exception, $"Could not snapshot file {createSnapshotProgressFailure.File}");
864 break;
865 }
866  
867 toolStripProgressBar1.Increment(1);
868 statusStrip1.Update();
869 });
870  
11 office 871 await Task.Factory.StartNew( () =>
5 office 872 {
7 office 873 CreateSnapshots(files, screenCapture, _mainForm.TrackedFolders, progress, _cancellationToken);
5 office 874 }, _cancellationToken);
1 office 875 }
876  
877 private async void FileToolStripMenuItem_Click(object sender, EventArgs e)
878 {
879 var dialog = new CommonOpenFileDialog();
880 if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
881 {
882 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode);
883  
884 var fileName = Path.GetFileName(dialog.FileName);
885 var directory = Path.GetDirectoryName(dialog.FileName);
886 var pathName = Path.Combine(directory, fileName);
887  
888 var color = Color.Empty;
889 if (_mainForm.TrackedFolders.TryGet(directory, out var folder))
890 {
891 color = folder.Color;
892 }
893  
894 try
895 {
11 office 896 await _snapshotDatabase.CreateSnapshotAsync(fileName, pathName, screenCapture, color,
1 office 897 _cancellationToken);
898 }
899 catch (SQLiteException exception)
900 {
901 if (exception.ResultCode == SQLiteErrorCode.Constraint)
902 {
903 Log.Information(exception, "Snapshot already exists.");
904 }
905 }
906 catch (Exception exception)
907 {
908 Log.Warning(exception, "Could not create snapshot.");
909 }
910 }
911 }
912  
913 private async void DirectoryToolStripMenuItem_Click(object sender, EventArgs e)
914 {
915 var dialog = new CommonOpenFileDialog { IsFolderPicker = true };
916 if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
917 {
918 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode);
919 foreach (var directoryFile in Directory.GetFiles(dialog.FileName, "*.*", SearchOption.AllDirectories))
920 {
921 var name = Path.GetFileName(directoryFile);
922 var directory = Path.GetDirectoryName(directoryFile);
923 var path = Path.Combine(directory, name);
924  
925 var color = Color.Empty;
926 if (_mainForm.TrackedFolders.TryGet(directory, out var folder))
927 {
928 color = folder.Color;
929 }
930  
931 try
932 {
11 office 933 await _snapshotDatabase.CreateSnapshotAsync(name, path, screenCapture, color, _cancellationToken);
1 office 934 }
935 catch (SQLiteException exception)
936 {
937 if (exception.ResultCode == SQLiteErrorCode.Constraint)
938 {
939 Log.Information(exception, "Snapshot already exists.");
940 }
941 }
942 catch (Exception exception)
943 {
944 Log.Warning(exception, "Could not create snapshot.");
945 }
946 }
947 }
948 }
949  
950 private async void RelocateToolStripMenuItem_Click(object sender, EventArgs e)
951 {
952 var commonOpenFileDialog = new CommonOpenFileDialog
953 {
954 InitialDirectory = _mainForm.Configuration.LastFolder,
955 IsFolderPicker = true
956 };
957  
958 if (commonOpenFileDialog.ShowDialog() != CommonFileDialogResult.Ok)
959 {
960 return;
961 }
962  
963 _mainForm.Configuration.LastFolder = commonOpenFileDialog.FileName;
964 _mainForm.ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
965 async () => await _mainForm.SaveConfiguration(), _cancellationToken);
966  
967 var directory = commonOpenFileDialog.FileName;
968  
969 var rows = GetSelectedDataGridViewRows(dataGridView1);
970  
971 var count = rows.Count;
972  
973 toolStripProgressBar1.Minimum = 0;
974 toolStripProgressBar1.Maximum = count;
975  
976 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
977 {
978 var path = Path.Combine(directory,
979 (string)rowProgress.Row.Cells["NameColumn"].Value);
980  
981 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
982 {
983 Log.Error(rowProgressFailure.Exception, "Could not relocate snapshot.");
984  
985 toolStripStatusLabel1.Text =
986 $"Could not relocate {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
987 toolStripProgressBar1.Value = rowProgress.Index + 1;
988  
989 statusStrip1.Update();
990  
991 return;
992 }
993  
994 rowProgress.Row.Cells["PathColumn"].Value = path;
995  
996 toolStripStatusLabel1.Text =
997 $"Relocated {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
998 toolStripProgressBar1.Value = rowProgress.Index + 1;
999  
1000 statusStrip1.Update();
1001 });
1002  
1003 await Task.Run(() => RelocateFiles(rows, directory, progress, _cancellationToken), _cancellationToken);
1004  
3 office 1005 if (_cancellationToken.IsCancellationRequested)
1 office 1006 {
1007 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1008 toolStripStatusLabel1.Text = "Done.";
1009 }
1010 }
1011  
1012 private async void NoteToolStripMenuItem_Click(object sender, EventArgs e)
1013 {
1014 if (_snapshotNote != null)
1015 {
1016 return;
1017 }
1018  
1019 var row = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault();
1020 if (row == null)
1021 {
1022 return;
1023 }
1024  
1025 try
1026 {
11 office 1027 var snapshotPreview = await _snapshotDatabase.RetrievePreviewAsync(
1 office 1028 (string)row.Cells["HashColumn"].Value, _cancellationToken);
1029  
1030 if (snapshotPreview == null)
1031 {
1032 return;
1033 }
1034  
1035 _snapshotNote = new SnapshotNoteForm(this, snapshotPreview);
1036 _snapshotNote.Owner = this;
1037 _snapshotNote.SaveNote += SnapshotNote_SaveNote;
1038 _snapshotNote.Closing += SnapshotNote_Closing;
1039 _snapshotNote.Show();
1040 }
1041 catch (Exception exception)
1042 {
1043 Log.Error(exception, "Could not open notes form.");
1044 }
1045 }
1046  
1047 private async void SnapshotNote_SaveNote(object sender, SaveNoteEventArgs e)
1048 {
1049 var rows = GetSelectedDataGridViewRows(dataGridView1);
1050  
1051 var count = rows.Count;
1052  
1053 toolStripProgressBar1.Minimum = 0;
1054 toolStripProgressBar1.Maximum = count;
1055  
1056 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1057 {
1058 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1059 {
1060 Log.Error(rowProgressFailure.Exception, "Could not update note for snapshot.");
1061  
1062 toolStripStatusLabel1.Text =
1063 $"Could not update note for {rowProgress.Row.Cells["NameColumn"].Value}...";
1064 toolStripProgressBar1.Value = rowProgress.Index + 1;
1065  
1066 statusStrip1.Update();
1067  
1068 return;
1069 }
1070  
1071 toolStripStatusLabel1.Text =
1072 $"Updated note for {rowProgress.Row.Cells["NameColumn"].Value}...";
1073 toolStripProgressBar1.Value = rowProgress.Index + 1;
1074  
1075 statusStrip1.Update();
1076 });
1077  
1078 await Task.Run(() => UpdateNote(rows, e.Note, progress, _cancellationToken), _cancellationToken);
1079  
3 office 1080 if (_cancellationToken.IsCancellationRequested)
1 office 1081 {
1082 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1083 toolStripStatusLabel1.Text = "Done.";
1084 }
1085 }
1086  
1087 private void SnapshotNote_Closing(object sender, CancelEventArgs e)
1088 {
1089 if (_snapshotNote == null)
1090 {
1091 return;
1092 }
1093  
1094 _snapshotNote.Closing -= SnapshotNote_Closing;
1095 _snapshotNote.Close();
1096 _snapshotNote = null;
1097 }
1098  
1099 private async void ViewHexToolStripMenuItem_Click(object sender, EventArgs e)
1100 {
1101 var rows = GetSelectedDataGridViewRows(dataGridView1);
1102 var row = rows.FirstOrDefault();
1103 if (row == null)
1104 {
1105 return;
1106 }
1107  
1108 var hash = (string)row.Cells["HashColumn"].Value;
1109  
11 office 1110 using (var memoryStream = await _snapshotDatabase.RetrieveFileStreamAsync(hash, _cancellationToken))
1 office 1111 {
1112 if (memoryStream == null)
1113 {
1114 return;
1115 }
1116  
1117 if (_hexViewForm != null)
1118 {
1119 _hexViewForm.UpdateData(memoryStream.ToArray());
1120 _hexViewForm.Activate();
1121 return;
1122 }
1123  
1124 _hexViewForm = new HexViewForm(_snapshotDatabase, hash, memoryStream.ToArray());
1125 _hexViewForm.Owner = this;
1126 _hexViewForm.Closing += HexViewForm_Closing;
1127 _hexViewForm.SaveData += HexViewForm_SaveData;
1128  
1129 _hexViewForm.Show();
1130 }
1131 }
1132  
1133 private async void HexViewForm_SaveData(object sender, SaveDataEventArgs e)
1134 {
11 office 1135 var hash = await _snapshotDatabase.UpdateFileAsync(e.Hash, e.Data, _cancellationToken);
1 office 1136  
1137 if (string.IsNullOrEmpty(hash))
1138 {
1139 return;
1140 }
1141  
1142 dataGridView1.InvokeIfRequired(dataGridView =>
1143 {
1144 // Update the hash in the datagridview.
1145 var removeRows = new List<DataGridViewRow>();
1146 foreach (var row in dataGridView.Rows.OfType<DataGridViewRow>())
1147 {
1148 if ((string)row.Cells["HashColumn"].Value == hash)
1149 {
1150 removeRows.Add(row);
1151 }
1152  
1153 if ((string)row.Cells["HashColumn"].Value != e.Hash)
1154 {
1155 continue;
1156 }
1157  
1158 row.Cells["HashColumn"].Value = hash;
1159 }
1160  
1161 // Remove rows that might have the same hash.
1162 foreach (var row in removeRows)
1163 {
1164 dataGridView.Rows.Remove(row);
1165 }
1166 });
1167 }
1168  
1169 private void HexViewForm_Closing(object sender, CancelEventArgs e)
1170 {
1171 if (_hexViewForm == null)
1172 {
1173 return;
1174 }
1175  
1176 _hexViewForm.SaveData -= HexViewForm_SaveData;
1177 _hexViewForm.Closing -= HexViewForm_Closing;
1178 _hexViewForm.Close();
1179 _hexViewForm = null;
1180 }
1181  
1182 private async void FileToolStripMenuItem2_Click(object sender, EventArgs e)
1183 {
1184 var commonOpenFileDialog = new CommonOpenFileDialog
1185 {
1186 InitialDirectory = _mainForm.Configuration.LastFolder,
1187 IsFolderPicker = true
1188 };
1189  
1190 if (commonOpenFileDialog.ShowDialog() == CommonFileDialogResult.Ok)
1191 {
1192 _mainForm.Configuration.LastFolder = commonOpenFileDialog.FileName;
1193 _mainForm.ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
1194 async () => await _mainForm.SaveConfiguration(), _cancellationToken);
1195  
1196 var directory = commonOpenFileDialog.FileName;
1197  
1198 var rows = GetSelectedDataGridViewRows(dataGridView1);
1199  
1200 var count = rows.Count;
1201  
1202 toolStripProgressBar1.Minimum = 0;
1203 toolStripProgressBar1.Maximum = count;
1204  
1205 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1206 {
1207 var fileInfo =
1208 new FileInfo((string)rowProgress.Row.Cells["NameColumn"].Value);
1209 var file = fileInfo.Name;
1210 var path = Path.Combine(directory, file);
1211  
1212 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1213 {
1214 Log.Error(rowProgressFailure.Exception, "Could not save snapshot.");
1215  
1216 toolStripStatusLabel1.Text =
1217 $"Could not save snapshot {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
1218 toolStripProgressBar1.Value = rowProgress.Index + 1;
1219  
1220 statusStrip1.Update();
1221  
1222 return;
1223 }
1224  
1225 toolStripStatusLabel1.Text =
1226 $"Saved {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
1227 toolStripProgressBar1.Value = rowProgress.Index + 1;
1228  
1229 statusStrip1.Update();
1230 });
1231  
1232 await Task.Run(() => SaveFilesTo(rows, directory, progress, _cancellationToken), _cancellationToken);
1233  
3 office 1234 if (_cancellationToken.IsCancellationRequested)
1 office 1235 {
1236 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1237 toolStripStatusLabel1.Text = "Done.";
1238 }
1239 }
1240 }
1241  
1242 private async void DirectoryToolStripMenuItem2_Click(object sender, EventArgs e)
1243 {
1244 var select = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault();
1245  
1246 if (select == null)
1247 {
1248 return;
1249 }
1250  
1251 // C:\aa\bbb\dd.txt
1252 var path = (string)select.Cells["PathColumn"].Value;
1253  
1254 // C:\aa\bbb\
1255 var basePath = Path.GetDirectoryName(path);
1256  
1257 var dialog = new CommonOpenFileDialog { IsFolderPicker = true };
1258 if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
1259 {
1260 //Log.Information(dialog.FileName);
1261 var rows = GetAllDataGridViewRows(dataGridView1);
1262 var count = rows.Count;
1263  
1264 toolStripProgressBar1.Minimum = 0;
1265 toolStripProgressBar1.Maximum = count;
1266 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1267 {
1268 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1269 {
1270 Log.Error(rowProgressFailure.Exception, "Could not save file.");
1271  
1272 toolStripStatusLabel1.Text =
1273 $"Could not save file {rowProgress.Row.Cells["NameColumn"].Value} to {basePath}...";
1274 toolStripProgressBar1.Value = rowProgress.Index + 1;
1275  
1276 statusStrip1.Update();
1277  
1278 return;
1279 }
1280  
1281 toolStripStatusLabel1.Text =
1282 $"Saved {rowProgress.Row.Cells["NameColumn"].Value} to {basePath}...";
1283 toolStripProgressBar1.Value = rowProgress.Index + 1;
1284  
1285 statusStrip1.Update();
1286 });
1287  
1288 await Task.Run(() => SaveDirectoryTo(rows, basePath, dialog.FileName, progress, _cancellationToken),
1289 _cancellationToken);
1290  
3 office 1291 if (_cancellationToken.IsCancellationRequested)
1 office 1292 {
1293 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1294 toolStripStatusLabel1.Text = "Done.";
1295 }
1296 }
1297 }
1298  
1299 private void TextBox1_TextChanged(object sender, EventArgs e)
1300 {
1301 _searchTextBoxChangedContinuation.Schedule(TimeSpan.FromSeconds(1), () =>
1302 {
1303 textBox1.InvokeIfRequired(textBox =>
1304 {
1305 var search = textBox.Text;
1306  
1307 dataGridView1.InvokeIfRequired(dataGridView =>
1308 {
1309 foreach (var row in GetAllDataGridViewRows(dataGridView))
1310 {
1311 if(row.Cells["PathColumn"].Value == null)
1312 {
1313 continue;
1314 }
1315  
1316 switch (((string)row.Cells["PathColumn"].Value).IndexOf(search,
1317 StringComparison.OrdinalIgnoreCase))
1318 {
1319 case -1:
1320 row.Visible = false;
1321 break;
1322 default:
1323 row.Visible = true;
1324 break;
1325 }
1326 }
1327 });
1328 });
1329 }, _cancellationToken);
1330 }
1331  
1332 private async void RecomputeHashesToolStripMenuItem1_Click(object sender, EventArgs e)
1333 {
1334 var rows = GetSelectedDataGridViewRows(dataGridView1);
1335  
1336 var count = rows.Count;
1337  
1338 toolStripProgressBar1.Minimum = 0;
1339 toolStripProgressBar1.Maximum = count;
1340  
1341 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1342 {
1343 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1344 {
1345 Log.Error(rowProgressFailure.Exception, "Could not recompute hash for snapshot.");
1346  
1347 toolStripStatusLabel1.Text =
1348 $"Could not recompute hash for {rowProgress.Row.Cells["NameColumn"].Value}...";
1349 toolStripProgressBar1.Value = rowProgress.Index + 1;
1350  
1351 statusStrip1.Update();
1352  
1353 return;
1354 }
1355  
1356 toolStripStatusLabel1.Text =
1357 $"Recomputed hash for {rowProgress.Row.Cells["NameColumn"].Value}...";
1358 toolStripProgressBar1.Value = rowProgress.Index + 1;
1359  
1360 statusStrip1.Update();
1361 });
1362  
1363 await Task.Run(() => RecomputeHashes(rows, progress, _cancellationToken), _cancellationToken);
1364  
3 office 1365 if (_cancellationToken.IsCancellationRequested)
1 office 1366 {
1367 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1368 toolStripStatusLabel1.Text = "Done.";
1369 }
1370 }
1371  
1372 private async void NormalizeDateTimeToolStripMenuItem_Click(object sender, EventArgs e)
1373 {
1374 var rows = GetSelectedDataGridViewRows(dataGridView1);
1375  
1376 var count = rows.Count;
1377  
1378 toolStripProgressBar1.Minimum = 0;
1379 toolStripProgressBar1.Maximum = count;
1380  
1381 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1382 {
1383 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1384 {
1385 Log.Error(rowProgressFailure.Exception, "Could not normalize date-time for snapshot.");
1386  
1387 toolStripStatusLabel1.Text =
1388 $"Could not normalize date-time for {rowProgress.Row.Cells["NameColumn"].Value}...";
1389 toolStripProgressBar1.Value = rowProgress.Index + 1;
1390  
1391 statusStrip1.Update();
1392  
1393 return;
1394 }
1395  
1396 toolStripStatusLabel1.Text =
1397 $"Normalized date-time for {rowProgress.Row.Cells["NameColumn"].Value}...";
1398 toolStripProgressBar1.Value = rowProgress.Index + 1;
1399  
1400 statusStrip1.Update();
1401 });
1402  
1403 await Task.Run(() => NormalizeDateTime(rows, progress, _cancellationToken), _cancellationToken);
1404  
3 office 1405 if (_cancellationToken.IsCancellationRequested)
1 office 1406 {
1407 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1408 toolStripStatusLabel1.Text = "Done.";
1409 }
1410 }
1411  
1412 #endregion
1413  
1414 #region Private Methods
12 office 1415 private void DataGridView1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
1416 {
1417 DataGridView dataGridView = sender as DataGridView;
1418 foreach (DataGridViewCell cell in dataGridView.Rows[e.RowIndex].Cells)
1419 {
1420 if (cell.Selected == false) { continue; }
1421 var bgColorCell = Color.White;
1422 if (cell.Style.BackColor != Color.Empty) { bgColorCell = cell.Style.BackColor; }
1423 else if (cell.InheritedStyle.BackColor != Color.Empty) { bgColorCell = cell.InheritedStyle.BackColor; }
1424 cell.Style.SelectionBackColor = MixColor(bgColorCell, Color.FromArgb(0, 150, 255), 10, 4);
1425 }
1426 }
1 office 1427  
12 office 1428 //Mix two colors
1429 //Example: Steps=10 & Position=4 makes Color2 mix 40% into Color1
1430 /// <summary>
1431 /// Mix two colors.
1432 /// </summary>
1433 /// <param name="Color1"></param>
1434 /// <param name="Color2"></param>
1435 /// <param name="Steps"></param>
1436 /// <param name="Position"></param>
1437 /// <example>Steps=10 & Positon=4 makes Color2 mix 40% into Color1</example>
1438 /// <remarks>https://stackoverflow.com/questions/38337849/transparent-selectionbackcolor-for-datagridview-cell</remarks>
1439 /// <returns></returns>
1440 public static Color MixColor(Color Color1, Color Color2, int Steps, int Position)
1441 {
1442 if (Position <= 0 || Steps <= 1) { return Color1; }
1443 if (Position >= Steps) { return Color2; }
1444 return Color.FromArgb(
1445 Color1.R + ((Color2.R - Color1.R) / Steps * Position),
1446 Color1.G + ((Color2.G - Color1.G) / Steps * Position),
1447 Color1.B + ((Color2.B - Color1.B) / Steps * Position)
1448 );
1449 }
1450  
1 office 1451 private async Task DeleteFiles(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress,
1452 CancellationToken cancellationToken)
1453 {
1454 var count = rows.Count;
1455  
1456 for (var index = 0; index < count && !cancellationToken.IsCancellationRequested; ++index)
1457 {
1458 try
1459 {
11 office 1460 await _snapshotDatabase.RemoveFileAsync((string)rows[index].Cells["HashColumn"].Value,
1 office 1461 cancellationToken);
1462  
1463 progress.Report(new DataGridViewRowProgressSuccess(rows[index], index));
1464 }
1465 catch (Exception exception)
1466 {
1467 progress.Report(new DataGridViewRowProgressFailure(rows[index], index, exception));
1468 }
1469 }
1470 }
1471  
1472 private async Task DeleteFilesFast(IReadOnlyList<DataGridViewRow> rows, CancellationToken cancellationToken)
1473 {
1474 var hashes = rows.Select(row => (string)row.Cells["HashColumn"].Value);
1475  
11 office 1476 await _snapshotDatabase.RemoveFileFastAsync(hashes, cancellationToken);
1 office 1477 }
1478  
1479 private async Task UpdateNote(IReadOnlyList<DataGridViewRow> rows, string note,
1480 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken)
1481 {
1482 var count = rows.Count;
1483  
1484 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1485 {
1486 try
1487 {
11 office 1488 await _snapshotDatabase.UpdateNoteAsync((string)rows[i].Cells["HashColumn"].Value, note,
1 office 1489 cancellationToken);
1490  
1491 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1492 }
1493 catch (Exception exception)
1494 {
1495 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1496 }
1497 }
1498 }
1499  
1500 private static List<DataGridViewRow> GetSelectedDataGridViewRows(DataGridView dataGridView)
1501 {
1502 return dataGridView.SelectedRows.OfType<DataGridViewRow>().ToList();
1503 }
1504  
1505 private static List<DataGridViewRow> GetAllDataGridViewRows(DataGridView dataGridView)
1506 {
1507 return dataGridView.Rows.OfType<DataGridViewRow>().ToList();
1508 }
1509  
1510 private async Task RemoveColorFiles(IReadOnlyList<DataGridViewRow> rows,
1511 IProgress<DataGridViewRowProgress> progress,
1512 CancellationToken cancellationToken)
1513 {
1514 var count = rows.Count;
1515  
1516 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1517 {
1518 try
1519 {
11 office 1520 await _snapshotDatabase.RemoveColorAsync((string)rows[i].Cells["HashColumn"].Value,
1 office 1521 cancellationToken);
1522  
1523 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1524 }
1525 catch (Exception exception)
1526 {
1527 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1528 }
1529 }
1530 }
1531  
1532 private async Task ColorFiles(IReadOnlyList<DataGridViewRow> rows, Color color,
1533 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken)
1534 {
1535 var count = rows.Count;
1536  
1537 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1538 {
1539 try
1540 {
11 office 1541 await _snapshotDatabase.UpdateColorAsync((string)rows[i].Cells["HashColumn"].Value, color,
1 office 1542 cancellationToken);
1543  
1544 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1545 }
1546 catch (Exception exception)
1547 {
1548 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1549 }
1550 }
1551 }
1552  
12 office 1553  
1554 private async Task TransferFiles(IReadOnlyList<DataGridViewRow> rows, Service service,
1555 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken)
1556 {
1557  
1558 var client = new WatsonTcpClient($"{service.HostEntry.AddressList[0]}", service.UPort);
1559 client.Events.MessageReceived += (sender, args) =>
1560 {
1561 Log.Information($"{args.Data.Length} byte long message received from {args.Client.IpPort}");
1562 };
1563  
1564 try
1565 {
1566 client.Connect();
1567  
1568 var count = rows.Count;
1569  
1570 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1571 {
1572 try
1573 {
1574 var completeSnapshot = await _snapshotDatabase.GetCompleteSnapshot((string)rows[i].Cells["HashColumn"].Value, cancellationToken);
1575  
1576 var jsonSnapshot = JsonConvert.SerializeObject(completeSnapshot);
1577  
1578 await client.SendAsync(jsonSnapshot, null, cancellationToken);
1579  
1580 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1581 }
1582 catch (Exception exception)
1583 {
1584 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1585 }
1586 }
1587 }
1588 finally
1589 {
1590 client.Dispose();
1591 }
1592  
1593 }
1594  
1 office 1595 private async Task RevertFile(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress,
1596 CancellationToken cancellationToken)
1597 {
1598 var count = rows.Count;
1599  
1600 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1601 {
1602 try
1603 {
11 office 1604 await _snapshotDatabase.RevertFileAsync((string)rows[i].Cells["NameColumn"].Value,
1 office 1605 (string)rows[i].Cells["HashColumn"].Value,
1606 cancellationToken, _mainForm.Configuration.AtomicOperations);
1607  
1608 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1609 }
1610 catch (Exception exception)
1611 {
1612 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1613 }
1614 }
1615 }
1616  
1617 private async void SaveFilesTo(IReadOnlyList<DataGridViewRow> rows, string directory,
1618 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken)
1619 {
1620 var count = rows.Count;
1621  
1622 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1623 {
1624 try
1625 {
1626 var fileInfo = new FileInfo((string)rows[i].Cells["NameColumn"].Value);
1627 var file = fileInfo.Name;
1628 var path = Path.Combine(directory, file);
1629  
11 office 1630 await _snapshotDatabase.SaveFileAsync(path, (string)rows[i].Cells["HashColumn"].Value,
1 office 1631 cancellationToken);
1632  
1633 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1634 }
1635 catch (Exception exception)
1636 {
1637 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1638 }
1639 }
1640 }
1641  
1642 private async Task RelocateFiles(IReadOnlyList<DataGridViewRow> rows, string directory,
1643 IProgress<DataGridViewRowProgress> progress,
1644 CancellationToken cancellationToken)
1645 {
1646 var count = rows.Count;
1647  
1648 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1649 {
1650 try
1651 {
1652 var path = Path.Combine(directory, (string)rows[i].Cells["NameColumn"].Value);
1653  
11 office 1654 await _snapshotDatabase.RelocateFileAsync((string)rows[i].Cells["HashColumn"].Value, path,
1 office 1655 cancellationToken);
1656  
1657 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1658 }
1659 catch (Exception exception)
1660 {
1661 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1662 }
1663 }
1664 }
1665  
1666 private async void RecomputeHashes(IReadOnlyList<DataGridViewRow> rows,
1667 IProgress<DataGridViewRowProgress> progress,
1668 CancellationToken cancellationToken)
1669 {
1670 var count = rows.Count;
1671  
1672 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1673 {
1674 try
1675 {
1676 using (var memoryStream =
11 office 1677 await _snapshotDatabase.RetrieveFileStreamAsync((string)rows[i].Cells["HashColumn"].Value,
1 office 1678 cancellationToken))
1679 {
1680 if (memoryStream == null)
1681 {
1682 continue;
1683 }
1684  
1685 using (var md5 = MD5.Create())
1686 {
1687 var recomputedHash = md5.ComputeHash(memoryStream);
1688 var hashHex = BitConverter.ToString(recomputedHash).Replace("-", "")
1689 .ToLowerInvariant();
1690  
11 office 1691 await _snapshotDatabase.UpdateHashAsync((string)rows[i].Cells["HashColumn"].Value, hashHex,
1 office 1692 cancellationToken);
1693  
1694 rows[i].Cells["HashColumn"].Value = hashHex;
1695  
1696 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1697 }
1698 }
1699 }
1700 catch (Exception exception)
1701 {
1702 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1703 }
1704 }
1705 }
1706  
1707 private async Task SaveDirectoryTo(IReadOnlyList<DataGridViewRow> rows, string basePath, string targetPath,
1708 IProgress<DataGridViewRowProgress> progress,
1709 CancellationToken cancellationToken)
1710 {
1711 var store = new HashSet<string>();
1712  
1713 var count = rows.Count;
1714  
1715 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1716 {
1717 try
1718 {
1719 // C:\aa\bbb\fff\gg.txt
1720 var rowPath = (string)rows[i].Cells["PathColumn"].Value;
1721 if (store.Contains(rowPath))
1722 {
1723 continue;
1724 }
1725  
1726 // C:\aa\bbb\fff\gg.txt subpath C:\aa\bbb\
1727 if (!rowPath.IsPathEqual(basePath) &&
1728 !rowPath.IsSubPathOf(basePath))
1729 {
1730 continue;
1731 }
1732  
1733 var rootPath = new DirectoryInfo(basePath).Name;
1734 var relPath = rowPath.Remove(0, basePath.Length).Trim('\\');
1735 var newPath = Path.Combine(targetPath, rootPath, relPath);
1736  
1737 var hash = (string)rows[i].Cells["HashColumn"].Value;
11 office 1738 await _snapshotDatabase.SaveFileAsync(newPath, hash, cancellationToken);
1 office 1739  
1740 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1741  
1742 if (!store.Contains(rowPath))
1743 {
1744 store.Add(rowPath);
1745 }
1746 }
1747 catch (Exception exception)
1748 {
1749 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1750 }
1751 }
1752 }
1753  
1754 private async Task NormalizeDateTime(IReadOnlyList<DataGridViewRow> rows,
1755 IProgress<DataGridViewRowProgress> progress,
1756 CancellationToken cancellationToken)
1757 {
1758 var count = rows.Count;
1759  
1760 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1761 {
1762 try
1763 {
11 office 1764 await _snapshotDatabase.NormalizeTimeAsync((string)rows[i].Cells["HashColumn"].Value,
1 office 1765 cancellationToken);
1766  
1767 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1768 }
1769 catch (Exception exception)
1770 {
1771 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1772 }
1773 }
1774 }
1775  
1776 private async void deleteToolStripMenuItem1_Click(object sender, EventArgs e)
1777 {
1778 var toolStripMenuItem = (ToolStripMenuItem)sender;
1779  
1780 var rows = GetSelectedDataGridViewRows(dataGridView1);
1781  
1782 var count = rows.Count;
1783  
1784 toolStripProgressBar1.Minimum = 0;
1785 toolStripProgressBar1.Maximum = count;
1786  
1787 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1788 {
1789 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1790 {
1791 Log.Error(rowProgressFailure.Exception, "Unable to delete screenshot.");
1792  
1793 toolStripStatusLabel1.Text =
1794 $"Could not delete screenshot for {rowProgress.Row.Cells["NameColumn"].Value}...";
1795 toolStripProgressBar1.Value = rowProgress.Index + 1;
1796  
1797 statusStrip1.Update();
1798  
1799 return;
1800 }
1801  
1802 toolStripStatusLabel1.Text =
1803 $"Colored {rowProgress.Row.Cells["NameColumn"].Value}...";
1804 toolStripProgressBar1.Value = rowProgress.Index + 1;
1805  
1806 statusStrip1.Update();
1807 });
1808  
1809 await Task.Run(() => DeleteScreenshots(rows, progress, _cancellationToken), _cancellationToken);
1810  
3 office 1811 if (_cancellationToken.IsCancellationRequested)
1 office 1812 {
1813 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1814 toolStripStatusLabel1.Text = "Done.";
1815 }
1816 }
1817  
1818 private async Task DeleteScreenshots(IReadOnlyList<DataGridViewRow> rows,
1819 IProgress<DataGridViewRowProgress> progress,
1820 CancellationToken cancellationToken)
1821 {
1822 var count = rows.Count;
1823  
1824 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1825 {
1826 try
1827 {
11 office 1828 await _snapshotDatabase.DeleteScreenshotAsync((string)rows[i].Cells["HashColumn"].Value,
1 office 1829 cancellationToken);
1830  
1831 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1832 }
1833 catch (Exception exception)
1834 {
1835 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1836 }
1837 }
1838 }
1839  
1840 #endregion
1841 }
1842 }