Horizon – Blame information for rev 24

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;
23 office 98 _snapshotDatabase.SnapshotTransferReceived += _snapshotDatabase_SnapshotTransferReceived;
1 office 99  
100 _cancellationTokenSource =
101 CancellationTokenSource.CreateLinkedTokenSource(_cancellationToken, cancellationToken);
102 _cancellationToken = _cancellationTokenSource.Token;
103 }
104  
105 /// <summary>
106 /// Clean up any resources being used.
107 /// </summary>
108 /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
109 protected override void Dispose(bool disposing)
110 {
111 if (disposing && components != null)
112 {
113 components.Dispose();
114 }
115  
116 _snapshotDatabase.SnapshotCreate -= SnapshotManager_SnapshotCreate;
23 office 117 _snapshotDatabase.SnapshotTransferReceived -= _snapshotDatabase_SnapshotTransferReceived;
1 office 118  
12 office 119 _horizonServiceBrowser.ServiceAdded -= OnHorizonServiceBrowserOnServiceAdded;
120 _horizonServiceBrowser.Dispose();
1 office 121 _localCancellationTokenSource.Cancel();
122  
123 base.Dispose(disposing);
124 }
125  
126 #endregion
127  
128 #region Event Handlers
12 office 129 private void contextMenuStrip1_Opening(object sender, CancelEventArgs e)
130 {
131 }
132  
133 private void contextMenuStrip1_Opened(object sender, EventArgs e)
134 {
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  
23 office 518 dataGridView.Rows[index].Selected = true;
1 office 519 dataGridView.Sort(dataGridView.Columns["TimeColumn"], ListSortDirection.Descending);
520 });
521 break;
522 case SnapshotCreateFailureEventArgs snapshotCreateFailure:
523 Log.Warning(snapshotCreateFailure.Exception, "Could not create snapshot.");
524 break;
525 }
526 }
527  
23 office 528  
529 private void _snapshotDatabase_SnapshotTransferReceived(object sender, SnapshotCreateEventArgs e)
530 {
531 switch (e)
532 {
533 case SnapshotCreateSuccessEventArgs snapshotCreateSuccessEventArgs:
534 dataGridView1.InvokeIfRequired(dataGridView =>
535 {
536 var index = dataGridView.Rows.Add();
537  
538 dataGridView.Rows[index].Cells["TimeColumn"].Value =
539 DateTime.Parse(snapshotCreateSuccessEventArgs.Time);
540 dataGridView.Rows[index].Cells["NameColumn"].Value = snapshotCreateSuccessEventArgs.Name;
541 dataGridView.Rows[index].Cells["PathColumn"].Value = snapshotCreateSuccessEventArgs.Path;
542 dataGridView.Rows[index].Cells["HashColumn"].Value = snapshotCreateSuccessEventArgs.Hash;
543 dataGridView.Rows[index].DefaultCellStyle.BackColor = snapshotCreateSuccessEventArgs.Color;
544  
545 dataGridView.Rows[index].Selected = true;
546 dataGridView.Sort(dataGridView.Columns["TimeColumn"], ListSortDirection.Descending);
547 });
548 break;
549 case SnapshotCreateFailureEventArgs snapshotCreateFailure:
550 Log.Warning(snapshotCreateFailure.Exception, "Could not create snapshot.");
551 break;
552 }
553 }
554  
1 office 555 private void RevertToThisToolStripMenuItem_Click(object sender, EventArgs e)
556 {
557 _mainForm.InvokeIfRequired(async form =>
558 {
559 var fileSystemWatchers = new List<FileSystemWatcherState>();
560 var watchPaths = new HashSet<string>();
561 // Temporary disable all filesystem watchers that are watching the selected file directory.
562 foreach (var row in GetSelectedDataGridViewRows(dataGridView1))
563 {
564 var path = (string)row.Cells["PathColumn"].Value;
565  
566 foreach (var fileSystemWatcher in form.FileSystemWatchers)
567 {
568 if (!path.IsPathEqual(fileSystemWatcher.Path) &&
569 !path.IsSubPathOf(fileSystemWatcher.Path))
570 {
571 continue;
572 }
573  
574 if (watchPaths.Contains(fileSystemWatcher.Path))
575 {
576 continue;
577 }
578  
579 fileSystemWatchers.Add(new FileSystemWatcherState(fileSystemWatcher));
580  
581 fileSystemWatcher.EnableRaisingEvents = false;
582  
583 watchPaths.Add(fileSystemWatcher.Path);
584 }
585 }
586  
587 try
588 {
589 var rows = GetSelectedDataGridViewRows(dataGridView1);
590  
591 var count = rows.Count;
592  
593 toolStripProgressBar1.Minimum = 0;
594 toolStripProgressBar1.Maximum = count;
595  
596 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
597 {
598 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
599 {
600 Log.Error(rowProgressFailure.Exception, "Could not revert to snapshot.");
601  
602 toolStripStatusLabel1.Text =
603 $"Could not revert {rowProgress.Row.Cells["NameColumn"].Value}...";
604 toolStripProgressBar1.Value = rowProgress.Index + 1;
605  
606 statusStrip1.Update();
607  
608 return;
609 }
610  
611 toolStripStatusLabel1.Text =
612 $"Reverted {rowProgress.Row.Cells["NameColumn"].Value}...";
613 toolStripProgressBar1.Value = rowProgress.Index + 1;
614  
615 statusStrip1.Update();
616 });
617  
618 await Task.Run(() => RevertFile(rows, progress, _cancellationToken), _cancellationToken);
619  
3 office 620 if (_cancellationToken.IsCancellationRequested)
1 office 621 {
622 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
623 toolStripStatusLabel1.Text = "Done.";
624 }
625 }
626 catch (Exception exception)
627 {
628 Log.Error(exception, "Could not update data grid view.");
629 }
630 finally
631 {
632 // Restore initial state.
633 foreach (var fileSystemWatcherState in fileSystemWatchers)
634 {
635 foreach (var fileSystemWatcher in form.FileSystemWatchers)
636 {
637 if (fileSystemWatcherState.FileSystemWatcher == fileSystemWatcher)
638 {
639 fileSystemWatcher.EnableRaisingEvents = fileSystemWatcherState.State;
640 }
641 }
642 }
643 }
644 });
645 }
646  
647 private async void SnapshotManagerForm_Load(object sender, EventArgs e)
648 {
12 office 649 // Start browsing for network services.
650 _horizonServiceBrowser.Browse("_horizon._tcp", "local");
651  
652 // Load snapshots.
1 office 653 toolStripProgressBar1.Minimum = 0;
11 office 654 toolStripProgressBar1.Maximum = (int)await _snapshotDatabase.CountSnapshotsAsync(_cancellationToken);
1 office 655  
656 var snapshotQueue = new ConcurrentQueue<Snapshot>();
657  
5 office 658 void IdleHandler(object idleHandlerSender, EventArgs idleHandlerArgs)
1 office 659 {
660 try
661 {
5 office 662 if (snapshotQueue.IsEmpty)
1 office 663 {
664 Application.Idle -= IdleHandler;
665  
666 dataGridView1.Sort(dataGridView1.Columns["TimeColumn"], ListSortDirection.Descending);
667 toolStripStatusLabel1.Text = "Done.";
5 office 668 }
1 office 669  
5 office 670 if (!snapshotQueue.TryDequeue(out var snapshot))
671 {
1 office 672 return;
673 }
674  
675 var index = dataGridView1.Rows.Add();
676  
677 dataGridView1.Rows[index].Cells["TimeColumn"].Value = DateTime.Parse(snapshot.Time);
678 dataGridView1.Rows[index].Cells["NameColumn"].Value = snapshot.Name;
679 dataGridView1.Rows[index].Cells["PathColumn"].Value = snapshot.Path;
680 dataGridView1.Rows[index].Cells["HashColumn"].Value = snapshot.Hash;
681 dataGridView1.Rows[index].DefaultCellStyle.BackColor = snapshot.Color;
682  
683 toolStripStatusLabel1.Text = $"Loaded {snapshot.Name}...";
684  
685 toolStripProgressBar1.Increment(1);
686  
687 statusStrip1.Update();
688 }
689 catch (Exception exception)
690 {
691 Log.Error(exception, "Could not update data grid view.");
692 }
693 }
5 office 694  
1 office 695 try
696 {
11 office 697 await foreach (var snapshot in _snapshotDatabase.LoadSnapshotsAsync(_cancellationToken).WithCancellation(_cancellationToken))
1 office 698 {
699 snapshotQueue.Enqueue(snapshot);
700 }
701  
5 office 702 Application.Idle += IdleHandler;
1 office 703 }
704 catch (Exception exception)
705 {
706 Application.Idle -= IdleHandler;
707  
708 Log.Error(exception, "Unable to load snapshots.");
709 }
710 }
711  
12 office 712 private void OnHorizonServiceBrowserOnServiceAdded(object o, Mono.Zeroconf.ServiceBrowseEventArgs args)
713 {
714 _resolvedService = args.Service;
715  
716 Log.Information("Found Service: {0}", _resolvedService.Name);
717  
718 _resolvedService.Resolved += OnServiceOnResolved;
719  
720 _resolvedService.Resolve();
721  
722 _resolvedService.Resolved -= OnServiceOnResolved;
723 }
724  
725 private void OnServiceOnResolved(object o, ServiceResolvedEventArgs args)
726 {
727 var service = (Service) args.Service;
728  
729 Log.Information("Resolved Service: {0} - {1}:{2} ({3} TXT record entries)", service.FullName, service.HostEntry.AddressList[0], service.UPort, service.TxtRecord.Count);
730  
14 office 731 // Do not add discovered services more than once.
732 if (!_discoveredHorizonNetworkShares.TryAdd(service.Name, service))
733 {
734 return;
735 }
12 office 736  
737 var discoveredServiceMenuItem = new ToolStripMenuItem(service.Name, null, OnShareWithNodeClicked);
738 discoveredServiceMenuItem.Tag = service;
739  
21 office 740 this.InvokeIfRequired(form =>
741 {
742 form.shareToToolStripMenuItem.DropDownItems.Add(discoveredServiceMenuItem);
743 });
12 office 744 }
745  
746 private async void OnShareWithNodeClicked(object sender, EventArgs e)
747 {
748 var toolStripMenuItem = (ToolStripMenuItem)sender;
749  
750 var service = (Service)toolStripMenuItem.Tag;
751  
752 var rows = GetSelectedDataGridViewRows(dataGridView1);
753  
754 var count = rows.Count;
755  
756 toolStripProgressBar1.Minimum = 0;
757 toolStripProgressBar1.Maximum = count;
758  
759 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
760 {
761 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
762 {
763 Log.Error(rowProgressFailure.Exception, "Unable to transfer data.");
764  
765 toolStripStatusLabel1.Text =
766 $"Could not transfer {rowProgress.Row.Cells["NameColumn"].Value}...";
767 toolStripProgressBar1.Value = rowProgress.Index + 1;
768  
769 statusStrip1.Update();
770  
771 return;
772 }
773  
774 toolStripStatusLabel1.Text =
775 $"Transferred {rowProgress.Row.Cells["NameColumn"].Value}...";
776 toolStripProgressBar1.Value = rowProgress.Index + 1;
777  
778 statusStrip1.Update();
779 });
780  
781 await Task.Run(() => TransferFiles(rows, service, progress, _cancellationToken), _cancellationToken);
782  
783 if (_cancellationToken.IsCancellationRequested)
784 {
785 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
786 toolStripStatusLabel1.Text = "Done.";
787 }
788 }
789  
1 office 790 private void SnapshotManagerForm_Closing(object sender, FormClosingEventArgs e)
791 {
792 _cancellationTokenSource.Cancel();
793  
794 if (_snapshotPreviewForm != null)
795 {
796 _snapshotPreviewForm.Close();
797 _snapshotPreviewForm = null;
798 }
799  
800 if (_hexViewForm != null)
801 {
802 _hexViewForm.Close();
803 _hexViewForm = null;
804 }
805 }
806  
807 private void DataGridView1_DragEnter(object sender, DragEventArgs e)
808 {
809 if (e.Data.GetDataPresent(DataFormats.FileDrop))
810 {
811 e.Effect = DragDropEffects.Copy;
812 return;
813 }
814  
815 e.Effect = DragDropEffects.None;
816 }
817  
7 office 818 private void CreateSnapshots(IReadOnlyList<string> files, Bitmap screenCapture, TrackedFolders.TrackedFolders trackedFolders, IProgress<CreateSnapshotProgress> progress, CancellationToken cancellationToken)
5 office 819 {
820 Parallel.ForEach(files, new ParallelOptions { MaxDegreeOfParallelism = 512 }, async file =>
821 {
822 var color = Color.Empty;
823 if (_mainForm.TrackedFolders.TryGet(file, out var folder))
824 {
825 color = folder.Color;
826 }
827  
828 var fileInfo = File.GetAttributes(file);
829 if (fileInfo.HasFlag(FileAttributes.Directory))
830 {
831 foreach (var directoryFile in Directory.GetFiles(file, "*.*", SearchOption.AllDirectories))
832 {
833 var name = Path.GetFileName(directoryFile);
834 var path = Path.Combine(Path.GetDirectoryName(directoryFile), name);
835  
836 try
837 {
11 office 838 await _snapshotDatabase.CreateSnapshotAsync(name, path, screenCapture, color,
5 office 839 _cancellationToken);
840  
841 progress.Report(new CreateSnapshotProgressSuccess(file));
842 }
843 catch (Exception exception)
844 {
845 progress.Report(new CreateSnapshotProgressFailure(file, exception));
846 }
847 }
848  
849 return;
850 }
851  
852 var fileName = Path.GetFileName(file);
853 var pathName = Path.Combine(Path.GetDirectoryName(file), fileName);
854  
855 try
856 {
11 office 857 await _snapshotDatabase.CreateSnapshotAsync(fileName, pathName, screenCapture, color,
5 office 858 _cancellationToken);
859  
860 progress.Report(new CreateSnapshotProgressSuccess(file));
861 }
862 catch (Exception exception)
863 {
864 progress.Report(new CreateSnapshotProgressFailure(file, exception));
865 }
866 });
867 }
1 office 868 private async void DataGridView1_DragDrop(object sender, DragEventArgs e)
869 {
870 if (!e.Data.GetDataPresent(DataFormats.FileDrop))
871 {
872 return;
873 }
874  
875 var files = (string[])e.Data.GetData(DataFormats.FileDrop);
876  
877 toolStripProgressBar1.Minimum = 0;
878 toolStripProgressBar1.Maximum = files.Length;
879 toolStripStatusLabel1.Text = "Snapshotting files...";
880  
881 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode);
5 office 882  
883 var progress = new Progress<CreateSnapshotProgress>(createSnapshotProgress =>
1 office 884 {
5 office 885 switch (createSnapshotProgress)
886 {
887 case CreateSnapshotProgressSuccess createSnapshotProgressSuccess:
888 toolStripStatusLabel1.Text = $"Snapshot taken of {createSnapshotProgressSuccess.File}.";
889 break;
890 case CreateSnapshotProgressFailure createSnapshotProgressFailure:
891 if (createSnapshotProgressFailure.Exception is SQLiteException { ResultCode: SQLiteErrorCode.Constraint })
892 {
893 toolStripStatusLabel1.Text = $"Snapshot of file {createSnapshotProgressFailure.File} already exists.";
894 break;
895 }
896  
897 toolStripStatusLabel1.Text = $"Could not snapshot file {createSnapshotProgressFailure.File}";
898 Log.Warning(createSnapshotProgressFailure.Exception, $"Could not snapshot file {createSnapshotProgressFailure.File}");
899 break;
900 }
901  
902 toolStripProgressBar1.Increment(1);
903 statusStrip1.Update();
904 });
905  
11 office 906 await Task.Factory.StartNew( () =>
5 office 907 {
7 office 908 CreateSnapshots(files, screenCapture, _mainForm.TrackedFolders, progress, _cancellationToken);
5 office 909 }, _cancellationToken);
1 office 910 }
911  
912 private async void FileToolStripMenuItem_Click(object sender, EventArgs e)
913 {
914 var dialog = new CommonOpenFileDialog();
915 if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
916 {
917 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode);
918  
919 var fileName = Path.GetFileName(dialog.FileName);
920 var directory = Path.GetDirectoryName(dialog.FileName);
921 var pathName = Path.Combine(directory, fileName);
922  
923 var color = Color.Empty;
924 if (_mainForm.TrackedFolders.TryGet(directory, out var folder))
925 {
926 color = folder.Color;
927 }
928  
929 try
930 {
11 office 931 await _snapshotDatabase.CreateSnapshotAsync(fileName, pathName, screenCapture, color,
1 office 932 _cancellationToken);
933 }
934 catch (SQLiteException exception)
935 {
936 if (exception.ResultCode == SQLiteErrorCode.Constraint)
937 {
938 Log.Information(exception, "Snapshot already exists.");
939 }
940 }
941 catch (Exception exception)
942 {
943 Log.Warning(exception, "Could not create snapshot.");
944 }
945 }
946 }
947  
948 private async void DirectoryToolStripMenuItem_Click(object sender, EventArgs e)
949 {
950 var dialog = new CommonOpenFileDialog { IsFolderPicker = true };
951 if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
952 {
953 var screenCapture = ScreenCapture.Capture((CaptureMode)_mainForm.Configuration.CaptureMode);
954 foreach (var directoryFile in Directory.GetFiles(dialog.FileName, "*.*", SearchOption.AllDirectories))
955 {
956 var name = Path.GetFileName(directoryFile);
957 var directory = Path.GetDirectoryName(directoryFile);
958 var path = Path.Combine(directory, name);
959  
960 var color = Color.Empty;
961 if (_mainForm.TrackedFolders.TryGet(directory, out var folder))
962 {
963 color = folder.Color;
964 }
965  
966 try
967 {
11 office 968 await _snapshotDatabase.CreateSnapshotAsync(name, path, screenCapture, color, _cancellationToken);
1 office 969 }
970 catch (SQLiteException exception)
971 {
972 if (exception.ResultCode == SQLiteErrorCode.Constraint)
973 {
974 Log.Information(exception, "Snapshot already exists.");
975 }
976 }
977 catch (Exception exception)
978 {
979 Log.Warning(exception, "Could not create snapshot.");
980 }
981 }
982 }
983 }
984  
985 private async void RelocateToolStripMenuItem_Click(object sender, EventArgs e)
986 {
987 var commonOpenFileDialog = new CommonOpenFileDialog
988 {
989 InitialDirectory = _mainForm.Configuration.LastFolder,
990 IsFolderPicker = true
991 };
992  
993 if (commonOpenFileDialog.ShowDialog() != CommonFileDialogResult.Ok)
994 {
995 return;
996 }
997  
998 _mainForm.Configuration.LastFolder = commonOpenFileDialog.FileName;
999 _mainForm.ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
1000 async () => await _mainForm.SaveConfiguration(), _cancellationToken);
1001  
1002 var directory = commonOpenFileDialog.FileName;
1003  
1004 var rows = GetSelectedDataGridViewRows(dataGridView1);
1005  
1006 var count = rows.Count;
1007  
1008 toolStripProgressBar1.Minimum = 0;
1009 toolStripProgressBar1.Maximum = count;
1010  
1011 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1012 {
1013 var path = Path.Combine(directory,
1014 (string)rowProgress.Row.Cells["NameColumn"].Value);
1015  
1016 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1017 {
1018 Log.Error(rowProgressFailure.Exception, "Could not relocate snapshot.");
1019  
1020 toolStripStatusLabel1.Text =
1021 $"Could not relocate {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
1022 toolStripProgressBar1.Value = rowProgress.Index + 1;
1023  
1024 statusStrip1.Update();
1025  
1026 return;
1027 }
1028  
1029 rowProgress.Row.Cells["PathColumn"].Value = path;
1030  
1031 toolStripStatusLabel1.Text =
1032 $"Relocated {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
1033 toolStripProgressBar1.Value = rowProgress.Index + 1;
1034  
1035 statusStrip1.Update();
1036 });
1037  
1038 await Task.Run(() => RelocateFiles(rows, directory, progress, _cancellationToken), _cancellationToken);
1039  
3 office 1040 if (_cancellationToken.IsCancellationRequested)
1 office 1041 {
1042 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1043 toolStripStatusLabel1.Text = "Done.";
1044 }
1045 }
1046  
1047 private async void NoteToolStripMenuItem_Click(object sender, EventArgs e)
1048 {
1049 if (_snapshotNote != null)
1050 {
1051 return;
1052 }
1053  
1054 var row = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault();
1055 if (row == null)
1056 {
1057 return;
1058 }
1059  
1060 try
1061 {
11 office 1062 var snapshotPreview = await _snapshotDatabase.RetrievePreviewAsync(
1 office 1063 (string)row.Cells["HashColumn"].Value, _cancellationToken);
1064  
1065 if (snapshotPreview == null)
1066 {
1067 return;
1068 }
1069  
1070 _snapshotNote = new SnapshotNoteForm(this, snapshotPreview);
1071 _snapshotNote.Owner = this;
1072 _snapshotNote.SaveNote += SnapshotNote_SaveNote;
1073 _snapshotNote.Closing += SnapshotNote_Closing;
1074 _snapshotNote.Show();
1075 }
1076 catch (Exception exception)
1077 {
1078 Log.Error(exception, "Could not open notes form.");
1079 }
1080 }
1081  
1082 private async void SnapshotNote_SaveNote(object sender, SaveNoteEventArgs e)
1083 {
1084 var rows = GetSelectedDataGridViewRows(dataGridView1);
1085  
1086 var count = rows.Count;
1087  
1088 toolStripProgressBar1.Minimum = 0;
1089 toolStripProgressBar1.Maximum = count;
1090  
1091 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1092 {
1093 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1094 {
1095 Log.Error(rowProgressFailure.Exception, "Could not update note for snapshot.");
1096  
1097 toolStripStatusLabel1.Text =
1098 $"Could not update note for {rowProgress.Row.Cells["NameColumn"].Value}...";
1099 toolStripProgressBar1.Value = rowProgress.Index + 1;
1100  
1101 statusStrip1.Update();
1102  
1103 return;
1104 }
1105  
1106 toolStripStatusLabel1.Text =
1107 $"Updated note for {rowProgress.Row.Cells["NameColumn"].Value}...";
1108 toolStripProgressBar1.Value = rowProgress.Index + 1;
1109  
1110 statusStrip1.Update();
1111 });
1112  
1113 await Task.Run(() => UpdateNote(rows, e.Note, progress, _cancellationToken), _cancellationToken);
1114  
3 office 1115 if (_cancellationToken.IsCancellationRequested)
1 office 1116 {
1117 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1118 toolStripStatusLabel1.Text = "Done.";
1119 }
1120 }
1121  
1122 private void SnapshotNote_Closing(object sender, CancelEventArgs e)
1123 {
1124 if (_snapshotNote == null)
1125 {
1126 return;
1127 }
1128  
1129 _snapshotNote.Closing -= SnapshotNote_Closing;
1130 _snapshotNote.Close();
1131 _snapshotNote = null;
1132 }
1133  
1134 private async void ViewHexToolStripMenuItem_Click(object sender, EventArgs e)
1135 {
1136 var rows = GetSelectedDataGridViewRows(dataGridView1);
1137 var row = rows.FirstOrDefault();
1138 if (row == null)
1139 {
1140 return;
1141 }
1142  
1143 var hash = (string)row.Cells["HashColumn"].Value;
1144  
11 office 1145 using (var memoryStream = await _snapshotDatabase.RetrieveFileStreamAsync(hash, _cancellationToken))
1 office 1146 {
1147 if (memoryStream == null)
1148 {
1149 return;
1150 }
1151  
1152 if (_hexViewForm != null)
1153 {
1154 _hexViewForm.UpdateData(memoryStream.ToArray());
1155 _hexViewForm.Activate();
1156 return;
1157 }
1158  
1159 _hexViewForm = new HexViewForm(_snapshotDatabase, hash, memoryStream.ToArray());
1160 _hexViewForm.Owner = this;
1161 _hexViewForm.Closing += HexViewForm_Closing;
1162 _hexViewForm.SaveData += HexViewForm_SaveData;
1163  
1164 _hexViewForm.Show();
1165 }
1166 }
1167  
1168 private async void HexViewForm_SaveData(object sender, SaveDataEventArgs e)
1169 {
11 office 1170 var hash = await _snapshotDatabase.UpdateFileAsync(e.Hash, e.Data, _cancellationToken);
1 office 1171  
1172 if (string.IsNullOrEmpty(hash))
1173 {
1174 return;
1175 }
1176  
1177 dataGridView1.InvokeIfRequired(dataGridView =>
1178 {
1179 // Update the hash in the datagridview.
1180 var removeRows = new List<DataGridViewRow>();
1181 foreach (var row in dataGridView.Rows.OfType<DataGridViewRow>())
1182 {
1183 if ((string)row.Cells["HashColumn"].Value == hash)
1184 {
1185 removeRows.Add(row);
1186 }
1187  
1188 if ((string)row.Cells["HashColumn"].Value != e.Hash)
1189 {
1190 continue;
1191 }
1192  
1193 row.Cells["HashColumn"].Value = hash;
1194 }
1195  
1196 // Remove rows that might have the same hash.
1197 foreach (var row in removeRows)
1198 {
1199 dataGridView.Rows.Remove(row);
1200 }
1201 });
1202 }
1203  
1204 private void HexViewForm_Closing(object sender, CancelEventArgs e)
1205 {
1206 if (_hexViewForm == null)
1207 {
1208 return;
1209 }
1210  
1211 _hexViewForm.SaveData -= HexViewForm_SaveData;
1212 _hexViewForm.Closing -= HexViewForm_Closing;
1213 _hexViewForm.Close();
1214 _hexViewForm = null;
1215 }
1216  
1217 private async void FileToolStripMenuItem2_Click(object sender, EventArgs e)
1218 {
1219 var commonOpenFileDialog = new CommonOpenFileDialog
1220 {
1221 InitialDirectory = _mainForm.Configuration.LastFolder,
1222 IsFolderPicker = true
1223 };
1224  
1225 if (commonOpenFileDialog.ShowDialog() == CommonFileDialogResult.Ok)
1226 {
1227 _mainForm.Configuration.LastFolder = commonOpenFileDialog.FileName;
1228 _mainForm.ChangedConfigurationContinuation.Schedule(TimeSpan.FromSeconds(1),
1229 async () => await _mainForm.SaveConfiguration(), _cancellationToken);
1230  
1231 var directory = commonOpenFileDialog.FileName;
1232  
1233 var rows = GetSelectedDataGridViewRows(dataGridView1);
1234  
1235 var count = rows.Count;
1236  
1237 toolStripProgressBar1.Minimum = 0;
1238 toolStripProgressBar1.Maximum = count;
1239  
1240 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1241 {
1242 var fileInfo =
1243 new FileInfo((string)rowProgress.Row.Cells["NameColumn"].Value);
1244 var file = fileInfo.Name;
1245 var path = Path.Combine(directory, file);
1246  
1247 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1248 {
1249 Log.Error(rowProgressFailure.Exception, "Could not save snapshot.");
1250  
1251 toolStripStatusLabel1.Text =
1252 $"Could not save snapshot {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
1253 toolStripProgressBar1.Value = rowProgress.Index + 1;
1254  
1255 statusStrip1.Update();
1256  
1257 return;
1258 }
1259  
1260 toolStripStatusLabel1.Text =
1261 $"Saved {rowProgress.Row.Cells["NameColumn"].Value} to {path}...";
1262 toolStripProgressBar1.Value = rowProgress.Index + 1;
1263  
1264 statusStrip1.Update();
1265 });
1266  
1267 await Task.Run(() => SaveFilesTo(rows, directory, progress, _cancellationToken), _cancellationToken);
1268  
3 office 1269 if (_cancellationToken.IsCancellationRequested)
1 office 1270 {
1271 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1272 toolStripStatusLabel1.Text = "Done.";
1273 }
1274 }
1275 }
1276  
1277 private async void DirectoryToolStripMenuItem2_Click(object sender, EventArgs e)
1278 {
1279 var select = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault();
1280  
1281 if (select == null)
1282 {
1283 return;
1284 }
1285  
1286 // C:\aa\bbb\dd.txt
1287 var path = (string)select.Cells["PathColumn"].Value;
1288  
1289 // C:\aa\bbb\
1290 var basePath = Path.GetDirectoryName(path);
1291  
1292 var dialog = new CommonOpenFileDialog { IsFolderPicker = true };
1293 if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
1294 {
1295 //Log.Information(dialog.FileName);
1296 var rows = GetAllDataGridViewRows(dataGridView1);
1297 var count = rows.Count;
1298  
1299 toolStripProgressBar1.Minimum = 0;
1300 toolStripProgressBar1.Maximum = count;
1301 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1302 {
1303 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1304 {
1305 Log.Error(rowProgressFailure.Exception, "Could not save file.");
1306  
1307 toolStripStatusLabel1.Text =
1308 $"Could not save file {rowProgress.Row.Cells["NameColumn"].Value} to {basePath}...";
1309 toolStripProgressBar1.Value = rowProgress.Index + 1;
1310  
1311 statusStrip1.Update();
1312  
1313 return;
1314 }
1315  
1316 toolStripStatusLabel1.Text =
1317 $"Saved {rowProgress.Row.Cells["NameColumn"].Value} to {basePath}...";
1318 toolStripProgressBar1.Value = rowProgress.Index + 1;
1319  
1320 statusStrip1.Update();
1321 });
1322  
1323 await Task.Run(() => SaveDirectoryTo(rows, basePath, dialog.FileName, progress, _cancellationToken),
1324 _cancellationToken);
1325  
3 office 1326 if (_cancellationToken.IsCancellationRequested)
1 office 1327 {
1328 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1329 toolStripStatusLabel1.Text = "Done.";
1330 }
1331 }
1332 }
1333  
1334 private void TextBox1_TextChanged(object sender, EventArgs e)
1335 {
1336 _searchTextBoxChangedContinuation.Schedule(TimeSpan.FromSeconds(1), () =>
1337 {
1338 textBox1.InvokeIfRequired(textBox =>
1339 {
1340 var search = textBox.Text;
1341  
1342 dataGridView1.InvokeIfRequired(dataGridView =>
1343 {
1344 foreach (var row in GetAllDataGridViewRows(dataGridView))
1345 {
1346 if(row.Cells["PathColumn"].Value == null)
1347 {
1348 continue;
1349 }
1350  
1351 switch (((string)row.Cells["PathColumn"].Value).IndexOf(search,
1352 StringComparison.OrdinalIgnoreCase))
1353 {
1354 case -1:
1355 row.Visible = false;
1356 break;
1357 default:
1358 row.Visible = true;
1359 break;
1360 }
1361 }
1362 });
1363 });
1364 }, _cancellationToken);
1365 }
1366  
1367 private async void RecomputeHashesToolStripMenuItem1_Click(object sender, EventArgs e)
1368 {
1369 var rows = GetSelectedDataGridViewRows(dataGridView1);
1370  
1371 var count = rows.Count;
1372  
1373 toolStripProgressBar1.Minimum = 0;
1374 toolStripProgressBar1.Maximum = count;
1375  
1376 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1377 {
1378 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1379 {
1380 Log.Error(rowProgressFailure.Exception, "Could not recompute hash for snapshot.");
1381  
1382 toolStripStatusLabel1.Text =
1383 $"Could not recompute hash for {rowProgress.Row.Cells["NameColumn"].Value}...";
1384 toolStripProgressBar1.Value = rowProgress.Index + 1;
1385  
1386 statusStrip1.Update();
1387  
1388 return;
1389 }
1390  
1391 toolStripStatusLabel1.Text =
1392 $"Recomputed hash for {rowProgress.Row.Cells["NameColumn"].Value}...";
1393 toolStripProgressBar1.Value = rowProgress.Index + 1;
1394  
1395 statusStrip1.Update();
1396 });
1397  
1398 await Task.Run(() => RecomputeHashes(rows, progress, _cancellationToken), _cancellationToken);
1399  
3 office 1400 if (_cancellationToken.IsCancellationRequested)
1 office 1401 {
1402 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1403 toolStripStatusLabel1.Text = "Done.";
1404 }
1405 }
1406  
1407 private async void NormalizeDateTimeToolStripMenuItem_Click(object sender, EventArgs e)
1408 {
1409 var rows = GetSelectedDataGridViewRows(dataGridView1);
1410  
1411 var count = rows.Count;
1412  
1413 toolStripProgressBar1.Minimum = 0;
1414 toolStripProgressBar1.Maximum = count;
1415  
1416 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1417 {
1418 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1419 {
1420 Log.Error(rowProgressFailure.Exception, "Could not normalize date-time for snapshot.");
1421  
1422 toolStripStatusLabel1.Text =
1423 $"Could not normalize date-time for {rowProgress.Row.Cells["NameColumn"].Value}...";
1424 toolStripProgressBar1.Value = rowProgress.Index + 1;
1425  
1426 statusStrip1.Update();
1427  
1428 return;
1429 }
1430  
1431 toolStripStatusLabel1.Text =
1432 $"Normalized date-time for {rowProgress.Row.Cells["NameColumn"].Value}...";
1433 toolStripProgressBar1.Value = rowProgress.Index + 1;
1434  
1435 statusStrip1.Update();
1436 });
1437  
1438 await Task.Run(() => NormalizeDateTime(rows, progress, _cancellationToken), _cancellationToken);
1439  
3 office 1440 if (_cancellationToken.IsCancellationRequested)
1 office 1441 {
1442 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1443 toolStripStatusLabel1.Text = "Done.";
1444 }
1445 }
1446  
1447 #endregion
1448  
1449 #region Private Methods
12 office 1450 private void DataGridView1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)
1451 {
1452 DataGridView dataGridView = sender as DataGridView;
1453 foreach (DataGridViewCell cell in dataGridView.Rows[e.RowIndex].Cells)
1454 {
1455 if (cell.Selected == false) { continue; }
1456 var bgColorCell = Color.White;
1457 if (cell.Style.BackColor != Color.Empty) { bgColorCell = cell.Style.BackColor; }
1458 else if (cell.InheritedStyle.BackColor != Color.Empty) { bgColorCell = cell.InheritedStyle.BackColor; }
1459 cell.Style.SelectionBackColor = MixColor(bgColorCell, Color.FromArgb(0, 150, 255), 10, 4);
1460 }
1461 }
1 office 1462  
12 office 1463 //Mix two colors
1464 //Example: Steps=10 & Position=4 makes Color2 mix 40% into Color1
1465 /// <summary>
1466 /// Mix two colors.
1467 /// </summary>
1468 /// <param name="Color1"></param>
1469 /// <param name="Color2"></param>
1470 /// <param name="Steps"></param>
1471 /// <param name="Position"></param>
1472 /// <example>Steps=10 & Positon=4 makes Color2 mix 40% into Color1</example>
1473 /// <remarks>https://stackoverflow.com/questions/38337849/transparent-selectionbackcolor-for-datagridview-cell</remarks>
1474 /// <returns></returns>
1475 public static Color MixColor(Color Color1, Color Color2, int Steps, int Position)
1476 {
1477 if (Position <= 0 || Steps <= 1) { return Color1; }
1478 if (Position >= Steps) { return Color2; }
1479 return Color.FromArgb(
1480 Color1.R + ((Color2.R - Color1.R) / Steps * Position),
1481 Color1.G + ((Color2.G - Color1.G) / Steps * Position),
1482 Color1.B + ((Color2.B - Color1.B) / Steps * Position)
1483 );
1484 }
1485  
1 office 1486 private async Task DeleteFiles(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress,
1487 CancellationToken cancellationToken)
1488 {
1489 var count = rows.Count;
1490  
1491 for (var index = 0; index < count && !cancellationToken.IsCancellationRequested; ++index)
1492 {
1493 try
1494 {
11 office 1495 await _snapshotDatabase.RemoveFileAsync((string)rows[index].Cells["HashColumn"].Value,
1 office 1496 cancellationToken);
1497  
1498 progress.Report(new DataGridViewRowProgressSuccess(rows[index], index));
1499 }
1500 catch (Exception exception)
1501 {
1502 progress.Report(new DataGridViewRowProgressFailure(rows[index], index, exception));
1503 }
1504 }
1505 }
1506  
1507 private async Task DeleteFilesFast(IReadOnlyList<DataGridViewRow> rows, CancellationToken cancellationToken)
1508 {
1509 var hashes = rows.Select(row => (string)row.Cells["HashColumn"].Value);
1510  
11 office 1511 await _snapshotDatabase.RemoveFileFastAsync(hashes, cancellationToken);
1 office 1512 }
1513  
1514 private async Task UpdateNote(IReadOnlyList<DataGridViewRow> rows, string note,
1515 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken)
1516 {
1517 var count = rows.Count;
1518  
1519 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1520 {
1521 try
1522 {
11 office 1523 await _snapshotDatabase.UpdateNoteAsync((string)rows[i].Cells["HashColumn"].Value, note,
1 office 1524 cancellationToken);
1525  
1526 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1527 }
1528 catch (Exception exception)
1529 {
1530 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1531 }
1532 }
1533 }
1534  
1535 private static List<DataGridViewRow> GetSelectedDataGridViewRows(DataGridView dataGridView)
1536 {
22 office 1537 return dataGridView.SelectedRows.OfType<DataGridViewRow>().Where(row => row.Visible).ToList();
1 office 1538 }
1539  
1540 private static List<DataGridViewRow> GetAllDataGridViewRows(DataGridView dataGridView)
1541 {
22 office 1542 return dataGridView.Rows.OfType<DataGridViewRow>().Where(row => row.Visible).ToList();
1 office 1543 }
1544  
1545 private async Task RemoveColorFiles(IReadOnlyList<DataGridViewRow> rows,
1546 IProgress<DataGridViewRowProgress> progress,
1547 CancellationToken cancellationToken)
1548 {
1549 var count = rows.Count;
1550  
1551 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1552 {
1553 try
1554 {
11 office 1555 await _snapshotDatabase.RemoveColorAsync((string)rows[i].Cells["HashColumn"].Value,
1 office 1556 cancellationToken);
1557  
1558 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1559 }
1560 catch (Exception exception)
1561 {
1562 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1563 }
1564 }
1565 }
1566  
1567 private async Task ColorFiles(IReadOnlyList<DataGridViewRow> rows, Color color,
1568 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken)
1569 {
1570 var count = rows.Count;
1571  
1572 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1573 {
1574 try
1575 {
11 office 1576 await _snapshotDatabase.UpdateColorAsync((string)rows[i].Cells["HashColumn"].Value, color,
1 office 1577 cancellationToken);
1578  
1579 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1580 }
1581 catch (Exception exception)
1582 {
1583 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1584 }
1585 }
1586 }
1587  
12 office 1588 private async Task TransferFiles(IReadOnlyList<DataGridViewRow> rows, Service service,
1589 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken)
1590 {
1591  
1592 var client = new WatsonTcpClient($"{service.HostEntry.AddressList[0]}", service.UPort);
21 office 1593 client.Events.MessageReceived += Events_MessageReceived;
1594 client.Events.ServerConnected += Events_ServerConnected;
1595 client.Events.ServerDisconnected += Events_ServerDisconnected;
1596 client.Events.ExceptionEncountered += Events_ExceptionEncountered;
12 office 1597  
1598 try
1599 {
1600 client.Connect();
1601  
1602 var count = rows.Count;
1603  
1604 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1605 {
1606 try
1607 {
21 office 1608 var completeSnapshot =
1609 await _snapshotDatabase.GenerateTransferSnapshotAsync(
1610 (string)rows[i].Cells["HashColumn"].Value, cancellationToken);
12 office 1611  
1612 var jsonSnapshot = JsonConvert.SerializeObject(completeSnapshot);
1613  
1614 await client.SendAsync(jsonSnapshot, null, cancellationToken);
1615  
1616 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1617 }
1618 catch (Exception exception)
1619 {
1620 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1621 }
1622 }
1623 }
24 office 1624 catch (Exception exception) when (exception is TimeoutException || exception is SocketException)
21 office 1625 {
1626 Log.Error(exception, "Client threw exception.");
1627 }
12 office 1628 finally
1629 {
21 office 1630 client.Events.MessageReceived -= Events_MessageReceived;
1631 client.Events.ServerConnected -= Events_ServerConnected;
1632 client.Events.ServerDisconnected -= Events_ServerDisconnected;
1633 client.Events.ExceptionEncountered -= Events_ExceptionEncountered;
1634  
12 office 1635 client.Dispose();
1636 }
21 office 1637  
12 office 1638 }
1639  
21 office 1640 private void Events_ExceptionEncountered(object sender, ExceptionEventArgs e)
1641 {
1642 Log.Error(e.Exception, $"Client threw exception.");
1643 }
1644  
1645 private void Events_ServerDisconnected(object sender, DisconnectionEventArgs e)
1646 {
1647 Log.Information($"{e.Client?.IpPort} connected to server due to: {e.Reason}.");
1648 }
1649  
1650 private void Events_MessageReceived(object sender, MessageReceivedEventArgs e)
1651 {
1652 Log.Information($"{e.Data?.Length} byte long message received from {e.Client?.IpPort}");
1653 }
1654  
1655 private void Events_ServerConnected(object sender, WatsonTcp.ConnectionEventArgs e)
1656 {
1657 Log.Information($"{e.Client?.IpPort} connected to server.");
1658 }
1659  
1 office 1660 private async Task RevertFile(IReadOnlyList<DataGridViewRow> rows, IProgress<DataGridViewRowProgress> progress,
1661 CancellationToken cancellationToken)
1662 {
1663 var count = rows.Count;
1664  
1665 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1666 {
1667 try
1668 {
11 office 1669 await _snapshotDatabase.RevertFileAsync((string)rows[i].Cells["NameColumn"].Value,
1 office 1670 (string)rows[i].Cells["HashColumn"].Value,
1671 cancellationToken, _mainForm.Configuration.AtomicOperations);
1672  
1673 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1674 }
1675 catch (Exception exception)
1676 {
1677 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1678 }
1679 }
1680 }
1681  
1682 private async void SaveFilesTo(IReadOnlyList<DataGridViewRow> rows, string directory,
1683 IProgress<DataGridViewRowProgress> progress, CancellationToken cancellationToken)
1684 {
1685 var count = rows.Count;
1686  
1687 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1688 {
1689 try
1690 {
1691 var fileInfo = new FileInfo((string)rows[i].Cells["NameColumn"].Value);
1692 var file = fileInfo.Name;
1693 var path = Path.Combine(directory, file);
1694  
11 office 1695 await _snapshotDatabase.SaveFileAsync(path, (string)rows[i].Cells["HashColumn"].Value,
1 office 1696 cancellationToken);
1697  
1698 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1699 }
1700 catch (Exception exception)
1701 {
1702 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1703 }
1704 }
1705 }
1706  
1707 private async Task RelocateFiles(IReadOnlyList<DataGridViewRow> rows, string directory,
1708 IProgress<DataGridViewRowProgress> progress,
1709 CancellationToken cancellationToken)
1710 {
1711 var count = rows.Count;
1712  
1713 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1714 {
1715 try
1716 {
1717 var path = Path.Combine(directory, (string)rows[i].Cells["NameColumn"].Value);
1718  
11 office 1719 await _snapshotDatabase.RelocateFileAsync((string)rows[i].Cells["HashColumn"].Value, path,
1 office 1720 cancellationToken);
1721  
1722 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1723 }
1724 catch (Exception exception)
1725 {
1726 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1727 }
1728 }
1729 }
1730  
1731 private async void RecomputeHashes(IReadOnlyList<DataGridViewRow> rows,
1732 IProgress<DataGridViewRowProgress> progress,
1733 CancellationToken cancellationToken)
1734 {
1735 var count = rows.Count;
1736  
1737 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1738 {
1739 try
1740 {
1741 using (var memoryStream =
11 office 1742 await _snapshotDatabase.RetrieveFileStreamAsync((string)rows[i].Cells["HashColumn"].Value,
1 office 1743 cancellationToken))
1744 {
1745 if (memoryStream == null)
1746 {
1747 continue;
1748 }
1749  
1750 using (var md5 = MD5.Create())
1751 {
1752 var recomputedHash = md5.ComputeHash(memoryStream);
1753 var hashHex = BitConverter.ToString(recomputedHash).Replace("-", "")
1754 .ToLowerInvariant();
1755  
11 office 1756 await _snapshotDatabase.UpdateHashAsync((string)rows[i].Cells["HashColumn"].Value, hashHex,
1 office 1757 cancellationToken);
1758  
1759 rows[i].Cells["HashColumn"].Value = hashHex;
1760  
1761 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1762 }
1763 }
1764 }
1765 catch (Exception exception)
1766 {
1767 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1768 }
1769 }
1770 }
1771  
1772 private async Task SaveDirectoryTo(IReadOnlyList<DataGridViewRow> rows, string basePath, string targetPath,
1773 IProgress<DataGridViewRowProgress> progress,
1774 CancellationToken cancellationToken)
1775 {
1776 var store = new HashSet<string>();
1777  
1778 var count = rows.Count;
1779  
1780 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1781 {
1782 try
1783 {
1784 // C:\aa\bbb\fff\gg.txt
1785 var rowPath = (string)rows[i].Cells["PathColumn"].Value;
1786 if (store.Contains(rowPath))
1787 {
1788 continue;
1789 }
1790  
1791 // C:\aa\bbb\fff\gg.txt subpath C:\aa\bbb\
1792 if (!rowPath.IsPathEqual(basePath) &&
1793 !rowPath.IsSubPathOf(basePath))
1794 {
1795 continue;
1796 }
1797  
1798 var rootPath = new DirectoryInfo(basePath).Name;
1799 var relPath = rowPath.Remove(0, basePath.Length).Trim('\\');
1800 var newPath = Path.Combine(targetPath, rootPath, relPath);
1801  
1802 var hash = (string)rows[i].Cells["HashColumn"].Value;
11 office 1803 await _snapshotDatabase.SaveFileAsync(newPath, hash, cancellationToken);
1 office 1804  
1805 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1806  
1807 if (!store.Contains(rowPath))
1808 {
1809 store.Add(rowPath);
1810 }
1811 }
1812 catch (Exception exception)
1813 {
1814 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1815 }
1816 }
1817 }
1818  
1819 private async Task NormalizeDateTime(IReadOnlyList<DataGridViewRow> rows,
1820 IProgress<DataGridViewRowProgress> progress,
1821 CancellationToken cancellationToken)
1822 {
1823 var count = rows.Count;
1824  
1825 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1826 {
1827 try
1828 {
11 office 1829 await _snapshotDatabase.NormalizeTimeAsync((string)rows[i].Cells["HashColumn"].Value,
1 office 1830 cancellationToken);
1831  
1832 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1833 }
1834 catch (Exception exception)
1835 {
1836 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1837 }
1838 }
1839 }
1840  
1841 private async void deleteToolStripMenuItem1_Click(object sender, EventArgs e)
1842 {
1843 var toolStripMenuItem = (ToolStripMenuItem)sender;
1844  
1845 var rows = GetSelectedDataGridViewRows(dataGridView1);
1846  
1847 var count = rows.Count;
1848  
1849 toolStripProgressBar1.Minimum = 0;
1850 toolStripProgressBar1.Maximum = count;
1851  
1852 var progress = new Progress<DataGridViewRowProgress>(rowProgress =>
1853 {
1854 if (rowProgress is DataGridViewRowProgressFailure rowProgressFailure)
1855 {
1856 Log.Error(rowProgressFailure.Exception, "Unable to delete screenshot.");
1857  
1858 toolStripStatusLabel1.Text =
1859 $"Could not delete screenshot for {rowProgress.Row.Cells["NameColumn"].Value}...";
1860 toolStripProgressBar1.Value = rowProgress.Index + 1;
1861  
1862 statusStrip1.Update();
1863  
1864 return;
1865 }
1866  
1867 toolStripStatusLabel1.Text =
1868 $"Colored {rowProgress.Row.Cells["NameColumn"].Value}...";
1869 toolStripProgressBar1.Value = rowProgress.Index + 1;
1870  
1871 statusStrip1.Update();
1872 });
1873  
1874 await Task.Run(() => DeleteScreenshots(rows, progress, _cancellationToken), _cancellationToken);
1875  
3 office 1876 if (_cancellationToken.IsCancellationRequested)
1 office 1877 {
1878 toolStripProgressBar1.Value = toolStripProgressBar1.Maximum;
1879 toolStripStatusLabel1.Text = "Done.";
1880 }
1881 }
1882  
1883 private async Task DeleteScreenshots(IReadOnlyList<DataGridViewRow> rows,
1884 IProgress<DataGridViewRowProgress> progress,
1885 CancellationToken cancellationToken)
1886 {
1887 var count = rows.Count;
1888  
1889 for (var i = 0; i < count && !cancellationToken.IsCancellationRequested; ++i)
1890 {
1891 try
1892 {
11 office 1893 await _snapshotDatabase.DeleteScreenshotAsync((string)rows[i].Cells["HashColumn"].Value,
1 office 1894 cancellationToken);
1895  
1896 progress.Report(new DataGridViewRowProgressSuccess(rows[i], i));
1897 }
1898 catch (Exception exception)
1899 {
1900 progress.Report(new DataGridViewRowProgressFailure(rows[i], i, exception));
1901 }
1902 }
1903 }
1904  
1905 #endregion
23 office 1906  
1907 private void copyHashToolStripMenuItem_Click(object sender, EventArgs e)
1908 {
1909 var row = GetSelectedDataGridViewRows(dataGridView1).FirstOrDefault();
1910 if (row == null)
1911 {
1912 return;
1913 }
1914  
1915 Clipboard.SetText($"{row.Cells["HashColumn"].Value}");
1916  
1917 }
1 office 1918 }
1919 }