diff --git a/src/SharpCompress/Archives/GZip/GZipArchive.cs b/src/SharpCompress/Archives/GZip/GZipArchive.cs index b896db4f..b86e154a 100644 --- a/src/SharpCompress/Archives/GZip/GZipArchive.cs +++ b/src/SharpCompress/Archives/GZip/GZipArchive.cs @@ -14,6 +14,7 @@ namespace SharpCompress.Archives.GZip public class GZipArchive : AbstractWritableArchive { #if !NO_FILE + /// /// Constructor expects a filepath to an existing file. /// @@ -36,6 +37,7 @@ namespace SharpCompress.Archives.GZip return new GZipArchive(fileInfo, readerOptions ?? new ReaderOptions()); } #endif + /// /// Takes a seekable Stream as a source /// @@ -54,11 +56,11 @@ namespace SharpCompress.Archives.GZip #if !NO_FILE -/// -/// Constructor with a FileInfo object to an existing file. -/// -/// -/// + /// + /// Constructor with a FileInfo object to an existing file. + /// + /// + /// internal GZipArchive(FileInfo fileInfo, ReaderOptions options) : base(ArchiveType.GZip, fileInfo, options) { @@ -179,7 +181,7 @@ namespace SharpCompress.Archives.GZip protected override IEnumerable LoadEntries(IEnumerable volumes) { Stream stream = volumes.Single().Stream; - yield return new GZipArchiveEntry(this, new GZipFilePart(stream)); + yield return new GZipArchiveEntry(this, new GZipFilePart(stream, ReaderOptions.ForceEncoding)); } protected override IReader CreateReaderForSolidExtraction() diff --git a/src/SharpCompress/Archives/Tar/TarArchive.cs b/src/SharpCompress/Archives/Tar/TarArchive.cs index 8103938f..2705ffdf 100644 --- a/src/SharpCompress/Archives/Tar/TarArchive.cs +++ b/src/SharpCompress/Archives/Tar/TarArchive.cs @@ -16,7 +16,7 @@ namespace SharpCompress.Archives.Tar public class TarArchive : AbstractWritableArchive { #if !NO_FILE - + /// /// Constructor expects a filepath to an existing file. /// @@ -39,7 +39,7 @@ namespace SharpCompress.Archives.Tar return new TarArchive(fileInfo, readerOptions ?? new ReaderOptions()); } #endif - + /// /// Takes a seekable Stream as a source /// @@ -52,6 +52,7 @@ namespace SharpCompress.Archives.Tar } #if !NO_FILE + public static bool IsTarFile(string filePath) { return IsTarFile(new FileInfo(filePath)); @@ -98,7 +99,6 @@ namespace SharpCompress.Archives.Tar protected override IEnumerable LoadVolumes(FileInfo file) { - return new TarVolume(file.OpenRead(), ReaderOptions).AsEnumerable(); } #endif @@ -127,7 +127,7 @@ namespace SharpCompress.Archives.Tar { Stream stream = volumes.Single().Stream; TarHeader previousHeader = null; - foreach (TarHeader header in TarHeaderFactory.ReadHeader(StreamingMode.Seekable, stream)) + foreach (TarHeader header in TarHeaderFactory.ReadHeader(StreamingMode.Seekable, stream, ReaderOptions.ForceEncoding)) { if (header != null) { @@ -152,7 +152,7 @@ namespace SharpCompress.Archives.Tar memoryStream.Position = 0; var bytes = memoryStream.ToArray(); - header.Name = ArchiveEncoding.Default.GetString(bytes, 0, bytes.Length).TrimNulls(); + header.Name = (ReaderOptions.ForceEncoding ?? ArchiveEncoding.Default).GetString(bytes, 0, bytes.Length).TrimNulls(); } } diff --git a/src/SharpCompress/Archives/Zip/ZipArchive.cs b/src/SharpCompress/Archives/Zip/ZipArchive.cs index e4c132c0..3de98abe 100644 --- a/src/SharpCompress/Archives/Zip/ZipArchive.cs +++ b/src/SharpCompress/Archives/Zip/ZipArchive.cs @@ -24,6 +24,7 @@ namespace SharpCompress.Archives.Zip public CompressionLevel DeflateCompressionLevel { get; set; } #if !NO_FILE + /// /// Constructor expects a filepath to an existing file. /// @@ -46,6 +47,7 @@ namespace SharpCompress.Archives.Zip return new ZipArchive(fileInfo, readerOptions ?? new ReaderOptions()); } #endif + /// /// Takes a seekable Stream as a source /// @@ -58,6 +60,7 @@ namespace SharpCompress.Archives.Zip } #if !NO_FILE + public static bool IsZipFile(string filePath, string password = null) { return IsZipFile(new FileInfo(filePath), password); @@ -109,7 +112,7 @@ namespace SharpCompress.Archives.Zip internal ZipArchive(FileInfo fileInfo, ReaderOptions readerOptions) : base(ArchiveType.Zip, fileInfo, readerOptions) { - headerFactory = new SeekableZipHeaderFactory(readerOptions.Password); + headerFactory = new SeekableZipHeaderFactory(readerOptions.Password, readerOptions.ForceEncoding); } protected override IEnumerable LoadVolumes(FileInfo file) @@ -131,7 +134,7 @@ namespace SharpCompress.Archives.Zip internal ZipArchive(Stream stream, ReaderOptions readerOptions) : base(ArchiveType.Zip, stream, readerOptions) { - headerFactory = new SeekableZipHeaderFactory(readerOptions.Password); + headerFactory = new SeekableZipHeaderFactory(readerOptions.Password, readerOptions.ForceEncoding); } protected override IEnumerable LoadVolumes(IEnumerable streams) @@ -150,19 +153,19 @@ namespace SharpCompress.Archives.Zip switch (h.ZipHeaderType) { case ZipHeaderType.DirectoryEntry: - { - yield return new ZipArchiveEntry(this, - new SeekableZipFilePart(headerFactory, - h as DirectoryEntryHeader, - stream)); - } + { + yield return new ZipArchiveEntry(this, + new SeekableZipFilePart(headerFactory, + h as DirectoryEntryHeader, + stream)); + } break; case ZipHeaderType.DirectoryEnd: - { - byte[] bytes = (h as DirectoryEndHeader).Comment; - volume.Comment = ArchiveEncoding.Default.GetString(bytes, 0, bytes.Length); - yield break; - } + { + byte[] bytes = (h as DirectoryEndHeader).Comment; + volume.Comment = (ReaderOptions.ForceEncoding ?? ArchiveEncoding.Default).GetString(bytes, 0, bytes.Length); + yield break; + } } } } diff --git a/src/SharpCompress/Common/GZip/GZipEntry.cs b/src/SharpCompress/Common/GZip/GZipEntry.cs index dd80e73e..ad621394 100644 --- a/src/SharpCompress/Common/GZip/GZipEntry.cs +++ b/src/SharpCompress/Common/GZip/GZipEntry.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; namespace SharpCompress.Common.GZip { @@ -39,9 +40,9 @@ namespace SharpCompress.Common.GZip internal override IEnumerable Parts => filePart.AsEnumerable(); - internal static IEnumerable GetEntries(Stream stream) + internal static IEnumerable GetEntries(Stream stream, Encoding forceEncoding) { - yield return new GZipEntry(new GZipFilePart(stream)); + yield return new GZipEntry(new GZipFilePart(stream, forceEncoding)); } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/GZip/GZipFilePart.cs b/src/SharpCompress/Common/GZip/GZipFilePart.cs index 7690a014..7b7cda03 100644 --- a/src/SharpCompress/Common/GZip/GZipFilePart.cs +++ b/src/SharpCompress/Common/GZip/GZipFilePart.cs @@ -5,6 +5,7 @@ using SharpCompress.Common.Tar.Headers; using SharpCompress.Compressors; using SharpCompress.Compressors.Deflate; using SharpCompress.Converters; +using System.Text; namespace SharpCompress.Common.GZip { @@ -12,12 +13,14 @@ namespace SharpCompress.Common.GZip { private string name; private readonly Stream stream; + private readonly Encoding forceEncoding; - internal GZipFilePart(Stream stream) + internal GZipFilePart(Stream stream, Encoding forceEncoding) { ReadAndValidateGzipHeader(stream); EntryStartPosition = stream.Position; this.stream = stream; + this.forceEncoding = forceEncoding; } internal long EntryStartPosition { get; } @@ -75,11 +78,11 @@ namespace SharpCompress.Common.GZip } if ((header[3] & 0x08) == 0x08) { - name = ReadZeroTerminatedString(stream); + name = ReadZeroTerminatedString(stream, forceEncoding); } if ((header[3] & 0x10) == 0x010) { - ReadZeroTerminatedString(stream); + ReadZeroTerminatedString(stream, forceEncoding); } if ((header[3] & 0x02) == 0x02) { @@ -87,7 +90,7 @@ namespace SharpCompress.Common.GZip } } - private static string ReadZeroTerminatedString(Stream stream) + private static string ReadZeroTerminatedString(Stream stream, Encoding forceEncoding) { byte[] buf1 = new byte[1]; var list = new List(); @@ -110,8 +113,8 @@ namespace SharpCompress.Common.GZip } } while (!done); - byte[] a = list.ToArray(); - return ArchiveEncoding.Default.GetString(a, 0, a.Length); + byte[] buffer = list.ToArray(); + return (forceEncoding ?? ArchiveEncoding.Default).GetString(buffer, 0, buffer.Length); } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/OptionsBase.cs b/src/SharpCompress/Common/OptionsBase.cs index 06e721db..03fa70b5 100644 --- a/src/SharpCompress/Common/OptionsBase.cs +++ b/src/SharpCompress/Common/OptionsBase.cs @@ -1,4 +1,6 @@ -namespace SharpCompress.Common +using System.Text; + +namespace SharpCompress.Common { public class OptionsBase { @@ -6,5 +8,7 @@ /// SharpCompress will keep the supplied streams open. Default is true. /// public bool LeaveStreamOpen { get; set; } = true; + + public Encoding ForceEncoding { get; set; } } } \ No newline at end of file diff --git a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs index f0359a24..d41fa76b 100644 --- a/src/SharpCompress/Common/Rar/Headers/FileHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/FileHeader.cs @@ -1,6 +1,6 @@ +using SharpCompress.IO; using System; using System.IO; -using SharpCompress.IO; namespace SharpCompress.Common.Rar.Headers { @@ -52,50 +52,50 @@ namespace SharpCompress.Common.Rar.Headers switch (HeaderType) { case HeaderType.FileHeader: - { - if (FileFlags.HasFlag(FileFlags.UNICODE)) { - int length = 0; - while (length < fileNameBytes.Length - && fileNameBytes[length] != 0) + if (FileFlags.HasFlag(FileFlags.UNICODE)) { - length++; - } - if (length != nameSize) - { - length++; - FileName = FileNameDecoder.Decode(fileNameBytes, length); + int length = 0; + while (length < fileNameBytes.Length + && fileNameBytes[length] != 0) + { + length++; + } + if (length != nameSize) + { + length++; + FileName = FileNameDecoder.Decode(fileNameBytes, length); + } + else + { + FileName = DecodeString(fileNameBytes); + } } else { - FileName = DecodeDefault(fileNameBytes); + FileName = DecodeString(fileNameBytes); } + FileName = ConvertPath(FileName, HostOS); } - else - { - FileName = DecodeDefault(fileNameBytes); - } - FileName = ConvertPath(FileName, HostOS); - } break; case HeaderType.NewSubHeader: - { - int datasize = HeaderSize - NEWLHD_SIZE - nameSize; - if (FileFlags.HasFlag(FileFlags.SALT)) { - datasize -= SALT_SIZE; - } - if (datasize > 0) - { - SubData = reader.ReadBytes(datasize); - } + int datasize = HeaderSize - NEWLHD_SIZE - nameSize; + if (FileFlags.HasFlag(FileFlags.SALT)) + { + datasize -= SALT_SIZE; + } + if (datasize > 0) + { + SubData = reader.ReadBytes(datasize); + } - if (NewSubHeaderType.SUBHEAD_TYPE_RR.Equals(fileNameBytes)) - { - RecoverySectors = SubData[8] + (SubData[9] << 8) - + (SubData[10] << 16) + (SubData[11] << 24); + if (NewSubHeaderType.SUBHEAD_TYPE_RR.Equals(fileNameBytes)) + { + RecoverySectors = SubData[8] + (SubData[9] << 8) + + (SubData[10] << 16) + (SubData[11] << 24); + } } - } break; } @@ -119,9 +119,9 @@ namespace SharpCompress.Common.Rar.Headers } //only the full .net framework will do other code pages than unicode/utf8 - private string DecodeDefault(byte[] bytes) + private string DecodeString(byte[] bytes) { - return ArchiveEncoding.Default.GetString(bytes, 0, bytes.Length); + return (ForceEncoding ?? ArchiveEncoding.Default).GetString(bytes, 0, bytes.Length); } private long UInt32To64(uint x, uint y) @@ -178,6 +178,7 @@ namespace SharpCompress.Common.Rar.Headers } internal long DataStartPosition { get; set; } + internal HostOS HostOS { get; private set; } internal uint FileCRC { get; private set; } @@ -199,6 +200,7 @@ namespace SharpCompress.Common.Rar.Headers internal FileFlags FileFlags => (FileFlags)Flags; internal long CompressedSize { get; private set; } + internal long UncompressedSize { get; private set; } internal string FileName { get; private set; } diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs index 02658389..f0a63a31 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeader.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeader.cs @@ -1,6 +1,7 @@ using System; using System.IO; using SharpCompress.IO; +using System.Text; namespace SharpCompress.Common.Rar.Headers { @@ -50,7 +51,8 @@ namespace SharpCompress.Common.Rar.Headers } } - protected virtual void ReadFromReader(MarkingBinaryReader reader) { + protected virtual void ReadFromReader(MarkingBinaryReader reader) + { throw new NotImplementedException(); } @@ -76,10 +78,11 @@ namespace SharpCompress.Common.Rar.Headers return header; } - private void VerifyHeaderCrc(ushort crc) { - if (HeaderType != HeaderType.MarkHeader) + private void VerifyHeaderCrc(ushort crc) + { + if (HeaderType != HeaderType.MarkHeader) { - if (crc != HeadCRC) + if (crc != HeadCRC) { throw new InvalidFormatException("rar header crc mismatch"); } @@ -106,6 +109,8 @@ namespace SharpCompress.Common.Rar.Headers protected short HeaderSize { get; private set; } + internal Encoding ForceEncoding { get; set; } + /// /// This additional size of the header could be file data /// diff --git a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs index 13e02722..cb713c0a 100644 --- a/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs +++ b/src/SharpCompress/Common/Rar/Headers/RarHeaderFactory.cs @@ -30,6 +30,8 @@ namespace SharpCompress.Common.Rar.Headers RarHeader header; while ((header = ReadNextHeader(stream)) != null) { + header.ForceEncoding = Options.ForceEncoding; + yield return header; if (header.HeaderType == HeaderType.EndArchiveHeader) { @@ -117,7 +119,7 @@ namespace SharpCompress.Common.Rar.Headers { #if !NO_CRYPTO var reader = new RarCryptoBinaryReader(stream, Options.Password); - + if (IsEncrypted) { if (Options.Password == null) @@ -141,110 +143,110 @@ namespace SharpCompress.Common.Rar.Headers switch (header.HeaderType) { case HeaderType.ArchiveHeader: - { - var ah = header.PromoteHeader(reader); - IsEncrypted = ah.HasPassword; - return ah; - } + { + var ah = header.PromoteHeader(reader); + IsEncrypted = ah.HasPassword; + return ah; + } case HeaderType.MarkHeader: - { - return header.PromoteHeader(reader); - } + { + return header.PromoteHeader(reader); + } case HeaderType.ProtectHeader: - { - ProtectHeader ph = header.PromoteHeader(reader); - - // skip the recovery record data, we do not use it. - switch (StreamingMode) { - case StreamingMode.Seekable: - { - reader.BaseStream.Position += ph.DataSize; - } - break; - case StreamingMode.Streaming: - { - reader.BaseStream.Skip(ph.DataSize); - } - break; - default: - { - throw new InvalidFormatException("Invalid StreamingMode"); - } - } + ProtectHeader ph = header.PromoteHeader(reader); - return ph; - } + // skip the recovery record data, we do not use it. + switch (StreamingMode) + { + case StreamingMode.Seekable: + { + reader.BaseStream.Position += ph.DataSize; + } + break; + case StreamingMode.Streaming: + { + reader.BaseStream.Skip(ph.DataSize); + } + break; + default: + { + throw new InvalidFormatException("Invalid StreamingMode"); + } + } + + return ph; + } case HeaderType.NewSubHeader: - { - FileHeader fh = header.PromoteHeader(reader); - switch (StreamingMode) { - case StreamingMode.Seekable: + FileHeader fh = header.PromoteHeader(reader); + switch (StreamingMode) { - fh.DataStartPosition = reader.BaseStream.Position; - reader.BaseStream.Position += fh.CompressedSize; - } - break; - case StreamingMode.Streaming: - { - //skip the data because it's useless? - reader.BaseStream.Skip(fh.CompressedSize); - } - break; - default: - { - throw new InvalidFormatException("Invalid StreamingMode"); + case StreamingMode.Seekable: + { + fh.DataStartPosition = reader.BaseStream.Position; + reader.BaseStream.Position += fh.CompressedSize; + } + break; + case StreamingMode.Streaming: + { + //skip the data because it's useless? + reader.BaseStream.Skip(fh.CompressedSize); + } + break; + default: + { + throw new InvalidFormatException("Invalid StreamingMode"); + } } + return fh; } - return fh; - } case HeaderType.FileHeader: - { - FileHeader fh = header.PromoteHeader(reader); - switch (StreamingMode) { - case StreamingMode.Seekable: + FileHeader fh = header.PromoteHeader(reader); + switch (StreamingMode) { - fh.DataStartPosition = reader.BaseStream.Position; - reader.BaseStream.Position += fh.CompressedSize; - } - break; - case StreamingMode.Streaming: - { - var ms = new ReadOnlySubStream(reader.BaseStream, fh.CompressedSize); - if (fh.Salt == null) - { - fh.PackedStream = ms; - } - else - { + case StreamingMode.Seekable: + { + fh.DataStartPosition = reader.BaseStream.Position; + reader.BaseStream.Position += fh.CompressedSize; + } + break; + case StreamingMode.Streaming: + { + var ms = new ReadOnlySubStream(reader.BaseStream, fh.CompressedSize); + if (fh.Salt == null) + { + fh.PackedStream = ms; + } + else + { #if !NO_CRYPTO - fh.PackedStream = new RarCryptoWrapper(ms, Options.Password, fh.Salt); + fh.PackedStream = new RarCryptoWrapper(ms, Options.Password, fh.Salt); #else throw new NotSupportedException("RarCrypto not supported"); #endif - } - } - break; - default: - { - throw new InvalidFormatException("Invalid StreamingMode"); + } + } + break; + default: + { + throw new InvalidFormatException("Invalid StreamingMode"); + } } + return fh; } - return fh; - } case HeaderType.EndArchiveHeader: - { - return header.PromoteHeader(reader); - } + { + return header.PromoteHeader(reader); + } default: - { - throw new InvalidFormatException("Invalid Rar Header: " + header.HeaderType); - } + { + throw new InvalidFormatException("Invalid Rar Header: " + header.HeaderType); + } } } } -} +} \ No newline at end of file diff --git a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs index ec51b157..79528835 100644 --- a/src/SharpCompress/Common/Tar/Headers/TarHeader.cs +++ b/src/SharpCompress/Common/Tar/Headers/TarHeader.cs @@ -20,6 +20,7 @@ namespace SharpCompress.Common.Tar.Headers internal DateTime LastModifiedTime { get; set; } internal EntryType EntryType { get; set; } internal Stream PackedStream { get; set; } + internal Encoding ForceEncoding { get; set; } internal const int BlockSize = 512; @@ -72,7 +73,7 @@ namespace SharpCompress.Common.Tar.Headers private void WriteLongFilenameHeader(Stream output) { - byte[] nameBytes = ArchiveEncoding.Default.GetBytes(Name); + byte[] nameBytes = (ForceEncoding ?? ArchiveEncoding.Default).GetBytes(Name); output.Write(nameBytes, 0, nameBytes.Length); // pad to multiple of BlockSize bytes, and make sure a terminating null is added @@ -99,7 +100,7 @@ namespace SharpCompress.Common.Tar.Headers } else { - Name = ArchiveEncoding.Default.GetString(buffer, 0, 100).TrimNulls(); + Name = (ForceEncoding ?? ArchiveEncoding.Default).GetString(buffer, 0, 100).TrimNulls(); } EntryType = ReadEntryType(buffer); @@ -111,12 +112,12 @@ namespace SharpCompress.Common.Tar.Headers long unixTimeStamp = ReadASCIIInt64Base8(buffer, 136, 11); LastModifiedTime = Epoch.AddSeconds(unixTimeStamp).ToLocalTime(); - Magic = ArchiveEncoding.Default.GetString(buffer, 257, 6).TrimNulls(); + Magic = (ForceEncoding ?? ArchiveEncoding.Default).GetString(buffer, 257, 6).TrimNulls(); if (!string.IsNullOrEmpty(Magic) && "ustar".Equals(Magic)) { - string namePrefix = ArchiveEncoding.Default.GetString(buffer, 345, 157); + string namePrefix = (ForceEncoding ?? ArchiveEncoding.Default).GetString(buffer, 345, 157); namePrefix = namePrefix.TrimNulls(); if (!string.IsNullOrEmpty(namePrefix)) { @@ -143,7 +144,7 @@ namespace SharpCompress.Common.Tar.Headers { reader.ReadBytes(remainingBytesToRead); } - return ArchiveEncoding.Default.GetString(nameBytes, 0, nameBytes.Length).TrimNulls(); + return (ForceEncoding ?? ArchiveEncoding.Default).GetString(nameBytes, 0, nameBytes.Length).TrimNulls(); } private static EntryType ReadEntryType(byte[] buffer) diff --git a/src/SharpCompress/Common/Tar/TarEntry.cs b/src/SharpCompress/Common/Tar/TarEntry.cs index 101271a9..f4d0d019 100644 --- a/src/SharpCompress/Common/Tar/TarEntry.cs +++ b/src/SharpCompress/Common/Tar/TarEntry.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using SharpCompress.Common.Tar.Headers; using SharpCompress.IO; +using System.Text; namespace SharpCompress.Common.Tar { @@ -43,9 +44,9 @@ namespace SharpCompress.Common.Tar internal override IEnumerable Parts => filePart.AsEnumerable(); internal static IEnumerable GetEntries(StreamingMode mode, Stream stream, - CompressionType compressionType) + CompressionType compressionType, Encoding forceEncoding) { - foreach (TarHeader h in TarHeaderFactory.ReadHeader(mode, stream)) + foreach (TarHeader h in TarHeaderFactory.ReadHeader(mode, stream, forceEncoding)) { if (h != null) { diff --git a/src/SharpCompress/Common/Tar/TarHeaderFactory.cs b/src/SharpCompress/Common/Tar/TarHeaderFactory.cs index bc31269c..e9ca829c 100644 --- a/src/SharpCompress/Common/Tar/TarHeaderFactory.cs +++ b/src/SharpCompress/Common/Tar/TarHeaderFactory.cs @@ -2,12 +2,13 @@ using System.IO; using SharpCompress.Common.Tar.Headers; using SharpCompress.IO; +using System.Text; namespace SharpCompress.Common.Tar { internal static class TarHeaderFactory { - internal static IEnumerable ReadHeader(StreamingMode mode, Stream stream) + internal static IEnumerable ReadHeader(StreamingMode mode, Stream stream, Encoding forceEncoding) { while (true) { @@ -15,7 +16,11 @@ namespace SharpCompress.Common.Tar try { BinaryReader reader = new BinaryReader(stream); - header = new TarHeader(); + header = new TarHeader() + { + ForceEncoding = forceEncoding + }; + if (!header.Read(reader)) { yield break; @@ -23,22 +28,22 @@ namespace SharpCompress.Common.Tar switch (mode) { case StreamingMode.Seekable: - { - header.DataStartPosition = reader.BaseStream.Position; + { + header.DataStartPosition = reader.BaseStream.Position; - //skip to nearest 512 - reader.BaseStream.Position += PadTo512(header.Size); - } + //skip to nearest 512 + reader.BaseStream.Position += PadTo512(header.Size); + } break; case StreamingMode.Streaming: - { - header.PackedStream = new TarReadOnlySubStream(stream, header.Size); - } + { + header.PackedStream = new TarReadOnlySubStream(stream, header.Size); + } break; default: - { - throw new InvalidFormatException("Invalid StreamingMode"); - } + { + throw new InvalidFormatException("Invalid StreamingMode"); + } } } catch diff --git a/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs b/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs index b5415463..6e35191c 100644 --- a/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs +++ b/src/SharpCompress/Common/Zip/Headers/HeaderFlags.cs @@ -5,6 +5,7 @@ namespace SharpCompress.Common.Zip.Headers [Flags] internal enum HeaderFlags : ushort { + None = 0, Encrypted = 1, // http://www.pkware.com/documents/casestudies/APPNOTE.TXT Bit1 = 2, Bit2 = 4, diff --git a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs index 0453c41e..d63c4f83 100644 --- a/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs +++ b/src/SharpCompress/Common/Zip/Headers/LocalEntryHeader.cs @@ -1,5 +1,6 @@ using System.IO; using System.Linq; +using System.Text; namespace SharpCompress.Common.Zip.Headers { diff --git a/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs b/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs index 6ffc2781..0266b0c1 100644 --- a/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs +++ b/src/SharpCompress/Common/Zip/Headers/ZipFileEntry.cs @@ -32,6 +32,11 @@ namespace SharpCompress.Common.Zip.Headers protected string DecodeString(byte[] str) { + if (ForceEncoding != null) + { + return ForceEncoding.GetString(str, 0, str.Length); + } + if (FlagUtility.HasFlag(Flags, HeaderFlags.UTF8)) { return Encoding.UTF8.GetString(str, 0, str.Length); @@ -42,15 +47,23 @@ namespace SharpCompress.Common.Zip.Headers protected byte[] EncodeString(string str) { + if (ForceEncoding != null) + { + return ForceEncoding.GetBytes(str); + } + if (FlagUtility.HasFlag(Flags, HeaderFlags.UTF8)) { return Encoding.UTF8.GetBytes(str); } + return ArchiveEncoding.Default.GetBytes(str); } internal Stream PackedStream { get; set; } + internal Encoding ForceEncoding { get; set; } + internal string Name { get; set; } internal HeaderFlags Flags { get; set; } @@ -64,7 +77,7 @@ namespace SharpCompress.Common.Zip.Headers internal long UncompressedSize { get; set; } internal List Extra { get; set; } - + public string Password { get; set; } internal PkwareTraditionalEncryptionData ComposeEncryptionData(Stream archiveStream) @@ -78,7 +91,7 @@ namespace SharpCompress.Common.Zip.Headers archiveStream.Read(buffer, 0, 12); PkwareTraditionalEncryptionData encryptionData = PkwareTraditionalEncryptionData.ForRead(Password, this, buffer); - + return encryptionData; } diff --git a/src/SharpCompress/Common/Zip/SeekableZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/SeekableZipHeaderFactory.cs index 3d78ce3b..03d97966 100644 --- a/src/SharpCompress/Common/Zip/SeekableZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/SeekableZipHeaderFactory.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using SharpCompress.Common.Zip.Headers; using SharpCompress.IO; +using System.Text; namespace SharpCompress.Common.Zip { @@ -11,8 +12,8 @@ namespace SharpCompress.Common.Zip private const int MAX_ITERATIONS_FOR_DIRECTORY_HEADER = 4096; private bool zip64; - internal SeekableZipHeaderFactory(string password) - : base(StreamingMode.Seekable, password) + internal SeekableZipHeaderFactory(string password, Encoding forceEncoding) + : base(StreamingMode.Seekable, password, forceEncoding) { } @@ -33,7 +34,7 @@ namespace SharpCompress.Common.Zip stream.Seek(zip64Locator.RelativeOffsetOfTheEndOfDirectoryRecord, SeekOrigin.Begin); uint zip64Signature = reader.ReadUInt32(); - if(zip64Signature != ZIP64_END_OF_CENTRAL_DIRECTORY) + if (zip64Signature != ZIP64_END_OF_CENTRAL_DIRECTORY) throw new ArchiveException("Failed to locate the Zip64 Header"); var zip64Entry = new Zip64DirectoryEndHeader(); diff --git a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs index 9c8015ed..92ae351a 100644 --- a/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/StreamingZipHeaderFactory.cs @@ -2,13 +2,14 @@ using System.IO; using SharpCompress.Common.Zip.Headers; using SharpCompress.IO; +using System.Text; namespace SharpCompress.Common.Zip { internal class StreamingZipHeaderFactory : ZipHeaderFactory { - internal StreamingZipHeaderFactory(string password) - : base(StreamingMode.Streaming, password) + internal StreamingZipHeaderFactory(string password, Encoding forceEncoding = null) + : base(StreamingMode.Streaming, password, forceEncoding) { } diff --git a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs index 9eeb087d..56c05fed 100644 --- a/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs +++ b/src/SharpCompress/Common/Zip/ZipHeaderFactory.cs @@ -5,6 +5,7 @@ using System.Linq; #endif using SharpCompress.Common.Zip.Headers; using SharpCompress.IO; +using System.Text; namespace SharpCompress.Common.Zip { @@ -23,11 +24,13 @@ namespace SharpCompress.Common.Zip protected LocalEntryHeader lastEntryHeader; private readonly string password; private readonly StreamingMode mode; + private readonly Encoding forceEncoding; - protected ZipHeaderFactory(StreamingMode mode, string password) + protected ZipHeaderFactory(StreamingMode mode, string password, Encoding forceEncoding) { this.mode = mode; this.password = password; + this.forceEncoding = forceEncoding; } protected ZipHeader ReadHeader(uint headerBytes, BinaryReader reader, bool zip64 = false) @@ -35,58 +38,64 @@ namespace SharpCompress.Common.Zip switch (headerBytes) { case ENTRY_HEADER_BYTES: - { - var entryHeader = new LocalEntryHeader(); - entryHeader.Read(reader); - LoadHeader(entryHeader, reader.BaseStream); + { + var entryHeader = new LocalEntryHeader() + { + ForceEncoding = forceEncoding + }; + entryHeader.Read(reader); + LoadHeader(entryHeader, reader.BaseStream); - lastEntryHeader = entryHeader; - return entryHeader; - } + lastEntryHeader = entryHeader; + return entryHeader; + } case DIRECTORY_START_HEADER_BYTES: - { - var entry = new DirectoryEntryHeader(); - entry.Read(reader); - return entry; - } + { + var entry = new DirectoryEntryHeader() + { + ForceEncoding = forceEncoding + }; + entry.Read(reader); + return entry; + } case POST_DATA_DESCRIPTOR: - { - if (FlagUtility.HasFlag(lastEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor)) { - lastEntryHeader.Crc = reader.ReadUInt32(); - lastEntryHeader.CompressedSize = zip64 ? (long)reader.ReadUInt64() : reader.ReadUInt32(); - lastEntryHeader.UncompressedSize = zip64 ? (long)reader.ReadUInt64() : reader.ReadUInt32(); + if (FlagUtility.HasFlag(lastEntryHeader.Flags, HeaderFlags.UsePostDataDescriptor)) + { + lastEntryHeader.Crc = reader.ReadUInt32(); + lastEntryHeader.CompressedSize = zip64 ? (long)reader.ReadUInt64() : reader.ReadUInt32(); + lastEntryHeader.UncompressedSize = zip64 ? (long)reader.ReadUInt64() : reader.ReadUInt32(); + } + else + { + reader.ReadBytes(zip64 ? 20 : 12); + } + return null; } - else - { - reader.ReadBytes(zip64 ? 20 : 12); - } - return null; - } case DIGITAL_SIGNATURE: return null; case DIRECTORY_END_HEADER_BYTES: - { - var entry = new DirectoryEndHeader(); - entry.Read(reader); - return entry; - } + { + var entry = new DirectoryEndHeader(); + entry.Read(reader); + return entry; + } case SPLIT_ARCHIVE_HEADER_BYTES: - { - return new SplitHeader(); - } + { + return new SplitHeader(); + } case ZIP64_END_OF_CENTRAL_DIRECTORY: - { - var entry = new Zip64DirectoryEndHeader(); - entry.Read(reader); - return entry; - } + { + var entry = new Zip64DirectoryEndHeader(); + entry.Read(reader); + return entry; + } case ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR: - { - var entry = new Zip64DirectoryEndLocatorHeader(); - entry.Read(reader); - return entry; - } + { + var entry = new Zip64DirectoryEndLocatorHeader(); + entry.Read(reader); + return entry; + } default: throw new NotSupportedException("Unknown header: " + headerBytes); } @@ -165,22 +174,22 @@ namespace SharpCompress.Common.Zip switch (mode) { case StreamingMode.Seekable: - { - entryHeader.DataStartPosition = stream.Position; - stream.Position += entryHeader.CompressedSize; - break; - } + { + entryHeader.DataStartPosition = stream.Position; + stream.Position += entryHeader.CompressedSize; + break; + } case StreamingMode.Streaming: - { - entryHeader.PackedStream = stream; - break; - } + { + entryHeader.PackedStream = stream; + break; + } default: - { - throw new InvalidFormatException("Invalid StreamingMode"); - } + { + throw new InvalidFormatException("Invalid StreamingMode"); + } } //} diff --git a/src/SharpCompress/Compressors/Deflate/DeflateStream.cs b/src/SharpCompress/Compressors/Deflate/DeflateStream.cs index 8ccaca1e..d041533e 100644 --- a/src/SharpCompress/Compressors/Deflate/DeflateStream.cs +++ b/src/SharpCompress/Compressors/Deflate/DeflateStream.cs @@ -26,6 +26,7 @@ using System; using System.IO; +using System.Text; namespace SharpCompress.Compressors.Deflate { @@ -36,9 +37,10 @@ namespace SharpCompress.Compressors.Deflate public DeflateStream(Stream stream, CompressionMode mode, CompressionLevel level = CompressionLevel.Default, - bool leaveOpen = false) + bool leaveOpen = false, + Encoding forceEncoding = null) { - _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, leaveOpen); + _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.DEFLATE, leaveOpen, forceEncoding); } #region Zlib properties diff --git a/src/SharpCompress/Compressors/Deflate/GZipStream.cs b/src/SharpCompress/Compressors/Deflate/GZipStream.cs index 8e775c99..80be7cdc 100644 --- a/src/SharpCompress/Compressors/Deflate/GZipStream.cs +++ b/src/SharpCompress/Compressors/Deflate/GZipStream.cs @@ -30,6 +30,7 @@ using System; using System.IO; using SharpCompress.Common; using SharpCompress.Converters; +using System.Text; namespace SharpCompress.Compressors.Deflate { @@ -47,6 +48,8 @@ namespace SharpCompress.Compressors.Deflate private bool firstReadDone; private int headerByteCount; + private readonly Encoding forceEncoding; + public GZipStream(Stream stream, CompressionMode mode) : this(stream, mode, CompressionLevel.Default, false) { @@ -62,9 +65,10 @@ namespace SharpCompress.Compressors.Deflate { } - public GZipStream(Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen) + public GZipStream(Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen, Encoding forceEncoding = null) { - BaseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.GZIP, leaveOpen); + BaseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.GZIP, leaveOpen, forceEncoding); + this.forceEncoding = forceEncoding; } #region Zlib properties @@ -346,7 +350,7 @@ namespace SharpCompress.Compressors.Deflate BaseStream.Write(buffer, offset, count); } - #endregion + #endregion Stream methods public String Comment { @@ -406,8 +410,10 @@ namespace SharpCompress.Compressors.Deflate private int EmitHeader() { - byte[] commentBytes = (Comment == null) ? null : ArchiveEncoding.Default.GetBytes(Comment); - byte[] filenameBytes = (FileName == null) ? null : ArchiveEncoding.Default.GetBytes(FileName); + byte[] commentBytes = (Comment == null) ? null + : (forceEncoding ?? ArchiveEncoding.Default).GetBytes(Comment); + byte[] filenameBytes = (FileName == null) ? null + : (forceEncoding ?? ArchiveEncoding.Default).GetBytes(FileName); int cbLength = (Comment == null) ? 0 : commentBytes.Length + 1; int fnLength = (FileName == null) ? 0 : filenameBytes.Length + 1; diff --git a/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs b/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs index 7051b1f2..a993b1cb 100644 --- a/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs +++ b/src/SharpCompress/Compressors/Deflate/ZlibBaseStream.cs @@ -1,20 +1,20 @@ // ZlibBaseStream.cs // ------------------------------------------------------------------ // -// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation. +// Copyright (c) 2009 Dino Chiesa and Microsoft Corporation. // All rights reserved. // // This code module is part of DotNetZip, a zipfile class library. // // ------------------------------------------------------------------ // -// This code is licensed under the Microsoft Public License. +// This code is licensed under the Microsoft Public License. // See the file License.txt for the license details. // More info on: http://dotnetzip.codeplex.com // // ------------------------------------------------------------------ // -// last saved (in emacs): +// last saved (in emacs): // Time-stamp: <2009-October-28 15:45:15> // // ------------------------------------------------------------------ @@ -30,6 +30,7 @@ using System.IO; using SharpCompress.Common; using SharpCompress.Common.Tar.Headers; using SharpCompress.Converters; +using System.Text; namespace SharpCompress.Compressors.Deflate { @@ -64,6 +65,8 @@ namespace SharpCompress.Compressors.Deflate protected internal DateTime _GzipMtime; protected internal int _gzipHeaderByteCount; + private readonly Encoding forceEncoding; + internal int Crc32 { get @@ -80,7 +83,8 @@ namespace SharpCompress.Compressors.Deflate CompressionMode compressionMode, CompressionLevel level, ZlibStreamFlavor flavor, - bool leaveOpen) + bool leaveOpen, + Encoding forceEncoding) { _flushMode = FlushType.None; @@ -91,6 +95,8 @@ namespace SharpCompress.Compressors.Deflate _flavor = flavor; _level = level; + this.forceEncoding = forceEncoding; + // workitem 7159 if (flavor == ZlibStreamFlavor.GZIP) { @@ -418,8 +424,8 @@ namespace SharpCompress.Compressors.Deflate } } while (!done); - byte[] a = list.ToArray(); - return ArchiveEncoding.Default.GetString(a, 0, a.Length); + byte[] buffer = list.ToArray(); + return (forceEncoding ?? ArchiveEncoding.Default).GetString(buffer, 0, buffer.Length); } private int _ReadAndValidateGzipHeader() @@ -593,7 +599,7 @@ namespace SharpCompress.Compressors.Deflate while (_z.AvailableBytesOut > 0 && !nomoreinput && rc == ZlibConstants.Z_OK); // workitem 8557 - // is there more room in output? + // is there more room in output? if (_z.AvailableBytesOut > 0) { if (rc == ZlibConstants.Z_OK && _z.AvailableBytesIn == 0) diff --git a/src/SharpCompress/Compressors/Deflate/ZlibStream.cs b/src/SharpCompress/Compressors/Deflate/ZlibStream.cs index 6777c20d..38558925 100644 --- a/src/SharpCompress/Compressors/Deflate/ZlibStream.cs +++ b/src/SharpCompress/Compressors/Deflate/ZlibStream.cs @@ -27,6 +27,7 @@ using System; using System.IO; +using System.Text; namespace SharpCompress.Compressors.Deflate { @@ -50,9 +51,9 @@ namespace SharpCompress.Compressors.Deflate { } - public ZlibStream(Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen) + public ZlibStream(Stream stream, CompressionMode mode, CompressionLevel level, bool leaveOpen, Encoding forceEncoding = null) { - _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.ZLIB, leaveOpen); + _baseStream = new ZlibBaseStream(stream, mode, level, ZlibStreamFlavor.ZLIB, leaveOpen, forceEncoding); } #region Zlib properties @@ -326,6 +327,6 @@ namespace SharpCompress.Compressors.Deflate _baseStream.Write(buffer, offset, count); } - #endregion + #endregion System.IO.Stream methods } } \ No newline at end of file diff --git a/src/SharpCompress/Readers/GZip/GZipReader.cs b/src/SharpCompress/Readers/GZip/GZipReader.cs index 4150428a..f5a2fd49 100644 --- a/src/SharpCompress/Readers/GZip/GZipReader.cs +++ b/src/SharpCompress/Readers/GZip/GZipReader.cs @@ -29,11 +29,11 @@ namespace SharpCompress.Readers.GZip return new GZipReader(stream, options ?? new ReaderOptions()); } - #endregion + #endregion Open internal override IEnumerable GetEntries(Stream stream) { - return GZipEntry.GetEntries(stream); + return GZipEntry.GetEntries(stream, Options.ForceEncoding); } } } \ No newline at end of file diff --git a/src/SharpCompress/Readers/ReaderOptions.cs b/src/SharpCompress/Readers/ReaderOptions.cs index f7a4df30..683a8598 100644 --- a/src/SharpCompress/Readers/ReaderOptions.cs +++ b/src/SharpCompress/Readers/ReaderOptions.cs @@ -8,6 +8,7 @@ namespace SharpCompress.Readers /// Look for RarArchive (Check for self-extracting archives or cases where RarArchive isn't at the start of the file) /// public bool LookForHeader { get; set; } + public string Password { get; set; } } } \ No newline at end of file diff --git a/src/SharpCompress/Readers/Tar/TarReader.cs b/src/SharpCompress/Readers/Tar/TarReader.cs index b2ceb40c..805445f2 100644 --- a/src/SharpCompress/Readers/Tar/TarReader.cs +++ b/src/SharpCompress/Readers/Tar/TarReader.cs @@ -114,11 +114,11 @@ namespace SharpCompress.Readers.Tar return new TarReader(rewindableStream, options, CompressionType.None); } - #endregion + #endregion Open internal override IEnumerable GetEntries(Stream stream) { - return TarEntry.GetEntries(StreamingMode.Streaming, stream, compressionType); + return TarEntry.GetEntries(StreamingMode.Streaming, stream, compressionType, Options.ForceEncoding); } } } \ No newline at end of file diff --git a/src/SharpCompress/Readers/Zip/ZipReader.cs b/src/SharpCompress/Readers/Zip/ZipReader.cs index 7500509b..78df2dee 100644 --- a/src/SharpCompress/Readers/Zip/ZipReader.cs +++ b/src/SharpCompress/Readers/Zip/ZipReader.cs @@ -14,7 +14,7 @@ namespace SharpCompress.Readers.Zip : base(options, ArchiveType.Zip) { Volume = new ZipVolume(stream, options); - headerFactory = new StreamingZipHeaderFactory(options.Password); + headerFactory = new StreamingZipHeaderFactory(options.Password, options.ForceEncoding); } public override ZipVolume Volume { get; } @@ -33,7 +33,7 @@ namespace SharpCompress.Readers.Zip return new ZipReader(stream, options ?? new ReaderOptions()); } - #endregion + #endregion Open internal override IEnumerable GetEntries(Stream stream) { @@ -44,15 +44,15 @@ namespace SharpCompress.Readers.Zip switch (h.ZipHeaderType) { case ZipHeaderType.LocalEntry: - { - yield return new ZipEntry(new StreamingZipFilePart(h as LocalEntryHeader, - stream)); - } + { + yield return new ZipEntry(new StreamingZipFilePart(h as LocalEntryHeader, + stream)); + } break; case ZipHeaderType.DirectoryEnd: - { - yield break; - } + { + yield break; + } } } } diff --git a/src/SharpCompress/Writers/AbstractWriter.cs b/src/SharpCompress/Writers/AbstractWriter.cs index dde6892a..127c2580 100644 --- a/src/SharpCompress/Writers/AbstractWriter.cs +++ b/src/SharpCompress/Writers/AbstractWriter.cs @@ -9,9 +9,10 @@ namespace SharpCompress.Writers private bool closeStream; private bool isDisposed; - protected AbstractWriter(ArchiveType type) + protected AbstractWriter(ArchiveType type, WriterOptions options) { WriterType = type; + WriterOptions = options; } protected void InitalizeStream(Stream stream, bool closeStream) @@ -24,6 +25,8 @@ namespace SharpCompress.Writers public ArchiveType WriterType { get; } + protected WriterOptions WriterOptions { get; private set; } + public abstract void Write(string filename, Stream source, DateTime? modificationTime); protected virtual void Dispose(bool isDisposing) diff --git a/src/SharpCompress/Writers/GZip/GZipWriter.cs b/src/SharpCompress/Writers/GZip/GZipWriter.cs index d9ef3562..24db4234 100644 --- a/src/SharpCompress/Writers/GZip/GZipWriter.cs +++ b/src/SharpCompress/Writers/GZip/GZipWriter.cs @@ -11,7 +11,7 @@ namespace SharpCompress.Writers.GZip private bool wroteToStream; public GZipWriter(Stream destination, bool leaveOpen = false) - : base(ArchiveType.GZip) + : base(ArchiveType.GZip, new WriterOptions(CompressionType.GZip)) { InitalizeStream(new GZipStream(destination, CompressionMode.Compress, leaveOpen), !leaveOpen); } diff --git a/src/SharpCompress/Writers/Tar/TarWriter.cs b/src/SharpCompress/Writers/Tar/TarWriter.cs index cd0a2069..28457b6a 100644 --- a/src/SharpCompress/Writers/Tar/TarWriter.cs +++ b/src/SharpCompress/Writers/Tar/TarWriter.cs @@ -12,7 +12,7 @@ namespace SharpCompress.Writers.Tar public class TarWriter : AbstractWriter { public TarWriter(Stream destination, WriterOptions options) - : base(ArchiveType.Tar) + : base(ArchiveType.Tar, options) { if (!destination.CanWrite) { @@ -72,7 +72,11 @@ namespace SharpCompress.Writers.Tar long realSize = size ?? source.Length; - TarHeader header = new TarHeader(); + TarHeader header = new TarHeader() + { + ForceEncoding = WriterOptions.ForceEncoding + }; + header.LastModifiedTime = modificationTime ?? TarHeader.Epoch; header.Name = NormalizeFilename(filename); header.Size = realSize; diff --git a/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs b/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs index 39e488df..eb04ceaa 100644 --- a/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs +++ b/src/SharpCompress/Writers/Zip/ZipCentralDirectoryEntry.cs @@ -11,14 +11,16 @@ namespace SharpCompress.Writers.Zip { private readonly ZipCompressionMethod compression; private readonly string fileName; + private readonly Encoding forceEncoding; - public ZipCentralDirectoryEntry(ZipCompressionMethod compression, string fileName, ulong headerOffset) + public ZipCentralDirectoryEntry(ZipCompressionMethod compression, string fileName, ulong headerOffset, Encoding forceEncoding) { this.compression = compression; this.fileName = fileName; HeaderOffset = headerOffset; + this.forceEncoding = forceEncoding; } - + internal DateTime? ModificationTime { get; set; } internal string Comment { get; set; } internal uint Crc { get; set; } @@ -29,11 +31,11 @@ namespace SharpCompress.Writers.Zip internal uint Write(Stream outputStream) { - byte[] encodedFilename = Encoding.UTF8.GetBytes(fileName); - byte[] encodedComment = Encoding.UTF8.GetBytes(Comment); + byte[] encodedFilename = (forceEncoding ?? Encoding.UTF8).GetBytes(fileName); + byte[] encodedComment = (forceEncoding ?? Encoding.UTF8).GetBytes(Comment); - var zip64_stream = Compressed >= uint.MaxValue || Decompressed >= uint.MaxValue; - var zip64 = zip64_stream || HeaderOffset >= uint.MaxValue || Zip64HeaderOffset != 0; + var zip64_stream = Compressed >= uint.MaxValue || Decompressed >= uint.MaxValue; + var zip64 = zip64_stream || HeaderOffset >= uint.MaxValue || Zip64HeaderOffset != 0; var compressedvalue = zip64 ? uint.MaxValue : (uint)Compressed; var decompressedvalue = zip64 ? uint.MaxValue : (uint)Decompressed; @@ -41,18 +43,18 @@ namespace SharpCompress.Writers.Zip var extralength = zip64 ? (2 + 2 + 8 + 8 + 8 + 4) : 0; var version = (byte)(zip64 ? 45 : 20); // Version 20 required for deflate/encryption - HeaderFlags flags = HeaderFlags.UTF8; + HeaderFlags flags = (forceEncoding ?? Encoding.UTF8) == Encoding.UTF8 ? HeaderFlags.UTF8 : HeaderFlags.None; if (!outputStream.CanSeek) { // Cannot use data descriptors with zip64: // https://blogs.oracle.com/xuemingshen/entry/is_zipinput_outputstream_handling_of - // We check that streams are not written too large in the ZipWritingStream, - // so this extra guard is not required, but kept to simplify changing the code - // once the zip64 post-data issue is resolved + // We check that streams are not written too large in the ZipWritingStream, + // so this extra guard is not required, but kept to simplify changing the code + // once the zip64 post-data issue is resolved if (!zip64_stream) flags |= HeaderFlags.UsePostDataDescriptor; - + if (compression == ZipCompressionMethod.LZMA) { flags |= HeaderFlags.Bit1; // eos marker diff --git a/src/SharpCompress/Writers/Zip/ZipWriter.cs b/src/SharpCompress/Writers/Zip/ZipWriter.cs index 3e981a01..defeb854 100644 --- a/src/SharpCompress/Writers/Zip/ZipWriter.cs +++ b/src/SharpCompress/Writers/Zip/ZipWriter.cs @@ -26,7 +26,7 @@ namespace SharpCompress.Writers.Zip private readonly bool isZip64; public ZipWriter(Stream destination, ZipWriterOptions zipWriterOptions) - : base(ArchiveType.Zip) + : base(ArchiveType.Zip, zipWriterOptions) { zipComment = zipWriterOptions.ArchiveComment ?? string.Empty; isZip64 = zipWriterOptions.UseZip64; @@ -65,6 +65,7 @@ namespace SharpCompress.Writers.Zip } base.Dispose(isDisposing); } + private static ZipCompressionMethod ToZipCompressionMethod(CompressionType compressionType) { switch (compressionType) @@ -97,9 +98,9 @@ namespace SharpCompress.Writers.Zip public override void Write(string entryPath, Stream source, DateTime? modificationTime) { Write(entryPath, source, new ZipWriterEntryOptions() - { - ModificationDateTime = modificationTime - }); + { + ModificationDateTime = modificationTime + }); } public void Write(string entryPath, Stream source, ZipWriterEntryOptions zipWriterEntryOptions) @@ -117,11 +118,11 @@ namespace SharpCompress.Writers.Zip entryPath = NormalizeFilename(entryPath); options.ModificationDateTime = options.ModificationDateTime ?? DateTime.Now; options.EntryComment = options.EntryComment ?? string.Empty; - var entry = new ZipCentralDirectoryEntry(compression, entryPath, (ulong)streamPosition) - { - Comment = options.EntryComment, - ModificationTime = options.ModificationDateTime - }; + var entry = new ZipCentralDirectoryEntry(compression, entryPath, (ulong)streamPosition, WriterOptions.ForceEncoding) + { + Comment = options.EntryComment, + ModificationTime = options.ModificationDateTime + }; // Use the archive default setting for zip64 and allow overrides var useZip64 = isZip64; @@ -130,7 +131,7 @@ namespace SharpCompress.Writers.Zip var headersize = (uint)WriteHeader(entryPath, options, entry, useZip64); streamPosition += headersize; - return new ZipWritingStream(this, OutputStream, entry, compression, + return new ZipWritingStream(this, OutputStream, entry, compression, options.DeflateCompressionLevel ?? compressionLevel); } @@ -149,12 +150,12 @@ namespace SharpCompress.Writers.Zip private int WriteHeader(string filename, ZipWriterEntryOptions zipWriterEntryOptions, ZipCentralDirectoryEntry entry, bool useZip64) { - // We err on the side of caution until the zip specification clarifies how to support this - if (!OutputStream.CanSeek && useZip64) - throw new NotSupportedException("Zip64 extensions are not supported on non-seekable streams"); + // We err on the side of caution until the zip specification clarifies how to support this + if (!OutputStream.CanSeek && useZip64) + throw new NotSupportedException("Zip64 extensions are not supported on non-seekable streams"); var explicitZipCompressionInfo = ToZipCompressionMethod(zipWriterEntryOptions.CompressionType ?? compressionType); - byte[] encodedFilename = ArchiveEncoding.Default.GetBytes(filename); + byte[] encodedFilename = (WriterOptions.ForceEncoding ?? ArchiveEncoding.Default).GetBytes(filename); OutputStream.Write(DataConverter.LittleEndian.GetBytes(ZipHeaderFactory.ENTRY_HEADER_BYTES), 0, 4); if (explicitZipCompressionInfo == ZipCompressionMethod.Deflate) @@ -162,17 +163,17 @@ namespace SharpCompress.Writers.Zip if (OutputStream.CanSeek && useZip64) OutputStream.Write(new byte[] { 45, 0 }, 0, 2); //smallest allowed version for zip64 else - OutputStream.Write(new byte[] { 20, 0 }, 0, 2); //older version which is more compatible + OutputStream.Write(new byte[] { 20, 0 }, 0, 2); //older version which is more compatible } else { OutputStream.Write(new byte[] { 63, 0 }, 0, 2); //version says we used PPMd or LZMA } - HeaderFlags flags = ArchiveEncoding.Default == Encoding.UTF8 ? HeaderFlags.UTF8 : 0; + HeaderFlags flags = (WriterOptions.ForceEncoding ?? ArchiveEncoding.Default) == Encoding.UTF8 ? HeaderFlags.UTF8 : 0; if (!OutputStream.CanSeek) { flags |= HeaderFlags.UsePostDataDescriptor; - + if (explicitZipCompressionInfo == ZipCompressionMethod.LZMA) { flags |= HeaderFlags.Bit1; // eos marker @@ -213,11 +214,11 @@ namespace SharpCompress.Writers.Zip private void WriteEndRecord(ulong size) { - byte[] encodedComment = ArchiveEncoding.Default.GetBytes(zipComment); + byte[] encodedComment = (WriterOptions.ForceEncoding ?? ArchiveEncoding.Default).GetBytes(zipComment); var zip64 = isZip64 || entries.Count > ushort.MaxValue || streamPosition >= uint.MaxValue || size >= uint.MaxValue; var sizevalue = size >= uint.MaxValue ? uint.MaxValue : (uint)size; - var streampositionvalue = streamPosition >= uint.MaxValue ? uint.MaxValue : (uint)streamPosition; + var streampositionvalue = streamPosition >= uint.MaxValue ? uint.MaxValue : (uint)streamPosition; if (zip64) { @@ -250,7 +251,7 @@ namespace SharpCompress.Writers.Zip } // Write normal end of central directory record - OutputStream.Write(new byte[] {80, 75, 5, 6, 0, 0, 0, 0}, 0, 8); + OutputStream.Write(new byte[] { 80, 75, 5, 6, 0, 0, 0, 0 }, 0, 8); OutputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)entries.Count), 0, 2); OutputStream.Write(DataConverter.LittleEndian.GetBytes((ushort)entries.Count), 0, 2); OutputStream.Write(DataConverter.LittleEndian.GetBytes(sizevalue), 0, 4); @@ -273,10 +274,10 @@ namespace SharpCompress.Writers.Zip private CountingWritableSubStream counting; private ulong decompressed; - // Flag to prevent throwing exceptions on Dispose - private bool limitsExceeded; + // Flag to prevent throwing exceptions on Dispose + private bool limitsExceeded; - internal ZipWritingStream(ZipWriter writer, Stream originalStream, ZipCentralDirectoryEntry entry, + internal ZipWritingStream(ZipWriter writer, Stream originalStream, ZipCentralDirectoryEntry entry, ZipCompressionMethod zipCompressionMethod, CompressionLevel compressionLevel) { this.writer = writer; @@ -305,39 +306,39 @@ namespace SharpCompress.Writers.Zip switch (zipCompressionMethod) { case ZipCompressionMethod.None: - { - return output; - } + { + return output; + } case ZipCompressionMethod.Deflate: - { - return new DeflateStream(counting, CompressionMode.Compress, compressionLevel, - true); - } + { + return new DeflateStream(counting, CompressionMode.Compress, compressionLevel, + true); + } case ZipCompressionMethod.BZip2: - { - return new BZip2Stream(counting, CompressionMode.Compress, true); - } + { + return new BZip2Stream(counting, CompressionMode.Compress, true); + } case ZipCompressionMethod.LZMA: - { - counting.WriteByte(9); - counting.WriteByte(20); - counting.WriteByte(5); - counting.WriteByte(0); + { + counting.WriteByte(9); + counting.WriteByte(20); + counting.WriteByte(5); + counting.WriteByte(0); - LzmaStream lzmaStream = new LzmaStream(new LzmaEncoderProperties(!originalStream.CanSeek), - false, counting); - counting.Write(lzmaStream.Properties, 0, lzmaStream.Properties.Length); - return lzmaStream; - } + LzmaStream lzmaStream = new LzmaStream(new LzmaEncoderProperties(!originalStream.CanSeek), + false, counting); + counting.Write(lzmaStream.Properties, 0, lzmaStream.Properties.Length); + return lzmaStream; + } case ZipCompressionMethod.PPMd: - { - counting.Write(writer.PpmdProperties.Properties, 0, 2); - return new PpmdStream(writer.PpmdProperties, counting, true); - } + { + counting.Write(writer.PpmdProperties.Properties, 0, 2); + return new PpmdStream(writer.PpmdProperties, counting, true); + } default: - { - throw new NotSupportedException("CompressionMethod: " + zipCompressionMethod); - } + { + throw new NotSupportedException("CompressionMethod: " + zipCompressionMethod); + } } } @@ -348,65 +349,65 @@ namespace SharpCompress.Writers.Zip { writeStream.Dispose(); - if (limitsExceeded) - { - // We have written invalid data into the archive, - // so we destroy it now, instead of allowing the user to continue - // with a defunct archive - originalStream.Dispose(); - return; - } + if (limitsExceeded) + { + // We have written invalid data into the archive, + // so we destroy it now, instead of allowing the user to continue + // with a defunct archive + originalStream.Dispose(); + return; + } entry.Crc = (uint)crc.Crc32Result; entry.Compressed = counting.Count; entry.Decompressed = decompressed; var zip64 = entry.Compressed >= uint.MaxValue || entry.Decompressed >= uint.MaxValue; - var compressedvalue = zip64 ? uint.MaxValue : (uint)counting.Count; - var decompressedvalue = zip64 ? uint.MaxValue : (uint)entry.Decompressed; + var compressedvalue = zip64 ? uint.MaxValue : (uint)counting.Count; + var decompressedvalue = zip64 ? uint.MaxValue : (uint)entry.Decompressed; if (originalStream.CanSeek) { originalStream.Position = (long)(entry.HeaderOffset + 6); originalStream.WriteByte(0); - + originalStream.Position = (long)(entry.HeaderOffset + 14); writer.WriteFooter(entry.Crc, compressedvalue, decompressedvalue); - // Ideally, we should not throw from Dispose() - // We should not get here as the Write call checks the limits - if (zip64 && entry.Zip64HeaderOffset == 0) - throw new NotSupportedException("Attempted to write a stream that is larger than 4GiB without setting the zip64 option"); + // Ideally, we should not throw from Dispose() + // We should not get here as the Write call checks the limits + if (zip64 && entry.Zip64HeaderOffset == 0) + throw new NotSupportedException("Attempted to write a stream that is larger than 4GiB without setting the zip64 option"); - // If we have pre-allocated space for zip64 data, - // fill it out, even if it is not required - if (entry.Zip64HeaderOffset != 0) - { - originalStream.Position = (long)(entry.HeaderOffset + entry.Zip64HeaderOffset); - originalStream.Write(DataConverter.LittleEndian.GetBytes((ushort)0x0001), 0, 2); - originalStream.Write(DataConverter.LittleEndian.GetBytes((ushort)(8 + 8)), 0, 2); + // If we have pre-allocated space for zip64 data, + // fill it out, even if it is not required + if (entry.Zip64HeaderOffset != 0) + { + originalStream.Position = (long)(entry.HeaderOffset + entry.Zip64HeaderOffset); + originalStream.Write(DataConverter.LittleEndian.GetBytes((ushort)0x0001), 0, 2); + originalStream.Write(DataConverter.LittleEndian.GetBytes((ushort)(8 + 8)), 0, 2); - originalStream.Write(DataConverter.LittleEndian.GetBytes(entry.Decompressed), 0, 8); - originalStream.Write(DataConverter.LittleEndian.GetBytes(entry.Compressed), 0, 8); - } + originalStream.Write(DataConverter.LittleEndian.GetBytes(entry.Decompressed), 0, 8); + originalStream.Write(DataConverter.LittleEndian.GetBytes(entry.Compressed), 0, 8); + } originalStream.Position = writer.streamPosition + (long)entry.Compressed; writer.streamPosition += (long)entry.Compressed; } else { - // We have a streaming archive, so we should add a post-data-descriptor, - // but we cannot as it does not hold the zip64 values - // Throwing an exception until the zip specification is clarified + // We have a streaming archive, so we should add a post-data-descriptor, + // but we cannot as it does not hold the zip64 values + // Throwing an exception until the zip specification is clarified - // Ideally, we should not throw from Dispose() - // We should not get here as the Write call checks the limits - if (zip64) - throw new NotSupportedException("Streams larger than 4GiB are not supported for non-seekable streams"); + // Ideally, we should not throw from Dispose() + // We should not get here as the Write call checks the limits + if (zip64) + throw new NotSupportedException("Streams larger than 4GiB are not supported for non-seekable streams"); - originalStream.Write(DataConverter.LittleEndian.GetBytes(ZipHeaderFactory.POST_DATA_DESCRIPTOR), 0, 4); - writer.WriteFooter(entry.Crc, + originalStream.Write(DataConverter.LittleEndian.GetBytes(ZipHeaderFactory.POST_DATA_DESCRIPTOR), 0, 4); + writer.WriteFooter(entry.Crc, (uint)compressedvalue, (uint)decompressedvalue); writer.streamPosition += (long)entry.Compressed + 16; @@ -437,36 +438,35 @@ namespace SharpCompress.Writers.Zip public override void Write(byte[] buffer, int offset, int count) { - // We check the limits first, because we can keep the archive consistent - // if we can prevent the writes from happening - if (entry.Zip64HeaderOffset == 0) - { - // Pre-check, the counting.Count is not exact, as we do not know the size before having actually compressed it - if (limitsExceeded || ((decompressed + (uint)count) > uint.MaxValue) || (counting.Count + (uint)count) > uint.MaxValue) - throw new NotSupportedException("Attempted to write a stream that is larger than 4GiB without setting the zip64 option"); - } + // We check the limits first, because we can keep the archive consistent + // if we can prevent the writes from happening + if (entry.Zip64HeaderOffset == 0) + { + // Pre-check, the counting.Count is not exact, as we do not know the size before having actually compressed it + if (limitsExceeded || ((decompressed + (uint)count) > uint.MaxValue) || (counting.Count + (uint)count) > uint.MaxValue) + throw new NotSupportedException("Attempted to write a stream that is larger than 4GiB without setting the zip64 option"); + } decompressed += (uint)count; crc.SlurpBlock(buffer, offset, count); writeStream.Write(buffer, offset, count); - if (entry.Zip64HeaderOffset == 0) - { - // Post-check, this is accurate - if ((decompressed > uint.MaxValue) || counting.Count > uint.MaxValue) - { - // We have written the data, so the archive is now broken - // Throwing the exception here, allows us to avoid - // throwing an exception in Dispose() which is discouraged - // as it can mask other errors - limitsExceeded = true; - throw new NotSupportedException("Attempted to write a stream that is larger than 4GiB without setting the zip64 option"); - } - } - + if (entry.Zip64HeaderOffset == 0) + { + // Post-check, this is accurate + if ((decompressed > uint.MaxValue) || counting.Count > uint.MaxValue) + { + // We have written the data, so the archive is now broken + // Throwing the exception here, allows us to avoid + // throwing an exception in Dispose() which is discouraged + // as it can mask other errors + limitsExceeded = true; + throw new NotSupportedException("Attempted to write a stream that is larger than 4GiB without setting the zip64 option"); + } + } } } - #endregion + #endregion Nested type: ZipWritingStream } } \ No newline at end of file