diff --git a/.editorconfig b/.editorconfig index 8b0b175..d793869 100644 --- a/.editorconfig +++ b/.editorconfig @@ -10,7 +10,7 @@ indent_style = space tab_width = 4 # Naming Conventions -dotnet_naming_style.pascal_case_style.capitalization = pascal_case +dotnet_naming_style.camel_case.capitalization = camel_case # New line preferences csharp_new_line_before_open_brace = none diff --git a/.gitignore b/.gitignore index e68f0aa..7576ac2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .vscode/ obj/ bin/ +Libraries/ diff --git a/README.md b/README.md index de78ccb..489a5da 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Checksums every file under the current directory Clone the project ```bash -git clone http://192.168.0.69:3000/ProfessionalUwU/chksum.git +git clone https://gitea.hopeless-cloud.xyz/ProfessionalUwU/chksum.git ``` Go to the project directory @@ -25,22 +25,22 @@ pacman -S dotnet-runtime dotnet-sdk Build project ```bash -dotnet build chksum.csproj +just build ``` Publish project ```bash -dotnet publish --configuration Release chksum.csproj +just publish ``` Go to the publish folder ```bash -cd bin/Release/net7.0/linux-x64/publish +cd src/Chksum/bin/Release/net7.0/linux-x64/publish ``` Run executable ```bash -./chksum -``` +./Chksum +``` \ No newline at end of file diff --git a/chksum.cs b/chksum.cs deleted file mode 100644 index 06af796..0000000 --- a/chksum.cs +++ /dev/null @@ -1,111 +0,0 @@ -// Go into folder -// Check if any file is in there -// If there is a file. Calculate md5sum > filename.md5 -// If there is no file. Repeat -public class Chksum { - - // int getDirectoryCount() { - // int folderCount = Directory.GetDirectories(Directory.GetCurrentDirectory()).Length; // Get folder count in current directory - // return folderCount; - // } - - private static int getFileCount() { - int fileCount = Directory.GetFiles(Directory.GetCurrentDirectory()).Length; // Get file count in current directory - return fileCount; - } - - // string getParentFolder() { - // string parentFolder = Directory.GetParent(Directory.GetCurrentDirectory()).ToString(); // Get parent folder of current directory - // return parentFolder; - // } - - private static string CalculateMD5(string filename) { - using (var md5 = System.Security.Cryptography.MD5.Create()) { - using (var stream = File.OpenRead(filename)) { - var hash = md5.ComputeHash(stream); - return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); - } - } - } - - public static void doTheThing() { - foreach (var directory in Directory.GetDirectories(Directory.GetCurrentDirectory())) { - Directory.SetCurrentDirectory(directory); // Set new root - if (getFileCount() >= 1) { - DirectoryInfo dir = new DirectoryInfo(Directory.GetCurrentDirectory()); - FileInfo[] files = dir.GetFiles(); - foreach (FileInfo file in files) { - string fileName = file.Name; - string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); - string checksumFile = Directory.GetCurrentDirectory() + "/" + fileNameWithoutExtension + ".md5"; - File.AppendAllText(checksumFile, CalculateMD5(fileName) + " " + fileName); - Console.WriteLine(checksumFile); - } - } - doTheThing(); - } - } - - private static int getTotalFileCount() { - int totalFileCount = Directory.GetFiles(Directory.GetCurrentDirectory(), "*", SearchOption.AllDirectories).Length; - return totalFileCount - 1; // Remove the program from the totalFileCount - } - - public static void countAllMd5Checksums() { - int totalMD5FileCount = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.md5", SearchOption.AllDirectories).Length; - Console.WriteLine("There are " + totalMD5FileCount + " md5 checksums"); - } - - public static void deleteAllMd5Checksums() { - foreach (var directory in Directory.GetDirectories(Directory.GetCurrentDirectory())) { - Directory.SetCurrentDirectory(directory); // Set new root - if (getFileCount() >= 1) { - DirectoryInfo dir = new DirectoryInfo(Directory.GetCurrentDirectory()); - FileInfo[] files = dir.GetFiles(); - foreach (FileInfo file in files) { - string fileName = file.Name; - string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); - string checksumFile = Directory.GetCurrentDirectory() + "/" + fileNameWithoutExtension + ".md5"; - File.Delete(checksumFile); - Console.WriteLine("Deleted " + checksumFile); - } - } - deleteAllMd5Checksums(); - } - } - - public static void compareChecksums() { - foreach (var directory in Directory.GetDirectories(Directory.GetCurrentDirectory())) { - Directory.SetCurrentDirectory(directory); // Set new root - if (getFileCount() >= 1) { - DirectoryInfo dir = new DirectoryInfo(Directory.GetCurrentDirectory()); - FileInfo[] files = dir.GetFiles(); - // files.ToList().ForEach(i => Console.WriteLine(i.ToString())); // Print all files in files array - foreach (FileInfo file in files) { - string fileName = file.Name; - string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); - string checksumFile = Directory.GetCurrentDirectory() + "/" + fileNameWithoutExtension + ".md5"; - string fileMd5Checksum = fileNameWithoutExtension + ".md5"; - if (File.Exists(fileMd5Checksum)) { - string newFileChecksum = CalculateMD5(fileName) + " " + fileName; - string existingFileChecksum = File.ReadAllText(fileMd5Checksum); - string newFileName = newFileChecksum.Substring(34); - string existingFileName = existingFileChecksum.Substring(34); - if (newFileChecksum.Equals(existingFileChecksum)) { - Console.WriteLine(newFileName + " and " + existingFileName + " are the same."); - } else { - Console.WriteLine(newFileName + " and " + existingFileName + " are not the same."); - Console.WriteLine("The checksum of " + newFileName + " is " + newFileChecksum); - Console.WriteLine("The checksum of the already exting file " + existingFileName + " is " + existingFileChecksum); - // TODO Tell the user to check which file is the correct one - } - } else { - File.AppendAllText(checksumFile, CalculateMD5(fileName) + " " + fileName); - Console.WriteLine("Calculated checksum for: " + checksumFile); - } - } - } - compareChecksums(); - } - } -} \ No newline at end of file diff --git a/justfile b/justfile index d9be799..6b0b79a 100644 --- a/justfile +++ b/justfile @@ -1,2 +1,31 @@ +default: + @just --list + +project_name := `printf '%s\n' "${PWD##*/}"` +uppercase_project_name := capitalize(project_name) + +setup: + @mkdir src + @dotnet new sln --name src/{{project_name}} + @dotnet new classlib -o src/{{uppercase_project_name}} + @dotnet new xunit -o src/{{uppercase_project_name}}.Tests + @dotnet sln add src/{{uppercase_project_name}}/{{uppercase_project_name}}.csproj + @dotnet sln add src/{{uppercase_project_name}}.Tests/{{uppercase_project_name}}.Tests.csproj + @dotnet add src/{{uppercase_project_name}}/{{uppercase_project_name}}.csproj reference src/{{uppercase_project_name}}.Tests/{{uppercase_project_name}}.Tests.csproj + +run: + @dotnet run + +build: + @dotnet build src/{{uppercase_project_name}}/{{uppercase_project_name}}.csproj + @dotnet build src/{{uppercase_project_name}}.Tests/{{uppercase_project_name}}.Tests.csproj + publish: - @dotnet publish --configuration Release chksum.csproj + @dotnet publish --configuration Release src/{{uppercase_project_name}}/{{uppercase_project_name}}.csproj + +format: + @dotnet format src/{{uppercase_project_name}} + @dotnet format src/{{uppercase_project_name}}.Tests + +test: build + @dotnet test src/{{uppercase_project_name}}.Tests diff --git a/src/Chksum.Tests/Chksum.Tests.csproj b/src/Chksum.Tests/Chksum.Tests.csproj new file mode 100644 index 0000000..10cd649 --- /dev/null +++ b/src/Chksum.Tests/Chksum.Tests.csproj @@ -0,0 +1,25 @@ + + + + net7.0 + enable + enable + + false + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + diff --git a/src/Chksum.Tests/Usings.cs b/src/Chksum.Tests/Usings.cs new file mode 100644 index 0000000..7fef4b0 --- /dev/null +++ b/src/Chksum.Tests/Usings.cs @@ -0,0 +1,2 @@ +global using Xunit; +global using FluentAssertions; \ No newline at end of file diff --git a/src/Chksum.Tests/doTheThingTest.cs b/src/Chksum.Tests/doTheThingTest.cs new file mode 100644 index 0000000..94ae38e --- /dev/null +++ b/src/Chksum.Tests/doTheThingTest.cs @@ -0,0 +1,8 @@ +namespace Chksum.Tests; + +public class doTheThingTest { + [Fact] + public void doTheThing_willNotThrowAnException() { + + } +} \ No newline at end of file diff --git a/chksum.csproj b/src/Chksum/Chksum.csproj similarity index 61% rename from chksum.csproj rename to src/Chksum/Chksum.csproj index b19ce67..fc84e00 100644 --- a/chksum.csproj +++ b/src/Chksum/Chksum.csproj @@ -1,5 +1,10 @@ + + + + + Exe net7.0 @@ -11,4 +16,8 @@ enable + + + + diff --git a/Program.cs b/src/Chksum/Program.cs similarity index 57% rename from Program.cs rename to src/Chksum/Program.cs index d0873c6..613c886 100644 --- a/Program.cs +++ b/src/Chksum/Program.cs @@ -1,4 +1,6 @@ -public class Program { +using Chksum.Utils; + +public class Program { static void Main(string[] args) { Console.ForegroundColor = ConsoleColor.Red; @@ -6,45 +8,39 @@ Console.WriteLine("Please specify an option."); PrintAvailableOptions(); return; - } else if (args.Length > 1) { + } else if (args.Length > 1 && args[0] != "compareDatabases") { Console.WriteLine("Too many options."); return; } + ChksumUtils utils = new ChksumUtils(); + + utils.getBaseDir(); + + utils.ExtractEmbeddedLibrary(); + Console.ForegroundColor = ConsoleColor.Green; switch (args[0]) { case "checksum": Console.WriteLine("Starting the checksum process."); Console.ResetColor(); - Chksum.doTheThing(); + utils.doTheThing(); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Checksum process finished"); break; - case "countmd5": - Console.WriteLine("Counting md5 checksum files."); + case "compareDatabases": Console.ResetColor(); - Chksum.countAllMd5Checksums(); - - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine("Finished counting all md5 checksum files."); + utils.compareDatabases(args[1]); break; - case "deletemd5": - Console.WriteLine("Deleting all md5 checksum files."); - Console.ResetColor(); - - Chksum.deleteAllMd5Checksums(); - - Console.ForegroundColor = ConsoleColor.Green; - Console.WriteLine("Deleted all md5 checksum files."); + case "createDB": + utils.initializeDB(); break; - case "compareChecksums": - Console.WriteLine("Comparing all md5 checksum files. If there is none, creating one."); + case "checkIfFileWasDeleted": Console.ResetColor(); - - Chksum.compareChecksums(); + utils.checkIfFileWasDeleted(); break; case "help": PrintAvailableOptions(); @@ -55,20 +51,21 @@ PrintAvailableOptions(); break; } + + utils.cleanup(); } static void PrintAvailableOptions() { String[] options = { "checksum", - "countmd5", - "deletemd5", "compareChecksums", + "createDB", + "checkIfFileWasDeleted", "help" }; Console.ResetColor(); - Console.WriteLine("usage: chksum [option]"); - Console.WriteLine("Here is a list of all available options:"); + Console.WriteLine("usage: chksum [option] \nHere is a list of all available options:"); foreach (String option in options) { Console.WriteLine("\t" + option); } diff --git a/src/Chksum/chksum.cs b/src/Chksum/chksum.cs new file mode 100644 index 0000000..8f0f1a6 --- /dev/null +++ b/src/Chksum/chksum.cs @@ -0,0 +1,268 @@ +using System.Reflection; +using Microsoft.Data.Sqlite; +namespace Chksum.Utils; +public class ChksumUtils { + + private int getFileCount() { + int fileCount = Directory.GetFiles(Directory.GetCurrentDirectory()).Length; // Get file count in current directory + return fileCount; + } + + public string DatabaseRoot { get; set; } = string.Empty; + public void getBaseDir() { + DatabaseRoot = AppDomain.CurrentDomain.BaseDirectory; + } + + public string libraryPath { get; set; } = string.Empty; + public void ExtractEmbeddedLibrary() { + libraryPath = Path.Combine(DatabaseRoot, "libe_sqlite3.so"); + + using (Stream? resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Chksum.Libraries.libe_sqlite3.so")) { + if (resourceStream != null) { + byte[] buffer = new byte[resourceStream.Length]; + resourceStream.Read(buffer, 0, buffer.Length); + File.WriteAllBytes(libraryPath, buffer); + } else { + throw new Exception(libraryPath + " could not be loaded"); + } + } + } + + public void initializeDB() { + if (File.Exists("chksum.db")) { + return; + } + + using (var connection = new SqliteConnection("Data Source=chksum.db")) { + connection.Open(); + + var command = connection.CreateCommand(); + command.CommandText = + @" + CREATE TABLE file ( + filehash TEXT NOT NULL PRIMARY KEY, + filename TEXT NOT NULL, + pathtofile TEXT NOT NULL, + artist TEXT, + playbacklength INTEGER + ); + "; + command.ExecuteNonQuery(); + } + } + + public void cleanDB() { + using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db")) { + var command = connection.CreateCommand(); + command.CommandText = + @" + vacuum; + "; + command.ExecuteNonQuery(); + } + } + + private string CalculateMD5(string filename) { + using (var md5 = System.Security.Cryptography.MD5.Create()) { + using (var stream = File.OpenRead(filename)) { + var hash = md5.ComputeHash(stream); + return BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant(); + } + } + } + + public void doTheThing() { + foreach (var directory in Directory.GetDirectories(Directory.GetCurrentDirectory())) + using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadWrite")) { + Directory.SetCurrentDirectory(directory); // Set new root + if (getFileCount() >= 1) { + DirectoryInfo dir = new DirectoryInfo(Directory.GetCurrentDirectory()); + FileInfo[] files = dir.GetFiles(); + foreach (FileInfo file in files) { + string fileName = file.Name; + string absolutePathToFile = Path.GetFullPath(fileName); + string pathToFile = Path.GetRelativePath(DatabaseRoot, absolutePathToFile); + string fileHash = CalculateMD5(fileName); + + if (checkIfFileMovedAndUpdatePathToFile(fileHash, fileName, pathToFile) == false && checkIfFileAlreadyExistsInDatabase(fileHash, fileName) == false) { + connection.Open(); + + var command = connection.CreateCommand(); + command.CommandText = + @" + INSERT INTO file (filehash, filename, pathtofile) + VALUES ($filehash, $filename, $pathtofile) + "; + command.Parameters.AddWithValue("$filehash", fileHash); + command.Parameters.AddWithValue("$filename", fileName); + command.Parameters.AddWithValue("$pathtofile", pathToFile); + command.ExecuteNonQuery(); + } + } + } + doTheThing(); + } + } + + private bool checkIfFileAlreadyExistsInDatabase(string fileHash, string pathToFile) { + string filehash = string.Empty; + string pathtofile = string.Empty; + bool doesExist = false; + + using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadOnly")) { + connection.Open(); + + var command = connection.CreateCommand(); + command.CommandText = + @" + SELECT filehash, pathtofile FROM file WHERE filehash = $filehash + "; + command.Parameters.AddWithValue("$filehash", fileHash); + + using (var reader = command.ExecuteReader()) { + while (reader.Read()) { + filehash = reader.GetString(0); + pathtofile = reader.GetString(1); + } + } + } + + if (fileHash == filehash) { + doesExist = true; + } + return doesExist; + } + + private bool checkIfFileMovedAndUpdatePathToFile(string fileHash, string fileName, string pathToFile) { + string pathtofile = string.Empty; + bool wasMoved = false; + + using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadWrite")) { + connection.Open(); + + var command = connection.CreateCommand(); + command.CommandText = + @" + SELECT pathtofile FROM file WHERE filehash = $filehash + "; + command.Parameters.AddWithValue("$filehash", fileHash); + + using (var reader = command.ExecuteReader()) { + while (reader.Read()) { + pathtofile = reader.GetString(0); + } + } + + if (pathToFile != pathtofile && pathtofile != "") { + var command2 = connection.CreateCommand(); + command2.CommandText = + @" + UPDATE file + SET pathtofile = $newpathtofile + WHERE filehash = $filehash + "; + command2.Parameters.AddWithValue("$newpathtofile", pathToFile); + command2.Parameters.AddWithValue("$filehash", fileHash); + command2.ExecuteNonQuery(); + + Console.WriteLine("File moved:"); + Console.WriteLine($"\tfrom\t{pathToFile}"); + Console.WriteLine($"\tto \t{pathtofile}\n"); + wasMoved = true; + } + return wasMoved; + } + } + + public void checkIfFileWasDeleted() { + string pathToFile = string.Empty; + + using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadWrite")) { + connection.Open(); + + var selectCommand = connection.CreateCommand(); + selectCommand.CommandText = + @" + Select pathtofile FROM file + "; + + using (var reader = selectCommand.ExecuteReader()) { + while (reader.Read()) { + pathToFile = reader.GetString(0); + + if (!File.Exists(pathToFile)) { + var deleteCommand = connection.CreateCommand(); + deleteCommand.CommandText = + @" + DELETE FROM file + WHERE pathtofile = $pathtofile + "; + deleteCommand.Parameters.AddWithValue("$pathtofile", pathToFile); + deleteCommand.ExecuteNonQuery(); + + Console.WriteLine("File deleted:"); + Console.WriteLine($"\t{pathToFile}\n"); + } + } + } + } + } + + private List getFilehashesFromDatabase(string connectionString) { + List filehashesFromDatabase = new List(); + + using (var connection = new SqliteConnection(connectionString)) { + string filehash = string.Empty; + + connection.Open(); + + var selectCommand = connection.CreateCommand(); + selectCommand.CommandText = + @" + Select filehash FROM file + "; + + using (var reader = selectCommand.ExecuteReader()) { + while (reader.Read()) { + filehash = reader.GetString(0); + filehashesFromDatabase.Add(filehash); + } + } + } + + return filehashesFromDatabase; + } + + public void compareDatabases(string filePathToOtherDatabase) { + List filesThatDoNotExistsInTheRemote = getFilehashesFromDatabase("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadOnly").Except(getFilehashesFromDatabase("Data Source=" + filePathToOtherDatabase + ";Mode=ReadOnly")).ToList(); + //List filesThatDoNotExistsInTheOrigin = filehashesOfRemoteDatabase.Except(filehashesOfOriginDatabase).ToList(); + + foreach (string file in filesThatDoNotExistsInTheRemote) { + using (var connection = new SqliteConnection("Data Source=" + DatabaseRoot + "chksum.db;Mode=ReadOnly")) { + string filename = string.Empty; + + connection.Open(); + + var selectCommand = connection.CreateCommand(); + selectCommand.CommandText = + @" + Select filename FROM file WHERE filehash = $filehash + "; + selectCommand.Parameters.AddWithValue("$filehash", file); + + using (var reader = selectCommand.ExecuteReader()) { + while (reader.Read()) { + filename = reader.GetString(0); + + Console.WriteLine("File not found in remote:"); + Console.WriteLine($"\t{filename}\n"); + } + } + } + } + } + + public void cleanup() { + File.Delete(libraryPath); + } +} \ No newline at end of file diff --git a/debugchksum.cs b/src/Chksum/debugchksum.cs similarity index 100% rename from debugchksum.cs rename to src/Chksum/debugchksum.cs diff --git a/src/chksum.sln b/src/chksum.sln new file mode 100644 index 0000000..b5f119a --- /dev/null +++ b/src/chksum.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chksum", "Chksum\Chksum.csproj", "{BBC56294-03CF-42E0-A838-75AF41EEE32B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chksum.Tests", "Chksum.Tests\Chksum.Tests.csproj", "{239727BC-7124-4985-A6F3-2700295AA06F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BBC56294-03CF-42E0-A838-75AF41EEE32B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BBC56294-03CF-42E0-A838-75AF41EEE32B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BBC56294-03CF-42E0-A838-75AF41EEE32B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BBC56294-03CF-42E0-A838-75AF41EEE32B}.Release|Any CPU.Build.0 = Release|Any CPU + {239727BC-7124-4985-A6F3-2700295AA06F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {239727BC-7124-4985-A6F3-2700295AA06F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {239727BC-7124-4985-A6F3-2700295AA06F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {239727BC-7124-4985-A6F3-2700295AA06F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal