diff --git a/LibGit2Sharp.Tests/EncodingFixture.cs b/LibGit2Sharp.Tests/EncodingFixture.cs new file mode 100644 index 000000000..2a770ba22 --- /dev/null +++ b/LibGit2Sharp.Tests/EncodingFixture.cs @@ -0,0 +1,27 @@ +using LibGit2Sharp.Tests.TestHelpers; +using System; +using System.IO; +using Xunit; +using Xunit.Extensions; + +namespace LibGit2Sharp.Tests +{ + public class EncodingFixture : BaseFixture + { + [Theory] + //[InlineData("7bc349e5efdb52884cf47d860852e4912b12c244", "MãcRömãñ", 1000)] + [InlineData("782dced4c3930bc3c291b24c115ba9a231049d80", "Lãtïñ Òñê", 1250)] + //[InlineData("29b28e7d3e84aef886c0e00ff9de1a8cc1fda352", "Cõdêpâgê850", 850)] + public void CorruptAuthorNameEncoding(string commitId, string authorName, int commitEncoding) + { + string path = CloneEncodingTestRepo(); + + using (var repo = new Repository(path)) + { + Commit commit = repo.Lookup(commitId); + Assert.Equal(authorName, commit.Author.Name); + } + } + + } +} diff --git a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj index f9dc8766f..2e6a7ad7b 100644 --- a/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj +++ b/LibGit2Sharp.Tests/LibGit2Sharp.Tests.csproj @@ -116,6 +116,7 @@ + diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/HEAD b/LibGit2Sharp.Tests/Resources/commitencodings.git/HEAD new file mode 100644 index 000000000..cb089cd89 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/commitencodings.git/HEAD @@ -0,0 +1 @@ +ref: refs/heads/master diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/config b/LibGit2Sharp.Tests/Resources/commitencodings.git/config new file mode 100644 index 000000000..ba3781a98 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/commitencodings.git/config @@ -0,0 +1,9 @@ +[core] + repositoryformatversion = 0 + filemode = false + bare = true + symlinks = false + ignorecase = true + hideDotFiles = dotGitOnly +[remote "origin"] + url = D:/temp/.\\commitencodings diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/description b/LibGit2Sharp.Tests/Resources/commitencodings.git/description new file mode 100644 index 000000000..498b267a8 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/commitencodings.git/description @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/info/exclude b/LibGit2Sharp.Tests/Resources/commitencodings.git/info/exclude new file mode 100644 index 000000000..a5196d1be --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/commitencodings.git/info/exclude @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/29/b28e7d3e84aef886c0e00ff9de1a8cc1fda352 b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/29/b28e7d3e84aef886c0e00ff9de1a8cc1fda352 new file mode 100644 index 000000000..3bff951ef --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/29/b28e7d3e84aef886c0e00ff9de1a8cc1fda352 @@ -0,0 +1,3 @@ +xM +0@a9i "x4F#wӃzGpV/eu w 8'3"t3EF2q'FF³_K'WT[ +Kˡ|Ҏ 5q@TwpQ_~86 \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/2e/0fd4b1141106f1c4695ae5882cbe76e0b80803 b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/2e/0fd4b1141106f1c4695ae5882cbe76e0b80803 new file mode 100644 index 000000000..5b75e2bbb Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/2e/0fd4b1141106f1c4695ae5882cbe76e0b80803 differ diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/51/b2b38d0f9047d699e10ff792f2b2d8eb62e2e4 b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/51/b2b38d0f9047d699e10ff792f2b2d8eb62e2e4 new file mode 100644 index 000000000..ff90da484 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/51/b2b38d0f9047d699e10ff792f2b2d8eb62e2e4 differ diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/59/7dd33db0c0f6300331a4f210423e3b8d983685 b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/59/7dd33db0c0f6300331a4f210423e3b8d983685 new file mode 100644 index 000000000..881ddeab7 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/59/7dd33db0c0f6300331a4f210423e3b8d983685 @@ -0,0 +1,2 @@ +x5˱ 0 Qj$v%4كKFCCyһtjBFt)v,,L}a\ +uǜb^@k6(ڧ?!^Cb#[_ &U \ No newline at end of file diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/78/2dced4c3930bc3c291b24c115ba9a231049d80 b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/78/2dced4c3930bc3c291b24c115ba9a231049d80 new file mode 100644 index 000000000..d95fa0657 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/78/2dced4c3930bc3c291b24c115ba9a231049d80 differ diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/7b/c349e5efdb52884cf47d860852e4912b12c244 b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/7b/c349e5efdb52884cf47d860852e4912b12c244 new file mode 100644 index 000000000..041fabfd7 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/7b/c349e5efdb52884cf47d860852e4912b12c244 differ diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/88/4af1c5d0696b59f69c75b9d67a9bd5ef8c1a86 b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/88/4af1c5d0696b59f69c75b9d67a9bd5ef8c1a86 new file mode 100644 index 000000000..2e5cb015d Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/88/4af1c5d0696b59f69c75b9d67a9bd5ef8c1a86 differ diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/c4/7091527cea53872960bd884a5a8dd906086bab b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/c4/7091527cea53872960bd884a5a8dd906086bab new file mode 100644 index 000000000..746f49ec0 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/c4/7091527cea53872960bd884a5a8dd906086bab differ diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/c6/2f9da614246e7f459a2a9d3dc2c325748435b3 b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/c6/2f9da614246e7f459a2a9d3dc2c325748435b3 new file mode 100644 index 000000000..33b4e0c37 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/c6/2f9da614246e7f459a2a9d3dc2c325748435b3 differ diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/dc/1c18daa88fce6f7e30c76807599df2a4120edd b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/dc/1c18daa88fce6f7e30c76807599df2a4120edd new file mode 100644 index 000000000..803f3f742 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/dc/1c18daa88fce6f7e30c76807599df2a4120edd differ diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/e2/815f18f98bb05b7647c823ed04ffa048295b6a b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/e2/815f18f98bb05b7647c823ed04ffa048295b6a new file mode 100644 index 000000000..7410bda03 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/e2/815f18f98bb05b7647c823ed04ffa048295b6a differ diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/ee/a2d88119c98e8f89c2a6cb528572414da19e7d b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/ee/a2d88119c98e8f89c2a6cb528572414da19e7d new file mode 100644 index 000000000..7c0a7a6d7 Binary files /dev/null and b/LibGit2Sharp.Tests/Resources/commitencodings.git/objects/ee/a2d88119c98e8f89c2a6cb528572414da19e7d differ diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/packed-refs b/LibGit2Sharp.Tests/Resources/commitencodings.git/packed-refs new file mode 100644 index 000000000..b734287f2 --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/commitencodings.git/packed-refs @@ -0,0 +1,3 @@ +# pack-refs with: peeled +29b28e7d3e84aef886c0e00ff9de1a8cc1fda352 refs/heads/dos850 +c62f9da614246e7f459a2a9d3dc2c325748435b3 refs/heads/master diff --git a/LibGit2Sharp.Tests/Resources/commitencodings.git/refs/heads/master b/LibGit2Sharp.Tests/Resources/commitencodings.git/refs/heads/master new file mode 100644 index 000000000..1a68813ff --- /dev/null +++ b/LibGit2Sharp.Tests/Resources/commitencodings.git/refs/heads/master @@ -0,0 +1 @@ +c62f9da614246e7f459a2a9d3dc2c325748435b3 diff --git a/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs b/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs index 2158b590f..8deed3cb2 100644 --- a/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs +++ b/LibGit2Sharp.Tests/TestHelpers/BaseFixture.cs @@ -23,6 +23,7 @@ static BaseFixture() public static string StandardTestRepoPath { get; private set; } public static string MergedTestRepoWorkingDirPath { get; private set; } public static string SubmoduleTestRepoWorkingDirPath { get; private set; } + public static string BareEncodingRepoPath { get; private set; } public static DirectoryInfo ResourcesDirectory { get; private set; } public static readonly Signature DummySignature = new Signature("Author N. Ame", "him@there.com", TruncateSubSeconds(DateTimeOffset.Now)); @@ -56,6 +57,7 @@ private static void SetUpTestEnvironment() StandardTestRepoPath = Path.Combine(StandardTestRepoWorkingDirPath, ".git"); MergedTestRepoWorkingDirPath = Path.Combine(ResourcesDirectory.FullName, "mergedrepo_wd"); SubmoduleTestRepoWorkingDirPath = Path.Combine(ResourcesDirectory.FullName, "submodule_wd"); + BareEncodingRepoPath = Path.Combine(ResourcesDirectory.FullName, "commitencodings.git"); } private static bool IsFileSystemCaseSensitiveInternal() @@ -107,6 +109,11 @@ protected string CloneMergedTestRepo() return Clone(MergedTestRepoWorkingDirPath); } + protected string CloneEncodingTestRepo() + { + return Clone(BareEncodingRepoPath); + } + public string CloneSubmoduleTestRepo() { var submoduleTarget = Path.Combine(ResourcesDirectory.FullName, "submodule_target_wd"); diff --git a/LibGit2Sharp/Core/Utf8Marshaler.cs b/LibGit2Sharp/Core/Utf8Marshaler.cs index ec78db8fc..68d77838f 100644 --- a/LibGit2Sharp/Core/Utf8Marshaler.cs +++ b/LibGit2Sharp/Core/Utf8Marshaler.cs @@ -48,6 +48,18 @@ internal class Utf8Marshaler : ICustomMarshaler { private static readonly Utf8Marshaler staticInstance = new Utf8Marshaler(); + private static readonly Encoding marshallerEncoding = UTF8EncodingWithFallBack; + + public static Encoding UTF8EncodingWithFallBack + { + get + { + Encoding encoding = (Encoding)Encoding.UTF8.Clone(); + encoding.DecoderFallback = new DecoderExceptionFallback(); + return encoding; + } + } + public static ICustomMarshaler GetInstance(String cookie) { return staticInstance; @@ -141,7 +153,7 @@ public static unsafe String FromNative(IntPtr pNativeData) return String.Empty; } - return new String((sbyte*)pNativeData.ToPointer(), 0, (int)(walk - start), Encoding.UTF8); + return FromNative(pNativeData, (int)(walk - start)); } public static unsafe String FromNative(IntPtr pNativeData, int length) @@ -156,7 +168,16 @@ public static unsafe String FromNative(IntPtr pNativeData, int length) return String.Empty; } - return new String((sbyte*)pNativeData.ToPointer(), 0, length, Encoding.UTF8); + try + { + // Try UTF8 with fallback + return new String((sbyte*)pNativeData.ToPointer(), 0, length, marshallerEncoding); + } + catch (DecoderFallbackException) + { + // If this fails, try the platform default. + return new String((sbyte*)pNativeData.ToPointer(), 0, length, Encoding.Default); + } } public static String Utf8FromBuffer(byte[] buffer) @@ -180,7 +201,16 @@ public static String Utf8FromBuffer(byte[] buffer) return String.Empty; } - return Encoding.UTF8.GetString(buffer, 0, length); + try + { + // Try UTF8 with fallback + return marshallerEncoding.GetString(buffer, 0, length); + } + catch (DecoderFallbackException) + { + // If fails, use platform default + return Encoding.Default.GetString(buffer, 0, length); + } } } }