diff --git a/src/FileTypeDetection/Infrastructure/ArchiveInternals.vb b/src/FileTypeDetection/Infrastructure/ArchiveInternals.vb index 7468e69..b9faba9 100644 --- a/src/FileTypeDetection/Infrastructure/ArchiveInternals.vb +++ b/src/FileTypeDetection/Infrastructure/ArchiveInternals.vb @@ -11,6 +11,8 @@ Option Strict On Option Explicit On Imports System.IO +Imports System.Reflection +Imports System.Security Namespace Global.Tomtastisch.FileClassifier ''' @@ -126,8 +128,13 @@ Namespace Global.Tomtastisch.FileClassifier Friend Shared Function OpenArchive(stream As Stream) As SharpCompress.Archives.IArchive Try Dim options = New SharpCompress.Readers.ReaderOptions() With {.LeaveStreamOpen = True} - Return SharpCompress.Archives.ArchiveFactory.OpenArchive(stream, options) + Return OpenArchiveFactoryCompat(stream, options) + Catch ex As TargetInvocationException When IsExpectedInvocationException(ex) + Return Nothing + Catch ex As TargetInvocationException + Throw Catch ex As Exception When _ + TypeOf ex Is MissingMethodException OrElse TypeOf ex Is InvalidOperationException OrElse TypeOf ex Is NotSupportedException OrElse TypeOf ex Is ArgumentException @@ -158,14 +165,67 @@ Namespace Global.Tomtastisch.FileClassifier Private Shared Function OpenGZipArchive(stream As Stream) As SharpCompress.Archives.IArchive Try Dim options = New SharpCompress.Readers.ReaderOptions() With {.LeaveStreamOpen = True} - Return SharpCompress.Archives.GZip.GZipArchive.OpenArchive(stream, options) + Return OpenGZipArchiveCompat(stream, options) + Catch ex As TargetInvocationException When IsExpectedInvocationException(ex) + Return Nothing + Catch ex As TargetInvocationException + Throw Catch ex As Exception When _ + TypeOf ex Is MissingMethodException OrElse TypeOf ex Is InvalidOperationException OrElse TypeOf ex Is NotSupportedException OrElse TypeOf ex Is ArgumentException Return Nothing End Try End Function + + Private Shared Function IsExpectedInvocationException(ex As TargetInvocationException) As Boolean + Dim inner = ex?.InnerException + If inner Is Nothing Then Return False + + Return TypeOf inner Is InvalidOperationException OrElse + TypeOf inner Is NotSupportedException OrElse + TypeOf inner Is ArgumentException OrElse + TypeOf inner Is InvalidDataException OrElse + TypeOf inner Is IOException + End Function + + Private Shared Function OpenArchiveFactoryCompat( + stream As Stream, + options As SharpCompress.Readers.ReaderOptions + ) As SharpCompress.Archives.IArchive + Dim method = GetOpenCompatMethod(GetType(SharpCompress.Archives.ArchiveFactory)) + Dim opened = method.Invoke(Nothing, New Object() {stream, options}) + Return CType(opened, SharpCompress.Archives.IArchive) + End Function + + Private Shared Function OpenGZipArchiveCompat( + stream As Stream, + options As SharpCompress.Readers.ReaderOptions + ) As SharpCompress.Archives.IArchive + Dim method = GetOpenCompatMethod(GetType(SharpCompress.Archives.GZip.GZipArchive)) + Dim opened = method.Invoke(Nothing, New Object() {stream, options}) + Return CType(opened, SharpCompress.Archives.IArchive) + End Function + + Private Shared Function GetOpenCompatMethod(type As Type) As System.Reflection.MethodInfo + Dim signature = New Type() {GetType(Stream), GetType(SharpCompress.Readers.ReaderOptions)} + Dim method = type.GetMethod("OpenArchive", BindingFlags.Public Or + BindingFlags.Static, + binder:=Nothing, + types:=signature, + modifiers:=Nothing) + If method IsNot Nothing Then Return method + + method = type.GetMethod("Open", BindingFlags.Public Or + BindingFlags.Static, + binder:=Nothing, + types:=signature, + modifiers:=Nothing) + If method IsNot Nothing Then Return method + + Throw New MissingMethodException(type.FullName, "OpenArchive/Open(Stream, ReaderOptions)") + End Function End Class ''' @@ -187,7 +247,7 @@ Namespace Global.Tomtastisch.FileClassifier End Using Catch ex As Exception When _ TypeOf ex Is UnauthorizedAccessException OrElse - TypeOf ex Is System.Security.SecurityException OrElse + TypeOf ex Is SecurityException OrElse TypeOf ex Is IOException OrElse TypeOf ex Is InvalidDataException OrElse TypeOf ex Is NotSupportedException OrElse @@ -227,7 +287,7 @@ Namespace Global.Tomtastisch.FileClassifier End Using Catch ex As Exception When _ TypeOf ex Is UnauthorizedAccessException OrElse - TypeOf ex Is System.Security.SecurityException OrElse + TypeOf ex Is SecurityException OrElse TypeOf ex Is IOException OrElse TypeOf ex Is InvalidDataException OrElse TypeOf ex Is NotSupportedException OrElse @@ -242,7 +302,7 @@ Namespace Global.Tomtastisch.FileClassifier StreamGuard.RewindToStart(stream) Catch ex As Exception When _ TypeOf ex Is UnauthorizedAccessException OrElse - TypeOf ex Is System.Security.SecurityException OrElse + TypeOf ex Is SecurityException OrElse TypeOf ex Is IOException OrElse TypeOf ex Is NotSupportedException OrElse TypeOf ex Is ArgumentException OrElse @@ -351,7 +411,7 @@ Namespace Global.Tomtastisch.FileClassifier Return entries.AsReadOnly() Catch ex As Exception When _ TypeOf ex Is UnauthorizedAccessException OrElse - TypeOf ex Is System.Security.SecurityException OrElse + TypeOf ex Is SecurityException OrElse TypeOf ex Is IOException OrElse TypeOf ex Is InvalidDataException OrElse TypeOf ex Is NotSupportedException OrElse @@ -389,7 +449,7 @@ Namespace Global.Tomtastisch.FileClassifier destinationFull = Path.GetFullPath(destinationDirectory) Catch ex As Exception When _ TypeOf ex Is UnauthorizedAccessException OrElse - TypeOf ex Is System.Security.SecurityException OrElse + TypeOf ex Is SecurityException OrElse TypeOf ex Is IOException OrElse TypeOf ex Is PathTooLongException OrElse TypeOf ex Is NotSupportedException OrElse @@ -425,7 +485,7 @@ Namespace Global.Tomtastisch.FileClassifier Return True Catch ex As Exception When _ TypeOf ex Is UnauthorizedAccessException OrElse - TypeOf ex Is System.Security.SecurityException OrElse + TypeOf ex Is SecurityException OrElse TypeOf ex Is IOException OrElse TypeOf ex Is InvalidDataException OrElse TypeOf ex Is NotSupportedException OrElse @@ -440,7 +500,7 @@ Namespace Global.Tomtastisch.FileClassifier Directory.Delete(stageDir, recursive:=True) Catch ex As Exception When _ TypeOf ex Is UnauthorizedAccessException OrElse - TypeOf ex Is System.Security.SecurityException OrElse + TypeOf ex Is SecurityException OrElse TypeOf ex Is IOException OrElse TypeOf ex Is NotSupportedException OrElse TypeOf ex Is ArgumentException @@ -466,7 +526,7 @@ Namespace Global.Tomtastisch.FileClassifier targetPath = Path.GetFullPath(Path.Combine(destinationPrefix, entryName)) Catch ex As Exception When _ TypeOf ex Is UnauthorizedAccessException OrElse - TypeOf ex Is System.Security.SecurityException OrElse + TypeOf ex Is SecurityException OrElse TypeOf ex Is IOException OrElse TypeOf ex Is PathTooLongException OrElse TypeOf ex Is NotSupportedException OrElse @@ -509,7 +569,7 @@ Namespace Global.Tomtastisch.FileClassifier Return True Catch ex As Exception When _ TypeOf ex Is UnauthorizedAccessException OrElse - TypeOf ex Is System.Security.SecurityException OrElse + TypeOf ex Is SecurityException OrElse TypeOf ex Is IOException OrElse TypeOf ex Is InvalidDataException OrElse TypeOf ex Is NotSupportedException OrElse @@ -551,7 +611,7 @@ Namespace Global.Tomtastisch.FileClassifier Return True Catch ex As Exception When _ TypeOf ex Is UnauthorizedAccessException OrElse - TypeOf ex Is System.Security.SecurityException OrElse + TypeOf ex Is SecurityException OrElse TypeOf ex Is IOException OrElse TypeOf ex Is InvalidDataException OrElse TypeOf ex Is NotSupportedException OrElse @@ -650,7 +710,7 @@ Namespace Global.Tomtastisch.FileClassifier End Using Catch ex As Exception When _ TypeOf ex Is UnauthorizedAccessException OrElse - TypeOf ex Is System.Security.SecurityException OrElse + TypeOf ex Is SecurityException OrElse TypeOf ex Is IOException OrElse TypeOf ex Is InvalidDataException OrElse TypeOf ex Is NotSupportedException OrElse @@ -680,7 +740,7 @@ Namespace Global.Tomtastisch.FileClassifier End Using Catch ex As Exception When _ TypeOf ex Is UnauthorizedAccessException OrElse - TypeOf ex Is System.Security.SecurityException OrElse + TypeOf ex Is SecurityException OrElse TypeOf ex Is IOException OrElse TypeOf ex Is InvalidDataException OrElse TypeOf ex Is NotSupportedException OrElse @@ -793,7 +853,7 @@ Namespace Global.Tomtastisch.FileClassifier Return True Catch ex As Exception When _ TypeOf ex Is UnauthorizedAccessException OrElse - TypeOf ex Is System.Security.SecurityException OrElse + TypeOf ex Is SecurityException OrElse TypeOf ex Is IOException OrElse TypeOf ex Is InvalidDataException OrElse TypeOf ex Is NotSupportedException OrElse @@ -883,7 +943,7 @@ Namespace Global.Tomtastisch.FileClassifier End Using Catch ex As Exception When _ TypeOf ex Is UnauthorizedAccessException OrElse - TypeOf ex Is System.Security.SecurityException OrElse + TypeOf ex Is SecurityException OrElse TypeOf ex Is IOException OrElse TypeOf ex Is InvalidDataException OrElse TypeOf ex Is NotSupportedException OrElse @@ -943,7 +1003,7 @@ Namespace Global.Tomtastisch.FileClassifier Return True Catch ex As Exception When _ TypeOf ex Is UnauthorizedAccessException OrElse - TypeOf ex Is System.Security.SecurityException OrElse + TypeOf ex Is SecurityException OrElse TypeOf ex Is IOException OrElse TypeOf ex Is InvalidDataException OrElse TypeOf ex Is NotSupportedException OrElse diff --git a/tests/FileTypeDetectionLib.Tests/Support/ArchivePayloadFactory.cs b/tests/FileTypeDetectionLib.Tests/Support/ArchivePayloadFactory.cs index d8fac7b..599f747 100644 --- a/tests/FileTypeDetectionLib.Tests/Support/ArchivePayloadFactory.cs +++ b/tests/FileTypeDetectionLib.Tests/Support/ArchivePayloadFactory.cs @@ -10,7 +10,7 @@ internal static class ArchivePayloadFactory internal static byte[] CreateZipWithSingleEntry(string entryName, string content) { using var ms = new MemoryStream(); - using (var writer = WriterFactory.OpenWriter(ms, ArchiveType.Zip, new WriterOptions(CompressionType.Deflate))) + using (var writer = SharpCompressApiCompat.OpenWriter(ms, ArchiveType.Zip, new WriterOptions(CompressionType.Deflate))) using (var payload = new MemoryStream(Encoding.UTF8.GetBytes(content))) { writer.Write(string.IsNullOrWhiteSpace(entryName) ? "note.txt" : entryName, payload, DateTime.UnixEpoch); @@ -22,7 +22,7 @@ internal static byte[] CreateZipWithSingleEntry(string entryName, string content internal static byte[] CreateTarWithSingleEntry(string entryName, string content) { using var ms = new MemoryStream(); - using (var writer = WriterFactory.OpenWriter(ms, ArchiveType.Tar, new WriterOptions(CompressionType.None))) + using (var writer = SharpCompressApiCompat.OpenWriter(ms, ArchiveType.Tar, new WriterOptions(CompressionType.None))) using (var payload = new MemoryStream(Encoding.UTF8.GetBytes(content))) { writer.Write(string.IsNullOrWhiteSpace(entryName) ? "note.txt" : entryName, payload, DateTime.UnixEpoch); @@ -34,7 +34,7 @@ internal static byte[] CreateTarWithSingleEntry(string entryName, string content internal static byte[] CreateGZipWithSingleEntry(string entryName, byte[] payload) { using var ms = new MemoryStream(); - using (var writer = WriterFactory.OpenWriter(ms, ArchiveType.GZip, new WriterOptions(CompressionType.GZip))) + using (var writer = SharpCompressApiCompat.OpenWriter(ms, ArchiveType.GZip, new WriterOptions(CompressionType.GZip))) using (var source = new MemoryStream(payload, false)) { writer.Write(string.IsNullOrWhiteSpace(entryName) ? "payload.bin" : entryName, source, DateTime.UnixEpoch); diff --git a/tests/FileTypeDetectionLib.Tests/Support/SharpCompressApiCompat.cs b/tests/FileTypeDetectionLib.Tests/Support/SharpCompressApiCompat.cs new file mode 100644 index 0000000..2caed64 --- /dev/null +++ b/tests/FileTypeDetectionLib.Tests/Support/SharpCompressApiCompat.cs @@ -0,0 +1,48 @@ +using SharpCompress.Archives; +using SharpCompress.Archives.Zip; +using SharpCompress.Common; +using SharpCompress.Readers; +using SharpCompress.Writers; + +namespace FileTypeDetectionLib.Tests.Support; + +internal static class SharpCompressApiCompat +{ + internal static IArchive OpenArchive(Stream stream) + { + var options = new ReaderOptions { LeaveStreamOpen = true }; + var opened = InvokeOpen(typeof(ArchiveFactory), stream, options); + return (IArchive)opened; + } + + internal static IArchive OpenZipArchive(Stream stream) + { + var options = new ReaderOptions { LeaveStreamOpen = true }; + var opened = InvokeOpen(typeof(ZipArchive), stream, options); + return (IArchive)opened; + } + + internal static IWriter OpenWriter(Stream stream, ArchiveType archiveType, WriterOptions options) + { + var args = new object[] { stream, archiveType, options }; + var signature = new[] { typeof(Stream), typeof(ArchiveType), typeof(WriterOptions) }; + + var method = typeof(WriterFactory).GetMethod("OpenWriter", signature) + ?? typeof(WriterFactory).GetMethod("Open", signature) + ?? throw new MissingMethodException(typeof(WriterFactory).FullName, "OpenWriter/Open(Stream, ArchiveType, WriterOptions) [compat]"); + + return (IWriter)method.Invoke(null, args)!; + } + + private static object InvokeOpen(Type type, Stream stream, ReaderOptions options) + { + var args = new object[] { stream, options }; + var signature = new[] { typeof(Stream), typeof(ReaderOptions) }; + + var method = type.GetMethod("OpenArchive", signature) + ?? type.GetMethod("Open", signature) + ?? throw new MissingMethodException(type.FullName, "OpenArchive/Open(Stream, ReaderOptions)"); + + return method.Invoke(null, args)!; + } +} diff --git a/tests/FileTypeDetectionLib.Tests/Unit/ArchiveInternalsNestedBranchUnitTests.cs b/tests/FileTypeDetectionLib.Tests/Unit/ArchiveInternalsNestedBranchUnitTests.cs index 84199c3..bfe0419 100644 --- a/tests/FileTypeDetectionLib.Tests/Unit/ArchiveInternalsNestedBranchUnitTests.cs +++ b/tests/FileTypeDetectionLib.Tests/Unit/ArchiveInternalsNestedBranchUnitTests.cs @@ -1,9 +1,7 @@ using System.Reflection; using FileTypeDetectionLib.Tests.Support; using SharpCompress.Archives; -using SharpCompress.Archives.Zip; using SharpCompress.Common; -using SharpCompress.Readers; using SharpCompress.Writers; using Tomtastisch.FileClassifier; @@ -86,14 +84,14 @@ public void TryReadEntryPayloadBoundedWithOptions_ReturnsFalse_ForInvalidInputs( private static IArchiveEntry CreateZipArchiveEntry(string name, byte[] payload) { using var ms = new MemoryStream(); - using (var writer = WriterFactory.OpenWriter(ms, ArchiveType.Zip, new WriterOptions(CompressionType.Deflate))) + using (var writer = SharpCompressApiCompat.OpenWriter(ms, ArchiveType.Zip, new WriterOptions(CompressionType.Deflate))) using (var data = new MemoryStream(payload, false)) { writer.Write(name, data, DateTime.UnixEpoch); } ms.Position = 0; - var archive = ZipArchive.OpenArchive(ms, new ReaderOptions { LeaveStreamOpen = true }); + var archive = SharpCompressApiCompat.OpenZipArchive(ms); return archive.Entries.First(); } } diff --git a/tests/FileTypeDetectionLib.Tests/Unit/CoreAndArchiveInternalsFailClosedUnitTests.cs b/tests/FileTypeDetectionLib.Tests/Unit/CoreAndArchiveInternalsFailClosedUnitTests.cs index b422bff..d84cb4d 100644 --- a/tests/FileTypeDetectionLib.Tests/Unit/CoreAndArchiveInternalsFailClosedUnitTests.cs +++ b/tests/FileTypeDetectionLib.Tests/Unit/CoreAndArchiveInternalsFailClosedUnitTests.cs @@ -1,8 +1,6 @@ using FileTypeDetectionLib.Tests.Support; using Microsoft.Extensions.Logging; -using SharpCompress.Archives; using SharpCompress.Common; -using SharpCompress.Readers; using Tomtastisch.FileClassifier; namespace FileTypeDetectionLib.Tests.Unit; @@ -162,7 +160,7 @@ public void SharpCompressEntryModel_MapsArchiveEntryFields_ForConcreteEntry() { var payload = ArchivePayloadFactory.CreateTarWithSingleEntry("inner/note.txt", "hello"); using var ms = new MemoryStream(payload, false); - using var archive = ArchiveFactory.OpenArchive(ms, new ReaderOptions { LeaveStreamOpen = true }); + using var archive = SharpCompressApiCompat.OpenArchive(ms); var entry = archive.Entries.First(e => !e.IsDirectory); var model = new SharpCompressEntryModel(entry); diff --git a/tests/FileTypeDetectionLib.Tests/Unit/SharpCompressArchiveBackendUnitTests.cs b/tests/FileTypeDetectionLib.Tests/Unit/SharpCompressArchiveBackendUnitTests.cs index 3f4780d..f633f49 100644 --- a/tests/FileTypeDetectionLib.Tests/Unit/SharpCompressArchiveBackendUnitTests.cs +++ b/tests/FileTypeDetectionLib.Tests/Unit/SharpCompressArchiveBackendUnitTests.cs @@ -109,7 +109,7 @@ public void Process_ReturnsFalse_WhenExtractorReturnsFalse() private static byte[] CreateTarWithEntries(int entryCount, int entrySize) { using var ms = new MemoryStream(); - using (var writer = WriterFactory.OpenWriter(ms, ArchiveType.Tar, new WriterOptions(CompressionType.None))) + using (var writer = SharpCompressApiCompat.OpenWriter(ms, ArchiveType.Tar, new WriterOptions(CompressionType.None))) { for (var i = 0; i < entryCount; i++) { diff --git a/tests/FileTypeDetectionLib.Tests/Unit/SharpCompressEntryModelNonNullUnitTests.cs b/tests/FileTypeDetectionLib.Tests/Unit/SharpCompressEntryModelNonNullUnitTests.cs index 39379f1..dedc3d2 100644 --- a/tests/FileTypeDetectionLib.Tests/Unit/SharpCompressEntryModelNonNullUnitTests.cs +++ b/tests/FileTypeDetectionLib.Tests/Unit/SharpCompressEntryModelNonNullUnitTests.cs @@ -1,7 +1,6 @@ using System.Text; -using SharpCompress.Archives; +using FileTypeDetectionLib.Tests.Support; using SharpCompress.Common; -using SharpCompress.Readers; using SharpCompress.Writers; using Tomtastisch.FileClassifier; @@ -14,7 +13,7 @@ public void Properties_ReturnValues_ForRealArchiveEntry() { var payload = CreateTarWithEntry("note.txt", "hello"); using var stream = new MemoryStream(payload, false); - using var archive = ArchiveFactory.OpenArchive(stream, new ReaderOptions { LeaveStreamOpen = true }); + using var archive = SharpCompressApiCompat.OpenArchive(stream); var entry = archive.Entries.First(); var model = new SharpCompressEntryModel(entry); @@ -31,7 +30,7 @@ public void Properties_ReturnValues_ForRealArchiveEntry() private static byte[] CreateTarWithEntry(string name, string content) { using var ms = new MemoryStream(); - using (var writer = WriterFactory.OpenWriter(ms, ArchiveType.Tar, new WriterOptions(CompressionType.None))) + using (var writer = SharpCompressApiCompat.OpenWriter(ms, ArchiveType.Tar, new WriterOptions(CompressionType.None))) using (var data = new MemoryStream(Encoding.UTF8.GetBytes(content))) { writer.Write(name, data, DateTime.UnixEpoch);