Horizon – Blame information for rev 22

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