Skip to content

Commit 3df6481

Browse files
authored
Merge pull request #1 from TechieGuy12/develop
Initial 1.0 version
2 parents 865c3f1 + d9e2e75 commit 3df6481

18 files changed

+2503
-1
lines changed

FileVerification.sln

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 16
4+
VisualStudioVersion = 16.0.30717.126
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileVerification", "FileVerification\FileVerification.csproj", "{9FEB60CA-C228-48A6-B15C-BDF1858C431D}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{9FEB60CA-C228-48A6-B15C-BDF1858C431D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{9FEB60CA-C228-48A6-B15C-BDF1858C431D}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{9FEB60CA-C228-48A6-B15C-BDF1858C431D}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{9FEB60CA-C228-48A6-B15C-BDF1858C431D}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {8C753EA0-EFD1-42E4-84AF-58303236AC04}
24+
EndGlobalSection
25+
EndGlobal

FileVerification/CheckSumFile.cs

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Collections.Concurrent;
4+
using System.IO;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
namespace TE.FileVerification
10+
{
11+
/// <summary>
12+
/// The fields used in the checksum file.
13+
/// </summary>
14+
public enum ChecksumFileLayout
15+
{
16+
/// <summary>
17+
/// The file name.
18+
/// </summary>
19+
NAME,
20+
/// <summary>
21+
/// The string representation of the hash algorithm.
22+
/// </summary>
23+
HASH_ALGORITHM,
24+
/// <summary>
25+
/// The hash of the file.
26+
/// </summary>
27+
HASH
28+
29+
}
30+
public class ChecksumFile
31+
{
32+
/// <summary>
33+
/// The default checksum file name.
34+
/// </summary>
35+
public const string DEFAULT_CHECKSUM_FILENAME = "__fv.txt";
36+
37+
/// <summary>
38+
/// Gets the directory where the checksum file is located.
39+
/// </summary>
40+
public string Directory { get; private set; }
41+
42+
/// <summary>
43+
/// Gets the full path of the checksum file.
44+
/// </summary>
45+
public string FullPath { get; private set; }
46+
47+
/// <summary>
48+
/// Gets the dictionary of checksums for the checksum file.
49+
/// </summary>
50+
public Dictionary<string, HashInfo> Checksums { get; private set; }
51+
52+
/// <summary>
53+
/// Gets the number of files in the checksum file.
54+
/// </summary>
55+
public int FileCount
56+
{
57+
get
58+
{
59+
return Checksums != null ? Checksums.Count : 0;
60+
}
61+
}
62+
63+
/// <summary>
64+
/// Creates an instance of the <see cref="ChecksumFile"/> class when
65+
/// provided with the full path to the checksum file.
66+
/// </summary>
67+
/// <param name="fullPath">
68+
/// Full path to the checksum file.
69+
/// </param>
70+
/// <exception cref="ArgumentNullException">
71+
/// The <paramref name="fullPath"/> parameter is null or empty.
72+
/// </exception>
73+
/// <exception cref="InvalidOperationException">
74+
/// The directory name to the checksum file could not be determined.
75+
/// </exception>
76+
public ChecksumFile(string fullPath)
77+
{
78+
if (string.IsNullOrWhiteSpace(fullPath))
79+
{
80+
throw new ArgumentNullException(nameof(fullPath));
81+
}
82+
83+
FullPath = fullPath;
84+
85+
string? directory = Path.GetDirectoryName(FullPath);
86+
if (string.IsNullOrWhiteSpace(directory))
87+
{
88+
throw new InvalidOperationException(
89+
"The directory name could not be determined from the full path to the checksum file.");
90+
}
91+
Directory = directory;
92+
93+
Checksums = new Dictionary<string, HashInfo>();
94+
95+
if (File.Exists(FullPath))
96+
{
97+
Read();
98+
}
99+
}
100+
101+
/// <summary>
102+
/// Reads the checksum file.
103+
/// </summary>
104+
public void Read()
105+
{
106+
if (!File.Exists(FullPath))
107+
{
108+
Logger.WriteLine($"The checksum file '{FullPath}' was not found.");
109+
return;
110+
}
111+
112+
if (string.IsNullOrWhiteSpace(Directory))
113+
{
114+
Logger.WriteLine("The directory value is null or empty.");
115+
return;
116+
}
117+
118+
try
119+
{
120+
using var reader = new StreamReader(FullPath);
121+
122+
while (!reader.EndOfStream)
123+
{
124+
string? line = reader.ReadLine();
125+
if (line == null)
126+
{
127+
continue;
128+
}
129+
130+
string[] values = line.Split(HashInfo.Separator);
131+
if (values.Length != Enum.GetNames(typeof(ChecksumFileLayout)).Length)
132+
{
133+
Logger.WriteLine($"WARNING: Record size incorrect (record will be created using the current file data). File: {FullPath}, Record: {line}.");
134+
continue;
135+
}
136+
137+
string fileName = values[(int)ChecksumFileLayout.NAME];
138+
HashInfo info =
139+
new HashInfo(
140+
fileName,
141+
values[(int)ChecksumFileLayout.HASH_ALGORITHM],
142+
values[(int)ChecksumFileLayout.HASH]);
143+
144+
// Get the full path to the file to use as the key to make
145+
// it unique so it can be used for searching
146+
Checksums.Add(Path.Combine(Directory, fileName), info);
147+
}
148+
}
149+
catch (UnauthorizedAccessException)
150+
{
151+
Logger.WriteLine($"ERROR: Not authorized to write to {FullPath}.");
152+
return;
153+
}
154+
catch (IOException ex)
155+
{
156+
Logger.WriteLine($"ERROR: Can't read the file. Reason: {ex.Message}");
157+
return;
158+
}
159+
}
160+
161+
/// <summary>
162+
/// Adds a checksum for a file.
163+
/// </summary>
164+
/// <param name="file">
165+
/// The full path, including the directory, of the file to add.
166+
/// </param>
167+
/// <param name="hashAlgorithm">
168+
/// The hash algorithm to use for files added to the checksum file.
169+
/// </param>
170+
public void Add(string file, HashAlgorithm hashAlgorithm)
171+
{
172+
if (string.IsNullOrWhiteSpace(file))
173+
{
174+
Logger.WriteLine("Could not add file to the checksum file because the path was not specified.");
175+
return;
176+
}
177+
178+
if (!File.Exists(file))
179+
{
180+
Logger.WriteLine($"Could not add file '{file}' to the checksum file because the file does not exist.");
181+
return;
182+
}
183+
184+
try
185+
{
186+
Checksums.Add(file, new HashInfo(file, hashAlgorithm));
187+
}
188+
catch(ArgumentNullException ex)
189+
{
190+
Logger.WriteLine($"Could not add file '{file}' to the checksum file. Reason: {ex.Message}");
191+
}
192+
}
193+
194+
/// <summary>
195+
/// Gets the checksum data for a file.
196+
/// </summary>
197+
/// <param name="fullPath">
198+
/// The full path to the file.
199+
/// </param>
200+
/// <returns>
201+
/// The data in a <see cref="HashInfo"/> object, or <c>null</c> if the
202+
/// data could not be retrieved.
203+
/// </returns>
204+
public HashInfo? GetFileData(string fullPath)
205+
{
206+
Checksums.TryGetValue(fullPath, out HashInfo? hashInfo);
207+
return hashInfo;
208+
}
209+
210+
/// <summary>
211+
/// Validates the hash information of a file matches what is stored in
212+
/// the checksum file.
213+
/// </summary>
214+
/// <param name="file">
215+
/// The full path, including the directory, of the file.
216+
/// </param>
217+
/// <param name="hashAlgorithm">
218+
/// The hash algorithm to use for files added to the checksum file.
219+
/// Existing files will use the hash algorithm stored in the checksum
220+
/// file.
221+
/// </param>
222+
public bool IsMatch(string file, HashAlgorithm hashAlgorithm)
223+
{
224+
if (string.IsNullOrWhiteSpace(file))
225+
{
226+
Logger.WriteLine("Could not validate file to the checksum file because the path was not specified.");
227+
return false;
228+
}
229+
230+
if (!File.Exists(file))
231+
{
232+
Logger.WriteLine($"Could not validate file '{file}' to the checksum file because the file does not exist.");
233+
return false;
234+
}
235+
236+
// Get the stored hash information for the file from the
237+
// checksum data
238+
HashInfo? hashInfo = GetFileData(file);
239+
240+
// Check if the file is in the checksum file
241+
if (hashInfo != null)
242+
{
243+
string? hash = HashInfo.GetFileHash(file, hashInfo.Algorithm);
244+
if (string.IsNullOrWhiteSpace(hash))
245+
{
246+
Logger.WriteLine($"Validating file '{file}' failed because the hash for the file could not be created using {hashInfo.Algorithm}.");
247+
return false;
248+
}
249+
250+
return hashInfo.IsHashEqual(hash);
251+
}
252+
else
253+
{
254+
// Add the file if it didn't exist in the checksum file and
255+
// then return true as it would match the hash that was just
256+
// generated
257+
Add(file, hashAlgorithm);
258+
return true;
259+
}
260+
}
261+
262+
/// <summary>
263+
/// Writes the checksum file.
264+
/// </summary>
265+
public void Write()
266+
{
267+
if (string.IsNullOrWhiteSpace(FullPath))
268+
{
269+
Logger.WriteLine("Could not write checksum file as the path of the file was not provided.");
270+
return;
271+
}
272+
273+
// Initialize the StringBuilder object that will contain the
274+
// contents of the verify file
275+
ConcurrentBag<string> info = new ConcurrentBag<string>();
276+
277+
// Loop through the file checksum information and append the file
278+
// information to the string builder so it can be written to the
279+
// checksum file
280+
Parallel.ForEach(Checksums, checksumInfo =>
281+
{
282+
info.Add(checksumInfo.Value.ToString() + Environment.NewLine);
283+
});
284+
285+
try
286+
{
287+
// Write the file hash information to the checksum file
288+
using StreamWriter sw = new StreamWriter(FullPath);
289+
sw.Write(string.Join("", info));
290+
}
291+
catch (DirectoryNotFoundException)
292+
{
293+
Logger.WriteLine($"Could not write the checksum file because the directory {Directory} was not found.");
294+
}
295+
catch (PathTooLongException)
296+
{
297+
Logger.WriteLine($"Could not write the checksum file because the path {FullPath} is too long.");
298+
}
299+
catch (UnauthorizedAccessException)
300+
{
301+
Logger.WriteLine($"Could not write the checksum file because the user is not authorized to write to {FullPath}.");
302+
}
303+
catch (Exception ex)
304+
when (ex is ArgumentException || ex is ArgumentNullException || ex is IOException || ex is System.Security.SecurityException)
305+
{
306+
Logger.WriteLine($"Could not write the checksum file. Reason: {ex.Message}");
307+
}
308+
}
309+
}
310+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace TE.FileVerification.Configuration
8+
{
9+
/// <summary>
10+
/// Configuration file interface.
11+
/// </summary>
12+
interface ISettingsFile
13+
{
14+
/// <summary>
15+
/// Reads the settings XML file.
16+
/// </summary>
17+
/// <param name="path">
18+
/// The path to the settings XML file.
19+
/// </param>
20+
/// <returns>
21+
/// A <see cref="Settings"/> object if the file was read successfully,
22+
/// otherwise <c>null</c>.
23+
/// </returns>
24+
public Settings? Read();
25+
}
26+
}

0 commit comments

Comments
 (0)