From 2d4dc14f9331338595a74c6e1abd9e2fcbbea7a7 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 18 Nov 2021 15:27:20 -0500 Subject: [PATCH 1/9] Fix ShouldOnlyShowUsersLevels test --- ProjectLighthouse.Tests/Tests/SlotTests.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ProjectLighthouse.Tests/Tests/SlotTests.cs b/ProjectLighthouse.Tests/Tests/SlotTests.cs index 65abac0f..52f4d690 100644 --- a/ProjectLighthouse.Tests/Tests/SlotTests.cs +++ b/ProjectLighthouse.Tests/Tests/SlotTests.cs @@ -17,13 +17,18 @@ namespace LBPUnion.ProjectLighthouse.Tests User userA = await database.CreateUser("unitTestUser0"); User userB = await database.CreateUser("unitTestUser1"); - Location l = new(); + Location l = new() + { + X = 0, + Y = 0, + }; database.Locations.Add(l); await database.SaveChangesAsync(); Slot slotA = new() { Creator = userA, + CreatorId = userA.UserId, Name = "slotA", Location = l, LocationId = l.Id, @@ -33,6 +38,7 @@ namespace LBPUnion.ProjectLighthouse.Tests Slot slotB = new() { Creator = userB, + CreatorId = userB.UserId, Name = "slotB", Location = l, LocationId = l.Id, @@ -49,8 +55,10 @@ namespace LBPUnion.ProjectLighthouse.Tests LoginResult loginResult = await this.Authenticate(); - HttpResponseMessage respMessageA = await this.AuthenticatedRequest("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser0", loginResult.AuthTicket); - HttpResponseMessage respMessageB = await this.AuthenticatedRequest("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser1", loginResult.AuthTicket); + HttpResponseMessage respMessageA = await this.AuthenticatedRequest + ("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser0&pageStart=1&pageSize=1", loginResult.AuthTicket); + HttpResponseMessage respMessageB = await this.AuthenticatedRequest + ("LITTLEBIGPLANETPS3_XML/slots/by?u=unitTestUser1&pageStart=1&pageSize=1", loginResult.AuthTicket); Assert.True(respMessageA.IsSuccessStatusCode); Assert.True(respMessageB.IsSuccessStatusCode); From dc429f2c8adfbc0f1b83307242fd2ff723d3e8b0 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 18 Nov 2021 15:31:37 -0500 Subject: [PATCH 2/9] Use im-open/process-dotnet-test-results@v2.0.1 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ee41701..5477e569 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,13 +60,13 @@ jobs: - name: Process Test Results (Control) if: ${{ matrix.os.prettyName == 'Linux' }} - uses: im-open/process-dotnet-test-results@v2.0.0 + uses: im-open/process-dotnet-test-results@v2.0.1 with: github-token: ${{ secrets.GITHUB_TOKEN }} - name: Process Test Results if: ${{ matrix.os.prettyName != 'Linux' }} - uses: im-open/process-dotnet-test-results@v2.0.0 + uses: im-open/process-dotnet-test-results@v2.0.1 with: github-token: ${{ secrets.GITHUB_TOKEN }} create-status-check: false From b0b35d6ae89f849f6557bf2d559a4f65cef1da0b Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 18 Nov 2021 18:27:29 -0500 Subject: [PATCH 3/9] Add section on running tests to readme --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e45b3290..f3ff44b4 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Finally, take a break. Chances are that took a while. ## Contributing Tips -### Database +### Database migrations Some modifications may require updates to the database schema. You can automatically create a migration file by: @@ -72,6 +72,12 @@ Some modifications may require updates to the database schema. You can automatic you're doing. 4. Running `dotnet ef migrations add --project ProjectLighthouse`. +### Running tests + +You can run tests either through your IDE or by running `dotnet tests`. + +Keep in mind while running database tests you need to have `LIGHTHOUSE_DB_CONNECTION_STRING` set. + ## Compatibility across games and platforms | Game | Console (PS3/Vita) | Emulator (RPCS3) | Next-Gen (PS4/PS5) | From 5d40c42c5730439b9d3718326c7173200e8481eb Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 18 Nov 2021 18:33:39 -0500 Subject: [PATCH 4/9] Add string.ToSafeXml() May resolve #61 --- .../Helpers/Extensions/StringExtensions.cs | 2 ++ .../Serialization/LbpSerializer.cs | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs b/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs index 72ef05af..0d4d4df1 100644 --- a/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs +++ b/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs @@ -6,5 +6,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers.Extensions public static class StringExtensions { public static string ToFileName(this string text) => Path.GetInvalidFileNameChars().Aggregate(text, (current, c) => current.Replace(c.ToString(), "")); + + public static string ToSafeXml(this string text) => text.Replace("<", "<").Replace(">", ">"); } } \ No newline at end of file diff --git a/ProjectLighthouse/Serialization/LbpSerializer.cs b/ProjectLighthouse/Serialization/LbpSerializer.cs index cfa64535..73a0c7f1 100644 --- a/ProjectLighthouse/Serialization/LbpSerializer.cs +++ b/ProjectLighthouse/Serialization/LbpSerializer.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using LBPUnion.ProjectLighthouse.Helpers.Extensions; namespace LBPUnion.ProjectLighthouse.Serialization { @@ -12,22 +13,27 @@ namespace LBPUnion.ProjectLighthouse.Serialization [SuppressMessage("ReSharper", "UnusedMember.Global")] public static class LbpSerializer { + // IMPORTANT: All functions using values must call .ToSafeXml(); public static string BlankElement(string key) => $"<{key}>"; - public static string StringElement(KeyValuePair pair) => $"<{pair.Key}>{pair.Value}"; + public static string StringElement(KeyValuePair pair) => $"<{pair.Key}>{pair.Value.ToString().ToSafeXml()}"; - public static string StringElement(string key, bool value) => $"<{key}>{value.ToString().ToLower()}"; + public static string StringElement(string key, bool value) => $"<{key}>{value.ToString().ToLower().ToSafeXml()}"; - public static string StringElement(string key, object value) => $"<{key}>{value}"; + public static string StringElement(string key, object value) => $"<{key}>{value.ToString().ToSafeXml()}"; public static string TaggedStringElement (KeyValuePair pair, KeyValuePair tagPair) - => $"<{pair.Key} {tagPair.Key}=\"{tagPair.Value}\">{pair.Value}"; + => $"<{pair.Key} {tagPair.Key}=\"{tagPair.Value.ToString().ToSafeXml()}\">{pair.Value.ToString().ToSafeXml()}"; - public static string TaggedStringElement(string key, object value, string tagKey, object tagValue) => $"<{key} {tagKey}=\"{tagValue}\">{value}"; + public static string TaggedStringElement + (string key, object value, string tagKey, object tagValue) + => $"<{key} {tagKey}=\"{tagValue.ToString().ToSafeXml()}\">{value.ToString().ToSafeXml()}"; public static string TaggedStringElement(string key, object value, Dictionary attrKeyValuePairs) - => $"<{key} " + attrKeyValuePairs.Aggregate(string.Empty, (current, kvp) => current + $"{kvp.Key}=\"{kvp.Value}\" ") + $">{value}"; + => $"<{key} " + + attrKeyValuePairs.Aggregate(string.Empty, (current, kvp) => current + $"{kvp.Key}=\"{kvp.Value.ToString().ToSafeXml()}\" ") + + $">{value.ToString().ToSafeXml()}"; public static string Elements (params KeyValuePair[] pairs) From eb0d91914c707c465d7258a23aab159c7066a844 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 18 Nov 2021 18:38:27 -0500 Subject: [PATCH 5/9] Revert "Add string.ToSafeXml()" This reverts commit 5d40c42c5730439b9d3718326c7173200e8481eb. --- .../Helpers/Extensions/StringExtensions.cs | 2 -- .../Serialization/LbpSerializer.cs | 18 ++++++------------ 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs b/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs index 0d4d4df1..72ef05af 100644 --- a/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs +++ b/ProjectLighthouse/Helpers/Extensions/StringExtensions.cs @@ -6,7 +6,5 @@ namespace LBPUnion.ProjectLighthouse.Helpers.Extensions public static class StringExtensions { public static string ToFileName(this string text) => Path.GetInvalidFileNameChars().Aggregate(text, (current, c) => current.Replace(c.ToString(), "")); - - public static string ToSafeXml(this string text) => text.Replace("<", "<").Replace(">", ">"); } } \ No newline at end of file diff --git a/ProjectLighthouse/Serialization/LbpSerializer.cs b/ProjectLighthouse/Serialization/LbpSerializer.cs index 73a0c7f1..cfa64535 100644 --- a/ProjectLighthouse/Serialization/LbpSerializer.cs +++ b/ProjectLighthouse/Serialization/LbpSerializer.cs @@ -1,7 +1,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -using LBPUnion.ProjectLighthouse.Helpers.Extensions; namespace LBPUnion.ProjectLighthouse.Serialization { @@ -13,27 +12,22 @@ namespace LBPUnion.ProjectLighthouse.Serialization [SuppressMessage("ReSharper", "UnusedMember.Global")] public static class LbpSerializer { - // IMPORTANT: All functions using values must call .ToSafeXml(); public static string BlankElement(string key) => $"<{key}>"; - public static string StringElement(KeyValuePair pair) => $"<{pair.Key}>{pair.Value.ToString().ToSafeXml()}"; + public static string StringElement(KeyValuePair pair) => $"<{pair.Key}>{pair.Value}"; - public static string StringElement(string key, bool value) => $"<{key}>{value.ToString().ToLower().ToSafeXml()}"; + public static string StringElement(string key, bool value) => $"<{key}>{value.ToString().ToLower()}"; - public static string StringElement(string key, object value) => $"<{key}>{value.ToString().ToSafeXml()}"; + public static string StringElement(string key, object value) => $"<{key}>{value}"; public static string TaggedStringElement (KeyValuePair pair, KeyValuePair tagPair) - => $"<{pair.Key} {tagPair.Key}=\"{tagPair.Value.ToString().ToSafeXml()}\">{pair.Value.ToString().ToSafeXml()}"; + => $"<{pair.Key} {tagPair.Key}=\"{tagPair.Value}\">{pair.Value}"; - public static string TaggedStringElement - (string key, object value, string tagKey, object tagValue) - => $"<{key} {tagKey}=\"{tagValue.ToString().ToSafeXml()}\">{value.ToString().ToSafeXml()}"; + public static string TaggedStringElement(string key, object value, string tagKey, object tagValue) => $"<{key} {tagKey}=\"{tagValue}\">{value}"; public static string TaggedStringElement(string key, object value, Dictionary attrKeyValuePairs) - => $"<{key} " + - attrKeyValuePairs.Aggregate(string.Empty, (current, kvp) => current + $"{kvp.Key}=\"{kvp.Value.ToString().ToSafeXml()}\" ") + - $">{value.ToString().ToSafeXml()}"; + => $"<{key} " + attrKeyValuePairs.Aggregate(string.Empty, (current, kvp) => current + $"{kvp.Key}=\"{kvp.Value}\" ") + $">{value}"; public static string Elements (params KeyValuePair[] pairs) From 616649e96a7567bd1918700ae8cd589fb5255fc7 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 18 Nov 2021 18:51:18 -0500 Subject: [PATCH 6/9] Implement sizeOfResources in Slot Closes #45 --- ProjectLighthouse/Helpers/FileHelper.cs | 2 ++ ProjectLighthouse/Types/Levels/Slot.cs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ProjectLighthouse/Helpers/FileHelper.cs b/ProjectLighthouse/Helpers/FileHelper.cs index f519effa..92084b7c 100644 --- a/ProjectLighthouse/Helpers/FileHelper.cs +++ b/ProjectLighthouse/Helpers/FileHelper.cs @@ -78,6 +78,8 @@ namespace LBPUnion.ProjectLighthouse.Helpers public static bool ResourceExists(string hash) => File.Exists(GetResourcePath(hash)); + public static int ResourceSize(string hash) => (int)new FileInfo(GetResourcePath(hash)).Length; + public static void EnsureDirectoryCreated(string path) { if (!Directory.Exists(path)) Directory.CreateDirectory(path ?? throw new ArgumentNullException(nameof(path))); diff --git a/ProjectLighthouse/Types/Levels/Slot.cs b/ProjectLighthouse/Types/Levels/Slot.cs index f21c7240..ff0c580e 100644 --- a/ProjectLighthouse/Types/Levels/Slot.cs +++ b/ProjectLighthouse/Types/Levels/Slot.cs @@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Xml.Serialization; +using LBPUnion.ProjectLighthouse.Helpers; using LBPUnion.ProjectLighthouse.Serialization; using LBPUnion.ProjectLighthouse.Types.Profiles; @@ -195,7 +196,8 @@ namespace LBPUnion.ProjectLighthouse.Types.Levels public string SerializeResources() { - return this.Resources.Aggregate("", (current, resource) => current + LbpSerializer.StringElement("resource", resource)); + return this.Resources.Aggregate("", (current, resource) => current + LbpSerializer.StringElement("resource", resource)) + + LbpSerializer.StringElement("sizeOfResources", this.Resources.Sum(FileHelper.ResourceSize)); } public string Serialize(RatedLevel? yourRatingStats = null, VisitedLevel? yourVisitedStats = null) From 4542bd331363dd725e4309633135c22942c16d6a Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 18 Nov 2021 18:55:53 -0500 Subject: [PATCH 7/9] Add Clear Queued Levels endpoint Closes #43 --- ProjectLighthouse/Controllers/ListController.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ProjectLighthouse/Controllers/ListController.cs b/ProjectLighthouse/Controllers/ListController.cs index 4e3014f8..f4139dc1 100644 --- a/ProjectLighthouse/Controllers/ListController.cs +++ b/ProjectLighthouse/Controllers/ListController.cs @@ -90,6 +90,19 @@ namespace LBPUnion.ProjectLighthouse.Controllers return this.Ok(); } + [HttpPost("lolcatftw/clear")] + public async Task ClearQueuedLevels() + { + User? user = await this.database.UserFromRequest(this.Request); + if (user == null) return this.StatusCode(403, ""); + + this.database.QueuedLevels.RemoveRange(this.database.QueuedLevels.Where(q => q.UserId == user.UserId)); + + await this.database.SaveChangesAsync(); + + return this.Ok(); + } + #endregion #region Hearted Levels From 09c72eb9c2c7d85485305aa91af5bf23e46944cc Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 18 Nov 2021 19:30:37 -0500 Subject: [PATCH 8/9] Implement file hashing on upload Closes #25 --- ProjectLighthouse.Tests/LighthouseTest.cs | 9 +++++++++ ProjectLighthouse.Tests/Tests/UploadTests.cs | 10 +++++----- ProjectLighthouse/Controllers/ResourcesController.cs | 7 ++++++- ProjectLighthouse/Helpers/HashHelper.cs | 12 ++++++------ 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/ProjectLighthouse.Tests/LighthouseTest.cs b/ProjectLighthouse.Tests/LighthouseTest.cs index df207d74..3efb3684 100644 --- a/ProjectLighthouse.Tests/LighthouseTest.cs +++ b/ProjectLighthouse.Tests/LighthouseTest.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Net.Http; +using System.Text; using System.Threading.Tasks; using System.Xml.Serialization; using LBPUnion.ProjectLighthouse.Helpers; @@ -55,6 +56,14 @@ namespace LBPUnion.ProjectLighthouse.Tests return this.Client.SendAsync(requestMessage); } + public async Task UploadFileEndpointRequest(string filePath) + { + byte[] bytes = Encoding.UTF8.GetBytes(await File.ReadAllTextAsync(filePath)); + string hash = HashHelper.Sha1Hash(bytes); + + return await this.Client.PostAsync($"/LITTLEBIGPLANETPS3_XML/upload/{hash}", new ByteArrayContent(bytes)); + } + public async Task UploadFileRequest(string endpoint, string filePath) => await this.Client.PostAsync(endpoint, new StringContent(await File.ReadAllTextAsync(filePath))); diff --git a/ProjectLighthouse.Tests/Tests/UploadTests.cs b/ProjectLighthouse.Tests/Tests/UploadTests.cs index 41612256..fb73be01 100644 --- a/ProjectLighthouse.Tests/Tests/UploadTests.cs +++ b/ProjectLighthouse.Tests/Tests/UploadTests.cs @@ -17,35 +17,35 @@ namespace LBPUnion.ProjectLighthouse.Tests [Fact] public async Task ShouldNotAcceptScript() { - HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/scriptTest", "ExampleFiles/TestScript.ff"); + HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestScript.ff"); Assert.False(response.IsSuccessStatusCode); } [Fact] public async Task ShouldNotAcceptFarc() { - HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/farcTest", "ExampleFiles/TestFarc.farc"); + HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestFarc.farc"); Assert.False(response.IsSuccessStatusCode); } [Fact] public async Task ShouldNotAcceptGarbage() { - HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/garbageTest", "ExampleFiles/TestGarbage.bin"); + HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestGarbage.bin"); Assert.False(response.IsSuccessStatusCode); } [Fact] public async Task ShouldAcceptTexture() { - HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/textureTest", "ExampleFiles/TestTexture.tex"); + HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestTexture.tex"); Assert.True(response.IsSuccessStatusCode); } [Fact] public async Task ShouldAcceptLevel() { - HttpResponseMessage response = await this.UploadFileRequest("/LITTLEBIGPLANETPS3_XML/upload/levelTest", "ExampleFiles/TestLevel.lvl"); + HttpResponseMessage response = await this.UploadFileEndpointRequest("ExampleFiles/TestLevel.lvl"); Assert.True(response.IsSuccessStatusCode); } } diff --git a/ProjectLighthouse/Controllers/ResourcesController.cs b/ProjectLighthouse/Controllers/ResourcesController.cs index 3680bbfd..d1a438db 100644 --- a/ProjectLighthouse/Controllers/ResourcesController.cs +++ b/ProjectLighthouse/Controllers/ResourcesController.cs @@ -54,7 +54,6 @@ namespace LBPUnion.ProjectLighthouse.Controllers [AllowSynchronousIo] public async Task UploadResource(string hash) { - string assetsDirectory = FileHelper.ResourcePath; string path = FileHelper.GetResourcePath(hash); @@ -70,6 +69,12 @@ namespace LBPUnion.ProjectLighthouse.Controllers return this.UnprocessableEntity(); } + if (HashHelper.Sha1Hash(file.Data) != hash) + { + Logger.Log($"File hash does not match the uploaded file! (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance); + return this.Conflict(); + } + Logger.Log($"File is OK! (hash: {hash}, type: {file.FileType})", LoggerLevelResources.Instance); await IOFile.WriteAllBytesAsync(path, file.Data); return this.Ok(); diff --git a/ProjectLighthouse/Helpers/HashHelper.cs b/ProjectLighthouse/Helpers/HashHelper.cs index ffda6e17..838a22a4 100644 --- a/ProjectLighthouse/Helpers/HashHelper.cs +++ b/ProjectLighthouse/Helpers/HashHelper.cs @@ -11,7 +11,7 @@ namespace LBPUnion.ProjectLighthouse.Helpers [SuppressMessage("ReSharper", "UnusedMember.Global")] public static class HashHelper { -// private static readonly SHA1 sha1 = SHA1.Create(); + private static readonly SHA1 sha1 = SHA1.Create(); private static readonly SHA256 sha256 = SHA256.Create(); private static readonly Random random = new(); @@ -67,11 +67,11 @@ namespace LBPUnion.ProjectLighthouse.Helpers public static string Sha256Hash(string str) => Sha256Hash(Encoding.UTF8.GetBytes(str)); - public static string Sha256Hash(byte[] bytes) - { - byte[] hash = sha256.ComputeHash(bytes); - return Encoding.UTF8.GetString(hash, 0, hash.Length); - } + public static string Sha256Hash(byte[] bytes) => BitConverter.ToString(sha256.ComputeHash(bytes)).Replace("-", ""); + + public static string Sha1Hash(string str) => Sha1Hash(Encoding.UTF8.GetBytes(str)); + + public static string Sha1Hash(byte[] bytes) => BitConverter.ToString(sha1.ComputeHash(bytes)).Replace("-", ""); public static string BCryptHash(string str) => BCrypt.Net.BCrypt.HashPassword(str); From 249b1d582c4a34ba99edbae39baf64dd9214d0d1 Mon Sep 17 00:00:00 2001 From: jvyden Date: Thu, 18 Nov 2021 19:34:28 -0500 Subject: [PATCH 9/9] Fix ShouldOnlyShowUsersLevels test --- ProjectLighthouse/Helpers/FileHelper.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/ProjectLighthouse/Helpers/FileHelper.cs b/ProjectLighthouse/Helpers/FileHelper.cs index 92084b7c..f85b3113 100644 --- a/ProjectLighthouse/Helpers/FileHelper.cs +++ b/ProjectLighthouse/Helpers/FileHelper.cs @@ -78,7 +78,17 @@ namespace LBPUnion.ProjectLighthouse.Helpers public static bool ResourceExists(string hash) => File.Exists(GetResourcePath(hash)); - public static int ResourceSize(string hash) => (int)new FileInfo(GetResourcePath(hash)).Length; + public static int ResourceSize(string hash) + { + try + { + return (int)new FileInfo(GetResourcePath(hash)).Length; + } + catch + { + return 0; + } + } public static void EnsureDirectoryCreated(string path) {